From 7d2a86d32727579c36f7bbf4e87c1e58be8e57c5 Mon Sep 17 00:00:00 2001 From: Sharad S Date: Thu, 17 Apr 2025 14:37:55 -0400 Subject: [PATCH 1/7] Add rolledback to SpDataset --- .../js_src/lib/components/WbActions/index.tsx | 5 +++++ .../lib/components/WbPlanView/Wrapped.tsx | 1 + .../js_src/lib/components/WorkBench/WbView.tsx | 10 +++++++++- .../migrations/0007_spdataset_rolledback.py | 18 ++++++++++++++++++ specifyweb/workbench/models.py | 2 ++ specifyweb/workbench/tasks.py | 3 ++- 6 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 specifyweb/workbench/migrations/0007_spdataset_rolledback.py diff --git a/specifyweb/frontend/js_src/lib/components/WbActions/index.tsx b/specifyweb/frontend/js_src/lib/components/WbActions/index.tsx index ac6afb9ca41..70e40984783 100644 --- a/specifyweb/frontend/js_src/lib/components/WbActions/index.tsx +++ b/specifyweb/frontend/js_src/lib/components/WbActions/index.tsx @@ -58,6 +58,7 @@ export function WbActions({ useBooleanState(); const [operationCompleted, openOperationCompleted, closeOperationCompleted] = useBooleanState(); + const { mode, refreshInitiatorAborted, startUpload, triggerStatusComponent } = useWbActions({ datasetId: dataset.id, @@ -262,6 +263,10 @@ function useWbActions({ const refreshInitiatorAborted = React.useRef(false); const loading = React.useContext(LoadingContext); + /** + * NOTE: Only validate and upload use startUpload + * For rollback, we directly call the API inside the RollbackConfirmation component + */ const startUpload = (newMode: WbStatus): void => { workbench.validation.stopLiveValidation(); loading( diff --git a/specifyweb/frontend/js_src/lib/components/WbPlanView/Wrapped.tsx b/specifyweb/frontend/js_src/lib/components/WbPlanView/Wrapped.tsx index 12da809ef87..505ec512d1d 100644 --- a/specifyweb/frontend/js_src/lib/components/WbPlanView/Wrapped.tsx +++ b/specifyweb/frontend/js_src/lib/components/WbPlanView/Wrapped.tsx @@ -78,6 +78,7 @@ export type Dataset = DatasetBase & readonly uploadplan: UploadPlan | null; readonly visualorder: RA | null; readonly isupdate: boolean; + readonly rolledback: boolean; }; /** diff --git a/specifyweb/frontend/js_src/lib/components/WorkBench/WbView.tsx b/specifyweb/frontend/js_src/lib/components/WorkBench/WbView.tsx index c502c2d6abf..c67ac760f4b 100644 --- a/specifyweb/frontend/js_src/lib/components/WorkBench/WbView.tsx +++ b/specifyweb/frontend/js_src/lib/components/WorkBench/WbView.tsx @@ -169,9 +169,17 @@ export function WbView({ const searchRef = React.useRef(null); + const hasBatchEditRolledBack = dataset.rolledback && dataset.isupdate; + return (
None: unupload_dataset(ds, agent, progress) ds.uploaderstatus = None - ds.save(update_fields=['uploaderstatus']) \ No newline at end of file + ds.rolledback = True + ds.save(update_fields=['uploaderstatus', 'rolledback']) \ No newline at end of file From e58c3348fb5767f18e1b4cba228ea4fff37882b5 Mon Sep 17 00:00:00 2001 From: Sharad S Date: Thu, 17 Apr 2025 20:08:12 +0000 Subject: [PATCH 2/7] Lint code with ESLint and Prettier Triggered by 7d2a86d32727579c36f7bbf4e87c1e58be8e57c5 on branch refs/heads/issue-6390 --- .../components/WbPlanView/uploadPlanBuilder.ts | 2 +- .../components/WbPlanView/uploadPlanParser.ts | 4 ++-- .../lib/components/WorkBench/resultsParser.ts | 16 +++++++++++++++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/WbPlanView/uploadPlanBuilder.ts b/specifyweb/frontend/js_src/lib/components/WbPlanView/uploadPlanBuilder.ts index e726fbe1015..960b9a54fa3 100644 --- a/specifyweb/frontend/js_src/lib/components/WbPlanView/uploadPlanBuilder.ts +++ b/specifyweb/frontend/js_src/lib/components/WbPlanView/uploadPlanBuilder.ts @@ -7,7 +7,7 @@ import type { Tables } from '../DataModel/types'; import { getTreeDefinitions, isTreeTable } from '../InitialContext/treeRanks'; import { defaultColumnOptions } from './linesGetter'; import type { BatchEditPrefs } from './Mapper'; -import type { SplitMappingPath} from './mappingHelpers'; +import type { SplitMappingPath } from './mappingHelpers'; import { valueIsTreeMeta } from './mappingHelpers'; import { getNameFromTreeDefinitionName, diff --git a/specifyweb/frontend/js_src/lib/components/WbPlanView/uploadPlanParser.ts b/specifyweb/frontend/js_src/lib/components/WbPlanView/uploadPlanParser.ts index 70c077fc0a7..d6b4960370d 100644 --- a/specifyweb/frontend/js_src/lib/components/WbPlanView/uploadPlanParser.ts +++ b/specifyweb/frontend/js_src/lib/components/WbPlanView/uploadPlanParser.ts @@ -1,16 +1,16 @@ import type { IR, PartialBy, RA, RR } from '../../utils/types'; -import { AnyTree } from '../DataModel/helperTypes'; +import type { AnyTree } from '../DataModel/helperTypes'; import type { SpecifyTable } from '../DataModel/specifyTable'; import { strictGetTable } from '../DataModel/tables'; import type { Tables } from '../DataModel/types'; import { softFail } from '../Errors/Crash'; import { getTreeDefinitions } from '../InitialContext/treeRanks'; import { defaultColumnOptions } from './linesGetter'; +import type { BatchEditPrefs, MappingPath } from './Mapper'; import type { SplitMappingPath } from './mappingHelpers'; import { formatTreeDefinition } from './mappingHelpers'; import { formatToManyIndex, formatTreeRank } from './mappingHelpers'; import { RANK_KEY_DELIMITER } from './uploadPlanBuilder'; -import type { BatchEditPrefs, MappingPath } from './Mapper'; export type MatchBehaviors = 'ignoreAlways' | 'ignoreNever' | 'ignoreWhenBlank'; diff --git a/specifyweb/frontend/js_src/lib/components/WorkBench/resultsParser.ts b/specifyweb/frontend/js_src/lib/components/WorkBench/resultsParser.ts index 0df58ddfca6..8c1dc83d106 100644 --- a/specifyweb/frontend/js_src/lib/components/WorkBench/resultsParser.ts +++ b/specifyweb/frontend/js_src/lib/components/WorkBench/resultsParser.ts @@ -154,7 +154,21 @@ type PropagatedFailure = State<'PropagatedFailure'>; type MatchedAndChanged = State<'MatchedAndChanged', Omit>; type RecordResultTypes = - Deleted | Deleted | FailedBusinessRule | Matched | MatchedAndChanged | MatchedAndChanged | MatchedMultiple | NoChange | NoChange | NoMatch | NullRecord | ParseFailures | PropagatedFailure | Updated | Uploaded; + | Deleted + | Deleted + | FailedBusinessRule + | Matched + | MatchedAndChanged + | MatchedAndChanged + | MatchedMultiple + | NoChange + | NoChange + | NoMatch + | NullRecord + | ParseFailures + | PropagatedFailure + | Updated + | Uploaded; // Records the specific result of attempting to upload a particular record type WbRecordResult = { From 3ba2786fff4290a2b31850d8de174953f52b90e2 Mon Sep 17 00:00:00 2001 From: Sharad S Date: Thu, 17 Apr 2025 16:58:29 -0400 Subject: [PATCH 3/7] Add text to indicate dataset cannot be edited --- .../js_src/lib/components/WorkBench/DataSetMeta.tsx | 6 ++++++ specifyweb/frontend/js_src/lib/localization/batchEdit.ts | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/specifyweb/frontend/js_src/lib/components/WorkBench/DataSetMeta.tsx b/specifyweb/frontend/js_src/lib/components/WorkBench/DataSetMeta.tsx index f2097e632ed..ea9c9ca54b5 100644 --- a/specifyweb/frontend/js_src/lib/components/WorkBench/DataSetMeta.tsx +++ b/specifyweb/frontend/js_src/lib/components/WorkBench/DataSetMeta.tsx @@ -4,6 +4,7 @@ import type { LocalizedString } from 'typesafe-i18n'; import { useBooleanState } from '../../hooks/useBooleanState'; import { useId } from '../../hooks/useId'; +import { batchEditText } from '../../localization/batchEdit'; import { commonText } from '../../localization/common'; import { StringToJsx } from '../../localization/utils'; import { wbText } from '../../localization/workbench'; @@ -376,6 +377,11 @@ export function DataSetName({ {dataset.uploadresult?.success === true && ( {wbText.dataSetUploadedLabel()} )} + {dataset.isupdate && dataset.rolledback && ( + + {batchEditText.cannotEditAfterRollback()} + + )} {getField(tables.WorkbenchTemplateMappingItem, 'metaData').label} diff --git a/specifyweb/frontend/js_src/lib/localization/batchEdit.ts b/specifyweb/frontend/js_src/lib/localization/batchEdit.ts index 0a4f6018582..8e699df85f4 100644 --- a/specifyweb/frontend/js_src/lib/localization/batchEdit.ts +++ b/specifyweb/frontend/js_src/lib/localization/batchEdit.ts @@ -109,4 +109,8 @@ export const batchEditText = createDictionary({ 'en-us': 'Batch Edit is disabled for system tables and scoping hierarchy tables', }, + cannotEditAfterRollback: { + 'en-us': + '(Batch Edit datasets cannot be edited after rollback - Read Only)', + }, } as const); From aea65bfaa95647f93f96bbc23c2f2024d450517d Mon Sep 17 00:00:00 2001 From: Sharad S Date: Thu, 17 Apr 2025 16:59:22 -0400 Subject: [PATCH 4/7] Make hot columns readonly based on context --- .../js_src/lib/components/WorkBench/WbSpreadsheet.tsx | 4 +++- .../js_src/lib/components/WorkBench/WbView.tsx | 1 + .../js_src/lib/components/WorkBench/hotProps.tsx | 10 ++++++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/WorkBench/WbSpreadsheet.tsx b/specifyweb/frontend/js_src/lib/components/WorkBench/WbSpreadsheet.tsx index d9d57c0dabf..404bbddf63c 100644 --- a/specifyweb/frontend/js_src/lib/components/WorkBench/WbSpreadsheet.tsx +++ b/specifyweb/frontend/js_src/lib/components/WorkBench/WbSpreadsheet.tsx @@ -41,6 +41,7 @@ function WbSpreadsheetComponent({ workbench, mappings, isResultsOpen, + hasBatchEditRolledBack, checkDeletedFail, spreadsheetChanged, onClickDisambiguate: handleClickDisambiguate, @@ -53,6 +54,7 @@ function WbSpreadsheetComponent({ readonly workbench: Workbench; readonly mappings: WbMapping | undefined; readonly isResultsOpen: boolean; + readonly hasBatchEditRolledBack: boolean; readonly checkDeletedFail: (statusCode: number) => boolean; readonly spreadsheetChanged: () => void; readonly onClickDisambiguate: () => void; @@ -173,7 +175,7 @@ function WbSpreadsheetComponent({ }; React.useEffect(() => { - if (hot === undefined) return; + if (hot === undefined || hasBatchEditRolledBack) return; hot.batch(() => { (mappings === undefined ? Promise.resolve({}) diff --git a/specifyweb/frontend/js_src/lib/components/WorkBench/WbView.tsx b/specifyweb/frontend/js_src/lib/components/WorkBench/WbView.tsx index c67ac760f4b..d684ecbbbf9 100644 --- a/specifyweb/frontend/js_src/lib/components/WorkBench/WbView.tsx +++ b/specifyweb/frontend/js_src/lib/components/WorkBench/WbView.tsx @@ -239,6 +239,7 @@ export function WbView({ hot={hot} isResultsOpen={showResults} isUploaded={isUploaded} + hasBatchEditRolledBack={hasBatchEditRolledBack} mappings={mappings} setHotTable={setHotTable} spreadsheetChanged={spreadsheetChanged} diff --git a/specifyweb/frontend/js_src/lib/components/WorkBench/hotProps.tsx b/specifyweb/frontend/js_src/lib/components/WorkBench/hotProps.tsx index d3a06cbbb2d..c156a0ed532 100644 --- a/specifyweb/frontend/js_src/lib/components/WorkBench/hotProps.tsx +++ b/specifyweb/frontend/js_src/lib/components/WorkBench/hotProps.tsx @@ -3,6 +3,7 @@ import ReactDOMServer from 'react-dom/server'; import { wbPlanText } from '../../localization/wbPlan'; import { icons } from '../Atoms/Icons'; +import { ReadOnlyContext } from '../Core/Contexts'; import { TableIcon } from '../Molecules/TableIcon'; import { userPreferences } from '../Preferences/userPreferences'; import type { Dataset } from '../WbPlanView/Wrapped'; @@ -26,6 +27,7 @@ export function useHotProps({ readonly mappings: WbMapping | undefined; readonly physicalColToMappingCol: (physicalCol: number) => number | undefined; }) { + const isReadOnly = React.useContext(ReadOnlyContext); const [autoWrapCol] = userPreferences.use( 'workBench', 'editor', @@ -46,12 +48,12 @@ export function useHotProps({ (_, physicalCol) => ({ // Get data from nth column for nth column data: physicalCol, - readOnly: [-1, undefined].includes( - physicalColToMappingCol(physicalCol) - ), + readOnly: + isReadOnly || + [-1, undefined].includes(physicalColToMappingCol(physicalCol)), }) ), - [dataset.columns.length] + [dataset.columns.length, isReadOnly] ); const [enterMovesPref] = userPreferences.use( From 0b1fb91edbed81db9f806369da9c8cddf346d6fc Mon Sep 17 00:00:00 2001 From: Sharad S Date: Thu, 17 Apr 2025 21:03:13 +0000 Subject: [PATCH 5/7] Lint code with ESLint and Prettier Triggered by dcbb5932fbdd9e25ac6decaf89e81ee244a2449d on branch refs/heads/issue-6390 --- specifyweb/frontend/js_src/lib/components/WorkBench/WbView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specifyweb/frontend/js_src/lib/components/WorkBench/WbView.tsx b/specifyweb/frontend/js_src/lib/components/WorkBench/WbView.tsx index d684ecbbbf9..c03ba4b5d2a 100644 --- a/specifyweb/frontend/js_src/lib/components/WorkBench/WbView.tsx +++ b/specifyweb/frontend/js_src/lib/components/WorkBench/WbView.tsx @@ -236,10 +236,10 @@ export function WbView({ checkDeletedFail={checkDeletedFail} data={data} dataset={dataset} + hasBatchEditRolledBack={hasBatchEditRolledBack} hot={hot} isResultsOpen={showResults} isUploaded={isUploaded} - hasBatchEditRolledBack={hasBatchEditRolledBack} mappings={mappings} setHotTable={setHotTable} spreadsheetChanged={spreadsheetChanged} From fc82377e427ffeec5defe6b5033dcab3d0095639 Mon Sep 17 00:00:00 2001 From: Sharad S Date: Tue, 22 Apr 2025 15:26:26 -0400 Subject: [PATCH 6/7] Reorder migration --- ...007_spdataset_rolledback.py => 0008_spdataset_rolledback.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename specifyweb/workbench/migrations/{0007_spdataset_rolledback.py => 0008_spdataset_rolledback.py} (87%) diff --git a/specifyweb/workbench/migrations/0007_spdataset_rolledback.py b/specifyweb/workbench/migrations/0008_spdataset_rolledback.py similarity index 87% rename from specifyweb/workbench/migrations/0007_spdataset_rolledback.py rename to specifyweb/workbench/migrations/0008_spdataset_rolledback.py index 3fc370bb730..431fd957b5e 100644 --- a/specifyweb/workbench/migrations/0007_spdataset_rolledback.py +++ b/specifyweb/workbench/migrations/0008_spdataset_rolledback.py @@ -6,7 +6,7 @@ class Migration(migrations.Migration): dependencies = [ - ('workbench', '0006_batch_edit'), + ('workbench', '0007_spdatasetattachment'), ] operations = [ From 0de10194cbfbe0c376dedfc753f9f904b3fd91ac Mon Sep 17 00:00:00 2001 From: Sharad S Date: Wed, 23 Apr 2025 15:26:52 -0400 Subject: [PATCH 7/7] Upgrade celery and its dependencies --- requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 85dcd6373a8..922ec732a82 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,8 +2,8 @@ setuptools>=50.0.0 tzdata wheel backports.zoneinfo==0.2.1 -kombu==5.2.4 -celery[redis]==5.2.7 +kombu==5.5.2 +celery[redis]==5.5.1 Django==4.2.18 mysqlclient==2.1.1 SQLAlchemy==1.2.11 @@ -12,4 +12,4 @@ pycryptodome==3.21.0 PyJWT==2.3.0 django-auth-ldap==1.2.17 jsonschema==3.2.0 -typing-extensions==4.3.0 +typing-extensions==4.12.2