From 309e6394fc798278781ff40d9fc7db2ed5343a24 Mon Sep 17 00:00:00 2001 From: Grant Fitzsimmons <37256050+grantfitzsimmons@users.noreply.github.com> Date: Wed, 23 Jul 2025 21:57:55 -0500 Subject: [PATCH 1/4] feat(datamodel): Add agent fields to Loan and Gift --- config/common/common.views.xml | 34 +++- .../js_src/lib/components/DataModel/types.ts | 10 ++ specifyweb/specify/datamodel.py | 10 ++ .../migration_utils/sp7_schemaconfig.py | 22 +++ .../0038_agent_fields_for_loan_and_gift.py | 153 ++++++++++++++++++ specifyweb/specify/models.py | 10 ++ 6 files changed, 237 insertions(+), 2 deletions(-) create mode 100644 specifyweb/specify/migrations/0038_agent_fields_for_loan_and_gift.py diff --git a/config/common/common.views.xml b/config/common/common.views.xml index 573e1e68532..94c312500c6 100644 --- a/config/common/common.views.xml +++ b/config/common/common.views.xml @@ -3514,7 +3514,22 @@ - --> + + + + + + + + + + + + + + + + --> @@ -4202,7 +4217,22 @@ - --> + + + + + + + + + + + + + + + + --> diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/types.ts b/specifyweb/frontend/js_src/lib/components/DataModel/types.ts index 3b38f5ce27b..ffe95745858 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/types.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/types.ts @@ -3369,6 +3369,11 @@ export type Gift = { }; readonly toOneDependent: { readonly addressOfRecord: AddressOfRecord | null }; readonly toOneIndependent: { + readonly agent1: Agent | null; + readonly agent2: Agent | null; + readonly agent3: Agent | null; + readonly agent4: Agent | null; + readonly agent5: Agent | null; readonly createdByAgent: Agent | null; readonly deaccession: Deaccession | null; readonly discipline: Discipline; @@ -3762,6 +3767,11 @@ export type Loan = { }; readonly toOneDependent: { readonly addressOfRecord: AddressOfRecord | null }; readonly toOneIndependent: { + readonly agent1: Agent | null; + readonly agent2: Agent | null; + readonly agent3: Agent | null; + readonly agent4: Agent | null; + readonly agent5: Agent | null; readonly createdByAgent: Agent | null; readonly discipline: Discipline; readonly division: Division | null; diff --git a/specifyweb/specify/datamodel.py b/specifyweb/specify/datamodel.py index 10f1d2f331a..da4f561043f 100644 --- a/specifyweb/specify/datamodel.py +++ b/specifyweb/specify/datamodel.py @@ -4065,6 +4065,11 @@ def is_tree_table(table: Table): ], relationships=[ Relationship(name='addressOfRecord', type='many-to-one',required=False, relatedModelName='AddressOfRecord', column='AddressOfRecordID', dependent=True), + Relationship(name='agent1', type='many-to-one',required=False, relatedModelName='Agent', column='Agent1ID'), + Relationship(name='agent2', type='many-to-one',required=False, relatedModelName='Agent', column='Agent2ID'), + Relationship(name='agent3', type='many-to-one',required=False, relatedModelName='Agent', column='Agent3ID'), + Relationship(name='agent4', type='many-to-one',required=False, relatedModelName='Agent', column='Agent4ID'), + Relationship(name='agent5', type='many-to-one',required=False, relatedModelName='Agent', column='Agent5ID'), Relationship(name='createdByAgent', type='many-to-one',required=False, relatedModelName='Agent', column='CreatedByAgentID'), Relationship(name='deaccession', type='many-to-one',required=False, relatedModelName='Deaccession', column='DeaccessionID', otherSideName='gifts'), Relationship(name='discipline', type='many-to-one',required=True, relatedModelName='Discipline', column='DisciplineID'), @@ -4591,6 +4596,11 @@ def is_tree_table(table: Table): ], relationships=[ Relationship(name='addressOfRecord', type='many-to-one',required=False, relatedModelName='AddressOfRecord', column='AddressOfRecordID', otherSideName='loans', dependent=True), + Relationship(name='agent1', type='many-to-one',required=False, relatedModelName='Agent', column='Agent1ID'), + Relationship(name='agent2', type='many-to-one',required=False, relatedModelName='Agent', column='Agent2ID'), + Relationship(name='agent3', type='many-to-one',required=False, relatedModelName='Agent', column='Agent3ID'), + Relationship(name='agent4', type='many-to-one',required=False, relatedModelName='Agent', column='Agent4ID'), + Relationship(name='agent5', type='many-to-one',required=False, relatedModelName='Agent', column='Agent5ID'), Relationship(name='createdByAgent', type='many-to-one',required=False, relatedModelName='Agent', column='CreatedByAgentID'), Relationship(name='discipline', type='many-to-one',required=True, relatedModelName='Discipline', column='DisciplineID'), Relationship(name='division', type='many-to-one',required=False, relatedModelName='Division', column='DivisionID'), diff --git a/specifyweb/specify/migration_utils/sp7_schemaconfig.py b/specifyweb/specify/migration_utils/sp7_schemaconfig.py index 943144b5cff..07caa8dc3cd 100644 --- a/specifyweb/specify/migration_utils/sp7_schemaconfig.py +++ b/specifyweb/specify/migration_utils/sp7_schemaconfig.py @@ -221,4 +221,26 @@ ('date2', 'Date 2', 'Date 2'), ('date2Precision', 'Date 2 Precision', 'Date 2 Precision'), ] +} + +MIGRATION_0038_FIELDS = { + 'Loan': ['agent1', 'agent2', 'agent3', 'agent4', 'agent5'], + 'Gift': ['agent1', 'agent2', 'agent3', 'agent4', 'agent5'], +} + +MIGRATION_0038_UPDATE_FIELDS = { + 'Loan': [ + ('agent1','Agent 1','Agent 1'), + ('agent2','Agent 2','Agent 2'), + ('agent3','Agent 3','Agent 3'), + ('agent4','Agent 4','Agent 4'), + ('agent5','Agent 5','Agent 5'), + ], + 'Gift': [ + ('agent1','Agent 1','Agent 1'), + ('agent2','Agent 2','Agent 2'), + ('agent3','Agent 3','Agent 3'), + ('agent4','Agent 4','Agent 4'), + ('agent5','Agent 5','Agent 5'), + ] } \ No newline at end of file diff --git a/specifyweb/specify/migrations/0038_agent_fields_for_loan_and_gift.py b/specifyweb/specify/migrations/0038_agent_fields_for_loan_and_gift.py new file mode 100644 index 00000000000..e8ef0a5e0ce --- /dev/null +++ b/specifyweb/specify/migrations/0038_agent_fields_for_loan_and_gift.py @@ -0,0 +1,153 @@ +# Generated by Django 4.2.22 on 2025-07-23 21:18 + +from django.apps import apps as specify_apps +from django.db import migrations, models +from specifyweb.specify.models import protect_with_blockers + +from specifyweb.specify.migration_utils.update_schema_config import ( + revert_table_field_schema_config, + update_table_field_schema_config_with_defaults, +) +from specifyweb.specify.migration_utils.sp7_schemaconfig import ( + MIGRATION_0038_FIELDS as SCHEMA_CONFIG_RAW_FIELDS, + MIGRATION_0038_UPDATE_FIELDS as SCHEMA_CONFIG_FIELD_DESC, +) + +def update_0038_fields(apps): + """ + Update table-field schema entries for plain field names + (e.g., MIGRATION_0038_FIELDS). + """ + Discipline = apps.get_model('specify', 'Discipline') + for discipline in Discipline.objects.all(): + for table, fields in SCHEMA_CONFIG_RAW_FIELDS.items(): + for field_name in fields: + update_table_field_schema_config_with_defaults(table, discipline.id, field_name, apps) + +def revert_0038_fields(apps): + """ + Revert table-field entries for plain field names. + """ + for table, fields in SCHEMA_CONFIG_RAW_FIELDS.items(): + for field_name in fields: + revert_table_field_schema_config(table, field_name, apps) + +def update_0038_schema_config_field_desc(apps, schema_editor): + """ + Update field descriptions and display names using MIGRATION_0038_UPDATE_FIELDS + (tuple: (fieldName, newLabel, newDesc)). + """ + Splocalecontainer = apps.get_model('specify', 'Splocalecontainer') + Splocalecontaineritem = apps.get_model('specify', 'Splocalecontaineritem') + Splocaleitemstr = apps.get_model('specify', 'Splocaleitemstr') + + for table, fields in SCHEMA_CONFIG_FIELD_DESC.items(): + containers = Splocalecontainer.objects.filter(name=table.lower()) + for container in containers: + for (field_name, new_name, new_desc) in fields: + items = Splocalecontaineritem.objects.filter( + container=container, + name=field_name.lower() + ) + for item in items: + item.ishidden = True + item.save() + desc_str = Splocaleitemstr.objects.filter(itemdesc_id=item.id).first() + name_str = Splocaleitemstr.objects.filter(itemname_id=item.id).first() + if not desc_str or not name_str: + continue + desc_str.text = new_desc + desc_str.save() + name_str.text = new_name + name_str.save() + +def revert_0038_schema_config_field_desc(apps, schema_editor): + """ + Revert the field name/description updates. + """ + Splocalecontainer = apps.get_model('specify', 'Splocalecontainer') + Splocalecontaineritem = apps.get_model('specify', 'Splocalecontaineritem') + + for table, fields in SCHEMA_CONFIG_FIELD_DESC.items(): + containers = Splocalecontainer.objects.filter(name=table.lower()) + for container in containers: + for (field_name, _, _) in fields: + items = Splocalecontaineritem.objects.filter( + container=container, + name=field_name.lower() + ) + for item in items: + # If needed, reset ishidden or revert text + pass + +def consolidated_0038_forward(apps, schema_editor): + update_0038_fields(apps) + update_0038_schema_config_field_desc(apps, schema_editor) + +def consolidated_0038_backward(apps, schema_editor): + revert_0038_schema_config_field_desc(apps, schema_editor) + revert_0038_fields(apps) + +class Migration(migrations.Migration): + + dependencies = [ + ('specify', '0037_make_smushed_default_false'), + ] + + operations = [ + migrations.AddField( + model_name='gift', + name='agent1', + field=models.ForeignKey(db_column='Agent1ID', null=True, on_delete=protect_with_blockers, related_name='+', to='specify.agent'), + ), + migrations.AddField( + model_name='gift', + name='agent2', + field=models.ForeignKey(db_column='Agent2ID', null=True, on_delete=protect_with_blockers, related_name='+', to='specify.agent'), + ), + migrations.AddField( + model_name='gift', + name='agent3', + field=models.ForeignKey(db_column='Agent3ID', null=True, on_delete=protect_with_blockers, related_name='+', to='specify.agent'), + ), + migrations.AddField( + model_name='gift', + name='agent4', + field=models.ForeignKey(db_column='Agent4ID', null=True, on_delete=protect_with_blockers, related_name='+', to='specify.agent'), + ), + migrations.AddField( + model_name='gift', + name='agent5', + field=models.ForeignKey(db_column='Agent5ID', null=True, on_delete=protect_with_blockers, related_name='+', to='specify.agent'), + ), + migrations.AddField( + model_name='loan', + name='agent1', + field=models.ForeignKey(db_column='Agent1ID', null=True, on_delete=protect_with_blockers, related_name='+', to='specify.agent'), + ), + migrations.AddField( + model_name='loan', + name='agent2', + field=models.ForeignKey(db_column='Agent2ID', null=True, on_delete=protect_with_blockers, related_name='+', to='specify.agent'), + ), + migrations.AddField( + model_name='loan', + name='agent3', + field=models.ForeignKey(db_column='Agent3ID', null=True, on_delete=protect_with_blockers, related_name='+', to='specify.agent'), + ), + migrations.AddField( + model_name='loan', + name='agent4', + field=models.ForeignKey(db_column='Agent4ID', null=True, on_delete=protect_with_blockers, related_name='+', to='specify.agent'), + ), + migrations.AddField( + model_name='loan', + name='agent5', + field=models.ForeignKey(db_column='Agent5ID', null=True, on_delete=protect_with_blockers, related_name='+', to='specify.agent'), + ), + migrations.RunPython( + consolidated_0038_forward, + consolidated_0038_backward, + atomic=True, + ), + ] \ No newline at end of file diff --git a/specifyweb/specify/models.py b/specifyweb/specify/models.py index b41d28b476f..c64d0eaab0b 100644 --- a/specifyweb/specify/models.py +++ b/specifyweb/specify/models.py @@ -3825,6 +3825,11 @@ class Gift(models.Model): # Relationships: Many-to-One addressofrecord = models.ForeignKey('AddressOfRecord', db_column='AddressOfRecordID', related_name='+', null=True, on_delete=protect_with_blockers) + agent1 = models.ForeignKey('Agent', db_column='Agent1ID', related_name='+', null=True, on_delete=protect_with_blockers) + agent2 = models.ForeignKey('Agent', db_column='Agent2ID', related_name='+', null=True, on_delete=protect_with_blockers) + agent3 = models.ForeignKey('Agent', db_column='Agent3ID', related_name='+', null=True, on_delete=protect_with_blockers) + agent4 = models.ForeignKey('Agent', db_column='Agent4ID', related_name='+', null=True, on_delete=protect_with_blockers) + agent5 = models.ForeignKey('Agent', db_column='Agent5ID', related_name='+', null=True, on_delete=protect_with_blockers) createdbyagent = models.ForeignKey('Agent', db_column='CreatedByAgentID', related_name='+', null=True, on_delete=protect_with_blockers) deaccession = models.ForeignKey('Deaccession', db_column='DeaccessionID', related_name='gifts', null=True, on_delete=protect_with_blockers) discipline = models.ForeignKey('Discipline', db_column='DisciplineID', related_name='+', null=False, on_delete=protect_with_blockers) @@ -4322,6 +4327,11 @@ class Loan(models.Model): # Relationships: Many-to-One addressofrecord = models.ForeignKey('AddressOfRecord', db_column='AddressOfRecordID', related_name='loans', null=True, on_delete=protect_with_blockers) + agent1 = models.ForeignKey('Agent', db_column='Agent1ID', related_name='+', null=True, on_delete=protect_with_blockers) + agent2 = models.ForeignKey('Agent', db_column='Agent2ID', related_name='+', null=True, on_delete=protect_with_blockers) + agent3 = models.ForeignKey('Agent', db_column='Agent3ID', related_name='+', null=True, on_delete=protect_with_blockers) + agent4 = models.ForeignKey('Agent', db_column='Agent4ID', related_name='+', null=True, on_delete=protect_with_blockers) + agent5 = models.ForeignKey('Agent', db_column='Agent5ID', related_name='+', null=True, on_delete=protect_with_blockers) createdbyagent = models.ForeignKey('Agent', db_column='CreatedByAgentID', related_name='+', null=True, on_delete=protect_with_blockers) discipline = models.ForeignKey('Discipline', db_column='DisciplineID', related_name='+', null=False, on_delete=protect_with_blockers) division = models.ForeignKey('Division', db_column='DivisionID', related_name='+', null=True, on_delete=protect_with_blockers) From 59d63accd5d0b197ffbc6b63c92d9f47a4629e9d Mon Sep 17 00:00:00 2001 From: Grant Fitzsimmons <37256050+grantfitzsimmons@users.noreply.github.com> Date: Fri, 8 Aug 2025 14:17:11 +0000 Subject: [PATCH 2/4] Lint code with ESLint and Prettier Triggered by 217a13b37c848d788b2265bafb76dd7193bdc75a on branch refs/heads/issue-7090 --- .../lib/components/AppResources/Aside.tsx | 29 +- .../lib/components/AppResources/Tabs.tsx | 56 +- .../AppResources/__tests__/Tabs.test.tsx | 60 +- .../__tests__/AttachmentCell.test.tsx | 258 +- .../js_src/lib/localization/attachments.ts | 1054 +++--- .../js_src/lib/localization/backEnd.ts | 790 ++-- .../js_src/lib/localization/batchEdit.ts | 704 ++-- .../js_src/lib/localization/common.ts | 1282 +++---- .../js_src/lib/localization/development.ts | 44 +- .../frontend/js_src/lib/localization/forms.ts | 1968 +++++----- .../js_src/lib/localization/header.ts | 688 ++-- .../js_src/lib/localization/interactions.ts | 624 ++-- .../js_src/lib/localization/merging.ts | 382 +- .../js_src/lib/localization/notifications.ts | 200 +- .../js_src/lib/localization/preferences.ts | 3260 ++++++++--------- .../js_src/lib/localization/report.ts | 274 +- .../js_src/lib/localization/resources.ts | 1456 ++++---- .../frontend/js_src/lib/localization/tree.ts | 1040 +++--- .../js_src/lib/localization/workbench.ts | 2398 ++++++------ 19 files changed, 8288 insertions(+), 8279 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/AppResources/Aside.tsx b/specifyweb/frontend/js_src/lib/components/AppResources/Aside.tsx index 28188fe280f..371c50ab03f 100644 --- a/specifyweb/frontend/js_src/lib/components/AppResources/Aside.tsx +++ b/specifyweb/frontend/js_src/lib/components/AppResources/Aside.tsx @@ -71,7 +71,8 @@ export function AppResourcesAside({