← Selected workAI Job Search
INDEPENDENT PYTHON PROJECT / V1.0.0 RELEASE

AI Job Search.

A structured Python pipeline for collecting, normalizing, and filtering software engineering listings.

Built to cut through noisy job boards, AI Job Search queries the Adzuna REST API, transforms raw, fragmented JSON payloads into strongly-typed dataclass models, and filters opportunities against custom preferences using role aliases and regex-based work arrangement detection.

18 Verified Tests100% pytest pass rate
4-Stage PipelineCollect → Normalize → Filter → Report
10 Role AliasesSoftware engineering expansions

Independent Python project verified locally with automated pytest test suites covering API fault tolerance, regex pattern extraction, role expansions, and preference filtering.

Job boards bury relevant roles under noisy algorithms and fragmented data.

Searching for software engineering positions across generalist job boards means sifting through hundreds of irrelevant postings, inaccurate location tags, and buried requirements.

General-purpose search algorithms on large job aggregators frequently return senior managerial roles, sales positions, or unrelated technical stacks when searching for entry-level or specialized developer roles. Furthermore, external job APIs return unpredictable payload structures: some omit salary fields, others nest location data three levels deep, and most bury remote/hybrid policies inside large blocks of unstructured text.

Without a disciplined collection and normalization pipeline, developers either miss opportunities due to minor title differences (e.g., "Python Developer" vs. "Backend Engineer") or waste hours manually parsing listings that don't match their work arrangement preferences.

AI Job Search solves this by establishing a deterministic four-stage data pipeline: querying external job boards via REST endpoints, mapping raw payloads to standardized Job dataclass models, expanding roles with semantic aliases, and filtering opportunities purely on clean, validated properties.

Modular four-stage pipeline with strict separation of concerns.

The application follows a linear, decoupled architecture where each stage has a single, well-defined responsibility. Network communication is isolated at the edge, data is normalized into structured domain models, and filtering operates purely on clean domain instances.

PIPELINE MAP4-STAGE MODULAR ARCHITECTURE
01 / EXTERNAL SOURCEAdzuna REST APIHTTP GET queries with parameters for role, location, country, and results count
02 / INGESTION & ERROR HANDLINGCollector (src/collectors/adzuna.py)HTTPX client managing requests, timeouts, and JSON decode error recovery
03 / DATA NORMALIZATIONNormalizer (src/normalizers/adzuna.py)Regex extraction for remote/hybrid/onsite tags and employment contract mapping
04 / STRUCTURED DOMAIN MODELJob Dataclass (src/models/job.py)Immutable typed representation: ID, title, company, salary, location, arrangement
05 / PREFERENCE FILTERINGJob Filter (src/filters/job_filter.py)Multi-criteria matching with role alias expansion and location/arrangement checks
06 / PIPELINE OUTPUTPipeline Runner (run_pipeline.py)CLI output formatting structured summaries of relevant opportunities

Modular architecture of AI Job Search V1.0.0. All collection, normalization, and filtering operates locally via Python.

Design choices that keep the pipeline clean and extensible.

V1 was architected with clean separation of concerns, ensuring that modifying data sources, expanding filter rules, or altering output formats does not require rewrites across the application.

01

Decoupled Normalization Layer

Raw API payloads are never passed directly to filter functions. A dedicated normalizer transforms vendor-specific keys into a standardized Job dataclass. This guarantees that adding future job boards (such as Greenhouse, Lever, or LinkedIn) only requires a new normalizer function rather than alterations to downstream filtering logic.

02

Role Alias Expansion Engine

Job titles vary widely across companies. The role_aliases.py module maps broad target titles (e.g., "software engineer") to ten related industry titles, including "backend engineer", "full stack engineer", "python developer", and "site reliability engineer", significantly improving recall without requiring multiple manual queries.

03

Regex Work-Arrangement Extraction

Because job boards rarely enforce structured remote/hybrid tags, extract_work_arrangement evaluates description text using targeted regular expressions (such as hybrid|remote \d+ days (per|a) week) to reliably categorize listings as remote, hybrid, or onsite.

04

Defensive Dataclass Modeling

Domain models are declared using Python's @dataclass with explicit type hints and defensive field factories (e.g., employment_types: list[str] = field(default_factory=list)). This ensures strict schema validation and eliminates runtime AttributeError or mutable default bugs.

18 verified automated tests with simulated failure handling.

Because reliability is paramount when automating job searches, the codebase is backed by an automated pytest suite covering network failure resilience, regex extraction accuracy, role alias lookups, and preference permutations.

API Network Fault Tolerance (1 test passed)

Verifies src/collectors/adzuna.py under network failures. Using monkeypatch to simulate an httpx.RequestError, the test confirms that get_jobs gracefully catches the exception, logs an error, and returns an empty list ([]) without crashing the pipeline.

Data Normalization & Regex Extraction (1 test passed)

Verifies src/normalizers/adzuna.py field mapping and evaluates 9 distinct regex pattern test cases, confirming accurate classification of complex hybrid strings (e.g., "Remote 3 days per week", "2 days remote per week"), pure remote, onsite, and employment contract types.

Preference & Role Alias Filtering Permutations (14 tests passed)

Validates src/filters/job_filter.py across 14 scenarios: case-insensitive matching ("SOFTWARE DEVELOPER"), role alias lookup, remote/hybrid/onsite constraints, multiple employment types, and location matching versus rejection.

Pipeline Orchestration & End-to-End Flow (2 tests passed)

Tests src/pipeline.py end-to-end using mock API fixtures, verifying that get_relevant_jobs handles zero-result API responses and filters populated job batches down to exact matching listings.

Potential future directions and exploratory ideas.

V1 established the data collection, normalization, and filtering baseline. Future exploration may investigate potential enhancements as non-committed research directions.

01

LLM-Assisted Job Fit Scoring (Potential Direction)

Explore integrating Google Gemini API or local Ollama models with structured JSON schemas to evaluate candidate résumés against job descriptions and compute an objective alignment score with rationale.

02

Geolocation & Real Commute Calculation (Potential Exploration)

Investigate connecting Google Maps Platform or OpenStreetMap distance matrix APIs to calculate real transit and driving times, activating the max_commute_minutes preference filter.

03

Multi-Source Collector Aggregation (Potential Direction)

Explore adding ingestion modules for Greenhouse, Lever, and USAJOBS API feeds, routing raw listings through dedicated normalizers into the unified Job pipeline.

04

Persistent Storage & Web Dashboard (Potential Concept)

Consider storing collected listings in a local SQLite database, tracking application stages (Saved, Applied, Interviewing, Offer), and prototyping a lightweight React web UI.

What building a production-style data pipeline taught me.

Reliable software begins with disciplined data modeling and clean separation of concerns before adding complex features.

The most important design decision in AI Job Search was isolating external API payloads behind dedicated normalizer functions. By transforming messy, vendor-specific JSON dictionaries into immutable, strongly-typed Job dataclasses immediately upon ingestion, the filtering logic remained pure, testable, and completely independent of Adzuna's specific API schema.

Equally valuable was the discipline of incremental scoping: rather than attempting to build an all-in-one AI auto-apply tool, V1 focused strictly on building a reliable, fault-tolerant collection and filtering engine with 100% automated test coverage. This created a solid foundation that makes future integrations—like LLM scoring or database persistence—straightforward and clean.