Resume Screening Desk (Python)
Updated
What it does#
Drop CVs and a job description, extract structured candidate profiles, review the criteria, then rank candidates by skill coverage and experience. Every score explains itself: which required skills matched, which are missing, and where the experience figure came from.
Source: resume-desk in the examples repository.

Run it#
Requires Python 3.10+.
cd resume-desk
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/python app.py
Open http://127.0.0.1:3102 and choose Explore sample data. With Python, SQL and REST APIs required, three years' minimum experience and an 80% skill weight, one candidate scores 100, one scores 66.67, and one stays unranked because their experience cannot be determined.
docxtract-sdk and the import is docxtract. The unscoped PyPI
docxtract is an unrelated DOCX text utility. requirements.txt pins the right one.The SDK calls#
app.py is a small Flask app. Two of its four routes call DocXtract.
from docxtract import DocXtract, DocXtractError
dx = DocXtract(key, max_retries=2)
models = dx.models() # costs no credits
Extraction uses a temporary directory as a context manager, so the uploaded bytes are gone by the time the function returns.
options = {"document_type": settings.get("documentType") or "resume", "store_db": False}
if model.strip():
options["model"] = model.strip()
with tempfile.TemporaryDirectory(prefix="resume-desk-") as folder:
path = Path(folder) / ("document" + ext)
path.write_bytes(content)
path.chmod(0o600)
result = dx.extract(str(path), **options)
return jsonify(raw=result.data, meta=result.meta, complete=result.complete)
The app extracts two kinds of document with the same call: CVs, and the job description when it arrives as a PDF or image. Each has its own document type and model setting. A resume-oriented template may not infer requirements from a job advert, so the app always shows the criteria for review before ranking.
The job description#
Three ways in, only one of which costs credits.
- PDF, PNG or JPG: extracted with the Python SDK. Recognised
required_skillsorskillsandminimum_experience_yearsormin_yearsprefill the criteria. - A
.txtfile: parsed locally, no API call. Labelled lines prefill the criteria. - Pasted text: kept as is. You enter the skills and years yourself. The app does not invent a skills list from prose.
For the text file, the labelled lines look like this:
Required skills: Python, SQL, REST APIs
Minimum experience: 3 years

The ranking rules#
score = skill coverage × skill weight + experience coverage × (1 − skill weight)
- Skill coverage is the share of distinct required skills found in the candidate's structured skills, normalised for case and whitespace. Matching is exact. Java does not match JavaScript, and there are no inferred synonyms.
- Experience coverage is
min(candidate years / required years, 1). - Reported total experience is preferred. If absent, complete ISO role dates are merged so overlapping jobs are not double-counted. Ambiguous dates stay unknown.
- Missing structured skills, unknown experience when a minimum applies, or a partial extraction keep the candidate unranked. Unknown is never scored as zero.
- Equal scores share a rank. Any correction or criteria change invalidates the previous ranking.
Scores are a review aid. The app does not accept or reject anyone and does not contact anyone. Personal fields in the extraction play no part in the score.
Field mapping#
Normalised keys are name, skills, years and experience. Defaults cover the common template shapes, including full_name, technical_skills, total_experience_years and work_experience. Skills may be a list, comma-separated text, or category objects that contain lists.
{"name":"Candidate.Name","skills":"Candidate.TechnicalSkills","years":"Candidate.TotalYears","experience":"Candidate.Roles"}
Where things are#
| File | Purpose |
|---|---|
app.py | Flask routes for connect, extract, analyse and rank |
includes/screening.py | Profile normalisation, merged experience intervals, explainable ranking |
includes/*.php | HTML-only view fragments assembled by Python. PHP is not required |
tests/test_screening.py | Ranking weights and ties, overlapping employment, ignored personal fields, cleanup |
.venv/bin/python -m unittest discover -s tests -v
Something wrong or missing on this page? Tell us.