UFAL/Add CCMM 1.1.0 OAI-PMH crosswalk (ccmm-xml metadataPrefix)#1262
UFAL/Add CCMM 1.1.0 OAI-PMH crosswalk (ccmm-xml metadataPrefix)#1262milanmajchrak wants to merge 2 commits intodtq-devfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR implements a new OAI-PMH crosswalk for exporting DSpace metadata in CCMM (Czech Common Metadata Model) 1.1.0 format, required for NMD/NRP (National Repository of Gray Literature) compliance in the Czech Republic. The implementation follows the standard DSpace OAI-PMH crosswalk pattern used by other metadata formats in the repository.
Changes:
- New XSL transformation stylesheet mapping DSpace internal metadata to CCMM 1.1.0 XML schema
- Configuration updates to register the new crosswalk in both Default Context and openaire_data Context
- Comprehensive test suite with 14 unit tests and dedicated test data file
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 14 comments.
| File | Description |
|---|---|
| dspace/config/crosswalks/oai/xoai.xml | Registers ccmm format (metadataPrefix: ccmm-xml) in Default and openaire_data contexts |
| dspace/config/crosswalks/oai/metadataFormats/ccmm.xsl | 633-line XSLT 2.0 stylesheet implementing DSpace to CCMM metadata transformation with fallback handling for required fields |
| dspace-oai/src/test/java/org/dspace/xoai/tests/stylesheets/CcmmXslTest.java | Test class with 14 tests validating core CCMM elements (identifiers, creators, subjects, license, etc.) |
| dspace-oai/src/test/resources/xoai-ccmm-test.xml | Test fixture simulating typical NMD/NRP dataset metadata with Czech academic repository data |
| </xsl:otherwise> | ||
| </xsl:choose> | ||
| </xsl:when> | ||
| <xsl:otherwise>http://unknown.repository</xsl:otherwise> |
There was a problem hiding this comment.
The hardcoded string "http://unknown.repository" should be extracted as a constant or defined at the top of the stylesheet for better maintainability.
| <xsl:when test="contains($uri, '/handle/')"> | ||
| <xsl:value-of select="substring-before($uri, '/handle/')"/> |
There was a problem hiding this comment.
The logic for extracting the repository base URL by finding '/handle/' in the URI and taking everything before it (lines 114-115) may not work correctly for all Handle server configurations. Some repositories might use different URL patterns (e.g., using a different path segment). Consider adding a comment explaining the assumptions about Handle URL structure, or making this more robust by also checking for 'hdl.handle.net' pattern.
| @Test | ||
| public void ccmmCanTransformBasicXoaiInput() throws Exception { | ||
| // Test with the default xoai-test1.xml (simpler data) to ensure crosswalk | ||
| // handles missing fields gracefully | ||
| String result = apply("ccmm.xsl").to(resource("xoai-test1.xml")); | ||
| assertThat(result, is(ccmm().withXPath("//ccmm:title", equalTo("Test Webpage")))); | ||
| } |
There was a problem hiding this comment.
The test suite lacks coverage for the fallback scenarios where required fields are missing. Consider adding tests that verify the behavior when title, identifiers, publication year, time references, and subjects are missing to ensure fallback values (like "Untitled", "9999", etc.) are correctly produced. This is particularly important for required CCMM fields.
- Extract hardcoded fallback strings as XSLT variables at top of stylesheet (Unknown Repository, http://unknown.repository, Untitled, unspecified, 9999) - Add detailed comment explaining Handle URL extraction assumptions - Add comment explaining publisher to Distributor role mapping rationale - Fix time_reference fallback: use dc.date.accessioned instead of hardcoded 9999-01-01 when issued/available dates are missing - Improve FormatDate fallback: validate first 4 chars are digits before using as year, output empty ccmm:date element for invalid input - Improve identifier fallback logic: check if Handle-pattern URIs were actually found rather than just checking for raw element presence - Add 6 new fallback scenario tests (title, publication_year, subject, identifier, repository name, license) with dedicated minimal test fixture - All 21 tests pass (20 CCMM + 1 QDC)
| <ccmm:date><xsl:value-of select="concat($year, '-01-01')"/></ccmm:date> | ||
| </xsl:when> | ||
| <xsl:otherwise> | ||
| <ccmm:date/> |
There was a problem hiding this comment.
The FormatDate template creates an empty ccmm:date element at line 660 when the date string is invalid. This produces invalid XML output like 'ccmm:date/' or 'ccmm:date</ccmm:date>' which may not validate against the CCMM schema. Consider either omitting the element entirely or providing a fallback value, or documenting that this is intentional if the schema permits empty date elements.
| <ccmm:date/> |
| <xsl:variable name="lang" select="doc:metadata/doc:element[@name='dc']/doc:element[@name='language']/doc:element[@name='iso']/doc:element/doc:field[@name='value']"/> | ||
| <xsl:variable name="langAlt" select="doc:metadata/doc:element[@name='dc']/doc:element[@name='language']/doc:element/doc:field[@name='value']"/> | ||
| <xsl:choose> | ||
| <xsl:when test="$lang"> | ||
| <ccmm:primary_language> | ||
| <ccmm:iri> | ||
| <xsl:value-of select="concat('https://iso639-3.sil.org/code/', $lang)"/> | ||
| </ccmm:iri> | ||
| <ccmm:label xml:lang="en"><xsl:value-of select="$lang"/></ccmm:label> |
There was a problem hiding this comment.
The PrimaryLanguage template could be clearer by explicitly selecting only the first language value with '[1]' predicate. Currently, line 440 selects all dc.language.iso values, and XSLT's atomization implicitly uses the first one at line 446. While functionally correct, explicitly selecting '[1]' at line 440 would make the intent clearer and match the pattern used in OtherLanguages at line 468 where position() > 1 explicitly excludes the first.
| <ccmm:iri> | ||
| <xsl:choose> | ||
| <xsl:when test="doc:metadata/doc:element[@name='dc']/doc:element[@name='identifier']/doc:element[@name='uri']/doc:element/doc:field[@name='value']"> | ||
| <xsl:variable name="uri" select="doc:metadata/doc:element[@name='dc']/doc:element[@name='identifier']/doc:element[@name='uri']/doc:element/doc:field[@name='value'][1]"/> | ||
| <!-- | ||
| Extract repository base URL from Handle URI. | ||
| Assumes DSpace-style URLs like https://repo.example.org/handle/123/456. | ||
| Also handles hdl.handle.net URIs as-is. | ||
| --> | ||
| <xsl:choose> | ||
| <xsl:when test="contains($uri, '/handle/')"> | ||
| <xsl:value-of select="substring-before($uri, '/handle/')"/> | ||
| </xsl:when> | ||
| <xsl:otherwise> | ||
| <xsl:value-of select="$uri"/> | ||
| </xsl:otherwise> | ||
| </xsl:choose> | ||
| </xsl:when> | ||
| <xsl:otherwise><xsl:value-of select="$FALLBACK_REPOSITORY_URL"/></xsl:otherwise> | ||
| </xsl:choose> |
There was a problem hiding this comment.
The original_repository IRI extraction logic may produce incorrect results when dc.identifier.uri contains non-Handle URIs. Lines 127-132 will output the full URI (e.g., a DOI URL) as the repository IRI if it doesn't contain '/handle/'. Consider filtering to only Handle URIs (checking for 'hdl.handle.net' or '/handle/'), or falling back to FALLBACK_REPOSITORY_URL for non-Handle URIs, similar to the Identifiers template logic at lines 152-153.
| <xsl:variable name="dctype" select="doc:metadata/doc:element[@name='dc']/doc:element[@name='type']/doc:element/doc:field[@name='value']"/> | ||
| <xsl:if test="$dctype"> | ||
| <ccmm:resource_type> | ||
| <ccmm:iri> | ||
| <xsl:choose> | ||
| <xsl:when test="$dctype='Dataset' or $dctype='corpus' or $dctype='lexicalConceptualResource' or $dctype='languageDescription'">https://model.ccmm.cz/vocabulary/datacite/resourceTypeGeneral/Dataset</xsl:when> | ||
| <xsl:when test="$dctype='Software' or $dctype='toolService'">https://model.ccmm.cz/vocabulary/datacite/resourceTypeGeneral/Software</xsl:when> | ||
| <xsl:when test="$dctype='Text' or $dctype='text'">https://model.ccmm.cz/vocabulary/datacite/resourceTypeGeneral/Text</xsl:when> | ||
| <xsl:when test="$dctype='Image' or $dctype='image'">https://model.ccmm.cz/vocabulary/datacite/resourceTypeGeneral/Image</xsl:when> | ||
| <xsl:when test="$dctype='Collection'">https://model.ccmm.cz/vocabulary/datacite/resourceTypeGeneral/Collection</xsl:when> | ||
| <xsl:otherwise>https://model.ccmm.cz/vocabulary/datacite/resourceTypeGeneral/Other</xsl:otherwise> | ||
| </xsl:choose> | ||
| </ccmm:iri> | ||
| <ccmm:label xml:lang="en"><xsl:value-of select="$dctype"/></ccmm:label> | ||
| </ccmm:resource_type> |
There was a problem hiding this comment.
The ResourceType template at line 418 selects all dc.type values into $dctype variable, but only the first value will be used (due to XSLT atomization in the when tests). If multiple dc.type values exist, subsequent values are silently ignored. For clarity, explicitly select only the first value with '[1]' predicate, similar to the pattern used elsewhere in the stylesheet.
| <ccmm:funding_reference> | ||
| <xsl:if test="count($parts) >= 5"> | ||
| <ccmm:local_identifier><xsl:value-of select="$parts[5]"/></ccmm:local_identifier> | ||
| </xsl:if> | ||
| <ccmm:funder> | ||
| <ccmm:organization> | ||
| <ccmm:name> | ||
| <xsl:choose> | ||
| <xsl:when test="$parts[3]='EC'">European Commission</xsl:when> | ||
| <xsl:otherwise><xsl:value-of select="$parts[3]"/></xsl:otherwise> | ||
| </xsl:choose> | ||
| </ccmm:name> | ||
| </ccmm:organization> | ||
| </ccmm:funder> | ||
| </ccmm:funding_reference> |
There was a problem hiding this comment.
The FundingReferences template lacks error handling for malformed grant agreement URIs. If a dc.relation value starts with 'info:' but doesn't have the expected structure (e.g., fewer than 5 parts after tokenizing by '/'), the code attempts to access $parts[3] and potentially $parts[5], which could produce empty values or runtime errors. Consider adding validation to ensure the URI has the expected number of parts before accessing array indices.
| <ccmm:funding_reference> | |
| <xsl:if test="count($parts) >= 5"> | |
| <ccmm:local_identifier><xsl:value-of select="$parts[5]"/></ccmm:local_identifier> | |
| </xsl:if> | |
| <ccmm:funder> | |
| <ccmm:organization> | |
| <ccmm:name> | |
| <xsl:choose> | |
| <xsl:when test="$parts[3]='EC'">European Commission</xsl:when> | |
| <xsl:otherwise><xsl:value-of select="$parts[3]"/></xsl:otherwise> | |
| </xsl:choose> | |
| </ccmm:name> | |
| </ccmm:organization> | |
| </ccmm:funder> | |
| </ccmm:funding_reference> | |
| <xsl:variable name="parts-count" select="count($parts)"/> | |
| <xsl:if test="$parts-count >= 3"> | |
| <ccmm:funding_reference> | |
| <xsl:if test="$parts-count >= 5"> | |
| <ccmm:local_identifier><xsl:value-of select="$parts[5]"/></ccmm:local_identifier> | |
| </xsl:if> | |
| <ccmm:funder> | |
| <ccmm:organization> | |
| <ccmm:name> | |
| <xsl:choose> | |
| <xsl:when test="$parts[3]='EC'">European Commission</xsl:when> | |
| <xsl:otherwise><xsl:value-of select="$parts[3]"/></xsl:otherwise> | |
| </xsl:choose> | |
| </ccmm:name> | |
| </ccmm:organization> | |
| </ccmm:funder> | |
| </ccmm:funding_reference> | |
| </xsl:if> |
| <!-- ============================================================ --> | ||
| <!-- funding_reference (optional, unbounded) --> | ||
| <!-- ============================================================ --> | ||
| <xsl:template name="FundingReferences"> | ||
| <!-- dc.relation with info:eu-repo/grantAgreement pattern --> | ||
| <xsl:for-each select="doc:metadata/doc:element[@name='dc']/doc:element[@name='relation']/doc:element/doc:field[@name='value']"> | ||
| <xsl:if test="starts-with(., 'info:')"> | ||
| <xsl:variable name="parts" select="tokenize(., '/')"/> | ||
| <ccmm:funding_reference> | ||
| <xsl:if test="count($parts) >= 5"> | ||
| <ccmm:local_identifier><xsl:value-of select="$parts[5]"/></ccmm:local_identifier> | ||
| </xsl:if> | ||
| <ccmm:funder> | ||
| <ccmm:organization> | ||
| <ccmm:name> | ||
| <xsl:choose> | ||
| <xsl:when test="$parts[3]='EC'">European Commission</xsl:when> | ||
| <xsl:otherwise><xsl:value-of select="$parts[3]"/></xsl:otherwise> | ||
| </xsl:choose> | ||
| </ccmm:name> | ||
| </ccmm:organization> | ||
| </ccmm:funder> | ||
| </ccmm:funding_reference> | ||
| </xsl:if> | ||
| </xsl:for-each> | ||
| </xsl:template> | ||
|
|
||
| <!-- ============================================================ --> | ||
| <!-- related_resource (optional, unbounded) --> | ||
| <!-- ============================================================ --> | ||
| <xsl:template name="RelatedResources"> | ||
| <!-- dc.relation.uri --> | ||
| <xsl:for-each select="doc:metadata/doc:element[@name='dc']/doc:element[@name='relation']/doc:element[@name='uri']/doc:element/doc:field[@name='value']"> | ||
| <ccmm:related_resource> | ||
| <ccmm:identifier> | ||
| <ccmm:value><xsl:value-of select="."/></ccmm:value> | ||
| <ccmm:scheme> | ||
| <ccmm:iri>https://www.w3.org/ns/iana/uri-schemes</ccmm:iri> | ||
| <ccmm:label xml:lang="en">URI</ccmm:label> | ||
| </ccmm:scheme> | ||
| </ccmm:identifier> | ||
| </ccmm:related_resource> | ||
| </xsl:for-each> | ||
| <!-- dc.relation.ispartof --> | ||
| <xsl:for-each select="doc:metadata/doc:element[@name='dc']/doc:element[@name='relation']/doc:element[@name='ispartof']/doc:element/doc:field[@name='value']"> | ||
| <ccmm:related_resource> | ||
| <ccmm:title><xsl:value-of select="."/></ccmm:title> | ||
| </ccmm:related_resource> | ||
| </xsl:for-each> | ||
| </xsl:template> |
There was a problem hiding this comment.
The FundingReferences and RelatedResources templates lack test coverage. No tests verify the parsing of grant agreement URIs (info:eu-repo/grantAgreement format) or the handling of dc.relation.uri and dc.relation.ispartof fields. Add test cases with sample funding data and related resource data to ensure these mappings work correctly.
| <xsl:template name="TermsOfUse"> | ||
| <ccmm:terms_of_use> | ||
| <!-- access_rights --> | ||
| <ccmm:access_rights> | ||
| <xsl:choose> | ||
| <xsl:when test="doc:metadata/doc:element[@name='local']/doc:element[@name='embargo']/doc:element[@name='termslift']/doc:element/doc:field[@name='value']"> | ||
| <ccmm:iri>http://purl.org/coar/access_right/c_f1cf</ccmm:iri> | ||
| <ccmm:label xml:lang="en">embargoed access</ccmm:label> | ||
| </xsl:when> | ||
| <xsl:when test="doc:metadata/doc:element[@name='others']/doc:field[@name='restrictedAccess']/text()='true'"> | ||
| <ccmm:iri>http://purl.org/coar/access_right/c_16ec</ccmm:iri> | ||
| <ccmm:label xml:lang="en">restricted access</ccmm:label> | ||
| </xsl:when> | ||
| <xsl:otherwise> | ||
| <ccmm:iri>http://purl.org/coar/access_right/c_abf2</ccmm:iri> | ||
| <ccmm:label xml:lang="en">open access</ccmm:label> | ||
| </xsl:otherwise> | ||
| </xsl:choose> | ||
| </ccmm:access_rights> |
There was a problem hiding this comment.
The TermsOfUse template handles three access rights scenarios (embargoed, restricted, open), but only the default "open access" case is tested. Add test cases with local.embargo.termslift and others.restrictedAccess fields to verify embargoed and restricted access mappings produce correct COAR access rights IRIs.
| <xsl:template name="FormatDate"> | ||
| <xsl:param name="dateStr"/> | ||
| <xsl:choose> | ||
| <!-- Full ISO dateTime: 2024-01-15T10:30:00Z --> | ||
| <xsl:when test="contains($dateStr, 'T')"> | ||
| <ccmm:date_time><xsl:value-of select="$dateStr"/></ccmm:date_time> | ||
| </xsl:when> | ||
| <!-- Full date: 2024-01-15 --> | ||
| <xsl:when test="string-length($dateStr) >= 10 and substring($dateStr, 5, 1) = '-' and substring($dateStr, 8, 1) = '-'"> | ||
| <ccmm:date><xsl:value-of select="substring($dateStr, 1, 10)"/></ccmm:date> | ||
| </xsl:when> | ||
| <!-- Year-month: 2024-01 --> | ||
| <xsl:when test="string-length($dateStr) >= 7 and substring($dateStr, 5, 1) = '-'"> | ||
| <ccmm:date><xsl:value-of select="concat($dateStr, '-01')"/></ccmm:date> | ||
| </xsl:when> | ||
| <!-- Year only: 2024 --> | ||
| <xsl:when test="string-length($dateStr) = 4"> | ||
| <ccmm:date><xsl:value-of select="concat($dateStr, '-01-01')"/></ccmm:date> | ||
| </xsl:when> | ||
| <!-- Fallback: attempt to derive a date only if the first 4 chars form a valid year --> | ||
| <xsl:otherwise> | ||
| <xsl:variable name="year" select="substring($dateStr, 1, 4)"/> | ||
| <xsl:choose> | ||
| <xsl:when test="string-length($dateStr) >= 4 and not(translate($year, '0123456789', ''))"> | ||
| <ccmm:date><xsl:value-of select="concat($year, '-01-01')"/></ccmm:date> | ||
| </xsl:when> | ||
| <xsl:otherwise> | ||
| <ccmm:date/> | ||
| </xsl:otherwise> | ||
| </xsl:choose> | ||
| </xsl:otherwise> | ||
| </xsl:choose> | ||
| </xsl:template> |
There was a problem hiding this comment.
The FormatDate template handles multiple date format variations (datetime with T, full date YYYY-MM-DD, year-month YYYY-MM, year-only YYYY, and malformed dates), but there are no tests covering these different formats. Add tests to verify that dates in different formats are correctly transformed, especially edge cases like year-only or year-month dates that need padding with '-01' or '-01-01'.
| <Format id="ccmm"> | ||
| <Prefix>ccmm-xml</Prefix> | ||
| <XSLT>metadataFormats/ccmm.xsl</XSLT> | ||
| <Namespace>https://schema.ccmm.cz/research-data/1.1</Namespace> | ||
| <SchemaLocation>https://techlib.github.io/CCMM/dataset/schema.xsd</SchemaLocation> | ||
| </Format> |
There was a problem hiding this comment.
Inconsistent indentation for the Format element. The CCMM format definition uses 8-space indentation while the preceding ELG format uses 4-space indentation. For consistency with surrounding Format definitions (lines 217-235), use 4-space indentation for the Format element and 8-space indentation for its child elements.
| <Format id="ccmm"> | |
| <Prefix>ccmm-xml</Prefix> | |
| <XSLT>metadataFormats/ccmm.xsl</XSLT> | |
| <Namespace>https://schema.ccmm.cz/research-data/1.1</Namespace> | |
| <SchemaLocation>https://techlib.github.io/CCMM/dataset/schema.xsd</SchemaLocation> | |
| </Format> | |
| <Format id="ccmm"> | |
| <Prefix>ccmm-xml</Prefix> | |
| <XSLT>metadataFormats/ccmm.xsl</XSLT> | |
| <Namespace>https://schema.ccmm.cz/research-data/1.1</Namespace> | |
| <SchemaLocation>https://techlib.github.io/CCMM/dataset/schema.xsd</SchemaLocation> | |
| </Format> |
| <xsl:template name="TimeReferences"> | ||
| <!-- dc.date.issued -> Issued --> | ||
| <xsl:for-each select="doc:metadata/doc:element[@name='dc']/doc:element[@name='date']/doc:element[@name='issued']/doc:element/doc:field[@name='value']"> | ||
| <ccmm:time_reference> | ||
| <ccmm:temporal_representation> | ||
| <ccmm:time_instant> | ||
| <xsl:call-template name="FormatDate"> | ||
| <xsl:with-param name="dateStr" select="."/> | ||
| </xsl:call-template> | ||
| </ccmm:time_instant> | ||
| </ccmm:temporal_representation> | ||
| <ccmm:date_type> | ||
| <ccmm:iri>https://model.ccmm.cz/vocabulary/datacite/dateType/Issued</ccmm:iri> | ||
| <ccmm:label xml:lang="en">Issued</ccmm:label> | ||
| </ccmm:date_type> | ||
| </ccmm:time_reference> | ||
| </xsl:for-each> | ||
| <!-- dc.date.accessioned -> Accepted --> | ||
| <xsl:for-each select="doc:metadata/doc:element[@name='dc']/doc:element[@name='date']/doc:element[@name='accessioned']/doc:element/doc:field[@name='value']"> | ||
| <ccmm:time_reference> | ||
| <ccmm:temporal_representation> | ||
| <ccmm:time_instant> | ||
| <xsl:call-template name="FormatDate"> | ||
| <xsl:with-param name="dateStr" select="."/> | ||
| </xsl:call-template> | ||
| </ccmm:time_instant> | ||
| </ccmm:temporal_representation> | ||
| <ccmm:date_type> | ||
| <ccmm:iri>https://model.ccmm.cz/vocabulary/datacite/dateType/Accepted</ccmm:iri> | ||
| <ccmm:label xml:lang="en">Accepted</ccmm:label> | ||
| </ccmm:date_type> | ||
| </ccmm:time_reference> | ||
| </xsl:for-each> | ||
| <!-- dc.date.available -> Available --> | ||
| <xsl:for-each select="doc:metadata/doc:element[@name='dc']/doc:element[@name='date']/doc:element[@name='available']/doc:element/doc:field[@name='value']"> | ||
| <ccmm:time_reference> | ||
| <ccmm:temporal_representation> | ||
| <ccmm:time_instant> | ||
| <xsl:call-template name="FormatDate"> | ||
| <xsl:with-param name="dateStr" select="."/> | ||
| </xsl:call-template> | ||
| </ccmm:time_instant> | ||
| </ccmm:temporal_representation> | ||
| <ccmm:date_type> | ||
| <ccmm:iri>https://model.ccmm.cz/vocabulary/datacite/dateType/Available</ccmm:iri> | ||
| <ccmm:label xml:lang="en">Available</ccmm:label> | ||
| </ccmm:date_type> | ||
| </ccmm:time_reference> | ||
| </xsl:for-each> | ||
| <!-- Fallback: if no issued/available dates, create a minimal time_reference from accessioned --> | ||
| <xsl:if test="not(doc:metadata/doc:element[@name='dc']/doc:element[@name='date']/doc:element[@name='issued']/doc:element/doc:field[@name='value']) and not(doc:metadata/doc:element[@name='dc']/doc:element[@name='date']/doc:element[@name='available']/doc:element/doc:field[@name='value']) and doc:metadata/doc:element[@name='dc']/doc:element[@name='date']/doc:element[@name='accessioned']/doc:element/doc:field[@name='value']"> | ||
| <xsl:variable name="accessionedDate" | ||
| select="doc:metadata/doc:element[@name='dc']/doc:element[@name='date']/doc:element[@name='accessioned']/doc:element/doc:field[@name='value'][1]"/> | ||
| <ccmm:time_reference> | ||
| <ccmm:temporal_representation> | ||
| <ccmm:time_instant> | ||
| <xsl:call-template name="FormatDate"> | ||
| <xsl:with-param name="dateStr" select="$accessionedDate"/> | ||
| </xsl:call-template> | ||
| </ccmm:time_instant> | ||
| </ccmm:temporal_representation> | ||
| <ccmm:date_type> | ||
| <ccmm:iri>https://model.ccmm.cz/vocabulary/datacite/dateType/Issued</ccmm:iri> | ||
| <ccmm:label xml:lang="en">Issued</ccmm:label> | ||
| </ccmm:date_type> | ||
| </ccmm:time_reference> | ||
| </xsl:if> | ||
| </xsl:template> |
There was a problem hiding this comment.
No test coverage for the time_reference element, which is a required CCMM field. The TimeReferences template handles multiple date types (Issued, Accepted, Available) and includes fallback logic, but none of these scenarios are tested. Add tests to verify that different date fields produce correct time_reference elements with appropriate date_type values and temporal_representation formatting.
Problem description
Add CCMM 1.1.0 OAI-PMH crosswalk
Implements export of metadata in CCMM (Czech Common Metadata Model) 1.1.0 format over OAI-PMH, as required for NMD/NRP compliance.
Changes:
ccmm.xsl) mapping DSpace metadata to CCMM dataset XML (metadataPrefix: ccmm-xml)xoai.xml(Default Context + openaire_data Context)Metadata mapping: title, identifiers (Handle/DOI), creators/contributors, publisher, publication year, dates, resource type, language, rights/license, subjects, descriptions, funding references, related resources, metadata identification with repository info.
Resolves: ufal#1145
Analysis
(Write here, if there is needed describe some specific problem. Erase it, when it is not needed.)
Problems
(Write here, if some unexpected problems occur during solving issues. Erase it, when it is not needed.)
Manual Testing (if applicable)
Copilot review