From 9d9a7cecda029fcf40e0ae9fb1b4ead7326512fc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alexa=20Valentov=C3=A1?= <git@imaniti.org>
Date: Wed, 18 Sep 2024 15:27:39 +0200
Subject: [PATCH] update API

---
 Makefile                  |   2 +-
 contracts/admin.py        |   1 +
 contracts/views.py        | 108 ++++++++++++++++++++++++++------------
 manage.py                 |   2 +-
 oidc/auth.py              |  10 ++--
 registry/settings/base.py |   4 +-
 registry/wsgi.py          |   2 +-
 7 files changed, 89 insertions(+), 40 deletions(-)

diff --git a/Makefile b/Makefile
index 6cfddd0..3dd614c 100644
--- a/Makefile
+++ b/Makefile
@@ -46,7 +46,7 @@ run: venv
 	${VENV}/bin/python manage.py runserver ${PORT} --settings=${SETTINGS}
 
 shell: venv
-	${VENV}/bin/python manage.py shell_plus --settings=${SETTINGS}
+	${VENV}/bin/python manage.py shell --settings=${SETTINGS}
 
 sync:
 	${VENV}/bin/python manage.py import_old_contracts https://github.com/pirati-web/smlouvy.pirati.cz.git gh-pages --settings=${SETTINGS} --delete -v 3
diff --git a/contracts/admin.py b/contracts/admin.py
index 5bd4f21..25550d3 100644
--- a/contracts/admin.py
+++ b/contracts/admin.py
@@ -649,6 +649,7 @@ class ContractAdmin(
             "Naše smluvní strana", "contractee_signatures__contractee"
         ),
         AutocompleteFilterFactory("Jiná smluvní strana", "signee_signatures__signee"),
+        AutocompleteFilterFactory("Primární smlouva", "primary_contract"),
         "status",
         "is_valid",
         "is_public",
diff --git a/contracts/views.py b/contracts/views.py
index 6e0d4f8..79e725f 100644
--- a/contracts/views.py
+++ b/contracts/views.py
@@ -436,7 +436,9 @@ def view_signees(request):
 
 
 # Basic contract view API
-def view_contract_json(request, id: int):
+
+
+def get_contract_dict(request, id: int, error_if_missing_perm: bool = True):
     filter = models.Q(status=Contract.StatusTypes.APPROVED)
 
     if not request.user.has_perm("contracts.view_confidential"):
@@ -449,40 +451,80 @@ def view_contract_json(request, id: int):
             )
         )
 
-    contract = get_object_or_404(
-        (get_objects_for_user(request.user, "contracts.view_contract").filter(filter)),
-        id=id,
-    )
+    try:
+        contract = get_object_or_404(
+            (
+                get_objects_for_user(request.user, "contracts.view_contract").filter(
+                    filter
+                )
+            ),
+            id=id,
+        )
+    except Exception as e:
+        if error_if_missing_perm:
+            # Re-raise
+            raise e
+        else:
+            return None
 
-    return JsonResponse(
-        {
-            "id": contract.id,
-            "created_by": {
-                "id": contract.created_by_id,
-                "username": contract.created_by.username,
+    subcontracts = []
+
+    for subcontract in contract.subcontracts.order_by("-valid_start_date").all():
+        subcontract_dict = get_contract_dict(request, subcontract.id, False)
+
+        if subcontract_dict is None:
+            continue
+
+        subcontracts.append(subcontract_dict)
+
+    return {
+        "id": contract.id,
+        "created_by": {
+            "id": contract.created_by_id,
+            "username": contract.created_by.preferred_username,
+        },
+        "created_on": contract.created_on,
+        "updated_on": contract.updated_on,
+        "status": contract.status,
+        "name": contract.name,
+        "id_number": contract.id_number,
+        "types": [{"id": type.id, "name": type.name} for type in contract.types.all()],
+        "summary": contract.summary,
+        "validity": {
+            "start_date": contract.valid_start_date,
+            "end_date": contract.valid_end_date,
+        },
+        "is_valid": contract.is_valid,
+        "is_public": contract.is_public,
+        "publishing_rejection_comment": contract.publishing_rejection_comment,
+        "cost": {
+            "amount": contract.cost_amount,
+            "unit": {
+                "enum": contract.cost_unit,
+                "display": contract.get_cost_unit_display(),
+                "other": contract.cost_unit_other,
             },
-            "created_on": contract.created_on,
-            "updated_on": contract.updated_on,
-            "status": contract.status,
-            "name": contract.name,
-            "id_number": contract.id_number,
-            "types": [
-                {"id": type.id, "name": type.name} for type in contract.types.all()
-            ],
-            "summary": contract.summary,
-            "valid_start_date": contract.valid_start_date,
-            "valid_end_date": contract.valid_end_date,
-            "is_valid": contract.is_valid,
-            "is_public": contract.is_public,
-            "publishing_rejection_comment": contract.publishing_rejection_comment,
-            "paper_form_state": contract.paper_form_state,
-            "paper_form_person_responsible": contract.paper_form_person_responsible,
-            "tender_url": contract.tender_url,
-            "issues": [
-                {"id": issue.id, "name": issue.name} for issue in contract.issues.all()
-            ],
-        }
-    )
+        },
+        "paper_form": {
+            "state": contract.paper_form_state,
+            "person_responsible": contract.paper_form_person_responsible,
+        },
+        "tender_url": contract.tender_url,
+        "issues": [
+            {"id": issue.id, "name": issue.name} for issue in contract.issues.all()
+        ],
+        "intents": [
+            {"id": intent.id, "name": intent.name, "url": intent.url}
+            for intent in contract.intents.all()
+        ],
+        "subcontracts": subcontracts,
+    }
+
+
+def view_contract_json(request, id: int):
+    contract_dict = get_contract_dict(request, id)
+
+    return JsonResponse(contract_dict)
 
 
 # ARES CORS proxy
diff --git a/manage.py b/manage.py
index 45b9a48..d8d5a96 100755
--- a/manage.py
+++ b/manage.py
@@ -6,7 +6,7 @@ import sys
 
 def main():
     """Run administrative tasks."""
-    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "registry.settings")
+    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "registry.settings.dev")
     try:
         from django.core.management import execute_from_command_line
     except ImportError as exc:
diff --git a/oidc/auth.py b/oidc/auth.py
index 3bda318..ec7743f 100644
--- a/oidc/auth.py
+++ b/oidc/auth.py
@@ -75,9 +75,13 @@ class RegistryOIDCAuthenticationBackend(PiratesOIDCAuthenticationBackend):
 
         try:
             result = client.execute(query)
-        except TransportQueryError:
-            # rv_gid was not found
-            raise HTTPExceptions.BAD_REQUEST
+        except (TransportQueryError, ConnectionError, Exception) as e:
+            print(e)
+
+            # Return no groups if the Chobotnice connection fails,
+            # so users can still at least log in.
+
+            return []
 
         groups = []
 
diff --git a/registry/settings/base.py b/registry/settings/base.py
index 9f97f39..d5ab694 100644
--- a/registry/settings/base.py
+++ b/registry/settings/base.py
@@ -162,7 +162,9 @@ OIDC_OP_TOKEN_ENDPOINT = OIDC_RP_REALM_URL + "protocol/openid-connect/token"
 OIDC_OP_USER_ENDPOINT = OIDC_RP_REALM_URL + "protocol/openid-connect/userinfo"
 
 # Chobotnice - group source
-CHOBOTNICE_API_URL = env.str("CHOBOTNICE_API_URL")
+CHOBOTNICE_API_URL = env.str(
+    "CHOBOTNICE_API_URL", "https://chobotnice.pirati.cz/graphql/"
+)
 
 
 ## Internationalization
diff --git a/registry/wsgi.py b/registry/wsgi.py
index bf93aed..f4221c6 100644
--- a/registry/wsgi.py
+++ b/registry/wsgi.py
@@ -11,6 +11,6 @@ import os
 
 from django.core.wsgi import get_wsgi_application
 
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "registry.settings")
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "registry.settings.dev")
 
 application = get_wsgi_application()
-- 
GitLab