From e7a71b1e8f432c4e0e85a738b580983eeeebc4bf Mon Sep 17 00:00:00 2001 From: alec_dev Date: Fri, 20 Dec 2024 11:24:53 -0600 Subject: [PATCH 1/9] add 'one-to-one' case in resourceApi --- .../js_src/lib/components/DataModel/resourceApi.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/resourceApi.ts b/specifyweb/frontend/js_src/lib/components/DataModel/resourceApi.ts index 2fe9e86ae5f..00fda185b29 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/resourceApi.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/resourceApi.ts @@ -215,6 +215,13 @@ export const ResourceBase = Backbone.Model.extend({ ); break; } + case 'one-to-one': { + newResource.set( + fieldName, + await related?.clone(cloneAll, isBulkCarry) + ); + break; + } default: { throw new Error('unhandled relationship type'); } From 64c8debf19f5d61550ffea78bfbe14bba59461c1 Mon Sep 17 00:00:00 2001 From: alec_dev Date: Fri, 20 Dec 2024 11:47:59 -0600 Subject: [PATCH 2/9] handle 'one-to-one' case in _handleInlineDataOrResource --- .../lib/components/DataModel/resourceApi.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/resourceApi.ts b/specifyweb/frontend/js_src/lib/components/DataModel/resourceApi.ts index 00fda185b29..1f36d328660 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/resourceApi.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/resourceApi.ts @@ -174,6 +174,7 @@ export const ResourceBase = Backbone.Model.extend({ { createdBy: 'clone' } ); + newResource.specifyTable = this.specifyTable; newResource.needsSaved = self.needsSaved; await Promise.all( @@ -557,9 +558,23 @@ export const ResourceBase = Backbone.Model.extend({ /* * Needed for taxonTreeDef on discipline because field.isVirtual equals false */ + // case 'one-to-one': { + // return value; + // } case 'one-to-one': { - return value; - } + if (!value) { + if (field.isDependent()) this.storeDependent(field, null); + else this.storeIndependent(field, null); + return value; + } + + const toOne = maybeMakeResource(value, relatedTable); + if (field.isDependent()) this.storeDependent(field, toOne); + else this.storeIndependent(field, toOne); + this.trigger(`change:${fieldName}`, this); + this.trigger('change', this); + return toOne.url(); + } } if (!field.isVirtual) softFail('Unhandled setting of relationship field', { From 7fedc6833333a38aef624dc3c3a3ef7e2ea54948 Mon Sep 17 00:00:00 2001 From: alec_dev Date: Fri, 20 Dec 2024 17:51:33 +0000 Subject: [PATCH 3/9] Lint code with ESLint and Prettier Triggered by 64c8debf19f5d61550ffea78bfbe14bba59461c1 on branch refs/heads/issue-5461 --- .../js_src/lib/components/DataModel/resourceApi.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/resourceApi.ts b/specifyweb/frontend/js_src/lib/components/DataModel/resourceApi.ts index 1f36d328660..b823de5681a 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/resourceApi.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/resourceApi.ts @@ -558,9 +558,11 @@ export const ResourceBase = Backbone.Model.extend({ /* * Needed for taxonTreeDef on discipline because field.isVirtual equals false */ - // case 'one-to-one': { - // return value; - // } + /* + * Case 'one-to-one': { + * return value; + * } + */ case 'one-to-one': { if (!value) { if (field.isDependent()) this.storeDependent(field, null); From bf4b6f3e4dce0678f3f24b10fe4517ec8867a822 Mon Sep 17 00:00:00 2001 From: alec_dev Date: Fri, 20 Dec 2024 11:53:19 -0600 Subject: [PATCH 4/9] rewrite functionality --- .../lib/components/DataModel/resourceApi.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/resourceApi.ts b/specifyweb/frontend/js_src/lib/components/DataModel/resourceApi.ts index 1f36d328660..9504410a7b9 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/resourceApi.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/resourceApi.ts @@ -558,23 +558,20 @@ export const ResourceBase = Backbone.Model.extend({ /* * Needed for taxonTreeDef on discipline because field.isVirtual equals false */ - // case 'one-to-one': { - // return value; - // } case 'one-to-one': { if (!value) { if (field.isDependent()) this.storeDependent(field, null); else this.storeIndependent(field, null); return value; } - - const toOne = maybeMakeResource(value, relatedTable); - if (field.isDependent()) this.storeDependent(field, toOne); - else this.storeIndependent(field, toOne); + + const oneToOne = maybeMakeResource(value, relatedTable); + if (field.isDependent()) this.storeDependent(field, oneToOne); + else this.storeIndependent(field, oneToOne); this.trigger(`change:${fieldName}`, this); this.trigger('change', this); - return toOne.url(); - } + return oneToOne.url(); + } } if (!field.isVirtual) softFail('Unhandled setting of relationship field', { From a4db8ea656ad7f76830e672b9a6838da925987b5 Mon Sep 17 00:00:00 2001 From: alec_dev Date: Fri, 20 Dec 2024 13:31:29 -0600 Subject: [PATCH 5/9] not a good fix, but a temp fix --- specifyweb/specify/api.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/specifyweb/specify/api.py b/specifyweb/specify/api.py index 13a38d6305e..73e1de0b39b 100644 --- a/specifyweb/specify/api.py +++ b/specifyweb/specify/api.py @@ -483,6 +483,9 @@ def create_obj(collection, agent, model, data: Dict[str, Any], parent_obj=None): model = get_model_or_404(model) data = cleanData(model, data, agent) obj = model() + if obj._meta.model_name == 'collectionobjectgroupjoin' and parent_obj._meta.model_name == 'collectionobject': + parent_obj.save() # temporary fix for saving co cojo.childco + data['childco'] = f"{data['childco']}{parent_obj.id}/" handle_fk_fields(collection, agent, obj, data) set_fields_from_data(obj, data) set_field_if_exists(obj, 'createdbyagent', agent) From f01ca5eb8b169a00be522cc7c37dcc253cbc7527 Mon Sep 17 00:00:00 2001 From: alec_dev Date: Fri, 20 Dec 2024 14:23:22 -0600 Subject: [PATCH 6/9] add childco conditional --- specifyweb/specify/api.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/specifyweb/specify/api.py b/specifyweb/specify/api.py index 73e1de0b39b..ca704b71db8 100644 --- a/specifyweb/specify/api.py +++ b/specifyweb/specify/api.py @@ -483,7 +483,11 @@ def create_obj(collection, agent, model, data: Dict[str, Any], parent_obj=None): model = get_model_or_404(model) data = cleanData(model, data, agent) obj = model() - if obj._meta.model_name == 'collectionobjectgroupjoin' and parent_obj._meta.model_name == 'collectionobject': + if ( + obj._meta.model_name == "collectionobjectgroupjoin" + and parent_obj._meta.model_name == "collectionobject" + and data["childco"] == "/api/specify/collectionobject/" + ): parent_obj.save() # temporary fix for saving co cojo.childco data['childco'] = f"{data['childco']}{parent_obj.id}/" handle_fk_fields(collection, agent, obj, data) @@ -1083,4 +1087,4 @@ def _handle_special_save_priors(obj): def _handle_special_update_priors(obj, data): data = modify_update_of_interaction_sibling_preps(obj, data) - pass \ No newline at end of file + pass From 2c647bb055023faeb8df71bbd0f845b6fd0dac81 Mon Sep 17 00:00:00 2001 From: alec_dev Date: Fri, 20 Dec 2024 14:34:02 -0600 Subject: [PATCH 7/9] cleanup create_obj function --- specifyweb/specify/api.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/specifyweb/specify/api.py b/specifyweb/specify/api.py index 13590a6662d..9694661fe13 100644 --- a/specifyweb/specify/api.py +++ b/specifyweb/specify/api.py @@ -483,13 +483,7 @@ def create_obj(collection, agent, model, data: Dict[str, Any], parent_obj=None): model = get_model_or_404(model) data = cleanData(model, data, agent) obj = model() - if ( - obj._meta.model_name == "collectionobjectgroupjoin" - and parent_obj._meta.model_name == "collectionobject" - and data["childco"] == "/api/specify/collectionobject/" - ): - parent_obj.save() # temporary fix for saving co cojo.childco - data['childco'] = f"{data['childco']}{parent_obj.id}/" + _handle_special_create_priors(obj, data, parent_obj) handle_fk_fields(collection, agent, obj, data) set_fields_from_data(obj, data) set_field_if_exists(obj, 'createdbyagent', agent) @@ -1091,4 +1085,15 @@ def _handle_special_save_priors(obj): def _handle_special_update_priors(obj, data): data = modify_update_of_interaction_sibling_preps(obj, data) - pass + +def _save_parent_co_prior(obj, data, parent_obj): + if ( + obj._meta.model_name == "collectionobjectgroupjoin" + and parent_obj._meta.model_name == "collectionobject" + and data["childco"] == "/api/specify/collectionobject/" + ): + parent_obj.save() # temporary fix for saving co cojo.childco + data['childco'] = f"{data['childco']}{parent_obj.id}/" + +def _handle_special_create_priors(obj, data, parent_obj): + _save_parent_co_prior(obj, data, parent_obj) From 0c1ef29fe3ccded3623e60776812bfb96f6cd4c3 Mon Sep 17 00:00:00 2001 From: alec_dev Date: Fri, 20 Dec 2024 15:00:47 -0600 Subject: [PATCH 8/9] handling null formatter during bulk carry --- .../js_src/lib/components/Forms/Save.tsx | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/Forms/Save.tsx b/specifyweb/frontend/js_src/lib/components/Forms/Save.tsx index c7f2f14ddad..08f9fbf9c1f 100644 --- a/specifyweb/frontend/js_src/lib/components/Forms/Save.tsx +++ b/specifyweb/frontend/js_src/lib/components/Forms/Save.tsx @@ -269,25 +269,26 @@ export function SaveButton({ carryForwardAmount > 1 ? async (): Promise>> => { const formatter = - tables.CollectionObject.strictGetLiteralField( - 'catalogNumber' - ).getUiFormatter()!; - const wildCard = formatter.valueOrWild(); - - const clonePromises = Array.from( - { length: carryForwardAmount }, - async () => { - const clonedResource = await resource.clone( - false, - true - ); + tables.CollectionObject.strictGetLiteralField( + 'catalogNumber' + ).getUiFormatter()!; + const wildCard =formatter === undefined ? null : formatter.valueOrWild(); + const clonePromises = Array.from( + { length: carryForwardAmount }, + async () => { + const clonedResource = await resource.clone( + false, + true + ); + if (wildCard !== null) { clonedResource.set( 'catalogNumber', wildCard as never ); - return clonedResource; } - ); + return clonedResource; + } + ); const clones = await Promise.all(clonePromises); From 5641d473537df381bfbe0be07a3eea47f7b81dc0 Mon Sep 17 00:00:00 2001 From: alec_dev Date: Mon, 23 Dec 2024 15:34:13 -0600 Subject: [PATCH 9/9] fix bulk co->cojo->childco creation --- specifyweb/specify/api.py | 48 +++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/specifyweb/specify/api.py b/specifyweb/specify/api.py index 9694661fe13..965ddbc5b04 100644 --- a/specifyweb/specify/api.py +++ b/specifyweb/specify/api.py @@ -484,7 +484,7 @@ def create_obj(collection, agent, model, data: Dict[str, Any], parent_obj=None): data = cleanData(model, data, agent) obj = model() _handle_special_create_priors(obj, data, parent_obj) - handle_fk_fields(collection, agent, obj, data) + handle_fk_fields(collection, agent, obj, data, parent_obj) set_fields_from_data(obj, data) set_field_if_exists(obj, 'createdbyagent', agent) set_field_if_exists(obj, 'collectionmemberid', collection.id) @@ -493,7 +493,8 @@ def create_obj(collection, agent, model, data: Dict[str, Any], parent_obj=None): except AutonumberOverflowException as e: logger.warn("autonumbering overflow: %s", e) - _handle_special_save_priors(obj) + # _handle_special_create_posteriors(obj, data, parent_obj, delay_fields) + _handle_special_save_priors(obj) # TODO: move before autonumbering_and_save if obj.id is not None: # was the object actually saved? check_table_permissions(collection, agent, obj, "create") @@ -575,7 +576,7 @@ def reorder_fields_for_embedding(cls, data: Dict[str, Any]) -> Iterable[Tuple[st yield (key, data[key]) -def handle_fk_fields(collection, agent, obj, data: Dict[str, Any]) -> Tuple[List, List[FieldChangeInfo]]: +def handle_fk_fields(collection, agent, obj, data: Dict[str, Any], parent_obj = None) -> Tuple[List, List[FieldChangeInfo]]: """Where 'obj' is a Django model instance and 'data' is a dict, set foreign key fields in the object from the provided data. """ @@ -615,8 +616,22 @@ def handle_fk_fields(collection, agent, obj, data: Dict[str, Any]) -> Tuple[List elif hasattr(val, 'items'): # i.e. it's a dict of some sort # The related object is represented by a nested dict of data. rel_model = field.related_model - - rel_obj = update_or_create_resource(collection, agent, rel_model, val, obj if dependent else None) + rel_obj = None + + if (field_name == 'childco' + and obj._meta.model_name == 'collectionobjectgroupjoin' + and parent_obj is not None + and parent_obj._meta.model_name == 'collectionobject'): + # parent_obj.save() # TODO: Decide to save here or in special save priors function + rel_obj = parent_obj + else: + rel_obj = update_or_create_resource( + collection, + agent, + rel_model, + val, + obj if dependent else None, + ) setattr(obj, field_name, rel_obj) if dependent and old_related and old_related.id != rel_obj.id: @@ -1087,13 +1102,22 @@ def _handle_special_update_priors(obj, data): data = modify_update_of_interaction_sibling_preps(obj, data) def _save_parent_co_prior(obj, data, parent_obj): - if ( - obj._meta.model_name == "collectionobjectgroupjoin" - and parent_obj._meta.model_name == "collectionobject" - and data["childco"] == "/api/specify/collectionobject/" - ): - parent_obj.save() # temporary fix for saving co cojo.childco - data['childco'] = f"{data['childco']}{parent_obj.id}/" + """ + Save the parent collection object and update the 'childco' field in the data. + """ + if obj._meta.model_name == "collectionobjectgroupjoin" and parent_obj._meta.model_name == "collectionobject": + if data.get("childco") == "/api/specify/collectionobject/": + parent_obj.save() # temporary fix for saving co cojo.childco + data["childco"] = f"{data['childco']}{parent_obj.id}/" + elif "catalognumber" not in data: # and 'id' not in data: + parent_obj.save() def _handle_special_create_priors(obj, data, parent_obj): _save_parent_co_prior(obj, data, parent_obj) + +def _save_cojo_child_co_posterior(obj, data, parent_obj=None): + if obj._meta.model_name == "collectionobjectgroupjoin" and data["childco"] == "/api/specify/collectionobject/": + obj.save() + +def _handle_special_create_posteriors(obj, data, parent_obj): + _save_cojo_child_co_posterior(obj, data, parent_obj)