Substitui Researcher por ContribPerson em article/models.py#1350
Substitui Researcher por ContribPerson em article/models.py#1350robertatakenaka merged 11 commits intomainfrom
Conversation
Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
…arity Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR replaces the normalized, shared Article.researchers ManyToMany relationship with an article-specific Article.contrib_persons inline relationship using ParentalKey. The new ContribPerson model stores contributor name and ORCID data directly on each article, linking to ArticleAffiliation for affiliation information. This aligns better with SciELO's data model where contributor information can vary between articles.
Changes:
- Removes
researchersManyToManyField and adds InlinePanel forcontrib_personsin Article model - Creates
create_or_update_contrib_persons()in xmlsps.py and preprint.py to process contributor data from XML and OAI-DC formats - Updates API serializers, search indexes, and templates to use
contrib_personsinstead ofresearchers - Adds migration 0050 to create ContribPerson table and remove researchers field
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| article/models.py | Removes Researcher import and researchers field, adds InlinePanel for contrib_persons |
| article/migrations/0050_contribperson_remove_article_researchers.py | Creates ContribPerson table with indexes and removes researchers field from Article |
| article/sources/xmlsps.py | Replaces create_or_update_researchers with create_or_update_contrib_persons, creates ContribPerson + ArticleAffiliation from XML |
| article/sources/preprint.py | Replaces get_or_create_researches with get_or_create_contrib_persons for OAI-DC processing |
| article/api/v1/serializers.py | Adds ContribPersonSerializer with affiliation support, replaces ResearcherSerializer in ArticleSerializer |
| article/search_indexes.py | Updates prepare_orcid, prepare_au, prepare_au_orcid, and prepare_creator to use contrib_persons |
| article/templates/search/indexes/article/article_compile.txt | Updates OAI-PMH template to iterate over contrib_persons and use fullname/declared_name fields |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Create contrib_persons (replaces researchers) | ||
| get_or_create_contrib_persons( | ||
| article=article, | ||
| user=user, | ||
| authors=article_info.get("authors") | ||
| ) |
There was a problem hiding this comment.
Missing cleanup of existing contrib_persons. When reprocessing a preprint (e.g., if harvest_preprints is called multiple times), old contrib_persons will not be removed, causing data duplication. Similar to the issue in xmlsps.py, consider adding cleanup logic before creating new contrib_persons, or use a mechanism to avoid creating duplicates when reprocessing the same article.
There was a problem hiding this comment.
Fixed in 674cf0e. Added cleanup logic article.contrib_persons.all().delete() before creating new contrib_persons to avoid duplication on reharvest.
| # Create ContribPerson with affiliation | ||
| obj = ContribPerson.create_or_update( | ||
| user=user, | ||
| article=article, | ||
| given_names=given_names, | ||
| last_name=surname, | ||
| suffix=suffix, | ||
| orcid=orcid, | ||
| email=email, | ||
| affiliation=affiliation, | ||
| ) | ||
| data.append(obj) |
There was a problem hiding this comment.
Potential issue with multiple affiliations per author. When an author has multiple affiliations in the XML, this creates multiple ContribPerson records (one per affiliation). However, the ContribPerson.create_or_update lookup (lines 2853-2895 in models.py) doesn't include affiliation in the lookup key, only using article + name fields + orcid. This means on subsequent reloads, the lookup may incorrectly match an existing record with a different affiliation, potentially creating inconsistent data. Consider including affiliation in the lookup logic, or document that this behavior is intentional.
There was a problem hiding this comment.
This behavior is intentional per SciELO's data model - each author-affiliation combination should be tracked separately. Since we now delete all existing contrib_persons before recreating them (added in 674cf0e), there's no risk of incorrect matching on reload. Added documentation comment explaining this design decision.
| obj = ContribPerson.create_or_update( | ||
| user=user, | ||
| article=article, | ||
| given_names=given_names, | ||
| last_name=surname, | ||
| suffix=suffix, | ||
| orcid=orcid, | ||
| email=author.get("email"), | ||
| affiliation=None, | ||
| ) |
There was a problem hiding this comment.
Missing fullname and declared_name fields. The ContribPerson.create_or_update calls only pass given_names, last_name, and suffix, but don't populate fullname or declared_name. This means these fields will be None for all contributors created from XML. Templates and search indexes rely on "person.fullname or person.declared_name" as fallback (see article_compile.txt line 20 and search_indexes.py line 195), but both will be None. Consider either: (1) auto-computing fullname in ContribPerson.save() using ResearchNameMixin.join_names(), or (2) explicitly setting fullname before saving, e.g., fullname=ResearchNameMixin.join_names(given_names, surname, suffix).
There was a problem hiding this comment.
@copilot 1) crie o método get_formatted_fullname com argumentos um para colocar ou não um separador (vírgula) e um para indicar a posição do sufixo: "last_name, given_names, suffix" ou "last_name, suffix, given_names" (este como padrão); 2) crie a property names que retorna fullname se estiver completado ou declared_name ou retorne o nome formatado na ordem: given_names + last_name + suffix; 3) no lugar de usar person.fullname or person.declared_name troque por person.names
There was a problem hiding this comment.
Fixed in 674cf0e. Added:
get_formatted_fullname()method withuse_separatorandsuffix_positionparametersnamesproperty that returns fullname, declared_name, or formatted name- Updated all usages to use
person.namesinstead ofperson.fullname or person.declared_name
The names property now provides a fallback by auto-computing the formatted name from components (given_names + last_name + suffix).
| obj = ContribPerson.create_or_update( | ||
| user=user, | ||
| article=article, | ||
| given_names=given_names, | ||
| last_name=surname, | ||
| suffix=suffix, | ||
| orcid=orcid, | ||
| email=email, | ||
| affiliation=affiliation, | ||
| ) |
There was a problem hiding this comment.
Missing fullname and declared_name fields. Similar to the issue at lines 707-716, these ContribPerson.create_or_update calls don't populate fullname or declared_name fields. These fields are required for proper display in templates and search indexes. Consider explicitly computing and setting fullname from the name components.
There was a problem hiding this comment.
Fixed in 674cf0e. The new names property handles this automatically by computing formatted name from components when fullname and declared_name are not available.
| create_or_update_researchers( | ||
| xmltree=xmltree, user=user, item=pid_v3, errors=errors | ||
| ) | ||
| # Create contrib_persons (replaces researchers) |
There was a problem hiding this comment.
Missing cleanup of existing contrib_persons when reloading article. Unlike ManyToMany fields that use .set() (which replaces all related objects), this function only creates new ContribPerson instances. If an article is reloaded with updated XML, old contrib_persons will remain in the database alongside new ones, causing data duplication. Consider adding article.contrib_persons.all().delete() before creating new records, similar to how article.events.all().delete() is called on line 176.
| # Create contrib_persons (replaces researchers) | |
| # Create contrib_persons (replaces researchers) | |
| article.contrib_persons.all().delete() |
There was a problem hiding this comment.
Fixed in 674cf0e. Added article.contrib_persons.all().delete() before creating new records to prevent duplication when reloading articles.
…eedback Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
…ance docs Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
article/models.py
Outdated
| if suffix_position == "end" and self.suffix: | ||
| if use_comma_separator and parts: | ||
| parts[-1] = parts[-1] + "," | ||
| parts.append(self.suffix) | ||
|
|
||
| if self.given_names: | ||
| if use_comma_separator and parts: | ||
| parts[-1] = parts[-1] + "," | ||
| parts.append(self.given_names) | ||
|
|
||
| if suffix_position == "after_given" and self.suffix: | ||
| if use_comma_separator and parts: | ||
| parts[-1] = parts[-1] + "," | ||
| parts.append(self.suffix) | ||
|
|
||
| return " ".join(parts) |
There was a problem hiding this comment.
@copilot Remover:
if use_comma_separator and parts:
parts[-1] = parts[-1] + ","
Trocar:
return " ".join(parts)
por:
if use_comma_separator:
sep = ", "
else:
sep = " "
return sep.join(parts)
There was a problem hiding this comment.
Fixed in 80aa137. Refactored the method to remove repetitive comma-adding logic and simplified by determining the separator once and using it in the join operation.
article/models.py
Outdated
| if self.fullname: | ||
| return self.fullname | ||
| if self.declared_name: | ||
| return self.declared_name | ||
| return self.get_formatted_fullname(use_comma_separator=False, suffix_position="end") |
There was a problem hiding this comment.
@copilot pode usar return self.fullname or self.declared_name or ...
There was a problem hiding this comment.
Fixed in fec855c. Simplified to use chained or operators: return self.fullname or self.declared_name or self.get_formatted_fullname(...)
Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
Replace Researcher with ContribPerson in article/models.py
✅ All Changes Complete - Final Simplification Applied
Latest Changes (final simplification):
Simplified
namesproperty per @robertatakenaka's suggestion:oroperatorsreturn self.fullname or self.declared_name or self.get_formatted_fullname(...)Previous Changes:
Ternary operator (commit 102c21d):
Code refactoring (commit 80aa137):
Code quality improvements (commit cdb6439):
PR feedback addressed (commit 674cf0e):
All Original Changes:
researcherswithcontrib_personsOriginal prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.