From b88766a263830a50c7ab009420476926fd8c8b79 Mon Sep 17 00:00:00 2001 From: sanchegm Date: Mon, 9 Dec 2024 13:12:44 -0800 Subject: [PATCH 1/5] Update API response to match previous JSON format - Add "ensure_ascii": false param to account for special characters - Add more formatting to directly match the old JSON format. - Fix formatting error with subgroups object - Change how names are displayed. - Only add approvers array if there are approvers. --- src/affiliations/views.py | 159 +++++++++++++++++++++++++------------- 1 file changed, 106 insertions(+), 53 deletions(-) diff --git a/src/affiliations/views.py b/src/affiliations/views.py index e776093..868e458 100644 --- a/src/affiliations/views.py +++ b/src/affiliations/views.py @@ -35,39 +35,66 @@ def affiliations_list_json_format(request): # pylint: disable=unused-argument response_obj = {} for affil in affils_queryset: affil_type = affil["type"].lower() - if affil["affiliation_id"] not in response_obj: - old_json_format = { - "affiliation_id": affil["affiliation_id"], - "affiliation_fullname": affil["full_name"], - "subgroups": { - affil_type: { - "id": affil["expert_panel_id"], - "fullname": affil["full_name"], + # In old JSON, SC-VCEPS are only considered VCEPS. + if affil_type == "sc_vcep": + affil_type = "vcep" + # In old JSON, Affiliation IDs and EP Ids are in string format. + affil_id = str(affil["affiliation_id"]) + ep_id = str(affil["expert_panel_id"]) + + if affil_id not in response_obj: + if affil_type in ["vcep", "gcep"]: + old_json_format = { + "affiliation_id": affil_id, + "affiliation_fullname": affil["full_name"], + "subgroups": { + affil_type: { + "id": ep_id, + "fullname": affil["full_name"], + }, }, - }, - "approver": [], - } - response_obj[affil["affiliation_id"]] = old_json_format - - elif affil_type not in response_obj[affil["affiliation_id"]]["subgroups"]: - response_obj[affil["affiliation_id"]]["affiliation_fullname"] = ( - response_obj[affil["affiliation_id"]]["affiliation_fullname"] - + "/" - + affil["type"] - ) - response_obj[affil["affiliation_id"]]["subgroups"][affil_type] = { - affil_type: { - "id": affil["expert_panel_id"], - "fullname": affil["full_name"], - }, + } + # Independent curation group format + else: + old_json_format = { + "affiliation_id": affil_id, + "affiliation_fullname": affil["full_name"], + } + response_obj[affil_id] = old_json_format + elif affil_type not in response_obj[affil_id]["subgroups"]: + # If VCEP or GCEP in full name, add other subgroup to end of name. + if (("VCEP" in response_obj[affil_id]["affiliation_fullname"]) or + ("GCEP" in response_obj[affil_id]["affiliation_fullname"])): + response_obj[affil_id]["affiliation_fullname"] = ( + response_obj[affil_id]["affiliation_fullname"] + + "/" + + affil["type"] + ) + # Else append affiliation subgroup name to full name + else: + response_obj[affil_id]["affiliation_fullname"] = response_obj[affil_id]["affiliation_fullname"] + \ + "/" + affil["full_name"] + + response_obj[affil_id]["subgroups"][affil_type] = { + "id": ep_id, + "fullname": affil["full_name"], } + # If there are approvers, add them to the object. approvers_queryset = Approver.objects.filter( affiliation_id=affil["id"] ).values_list("approver_name", flat=True) + if approvers_queryset and "approver" not in response_obj[affil_id]: + response_obj[affil_id]["approver"] = [] for name in approvers_queryset: - response_obj[affil["affiliation_id"]]["approver"].append(name) + response_obj[affil_id]["approver"].append(name) - return JsonResponse(list(response_obj.values()), status=200, safe=False) + return JsonResponse(list(response_obj.values()), + status=200, + safe=False, + json_dumps_params={ + "ensure_ascii": False + } + ) @login_required @@ -81,36 +108,62 @@ def affiliation_detail_json_format(request): response_obj = {} for affil in affils_queryset: affil_type = affil["type"].lower() - if affil["affiliation_id"] not in response_obj: - old_json_format = { - "affiliation_id": affil["affiliation_id"], - "affiliation_fullname": affil["full_name"], - "subgroups": { - affil_type: { - "id": affil["expert_panel_id"], - "fullname": affil["full_name"], + # In old JSON, SC-VCEPS are only considered VCEPS. + if affil_type == "sc_vcep": + affil_type = "vcep" + # In old JSON, Affiliation IDs and EP Ids are in string format. + affil_id = str(affil["affiliation_id"]) + ep_id = str(affil["expert_panel_id"]) + + if affil_id not in response_obj: + if affil_type in ["vcep", "gcep"]: + old_json_format = { + "affiliation_id": affil_id, + "affiliation_fullname": affil["full_name"], + "subgroups": { + affil_type: { + "id": ep_id, + "fullname": affil["full_name"], + }, }, - }, - "approver": [], - } - response_obj[affil["affiliation_id"]] = old_json_format - - elif affil_type not in response_obj[affil["affiliation_id"]]["subgroups"]: - response_obj[affil["affiliation_id"]]["affiliation_fullname"] = ( - response_obj[affil["affiliation_id"]]["affiliation_fullname"] - + "/" - + affil["full_name"] - ) - response_obj[affil["affiliation_id"]]["subgroups"][affil_type] = { - affil_type: { - "id": affil["expert_panel_id"], - "fullname": affil["full_name"], - }, + } + # Independent curation group format + else: + old_json_format = { + "affiliation_id": affil_id, + "affiliation_fullname": affil["full_name"], + } + response_obj[affil_id] = old_json_format + elif affil_type not in response_obj[affil_id]["subgroups"]: + # If VCEP or GCEP in full name, add other subgroup to end of name. + if (("VCEP" in response_obj[affil_id]["affiliation_fullname"]) or + ("GCEP" in response_obj[affil_id]["affiliation_fullname"])): + response_obj[affil_id]["affiliation_fullname"] = ( + response_obj[affil_id]["affiliation_fullname"] + + "/" + + affil["type"] + ) + # Else append affiliation subgroup name to full name + else: + response_obj[affil_id]["affiliation_fullname"] = response_obj[affil_id]["affiliation_fullname"] + \ + "/" + affil["full_name"] + + response_obj[affil_id]["subgroups"][affil_type] = { + "id": ep_id, + "fullname": affil["full_name"], } + # If there are approvers, add them to the object. approvers_queryset = Approver.objects.filter( affiliation_id=affil["id"] ).values_list("approver_name", flat=True) + if approvers_queryset and "approver" not in response_obj[affil_id]: + response_obj[affil_id]["approver"] = [] for name in approvers_queryset: - response_obj[affil["affiliation_id"]]["approver"].append(name) - - return JsonResponse(list(response_obj.values()), status=200, safe=False) + response_obj[affil_id]["approver"].append(name) + return JsonResponse(list(response_obj.values()), + status=200, + safe=False, + json_dumps_params={ + "ensure_ascii": False + } + ) From 7e4d0751a7f600663d4db5d5b5313b1535d178b5 Mon Sep 17 00:00:00 2001 From: sanchegm Date: Mon, 9 Dec 2024 13:45:22 -0800 Subject: [PATCH 2/5] linting and update tests --- src/affiliations/tests.py | 6 ++--- src/affiliations/views.py | 54 ++++++++++++++++++++------------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/affiliations/tests.py b/src/affiliations/tests.py index 3ac91c1..4e51d20 100644 --- a/src/affiliations/tests.py +++ b/src/affiliations/tests.py @@ -184,18 +184,16 @@ def test_should_be_able_to_view_single_affiliation_detail(self): def test_detail_affiliation_json_call(self): """Make sure the API response of a single affiliation is returned in the original JSON format .""" - _ = User.objects.create_user(username="test_user", password="secret") - self.client.login(username="test_user", password="secret") response = self.client.get("/api/affiliation_detail/?affil_id=10000") self.assertEqual( response.json(), [ { - "affiliation_id": 10000, + "affiliation_id": "10000", "affiliation_fullname": "Test Success Result Affil", "subgroups": { "gene curation expert panel": { - "id": 40000, + "id": "40000", "fullname": "Test Success Result Affil", } }, diff --git a/src/affiliations/views.py b/src/affiliations/views.py index 868e458..50f7b27 100644 --- a/src/affiliations/views.py +++ b/src/affiliations/views.py @@ -63,17 +63,19 @@ def affiliations_list_json_format(request): # pylint: disable=unused-argument response_obj[affil_id] = old_json_format elif affil_type not in response_obj[affil_id]["subgroups"]: # If VCEP or GCEP in full name, add other subgroup to end of name. - if (("VCEP" in response_obj[affil_id]["affiliation_fullname"]) or - ("GCEP" in response_obj[affil_id]["affiliation_fullname"])): + if ("VCEP" in response_obj[affil_id]["affiliation_fullname"]) or ( + "GCEP" in response_obj[affil_id]["affiliation_fullname"] + ): response_obj[affil_id]["affiliation_fullname"] = ( - response_obj[affil_id]["affiliation_fullname"] - + "/" - + affil["type"] + response_obj[affil_id]["affiliation_fullname"] + "/" + affil["type"] ) # Else append affiliation subgroup name to full name else: - response_obj[affil_id]["affiliation_fullname"] = response_obj[affil_id]["affiliation_fullname"] + \ - "/" + affil["full_name"] + response_obj[affil_id]["affiliation_fullname"] = ( + response_obj[affil_id]["affiliation_fullname"] + + "/" + + affil["full_name"] + ) response_obj[affil_id]["subgroups"][affil_type] = { "id": ep_id, @@ -88,12 +90,11 @@ def affiliations_list_json_format(request): # pylint: disable=unused-argument for name in approvers_queryset: response_obj[affil_id]["approver"].append(name) - return JsonResponse(list(response_obj.values()), - status=200, - safe=False, - json_dumps_params={ - "ensure_ascii": False - } + return JsonResponse( + list(response_obj.values()), + status=200, + safe=False, + json_dumps_params={"ensure_ascii": False}, ) @@ -136,17 +137,19 @@ def affiliation_detail_json_format(request): response_obj[affil_id] = old_json_format elif affil_type not in response_obj[affil_id]["subgroups"]: # If VCEP or GCEP in full name, add other subgroup to end of name. - if (("VCEP" in response_obj[affil_id]["affiliation_fullname"]) or - ("GCEP" in response_obj[affil_id]["affiliation_fullname"])): + if ("VCEP" in response_obj[affil_id]["affiliation_fullname"]) or ( + "GCEP" in response_obj[affil_id]["affiliation_fullname"] + ): response_obj[affil_id]["affiliation_fullname"] = ( - response_obj[affil_id]["affiliation_fullname"] - + "/" - + affil["type"] + response_obj[affil_id]["affiliation_fullname"] + "/" + affil["type"] ) # Else append affiliation subgroup name to full name else: - response_obj[affil_id]["affiliation_fullname"] = response_obj[affil_id]["affiliation_fullname"] + \ - "/" + affil["full_name"] + response_obj[affil_id]["affiliation_fullname"] = ( + response_obj[affil_id]["affiliation_fullname"] + + "/" + + affil["full_name"] + ) response_obj[affil_id]["subgroups"][affil_type] = { "id": ep_id, @@ -160,10 +163,9 @@ def affiliation_detail_json_format(request): response_obj[affil_id]["approver"] = [] for name in approvers_queryset: response_obj[affil_id]["approver"].append(name) - return JsonResponse(list(response_obj.values()), - status=200, - safe=False, - json_dumps_params={ - "ensure_ascii": False - } + return JsonResponse( + list(response_obj.values()), + status=200, + safe=False, + json_dumps_params={"ensure_ascii": False}, ) From d7339407a98b40b50225f6030b669bec00cd55fb Mon Sep 17 00:00:00 2001 From: sanchegm Date: Mon, 9 Dec 2024 13:59:22 -0800 Subject: [PATCH 3/5] Update tests.py --- src/affiliations/tests.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/affiliations/tests.py b/src/affiliations/tests.py index 4e51d20..7037f1b 100644 --- a/src/affiliations/tests.py +++ b/src/affiliations/tests.py @@ -30,7 +30,7 @@ def setUpTestData(cls): "full_name": "Test Success Result Affil", "short_name": "Successful", "status": "Inactive", - "type": "Gene Curation Expert Panel", + "type": "GCEP", "clinical_domain_working_group": "Neurodevelopmental Disorders", "members": "Bulbasaur, Charmander, Squirtle", "is_deleted": False, @@ -184,6 +184,8 @@ def test_should_be_able_to_view_single_affiliation_detail(self): def test_detail_affiliation_json_call(self): """Make sure the API response of a single affiliation is returned in the original JSON format .""" + _ = User.objects.create_user(username="test_user", password="secret") + self.client.login(username="test_user", password="secret") response = self.client.get("/api/affiliation_detail/?affil_id=10000") self.assertEqual( response.json(), @@ -192,14 +194,12 @@ def test_detail_affiliation_json_call(self): "affiliation_id": "10000", "affiliation_fullname": "Test Success Result Affil", "subgroups": { - "gene curation expert panel": { + "gcep": { "id": "40000", "fullname": "Test Success Result Affil", } }, - "approver": [ - "Mew", - ], + "approver": ["Mew"], } ], ) From 121ce4e18bb3f05ea03dfc59330a5b5db273b157 Mon Sep 17 00:00:00 2001 From: sanchegm Date: Wed, 11 Dec 2024 10:45:58 -0800 Subject: [PATCH 4/5] Add JSON Migration Script - Add JSON file approver migration script to migrate approver values into the DB from the existing JSON file. - Updated docs and links to reflect the change. - Rename load.py to csv_load.py --- doc/tutorial.md | 6 +-- src/scripts/{load.py => csv_load.py} | 4 +- src/scripts/json_load.py | 62 ++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 5 deletions(-) rename src/scripts/{load.py => csv_load.py} (95%) create mode 100644 src/scripts/json_load.py diff --git a/doc/tutorial.md b/doc/tutorial.md index caf77ec..9e831d6 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -20,11 +20,11 @@ oriented towards learning how rather than learning what. - Run the development server: `cd src`, then `python manage.py runserver`. - Install [yamlfmt](https://github.com/google/yamlfmt): `brew install yamlfmt`. -## Running the load.py script to import CSV data into the database +## Running the load.py script to import data into the database - Make sure all dependencies are synced: `pipenv sync --dev`. -- Save CSV into `scripts` folder in directory. -- Run `python manage.py runscript load`. +- Save file into `scripts` folder in directory. +- Run `python manage.py runscript {script_name}`. ## Running database backup diff --git a/src/scripts/load.py b/src/scripts/csv_load.py similarity index 95% rename from src/scripts/load.py rename to src/scripts/csv_load.py index 223beba..eb6862c 100644 --- a/src/scripts/load.py +++ b/src/scripts/csv_load.py @@ -5,10 +5,10 @@ CSV needs to be saved in the `scripts` folder in directory before running. You can then run this script by running: -`python manage.py runscript load` in the command line from the directory. +`python manage.py runscript csv_load` in the command line from the directory. Follow steps outlined in [tutorial.md]( -doc/tutorial.md/#running-the-loadpy-script-to-import-csv-data-into-the-database). +doc/tutorial.md/#running-the-loadpy-script-to-import-data-into-the-database). """ from pathlib import Path diff --git a/src/scripts/json_load.py b/src/scripts/json_load.py new file mode 100644 index 0000000..1d90c48 --- /dev/null +++ b/src/scripts/json_load.py @@ -0,0 +1,62 @@ +""" +Script to be run to insert existing data from +the affiliations JSON file to the database. + +JSON file needs to be saved in the `scripts` folder in directory before running. + +You can then run this script by running: +`python manage.py runscript json_load` in the command line from the directory. + +Follow steps outlined in [tutorial.md]( +doc/tutorial.md/#running-the-loadpy-script-to-import-data-into-the-database). +""" + +from pathlib import Path +import json +from affiliations.models import Affiliation, Approver + +FILEPATH = Path(__file__).parent / "affiliations.json" +COUNT = 0 + +def run(): + """Iterate through JSON file and update Affiliation with Approver + objects in the DB.""" + with open(FILEPATH, encoding="utf-8") as json_file: + + + data = json.load(json_file) + for item in data: + if "approver" in item: + affiliation_id = item["affiliation_id"] + approver = item["approver"] + if "subgroups" in item: + if "vcep" in item["subgroups"]: + vcep_id = item["subgroups"]["vcep"]["id"] + affil_obj = Affiliation.objects.get(affiliation_id=affiliation_id, expert_panel_id=vcep_id) + create_approver_model(approver, affil_obj) + + if "gcep" in item["subgroups"]: + gcep_id = item["subgroups"]["gcep"]["id"] + affil_obj = Affiliation.objects.get( + affiliation_id=affiliation_id, expert_panel_id=gcep_id) + create_approver_model(approver, affil_obj) + else: + affil_obj = Affiliation.objects.get( + affiliation_id=affiliation_id) + create_approver_model(approver, affil_obj) + print(COUNT, "changed") + +def create_approver_model(approver, affil_obj): + """Check if approver exists, if not create approver foreign key model.""" + for approver_name in approver: + if not (Approver.objects.filter( + affiliation=affil_obj, + approver_name=approver_name, + ).exists() + ): + COUNT += 1 + Approver.objects.create( + affiliation=affil_obj, + approver_name=approver_name, + ) + \ No newline at end of file From e9d65c2fdc8923a7cdcc3ec2ecccdf8e7600434a Mon Sep 17 00:00:00 2001 From: sanchegm Date: Wed, 11 Dec 2024 10:56:28 -0800 Subject: [PATCH 5/5] Add JSON compare file - Formatting - Add script to compare JSON file and API response saved to JSON file. --- Pipfile | 1 + Pipfile.lock | 305 ++++++++++++++++++++-------------- src/scripts/affils_compare.py | 46 +++++ src/scripts/json_load.py | 35 ++-- 4 files changed, 243 insertions(+), 144 deletions(-) create mode 100644 src/scripts/affils_compare.py diff --git a/Pipfile b/Pipfile index ee72359..8c7168c 100644 --- a/Pipfile +++ b/Pipfile @@ -22,6 +22,7 @@ watchtower = "3.*" boto3 = "1.*" boto3-stubs = "1.*" django-cors-headers = "4.*" +deepdiff = "8.*" [dev-packages] black = "==24.*" diff --git a/Pipfile.lock b/Pipfile.lock index ca653b6..f330bdd 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "f2b8e0a56eb1714fd4033d2b263b60912808c04220adca8e596bec802f36cb12" + "sha256": "f1e556ac669fff03b40f7d197c56abfc5ae22b49381d0aab454cd38e66f2804f" }, "pipfile-spec": 6, "requires": { @@ -65,37 +65,37 @@ }, "boto3": { "hashes": [ - "sha256:acbca38322b66516450f959c7874826267d431becdc2b080e331e56c2ebbe507", - "sha256:f6c266b4124b92b1603727bf1ed1917e0b74a899bd0e326f151d80c3eaed27a1" + "sha256:5ef7166fe5060637b92af8dc152cd7acecf96b3fc9c5456706a886cadb534391", + "sha256:fc8001519c8842e766ad3793bde3fbd0bb39e821a582fc12cf67876b8f3cf7f1" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.35.65" + "version": "==1.35.78" }, "boto3-stubs": { "hashes": [ - "sha256:1a87bc770abc7f24cb2a86cbe3c38f1a51f74c2d420cf50f451edf24a2424783", - "sha256:8c2407b715a7561a844cbb2295430427c9ba44b84e556c865045b0cf5259137d" + "sha256:5d023cf1fcc723dfdba29653e0ad9b9933985c813a25bc21807e21eab81e21b4", + "sha256:bb7824c09cbf868940b8d500e7f4ca99cb7e074c0f86771f832ce15c33a313bd" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.35.65" + "version": "==1.35.78" }, "botocore": { "hashes": [ - "sha256:46652f732f2b2fb395fffcc33cacb288d05ea283047c9a996fb59d6849464919", - "sha256:8fcaa82ab2338f412e1494449c4c57f9ca785623fb0303f6be5b279c4d27522c" + "sha256:41c37bd7c0326f25122f33ec84fb80fc0a14d7fcc9961431b0e57568e88c9cb5", + "sha256:6905036c25449ae8dba5e950e4b908e4b8a6fe6b516bf61e007ecb62fa21f323" ], "markers": "python_version >= '3.8'", - "version": "==1.35.65" + "version": "==1.35.78" }, "botocore-stubs": { "hashes": [ - "sha256:703be3799cb446c70360b57092cd4009926617781e673961357e55fd81009cc6", - "sha256:88b4c3692bad8472f22084eaecb4b8e3f0fab009fb95813af84743375361af0d" + "sha256:617508d023e0bc98901e0189b794c4b3f289c1747c7cc410173ad698c819a716", + "sha256:c977a049481d50a14bf2db0ef15020b76734ff628d4b8e0e77b8d1c65318369e" ], "markers": "python_version >= '3.8'", - "version": "==1.35.65" + "version": "==1.35.76" }, "cffi": { "hashes": [ @@ -180,36 +180,38 @@ }, "cryptography": { "hashes": [ - "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362", - "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4", - "sha256:1ec0bcf7e17c0c5669d881b1cd38c4972fade441b27bda1051665faaa89bdcaa", - "sha256:281c945d0e28c92ca5e5930664c1cefd85efe80e5c0d2bc58dd63383fda29f83", - "sha256:2ce6fae5bdad59577b44e4dfed356944fbf1d925269114c28be377692643b4ff", - "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805", - "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6", - "sha256:4a02ded6cd4f0a5562a8887df8b3bd14e822a90f97ac5e544c162899bc467664", - "sha256:53a583b6637ab4c4e3591a15bc9db855b8d9dee9a669b550f311480acab6eb08", - "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e", - "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18", - "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f", - "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73", - "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5", - "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984", - "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd", - "sha256:a2a431ee15799d6db9fe80c82b055bae5a752bef645bba795e8e52687c69efe3", - "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e", - "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405", - "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2", - "sha256:d03b5621a135bffecad2c73e9f4deb1a0f977b9a8ffe6f8e002bf6c9d07b918c", - "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995", - "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73", - "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16", - "sha256:f18c716be16bc1fea8e95def49edf46b82fccaa88587a45f8dc0ff6ab5d8e0a7", - "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd", - "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7" - ], - "markers": "python_version >= '3.7'", - "version": "==43.0.3" + "sha256:1923cb251c04be85eec9fda837661c67c1049063305d6be5721643c22dd4e2b7", + "sha256:37d76e6863da3774cd9db5b409a9ecfd2c71c981c38788d3fcfaf177f447b731", + "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b", + "sha256:404fdc66ee5f83a1388be54300ae978b2efd538018de18556dde92575e05defc", + "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543", + "sha256:60eb32934076fa07e4316b7b2742fa52cbb190b42c2df2863dbc4230a0a9b385", + "sha256:62901fb618f74d7d81bf408c8719e9ec14d863086efe4185afd07c352aee1d2c", + "sha256:660cb7312a08bc38be15b696462fa7cc7cd85c3ed9c576e81f4dc4d8b2b31591", + "sha256:708ee5f1bafe76d041b53a4f95eb28cdeb8d18da17e597d46d7833ee59b97ede", + "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb", + "sha256:831c3c4d0774e488fdc83a1923b49b9957d33287de923d58ebd3cec47a0ae43f", + "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123", + "sha256:8b3e6eae66cf54701ee7d9c83c30ac0a1e3fa17be486033000f2a73a12ab507c", + "sha256:9abcc2e083cbe8dde89124a47e5e53ec38751f0d7dfd36801008f316a127d7ba", + "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c", + "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285", + "sha256:abc998e0c0eee3c8a1904221d3f67dcfa76422b23620173e28c11d3e626c21bd", + "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092", + "sha256:be4ce505894d15d5c5037167ffb7f0ae90b7be6f2a98f9a5c3442395501c32fa", + "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289", + "sha256:cd4e834f340b4293430701e772ec543b0fbe6c2dea510a5286fe0acabe153a02", + "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64", + "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053", + "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417", + "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e", + "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e", + "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7", + "sha256:f5e7cb1e5e56ca0933b4873c0220a78b773b24d40d186b6738080b73d3d0a756", + "sha256:f677e1268c4e23420c3acade68fac427fffcb8d19d7df95ed7ad17cdef8404f4" + ], + "markers": "python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1'", + "version": "==44.0.0" }, "daphne": { "hashes": [ @@ -220,6 +222,15 @@ "markers": "python_version >= '3.8'", "version": "==4.1.2" }, + "deepdiff": { + "hashes": [ + "sha256:245599a4586ab59bb599ca3517a9c42f3318ff600ded5e80a3432693c8ec3c4b", + "sha256:42e99004ce603f9a53934c634a57b04ad5900e0d8ed0abb15e635767489cbc05" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==8.0.1" + }, "dill": { "hashes": [ "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a", @@ -231,11 +242,11 @@ }, "django": { "hashes": [ - "sha256:8b38a9a12da3ae00cb0ba72da985ec4b14de6345046b1e174b1fd7254398f818", - "sha256:c0fa0e619c39325a169208caef234f90baa925227032ad3f44842ba14d75234a" + "sha256:236e023f021f5ce7dee5779de7b286565fdea5f4ab86bae5338e3f7b69896cf0", + "sha256:de450c09e91879fa5a307f696e57c851955c910a438a35e6b4c895e86bedc82a" ], "markers": "python_version >= '3.10'", - "version": "==5.1.3" + "version": "==5.1.4" }, "django-admin-logs": { "hashes": [ @@ -293,12 +304,12 @@ }, "django-unfold": { "hashes": [ - "sha256:34f005bd5d95c25a62129d61099ebac8974e1e3b1910e6344eb869259f01b94c", - "sha256:e8b37484efa86a86be10c9c843252ceb01e5d5609114cfc2ed2d39bd9dfbb575" + "sha256:c1f2e4314b87db2f4d845f6507667404df8e33ea0e8f336d604c5863c9358031", + "sha256:e60252393fadd76b6a2c90437512bac92c9a1c5d162d8df3bc13ebd401226181" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==0.41.0" + "version": "==0.43.0" }, "djangorestframework": { "hashes": [ @@ -339,6 +350,14 @@ "markers": "python_version >= '3.7'", "version": "==1.0.1" }, + "orderly-set": { + "hashes": [ + "sha256:52a18b86aaf3f5d5a498bbdb27bf3253a4e5c57ab38e5b7a56fa00115cd28448", + "sha256:f7a37c95a38c01cdfe41c3ffb62925a318a2286ea0a41790c057fc802aec54da" + ], + "markers": "python_version >= '3.8'", + "version": "==5.2.2" + }, "psycopg": { "hashes": [ "sha256:644d3973fe26908c73d4be746074f6e5224b03c1101d302d9a53bf565ad64907", @@ -374,10 +393,10 @@ }, "pyopenssl": { "hashes": [ - "sha256:4247f0dbe3748d560dcbb2ff3ea01af0f9a1a001ef5f7c4c647956ed8cbf0e95", - "sha256:967d5719b12b243588573f39b0c677637145c7a1ffedcd495a487e58177fbb8d" + "sha256:49f7a019577d834746bc55c5fce6ecbcec0f2b4ec5ce1cf43a9a173b8138bb36", + "sha256:e474f5a473cd7f92221cc04976e48f4d11502804657a08a989fb3be5514c904a" ], - "version": "==24.2.1" + "version": "==24.3.0" }, "python-dateutil": { "hashes": [ @@ -405,11 +424,11 @@ }, "s3transfer": { "hashes": [ - "sha256:263ed587a5803c6c708d3ce44dc4dfedaab4c1a32e8329bab818933d79ddcf5d", - "sha256:4f50ed74ab84d474ce614475e0b8d5047ff080810aac5d01ea25231cfc944b0c" + "sha256:244a76a24355363a68164241438de1b72f8781664920260c48465896b712a41e", + "sha256:29edc09801743c21eb5ecbc617a152df41d3c287f67b615f73e5f750583666a7" ], "markers": "python_version >= '3.8'", - "version": "==0.10.3" + "version": "==0.10.4" }, "service-identity": { "hashes": [ @@ -420,47 +439,77 @@ }, "setuptools": { "hashes": [ - "sha256:5c4ccb41111392671f02bb5f8436dfc5a9a7185e80500531b133f5775c4163ef", - "sha256:87cb777c3b96d638ca02031192d40390e0ad97737e27b6b4fa831bea86f2f829" + "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6", + "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d" ], "markers": "python_version >= '3.9'", - "version": "==75.5.0" + "version": "==75.6.0" }, "six": { "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", + "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", - "version": "==1.16.0" + "version": "==1.17.0" }, "sqlparse": { "hashes": [ - "sha256:9e37b35e16d1cc652a2545f0997c1deb23ea28fa1f3eefe609eee3063c3b105f", - "sha256:e99bc85c78160918c3e1d9230834ab8d80fc06c59d03f8db2618f65f65dda55e" + "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272", + "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca" ], "markers": "python_version >= '3.8'", - "version": "==0.5.2" + "version": "==0.5.3" }, "tomli": { "hashes": [ - "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8", - "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391" + "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", + "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", + "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", + "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", + "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", + "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", + "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", + "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", + "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", + "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", + "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", + "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", + "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", + "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", + "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", + "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", + "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", + "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", + "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", + "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", + "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", + "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", + "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", + "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", + "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", + "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", + "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", + "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", + "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", + "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", + "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", + "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==2.1.0" + "version": "==2.2.1" }, "twisted": { "extras": [ "tls" ], "hashes": [ - "sha256:02951299672595fea0f70fa2d5f7b5e3d56836157eda68859a6ad6492d36756e", - "sha256:67aa7c8aa94387385302acf44ade12967c747858c8bcce0f11d38077a11c5326" + "sha256:695d0556d5ec579dcc464d2856b634880ed1319f45b10d19043f2b57eb0115b5", + "sha256:fe403076c71f04d5d2d789a755b687c5637ec3bcd3b2b8252d76f2ba65f54261" ], "markers": "python_full_version >= '3.8.0'", - "version": "==24.10.0" + "version": "==24.11.0" }, "txaio": { "hashes": [ @@ -472,19 +521,19 @@ }, "types-awscrt": { "hashes": [ - "sha256:3fd1edeac923d1956c0e907c973fb83bda465beae7f054716b371b293f9b5fdc", - "sha256:517d9d06f19cf58d778ca90ad01e52e0489466bf70dcf78c7f47f74fdf151a60" + "sha256:043c0ae0fe5d272618294cbeaf1c349a654a9f7c00121be64d27486933ac4a26", + "sha256:cc0057885cb7ce1e66856123a4c2861b051e9f0716b1767ad72bfe4ca26bbcd4" ], "markers": "python_version >= '3.8'", - "version": "==0.23.0" + "version": "==0.23.3" }, "types-s3transfer": { "hashes": [ - "sha256:d34c5a82f531af95bb550927136ff5b737a1ed3087f90a59d545591dfde5b4cc", - "sha256:f761b2876ac4c208e6c6b75cdf5f6939009768be9950c545b11b0225e7703ee7" + "sha256:03123477e3064c81efe712bf9d372c7c72f2790711431f9baa59cf96ea607267", + "sha256:22ac1aabc98f9d7f2928eb3fb4d5c02bf7435687f0913345a97dd3b84d0c217d" ], "markers": "python_version >= '3.8'", - "version": "==0.10.3" + "version": "==0.10.4" }, "typing-extensions": { "hashes": [ @@ -522,46 +571,46 @@ }, "zope-interface": { "hashes": [ - "sha256:0de23bcb93401994ea00bc5c677ef06d420340ac0a4e9c10d80e047b9ce5af3f", - "sha256:179ad46ece518c9084cb272e4a69d266b659f7f8f48e51706746c2d8a426433e", - "sha256:190eeec67e023d5aac54d183fa145db0b898664234234ac54643a441da434616", - "sha256:1a2ed0852c25950cf430067f058f8d98df6288502ac313861d9803fe7691a9b3", - "sha256:1c4e1b4c06d9abd1037c088dae1566c85f344a3e6ae4350744c3f7f7259d9c67", - "sha256:1d0e23c6b746eb8ce04573cc47bcac60961ac138885d207bd6f57e27a1431ae8", - "sha256:2317e1d4dba68203a5227ea3057f9078ec9376275f9700086b8f0ffc0b358e1b", - "sha256:2d553e02b68c0ea5a226855f02edbc9eefd99f6a8886fa9f9bdf999d77f46585", - "sha256:3603ef82a9920bd0bfb505423cb7e937498ad971ad5a6141841e8f76d2fd5446", - "sha256:3defc925c4b22ac1272d544a49c6ba04c3eefcce3200319ee1be03d9270306dd", - "sha256:3e59f175e868f856a77c0a77ba001385c377df2104fdbda6b9f99456a01e102a", - "sha256:4284d664ef0ff7b709836d4de7b13d80873dc5faeffc073abdb280058bfac5e3", - "sha256:55c373becbd36a44d0c9be1d5271422fdaa8562d158fb44b4192297b3c67096c", - "sha256:5836b8fb044c6e75ba34dfaabc602493019eadfa0faf6ff25f4c4c356a71a853", - "sha256:5cdb7e7e5524b76d3ec037c1d81a9e2c7457b240fd4cb0a2476b65c3a5a6c81f", - "sha256:6650bd56ef350d37c8baccfd3ee8a0483ed6f8666e641e4b9ae1a1827b79f9e5", - "sha256:7395f13533318f150ee72adb55b29284b16e73b6d5f02ab21f173b3e83f242b8", - "sha256:7720322763aceb5e0a7cadcc38c67b839efe599f0887cbf6c003c55b1458c501", - "sha256:7cd5e3d910ac87652a09f6e5db8e41bc3b49cf08ddd2d73d30afc644801492cd", - "sha256:81744a7e61b598ebcf4722ac56a7a4f50502432b5b4dc7eb29075a89cf82d029", - "sha256:84e87eba6b77a3af187bae82d8de1a7c208c2a04ec9f6bd444fd091b811ad92e", - "sha256:8d0fe45be57b5219aa4b96e846631c04615d5ef068146de5a02ccd15c185321f", - "sha256:9595e478047ce752b35cfa221d7601a5283ccdaab40422e0dc1d4a334c70f580", - "sha256:99c14f0727c978639139e6cad7a60e82b7720922678d75aacb90cf4ef74a068c", - "sha256:9b1eed7670d564f1025d7cda89f99f216c30210e42e95de466135be0b4a499d9", - "sha256:9fad9bd5502221ab179f13ea251cb30eef7cf65023156967f86673aff54b53a0", - "sha256:ad339509dcfbbc99bf8e147db6686249c4032f26586699ec4c82f6e5909c9fe2", - "sha256:bcbeb44fc16e0078b3b68a95e43f821ae34dcbf976dde6985141838a5f23dd3d", - "sha256:c8e7b05dc6315a193cceaec071cc3cf1c180cea28808ccded0b1283f1c38ba73", - "sha256:ca95594d936ee349620900be5b46c0122a1ff6ce42d7d5cb2cf09dc84071ef16", - "sha256:d029fac6a80edae80f79c37e5e3abfa92968fe921886139b3ee470a1b177321a", - "sha256:d17e7fc814eaab93409b80819fd6d30342844345c27f3bc3c4b43c2425a8d267", - "sha256:d6821ef9870f32154da873fcde439274f99814ea452dd16b99fa0b66345c4b6b", - "sha256:e6503534b52bb1720ace9366ee30838a58a3413d3e197512f3338c8f34b5d89d", - "sha256:ed1df8cc01dd1e3970666a7370b8bfc7457371c58ba88c57bd5bca17ab198053", - "sha256:f1d52d052355e0c5c89e0630dd2ff7c0b823fd5f56286a663e92444761b35e25", - "sha256:f85b290e5b8b11814efb0d004d8ce6c9a483c35c462e8d9bf84abb93e79fa770" - ], - "markers": "python_version >= '3.8'", - "version": "==7.1.1" + "sha256:033b3923b63474800b04cba480b70f6e6243a62208071fc148354f3f89cc01b7", + "sha256:05b910a5afe03256b58ab2ba6288960a2892dfeef01336dc4be6f1b9ed02ab0a", + "sha256:086ee2f51eaef1e4a52bd7d3111a0404081dadae87f84c0ad4ce2649d4f708b7", + "sha256:0ef9e2f865721553c6f22a9ff97da0f0216c074bd02b25cf0d3af60ea4d6931d", + "sha256:1090c60116b3da3bfdd0c03406e2f14a1ff53e5771aebe33fec1edc0a350175d", + "sha256:144964649eba4c5e4410bb0ee290d338e78f179cdbfd15813de1a664e7649b3b", + "sha256:15398c000c094b8855d7d74f4fdc9e73aa02d4d0d5c775acdef98cdb1119768d", + "sha256:1909f52a00c8c3dcab6c4fad5d13de2285a4b3c7be063b239b8dc15ddfb73bd2", + "sha256:21328fcc9d5b80768bf051faa35ab98fb979080c18e6f84ab3f27ce703bce465", + "sha256:224b7b0314f919e751f2bca17d15aad00ddbb1eadf1cb0190fa8175edb7ede62", + "sha256:25e6a61dcb184453bb00eafa733169ab6d903e46f5c2ace4ad275386f9ab327a", + "sha256:27f926f0dcb058211a3bb3e0e501c69759613b17a553788b2caeb991bed3b61d", + "sha256:29caad142a2355ce7cfea48725aa8bcf0067e2b5cc63fcf5cd9f97ad12d6afb5", + "sha256:2ad9913fd858274db8dd867012ebe544ef18d218f6f7d1e3c3e6d98000f14b75", + "sha256:31d06db13a30303c08d61d5fb32154be51dfcbdb8438d2374ae27b4e069aac40", + "sha256:3e0350b51e88658d5ad126c6a57502b19d5f559f6cb0a628e3dc90442b53dd98", + "sha256:3f6771d1647b1fc543d37640b45c06b34832a943c80d1db214a37c31161a93f1", + "sha256:4893395d5dd2ba655c38ceb13014fd65667740f09fa5bb01caa1e6284e48c0cd", + "sha256:52e446f9955195440e787596dccd1411f543743c359eeb26e9b2c02b077b0519", + "sha256:550f1c6588ecc368c9ce13c44a49b8d6b6f3ca7588873c679bd8fd88a1b557b6", + "sha256:72cd1790b48c16db85d51fbbd12d20949d7339ad84fd971427cf00d990c1f137", + "sha256:7bd449c306ba006c65799ea7912adbbfed071089461a19091a228998b82b1fdb", + "sha256:7dc5016e0133c1a1ec212fc87a4f7e7e562054549a99c73c8896fa3a9e80cbc7", + "sha256:802176a9f99bd8cc276dcd3b8512808716492f6f557c11196d42e26c01a69a4c", + "sha256:80ecf2451596f19fd607bb09953f426588fc1e79e93f5968ecf3367550396b22", + "sha256:8b49f1a3d1ee4cdaf5b32d2e738362c7f5e40ac8b46dd7d1a65e82a4872728fe", + "sha256:8e7da17f53e25d1a3bde5da4601e026adc9e8071f9f6f936d0fe3fe84ace6d54", + "sha256:a102424e28c6b47c67923a1f337ede4a4c2bba3965b01cf707978a801fc7442c", + "sha256:a19a6cc9c6ce4b1e7e3d319a473cf0ee989cbbe2b39201d7c19e214d2dfb80c7", + "sha256:a71a5b541078d0ebe373a81a3b7e71432c61d12e660f1d67896ca62d9628045b", + "sha256:baf95683cde5bc7d0e12d8e7588a3eb754d7c4fa714548adcd96bdf90169f021", + "sha256:cab15ff4832580aa440dc9790b8a6128abd0b88b7ee4dd56abacbc52f212209d", + "sha256:ce290e62229964715f1011c3dbeab7a4a1e4971fd6f31324c4519464473ef9f2", + "sha256:d3a8ffec2a50d8ec470143ea3d15c0c52d73df882eef92de7537e8ce13475e8a", + "sha256:e204937f67b28d2dca73ca936d3039a144a081fc47a07598d44854ea2a106239", + "sha256:eb23f58a446a7f09db85eda09521a498e109f137b85fb278edb2e34841055398", + "sha256:f6dd02ec01f4468da0f234da9d9c8545c5412fef80bc590cc51d8dd084138a89" + ], + "markers": "python_version >= '3.8'", + "version": "==7.2" } }, "develop": { @@ -575,11 +624,11 @@ }, "astroid": { "hashes": [ - "sha256:5cfc40ae9f68311075d27ef68a4841bdc5cc7f6cf86671b49f00607d30188e2d", - "sha256:a9d1c946ada25098d790e079ba2a1b112157278f3fb7e718ae6a9252f5835dc8" + "sha256:6aaea045f938c735ead292204afdb977a36e989522b7833ef6fea94de743f442", + "sha256:db676dc4f3ae6bfe31cda227dc60e03438378d7a896aec57422c95634e8d722f" ], "markers": "python_full_version >= '3.9.0'", - "version": "==3.3.5" + "version": "==3.3.6" }, "black": { "hashes": [ @@ -748,11 +797,11 @@ }, "django": { "hashes": [ - "sha256:8b38a9a12da3ae00cb0ba72da985ec4b14de6345046b1e174b1fd7254398f818", - "sha256:c0fa0e619c39325a169208caef234f90baa925227032ad3f44842ba14d75234a" + "sha256:236e023f021f5ce7dee5779de7b286565fdea5f4ab86bae5338e3f7b69896cf0", + "sha256:de450c09e91879fa5a307f696e57c851955c910a438a35e6b4c895e86bedc82a" ], "markers": "python_version >= '3.10'", - "version": "==5.1.3" + "version": "==5.1.4" }, "django-stubs": { "extras": [ @@ -909,12 +958,12 @@ }, "pylint": { "hashes": [ - "sha256:2f846a466dd023513240bc140ad2dd73bfc080a5d85a710afdb728c420a5a2b9", - "sha256:9f3dcc87b1203e612b78d91a896407787e708b3f189b5fa0b307712d49ff0c6e" + "sha256:77f068c287d49b8683cd7c6e624243c74f92890f767f106ffa1ddf3c0a54cb7a", + "sha256:9ec054ec992cd05ad30a6df1676229739a73f8feeabf3912c995d17601052b01" ], "index": "pypi", "markers": "python_full_version >= '3.9.0'", - "version": "==3.3.1" + "version": "==3.3.2" }, "requests": { "hashes": [ @@ -926,11 +975,11 @@ }, "sqlparse": { "hashes": [ - "sha256:9e37b35e16d1cc652a2545f0997c1deb23ea28fa1f3eefe609eee3063c3b105f", - "sha256:e99bc85c78160918c3e1d9230834ab8d80fc06c59d03f8db2618f65f65dda55e" + "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272", + "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca" ], "markers": "python_version >= '3.8'", - "version": "==0.5.2" + "version": "==0.5.3" }, "tomlkit": { "hashes": [ diff --git a/src/scripts/affils_compare.py b/src/scripts/affils_compare.py new file mode 100644 index 0000000..b7226fc --- /dev/null +++ b/src/scripts/affils_compare.py @@ -0,0 +1,46 @@ +""" +Script to be run to compare JSON file and a JSON file of API response. + +Both files need to be saved in the `scripts` folder in directory before running. + +You can then run this script by running: +`python manage.py runscript affils_compare` in the command line from the directory. + +Follow steps outlined in [tutorial.md]( +doc/tutorial.md/#running-the-loadpy-script-to-import-data-into-the-database). +""" + +from pathlib import Path +import os +import json +from deepdiff import DeepDiff # type: ignore + +CURRENT_DIR = os.path.dirname(__file__) +FILENAME = os.path.join(CURRENT_DIR, "affils_diff_output.txt") + +AFFIL_JSON_PATH = Path(__file__).parent / "affiliations.json" +AFFIL_RESPONSE_PATH = Path(__file__).parent / "affils_response.json" + + +def run(): + """Compare JSON file to API response and return a txt file of any differences.""" + with open(AFFIL_JSON_PATH, encoding="utf-8") as f, open( + AFFIL_RESPONSE_PATH, encoding="utf-8" + ) as f2: + affils_json = json.load(f) + affils_response = json.load(f2) + affils_json_dict = {} + affils_response_dict = {} + + # Build dict for each file. + for affil in affils_json: + affil_id = affil["affiliation_id"] + affils_json_dict[affil_id] = affil + for affil in affils_response: + affil_id = affil["affiliation_id"] + affils_response_dict[affil_id] = affil + + # Compare both files + diff = DeepDiff(affils_json_dict, affils_response_dict) + with open(FILENAME, "w", encoding="utf-8") as f: + print(diff, file=f) diff --git a/src/scripts/json_load.py b/src/scripts/json_load.py index 1d90c48..4427e90 100644 --- a/src/scripts/json_load.py +++ b/src/scripts/json_load.py @@ -16,13 +16,13 @@ from affiliations.models import Affiliation, Approver FILEPATH = Path(__file__).parent / "affiliations.json" -COUNT = 0 + def run(): """Iterate through JSON file and update Affiliation with Approver objects in the DB.""" + count = 0 with open(FILEPATH, encoding="utf-8") as json_file: - data = json.load(json_file) for item in data: @@ -32,31 +32,34 @@ def run(): if "subgroups" in item: if "vcep" in item["subgroups"]: vcep_id = item["subgroups"]["vcep"]["id"] - affil_obj = Affiliation.objects.get(affiliation_id=affiliation_id, expert_panel_id=vcep_id) - create_approver_model(approver, affil_obj) + affil_obj = Affiliation.objects.get( + affiliation_id=affiliation_id, expert_panel_id=vcep_id + ) + create_approver_model(approver, affil_obj, count) if "gcep" in item["subgroups"]: gcep_id = item["subgroups"]["gcep"]["id"] affil_obj = Affiliation.objects.get( - affiliation_id=affiliation_id, expert_panel_id=gcep_id) - create_approver_model(approver, affil_obj) + affiliation_id=affiliation_id, expert_panel_id=gcep_id + ) + create_approver_model(approver, affil_obj, count) else: - affil_obj = Affiliation.objects.get( - affiliation_id=affiliation_id) - create_approver_model(approver, affil_obj) - print(COUNT, "changed") + affil_obj = Affiliation.objects.get(affiliation_id=affiliation_id) + create_approver_model(approver, affil_obj, count) + print(count, "changed") + -def create_approver_model(approver, affil_obj): +def create_approver_model(approver, affil_obj, count): """Check if approver exists, if not create approver foreign key model.""" for approver_name in approver: - if not (Approver.objects.filter( - affiliation=affil_obj, - approver_name=approver_name, + if not ( + Approver.objects.filter( + affiliation=affil_obj, + approver_name=approver_name, ).exists() ): - COUNT += 1 + count += 1 Approver.objects.create( affiliation=affil_obj, approver_name=approver_name, ) - \ No newline at end of file