Skip to content

Commit 2dc866c

Browse files
Merge pull request #7335 from specify/issue-7334
[reorganization]:Move inheritance logic to its own app
2 parents 172928f + 42a2584 commit 2dc866c

File tree

16 files changed

+250
-229
lines changed

16 files changed

+250
-229
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from specifyweb.backend.inheritance.utils import get_cat_num_inheritance_setting, get_parent_cat_num_inheritance_setting
2+
from specifyweb.specify.models import Collectionobject, Collectionobjectgroupjoin
3+
4+
def parent_inheritance_post_query_processing(query, tableid, field_specs, collection, user, should_list_query=True): # pragma: no cover
5+
if tableid == 1 and 'catalogNumber' in [fs.fieldspec.join_path[0].name for fs in field_specs if fs.fieldspec.join_path]:
6+
if not get_parent_cat_num_inheritance_setting(collection, user):
7+
return list(query)
8+
9+
# Get the catalogNumber field index
10+
catalog_number_field_index = [fs.fieldspec.join_path[0].name for fs in field_specs].index('catalogNumber') + 1
11+
12+
if field_specs[catalog_number_field_index - 1].op_num != 1:
13+
return list(query)
14+
15+
results = list(query)
16+
updated_results = []
17+
18+
# Map results, replacing null catalog numbers with the parent catalog number
19+
for result in results:
20+
result = list(result)
21+
if result[catalog_number_field_index] is None or result[catalog_number_field_index] == '':
22+
component_id = result[0] # Assuming the first column is the child's ID
23+
component_obj = Collectionobject.objects.filter(id=component_id).first()
24+
if component_obj and component_obj.componentParent:
25+
result[catalog_number_field_index] = component_obj.componentParent.catalognumber
26+
updated_results.append(tuple(result))
27+
28+
return updated_results
29+
30+
return query
31+
32+
def cog_inheritance_post_query_processing(query, tableid, field_specs, collection, user):
33+
if tableid == 1 and 'catalogNumber' in [fs.fieldspec.join_path[0].name for fs in field_specs if fs.fieldspec.join_path]:
34+
if not get_cat_num_inheritance_setting(collection, user):
35+
# query = query.filter(collectionobjectgroupjoin_1.isprimary == 1)
36+
return list(query)
37+
38+
# Get the catalogNumber field index
39+
catalog_number_field_index = [fs.fieldspec.join_path[0].name for fs in field_specs if fs.fieldspec.join_path].index('catalogNumber') + 1
40+
41+
if field_specs[catalog_number_field_index - 1].op_num != 1:
42+
return list(query)
43+
44+
results = list(query)
45+
updated_results = []
46+
47+
# Map results, replacing null catalog numbers with the collection object group primary collection catalog number
48+
for result in results:
49+
result = list(result)
50+
if result[catalog_number_field_index] is None or result[catalog_number_field_index] == '':
51+
cojo = Collectionobjectgroupjoin.objects.filter(childco_id=result[0]).first()
52+
if cojo:
53+
primary_cojo = Collectionobjectgroupjoin.objects.filter(
54+
parentcog=cojo.parentcog, isprimary=True).first()
55+
if primary_cojo:
56+
result[catalog_number_field_index] = primary_cojo.childco.catalognumber
57+
updated_results.append(tuple(result))
58+
59+
return updated_results
60+
61+
return query

specifyweb/specify/tests/test_catalog_number_for_sibling.py renamed to specifyweb/backend/inheritance/tests/test_catalog_number_for_sibling.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def setUp(self):
1616
def test_no_id_error(self):
1717
co = self.collectionobjects[0]
1818
response = self.c.post(
19-
"/api/specify/catalog_number_for_sibling/",
19+
"/inheritance/catalog_number_for_sibling/",
2020
json.dumps(dict(catalognumber=co.catalognumber)),
2121
content_type="application/json"
2222
)
@@ -27,7 +27,7 @@ def test_no_id_error(self):
2727
def test_invalid_body(self):
2828
co = self.collectionobjects[0]
2929
response = self.c.post(
30-
"/api/specify/catalog_number_for_sibling/",
30+
"/inheritance/catalog_number_for_sibling/",
3131
"Not a JSON: Value",
3232
content_type="application/json"
3333
)
@@ -38,7 +38,7 @@ def test_invalid_body(self):
3838
def test_simple_co(self):
3939
co = self.collectionobjects[0]
4040
response = self.c.post(
41-
"/api/specify/catalog_number_for_sibling/",
41+
"/inheritance/catalog_number_for_sibling/",
4242
json.dumps(dict(id=co.id)),
4343
content_type="application/json"
4444
)
@@ -49,7 +49,7 @@ def test_simple_co(self):
4949
def test_simple_co_catalognumber(self):
5050
co = self.collectionobjects[0]
5151
response = self.c.post(
52-
"/api/specify/catalog_number_for_sibling/",
52+
"/inheritance/catalog_number_for_sibling/",
5353
json.dumps(dict(id=co.id)),
5454
content_type="application/json"
5555
)
@@ -65,7 +65,7 @@ def test_self_is_primary(self):
6565

6666
co = self.collectionobjects[0]
6767
response = self.c.post(
68-
"/api/specify/catalog_number_for_sibling/",
68+
"/inheritance/catalog_number_for_sibling/",
6969
json.dumps(dict(id=co.id, catalognumber=None)),
7070
content_type="application/json"
7171
)
@@ -81,7 +81,7 @@ def test_other_is_primary(self):
8181

8282
for co in self.collectionobjects[1:3]:
8383
response = self.c.post(
84-
"/api/specify/catalog_number_for_sibling/",
84+
"/inheritance/catalog_number_for_sibling/",
8585
json.dumps(dict(id=co.id)),
8686
content_type="application/json"
8787
)
@@ -97,7 +97,7 @@ def test_none_is_primary(self):
9797

9898
for co in self.collectionobjects[1:3]:
9999
response = self.c.post(
100-
"/api/specify/catalog_number_for_sibling/",
100+
"/inheritance/catalog_number_for_sibling/",
101101
json.dumps(dict(id=co.id)),
102102
content_type="application/json"
103103
)

specifyweb/specify/tests/test_utils/test_get_cat_num_inheritance_setting.py renamed to specifyweb/backend/inheritance/tests/test_get_cat_num_inheritance_setting.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
from specifyweb.backend.inheritance.utils import get_cat_num_inheritance_setting
12
from specifyweb.specify.tests.test_utils.test_collection_preference_context import TestCollectionPreferenceContext
2-
from specifyweb.specify.utils import get_cat_num_inheritance_setting
33

44
class TestCatNumInheritanceSetting(TestCollectionPreferenceContext):
55

specifyweb/specify/tests/test_utils/test_get_parent_cat_num_inheritance_setting.py renamed to specifyweb/backend/inheritance/tests/test_get_parent_cat_num_inheritance_setting.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
from specifyweb.backend.inheritance.utils import get_parent_cat_num_inheritance_setting
12
from specifyweb.specify.tests.test_utils.test_collection_preference_context import TestCollectionPreferenceContext
2-
from specifyweb.specify.utils import get_parent_cat_num_inheritance_setting
33

44
class TestCatNumInheritanceSetting(TestCollectionPreferenceContext):
55

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
from django.urls import include, path, re_path
3+
4+
from specifyweb.backend.inheritance import views
5+
6+
urlpatterns = [
7+
# cat num for siblings
8+
re_path(r'^catalog_number_for_sibling/$', views.catalog_number_for_sibling),
9+
10+
# cat num for parent
11+
re_path(r'^catalog_number_from_parent/$', views.catalog_number_from_parent),
12+
]
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import json
2+
import logging
3+
4+
from django.conf import settings
5+
6+
logger = logging.getLogger(__name__)
7+
8+
def get_parent_cat_num_inheritance_setting(collection, user) -> bool:
9+
import specifyweb.backend.context.app_resource as app_resource
10+
11+
parent_inheritance_enabled: bool = False
12+
13+
try:
14+
collection_prefs_json, _, __ = app_resource.get_app_resource(collection, user, 'CollectionPreferences')
15+
16+
if collection_prefs_json is not None:
17+
collection_prefs_dict = json.loads(collection_prefs_json)
18+
19+
catalog_number_parent_inheritance = collection_prefs_dict.get('catalogNumberParentInheritance', {})
20+
behavior = catalog_number_parent_inheritance.get('behavior', {}) \
21+
if isinstance(catalog_number_parent_inheritance, dict) else {}
22+
parent_inheritance_enabled = behavior.get('inheritance', False) if isinstance(behavior, dict) else False
23+
24+
if not isinstance(parent_inheritance_enabled, bool):
25+
parent_inheritance_enabled = False
26+
27+
except json.JSONDecodeError:
28+
logger.warning(f"Error: Could not decode JSON for collection preferences")
29+
except TypeError as e:
30+
logger.warning(f"Error: Unexpected data structure in collection preferences: {e}")
31+
except Exception as e:
32+
logger.warning(f"An unexpected error occurred: {e}")
33+
34+
return parent_inheritance_enabled
35+
36+
37+
def get_cat_num_inheritance_setting(collection, user) -> bool:
38+
import specifyweb.backend.context.app_resource as app_resource
39+
40+
inheritance_enabled: bool = False
41+
42+
try:
43+
collection_prefs_json, _, __ = app_resource.get_app_resource(collection, user, 'CollectionPreferences')
44+
45+
if collection_prefs_json is not None:
46+
collection_prefs_dict = json.loads(collection_prefs_json)
47+
48+
catalog_number_inheritance = collection_prefs_dict.get('catalogNumberInheritance', {})
49+
behavior = catalog_number_inheritance.get('behavior', {}) \
50+
if isinstance(catalog_number_inheritance, dict) else {}
51+
inheritance_enabled = behavior.get('inheritance', False) if isinstance(behavior, dict) else False
52+
53+
if not isinstance(inheritance_enabled, bool):
54+
inheritance_enabled = False
55+
56+
except json.JSONDecodeError:
57+
logger.warning(f"Error: Could not decode JSON for collection preferences")
58+
except TypeError as e:
59+
logger.warning(f"Error: Unexpected data structure in collection preferences: {e}")
60+
except Exception as e:
61+
logger.warning(f"An unexpected error occurred: {e}")
62+
63+
return inheritance_enabled
64+
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import json
2+
from django import http
3+
from specifyweb.specify import models
4+
from specifyweb.specify.views import login_maybe_required
5+
from django.views.decorators.http import require_POST
6+
7+
8+
@login_maybe_required
9+
@require_POST
10+
def catalog_number_for_sibling(request: http.HttpRequest):
11+
"""
12+
Returns the catalog number of the primary CO of a COG if one is present
13+
"""
14+
try:
15+
request_data = json.loads(request.body)
16+
object_id = request_data.get('id')
17+
provided_catalog_number = request_data.get('catalognumber')
18+
except json.JSONDecodeError:
19+
return http.JsonResponse({'error': 'Invalid JSON body.'}, status=400)
20+
21+
if object_id is None:
22+
return http.JsonResponse({'error': "'id' field is required."}, status=400)
23+
24+
if provided_catalog_number is not None:
25+
return http.JsonResponse(None, safe=False)
26+
27+
try:
28+
# Find the join record for the requesting object and its parent group ID
29+
requesting_cojo = models.Collectionobjectgroupjoin.objects.filter(
30+
childco_id=object_id
31+
).values('parentcog_id').first()
32+
33+
if not requesting_cojo:
34+
return http.JsonResponse(None, safe=False)
35+
36+
parent_cog_id = requesting_cojo['parentcog_id']
37+
38+
primary_cojo = models.Collectionobjectgroupjoin.objects.filter(
39+
parentcog_id=parent_cog_id,
40+
isprimary=True
41+
).select_related('childco').first()
42+
43+
# Extract the catalog number if a primary sibling CO exists
44+
primary_catalog_number = None
45+
if primary_cojo and primary_cojo.childco:
46+
primary_catalog_number = primary_cojo.childco.catalognumber
47+
48+
return http.JsonResponse(primary_catalog_number, safe=False)
49+
50+
except Exception as e:
51+
print(f"Error processing request: {e}")
52+
return http.JsonResponse({'error': 'An internal server error occurred.'}, status=500)
53+
54+
55+
@login_maybe_required
56+
@require_POST
57+
def catalog_number_from_parent(request: http.HttpRequest):
58+
"""
59+
Returns the catalog number of the parent component
60+
"""
61+
try:
62+
request_data = json.loads(request.body)
63+
object_id = request_data.get('id')
64+
provided_catalog_number = request_data.get('catalognumber')
65+
except json.JSONDecodeError:
66+
return http.JsonResponse({'error': 'Invalid JSON body.'}, status=400)
67+
68+
if object_id is None:
69+
return http.JsonResponse({'error': "'id' field is required."}, status=400)
70+
71+
if provided_catalog_number is not None:
72+
return http.JsonResponse(None, safe=False)
73+
74+
try:
75+
# Get the child CO
76+
child = models.Collectionobject.objects.get(id=object_id)
77+
78+
# Get the parent CO
79+
parent = child.componentParent
80+
81+
if parent and parent.catalognumber:
82+
return http.JsonResponse(parent.catalognumber, safe=False)
83+
else:
84+
return http.JsonResponse({'error': 'Parent or parent catalog number not found.'}, status=404)
85+
86+
except Exception as e:
87+
print(f"Error processing request: {e}")
88+
return http.JsonResponse({'error': 'An internal server error occurred.'}, status=500)
89+

0 commit comments

Comments
 (0)