Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow Independent Subviews via API #5253

Open
wants to merge 123 commits into
base: production
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
123 commits
Select commit Hold shift + click to select a range
8b8ae16
Allow independent subviews
maxpatiiuk Mar 8, 2023
a9a0aff
Turn independent sub views into buttons
maxpatiiuk Mar 8, 2023
2b90246
Merge branch 'production' into issue-114
grantfitzsimmons Mar 9, 2023
a5101a8
Merge remote-tracking branch 'origin/production' into issue-114
CarolineDenis Oct 9, 2023
0d9168c
Merge branch 'production' into issue-114
melton-jason Oct 25, 2023
9d24da9
Merge branch 'production' into issue-114
melton-jason Nov 16, 2023
dfaa5d0
Merge branch 'production' into issue-114
melton-jason Nov 24, 2023
8db2925
Lint code with ESLint and Prettier
melton-jason Nov 24, 2023
1359149
Merge remote-tracking branch 'origin/production' into issue-114
CarolineDenis Jan 17, 2024
a373d00
Lint code with ESLint and Prettier
CarolineDenis Jan 17, 2024
a512205
Merge remote-tracking branch 'origin/production' into issue-114
melton-jason Mar 15, 2024
f168bb4
Merge remote-tracking branch 'origin/production' into issue-114
melton-jason May 6, 2024
90cc9d4
Merge branch 'production' into issue-114
melton-jason May 14, 2024
009fad3
Merge remote-tracking branch 'origin/production' into issue-114
melton-jason May 23, 2024
7b0c29b
Merge remote-tracking branch 'origin/production' into issue-114-backend
melton-jason Jun 25, 2024
74502de
Allow modifying remote side of indedpenent resources via API
melton-jason Jun 26, 2024
a55de9e
Add tests for inline independent resources
melton-jason Jun 26, 2024
d970ca1
Write test for reassigning to-one relationship from to-many side
melton-jason Jun 27, 2024
8d0ad64
Merge remote-tracking branch 'origin/production' into issue-114-backend
melton-jason Jul 1, 2024
efd44b9
Modify frontend orm to handle indpendent to-many relationships
melton-jason Jul 3, 2024
63aff69
Merge remote-tracking branch 'origin/production' into issue-114-backend
melton-jason Jul 16, 2024
495c50b
Merge remote-tracking branch 'origin/production' into issue-114-backend
melton-jason Jul 24, 2024
f2a4397
Add Independent Collection to Collection API
melton-jason Jul 25, 2024
e63365c
Recompute resource delete blockers on save
melton-jason Jul 26, 2024
5b41b02
Don't default independent subviews as buttons
melton-jason Jul 26, 2024
5f196c5
Propagate save blockers on independent resources to related record
melton-jason Jul 26, 2024
0ea5089
Merge remote-tracking branch 'origin/production' into issue-114-backend
melton-jason Aug 8, 2024
59d1ff8
Add CO -> isMemberOfCOG virtual field to frontend
melton-jason Aug 22, 2024
4abacf0
Improve date backendfilters on frontend
melton-jason Aug 22, 2024
dc570f6
Allow creating new records in Independent ToMany Collections
melton-jason Aug 22, 2024
d557e91
Merge remote-tracking branch 'origin/production' into issue-114-backend
melton-jason Aug 22, 2024
08544c3
Allow creating new resources from independent to-one side
melton-jason Aug 22, 2024
70c3345
Remove toApiJSON method declaration on independent to-one resources
melton-jason Aug 22, 2024
02e262e
Remove changedResources on IndependentCollection
melton-jason Aug 23, 2024
a2d3574
Display loading indicator while fetching collection
melton-jason Aug 23, 2024
cf89d5b
Use ResourceView dialog when adding independent resource
melton-jason Aug 23, 2024
ad5a8ef
Only show loading indicators for non-button subviews
melton-jason Aug 23, 2024
d555321
Only show ResourceView dialog when specified viewname differs from ta…
melton-jason Aug 23, 2024
2664cf1
Use aria-pressed on add button when ResourceView is open
melton-jason Aug 23, 2024
14e2ab3
Refactor getDependentToMany
melton-jason Aug 23, 2024
d47585d
Fix bug when fetching independent collection when related is initiali…
melton-jason Aug 27, 2024
5c9f22d
Only update related version when changed
melton-jason Aug 27, 2024
3a6b9b5
Fix test not being automatically ran
melton-jason Aug 29, 2024
0405f3f
Merge remote-tracking branch 'origin/production' into issue-114-backend
melton-jason Aug 29, 2024
2e46526
Show helpful error when rendering to-many as querycbx
melton-jason Sep 2, 2024
5921a39
Hide relationship when needed to avoid cyclical rendering
melton-jason Sep 2, 2024
137a277
Resolve type errors
melton-jason Sep 2, 2024
014f871
Resolve frontend tests
melton-jason Sep 2, 2024
629b92c
Check independent update permission when removing from relationship
melton-jason Sep 2, 2024
6ab0e34
Don't render SubView if reverse relationship doesn't exist
melton-jason Sep 3, 2024
0417494
Insert removal of independent to-manys into auditlog
melton-jason Sep 4, 2024
067aaa6
Resolve backend tests
melton-jason Sep 4, 2024
f10e410
Resolve frontend tests
melton-jason Sep 4, 2024
a6a233d
Make independent subview lazy
melton-jason Sep 9, 2024
434047b
Cleanup handle_to_many code
melton-jason Sep 9, 2024
febbc16
Cleanup API for to-many Collections
melton-jason Sep 11, 2024
794fdee
Don't automatically fetch Independent ToOne Collections when modified
melton-jason Sep 11, 2024
ab8150b
Merge remote-tracking branch 'origin/production' into issue-114-backend
melton-jason Sep 12, 2024
872f601
Initialize totalCount variable for lazy collections
melton-jason Sep 12, 2024
868e76a
Remove import + chnage url test
CarolineDenis Sep 13, 2024
22d057f
Indep-to-many: use Django fieldnames over datamodel field names
melton-jason Sep 16, 2024
9ce312e
Define useCollection hook, reset collection on parent save, fix formt…
melton-jason Sep 18, 2024
134c65f
Resolve TS errors
melton-jason Sep 18, 2024
dd61235
Resolve backend tests, update URI for frontend format test
melton-jason Sep 18, 2024
d512e33
Change ordering for setting of _neverFetched
melton-jason Sep 18, 2024
473ea5c
Improve orderby resolution for to-many subviews
melton-jason Sep 18, 2024
cf84978
Resolving failing frontend test
melton-jason Sep 18, 2024
3795099
Merge remote-tracking branch 'origin/production' into issue-114-backend
melton-jason Sep 19, 2024
7514e3f
Merge remote-tracking branch 'origin/production' into issue-114-backend
melton-jason Sep 24, 2024
33515c4
Prevent storing independent toMany if fetch not successful
melton-jason Sep 24, 2024
be03bcd
Set independent toOne on resource change
melton-jason Sep 27, 2024
7bcab20
Fetch independnet toOne when rgetPromise called
melton-jason Sep 30, 2024
05ecc78
Merge branch 'production' into issue-114-backend
melton-jason Oct 3, 2024
bab5e1d
Separate isComplete logic for Lazy/Relationship fetch calls
melton-jason Oct 3, 2024
d1cd4df
Lint code with ESLint and Prettier
melton-jason Oct 3, 2024
b1b457e
Merge remote-tracking branch 'origin/production' into issue-114-backend
melton-jason Oct 4, 2024
91b8031
Write frontend tests for Independent Collections
melton-jason Oct 6, 2024
dc01fc4
Lint code with ESLint and Prettier
melton-jason Oct 6, 2024
5a9fe94
Always respect options to collection fetch calls
melton-jason Oct 8, 2024
d382170
Write test for collection fetch options
melton-jason Oct 8, 2024
ad2a9d5
Fix type error in test
melton-jason Oct 8, 2024
dc4ae07
Merge branch 'production' into issue-114-backend
melton-jason Oct 8, 2024
eb724c9
Merge branch 'production' into issue-114-backend
melton-jason Oct 15, 2024
98b61a3
Merge branch 'production' into issue-114-backend
melton-jason Oct 18, 2024
fb3dc17
Propagate change from toMany relationship collections to related
melton-jason Oct 18, 2024
49d8307
Lint code with ESLint and Prettier
melton-jason Oct 18, 2024
7c45847
Merge branch 'production' into issue-114-backend
melton-jason Oct 22, 2024
44aa226
Lint code with ESLint and Prettier
melton-jason Oct 22, 2024
075614d
Improve collection fetching for Grid-based Subviews
melton-jason Oct 22, 2024
c6c9d8c
Allow passing filters to resource.rgetCollection
melton-jason Oct 23, 2024
81b8751
Lint code with ESLint and Prettier
melton-jason Oct 23, 2024
f53e48d
Update comment in fetchToOneCollection
melton-jason Oct 23, 2024
10f544d
Merge remote-tracking branch 'origin/production' into issue-114-backend
melton-jason Oct 24, 2024
8a2bda6
Passing filters to toOneCollection when using useCollection hook
melton-jason Oct 28, 2024
193a1c7
Merge remote-tracking branch 'origin/production' into issue-114-backend
melton-jason Oct 28, 2024
dc9026e
Fix misc. bugs
melton-jason Oct 28, 2024
107112d
Merge remote-tracking branch 'origin/production' into issue-114-backend
melton-jason Oct 29, 2024
144c282
Improve error message when related no longer exists
melton-jason Oct 30, 2024
a24717d
Merge branch 'production' into issue-114-backend
sharadsw Oct 30, 2024
cccb637
Allow negating filters when fetching resources
melton-jason Nov 1, 2024
b8e2dcd
Show a resource as readonly if already rendered in SubView hierarchy
melton-jason Nov 1, 2024
0354172
Render independent self-referential tree subviews as read only
melton-jason Nov 1, 2024
f122e00
Merge branch 'issue-114-backend' of https://github.com/specify/specif…
melton-jason Nov 1, 2024
f7edb40
Resolve typeerrors
melton-jason Nov 1, 2024
4fdb2d4
Assert types when getting resources cached dict
melton-jason Nov 1, 2024
8e88808
Merge remote-tracking branch 'origin/production' into issue-114-backend
melton-jason Nov 4, 2024
6dd267a
Format test_trees file
melton-jason Nov 4, 2024
743f84d
Add test for inferring parent based on rankid
melton-jason Nov 4, 2024
34eaffb
Merge branch 'production' into issue-114-backend
melton-jason Nov 4, 2024
132ea6d
Resolve mypy errors
melton-jason Nov 4, 2024
f3c305e
Merge branch 'production' into issue-114-backend
melton-jason Nov 4, 2024
9367f20
Add type annotations
melton-jason Nov 4, 2024
a3ac90e
Rename variables
melton-jason Nov 5, 2024
b8fb859
Correct unbound variable
melton-jason Nov 5, 2024
8797a26
Further correct variables
melton-jason Nov 5, 2024
68627a2
Correct arguments to type conversion
melton-jason Nov 5, 2024
94c2d52
Merge branch 'production' into issue-114-backend
melton-jason Nov 5, 2024
048738b
Always refetch independent to-one related resources
melton-jason Nov 6, 2024
1d4e3bb
Update fetching related independent test
melton-jason Nov 6, 2024
387fb1c
Merge remote-tracking branch 'origin/production' into issue-114-backend
melton-jason Nov 7, 2024
2aec725
Merge branch 'production' into issue-114-backend
melton-jason Nov 8, 2024
bedeee6
Fix remove button bugs
melton-jason Nov 8, 2024
53cdef1
Merge remote-tracking branch 'origin/production' into issue-114-backend
melton-jason Nov 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ exports[`fields are loaded 1`] = `
"[literalField CollectionObject.text2]",
"[literalField CollectionObject.inventoryDate]",
"[literalField CollectionObject.inventoryDatePrecision]",
"[literalField CollectionObject.isMemberOfCOG]",
"[literalField CollectionObject.modifier]",
"[literalField CollectionObject.name]",
"[literalField CollectionObject.notifications]",
Expand Down Expand Up @@ -101,6 +102,108 @@ exports[`fields are loaded 1`] = `
]
`;

exports[`indexed fields are loaded 1`] = `
{
"accession": "[relationship CollectionObject.accession]",
"actualTotalCountAmt": "[literalField CollectionObject.actualTotalCountAmt]",
"agent1": "[relationship CollectionObject.agent1]",
"altCatalogNumber": "[literalField CollectionObject.altCatalogNumber]",
"appraisal": "[relationship CollectionObject.appraisal]",
"availability": "[literalField CollectionObject.availability]",
"catalogNumber": "[literalField CollectionObject.catalogNumber]",
"catalogedDate": "[literalField CollectionObject.catalogedDate]",
"catalogedDatePrecision": "[literalField CollectionObject.catalogedDatePrecision]",
"catalogedDateVerbatim": "[literalField CollectionObject.catalogedDateVerbatim]",
"cataloger": "[relationship CollectionObject.cataloger]",
"cojo": "[relationship CollectionObject.cojo]",
"collectingEvent": "[relationship CollectionObject.collectingEvent]",
"collection": "[relationship CollectionObject.collection]",
"collectionMemberId": "[literalField CollectionObject.collectionMemberId]",
"collectionObjectAttachments": "[relationship CollectionObject.collectionObjectAttachments]",
"collectionObjectAttribute": "[relationship CollectionObject.collectionObjectAttribute]",
"collectionObjectAttrs": "[relationship CollectionObject.collectionObjectAttrs]",
"collectionObjectCitations": "[relationship CollectionObject.collectionObjectCitations]",
"collectionObjectProperties": "[relationship CollectionObject.collectionObjectProperties]",
"collectionObjectType": "[relationship CollectionObject.collectionObjectType]",
"conservDescriptions": "[relationship CollectionObject.conservDescriptions]",
"container": "[relationship CollectionObject.container]",
"containerOwner": "[relationship CollectionObject.containerOwner]",
"countAmt": "[literalField CollectionObject.countAmt]",
"createdByAgent": "[relationship CollectionObject.createdByAgent]",
"currentDetermination": "[relationship CollectionObject.currentDetermination]",
"date1": "[literalField CollectionObject.date1]",
"date1Precision": "[literalField CollectionObject.date1Precision]",
"deaccessioned": "[literalField CollectionObject.deaccessioned]",
"description": "[literalField CollectionObject.description]",
"determinations": "[relationship CollectionObject.determinations]",
"dnaSequences": "[relationship CollectionObject.dnaSequences]",
"embargoAuthority": "[relationship CollectionObject.embargoAuthority]",
"embargoReason": "[literalField CollectionObject.embargoReason]",
"embargoReleaseDate": "[literalField CollectionObject.embargoReleaseDate]",
"embargoReleaseDatePrecision": "[literalField CollectionObject.embargoReleaseDatePrecision]",
"embargoStartDate": "[literalField CollectionObject.embargoStartDate]",
"embargoStartDatePrecision": "[literalField CollectionObject.embargoStartDatePrecision]",
"exsiccataItems": "[relationship CollectionObject.exsiccataItems]",
"fieldNotebookPage": "[relationship CollectionObject.fieldNotebookPage]",
"fieldNumber": "[literalField CollectionObject.fieldNumber]",
"guid": "[literalField CollectionObject.guid]",
"integer1": "[literalField CollectionObject.integer1]",
"integer2": "[literalField CollectionObject.integer2]",
"inventorizedBy": "[relationship CollectionObject.inventorizedBy]",
"inventoryDate": "[literalField CollectionObject.inventoryDate]",
"inventoryDatePrecision": "[literalField CollectionObject.inventoryDatePrecision]",
"isMemberOfCOG": "[literalField CollectionObject.isMemberOfCOG]",
"leftSideRels": "[relationship CollectionObject.leftSideRels]",
"modifiedByAgent": "[relationship CollectionObject.modifiedByAgent]",
"modifier": "[literalField CollectionObject.modifier]",
"name": "[literalField CollectionObject.name]",
"notifications": "[literalField CollectionObject.notifications]",
"number1": "[literalField CollectionObject.number1]",
"number2": "[literalField CollectionObject.number2]",
"numberOfDuplicates": "[literalField CollectionObject.numberOfDuplicates]",
"objectCondition": "[literalField CollectionObject.objectCondition]",
"ocr": "[literalField CollectionObject.ocr]",
"otherIdentifiers": "[relationship CollectionObject.otherIdentifiers]",
"paleoContext": "[relationship CollectionObject.paleoContext]",
"preparations": "[relationship CollectionObject.preparations]",
"projectNumber": "[literalField CollectionObject.projectNumber]",
"projects": "[relationship CollectionObject.projects]",
"remarks": "[literalField CollectionObject.remarks]",
"reservedInteger3": "[literalField CollectionObject.reservedInteger3]",
"reservedInteger4": "[literalField CollectionObject.reservedInteger4]",
"reservedText": "[literalField CollectionObject.reservedText]",
"reservedText2": "[literalField CollectionObject.reservedText2]",
"reservedText3": "[literalField CollectionObject.reservedText3]",
"restrictions": "[literalField CollectionObject.restrictions]",
"rightSideRels": "[relationship CollectionObject.rightSideRels]",
"sgrStatus": "[literalField CollectionObject.sgrStatus]",
"text1": "[literalField CollectionObject.text1]",
"text2": "[literalField CollectionObject.text2]",
"text3": "[literalField CollectionObject.text3]",
"text4": "[literalField CollectionObject.text4]",
"text5": "[literalField CollectionObject.text5]",
"text6": "[literalField CollectionObject.text6]",
"text7": "[literalField CollectionObject.text7]",
"text8": "[literalField CollectionObject.text8]",
"timestampCreated": "[literalField CollectionObject.timestampCreated]",
"timestampModified": "[literalField CollectionObject.timestampModified]",
"totalCountAmt": "[literalField CollectionObject.totalCountAmt]",
"totalValue": "[literalField CollectionObject.totalValue]",
"treatmentEvents": "[relationship CollectionObject.treatmentEvents]",
"uniqueIdentifier": "[literalField CollectionObject.uniqueIdentifier]",
"version": "[literalField CollectionObject.version]",
"visibility": "[literalField CollectionObject.visibility]",
"visibilitySetBy": "[relationship CollectionObject.visibilitySetBy]",
"voucherRelationships": "[relationship CollectionObject.voucherRelationships]",
"yesNo1": "[literalField CollectionObject.yesNo1]",
"yesNo2": "[literalField CollectionObject.yesNo2]",
"yesNo3": "[literalField CollectionObject.yesNo3]",
"yesNo4": "[literalField CollectionObject.yesNo4]",
"yesNo5": "[literalField CollectionObject.yesNo5]",
"yesNo6": "[literalField CollectionObject.yesNo6]",
}
`;

exports[`literal fields are loaded 1`] = `
[
"[literalField CollectionObject.actualTotalCountAmt]",
Expand All @@ -127,6 +230,7 @@ exports[`literal fields are loaded 1`] = `
"[literalField CollectionObject.text2]",
"[literalField CollectionObject.inventoryDate]",
"[literalField CollectionObject.inventoryDatePrecision]",
"[literalField CollectionObject.isMemberOfCOG]",
"[literalField CollectionObject.modifier]",
"[literalField CollectionObject.name]",
"[literalField CollectionObject.notifications]",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ test('getFieldsToNotClone', () => {
'catalogNumber',
'timestampModified',
'guid',
'isMemberOfCOG',
'timestampCreated',
'totalCountAmt',
'uniqueIdentifier',
Expand All @@ -307,6 +308,7 @@ test('getFieldsToNotClone', () => {
'catalogNumber',
'timestampModified',
'guid',
'isMemberOfCOG',
'text1',
'timestampCreated',
'totalCountAmt',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ const accessionsResponse = [
];

overrideAjax(
'/api/specify/accession/?domainfilter=false&addressofrecord=42&offset=0',
'/api/specify/accession/?addressofrecord=42&domainfilter=false&limit=0',
{
meta: { total_count: 2 },
objects: accessionsResponse,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -364,103 +364,4 @@ test('tableScoping', () =>
).toMatchSnapshot());

test('indexed fields are loaded', () =>
expect(tables.CollectionObject.field).toMatchInlineSnapshot(`
{
"accession": "[relationship CollectionObject.accession]",
"actualTotalCountAmt": "[literalField CollectionObject.actualTotalCountAmt]",
"agent1": "[relationship CollectionObject.agent1]",
"altCatalogNumber": "[literalField CollectionObject.altCatalogNumber]",
"appraisal": "[relationship CollectionObject.appraisal]",
"availability": "[literalField CollectionObject.availability]",
"catalogNumber": "[literalField CollectionObject.catalogNumber]",
"catalogedDate": "[literalField CollectionObject.catalogedDate]",
"catalogedDatePrecision": "[literalField CollectionObject.catalogedDatePrecision]",
"catalogedDateVerbatim": "[literalField CollectionObject.catalogedDateVerbatim]",
"cataloger": "[relationship CollectionObject.cataloger]",
"cojo": "[relationship CollectionObject.cojo]",
"collectingEvent": "[relationship CollectionObject.collectingEvent]",
"collection": "[relationship CollectionObject.collection]",
"collectionMemberId": "[literalField CollectionObject.collectionMemberId]",
"collectionObjectAttachments": "[relationship CollectionObject.collectionObjectAttachments]",
"collectionObjectAttribute": "[relationship CollectionObject.collectionObjectAttribute]",
"collectionObjectAttrs": "[relationship CollectionObject.collectionObjectAttrs]",
"collectionObjectCitations": "[relationship CollectionObject.collectionObjectCitations]",
"collectionObjectProperties": "[relationship CollectionObject.collectionObjectProperties]",
"collectionObjectType": "[relationship CollectionObject.collectionObjectType]",
"conservDescriptions": "[relationship CollectionObject.conservDescriptions]",
"container": "[relationship CollectionObject.container]",
"containerOwner": "[relationship CollectionObject.containerOwner]",
"countAmt": "[literalField CollectionObject.countAmt]",
"createdByAgent": "[relationship CollectionObject.createdByAgent]",
"currentDetermination": "[relationship CollectionObject.currentDetermination]",
"date1": "[literalField CollectionObject.date1]",
"date1Precision": "[literalField CollectionObject.date1Precision]",
"deaccessioned": "[literalField CollectionObject.deaccessioned]",
"description": "[literalField CollectionObject.description]",
"determinations": "[relationship CollectionObject.determinations]",
"dnaSequences": "[relationship CollectionObject.dnaSequences]",
"embargoAuthority": "[relationship CollectionObject.embargoAuthority]",
"embargoReason": "[literalField CollectionObject.embargoReason]",
"embargoReleaseDate": "[literalField CollectionObject.embargoReleaseDate]",
"embargoReleaseDatePrecision": "[literalField CollectionObject.embargoReleaseDatePrecision]",
"embargoStartDate": "[literalField CollectionObject.embargoStartDate]",
"embargoStartDatePrecision": "[literalField CollectionObject.embargoStartDatePrecision]",
"exsiccataItems": "[relationship CollectionObject.exsiccataItems]",
"fieldNotebookPage": "[relationship CollectionObject.fieldNotebookPage]",
"fieldNumber": "[literalField CollectionObject.fieldNumber]",
"guid": "[literalField CollectionObject.guid]",
"integer1": "[literalField CollectionObject.integer1]",
"integer2": "[literalField CollectionObject.integer2]",
"inventorizedBy": "[relationship CollectionObject.inventorizedBy]",
"inventoryDate": "[literalField CollectionObject.inventoryDate]",
"inventoryDatePrecision": "[literalField CollectionObject.inventoryDatePrecision]",
"leftSideRels": "[relationship CollectionObject.leftSideRels]",
"modifiedByAgent": "[relationship CollectionObject.modifiedByAgent]",
"modifier": "[literalField CollectionObject.modifier]",
"name": "[literalField CollectionObject.name]",
"notifications": "[literalField CollectionObject.notifications]",
"number1": "[literalField CollectionObject.number1]",
"number2": "[literalField CollectionObject.number2]",
"numberOfDuplicates": "[literalField CollectionObject.numberOfDuplicates]",
"objectCondition": "[literalField CollectionObject.objectCondition]",
"ocr": "[literalField CollectionObject.ocr]",
"otherIdentifiers": "[relationship CollectionObject.otherIdentifiers]",
"paleoContext": "[relationship CollectionObject.paleoContext]",
"preparations": "[relationship CollectionObject.preparations]",
"projectNumber": "[literalField CollectionObject.projectNumber]",
"projects": "[relationship CollectionObject.projects]",
"remarks": "[literalField CollectionObject.remarks]",
"reservedInteger3": "[literalField CollectionObject.reservedInteger3]",
"reservedInteger4": "[literalField CollectionObject.reservedInteger4]",
"reservedText": "[literalField CollectionObject.reservedText]",
"reservedText2": "[literalField CollectionObject.reservedText2]",
"reservedText3": "[literalField CollectionObject.reservedText3]",
"restrictions": "[literalField CollectionObject.restrictions]",
"rightSideRels": "[relationship CollectionObject.rightSideRels]",
"sgrStatus": "[literalField CollectionObject.sgrStatus]",
"text1": "[literalField CollectionObject.text1]",
"text2": "[literalField CollectionObject.text2]",
"text3": "[literalField CollectionObject.text3]",
"text4": "[literalField CollectionObject.text4]",
"text5": "[literalField CollectionObject.text5]",
"text6": "[literalField CollectionObject.text6]",
"text7": "[literalField CollectionObject.text7]",
"text8": "[literalField CollectionObject.text8]",
"timestampCreated": "[literalField CollectionObject.timestampCreated]",
"timestampModified": "[literalField CollectionObject.timestampModified]",
"totalCountAmt": "[literalField CollectionObject.totalCountAmt]",
"totalValue": "[literalField CollectionObject.totalValue]",
"treatmentEvents": "[relationship CollectionObject.treatmentEvents]",
"uniqueIdentifier": "[literalField CollectionObject.uniqueIdentifier]",
"version": "[literalField CollectionObject.version]",
"visibility": "[literalField CollectionObject.visibility]",
"visibilitySetBy": "[relationship CollectionObject.visibilitySetBy]",
"voucherRelationships": "[relationship CollectionObject.voucherRelationships]",
"yesNo1": "[literalField CollectionObject.yesNo1]",
"yesNo2": "[literalField CollectionObject.yesNo2]",
"yesNo3": "[literalField CollectionObject.yesNo3]",
"yesNo4": "[literalField CollectionObject.yesNo4]",
"yesNo5": "[literalField CollectionObject.yesNo5]",
"yesNo6": "[literalField CollectionObject.yesNo6]",
}
`));
expect(tables.CollectionObject.field).toMatchSnapshot());
109 changes: 108 additions & 1 deletion specifyweb/frontend/js_src/lib/components/DataModel/collectionApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

import _ from 'underscore';

import { removeKey } from '../../utils/utils';
import { assert } from '../Errors/assert';
import { Backbone } from './backbone';
import type { AnySchema } from './helperTypes';
import type { SpecifyResource } from './legacyTypes';

// REFACTOR: remove @ts-nocheck

Expand All @@ -14,6 +17,10 @@ const Base = Backbone.Collection.extend({
},
});

export const isRelationshipCollection = (value: unknown): boolean =>
value instanceof DependentCollection ||
value instanceof IndependentCollection;

function notSupported() {
throw new Error('method is not supported');
}
Expand Down Expand Up @@ -134,7 +141,7 @@ export const LazyCollection = Base.extend({
options.data =
options.data ||
_.extend({ domainfilter: this.domainfilter }, this.filters);
options.data.offset = this.length;
options.data.offset = options.offset || this.length;

_(options).has('limit') && (options.data.limit = options.limit);
this._fetch = Backbone.Collection.prototype.fetch.call(this, options);
Expand All @@ -154,6 +161,106 @@ export const LazyCollection = Base.extend({
},
});

export const IndependentCollection = LazyCollection.extend({
__name__: 'IndependentCollectionBase',
constructor(options, records = []) {
this.table = this.model;
assert(_.isArray(records));
Base.call(this, records, options);
this.filters = options.filters || {};
this.domainfilter =
Boolean(options.domainfilter) &&
this.model?.specifyTable.getScopingRelationship() !== undefined;

this.removed = new Set<string>();
this.updated = {};
},
initialize(_tables, options) {
this.on(
'change',
function (resource: SpecifyResource<AnySchema>) {
if (!resource.isBeingInitialized()) {
this.updated[resource.cid] = resource;
this.trigger('saverequired');
}
},
this
);

this.on(
'add',
function (resource: SpecifyResource<AnySchema>) {
if (!resource.isNew()) {
(this.removed as Set<string>).delete(resource.url());
this.updated[resource.cid] = resource.url();
} else {
this.updated[resource.cid] = resource;
}
this._totalCount += 1;
this.trigger('saverequired');
},
this
);

this.on(
'remove',
function (resource: SpecifyResource<AnySchema>) {
if (!resource.isNew()) {
(this.removed as Set<string>).add(resource.url());
}
this.updated = removeKey(this.updated, resource.cid);
this._totalCount -= 1;
this.trigger('saverequired');
},
this
);

this.listenTo(options.related, 'saved', function () {
this.updated = {};
this.removed = new Set<string>();
});

setupToOne(this, options);
},
parse(resp) {
const self = this;
const records = Reflect.apply(
LazyCollection.prototype.parse,
this,
arguments
);

this._totalCount -= (this.removed as Set<string>).size;

return records.filter(
({ resource_uri }) => !(this.removed as Set<string>).has(resource_uri)
);
},
async fetch(options) {
if (this.related.isBeingInitialized()) {
return this;
}
this.filters[this.field.name.toLowerCase()] = this.related.id;

const offset =
this.length === 0 && this.removed.size > 0
? this.removed.size
: this.length;

options = { ...(options ?? {}), silent: true, offset };

return Reflect.apply(LazyCollection.prototype.fetch, this, [options]);
melton-jason marked this conversation as resolved.
Show resolved Hide resolved
},
toApiJSON(options) {
const self = this;

return {
update: Object.values(this.updated),
remove: Array.from(self.removed),
};
},
});

export const ToOneCollection = LazyCollection.extend({
__name__: 'LazyToOneCollectionBase',
initialize(_models, options) {
Expand Down
Loading
Loading