A company can operate in several fields: industries become a per-person vocabulary, many per company #39

Closed
opened 2026-09-05 14:09:01 +00:00 by tiagoagueda · 0 comments
Owner

Observation

a single company may have several fields of operation, don't restrict to a single one

What exists today

Company.industry is one free-text field of up to 120 characters (jobs/models.py, line 34). It is shown on the company page and in the companies table, searched by the table's box (jobs/views.py, line 52), exported (core/export.py, line 52), and filled by seed_demo. One string per company: a bank that is also an insurer and a software house gets whichever word the person typed first, and "Software, Insurance, Banking" typed into the one box is unsearchable as anything but a string.

There is already a pattern for exactly this: Tag (core/models.py, line 60) is an owner-scoped vocabulary — name, slug, colour, unique per owner — attached to applications through a many-to-many.

Shape

1. An Industry model, per person, many per company. Owner-scoped like Tag (name, slug, unique per owner), and a many-to-many Company.industries. Owner-scoped rather than shared because companies are owner-scoped by design — two people on one instance may describe the same employer differently, and neither should see the other's vocabulary.

2. Entry. A tag-style input on the company form: type, pick from the person's existing industries, or create one on the spot. The same starter list is offered to everyone on an empty vocabulary — a few dozen broad fields in English, French and Portuguese (software, finance, insurance, health, education, public sector, energy, retail, manufacturing, media, non-profit…) — as suggestions, never as a closed list. A person who works in a niche types the niche.

3. Migration. Every existing industry string becomes one Industry row for that owner (deduplicated by slug) linked to the company; the column is then dropped. Nothing is lost and nothing needs retyping.

4. Everywhere the field was used:

  • the company page lists them as small labels; the companies table shows all of them in one cell, and #20's header filter matches any of them;
  • the search box (and #29) matches on industry names;
  • Insights gains applications and outcomes by industry, which the single field could only approximate — and a company in three fields counts in all three, which is the honest reading;
  • export carries the list; the importer accepts both the old single string (format 1) and the list (format 2), so #25's version bump covers this too;
  • #31's CSV import splits an industry column on ; or ,;
  • the API (#12) exposes them as a list of names;
  • seed_demo gives its fictional companies two or three each.

5. Optionally, a standard code beside the name. NACE Rev. 2 sections (the EU's classification, 21 sections A to U) or ISIC could be offered as an optional attribute of an industry, so that two people's "Software" and "IT" can be recognised as the same section in an aggregate. Not in the first cut: the free vocabulary is what people use; the code is a mapping to add when anyone asks for cross-instance comparability.

Classification

Enhancement. Not breaking: the migration converts every existing value, and the export format keeps accepting the old shape.

Open questions

  1. Should the starter list be seeded into every new account's vocabulary, or only offered as suggestions until used? Proposal: suggestions only; an unused vocabulary is noise on the filter.
  2. Merge tool: two industries the person created as near-duplicates ("Fintech", "FinTech") — a merge into action on the vocabulary page, or leave it to editing? Proposal: a merge action; the same is wanted for tags and can share the code.
## Observation > a single company may have several fields of operation, don't restrict to a single one ## What exists today `Company.industry` is one free-text field of up to 120 characters (`jobs/models.py`, line 34). It is shown on the company page and in the companies table, searched by the table's box (`jobs/views.py`, line 52), exported (`core/export.py`, line 52), and filled by `seed_demo`. One string per company: a bank that is also an insurer and a software house gets whichever word the person typed first, and "Software, Insurance, Banking" typed into the one box is unsearchable as anything but a string. There is already a pattern for exactly this: `Tag` (`core/models.py`, line 60) is an owner-scoped vocabulary — name, slug, colour, unique per owner — attached to applications through a many-to-many. ## Shape **1. An `Industry` model, per person, many per company.** Owner-scoped like `Tag` (name, slug, unique per owner), and a many-to-many `Company.industries`. Owner-scoped rather than shared because companies are owner-scoped by design — two people on one instance may describe the same employer differently, and neither should see the other's vocabulary. **2. Entry.** A tag-style input on the company form: type, pick from the person's existing industries, or create one on the spot. The same starter list is offered to everyone on an empty vocabulary — a few dozen broad fields in English, French and Portuguese (software, finance, insurance, health, education, public sector, energy, retail, manufacturing, media, non-profit…) — as *suggestions*, never as a closed list. A person who works in a niche types the niche. **3. Migration.** Every existing `industry` string becomes one `Industry` row for that owner (deduplicated by slug) linked to the company; the column is then dropped. Nothing is lost and nothing needs retyping. **4. Everywhere the field was used:** - the company page lists them as small labels; the companies table shows all of them in one cell, and #20's header filter matches *any* of them; - the search box (and #29) matches on industry names; - **Insights** gains applications and outcomes *by industry*, which the single field could only approximate — and a company in three fields counts in all three, which is the honest reading; - export carries the list; the importer accepts both the old single string (format 1) and the list (format 2), so #25's version bump covers this too; - #31's CSV import splits an industry column on `;` or `,`; - the API (#12) exposes them as a list of names; - `seed_demo` gives its fictional companies two or three each. **5. Optionally, a standard code beside the name.** NACE Rev. 2 sections (the EU's classification, 21 sections A to U) or ISIC could be offered as an optional attribute of an industry, so that two people's "Software" and "IT" can be recognised as the same section in an aggregate. Not in the first cut: the free vocabulary is what people use; the code is a mapping to add when anyone asks for cross-instance comparability. ## Classification Enhancement. Not breaking: the migration converts every existing value, and the export format keeps accepting the old shape. ## Open questions 1. Should the starter list be seeded into every new account's vocabulary, or only offered as suggestions until used? Proposal: suggestions only; an unused vocabulary is noise on the filter. 2. Merge tool: two industries the person created as near-duplicates ("Fintech", "FinTech") — a *merge into* action on the vocabulary page, or leave it to editing? Proposal: a merge action; the same is wanted for tags and can share the code.
tiagoagueda added this to the 0.2.0 milestone 2026-09-05 14:09:01 +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#39
No description provided.