From bb03fd85d201eb0a6ec6eb84aaa312cfc38dadac Mon Sep 17 00:00:00 2001 From: Amirhossein Akhlaghpour Date: Mon, 10 Nov 2025 16:54:03 +0330 Subject: [PATCH 1/4] feat: v2 via front Signed-off-by: Amirhossein Akhlaghpour --- .DS_Store | Bin 0 -> 14340 bytes .dockerignore | 62 + .pre-commit-config.yaml | 56 + Dockerfile | 74 + backend/.github/workflows/python-lint.yml | 36 + backend/.gitignore | 47 + backend/.python-version | 1 + backend/CONTRIBUTING.md | 927 ++ backend/Dockerfile | 43 + backend/README.md | 610 + backend/TESTING_RESULTS.md | 325 + backend/core/jobs/create_job.py | 56 + backend/core/jobs/interfaces.py | 32 + backend/core/jobs/list_jobs.py | 39 + backend/core/jobs/models.py | 38 + backend/core/jobs/repository.py | 66 + backend/core/jobs/utils.py | 21 + backend/docker-compose.yml | 36 + backend/infrastructure/api/jobs_controller.py | 79 + backend/infrastructure/db/database.py | 39 + backend/infrastructure/db/models.py | 149 + backend/infrastructure/db/repository.py | 138 + backend/main.py | 74 + backend/pyproject.toml | 70 + backend/requirements.txt | 22 + backend/schemas/job_schemas.py | 93 + backend/seed_data.json | 18 + backend/seed_data.py | 41 + backend/tests/__init__.py | 0 backend/tests/conftest.py | 146 + backend/tests/test_api/__init__.py | 0 backend/tests/test_api/test_jobs.py | 284 + backend/tests/test_health.py | 24 + backend/tests/test_models/__init__.py | 0 backend/tests/test_services/__init__.py | 0 .../tests/test_services/test_job_service.py | 184 + backend/uv.lock | 1143 ++ docs/01-JOBS-API.md | 1222 ++ docs/02-CANDIDATES-API.md | 1267 +++ docs/03-REFERRALS-API.md | 431 + docs/04-MCP-TOOLS.md | 1039 ++ docs/05-DATA-MODELS.md | 1183 ++ docs/06-API-INDEX.md | 401 + docs/07-STATE-API.md | 963 ++ docs/README.md | 659 ++ docs/sepal-greenhouse-specification.md | 202 + frontend/.gitignore | 38 + frontend/.prettierignore | 8 + frontend/.prettierrc | 10 + .../app/(authenticated)/approvals/page.tsx | 31 + .../(authenticated)/candidates/[id]/page.tsx | 338 + .../app/(authenticated)/candidates/page.tsx | 218 + .../app/(authenticated)/configure/page.tsx | 205 + frontend/app/(authenticated)/crm/page.tsx | 304 + .../app/(authenticated)/dashboard/page.tsx | 593 + .../4000002009/from_template/page.tsx | 493 + .../4000003009/from_template/page.tsx | 493 + .../get_started/show_existing_jobs/page.tsx | 278 + .../app/(authenticated)/integrations/page.tsx | 65 + .../app/(authenticated)/interviews/page.tsx | 41 + .../app/(authenticated)/jobs/[id]/page.tsx | 503 + .../app/(authenticated)/jobs/new/page.tsx | 230 + frontend/app/(authenticated)/jobs/page.tsx | 388 + frontend/app/(authenticated)/layout.tsx | 17 + .../4040160009/ramp_up/hiring_team/page.tsx | 493 + .../app/(authenticated)/referrals/page.tsx | 39 + frontend/app/(authenticated)/reports/page.tsx | 145 + frontend/app/globals.css | 84 + frontend/app/layout.tsx | 22 + frontend/app/login/page.tsx | 204 + .../components/ApplicationTrendsChart.tsx | 49 + frontend/components/Header.tsx | 152 + frontend/components/JobSidebar.tsx | 59 + frontend/components/RecentlyViewed.tsx | 55 + frontend/components/SourceBreakdownChart.tsx | 44 + frontend/components/ui/badge.tsx | 42 + frontend/components/ui/button.tsx | 54 + frontend/components/ui/card.tsx | 78 + frontend/components/ui/checkbox.tsx | 33 + frontend/components/ui/dialog.tsx | 122 + frontend/components/ui/dropdown-menu.tsx | 134 + frontend/components/ui/input.tsx | 27 + frontend/components/ui/label.tsx | 22 + .../ui/rich-text-editor-client..tsx | 188 + frontend/components/ui/select.tsx | 107 + frontend/components/ui/table.tsx | 80 + frontend/components/ui/tabs.tsx | 57 + frontend/components/ui/textarea.tsx | 27 + frontend/eslint.config.mjs | 14 + frontend/lib/dummy-data.ts | 215 + frontend/lib/utils.ts | 36 + frontend/next.config.ts | 21 + frontend/package-lock.json | 9851 +++++++++++++++++ frontend/package.json | 51 + frontend/postcss.config.mjs | 10 + .../public/fonts/UntitledSansWeb-Light.woff2 | Bin 0 -> 34225 bytes .../public/fonts/UntitledSansWeb-Medium.woff2 | Bin 0 -> 34985 bytes .../fonts/UntitledSansWeb-Regular.woff2 | Bin 0 -> 34745 bytes .../fonts/UntitledSerifWeb-Regular.woff2 | Bin 0 -> 49870 bytes frontend/tailwind.config.ts | 12 + frontend/tsconfig.json | 31 + 101 files changed, 29151 insertions(+) create mode 100644 .DS_Store create mode 100644 .dockerignore create mode 100644 .pre-commit-config.yaml create mode 100644 Dockerfile create mode 100644 backend/.github/workflows/python-lint.yml create mode 100644 backend/.gitignore create mode 100644 backend/.python-version create mode 100644 backend/CONTRIBUTING.md create mode 100644 backend/Dockerfile create mode 100644 backend/README.md create mode 100644 backend/TESTING_RESULTS.md create mode 100644 backend/core/jobs/create_job.py create mode 100644 backend/core/jobs/interfaces.py create mode 100644 backend/core/jobs/list_jobs.py create mode 100644 backend/core/jobs/models.py create mode 100644 backend/core/jobs/repository.py create mode 100644 backend/core/jobs/utils.py create mode 100644 backend/docker-compose.yml create mode 100644 backend/infrastructure/api/jobs_controller.py create mode 100644 backend/infrastructure/db/database.py create mode 100644 backend/infrastructure/db/models.py create mode 100644 backend/infrastructure/db/repository.py create mode 100644 backend/main.py create mode 100644 backend/pyproject.toml create mode 100644 backend/requirements.txt create mode 100644 backend/schemas/job_schemas.py create mode 100644 backend/seed_data.json create mode 100644 backend/seed_data.py create mode 100644 backend/tests/__init__.py create mode 100644 backend/tests/conftest.py create mode 100644 backend/tests/test_api/__init__.py create mode 100644 backend/tests/test_api/test_jobs.py create mode 100644 backend/tests/test_health.py create mode 100644 backend/tests/test_models/__init__.py create mode 100644 backend/tests/test_services/__init__.py create mode 100644 backend/tests/test_services/test_job_service.py create mode 100644 backend/uv.lock create mode 100644 docs/01-JOBS-API.md create mode 100644 docs/02-CANDIDATES-API.md create mode 100644 docs/03-REFERRALS-API.md create mode 100644 docs/04-MCP-TOOLS.md create mode 100644 docs/05-DATA-MODELS.md create mode 100644 docs/06-API-INDEX.md create mode 100644 docs/07-STATE-API.md create mode 100644 docs/README.md create mode 100644 docs/sepal-greenhouse-specification.md create mode 100644 frontend/.gitignore create mode 100644 frontend/.prettierignore create mode 100644 frontend/.prettierrc create mode 100644 frontend/app/(authenticated)/approvals/page.tsx create mode 100644 frontend/app/(authenticated)/candidates/[id]/page.tsx create mode 100644 frontend/app/(authenticated)/candidates/page.tsx create mode 100644 frontend/app/(authenticated)/configure/page.tsx create mode 100644 frontend/app/(authenticated)/crm/page.tsx create mode 100644 frontend/app/(authenticated)/dashboard/page.tsx create mode 100644 frontend/app/(authenticated)/get_started/job_types/4000002009/from_template/page.tsx create mode 100644 frontend/app/(authenticated)/get_started/job_types/4000003009/from_template/page.tsx create mode 100644 frontend/app/(authenticated)/get_started/show_existing_jobs/page.tsx create mode 100644 frontend/app/(authenticated)/integrations/page.tsx create mode 100644 frontend/app/(authenticated)/interviews/page.tsx create mode 100644 frontend/app/(authenticated)/jobs/[id]/page.tsx create mode 100644 frontend/app/(authenticated)/jobs/new/page.tsx create mode 100644 frontend/app/(authenticated)/jobs/page.tsx create mode 100644 frontend/app/(authenticated)/layout.tsx create mode 100644 frontend/app/(authenticated)/plans/4040160009/ramp_up/hiring_team/page.tsx create mode 100644 frontend/app/(authenticated)/referrals/page.tsx create mode 100644 frontend/app/(authenticated)/reports/page.tsx create mode 100644 frontend/app/globals.css create mode 100644 frontend/app/layout.tsx create mode 100644 frontend/app/login/page.tsx create mode 100644 frontend/components/ApplicationTrendsChart.tsx create mode 100644 frontend/components/Header.tsx create mode 100644 frontend/components/JobSidebar.tsx create mode 100644 frontend/components/RecentlyViewed.tsx create mode 100644 frontend/components/SourceBreakdownChart.tsx create mode 100644 frontend/components/ui/badge.tsx create mode 100644 frontend/components/ui/button.tsx create mode 100644 frontend/components/ui/card.tsx create mode 100644 frontend/components/ui/checkbox.tsx create mode 100644 frontend/components/ui/dialog.tsx create mode 100644 frontend/components/ui/dropdown-menu.tsx create mode 100644 frontend/components/ui/input.tsx create mode 100644 frontend/components/ui/label.tsx create mode 100644 frontend/components/ui/rich-text-editor-client..tsx create mode 100644 frontend/components/ui/select.tsx create mode 100644 frontend/components/ui/table.tsx create mode 100644 frontend/components/ui/tabs.tsx create mode 100644 frontend/components/ui/textarea.tsx create mode 100644 frontend/eslint.config.mjs create mode 100644 frontend/lib/dummy-data.ts create mode 100644 frontend/lib/utils.ts create mode 100644 frontend/next.config.ts create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/postcss.config.mjs create mode 100644 frontend/public/fonts/UntitledSansWeb-Light.woff2 create mode 100644 frontend/public/fonts/UntitledSansWeb-Medium.woff2 create mode 100644 frontend/public/fonts/UntitledSansWeb-Regular.woff2 create mode 100644 frontend/public/fonts/UntitledSerifWeb-Regular.woff2 create mode 100644 frontend/tailwind.config.ts create mode 100644 frontend/tsconfig.json diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..73da50a5b75eb879b8561dab6cfd84ac7c555fc2 GIT binary patch literal 14340 zcmeHOTWl0n7(V~>HbVz`fFcEV!a^YkS)jB)%gtzlB$FhPa!0XamiqQ^KTBt<;i%QYu$ zH#w%Z$Pg9~7MMsDkZ>^U{Vn#rLEPKl-=bKu&$hfwP0cqT6&071mdVPLa&@ZK8Sm=e zt0w4-oMra6^<^yA@%ET$4`qT@cs*|a=9KH~Oq;B{Gv(UZUdKz&=_%b#Wog7g_laZw!PE!EY?g{JYx=ISlO7Oc(>=KGtGWuaSPk$wP@BecbnL#l`~tgX|J-t z!l0a=HhbQ}N0!yrZ-};T-oEQ<1)V-)W~HijTZZXmtV5QW?&!CSOi!2YrA)`LoWq?t z%d?WU+3x7Q=5&ZC%GHXhIddNlY1*Bt#Wb ze#cA)xmz&Tf25etlv)id@FiS_AK*v$32wk&@HgC)N~GCRl{80M zAVs8Q(sHR@YLM1R>!lr1n-rIH>4@Y?1AGD!d##w;&qY3zj+2QpWgJp4NVz;eJRyYz zgaw2J0t>L;hcJ33qk$ae(*NnefI$dW&LD*EFpfOH&kF}K8pvTTeO1))RZ((P2q*?{ zb<7X5>L{au9Oja%6X5Cuf}SB@DB$;wdSSmhfhdboj-X-=qw~mlG#>Xs_-)YL2jM(mVjG6P!7lbRY;6iWO=s5#UsBf6*Zy@K7>$aV5F`HkE}*gXwq zLKQ5A8U)*q!Y0^(aGQbc4mbb_&>;y1^nwF@a14R>Nf?4BU>Kf+(+IrJ!ZYwJyZ|r4 zOYjQ34sRg%ehc14==~mCg7@Jg_!ut3rvV8679ser@H=0)Bd~h=?F$+H7`=*fv9x2m z&SA`Bn}tjcA%$>>Lo&p~&`9=s!73-0@~iyoJi{%M%jNwsq_CW`H0kNREYtu8b>5{L2bP}uV>%<4{_BXj%$MAgJ7%z|&SxL~U!NJ=4)sb-Bnl(d_ z@Zj3onn-w6-I}2xMV?i?V*T#KQM=c1Px1Ldu-W-1Y&?sHr^lPut&=yw+y!$N%>7<7 z_i=to@RbuACLUmk4Rf4xs)@c~j@cLsHqOx-j))5j2nz@c2n$Ry3o!m)Mv_FwZ+}N8 z6+bGNmEooAB(gY0sS1h&aT1?A#V61E_sMg-7f#S7L7T?^yg$)tQ^Dp@K$=F*(kH_y zqc|}CH}_N~`a%}`f0AK&F&$w6VF6)*yR|?GuUNzG|Fz!v{{P)JC+Z0c2n#%b7J%ZG zSW6QQJb&Gwa1QLQT^RjA^vtn(!(93*7zEex{0O^>*MNS+VR@7#P!4nHt6`d}{b!c| T@7sS2#66?`%{`+@!vFsPhn^EK literal 0 HcmV?d00001 diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..00d0fd1 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,62 @@ +# Node.js +node_modules/ +npm-debug.log +yarn-debug.log +yarn-error.log +.npm +.yarn + +# Next.js +.next/ +out/ +.vercel + +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +.venv/ +venv/ +ENV/ +env/ + +# Git +.git/ +.gitignore +.gitattributes + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ +.DS_Store + +# Documentation +*.md +docs/ + +# Misc +.env +.env.local +.env*.local +*.log + diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..b29ba4f --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,56 @@ +# Pre-commit hooks configuration +# See https://pre-commit.com for more information + +repos: + # Python Backend Hooks + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.14.4 + hooks: + # Run the linter + - id: ruff + name: ruff (backend) + files: ^backend/ + args: [--fix] + # Run the formatter + - id: ruff-format + name: ruff format (backend) + files: ^backend/ + + # Python type checking + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.18.2 + hooks: + - id: mypy + name: mypy (backend) + files: ^backend/ + additional_dependencies: [] + + # Frontend linting and formatting (using local tools) + - repo: local + hooks: + - id: eslint-frontend + name: eslint (frontend) + entry: bash -c 'cd frontend && npm run lint -- --fix' + language: system + files: ^frontend/.*\.(ts|tsx|js|jsx)$ + pass_filenames: false + - id: prettier-frontend + name: prettier (frontend) + entry: bash -c 'files=("$@"); files=("${files[@]#frontend/}"); cd frontend && npx prettier --write "${files[@]}"' -- + language: system + files: ^frontend/.*\.(ts|tsx|js|jsx|json|css|md)$ + + # General hooks for all files + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: trailing-whitespace + exclude: ^frontend/package-lock\.json$ + - id: end-of-file-fixer + exclude: ^frontend/package-lock\.json$ + - id: check-yaml + - id: check-added-large-files + args: [--maxkb=1000] + - id: check-merge-conflict + - id: check-json + exclude: ^frontend/package-lock\.json$ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f5d5a91 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,74 @@ +# syntax=docker/dockerfile:1 +FROM --platform=$BUILDPLATFORM ubuntu:24.04 + +# Set build arguments for multi-platform support +ARG TARGETPLATFORM +ARG BUILDPLATFORM +ARG TARGETOS +ARG TARGETARCH + +# Prevent interactive prompts during package installation +ENV DEBIAN_FRONTEND=noninteractive + +# Set Python version +ENV PYTHON_VERSION=3.12.7 + +# Install system dependencies and build tools +RUN apt-get update && apt-get install -y \ + wget \ + curl \ + git \ + build-essential \ + libssl-dev \ + zlib1g-dev \ + libbz2-dev \ + libreadline-dev \ + libsqlite3-dev \ + libncursesw5-dev \ + xz-utils \ + tk-dev \ + libxml2-dev \ + libxmlsec1-dev \ + libffi-dev \ + liblzma-dev \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +# Install Python 3.12.7 from source +RUN wget https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tar.xz \ + && tar -xf Python-${PYTHON_VERSION}.tar.xz \ + && cd Python-${PYTHON_VERSION} \ + && ./configure --enable-optimizations --with-ensurepip=install \ + && make -j$(nproc) \ + && make altinstall \ + && cd .. \ + && rm -rf Python-${PYTHON_VERSION} Python-${PYTHON_VERSION}.tar.xz + +# Create symlinks for python and pip +RUN ln -s /usr/local/bin/python3.12 /usr/local/bin/python \ + && ln -s /usr/local/bin/python3.12 /usr/local/bin/python3 \ + && ln -s /usr/local/bin/pip3.12 /usr/local/bin/pip \ + && ln -s /usr/local/bin/pip3.12 /usr/local/bin/pip3 + +# Install uv +RUN curl -LsSf https://astral.sh/uv/install.sh | sh \ + && /root/.local/bin/uv --version \ + && ln -s /root/.local/bin/uv /usr/local/bin/uv \ + && ln -s /root/.local/bin/uvx /usr/local/bin/uvx + +# Install Node.js 20.x LTS (compatible with Next.js 15.3.1) +RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ + && apt-get install -y nodejs \ + && rm -rf /var/lib/apt/lists/* + +# Install TypeScript globally +RUN npm install -g typescript@5.8.3 + +# Verify installations +RUN echo "=== Installation Verification ===" \ + && python --version \ + && pip --version \ + && uv --version \ + && node --version \ + && npm --version \ + && tsc --version diff --git a/backend/.github/workflows/python-lint.yml b/backend/.github/workflows/python-lint.yml new file mode 100644 index 0000000..451db10 --- /dev/null +++ b/backend/.github/workflows/python-lint.yml @@ -0,0 +1,36 @@ +name: Greenhouse Python Linting + +on: + pull_request: + branches: + - main + push: + branches: + - main + +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Python 3.10 + uses: actions/setup-python@v2 + with: + python-version: "3.10" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pylint + pip install -r requirements.txt + + - name: Run pylint + run: | + pylint **/*.py + + - name: Check for linting issues + run: | + pylint --exit-zero **/*.py diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..d65058b --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,47 @@ +# Python bytecode +__pycache__/ +*.py[cod] +*.pyo + +# Virtual environment +venv/ +env/ +.venv/ +ENV/ +*.env + +# Database files +*.db +*.sqlite + +# FastAPI specific +*.log +*.pid +*.sock + +# IDEs and Editors +.idea/ +.vscode/ +*.swp + +# Docker +docker-compose.override.yml +docker-compose.prod.yml + +# Testing coverage and other generated files +.coverage +.tox/ +.nox/ +*.cover +*.hypothesis/ + +# Pydantic generated files +*.pydantic.* + +# Miscellaneous files +*.DS_Store +*.pytest_cache +*.coverage + +# DB +test.db diff --git a/backend/.python-version b/backend/.python-version new file mode 100644 index 0000000..37504c5 --- /dev/null +++ b/backend/.python-version @@ -0,0 +1 @@ +3.11 diff --git a/backend/CONTRIBUTING.md b/backend/CONTRIBUTING.md new file mode 100644 index 0000000..fec0d5d --- /dev/null +++ b/backend/CONTRIBUTING.md @@ -0,0 +1,927 @@ +# Contributing to Sepal Greenhouse Backend + +Thank you for your interest in contributing to the Sepal Greenhouse Backend! This document provides guidelines and instructions for contributing to this project. + +## Table of Contents + +- [Code of Conduct](#code-of-conduct) +- [Getting Started](#getting-started) +- [Development Setup](#development-setup) +- [Development Workflow](#development-workflow) +- [Code Standards](#code-standards) +- [Architecture Guidelines](#architecture-guidelines) +- [Testing](#testing) +- [Database Changes](#database-changes) +- [Git Workflow](#git-workflow) +- [Adding New Features](#adding-new-features) +- [Documentation](#documentation) +- [Pull Request Process](#pull-request-process) +- [Troubleshooting](#troubleshooting) + +--- + +## Code of Conduct + +This project follows a code of conduct. Please be respectful, inclusive, and professional in all interactions. + +--- + +## Getting Started + +### Prerequisites + +Before you begin, ensure you have the following installed: + +- **Python 3.11+**: [Download Python](https://www.python.org/downloads/) +- **uv**: Ultra-fast Python package installer - [Install uv](https://github.com/astral-sh/uv) (optional for local dev) +- **Docker & Docker Compose**: [Install Docker](https://docs.docker.com/get-docker/) +- **Git**: [Install Git](https://git-scm.com/downloads/) +- **Code Editor**: VS Code, PyCharm, or your preferred IDE + +### First-Time Contributors + +1. **Familiarize yourself with the project**: + - Read the [README.md](README.md) + - Review the [API documentation](http://localhost:8000/docs) (after running the app) + - Check the [project documentation](/docs) + +2. **Set up your development environment** (see below) + +3. **Find an issue to work on**: + - Look for issues tagged with `good first issue` + - Ask questions if anything is unclear + +--- + +## Development Setup + +### 1. Fork and Clone + +```bash +# Fork the repository on GitHub, then clone your fork +git clone https://github.com/YOUR_USERNAME/sepal-greenhouse-internal.git +cd sepal-greenhouse-internal/backend +``` + +### 2. Set Up Environment + +```bash +# Copy environment file +cp .env.example .env + +# Edit .env if needed (usually defaults are fine for development) +``` + +### 3. Start with Docker (Recommended) + +```bash +# Build and start containers +docker-compose up --build + +# The API will be available at http://localhost:8000 +# API docs at http://localhost:8000/docs +``` + +### 4. Seed the Database + +```bash +# In a new terminal +docker-compose exec api python infrastructure/db/seed_data.py +``` + +### 5. Verify Setup + +```bash +# Test health endpoint +curl http://localhost:8000/health + +# Should return: {"status": "healthy", "version": "2.0"} +``` + +### Alternative: Local Development (Without Docker) + +```bash +# Install uv (if not already installed) +curl -LsSf https://astral.sh/uv/install.sh | sh + +# Install dependencies with uv +uv pip install -e . + +# Initialize database +python -c "from infrastructure.db.database import Base, engine; Base.metadata.create_all(bind=engine)" + +# Seed database +python infrastructure/db/seed_data.py + +# Run the server +uvicorn main:app --reload --host 0.0.0.0 --port 8000 +``` + +--- + +## Development Workflow + +### Branch Naming Conventions + +Use descriptive branch names following this pattern: + +- `feature/add-candidate-api` - New features +- `fix/job-creation-bug` - Bug fixes +- `refactor/improve-error-handling` - Code refactoring +- `docs/update-contributing` - Documentation updates +- `test/add-service-tests` - Test additions + +### Making Changes + +1. **Create a new branch**: + ```bash + git checkout -b feature/your-feature-name + ``` + +2. **Make your changes** with hot-reload enabled: + - Docker automatically reloads on file changes + - Check logs: `docker-compose logs -f api` + +3. **Test your changes**: + ```bash + # Run all tests + docker-compose exec api pytest tests/ -v + + # Run specific test file + docker-compose exec api pytest tests/test_api/test_jobs.py -v + + # Run with coverage + docker-compose exec api pytest tests/ --cov=infrastructure --cov=schemas + ``` + +4. **Check code quality**: + ```bash + # Format code (if using ruff) + docker-compose exec api ruff format . + + # Lint code + docker-compose exec api ruff check . + ``` + +--- + +## Code Standards + +### Python Style Guide + +We follow [PEP 8](https://pep8.org/) with these specific guidelines: + +#### Naming Conventions + +- **Files**: `snake_case.py` (e.g., `job_service.py`) +- **Classes**: `PascalCase` (e.g., `JobService`, `JobCreate`) +- **Functions/Methods**: `snake_case` (e.g., `create_job`, `get_all_jobs`) +- **Variables**: `snake_case` (e.g., `job_data`, `total_count`) +- **Constants**: `UPPER_SNAKE_CASE` (e.g., `MAX_PAGE_SIZE`) +- **Private members**: `_leading_underscore` (e.g., `_internal_method`) + +#### Import Ordering + +```python +# Standard library imports +import os +from datetime import datetime +from typing import List, Optional + +# Third-party imports +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.orm import Session +from pydantic import BaseModel + +# Local application imports +from infrastructure.db.database import get_db +from infrastructure.db.models import Job +from infrastructure.db.repository import JobService +from schemas.job_schemas import JobCreate +``` + +#### Type Hints + +Always use type hints for function parameters and return values: + +```python +def create_job(db: Session, job_data: JobCreate) -> Job: + """ + Create a new job. + + Args: + db: Database session + job_data: Job creation data + + Returns: + Created job instance + + Raises: + HTTPException: If validation fails + """ + # Implementation + pass +``` + +#### Docstrings + +Use Google-style docstrings: + +```python +def process_data(data: dict, validate: bool = True) -> dict: + """ + Process incoming data with optional validation. + + Args: + data: Dictionary containing raw data + validate: Whether to validate data (default: True) + + Returns: + Dictionary with processed data + + Raises: + ValueError: If data is invalid when validate=True + + Example: + >>> result = process_data({"name": "test"}) + >>> print(result) + {'name': 'test', 'processed': True} + """ + pass +``` + +#### Code Formatting + +- **Line length**: Max 100 characters (soft limit), 120 (hard limit) +- **Indentation**: 4 spaces (no tabs) +- **Blank lines**: 2 before class/function definitions, 1 between methods +- **String quotes**: Use double quotes `"` for strings, single `'` for internal strings + +--- + +## Architecture Guidelines + +### Domain-Driven Design (DDD) + +The project follows Domain-Driven Design architecture: + +``` +backend/ +├── main.py # Application entry point +├── infrastructure/ # Infrastructure layer +│ ├── db/ # Database infrastructure +│ │ ├── database.py # DB connection & session +│ │ ├── models.py # All ORM models (8 models) +│ │ └── repository.py # JobService (business logic) +│ └── api/ # API controllers +│ └── jobs_controller.py # HTTP endpoints +└── schemas/ # Pydantic validation schemas + └── job_schemas.py # Request/response schemas +``` + +### When to Create New Components + +#### New Model (infrastructure/db/models.py) +When you need to: +- Store data in a new database table +- Define relationships with existing models +- **Note**: All models are consolidated in a single file + +#### New Schema (schemas/) +When you need to: +- Validate request data +- Format response data +- Define different views of the same model (Create, Update, Response) + +#### New Service (infrastructure/db/repository.py) +When you need to: +- Implement business logic +- Orchestrate multiple model operations +- Add complex validation +- **Note**: Currently using JobService pattern + +#### New Controller (infrastructure/api/) +When you need to: +- Expose a new API endpoint +- Handle HTTP-specific concerns + +### Error Handling Pattern + +Always use consistent error responses: + +```python +from fastapi import HTTPException, status + +# Not found errors +raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail={ + "error": { + "code": "RESOURCE_NOT_FOUND", + "message": "Resource with ID {id} not found", + "field": None, + } + }, +) + +# Validation errors +raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail={ + "error": { + "code": "VALIDATION_ERROR", + "message": "Invalid input data", + "field": "field_name", + } + }, +) +``` + +### Response Format Pattern + +All successful responses use this format: + +```python +from datetime import datetime + +def create_response(data): + """Create standardized API response.""" + return { + "data": data, + "metadata": { + "timestamp": datetime.utcnow().isoformat() + "Z", + "version": "2.0" + } + } + +return create_response(data) +# Returns: {"data": {...}, "metadata": {"timestamp": "...", "version": "2.0"}} +``` + +--- + +## Testing + +### Testing Principles + +- **Write tests for all new features** +- **Maintain >80% code coverage** +- **Test both success and failure paths** +- **Use descriptive test names** + +### Test Structure + +```python +def test_create_job_success(client, sample_department): + """Test creating a job with valid data.""" + # Arrange + job_data = { + "name": "Software Engineer", + "status": "open", + "department_id": sample_department.id + } + + # Act + response = client.post("/api/jobs", json=job_data) + + # Assert + assert response.status_code == 201 + data = response.json()["data"] + assert data["name"] == "Software Engineer" + assert "id" in data +``` + +### Test Categories + +1. **Unit Tests** (`tests/test_services/`): + - Test service layer logic in isolation + - Mock database calls if needed + +2. **API Tests** (`tests/test_api/`): + - Test HTTP endpoints + - Verify request/response format + - Test error handling + +3. **Integration Tests**: + - Test complete workflows + - Verify database operations + +### Running Tests + +```bash +# All tests +docker-compose exec api pytest tests/ -v + +# Specific file +docker-compose exec api pytest tests/test_api/test_jobs.py -v + +# Specific test +docker-compose exec api pytest tests/test_api/test_jobs.py::test_create_job_success -v + +# With coverage +docker-compose exec api pytest tests/ --cov=infrastructure --cov=schemas --cov-report=html + +# Coverage report will be in htmlcov/index.html +``` + +### Test Fixtures + +Use fixtures defined in `conftest.py`: + +```python +def test_example(client, db_session, sample_department, sample_office): + """ + Available fixtures: + - client: TestClient for API calls + - db_session: Database session (auto-cleaned) + - sample_department: Pre-created department + - sample_office: Pre-created office + - sample_offices: List of 3 offices + - sample_source: Pre-created source + """ + pass +``` + +--- + +## Database Changes + +### Creating Migrations + +When you modify models, create a migration: + +```bash +# Initialize Alembic (if not done) +docker-compose exec api alembic init alembic + +# Create a migration +docker-compose exec api alembic revision --autogenerate -m "Add candidate table" + +# Review the migration file in alembic/versions/ + +# Apply migration +docker-compose exec api alembic upgrade head + +# Rollback if needed +docker-compose exec api alembic downgrade -1 +``` + +### Schema Design Principles + +- Use **UUIDs** for primary keys (string type) +- Include **created_at** and **updated_at** timestamps +- Define **foreign keys** with proper constraints +- Add **indexes** on frequently queried fields +- Use **nullable=False** for required fields +- Define **cascade deletes** where appropriate + +Example model (add to infrastructure/db/models.py): + +```python +from infrastructure.db.database import Base +from sqlalchemy import Column, String, ForeignKey, Boolean, DateTime +from sqlalchemy.orm import relationship +from sqlalchemy.sql import func +import uuid + +class Example(Base): + """Example entity.""" + + __tablename__ = "examples" + + # Primary key and timestamps (following BaseModel pattern) + id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4())) + created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False) + updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) + + # Fields + name = Column(String, nullable=False, index=True) + status = Column(String, nullable=False, default="active") + is_active = Column(Boolean, default=True) + + # Foreign keys + parent_id = Column(String, ForeignKey("parents.id"), nullable=True) + + # Relationships + parent = relationship("Parent", back_populates="examples") + + def __repr__(self): + return f"" +``` + +### Seeding Data + +Update `infrastructure/db/seed_data.py` if you add reference data: + +```python +def seed_new_entity(db: Session) -> None: + """Seed new entity data.""" + entities = [ + {"name": "Entity 1"}, + {"name": "Entity 2"}, + ] + + for entity_data in entities: + existing = db.query(Entity).filter(Entity.name == entity_data["name"]).first() + if not existing: + entity = Entity(**entity_data) + db.add(entity) + logger.info(f"Created entity: {entity_data['name']}") + + db.commit() +``` + +--- + +## Git Workflow + +### Commit Messages + +Follow the [Conventional Commits](https://www.conventionalcommits.org/) specification: + +``` +(): + + + +