Skip to content

IN-1672-base-metadata-transformer - #268

Merged
ehanson8 merged 4 commits into
mainfrom
IN-1672-base-metadata-transformer
Sep 17, 2026
Merged

ehanson8 merged 4 commits into
mainfrom
IN-1672-base-metadata-transformer

Conversation

@ehanson8

@ehanson8 ehanson8 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Purpose and background context

This PR creates a Protocol-based MetadataTransformer class as a parent class for all transformers. This is needed given the differing inputs to the transform methods on each transformer class (e.g. XML string, JSON). Eventually all workflows will use a transformer class and the metadata mapping path will be removed in DSO-64.

It also creates a BaseTransformer class that corresponds to the sccs/simple_csv workflow.

How can a reviewer manually see the effects of these changes?

Due to continued issues with our MIT Open Scholarship test instance, a Docker container was used for local DSpace testing. Tests were performed for several workflows to confirm they still produced metadata that could be ingested by DSpace. Some were not possible to test with reasons noted below:

SCCS
Create

Submit

Finalize

OpenCourseWare
Create

Submit

Finalize

Wiley
Only create has been implemented which doesn't use the transformer class but this test shows it still functions.
Create

Digitized Theses
Can't be tested given that the workflow reaches out to the DSpace instance during the create step

DSS logs for ingesting SCCS and OpenCourseWare in the local DSpace 8 instance

2026-09-10 13:52:48,946 INFO submitter.cli.main(): Logger 'root' configured with level=INFO
2026-09-10 13:52:48,946 INFO submitter.config.configure_sentry(): No Sentry DSN found, exceptions will not be sent to Sentry
2026-09-10 13:52:48,947 INFO submitter.cli.start(): Starting processing messages from queue dss-input-dsc-dev
2026-09-10 13:52:48,947 INFO submitter.sqs.message_loop(): Message loop started
2026-09-10 13:52:49,217 INFO submitter.sqs.retrieve_messages_from_queue(): Polling queue dss-input-dsc-dev for messages
2026-09-10 13:52:49,277 INFO submitter.sqs.retrieve_messages_from_queue(): 2 messages received
2026-09-10 13:52:49,277 INFO submitter.sqs.process(): Processing message 'fe8be9ae-dc2f-4637-a8dd-e50f097b6e62' from queue 'dss-input-dsc-dev'
2026-09-10 13:52:49,325 INFO root.authenticate_with_credentials(): Authenticated successfully as test@test.edu
2026-09-10 13:52:49,325 INFO submitter.submission._create_dspace_client(): Successfully authenticated to "http://localhost:8080/server/api" as "test@test.edu"
2026-09-10 13:52:49,667 INFO root.create_dso(): item 6f0a1323-dc37-4980-8684-9bb9c1c79e98 created successfully!
2026-09-10 13:52:49,667 INFO submitter.submission._create_item(): Item created with handle: 123456789/91
2026-09-10 13:52:49,728 INFO submitter.submission._create_bundle(): Bundle created with UUID: f3769d50-1e5b-4a65-8e14-aecd79e26535
2026-09-10 13:52:50,528 INFO submitter.submission._create_bitstream(): Bitstream created with UUID: 62a0e58a-e02d-4ed9-b9a3-5bf29d897560
2026-09-10 13:52:50,724 INFO submitter.sqs.process(): Deleted message 'fe8be9ae-dc2f-4637-a8dd-e50f097b6e62' from input queue
2026-09-10 13:52:50,724 INFO submitter.sqs.process(): Processing message '49be440a-21cd-4c02-8cbf-460d0c01a7fa' from queue 'dss-input-dsc-dev'
2026-09-10 13:52:51,064 INFO root.create_dso(): item dc5d929f-b256-45d0-84d0-0991584ded1a created successfully!
2026-09-10 13:52:51,064 INFO submitter.submission._create_item(): Item created with handle: 123456789/92
2026-09-10 13:52:51,122 INFO submitter.submission._create_bundle(): Bundle created with UUID: d7319470-d8b9-4e2f-b494-9cfddd971f2b
2026-09-10 13:52:51,917 INFO submitter.submission._create_bitstream(): Bitstream created with UUID: e31cf275-23ce-4885-a3ea-4a69d38604c8
2026-09-10 13:52:51,988 INFO submitter.sqs.process(): Deleted message '49be440a-21cd-4c02-8cbf-460d0c01a7fa' from input queue
2026-09-10 13:52:51,988 INFO submitter.sqs.retrieve_messages_from_queue(): Polling queue dss-input-dsc-dev for messages
2026-09-10 13:52:52,049 INFO submitter.sqs.retrieve_messages_from_queue(): 1 messages received
2026-09-10 13:52:52,049 INFO submitter.sqs.process(): Processing message '387d071a-b270-48b5-8e3c-718ef559e967' from queue 'dss-input-dsc-dev'
2026-09-10 13:52:52,342 INFO root.create_dso(): item 892de414-0d50-4103-a9e7-a042cd84b8c0 created successfully!
2026-09-10 13:52:52,342 INFO submitter.submission._create_item(): Item created with handle: 123456789/93
2026-09-10 13:52:52,394 INFO submitter.submission._create_bundle(): Bundle created with UUID: 21c508cd-a7d4-4ced-b10e-662631b2e47a
2026-09-10 13:52:53,155 INFO submitter.submission._create_bitstream(): Bitstream created with UUID: f7f93d34-4b27-4ffb-96ad-df7d6f745928
2026-09-10 13:52:53,235 INFO submitter.sqs.process(): Deleted message '387d071a-b270-48b5-8e3c-718ef559e967' from input queue
2026-09-10 13:52:53,235 INFO submitter.sqs.retrieve_messages_from_queue(): Polling queue dss-input-dsc-dev for messages
2026-09-10 13:52:53,298 INFO submitter.sqs.retrieve_messages_from_queue(): 1 messages received
2026-09-10 13:52:53,298 INFO submitter.sqs.process(): Processing message '34b10f23-ab96-40e7-b3b4-e9defeb49865' from queue 'dss-input-dsc-dev'
2026-09-10 13:52:53,594 INFO root.create_dso(): item 4023db4f-c5cb-442c-92d8-87d0d69c2082 created successfully!
2026-09-10 13:52:53,595 INFO submitter.submission._create_item(): Item created with handle: 123456789/94
2026-09-10 13:52:53,641 INFO submitter.submission._create_bundle(): Bundle created with UUID: d4a09f8f-38d7-468c-a801-bfa7356507a2
2026-09-10 13:52:55,884 INFO submitter.submission._create_bitstream(): Bitstream created with UUID: 569b5213-c18d-41a6-9275-8fc3de856d2b
2026-09-10 13:52:55,968 INFO submitter.sqs.process(): Deleted message '34b10f23-ab96-40e7-b3b4-e9defeb49865' from input queue
2026-09-10 13:52:55,968 INFO submitter.sqs.retrieve_messages_from_queue(): Polling queue dss-input-dsc-dev for messages
2026-09-10 13:52:56,059 INFO submitter.sqs.retrieve_messages_from_queue(): 1 messages received
2026-09-10 13:52:56,059 INFO submitter.sqs.process(): Processing message 'c363bb94-d8b1-4cb9-9898-79ea7ce833f9' from queue 'dss-input-dsc-dev'
2026-09-10 13:52:56,362 INFO root.create_dso(): item 075c9664-3d17-4137-88dd-3fff2dfcb957 created successfully!
2026-09-10 13:52:56,362 INFO submitter.submission._create_item(): Item created with handle: 123456789/95
2026-09-10 13:52:56,418 INFO submitter.submission._create_bundle(): Bundle created with UUID: 1702cf35-8d5e-4ab5-870f-1c8cbeeb2728
2026-09-10 13:52:57,184 INFO submitter.submission._create_bitstream(): Bitstream created with UUID: da9c7378-4d24-4518-aa54-886b97c82c31
2026-09-10 13:52:57,273 INFO submitter.sqs.process(): Deleted message 'c363bb94-d8b1-4cb9-9898-79ea7ce833f9' from input queue
2026-09-10 13:52:57,273 INFO submitter.sqs.retrieve_messages_from_queue(): Polling queue dss-input-dsc-dev for messages
2026-09-10 13:53:17,419 INFO submitter.sqs.retrieve_messages_from_queue(): 0 messages received
2026-09-10 13:53:17,419 INFO submitter.sqs.message_loop(): No messages available in queue dss-input-dsc-dev
2026-09-10 13:53:17,420 INFO submitter.cli.start(): Completed processing messages from queue dss-input-dsc-dev
ehanson@JYHL47HX0D dspace-submission-service % uv run --env-file=.env submitter start --queue dss-input-dsc-dev
2026-09-10 14:35:27,892 INFO submitter.cli.main(): Logger 'root' configured with level=INFO
2026-09-10 14:35:27,892 INFO submitter.config.configure_sentry(): No Sentry DSN found, exceptions will not be sent to Sentry
2026-09-10 14:35:27,892 INFO submitter.cli.start(): Starting processing messages from queue dss-input-dsc-dev
2026-09-10 14:35:27,892 INFO submitter.sqs.message_loop(): Message loop started
2026-09-10 14:35:28,396 INFO submitter.sqs.retrieve_messages_from_queue(): Polling queue dss-input-dsc-dev for messages
2026-09-10 14:35:28,460 INFO submitter.sqs.retrieve_messages_from_queue(): 2 messages received
2026-09-10 14:35:28,461 INFO submitter.sqs.process(): Processing message 'd875814b-44c5-49ba-8760-4c1e0d4e959d' from queue 'dss-input-dsc-dev'
2026-09-10 14:35:28,557 INFO root.authenticate_with_credentials(): Authenticated successfully as test@test.edu
2026-09-10 14:35:28,557 INFO submitter.submission._create_dspace_client(): Successfully authenticated to "http://localhost:8080/server/api" as "test@test.edu"
2026-09-10 14:35:28,966 INFO root.create_dso(): item 154372fa-383e-4433-9cee-2a8b46bd0a23 created successfully!
2026-09-10 14:35:28,966 INFO submitter.submission._create_item(): Item created with handle: 123456789/96
2026-09-10 14:35:29,043 INFO submitter.submission._create_bundle(): Bundle created with UUID: cae267ea-49eb-4c69-a8c2-00dd8e063d34
2026-09-10 14:35:29,527 INFO submitter.submission._create_bitstream(): Bitstream created with UUID: d25ae443-8295-4193-8e08-1bb8030bbe60
2026-09-10 14:35:29,726 INFO submitter.sqs.process(): Deleted message 'd875814b-44c5-49ba-8760-4c1e0d4e959d' from input queue
2026-09-10 14:35:29,727 INFO submitter.sqs.process(): Processing message 'e4ca2b31-9d4f-4f00-8b6b-6f6fd19382e9' from queue 'dss-input-dsc-dev'
2026-09-10 14:35:30,043 INFO root.create_dso(): item 3afa2e82-a2d4-407c-a5ab-a4a76b86d81d created successfully!
2026-09-10 14:35:30,044 INFO submitter.submission._create_item(): Item created with handle: 123456789/97
2026-09-10 14:35:30,098 INFO submitter.submission._create_bundle(): Bundle created with UUID: a34b6f4b-5a8d-469d-bd6e-3d853af0c9e9
2026-09-10 14:35:30,517 INFO submitter.submission._create_bitstream(): Bitstream created with UUID: 2a86d758-fa72-485b-ad39-84b3ad920959
2026-09-10 14:35:30,619 INFO submitter.sqs.process(): Deleted message 'e4ca2b31-9d4f-4f00-8b6b-6f6fd19382e9' from input queue
2026-09-10 14:35:30,619 INFO submitter.sqs.retrieve_messages_from_queue(): Polling queue dss-input-dsc-dev for messages
2026-09-10 14:35:30,685 INFO submitter.sqs.retrieve_messages_from_queue(): 4 messages received
2026-09-10 14:35:30,686 INFO submitter.sqs.process(): Processing message 'b7e82f1f-1624-42a6-a73b-2d644cc76877' from queue 'dss-input-dsc-dev'
2026-09-10 14:35:31,024 INFO root.create_dso(): item 49792816-e75f-4552-8322-a654e9897c80 created successfully!
2026-09-10 14:35:31,024 INFO submitter.submission._create_item(): Item created with handle: 123456789/98
2026-09-10 14:35:31,084 INFO submitter.submission._create_bundle(): Bundle created with UUID: 79e22ce4-f258-4fbf-bd3a-604c17800e79
2026-09-10 14:35:31,953 INFO submitter.submission._create_bitstream(): Bitstream created with UUID: 1d60b4c7-9e09-4a3d-8a36-44c2fbff0ebe
2026-09-10 14:35:32,024 INFO submitter.sqs.process(): Deleted message 'b7e82f1f-1624-42a6-a73b-2d644cc76877' from input queue
2026-09-10 14:35:32,025 INFO submitter.sqs.process(): Processing message '837a6733-ea5b-4a67-ba6a-07352ae95a98' from queue 'dss-input-dsc-dev'
2026-09-10 14:35:32,603 INFO root.create_dso(): item e7268451-493b-41c8-ad38-d1eb24bcdb55 created successfully!
2026-09-10 14:35:32,603 INFO submitter.submission._create_item(): Item created with handle: 123456789/99
2026-09-10 14:35:32,650 INFO submitter.submission._create_bundle(): Bundle created with UUID: e0cecb17-ee76-4f3f-a214-569082d1d8b8
2026-09-10 14:35:33,395 INFO submitter.submission._create_bitstream(): Bitstream created with UUID: 4e9a7016-fe75-4a37-9bc8-6c441366d1a8
2026-09-10 14:35:33,474 INFO submitter.sqs.process(): Deleted message '837a6733-ea5b-4a67-ba6a-07352ae95a98' from input queue
2026-09-10 14:35:33,475 INFO submitter.sqs.process(): Processing message '4bf4cf5b-3800-498e-b190-5a44ea2556ca' from queue 'dss-input-dsc-dev'
2026-09-10 14:35:33,765 INFO root.create_dso(): item 135e3af0-67e2-4140-ab55-76412bb57542 created successfully!
2026-09-10 14:35:33,765 INFO submitter.submission._create_item(): Item created with handle: 123456789/100
2026-09-10 14:35:33,810 INFO submitter.submission._create_bundle(): Bundle created with UUID: 75d53295-b606-4099-9f58-ef0ac17b8b70
2026-09-10 14:35:34,217 INFO submitter.submission._create_bitstream(): Bitstream created with UUID: f8a494bd-e4f7-4f15-9f5f-6f6ef09ffab1
2026-09-10 14:35:34,291 INFO submitter.sqs.process(): Deleted message '4bf4cf5b-3800-498e-b190-5a44ea2556ca' from input queue
2026-09-10 14:35:34,291 INFO submitter.sqs.process(): Processing message '7eef9963-f100-497b-a8df-55fe947b9360' from queue 'dss-input-dsc-dev'
2026-09-10 14:35:34,572 INFO root.create_dso(): item a6ba8a88-6b69-4062-8368-acbf7096c2ec created successfully!
2026-09-10 14:35:34,572 INFO submitter.submission._create_item(): Item created with handle: 123456789/101
2026-09-10 14:35:34,618 INFO submitter.submission._create_bundle(): Bundle created with UUID: e0c0563e-ec6f-46cd-9f46-7bde157111c9
2026-09-10 14:35:35,049 INFO submitter.submission._create_bitstream(): Bitstream created with UUID: 58cbc214-653b-47c7-bed6-b63de5f293aa
2026-09-10 14:35:35,120 INFO submitter.sqs.process(): Deleted message '7eef9963-f100-497b-a8df-55fe947b9360' from input queue
2026-09-10 14:35:35,120 INFO submitter.sqs.retrieve_messages_from_queue(): Polling queue dss-input-dsc-dev for messages
2026-09-10 14:35:35,191 INFO submitter.sqs.retrieve_messages_from_queue(): 1 messages received
2026-09-10 14:35:35,191 INFO submitter.sqs.process(): Processing message '80d0c104-3e36-4452-8279-ceed24b7ae38' from queue 'dss-input-dsc-dev'
2026-09-10 14:35:35,485 INFO root.create_dso(): item cd59d4df-1f78-4cb5-9016-4eaa754920f3 created successfully!
2026-09-10 14:35:35,485 INFO submitter.submission._create_item(): Item created with handle: 123456789/102
2026-09-10 14:35:35,530 INFO submitter.submission._create_bundle(): Bundle created with UUID: 1b9dfff3-9215-43ec-9323-b850071ac9b6
2026-09-10 14:35:36,367 INFO submitter.submission._create_bitstream(): Bitstream created with UUID: b3290f85-8ae9-4853-aad0-465e92cff742
2026-09-10 14:35:36,453 INFO submitter.sqs.process(): Deleted message '80d0c104-3e36-4452-8279-ceed24b7ae38' from input queue
2026-09-10 14:35:36,453 INFO submitter.sqs.retrieve_messages_from_queue(): Polling queue dss-input-dsc-dev for messages
2026-09-10 14:35:36,519 INFO submitter.sqs.retrieve_messages_from_queue(): 1 messages received
2026-09-10 14:35:36,519 INFO submitter.sqs.process(): Processing message '404ac501-1639-4e05-becf-a643035d5d6c' from queue 'dss-input-dsc-dev'
2026-09-10 14:35:36,821 INFO root.create_dso(): item 399d3194-7f64-411e-b979-b96df07b9454 created successfully!
2026-09-10 14:35:36,821 INFO submitter.submission._create_item(): Item created with handle: 123456789/103
2026-09-10 14:35:36,867 INFO submitter.submission._create_bundle(): Bundle created with UUID: 054c5dc8-692b-4d72-8b72-ceb04c156db4
2026-09-10 14:35:37,975 INFO submitter.submission._create_bitstream(): Bitstream created with UUID: a2bc4fe7-f556-4148-9b5c-3d9847988a17
2026-09-10 14:35:38,052 INFO submitter.sqs.process(): Deleted message '404ac501-1639-4e05-becf-a643035d5d6c' from input queue
2026-09-10 14:35:38,052 INFO submitter.sqs.retrieve_messages_from_queue(): Polling queue dss-input-dsc-dev for messages
2026-09-10 14:35:38,128 INFO submitter.sqs.retrieve_messages_from_queue(): 2 messages received
2026-09-10 14:35:38,128 INFO submitter.sqs.process(): Processing message '6328d637-d868-4bb8-8b26-045ea95f7923' from queue 'dss-input-dsc-dev'
2026-09-10 14:35:38,423 INFO root.create_dso(): item 557c8d2f-bddf-4171-830f-1bdab01c6452 created successfully!
2026-09-10 14:35:38,423 INFO submitter.submission._create_item(): Item created with handle: 123456789/104
2026-09-10 14:35:38,464 INFO submitter.submission._create_bundle(): Bundle created with UUID: ac652233-d512-427f-971c-c356ddd4cca6
2026-09-10 14:35:39,256 INFO submitter.submission._create_bitstream(): Bitstream created with UUID: 634f8191-24c3-4cb9-bd80-e40c182df8f9
2026-09-10 14:35:39,355 INFO submitter.sqs.process(): Deleted message '6328d637-d868-4bb8-8b26-045ea95f7923' from input queue
2026-09-10 14:35:39,356 INFO submitter.sqs.process(): Processing message 'cc790276-a15d-4d1b-90f3-47cf356448f9' from queue 'dss-input-dsc-dev'
2026-09-10 14:35:39,641 INFO root.create_dso(): item 18ba5598-25e6-440e-b5e9-bebbfaa558b9 created successfully!
2026-09-10 14:35:39,641 INFO submitter.submission._create_item(): Item created with handle: 123456789/105
2026-09-10 14:35:39,684 INFO submitter.submission._create_bundle(): Bundle created with UUID: 359a158b-c25d-479a-81b1-26343dfa575c
2026-09-10 14:35:40,098 INFO submitter.submission._create_bitstream(): Bitstream created with UUID: 552f9f33-b050-4433-ba5e-47fea474c96d
2026-09-10 14:35:40,177 INFO submitter.sqs.process(): Deleted message 'cc790276-a15d-4d1b-90f3-47cf356448f9' from input queue
2026-09-10 14:35:40,177 INFO submitter.sqs.retrieve_messages_from_queue(): Polling queue dss-input-dsc-dev for messages
2026-09-10 14:36:00,222 INFO submitter.sqs.retrieve_messages_from_queue(): 0 messages received
2026-09-10 14:36:00,222 INFO submitter.sqs.message_loop(): No messages available in queue dss-input-dsc-dev
2026-09-10 14:36:00,222 INFO submitter.cli.start(): Completed processing messages from queue dss-input-dsc-dev

Includes new or updated dependencies?

YES

Changes expectations for external applications?

NO

What are the relevant tickets?

Code review

  • Code review best practices are documented here and you are encouraged to have a constructive dialogue with your reviewers about their preferences and expectations.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new submission path rejects previously valid SCCS metadata, and Wiley’s inherited transformer stub returns None.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Introduces a shared metadata-transformer framework and moves transformation into the base submission workflow.

Changes:

  • Adds transformer protocol and reusable field-based transformer.
  • Migrates SCCS, OCW, Wiley, and Digitized Theses transformer declarations.
  • Updates OCW metadata iteration and tests.
File summaries
File Description
dsc/workflows/base/transformer.py Adds shared transformer abstractions.
dsc/workflows/base/workflow.py Runs transformers during submission.
dsc/workflows/base/__init__.py Exports transformer types.
dsc/workflows/sccs/transformer.py Adds SCCS field transformation.
dsc/workflows/sccs/workflow.py Configures the SCCS transformer.
dsc/workflows/sccs/__init__.py Exports the SCCS transformer.
dsc/workflows/opencourseware/transformer.py Implements the transformer protocol.
dsc/workflows/opencourseware/workflow.py Yields untransformed source metadata.
dsc/workflows/digitized_theses/transformer.py Implements the transformer protocol.
dsc/workflows/wiley/transformer.py Adds protocol inheritance.
dsc/workflows/archivesspace/workflow.py Explicitly retains mapping-based metadata.
tests/test_workflow_opencourseware.py Updates OCW transformation expectations.
Review details
  • Files reviewed: 12/13 changed files
  • Comments generated: 3
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +326 to +330
item_submission.prepare_dspace_metadata(
item_metadata=item_metadata,
s3_bucket=self.s3_bucket,
batch_path=self.batch_path,
)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine, it looks like the metadata example is being misinterpreted as a requirement

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note: I also think that those two fields are the minimum required metadata fields in the DSpace REST API contract, so I think it would raise an issue if dc.date.issued was missing from the record. 🤔

Comment thread dsc/workflows/wiley/transformer.py Outdated
Comment thread dsc/workflows/sccs/transformer.py Outdated
from dsc.workflows.base import BaseTransformer


class SCCSTransformer(BaseTransformer):

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do!

ehanson8 and others added 2 commits September 10, 2026 17:17
Why these changes are being introduced:
* Create a MetadataTransformer and BaseTransformer class to align the metadata transformation of the various workflows

How this addresses that need:
* Add Protocol-based MetadataTransformer class as transformer parent class
* Add BaseTransformer class to transform source metadata where the field names correspond to DSpace field names
* Add metadata_transformer attribute to Workflow class, update submit_items method to account for metadata transformer path, and add _run_metadata_transformer method
* Update type hinting for DigitizedThesesTransformer and OpenCourseWareTransformer fields attribute
* Update OpenCourseWare.item_metadata_iter method to align with other workflows
* Add SCCSTransformer class
* Add metadata_transformer attribute to SCCS class
* Update OCW unit test to use metadata transformer
* Update dependencies

Side effects of this change:
* NA

Relevant ticket(s):
* https://mitlibraries.atlassian.net/browse/IN-1672
* Add stub transform method to WileyTransformer
* Add unit tests for SCCSTransformer

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@ehanson8
ehanson8 force-pushed the IN-1672-base-metadata-transformer branch from 2749717 to 223295b Compare September 10, 2026 21:18
Comment on lines +393 to +395
raise NotImplementedError(
f"'{self.workflow_name}' does not have a metadata_transformer."
)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be removed once we deprecate the metadata mapping path

Comment on lines +331 to +337
else:
item_metadata = batch_metadata[item_identifier]
item_submission.prepare_dspace_metadata(
metadata_mapping=self.metadata_mapping,
item_metadata=item_metadata,
s3_bucket=self.s3_bucket,
batch_path=self.batch_path,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be removed once we deprecate the metadata mapping path

@ehanson8
ehanson8 marked this pull request as ready for review September 11, 2026 14:23
@ehanson8
ehanson8 requested a review from a team as a code owner September 11, 2026 14:23

@ghukill ghukill left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the nature of comments are pretty circular, they all kind of reference one another. I'm opting to pause here to lodge these comments/questions, and it's quite possible an easy explanation addresses them all at once!

Though I suppose there is a legit request for a docstring in the base Workflow class.

All that said, really liking the shape of this! I like the class level metadata_transformer property, where workflows kind of "register" their transformer at definition time.

Looking good, just looking for some clarity re: my questions before I continue.

workflow_name: str = "base"
submission_system: str = "IR-8"
required_env_vars: ClassVar[list] = []
metadata_transformer: ClassVar[type[MetadataTransformer] | None] = None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This.... I like. But it took me awhile to get there. Let me explain my confusion, and perhaps it would suggest where a comment or docstring could help.

I'm not immediately sure where a docstring could have helped me, but I think it'd be in the base workflow class. My proposal would be defining and explaining the class level properties in the base workflows's docstsring, e.g.:

class Workflow(ABC):
    """A base workflow class from which other workflow classes are derived.

    Class properties:
        - workflow_name: ...
        - submission_system: ...
        - ...
        - ...
        - metadata_transformer: <as lengthy an explanation as needed that it defaults to None, but workflows can define or reuse a pre-existing transformer and set it here>
    """

Comment thread dsc/workflows/archivesspace/workflow.py Outdated

workflow_name: str = "archivesspace"
submission_system: str = "Dome"
metadata_transformer = None # uses metadata_mapping.json

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might propose omitting this metadata_transformer = None # uses metadata_mapping.json and just let it fall-through to the base workflow's default of None. Unless we have compelling reasons not to.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm returning to this comment as I'm either more confused... or would like to rescind this suggestion 😅.

Why does this workflow, and only this workflow, not need to define a transformer class and thus doesn't register one on the workflow?

I'm a bit unclear how this archivesspace workflow differs from one like sccs which extends the BaseTransformer (but also see my note about that class naming) and registers its custom transformer.

It seems like archivesspace is not using a transformer at all?

Comment thread dsc/workflows/base/transformer.py Outdated
Comment on lines +15 to +19
class BaseTransformer(MetadataTransformer):
"""Base transformer for workflows where field names correspond to DSpace field names.

Subclasses declare which fields to include and which are pipe-delimited.
"""

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After a bit of circling around, I think I understand my confusion here.

On my first read through, I thought BaseTransformer was what all child transformers would inherit from. I understand that's incorrect, and it's actually MetadataTransformer.

Before I understood that, I thought this class was somehow handling custom vs delimited mapped fields, and I couldn't see that anywhere in the logic.

What if this class were renamed to something like DelimitedMappingTransformer?

* Add stub ArchivesSpaceTransformer class and update the workflow to call it
* Update metadata_mapping_path attributes to raise NotImplementedError for all workflows
* Rename BaseTransformer > SimpleCSVTransformer and shift to simple_csv workflow folder
@ehanson8

Copy link
Copy Markdown
Contributor Author

@ghukill Pushed a new commit based on our discussion!

@ghukill ghukill left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enthusiastic approve! Nice work, and thanks for the huddle last week. All my questions virtually vanish now.

I had one parting though that is not a request, and maybe not even something we want to do today, but came to mind.

It feels like simple_csv is sort of a "generic" workflow that needs extension + opinionation. SCCS comes to mind obviously, maybe there are others.

I wonder if there is value in docstring additions to both SimpleCSV(Workflow) and SimpleCSVTransformer(MetadataTransformer) that make pretty clear they are not designed to be used without extension?

I wouldn't be surprised if we have a test that treats it like a real workflow, but we know that we'd never call SimpleCSV -- workflow or transformer -- directly in a real ingest. I don't think we really need programattic business logic to enforce this, but something in the directory structure or docstrings that could hint to future us that these are meant to be extended could be helpful. The fact that it's a sibling directory to sccs, opencourseware, etc., makes it a bit confusing.

But again, optional and not need right now. This PR is approved as-is.

Comment thread dsc/workflows/archivesspace/workflow.py Outdated
Comment thread dsc/workflows/simple_csv/transformer.py
@ehanson8

Copy link
Copy Markdown
Contributor Author

Enthusiastic approve! Nice work, and thanks for the huddle last week. All my questions virtually vanish now.

I had one parting though that is not a request, and maybe not even something we want to do today, but came to mind.

It feels like simple_csv is sort of a "generic" workflow that needs extension + opinionation. SCCS comes to mind obviously, maybe there are others.

I wonder if there is value in docstring additions to both SimpleCSV(Workflow) and SimpleCSVTransformer(MetadataTransformer) that make pretty clear they are not designed to be used without extension?

I wouldn't be surprised if we have a test that treats it like a real workflow, but we know that we'd never call SimpleCSV -- workflow or transformer -- directly in a real ingest. I don't think we really need programattic business logic to enforce this, but something in the directory structure or docstrings that could hint to future us that these are meant to be extended could be helpful. The fact that it's a sibling directory to sccs, opencourseware, etc., makes it a bit confusing.

But again, optional and not need right now. This PR is approved as-is.

Let's discuss when @jonavellecuerdo gets back, good suggestions!

@jonavellecuerdo

Copy link
Copy Markdown
Contributor

Re: @ghukill 's last comment (#268 (review)) --

  • I think we should include a docstring for now communicating that SimpleCSVTransformer needs to be extended.
  • I agree that it is slightly confusing that simple_csv/ is a sibling directory to extended/implemented DSC workflows. 🤔 @ghukill Do you mind creating a ticket with the contents of your comment above about making this clear in the folder hierarchy of DSC? You can tag it as part of the Q2 release! I'm assuming the solution is something akin to what we do for transmogrifier: https://github.com/MITLibraries/transmogrifier/tree/main/transmogrifier/sources.

@ehanson8 If you update the docstring, I think everything else is looking good and happy to approve then!

* Update SimpleCSVTransformer docstring to reflect that it needs to be extended
@ehanson8

Copy link
Copy Markdown
Contributor Author

@ehanson8 If you update the docstring, I think everything else is looking good and happy to approve then

@jonavellecuerdo Updated!

@ehanson8
ehanson8 merged commit b67c24e into main Sep 17, 2026
6 checks passed
@ehanson8
ehanson8 deleted the IN-1672-base-metadata-transformer branch September 17, 2026 17:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants