Reported as "LinkedIn is not showing though the resume has it". The LinkedIn
was never the problem.
Traced on the live application (Mohammad Raza, inbox row 2f81cebc). Its stored
resume_text is 4,555 characters over 2,278 lines, and every one of those lines
is exactly one character long. The CV really does say
LinkedIn:linkedin.com/in/mohammad-raza-digital-marketer
but it is stored as forty separate lines, so nothing that looks for a substring
can find it. Not a link annotation, not an image, not OCR: pypdf's default mode
breaks after every glyph on PDFs whose author positioned each one separately,
which design tools do routinely.
It survived review because a model reads that text fine. The candidate was
classified, matched and scored normally. What fails, silently, is every check
that asks "does this string appear in the resume":
- slugs_from_text finds no profile, so linkedin_slug is stored empty
- _clean_skills drops every skill, since each must appear in the text
- the company and education clamps drop theirs for the same reason
- verify_matched_keywords drops every matched keyword in the ATS engine
despace_line could not help: it rebuilds glyphs padded *within* a line, and
here there is nothing left on a line to rebuild.
is_glyph_fragmented measures the giveaway — the share of non-empty lines that
are a single character — and extract_pdf_text re-extracts with pypdf's layout
mode when it trips. Layout mode is the fallback, never the default: it is
slower and pads ordinary documents with alignment whitespace, so a CV that
extracts cleanly today is untouched. The fallback is checked before it is
trusted; fragmented text still scores a candidate, empty text fails them.
Both extractors had the defect, so the helpers live in app/services/pdf.py,
which owns PDF handling and is already imported by the recruiting path.
Measured against that real CV, before and after:
slugs_from_text [] -> ['mohammad-raza-digital-marketer']
profile_url_from_text None -> https://www.linkedin.com/in/...
lines 2278 -> 61
single-char lines 2278 -> 0
'performance' found False -> True
'google ads' found False -> True
Existing rows keep their broken text; extraction runs at ingest. Re-running
the match on affected rows is what backfills them.
.gitignore had `tests/**` twice and `/backend/tests/**` once. Both suites are
tracked and both run in CI, so the rules were inert for existing files and did
nothing but swallow new ones — this test was invisible to `git status` until
they went. That is also why they are removed rather than negated.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Deploy to S3 / deploy (push) Successful in 31sDetails
Production containers have ephemeral filesystems, so the disk-backed
image from the previous commit would vanish on redeploy. New
job_post_images table (bytea, PK = job_posts FK so re-upload replaces),
created everywhere by manual migration 009 which run_manual_sql applies
automatically at startup — prod boots with DB_AUTOGENERATE=false and
never autogenerates tables. Upload/fetch endpoints unchanged for the
frontend; fetch now serves bytes from the row. The one locally stored
disk image was imported into the table and backend/uploads removed.
E2E re-verified: create-with-image 200, fetch 200, cover renders.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deploy to S3 / deploy (push) Successful in 32sDetails
The create modal collected an image but dropped it. Now POST
/job/image/upload stores it on disk keyed by the post id (uuid-validated,
5 MB / png-jpg-webp-gif, replace-on-reupload; no DB migration) and GET
/job/image/fetch serves it. The create flow uploads right after the row
exists, image failure downgrades to a toast instead of failing the create,
and Job Details renders the cover via an authorized blob fetch.
E2E-verified: upload 200, fetch 200, cover renders in Job Details.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Deleted the `.env.example` file to centralize configurations in `backend/.env`.
- Adjusted `docker-compose.yml` and `docker-compose.dev.yml` for improved environment variable handling.
- Enhanced nginx configuration for better API request management.
These changes streamline the setup process and improve configuration management.
Deploy to S3 / deploy (push) Successful in 34sDetails
Both branches appended at EOF; the merge kept only main's hunk and its
missing trailing newline. Root-anchored so backend/candidate_forms/
(the forms domain package) stays tracked.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Deleted the `.env.example` file as it is no longer needed; all configurations are now centralized in `backend/.env`.
- Updated `docker-compose.yml` and `docker-compose.dev.yml` to reflect changes in environment variable handling, ensuring that the application reads from `backend/.env` exclusively.
- Adjusted the nginx configuration to improve API request handling and ensure proper proxying for frontend interactions.
These changes streamline the environment setup process and enhance the overall configuration management for local and production deployments.
Deploy to S3 / deploy (push) Successful in 37sDetails
Backend: new candidate_forms domain (requisition, interview analysis,
cultural fit) with XOR inbox/manual keys, server-recomputed section
averages and combined summary, INTERVIEW-stage gate (409), history
events, INTERVIEWS_* permissions + 008 RBAC seed; offers table gains
the seven Annexure-J fields.
Frontend: Forms tab in the candidate profile (paper-exact labels from
/forms/definitions, rating tables, score summary tiles, completion
dots); profile converted to a full page at /candidate/:userId opened
from Candidates, Talent Pool and Pipeline; live Advance Stage now calls
PATCH /candidate/stage; workflow-ordered tabs; responsive pass verified
by headless-Edge screenshots at 375-2400px; Stars import crash fix in
Interviews; Matching tab strip wraps on phones.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Updated the interview model to include `graph_event_id` and `web_link` for calendar integration.
- Added methods for setting calendar events and retrieving job titles associated with interviews.
- Modified serializers to include new fields in the interview response.
- Enhanced the job post model to include applicant counts.
- Updated frontend components to handle new fields and improve user experience with calendar event management.
- Introduced image upload functionality in job forms with validation for file types and sizes.
- Improved dashboard and calendar UI for better data presentation.
This commit streamlines interview scheduling and job management processes, enhancing overall application functionality.