Your career: citations, read as how often your published work has been cited, and when it was counted #697

Closed
opened 2026-10-03 01:59:08 +00:00 by tiagoagueda · 0 comments
Owner

The maintainer's list, under Your career, names “Citations” and does not say what it means. This issue states a reading, offers the others under Decisions, and proposes the smallest record that serves the one recommended: a count of how often a person's published work has been cited, from a named source, on a named date, optionally for one of their publications (#687). It follows the shape and the checklist settled in #692. It is 1.0.0 because the meaning is the maintainer's to confirm and the record needs publications first.

What exists today

  • Nothing. The career has no citation record and no publication record either; the only trace of a paper is a Link of kind “Publication” (src/postulo/resume/models.py:273-318). The kinds are in src/postulo/resume/registry.py:28-80. #687 proposes the publication record this one points at.
  • Where the word comes from. The note's eight names are the headings Europass's CV template lists under Additional information (Cedefop, Instructions for filling in the Europass CV, © European Union 2002-2017, p. 5; the same sheet is hosted by other bodies, among them europass.at under the name Europass_CV_Instructions_EN.pdf, not fetched). The sheet never defines “Citations”; it gives examples for Publication, Project, Membership and References only. The older XML had an Achievement code citations (Europass XML schema documentation v3.4.0, §4.7.6), and the same document's example of an attached resource is “List of Citations” beside a “Video CV” link. The current Candidate format has no element for it. The heading sits in a list of academic and professional items (publications, presentations, conferences, honours, memberships), which is why the reading below is the researcher's.
  • The plugin reads none of it (src/postulo/plugins/europass/candidate.py:136-147): a citation typed into Others arrives as a Project (:603-619), and PublicationHistory is named in the “nowhere to go” notice (tests/test_europass_candidate.py:561-577).
  • A count is a fact from outside, and it ages. Postulo asks no bibliographic service anything today: “nothing here asks ORCID whether an identifier exists” (src/postulo/accounts/identifiers.py:21-23), and the outbound requests are inventoried in #366. The person's ORCID, ResearcherID and Scopus Author ID are stored identifiers (#46, #638). #688 proposes the first opt-in lookups, for publications.
  • How an entry points at another one. A skill names its group by a local id in the candidate file and the archive, and the reader resolves it (resume/candidate.py:170-171, :1157-1182); a new section that points at a publication does the same.
  • Printing a number with its date. A CV prints a month and a year for a dated entry (src/postulo/templates/documents/themes/base_cv.html:144-147), and translated plurals are used wherever a count is said (overview.html:101).

Checked against main at f09b6010b.

Proposal

  1. One model, Citation (“Citation count”; heading “Citations”). A ResumeItem with: source (required text, 200: “Google Scholar”, “Scopus”, “Web of Science”, “Crossref”, “OpenCitations”, or whatever the person counted from; the form offers those as suggestions in a datalist, not as a closed list); count (a whole number from 1 to 10,000,000); as_of (a date, required, proposed as today: a count with no date is a claim that cannot age); publication (optional, one of the owner's publications; empty means the body of work as a whole; cascade, since a count of a deleted paper means nothing); url (optional: a profile or record page); summary (optional, one line). Slug citation, placed in OVERVIEW_ORDER where #692 puts it. Needs #687 for the link; without it the field is left out and added with it.
  2. The CV prints “Cited 1,240 times (Scopus, September 2026)” (a plural-aware message, in the document's language), and, for an entry tied to a publication, its title first. Both base themes, the plain text and the Word file print the same line. The entry is on a CV only when the person puts it there.
  3. Translations: summary only. The source is somebody's name, the count a number.
  4. Archive and candidate file: block citations in RESUME_FIELDS, RESUME_MODELS and TRANSLATION_SECTIONS; the importer's maps; a Kind whose names are the source and the title of the publication it names (as a skill's key includes its group) and whose dates are as_of, so a later count from the same source is a new entry and the review page says “You have one like it with other dates”. The publication_id is a local id resolved after publications, as group_id is after groups; a reference to a publication not in the file leaves the count as one for the whole body of work and says so. Both formats go up by one from whatever they are when the commit lands. count is read through the form's own bounds.
  5. Europass. No reading is added: the Candidate has no element for it, and a count is not something to infer from prose. The record is shaped so an exporter could write a citation as a line in PublicationHistory or Others.
  6. Search by source, summary and the publication's title, linking to #section-citation. Demo data: one for the whole body of work and one for a paper. Wiki: Your career record says what an entry is and that nothing is counted for the person.
  7. Not in this issue: looking a count up. A “Count it again” action on an entry whose publication has a DOI would call a bibliographic service through the guarded client (postulo.plugins.http, with size, time and address limits), opt-in per request and never automatic, as #688 does for a publication. It is a separate issue once #688's client exists; the record stores the source and as_of it would write.

Decisions

These are recommendations the maintainer has not confirmed.

  • What “Citations” means. Options: (a) the list of works that cite the person's work, one entry per citing work; (b) how often the person's work has been cited, as counts with a source and a date; (c) what others have said about the person, quotations and mentions in the press or in testimonials; (d) the text that accompanies an award. Recommended: (b). It is what a researcher's CV prints (“cited 1,240 times, Scopus, September 2026”), it needs one small table, and it points at the publications record without copying it. (a) would copy a publication's whole shape per citing work, has no sensible ceiling (the Crossref record of a famous paper counts tens of thousands) and goes stale; a lookup could one day suggest the citing works from a DOI, as it could suggest publications. (c) is a testimonial and sits closer to #666 and to references than to a researcher's CV; quoting a third party has consent and copyright questions this issue would rather not open. (d) is a line in an honour's summary (#693), not a section. Question for the maintainer: is (b) what the list meant? If it is (c), this issue is replaced by one for quotations.
  • A section, or fields on a publication. A count for one paper could be two fields on it. Recommended: a section, because the common case is a count for the whole body of work from a profile page, which no publication owns, and because a count has a source and a date that a paper's own fields should not carry. An entry may still name one publication.
  • A closed list of sources or text. Services come and go, and one instance's source is another's. Recommended: text with suggestions. Names are plain words; no logo is involved (TRADEMARKS.md, #654 is about marks).
  • Required date. Recommended: required, defaulted to today in the form. An undated count on a CV is the one thing here that can quietly mislead.
  • Zero. Recommended: refused. Nobody lists “cited 0 times”.
  • Where it is counted from. The person types it, or later the lookup writes it. Recommended: typed first; the record does not depend on a lookup and keeps working with the lookup absent.
  • The person's identifiers. The ORCID, ResearcherID and Scopus Author ID already stored could build the profile link. Recommended: not now; url is typed, and Postulo builds no link nobody asked for.

Prior art

No dependency in this issue. For the later lookup, from sources read in this session:

  • Crossref's REST API returns is-referenced-by-count in a work's record: a request for one DOI on 2026-10-02 returned the field and an indexed timestamp for the record. The terms (the polite pool, rate limits) and the Crossref client libraries are compared in #688.
  • OpenCitations Index API v2 (api.opencitations.net/index/v2): /citation-count/{id} for a DOI, PMID, ISSN, ISBN or OMID, and /citations/{id} for the works citing it; a token is optional but recommended; “180 requests/minute per IP address”; its page licenses the API documentation under CC BY 4.0 and says the software behind it is ISC; the licence of the index data itself is not stated there and is not verified here, and a lookup issue must check it.
  • Google Scholar shows an author's citation counts on a profile page (https://scholar.google.com/intl/en/scholar/citations.html, from a search result, not fetched); whether it offers any API or permits automated queries is not verified, and a lookup issue must check before naming it.
  • Hand-written standards on purpose elsewhere: applications/ical.py and documents/docx.py. The precedent does not apply: no file format is written here.

Builds on

Issues #46 and #638 (the person's research identifiers), #181 (the candidate file), #203 (the order of entries), #244 and #318 (the Europass reader), #366 (the inventory of outbound requests). New issues of this set: #692 (shape and checklist), #687 (publications), #688 (looking a publication up), #693 (honours and awards).

Needed by

A lookup of a count from a DOI (not filed in this set) builds on it.

Done when

  • A new tests/test_citations.py: create, edit, delete and move through the registry; count accepts 1 to 10,000,000 and refuses 0, a negative and text; as_of is required and proposed as today; an entry may name one of the owner's publications and a foreign publication is refused by the form and by the save; deleting a publication deletes its counts; the CV prints the line, plural-aware, with its date, in the HTML themes, the plain text and the Word file, and a portfolio prints it without an empty heading.
  • tests/test_career_sections.py (the #692 guard) passes with the section; tests/test_career_languages.py gains “a citation translates its summary only”.
  • The standing checklist, for a new record type: the archive format goes up by one (core/export.py FORMAT_VERSION, 29 at f09b6010b; one more than whatever it is when the commit lands) with a round trip in tests/test_export.py, a CV holding one and an older archive that still loads; the candidate format (4) goes up by one with its line in SHAPES (tests/test_candidate_file.py:372-387), a review row for a good entry, one with a missing publication and one refused, and tests/security/test_candidate_file.py for hostile values; tests/test_europass_candidate.py asserts a citation is not invented from a file; the API is unchanged (there is none for the career); search finds one (tests/test_search.py); CV print choice (#308) and the processing record (core/gdpr.py) do not apply; tests/test_wiki_surface.py is untouched; tests/test_page_coverage.py needs no entry; tests/security/test_isolation_sweep.py and a per-section test show another account's entry is a 404; account deletion removes them (tests/test_account_deletion.py); strings, including the plural message, are in the working catalogues (en-GB, fr-FR, pt-PT as drafts) and Weblate (#349).
  • The wiki and the CHANGELOG line (ends (#N)) are in the same commit.

From the list of improvements of 2026-10-02.

The maintainer's list, under Your career, names “Citations” and does not say what it means. This issue states a reading, offers the others under Decisions, and proposes the smallest record that serves the one recommended: a count of how often a person's published work has been cited, from a named source, on a named date, optionally for one of their publications (#687). It follows the shape and the checklist settled in #692. It is 1.0.0 because the meaning is the maintainer's to confirm and the record needs publications first. ## What exists today - **Nothing.** The career has no citation record and no publication record either; the only trace of a paper is a `Link` of kind “Publication” (`src/postulo/resume/models.py:273-318`). The kinds are in `src/postulo/resume/registry.py:28-80`. #687 proposes the publication record this one points at. - **Where the word comes from.** The note's eight names are the headings Europass's CV template lists under *Additional information* (Cedefop, *Instructions for filling in the Europass CV*, © European Union 2002-2017, p. 5; the same sheet is hosted by other bodies, among them europass.at under the name `Europass_CV_Instructions_EN.pdf`, not fetched). The sheet never defines “Citations”; it gives examples for *Publication*, *Project*, *Membership* and *References* only. The older XML had an `Achievement` code `citations` (*Europass XML schema documentation* v3.4.0, §4.7.6), and the same document's example of an attached resource is “List of Citations” beside a “Video CV” link. The current Candidate format has no element for it. The heading sits in a list of academic and professional items (publications, presentations, conferences, honours, memberships), which is why the reading below is the researcher's. - **The plugin reads none of it** (`src/postulo/plugins/europass/candidate.py:136-147`): a citation typed into `Others` arrives as a Project (`:603-619`), and `PublicationHistory` is named in the “nowhere to go” notice (`tests/test_europass_candidate.py:561-577`). - **A count is a fact from outside, and it ages.** Postulo asks no bibliographic service anything today: “nothing here asks ORCID whether an identifier exists” (`src/postulo/accounts/identifiers.py:21-23`), and the outbound requests are inventoried in #366. The person's ORCID, ResearcherID and Scopus Author ID are stored identifiers (#46, #638). #688 proposes the first opt-in lookups, for publications. - **How an entry points at another one.** A skill names its group by a local id in the candidate file and the archive, and the reader resolves it (`resume/candidate.py:170-171`, `:1157-1182`); a new section that points at a publication does the same. - **Printing a number with its date.** A CV prints a month and a year for a dated entry (`src/postulo/templates/documents/themes/base_cv.html:144-147`), and translated plurals are used wherever a count is said (`overview.html:101`). Checked against `main` at `f09b6010b`. ## Proposal 1. **One model, `Citation`** (“Citation count”; heading “Citations”). A `ResumeItem` with: `source` (required text, 200: “Google Scholar”, “Scopus”, “Web of Science”, “Crossref”, “OpenCitations”, or whatever the person counted from; the form offers those as suggestions in a datalist, not as a closed list); `count` (a whole number from 1 to 10,000,000); `as_of` (a date, required, proposed as today: a count with no date is a claim that cannot age); `publication` (optional, one of the owner's publications; empty means the body of work as a whole; cascade, since a count of a deleted paper means nothing); `url` (optional: a profile or record page); `summary` (optional, one line). Slug `citation`, placed in `OVERVIEW_ORDER` where #692 puts it. Needs #687 for the link; without it the field is left out and added with it. 2. **The CV** prints “Cited 1,240 times (Scopus, September 2026)” (a plural-aware message, in the document's language), and, for an entry tied to a publication, its title first. Both base themes, the plain text and the Word file print the same line. The entry is on a CV only when the person puts it there. 3. **Translations**: `summary` only. The source is somebody's name, the count a number. 4. **Archive and candidate file**: block `citations` in `RESUME_FIELDS`, `RESUME_MODELS` and `TRANSLATION_SECTIONS`; the importer's maps; a `Kind` whose `names` are the source and the title of the publication it names (as a skill's key includes its group) and whose `dates` are `as_of`, so a later count from the same source is a new entry and the review page says “You have one like it with other dates”. The `publication_id` is a local id resolved after publications, as `group_id` is after groups; a reference to a publication not in the file leaves the count as one for the whole body of work and says so. Both formats go up by one from whatever they are when the commit lands. `count` is read through the form's own bounds. 5. **Europass.** No reading is added: the Candidate has no element for it, and a count is not something to infer from prose. The record is shaped so an exporter could write a citation as a line in `PublicationHistory` or `Others`. 6. **Search** by source, summary and the publication's title, linking to `#section-citation`. **Demo data**: one for the whole body of work and one for a paper. **Wiki**: *Your career record* says what an entry is and that nothing is counted for the person. 7. **Not in this issue:** looking a count up. A “Count it again” action on an entry whose publication has a DOI would call a bibliographic service through the guarded client (`postulo.plugins.http`, with size, time and address limits), opt-in per request and never automatic, as #688 does for a publication. It is a separate issue once #688's client exists; the record stores the `source` and `as_of` it would write. ## Decisions These are recommendations the maintainer has not confirmed. - **What “Citations” means.** Options: (a) the list of works that cite the person's work, one entry per citing work; (b) how often the person's work has been cited, as counts with a source and a date; (c) what others have said about the person, quotations and mentions in the press or in testimonials; (d) the text that accompanies an award. **Recommended: (b).** It is what a researcher's CV prints (“cited 1,240 times, Scopus, September 2026”), it needs one small table, and it points at the publications record without copying it. (a) would copy a publication's whole shape per citing work, has no sensible ceiling (the Crossref record of a famous paper counts tens of thousands) and goes stale; a lookup could one day *suggest* the citing works from a DOI, as it could suggest publications. (c) is a testimonial and sits closer to #666 and to references than to a researcher's CV; quoting a third party has consent and copyright questions this issue would rather not open. (d) is a line in an honour's summary (#693), not a section. **Question for the maintainer:** is (b) what the list meant? If it is (c), this issue is replaced by one for quotations. - **A section, or fields on a publication.** A count for one paper could be two fields on it. **Recommended: a section**, because the common case is a count for the whole body of work from a profile page, which no publication owns, and because a count has a source and a date that a paper's own fields should not carry. An entry may still name one publication. - **A closed list of sources or text.** Services come and go, and one instance's source is another's. **Recommended: text with suggestions.** Names are plain words; no logo is involved (`TRADEMARKS.md`, #654 is about marks). - **Required date.** **Recommended: required**, defaulted to today in the form. An undated count on a CV is the one thing here that can quietly mislead. - **Zero.** **Recommended: refused.** Nobody lists “cited 0 times”. - **Where it is counted from.** The person types it, or later the lookup writes it. **Recommended:** typed first; the record does not depend on a lookup and keeps working with the lookup absent. - **The person's identifiers.** The ORCID, ResearcherID and Scopus Author ID already stored could build the profile link. **Recommended:** not now; `url` is typed, and Postulo builds no link nobody asked for. ## Prior art No dependency in this issue. For the later lookup, from sources read in this session: - **Crossref's REST API** returns `is-referenced-by-count` in a work's record: a request for one DOI on 2026-10-02 returned the field and an `indexed` timestamp for the record. The terms (the polite pool, rate limits) and the Crossref client libraries are compared in #688. - **OpenCitations Index API v2** (`api.opencitations.net/index/v2`): `/citation-count/{id}` for a DOI, PMID, ISSN, ISBN or OMID, and `/citations/{id}` for the works citing it; a token is optional but recommended; “180 requests/minute per IP address”; its page licenses the API documentation under CC BY 4.0 and says the software behind it is ISC; the licence of the index data itself is not stated there and is not verified here, and a lookup issue must check it. - **Google Scholar** shows an author's citation counts on a profile page (<https://scholar.google.com/intl/en/scholar/citations.html>, from a search result, not fetched); whether it offers any API or permits automated queries is not verified, and a lookup issue must check before naming it. - Hand-written standards on purpose elsewhere: `applications/ical.py` and `documents/docx.py`. The precedent does not apply: no file format is written here. ## Builds on Issues #46 and #638 (the person's research identifiers), #181 (the candidate file), #203 (the order of entries), #244 and #318 (the Europass reader), #366 (the inventory of outbound requests). New issues of this set: #692 (shape and checklist), #687 (publications), #688 (looking a publication up), #693 (honours and awards). ## Needed by A lookup of a count from a DOI (not filed in this set) builds on it. ## Done when - A new `tests/test_citations.py`: create, edit, delete and move through the registry; `count` accepts 1 to 10,000,000 and refuses 0, a negative and text; `as_of` is required and proposed as today; an entry may name one of the owner's publications and a foreign publication is refused by the form and by the save; deleting a publication deletes its counts; the CV prints the line, plural-aware, with its date, in the HTML themes, the plain text and the Word file, and a portfolio prints it without an empty heading. - `tests/test_career_sections.py` (the #692 guard) passes with the section; `tests/test_career_languages.py` gains “a citation translates its summary only”. - The standing checklist, for a new record type: the archive format goes up by one (`core/export.py` `FORMAT_VERSION`, 29 at `f09b6010b`; one more than whatever it is when the commit lands) with a round trip in `tests/test_export.py`, a CV holding one and an older archive that still loads; the candidate format (4) goes up by one with its line in `SHAPES` (`tests/test_candidate_file.py:372-387`), a review row for a good entry, one with a missing publication and one refused, and `tests/security/test_candidate_file.py` for hostile values; `tests/test_europass_candidate.py` asserts a citation is not invented from a file; the API is unchanged (there is none for the career); search finds one (`tests/test_search.py`); CV print choice (#308) and the processing record (`core/gdpr.py`) do not apply; `tests/test_wiki_surface.py` is untouched; `tests/test_page_coverage.py` needs no entry; `tests/security/test_isolation_sweep.py` and a per-section test show another account's entry is a 404; account deletion removes them (`tests/test_account_deletion.py`); strings, including the plural message, are in the working catalogues (en-GB, fr-FR, pt-PT as drafts) and Weblate (#349). - The wiki and the CHANGELOG line (ends `(#N)`) are in the same commit. From the list of improvements of 2026-10-02.
tiagoagueda added this to the 1.0.0 milestone 2026-10-03 01:59:08 +00:00
tiagoagueda modified the milestone from 1.0.0 to 0.6.0 2026-10-25 17:05:45 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo#697
No description provided.