Repository navigation
Beyond CRUD Flows
PULSE is more than a CRUD app (create / read / update / delete forms). Analysts need the system to recognize when two messy records are really the same organization or person, even when the spelling, language or formatting differs. That recognition work is Entity Resolution (#4).
The first building block is identifier normalization (#26): before we match anything, we clean names, websites, LinkedIn links, emails and phone numbers into one standard form. The original text from the source is kept; the cleaned value sits beside it for comparison.
Quebec ecosystem data mixes French and English. The same company might appear as:
Groupe Exemple ltéeGROUPE EXEMPLEGroupe Exemple Inc.- website
https://www.boutique.exemple.qc.ca/a-propos
Without cleaning, the dashboard would show three “different” organizations and undercount activity. After normalization, those name variants all become the comparison key groupe exemple, and the website becomes exemple.qc.ca. Downstream matching (#61) can then link them with confidence.
This is also our main beyond-CRUD evidence for the course code rubric: real domain rules and libraries, not just storing and retrieving rows.
| Piece | Role |
|---|---|
normalization.py |
One clean-up function per identifier type (name, domain, LinkedIn, email, phone) |
legal_suffixes.toml |
Editable list of company endings to strip (inc, ltée, s.e.n.c., …) so we do not hard-code them in Python |
test_normalization.py |
Unit tests for normal cases, empty input, accents and spelling variants |
The module is pure logic: it takes strings in and returns cleaned keys (or “unusable”). It does not talk to the database or the internet. Other pipeline steps (for example mapping Omerlo payloads in #57) call it after they pull fields out of a source record.
| Field | Business intent | What the code does |
|---|---|---|
| Organization name | Treat French/English legal endings and punctuation as noise | Lowercase, remove accents for comparison, collapse & / et / and, strip trailing legal forms from the config file |
| Person name | Keep hyphenated Quebec names consistent | Lowercase, remove accents; treat hyphens like spaces so Marie-Ève and Marie Eve match |
| Website / domain | Compare the real company site, not a deep link | Drop https, www, path and query; keep the registered domain (e.g. exemple.qc.ca) |
| One key per company or person profile | Reduce to company/<slug> or in/<slug>, ignore tracking parameters |
|
| Stable address + optional domain signal | Lowercase and trim; also expose the domain part for weaker matching | |
| Phone | One international form for Canadian numbers | Convert to E.164 (default region Canada). Bad numbers return “empty / unparseable / impossible” warnings instead of crashing |
Chosen on Infrastructure and Tools (#15):
| Library | Why (business) | How (technical) |
|---|---|---|
phonenumbers |
Phone formats vary a lot; we need one standard | Formats to E.164 with Canada as the default region |
tldextract |
.qc.ca and similar suffixes are easy to get wrong by hand |
Uses the public suffix list; offline only (no download at runtime) |
unicodedata (built into Python) |
Accents should not block a match | Folds Québec → quebec without an extra dependency |
Source data (Omerlo, etc.)
→ extract fields
→ normalize identifiers ← this page / #26
→ match same entities ← #61 (later)
→ scores, changes, dashboard
Normalization alone does not decide “these two rows are the same company.” It only makes that decision possible and reliable.
PULSE Wiki
- Home
- Team Structure
- Product Roadmap
- Meeting Minutes
- Presentations
- Risks
- User Consent and EULA
- Legal and Ethical Issues
- PULSE Identity and Business Orientation
- Personas
- Diversity Statement
- Overall Architecture and Class Diagrams
- Architecture Decision Records
- Infrastructure and Tools
- Beyond-CRUD Flows
- Naming Conventions
- Code Style
- Testing Plan and Continuous Integration
- Security
- Performance
- Deployment Plan and Infrastructure
- Missing Knowledge and Independent Learning
-
Iteration and Release Notes
- Iteration 1 (Oct 6, 2026)
- Iteration 2 (Oct 20, 2026)
- Iteration 3 (Nov 3, 2026)
- Iteration 4 (Nov 17, 2026)
- Iteration 5 (Dec 1, 2026)
- Iteration 6 (Dec 15, 2026)
- Iteration 7 (Jan 19, 2027)
- Iteration 8 (Feb 5, 2027)
- Iteration 9 (Feb 16, 2027)
- Iteration 10 (Mar 2, 2027)
- Iteration 11 (Mar 16, 2027)
- Iteration 12 (Mar 30, 2027)
- Iteration 13 (Apr 13, 2027)