Metadata-Version: 2.4
Name: resume-screener
Version: 1.0.0
Summary: Score a folder of PDF resumes against your keywords and shortlist the best matches, as a native-feeling macOS desktop app.
License-Expression: MIT
Project-URL: Homepage, https://github.com/Dheerajzxx/resume_screener
Project-URL: Source, https://github.com/Dheerajzxx/resume_screener
Project-URL: Issues, https://github.com/Dheerajzxx/resume_screener/issues
Project-URL: Releases, https://github.com/Dheerajzxx/resume_screener/releases
Keywords: resume,cv,screening,pdf,recruiting,shortlist,pyside6
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: X11 Applications :: Qt
Classifier: Intended Audience :: End Users/Desktop
Classifier: Operating System :: MacOS
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Utilities
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: licenses/AGPL-3.0.txt
License-File: licenses/LGPL-3.0.txt
Requires-Dist: PySide6<7,>=6.7
Requires-Dist: PyMuPDF<3,>=1.24
Provides-Extra: ocr
Requires-Dist: pytesseract<1,>=0.3.10; extra == "ocr"
Provides-Extra: dev
Requires-Dist: pyinstaller<7,>=6.6; extra == "dev"
Requires-Dist: pytest<10,>=8; extra == "dev"
Dynamic: license-file

# Resume Screener

![Version](https://img.shields.io/badge/version-1.0.0-7C5CFF)
![Python](https://img.shields.io/badge/Python-3.12-3776AB?logo=python&logoColor=white)
![Platform](https://img.shields.io/badge/platform-macOS%20arm64-000000?logo=apple&logoColor=white)
![Source license](https://img.shields.io/badge/source-MIT-green)
![Binary license](https://img.shields.io/badge/binary-AGPL--3.0-blue)

A native-feeling macOS app that reads a folder of PDF resumes, scores each one
against your keywords, and copies or moves the strongest matches into a
`Shortlisted` folder.

Built with **Python 3.12 + PySide6**, with PyMuPDF for text extraction. Everything
runs locally — resumes are never uploaded.

![Resume Screener main window](https://raw.githubusercontent.com/Dheerajzxx/resume_screener/master/docs/screenshot.png)

## Features

- Folder picker, drag-and-drop, and a live PDF count
- Comma-separated keywords, with `js|javascript` alternatives
- Weighted keywords (`python:3`) and saved keyword presets
- Background scanning with progress and cancel — the window never freezes
- Ranked, sortable results with a match bar, matched/missing keywords, and status
- Live count of resumes above the threshold
- CSV export of the visible results in their current ranked order
- Shortlist by copying (default) or moving, with collision-safe filenames
- Handles scanned, encrypted, and corrupt PDFs without aborting the scan
- Dark theme, saved settings, and keyboard shortcuts
- Built-in user guide via **Help → User Guide** or the header button

## Download a release

Release builds are published on the repository's **Releases** page as
`Resume-Screener-vX.Y.Z-macOS-arm64.zip`. Download the zip, unzip it, and move
`Resume Screener.app` wherever you like. Each release attaches
`SHA256SUMS.txt`, `LICENSE`, `THIRD_PARTY_NOTICES.md`, and the full
`AGPL-3.0.txt`/`LGPL-3.0.txt` texts. The packaged app is distributed as an
**AGPL-3.0 combined work** (see **License** below).

The app is **ad-hoc signed but not Developer ID-signed or notarized**. On
another Mac, right-click the app and choose **Open** the first time. To
distribute more widely, sign and notarize the build with an Apple Developer ID.

## Install with pip

```bash
pip install resume-screener
resume-screener
```

Requires Python 3.12. `PySide6` and `PyMuPDF` are installed as regular
dependencies, and the entry point launches the same window as the packaged app.
Everything still runs locally — resumes are never uploaded.

## Requirements (from source)

- macOS on Apple silicon
- Python 3.12
- Xcode Command Line Tools
- Homebrew

`install.sh` checks these and installs only what is missing.

## Quick start

From the project folder:

```bash
chmod +x install.sh run.sh build_app.sh
./install.sh     # checks everything and installs only missing dependencies
./run.sh         # launches the app
```

If you prefer to set up manually:

```bash
python3.12 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python app.py
```

## Usage

1. Choose the folder that holds your PDF resumes.
2. Enter keywords, for example `python:3, django, sql, aws, js|javascript`.
   Append `:N` to make a keyword count more heavily.
3. Press **Start Processing**. Results appear as each PDF is read.
4. Use the search box and column headers to filter and sort the results.
5. Optionally press **Export CSV…** (or `Cmd+E`) to save the visible rows.
6. Set the minimum match percentage and press **Shortlist**.
7. Resumes land in `<selected folder>/Shortlisted`. Copy is the default; untick
   **Copy instead of move** to move the originals instead.

Matching is case-insensitive and whole-word: `java` does not match
`javascript`, `C` does not match `C++`, and `js|javascript` counts as one
keyword where either spelling is accepted. Match % is the share of total
keyword weight that was found, so `python:3, sql` scores 75% when Python is
present but SQL is missing. Weights are positive integers and default to 1.

Use **Save preset…** next to the keyword field to name and store the current
keywords (weights included). Pick a preset from the dropdown to load it, or
select one and press **Delete**. Presets are stored with the rest of the app
settings and survive restarts.

Double-click a row to open the PDF in your default viewer.

The CSV export contains rank, file, numeric match %, matched/missing keywords,
status, and full path. Keyword lists are semicolon-separated, and the file is
UTF-8 with a BOM so Excel opens accented filenames correctly. Exporting respects
the current search filter and sort order.

## User guide

The full guide covers installation, scoring, presets, statuses, shortlisting,
CSV export, shortcuts, troubleshooting, and privacy:

- Read [USER_GUIDE.md](https://github.com/Dheerajzxx/resume_screener/blob/master/USER_GUIDE.md).
- Or open **Help → User Guide** inside the app (also the **User Guide** button).

![In-app user guide](https://raw.githubusercontent.com/Dheerajzxx/resume_screener/master/docs/user-guide.png)

## Tests

```bash
source .venv/bin/activate
pytest
```

The suite covers keyword parsing/weighting/scoring edge cases, preset
persistence, CSV export, shortlist folder and collision behavior, PDF extraction
statuses (OK, scanned, encrypted, corrupt), user-guide content sync and
rendering, and a headless end-to-end scan through the real window and worker
thread.

The GitHub Actions CI workflow runs the same checks on an arm64 macOS runner,
plus Python and shell syntax checks.

## Build a macOS app

```bash
./build_app.sh
# Result: dist/Resume Screener.app
```

Build on Apple silicon for an arm64 bundle. If `assets/icon.icns` exists it is
used automatically. The build excludes PyMuPDF 2.0's optional ONNX layout
subsystem (the app only needs text extraction), which keeps roughly 50 MB of
model files out of the bundle. The bundle's Finder/Get Info version is synced
from `core.__version__`.

The release workflow runs when a `v*` tag is pushed, verifies the tag against
`core.__version__`, runs the tests, builds the `.app`, creates a zip plus
SHA-256 checksums, and publishes a GitHub release. The PyPI workflow publishes
the same tag to PyPI. See
[CONTRIBUTING.md](https://github.com/Dheerajzxx/resume_screener/blob/master/CONTRIBUTING.md#release-process)
for the first-publish and tagging steps.

## Project layout

```
app.py                  entry point and theme setup
USER_GUIDE.md           end-user guide (also rendered in the app)
THIRD_PARTY_NOTICES.md  bundled dependency licenses and release obligations
licenses/               full AGPL-3.0 and LGPL-3.0 texts
core/                   Qt-free domain logic
  extractor.py          PDF -> text; OK/SCANNED/ENCRYPTED/ERROR
  matcher.py            parse_keywords, weighted score, qualifies
  export.py             ranked results -> UTF-8 CSV
  shortlist.py          Shortlisted folder, copy/move, collisions
  models.py             ResultRow dataclass + status labels
ui/
  main_window.py        layout, threading, shortlist flow, settings
  worker.py             background ScanWorker
  presets.py            QSettings-backed keyword preset store
  user_guide.py         embedded guide text + modeless guide dialog
  results_model.py      QAbstractTableModel
  delegates.py          match bar and status badge painters
  theme.py              design tokens, palette, QSS
tests/                  core, presets, CSV, guide, and headless UI tests
.github/                CI, release workflow, issue/PR templates, Dependabot
docs/                   README screenshots
```

`core/` never imports Qt, so the matching and file logic can be tested without
opening a window.

## Privacy and limitations

- Everything runs locally; no resume text leaves the machine.
- OCR is not enabled: image-only PDFs are flagged **Scanned**, shown with no
  percentage, and excluded from shortlisting.
- Password-protected and corrupt PDFs are flagged and skipped.
- Only the top level of the selected folder is scanned; the `Shortlisted`
  folder itself is never reprocessed.
- Only PDF input is supported. DOCX input and Excel export are not included.
- Extracted text is cached by path and modification time, so rerunning with new
  keywords skips PDF parsing for unchanged files.
- macOS asks for permission the first time the app reads Desktop, Documents, or
  Downloads.
- Tesseract is installed for future OCR work but is not bundled in the app.

## Contributing

Contributions are welcome. Read
[CONTRIBUTING.md](https://github.com/Dheerajzxx/resume_screener/blob/master/CONTRIBUTING.md)
for setup, conventions, and the release process, and follow the
[Code of Conduct](https://github.com/Dheerajzxx/resume_screener/blob/master/CODE_OF_CONDUCT.md).
Please open an issue using the provided templates before large changes.

For security reports, follow
[SECURITY.md](https://github.com/Dheerajzxx/resume_screener/blob/master/SECURITY.md)
instead of opening a public issue.

## License

Two licenses apply, and the binary is **not** MIT-only:

- **Source code in this repository** —
  [MIT](https://github.com/Dheerajzxx/resume_screener/blob/master/LICENSE)
  © 2026 Resume Screener contributors. MIT is permissive, so this code may be
  included in an AGPL-3.0 combined work.
- **Distributed binaries (`.app` / release zip)** — **AGPL-3.0 combined work**,
  because the app bundles PyMuPDF (AGPL-3.0). Corresponding source is this
  repository: <https://github.com/Dheerajzxx/resume_screener>. The full
  AGPL-3.0 text ships in `licenses/AGPL-3.0.txt` and is attached to every
  release. If you build against a commercial PyMuPDF license from
  [Artifex](https://artifex.com/licensing/), the PyMuPDF AGPL obligation does
  not apply — say so explicitly in the release notes.

**PySide6/Qt** stays under LGPL-3.0/GPL: the Qt libraries are bundled unmodified
as dynamic libraries and remain replaceable, with `licenses/LGPL-3.0.txt`
attached to every release.

See
[THIRD_PARTY_NOTICES.md](https://github.com/Dheerajzxx/resume_screener/blob/master/THIRD_PARTY_NOTICES.md)
for the full dependency list, the release checklist, and redistribution
obligations, and
[CHANGELOG.md](https://github.com/Dheerajzxx/resume_screener/blob/master/CHANGELOG.md)
for release history.
