A company's name in each language, taken from Wikidata, with a typed name only where Wikidata has none #673
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#673
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
From the list: “A company can have a different denomination in different languages, for example “Universidade de Aveiro” is “University of Aveiro” (en) and “Université d'Aveiro” (fr). Fetch it from Wikidata. If none is available, only then enable user to define it or fall back to the default.” A company has one name, and nothing in Postulo reads Wikidata.
What exists today
Company.nameis aCharField(200)withUniqueConstraint(owner, name)(src/postulo/jobs/models.py:248,:312-314).company.nameis read 96 times in 54 files (the letters' context atsrc/postulo/documents/rendering.py:476, the report atsrc/postulo/applications/reports.py:454, search, the API, the archive), and it is the key the CSV importer, the duplicate notice and the merge compare (jobs/duplicates.py,jobs/merging.py,core/csv_import.py).src/postulo/plugins/identifiers/schemes.py:167-177) and linked. #42 deferred “Look up” and “Fill from Wikidata”: “behind a deliberate per-request action and never automatically”. Nothing fetches from wikidata.org.src/postulo/resume/translatable.py:16-27). Two tests pin it:tests/test_career_languages.py::test_a_name_that_belongs_to_somebody_else_is_not_translatable(:72-79) and the check that the organisation has no translation box (:305-312). They pin free-text career fields, which are not companies today; #683 and #685 propose that they become companies.docs/THREAT-MODEL.md:74(rule 5):postulo.plugins.http, withpublic_only_client(),deadline_in,untilandread_body(limit=…)(src/postulo/plugins/http.py:206,:225,:241,:342) and the user agentPostulo (+https://source.tiagoagueda.com/postulo/postulo)(src/postulo/plugins/public_addresses.py:29). #366 is the open inventory of outbound destinations; it lists no Wikidata fetch.Special:EntityData/Q29671.jsonis the University of Aveiro, 49,969 bytes, 29 labels.enis “University of Aveiro”,pt“Universidade de Aveiro”,fr“université d'Aveiro” (a lower-case initial, Wikidata's style); the item has an ISNI (P213), coordinates (P625), a website (P856) and a Crunchbase id (P2088), but no official name (P1448).Checked against
mainatf09b6010b.Proposal
CompanyName(anOwnedModel): company,language(LanguageField), the name, its source (Wikidata or typed), when fetched; one per company and language.Company.namestays the one name that identifies the company; uniqueness, duplicates, merge matching and CSV matching are unchanged.postulo.jobs.wikidata: from a Q-number the person already recorded it makes oneGET https://www.wikidata.org/wiki/Special:EntityData/<Q>.jsonthroughpublic_only_client(), with a deadline, a body limit well above the 50 KB seen and below a megabyte, at most three redirects (a merged item redirects) and a check that the answer came fromwww.wikidata.org. It returns{language tag: label}.postulo.core.languages, sopt-bris storedpt-BR; a regional tag falls back to its base language bylanguages.match). Labels in Wikidata'smulcode (the default for all languages, per Wikidata's help page on default values for labels) serve only a language with no label of its own.<bdi>.Company.name_in(language): the stored name for the language, then its base language, thenCompany.name. Used where a document's language is known (letters, the report, a CV once #683 lands). Lists, tables, search keys and the API'snamekeep the name the person typed; the API lists the names beside it.Decisions
TRANSLATABLEas they are here, since they pin free-text fields, and settle the career side in #683 and #685, where an organisation becomes a company and a CV can print its name in the CV's language. The docstring attranslatable.py:16-27then gets the new reason.Special:EntityData(documented for reading one known entity),wbgetentities(can ask for chosen languages only), or SPARQL (60-second deadline, 60 seconds of processing per 60 seconds per client, 30 error queries a minute, 5 parallel queries per IP). Recommended:Special:EntityData; SPARQL finds things by characteristics, which one item does not need.<client name>/<version> (<contact information>) <library>/<version>, contact being “an email address, a website, or a wiki user”, and warns generic agents “may be blocked without notice”; on a429it says to stop sending. Recommended:Postulo/<version> (https://source.tiagoagueda.com/postulo/postulo) httpx/<version>through the client'sheaders=, and a per-person allowance like the other named rates (src/postulo/config/settings/base.py:411-452), one fetch per press, no retry, no background refresh. Whether a project address satisfies the policy for software every operator runs is not verified.src/postulo/plugins/identifiers/__init__.py:59) stays true: the lookup lives injobs.Prior art
Verified on 2026-10-02 from the documents, PyPI and OSV. None of these packages has an advisory on OSV, so the maintainer's “unpatched severe advisory” exception does not apply; the reason to reject is the guard.
Special:EntityData/<Q>.jsonis documented as the way to read an entity you already know; the data is CC0, so no attribution is owed, though the card should say where a name came from.wikidata0.9.0 (dahlia/wikidata; GPL-3.0-or-later, combinable with AGPL-3.0; released 2025-10-31; declares no runtime dependency). Reject: a dependency for one JSON parse; its HTTP layer is not verified here, and a client with its own requests sits outsideplugins.http, which rule 5 requires.qwikidata0.4.2 (Apache-2.0, 2022-11-09) andSPARQLWrapper2.0.0 (W3C, 2022-03-13, needsrdflib): reject, stale, and SPARQL is not needed.pywikibot11.8.0 (MIT, 2026-09-29) andwikibaseintegrator0.12.15 (MIT, 2025-12-21): bot and write frameworks on therequestslibrary (a transitive dependency inuv.lock; Postulo itself useshttpx). Reject: far more than a read, outside the guard.applications/ical.pyanddocuments/docx.py, which exist because a library was heavier than the part of a format used: here the part used is “GET one JSON document and readlabels”, over the existing guarded client. Not verified: whether a maintained library could be told to useplugins.http's client.Builds on
Issues #42 (identifier, lookup deferred), #366 (outbound inventory), #131 (career entries per language), #337 (BCP 47 tags). #653 for the library-first rule.
Needed by
#675 (the online half), #701 (Wikidata coordinates), #683 and #685 (a CV printing a company's name in its language).
Done when
tests/test_company_names.py: labels reduced to the registry's languages and written throughcore.languages; the base-language fallback; control and bidi characters cleaned; an oversize, slow or non-JSON answer refused with a sentence; a redirect to another host refused; a typed name refused where Wikidata gave one;Company.name_infalls back as above; nothing fetched by a page load, save or merge.tests/security/test_outbound.py(new cases,MockTransportonly): the request goes throughpublic_only_clientwith the Wikimedia-style agent; the isolation sweep (tests/security/test_isolation_sweep.py) andtests/test_page_coverage.pycover the new views.tests/test_merging.py,tests/test_search.py,tests/test_api.py,tests/test_export.py: merge, search, the API's names, the round trip. New record type, so the standing checklist applies: the archive format goes up by one (src/postulo/core/export.pyFORMAT_VERSION, to one more than whatever it is when the commit lands; 29 atf09b6010b;tests/test_export.py:96follows it); the candidate format (4) does not change; the processing record (src/postulo/core/gdpr.py) concerns contacts and is unchanged;tests/test_wiki_surface.pyonly if the plugin surface gains a name; the CV print choice (#308) is #683's.From the list of improvements of 2026-10-02.