-
Notifications
You must be signed in to change notification settings - Fork 10
Modifica template OAI-PMH article_compile.txt para geração de metadados nos formatos AOI_DC e MODS #1236
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
base: main
Are you sure you want to change the base?
Modifica template OAI-PMH article_compile.txt para geração de metadados nos formatos AOI_DC e MODS #1236
Conversation
| <!-- ========== SOURCE ========== --> | ||
| <element name="source"> | ||
| <element name="none"> | ||
| <field name="value">{{ object.journal.title }}{% if object.issue.volume %}, v.{{ object.issue.volume }}{% endif %}{% if object.issue.number %}, n.{{ object.issue.number }}{% endif %}{% if object.first_page and object.last_page %}, p.{{ object.first_page }}-{{ object.last_page }}{% endif %}{% if object.pub_date_year %}, {{ object.pub_date_year }}{% endif %}</field> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Rossi-Luciano apesar de não existir um campo, há o property Article.source. Não funcionou?
| <!-- URL da licença (mapeamento Creative Commons) --> | ||
| <element name="uri"> | ||
| <element> | ||
| {% if 'CC BY 4.0' in object.license.license_type or 'CC-BY 4.0' in object.license.license_type %} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Rossi-Luciano criei o Article.license_url (property) para simplificar este código
| {% if ph.institution %} | ||
| <field name="value">{{ ph.institution.institution.institution_identification.name }}</field> | ||
| {% elif ph.organization %} | ||
| <field name="value">{{ ph.organization.name }}</field> | ||
| {% endif %} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Rossi-Luciano dar prioridade para organization que conterá dados padronizados
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request modifies the OAI-PMH template for article metadata generation to fix several field mapping issues and improve compatibility with OAI_DC and MODS formats. The changes address incorrect field paths, add proper Dublin Core qualifiers, and expand metadata elements to better support both metadata standards.
Changes:
- Corrects field access patterns for publisher, source, rights, and description metadata by fixing ForeignKey vs ManyToMany usage and invalid model paths
- Restructures Dublin Core elements with proper qualifiers (e.g., dc:contributor/author, dc:language/iso, dc:description/abstract) and adds new metadata sections for citation, relation, and funding
- Expands date fields to include issued, accessioned, and available qualifiers, and adds ISSN identifier elements with proper print/electronic distinction
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| {% if 'CC BY 4.0' in object.license.license_type or 'CC-BY 4.0' in object.license.license_type %} | ||
| <field name="value">http://creativecommons.org/licenses/by/4.0/</field> | ||
| {% elif 'CC BY-NC 4.0' in object.license.license_type or 'CC-BY-NC 4.0' in object.license.license_type %} | ||
| <field name="value">http://creativecommons.org/licenses/by-nc/4.0/</field> | ||
| {% elif 'CC BY-SA 4.0' in object.license.license_type or 'CC-BY-SA 4.0' in object.license.license_type %} | ||
| <field name="value">http://creativecommons.org/licenses/by-sa/4.0/</field> | ||
| {% elif 'CC BY-ND 4.0' in object.license.license_type or 'CC-BY-ND 4.0' in object.license.license_type %} | ||
| <field name="value">http://creativecommons.org/licenses/by-nd/4.0/</field> | ||
| {% elif 'CC BY-NC-SA 4.0' in object.license.license_type or 'CC-BY-NC-SA 4.0' in object.license.license_type %} | ||
| <field name="value">http://creativecommons.org/licenses/by-nc-sa/4.0/</field> | ||
| {% elif 'CC BY-NC-ND 4.0' in object.license.license_type or 'CC-BY-NC-ND 4.0' in object.license.license_type %} | ||
| <field name="value">http://creativecommons.org/licenses/by-nc-nd/4.0/</field> | ||
| {% elif 'CC BY 3.0' in object.license.license_type or 'CC-BY 3.0' in object.license.license_type %} | ||
| <field name="value">http://creativecommons.org/licenses/by/3.0/</field> | ||
| {% elif 'CC BY-NC 3.0' in object.license.license_type or 'CC-BY-NC 3.0' in object.license.license_type %} | ||
| <field name="value">http://creativecommons.org/licenses/by-nc/3.0/</field> |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The license type string matching logic uses multiple conditions with 'in' checks for different hyphenation formats (e.g., 'CC BY 4.0' and 'CC-BY 4.0'). This approach is brittle and could produce incorrect matches. For example, 'CC BY-NC 4.0' would match the condition for 'CC BY 4.0' on line 246 because 'CC BY 4.0' is a substring of 'CC BY-NC 4.0'. The conditions should be reordered to check more specific licenses first (those with more components like CC BY-NC-ND, CC BY-NC-SA, CC BY-NC, CC BY-SA, CC BY-ND) before checking less specific ones (CC BY), or use exact string matching with multiple elif conditions for each variant.
| {% if 'CC BY 4.0' in object.license.license_type or 'CC-BY 4.0' in object.license.license_type %} | |
| <field name="value">http://creativecommons.org/licenses/by/4.0/</field> | |
| {% elif 'CC BY-NC 4.0' in object.license.license_type or 'CC-BY-NC 4.0' in object.license.license_type %} | |
| <field name="value">http://creativecommons.org/licenses/by-nc/4.0/</field> | |
| {% elif 'CC BY-SA 4.0' in object.license.license_type or 'CC-BY-SA 4.0' in object.license.license_type %} | |
| <field name="value">http://creativecommons.org/licenses/by-sa/4.0/</field> | |
| {% elif 'CC BY-ND 4.0' in object.license.license_type or 'CC-BY-ND 4.0' in object.license.license_type %} | |
| <field name="value">http://creativecommons.org/licenses/by-nd/4.0/</field> | |
| {% elif 'CC BY-NC-SA 4.0' in object.license.license_type or 'CC-BY-NC-SA 4.0' in object.license.license_type %} | |
| <field name="value">http://creativecommons.org/licenses/by-nc-sa/4.0/</field> | |
| {% elif 'CC BY-NC-ND 4.0' in object.license.license_type or 'CC-BY-NC-ND 4.0' in object.license.license_type %} | |
| <field name="value">http://creativecommons.org/licenses/by-nc-nd/4.0/</field> | |
| {% elif 'CC BY 3.0' in object.license.license_type or 'CC-BY 3.0' in object.license.license_type %} | |
| <field name="value">http://creativecommons.org/licenses/by/3.0/</field> | |
| {% elif 'CC BY-NC 3.0' in object.license.license_type or 'CC-BY-NC 3.0' in object.license.license_type %} | |
| <field name="value">http://creativecommons.org/licenses/by-nc/3.0/</field> | |
| {% if 'CC BY-NC-ND 4.0' in object.license.license_type or 'CC-BY-NC-ND 4.0' in object.license.license_type %} | |
| <field name="value">http://creativecommons.org/licenses/by-nc-nd/4.0/</field> | |
| {% elif 'CC BY-NC-SA 4.0' in object.license.license_type or 'CC-BY-NC-SA 4.0' in object.license.license_type %} | |
| <field name="value">http://creativecommons.org/licenses/by-nc-sa/4.0/</field> | |
| {% elif 'CC BY-NC 4.0' in object.license.license_type or 'CC-BY-NC 4.0' in object.license.license_type %} | |
| <field name="value">http://creativecommons.org/licenses/by-nc/4.0/</field> | |
| {% elif 'CC BY-SA 4.0' in object.license.license_type or 'CC-BY-SA 4.0' in object.license.license_type %} | |
| <field name="value">http://creativecommons.org/licenses/by-sa/4.0/</field> | |
| {% elif 'CC BY-ND 4.0' in object.license.license_type or 'CC-BY-ND 4.0' in object.license.license_type %} | |
| <field name="value">http://creativecommons.org/licenses/by-nd/4.0/</field> | |
| {% elif 'CC BY 4.0' in object.license.license_type or 'CC-BY 4.0' in object.license.license_type %} | |
| <field name="value">http://creativecommons.org/licenses/by/4.0/</field> | |
| {% elif 'CC BY-NC 3.0' in object.license.license_type or 'CC-BY-NC 3.0' in object.license.license_type %} | |
| <field name="value">http://creativecommons.org/licenses/by-nc/3.0/</field> | |
| {% elif 'CC BY 3.0' in object.license.license_type or 'CC-BY 3.0' in object.license.license_type %} | |
| <field name="value">http://creativecommons.org/licenses/by/3.0/</field> |
| xsi:schemaLocation="http://www.lyncode.com/xoai http://www.lyncode.com/xsd/xoai.xsd"> | ||
|
|
||
| <!-- ================================================================== --> | ||
| <!-- DUBLIN CORE (OAI_DC) - Metadados básicos para interoperabilidade --> |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo in comment: 'AOI_DC' should be 'OAI_DC' (Open Archives Initiative - Dublin Core)
| {% for kwd in object.keywords.all %} | ||
| <field name="value" vocabulary="{{ kwd.vocabulary.acronym }}">{{ kwd.text }}</field> |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line 121 accesses kwd.vocabulary.acronym without checking if vocabulary is null. Since vocabulary is a nullable ForeignKey on the Keyword model, this could cause a template error if a keyword doesn't have an associated vocabulary. Consider adding a conditional check or using a default filter.
| {% for kwd in object.keywords.all %} | |
| <field name="value" vocabulary="{{ kwd.vocabulary.acronym }}">{{ kwd.text }}</field> | |
| {% for kwd in object.keywords.all %} | |
| {% if kwd.vocabulary %} | |
| <field name="value" vocabulary="{{ kwd.vocabulary.acronym }}">{{ kwd.text }}</field> | |
| {% else %} | |
| <field name="value">{{ kwd.text }}</field> | |
| {% endif %} |
| <!-- Abstract específico (CRÍTICO para MODS!) --> | ||
| <element name="abstract"> | ||
| {% for abs in object.abstracts.all %} | ||
| <element> | ||
| <field name="value">{{ abs.plain_text }}</field> | ||
| </element> | ||
| {% endfor %} | ||
| </element> |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment on line 128 states 'Abstract específico (CRÍTICO para MODS!)' which suggests this is critical for MODS format. However, the element is named 'abstract' which is a qualifier under 'description', following Dublin Core convention. This is appropriate for OAI_DC but should be verified against MODS requirements. According to MODS schema, abstracts should be in mods:abstract elements directly, not under description qualifiers. Consider reviewing if this structure properly supports both OAI_DC and MODS formats as intended.
| <!-- Título do periódico --> | ||
| <element name="ispartof"> | ||
| <element> | ||
| <field name="value">{{ object.journal.title }}</field> | ||
| </element> | ||
| </element> |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line 170 accesses object.journal.title without checking if object.journal is null. Since the journal field on Article is nullable (null=True, blank=True), this could cause a template error if an article doesn't have an associated journal. Consider wrapping this section in a conditional check {% if object.journal %} or using a default filter.
| <!-- Título do periódico --> | |
| <element name="ispartof"> | |
| <element> | |
| <field name="value">{{ object.journal.title }}</field> | |
| </element> | |
| </element> | |
| <!-- Título do periódico --> | |
| {% if object.journal %} | |
| <element name="ispartof"> | |
| <element> | |
| <field name="value">{{ object.journal.title }}</field> | |
| </element> | |
| </element> | |
| {% endif %} |
| {% if ph.institution %} | ||
| <field name="value">{{ ph.institution.institution.institution_identification.name }}</field> | ||
| {% elif ph.organization %} |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The publisher extraction accesses deep nullable relationships (ph.institution.institution.institution_identification.name and ph.organization.name) without null checks beyond verifying ph and the top-level fields exist. If institution or institution_identification is null in the institution branch, this will cause a template error. Consider adding nested conditional checks to verify the entire chain is not null before accessing the name field.
| {% if ph.institution %} | |
| <field name="value">{{ ph.institution.institution.institution_identification.name }}</field> | |
| {% elif ph.organization %} | |
| {% if ph.institution and ph.institution.institution and ph.institution.institution.institution_identification %} | |
| <field name="value">{{ ph.institution.institution.institution_identification.name }}</field> | |
| {% elif ph.organization and ph.organization.name %} |
| <field name="value" orcid="{{ research.orcid }}"{% if research.affiliation %} affiliation="{{ research.affiliation.institution.institution_identification.name }}{% if research.affiliation.institution.level_1 %}, {{ research.affiliation.institution.level_1 }}{% endif %}"{% endif %}>{{ research.person_name }}</field> | ||
| {% else %} | ||
| <field name="value"{% if research.affiliation %} affiliation="{{ research.affiliation.institution.institution_identification.name }}{% if research.affiliation.institution.level_1 %}, {{ research.affiliation.institution.level_1 }}{% endif %}"{% endif %}>{{ research.person_name }}</field> |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The affiliation display accesses a deep chain of nullable relationships (research.affiliation.institution.institution_identification.name and research.affiliation.institution.level_1) without complete null checks. While there's a check for research.affiliation, if institution or institution_identification is null, this will cause a template error. Consider adding nested conditional checks or using Django's template filter 'default' to handle null values gracefully.
| <field name="value" orcid="{{ research.orcid }}"{% if research.affiliation %} affiliation="{{ research.affiliation.institution.institution_identification.name }}{% if research.affiliation.institution.level_1 %}, {{ research.affiliation.institution.level_1 }}{% endif %}"{% endif %}>{{ research.person_name }}</field> | |
| {% else %} | |
| <field name="value"{% if research.affiliation %} affiliation="{{ research.affiliation.institution.institution_identification.name }}{% if research.affiliation.institution.level_1 %}, {{ research.affiliation.institution.level_1 }}{% endif %}"{% endif %}>{{ research.person_name }}</field> | |
| <field name="value" orcid="{{ research.orcid }}"{% if research.affiliation and research.affiliation.institution and research.affiliation.institution.institution_identification %} affiliation="{{ research.affiliation.institution.institution_identification.name }}{% if research.affiliation.institution.level_1 %}, {{ research.affiliation.institution.level_1 }}{% endif %}"{% endif %}>{{ research.person_name }}</field> | |
| {% else %} | |
| <field name="value"{% if research.affiliation and research.affiliation.institution and research.affiliation.institution.institution_identification %} affiliation="{{ research.affiliation.institution.institution_identification.name }}{% if research.affiliation.institution.level_1 %}, {{ research.affiliation.institution.level_1 }}{% endif %}"{% endif %}>{{ research.person_name }}</field> |
| </element> | ||
|
|
||
| <!-- ========== PUBLISHER ========== --> | ||
| {% with ph=object.journal.publisher_history.first %} |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line 151 accesses object.journal.publisher_history without checking if object.journal is null. Since the journal field is nullable, this could cause a template error. The {% with %} block should include a null check for object.journal before accessing its publisher_history.
| {% for funding in object.fundings.all %} | ||
| <element> | ||
| <field name="value">{{ funding.funding_source.institution.institution_identification.name }}{% if funding.award_id %} grant {{ funding.award_id }}{% endif %}</field> | ||
| </element> |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The funding section accesses a deep chain of nullable relationships (funding.funding_source.institution.institution_identification.name) without null checks. If any intermediate relationship is null, this will cause a template error. Consider adding conditional checks to ensure funding_source, institution, and institution_identification are not None before accessing the name field.
| {% for funding in object.fundings.all %} | |
| <element> | |
| <field name="value">{{ funding.funding_source.institution.institution_identification.name }}{% if funding.award_id %} grant {{ funding.award_id }}{% endif %}</field> | |
| </element> | |
| {% for funding in object.fundings.all %} | |
| {% if funding.funding_source and funding.funding_source.institution and funding.funding_source.institution.institution_identification %} | |
| <element> | |
| <field name="value">{{ funding.funding_source.institution.institution_identification.name }}{% if funding.award_id %} grant {{ funding.award_id }}{% endif %}</field> | |
| </element> | |
| {% endif %} |
| <!-- ========== SOURCE ========== --> | ||
| <element name="source"> | ||
| <element name="none"> | ||
| <field name="value">{{ object.journal.title }}{% if object.issue.volume %}, v.{{ object.issue.volume }}{% endif %}{% if object.issue.number %}, n.{{ object.issue.number }}{% endif %}{% if object.first_page and object.last_page %}, p.{{ object.first_page }}-{{ object.last_page }}{% endif %}{% if object.pub_date_year %}, {{ object.pub_date_year }}{% endif %}</field> |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line 231 accesses object.issue.volume and object.issue.number in the source citation without checking if object.issue is null. Since the issue field is nullable, this could cause a template error. Consider adding a conditional check or restructuring the source generation logic to handle null issue safely.
| <element name="author"> | ||
| {% for research in object.researchers.all %} | ||
| <element> | ||
| {% if research.orcid %} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Rossi-Luciano deixe o código mais enxuto aqui. Transfira a complexidade para o modelo Researcher, assim como foi feito para Article.source ou Article.license_url (ambos property). O Article.license_url ficará disponível em um futuro PR meu
O que esse PR faz?
Modifica template OAI-PMH
article_compile.txtpara geração de metadados nos formatos AOI_DC e MODSProblemas corrigidos:
dc:publisherusava caminho inválidoobject.publisher(Article não tem este campo)dc:sourceusava campo inexistenteobject.sourcedc:rightsusavaobject.license.all()(license é ForeignKey, não ManyToMany)dc:descriptionusava{{ abs }}ao invés de{{ abs.plain_text }}dc:languagenão seguia qualificador ISO padrãoImplementações:
dc:publisherextraído dejournal.publisher_history.first()com suporte a institution/organizationdc:sourcegerado dinamicamente como citação bibliográfica (título, volume, número, páginas, ano)dc:rightscorrigido para ForeignKey + license_statements (ManyToMany)dc:dateexpandido com issued/accessioned/availabledc:identifierestruturado com qualificadores none/doi/uridc:contributor/authorao invés dedc:creator/nonedc:language/isoao invés dedc:language/nonedc:description/abstractao invés dedc:description/noneOnde a revisão poderia começar?
Arquivo:
article/templates/search/indexes/article/article_compile.txtAlgum cenário de contexto que queira dar?
Mantidos os índices Solr originais. A geração de metadados OAI_DC e MODS ocorre via template
article_compile.txt, independente da indexação. Os métodosprepare_mods_*para indexação de campos MODS já foram desenvolvidos e podem ser integrados quando necessário.Screenshots
índice enxuto

formatos disponíveis

exemplo padrão MODS

exemplo padrão OAI_DC

Exemplo de XML gerado
XML original
Formato OAI_DC gerado
Formato MODS gerado
XML após compatibilidade com Biodiversity Heritage Library
Referências
Dublin Core:
OAI-PMH:
Framework: