From 1ed3d27533bf9036ff1d8c6f1334ba7a92e28955 Mon Sep 17 00:00:00 2001 From: moanos Date: Sun, 3 Aug 2025 10:00:42 +0200 Subject: [PATCH] feat: add option to sync to twenty --- .../management/commands/sync_to_twenty.py | 19 +++++++ ...9_rescueorganization_twenty_id_and_more.py | 23 ++++++++ src/fellchensammlung/models.py | 2 + src/fellchensammlung/tools/twenty.py | 52 +++++++++++++++++++ 4 files changed, 96 insertions(+) create mode 100644 src/fellchensammlung/management/commands/sync_to_twenty.py create mode 100644 src/fellchensammlung/migrations/0059_rescueorganization_twenty_id_and_more.py create mode 100644 src/fellchensammlung/tools/twenty.py diff --git a/src/fellchensammlung/management/commands/sync_to_twenty.py b/src/fellchensammlung/management/commands/sync_to_twenty.py new file mode 100644 index 0000000..052ac3f --- /dev/null +++ b/src/fellchensammlung/management/commands/sync_to_twenty.py @@ -0,0 +1,19 @@ +from django.core.management import BaseCommand +from tqdm import tqdm + +from fellchensammlung.models import RescueOrganization +from fellchensammlung.tools.twenty import sync_rescue_org_to_twenty + + +class Command(BaseCommand): + help = 'Send rescue organizations as companies to twenty' + + def add_arguments(self, parser): + parser.add_argument("base_url", type=str) + parser.add_argument("token", type=str) + + def handle(self, *args, **options): + base_url = options["base_url"] + token = options["token"] + for rescue_org in tqdm(RescueOrganization.objects.all()): + sync_rescue_org_to_twenty(rescue_org, base_url, token) diff --git a/src/fellchensammlung/migrations/0059_rescueorganization_twenty_id_and_more.py b/src/fellchensammlung/migrations/0059_rescueorganization_twenty_id_and_more.py new file mode 100644 index 0000000..2c51a24 --- /dev/null +++ b/src/fellchensammlung/migrations/0059_rescueorganization_twenty_id_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 5.2.1 on 2025-08-02 09:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('fellchensammlung', '0058_socialmediapost'), + ] + + operations = [ + migrations.AddField( + model_name='rescueorganization', + name='twenty_id', + field=models.UUIDField(blank=True, help_text='ID der der Organisation in Twenty', null=True, verbose_name='Twenty-ID'), + ), + migrations.AlterField( + model_name='rescueorganization', + name='specializations', + field=models.ManyToManyField(blank=True, to='fellchensammlung.species'), + ), + ] diff --git a/src/fellchensammlung/models.py b/src/fellchensammlung/models.py index 765a4c0..eb95ee3 100644 --- a/src/fellchensammlung/models.py +++ b/src/fellchensammlung/models.py @@ -173,6 +173,8 @@ class RescueOrganization(models.Model): parent_org = models.ForeignKey("RescueOrganization", on_delete=models.PROTECT, blank=True, null=True) # allows to specify if a rescue organization has a specialization for dedicated species specializations = models.ManyToManyField(Species, blank=True) + twenty_id = models.UUIDField(verbose_name=_("Twenty-ID"), null=True, blank=True, + help_text=_("ID der der Organisation in Twenty")) class Meta: unique_together = ('external_object_identifier', 'external_source_identifier',) diff --git a/src/fellchensammlung/tools/twenty.py b/src/fellchensammlung/tools/twenty.py new file mode 100644 index 0000000..9951fdf --- /dev/null +++ b/src/fellchensammlung/tools/twenty.py @@ -0,0 +1,52 @@ +import requests + +from fellchensammlung.models import RescueOrganization + + +def sync_rescue_org_to_twenty(rescue_org: RescueOrganization, base_url, token: str): + if rescue_org.twenty_id: + update = True + else: + update = False + + payload = { + "eMails": { + "primaryEmail": rescue_org.email, + "additionalEmails": None + }, + "domainName": { + "primaryLinkLabel": rescue_org.website, + "primaryLinkUrl": rescue_org.website, + "additionalLinks": [] + }, + "name": rescue_org.name, + } + + if rescue_org.location: + payload["address"] = { + "addressStreet1": f"{rescue_org.location.street} {rescue_org.location.housenumber}", + "addressCity": rescue_org.location.city, + "addressPostcode": rescue_org.location.postcode, + "addressCountry": rescue_org.location.countrycode, + "addressLat": rescue_org.location.latitude, + "addressLng": rescue_org.location.longitude, + } + headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {token}" + } + + if update: + url = f"{base_url}/rest/companies/{rescue_org.twenty_id}" + response = requests.patch(url, json=payload, headers=headers) + assert response.status_code == 200 + else: + url = f"{base_url}/rest/companies" + response = requests.post(url, json=payload, headers=headers) + try: + assert response.status_code == 201 + except AssertionError: + print(response.request.body) + return + rescue_org.twenty_id = response.json()["data"]["createCompany"]["id"] + rescue_org.save()