From 448088e4d9d5d577e78b4e6deca388f705cb17ac Mon Sep 17 00:00:00 2001 From: Joaquin Hernandez Martinez Date: Wed, 15 Apr 2026 13:14:33 +0200 Subject: [PATCH 1/8] v1.0.5 - Added CI/CD --- .github/CODEOWNERS | 5 + .github/dependabot.yml | 40 ++++ .github/workflows/auto-merge.yml | 204 ++++++++++++++++ .github/workflows/ci.yml | 127 ++++++++++ .github/workflows/codeql.yml | 36 +++ .github/workflows/notify-production.yml | 81 +++++++ project/project/settings.py | 2 +- pyproject.toml | 2 +- readme.md | 299 +++++++++++++++++++++++- uv.lock | 2 +- 10 files changed, 787 insertions(+), 11 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/auto-merge.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/notify-production.yml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..a2b9a1f --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,5 @@ +# Five a Day — Code Owners +# Docs: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners + +# Both accounts own everything +* @starseeker-code-public @starseeker-code diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..671ec87 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,40 @@ +version: 2 + +updates: + # Python dependencies (pip / uv) + - package-ecosystem: pip + directory: / + schedule: + interval: weekly + day: monday + time: "08:00" + timezone: "Europe/Madrid" + open-pull-requests-limit: 5 + target-branch: development + labels: + - dependencies + - python + groups: + # Group all non-breaking minor/patch updates into one PR + python-minor-patch: + update-types: + - minor + - patch + ignore: + # Django major versions require manual upgrade planning + - dependency-name: django + update-types: [version-update:semver-major] + + # GitHub Actions + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + day: monday + time: "08:00" + timezone: "Europe/Madrid" + open-pull-requests-limit: 5 + target-branch: development + labels: + - dependencies + - github-actions diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml new file mode 100644 index 0000000..e68195b --- /dev/null +++ b/.github/workflows/auto-merge.yml @@ -0,0 +1,204 @@ +name: Auto-merge development → testing + +# Runs every hour. Merges development into testing when: +# 1. development has commits not yet in testing +# 2. the last commit on development is at least 24h old +# 3. CI is passing on the latest development commit +# After a successful merge, creates a PR from testing → main (if one doesn't exist). + +on: + schedule: + - cron: '0 * * * *' # Every hour at :00 + workflow_dispatch: # Manual trigger from the Actions tab + +permissions: + contents: write + pull-requests: write + checks: read + +jobs: + auto-merge: + name: Check and merge + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + # Use a PAT so that the push and PR creation trigger downstream CI. + # Falls back to GITHUB_TOKEN if GH_PAT is not configured. + token: ${{ secrets.GH_PAT || github.token }} + + - name: Check merge conditions + id: conditions + run: | + git fetch origin + + # 1. Is development ahead of testing? + AHEAD=$(git rev-list --count origin/testing..origin/development) + if [ "$AHEAD" -eq 0 ]; then + echo "development is not ahead of testing — nothing to do." + echo "should_merge=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + # 2. Is the last commit on development at least 24 hours old? + LAST_COMMIT_EPOCH=$(git log origin/development -1 --format="%ct") + NOW_EPOCH=$(date +%s) + AGE=$(( NOW_EPOCH - LAST_COMMIT_EPOCH )) + echo "Last commit age: ${AGE}s (need ≥ 86400)" + + if [ "$AGE" -lt 86400 ]; then + echo "Last commit is less than 24 h old — waiting." + echo "should_merge=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + LAST_SHA=$(git rev-parse origin/development) + LAST_MSG=$(git log origin/development -1 --format="%s") + + echo "should_merge=true" >> "$GITHUB_OUTPUT" + echo "last_sha=$LAST_SHA" >> "$GITHUB_OUTPUT" + echo "last_msg=$LAST_MSG" >> "$GITHUB_OUTPUT" + + - name: Check CI status on development + id: ci + if: steps.conditions.outputs.should_merge == 'true' + uses: actions/github-script@v7 + with: + script: | + const sha = '${{ steps.conditions.outputs.last_sha }}'; + + const { data } = await github.rest.checks.listForRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: sha, + filter: 'latest', + per_page: 100 + }); + + // Exclude this workflow itself from the check + const relevant = data.check_runs.filter(r => r.name !== 'Check and merge'); + + if (relevant.length === 0) { + console.log('No CI runs found on this commit — proceeding.'); + core.setOutput('passed', 'true'); + return; + } + + const inProgress = relevant.filter(r => r.status !== 'completed'); + if (inProgress.length > 0) { + console.log('CI still in progress:', inProgress.map(r => r.name).join(', ')); + core.setOutput('passed', 'false'); + return; + } + + const failed = relevant.filter( + r => !['success', 'skipped', 'neutral'].includes(r.conclusion) + ); + if (failed.length > 0) { + console.log('CI failed:', failed.map(r => `${r.name}: ${r.conclusion}`).join(', ')); + core.setOutput('passed', 'false'); + } else { + console.log('CI passed on', sha); + core.setOutput('passed', 'true'); + } + + - name: Merge development into testing + id: merge + if: > + steps.conditions.outputs.should_merge == 'true' && + steps.ci.outputs.passed == 'true' + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + DATE=$(date +%Y-%m-%d) + LAST_MSG="${{ steps.conditions.outputs.last_msg }}" + MERGE_MSG="$DATE - $LAST_MSG" + + git checkout -B testing origin/testing + git merge origin/development --no-ff -m "$MERGE_MSG" + git push origin testing + + echo "merge_msg=$MERGE_MSG" >> "$GITHUB_OUTPUT" + echo "Merged with message: $MERGE_MSG" + + - name: Create PR testing → main + id: pr + if: steps.merge.conclusion == 'success' + uses: actions/github-script@v7 + with: + script: | + // Skip if a PR already exists + const existing = await github.rest.pulls.list({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + head: `${context.repo.owner}:testing`, + base: 'main' + }); + + if (existing.data.length > 0) { + console.log('PR already open:', existing.data[0].html_url); + core.setOutput('url', existing.data[0].html_url); + core.setOutput('already_existed', 'true'); + return; + } + + const mergeMsg = '${{ steps.merge.outputs.merge_msg }}'; + + const pr = await github.rest.pulls.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: mergeMsg, + body: [ + '## Automated merge: `testing` → `main`', + '', + 'This PR was created automatically after `development` was merged into `testing`.', + '', + '### Pre-merge checklist', + '- [ ] All CI checks passing on this PR', + '- [ ] Manual review completed', + '- [ ] Version confirmed in `pyproject.toml` and `project/settings.py`', + '- [ ] `DEPLOYMENT.md` is up to date if infrastructure changed', + '- [ ] No breaking changes to the DB schema without a migration', + ].join('\n'), + head: 'testing', + base: 'main' + }); + + console.log('Created PR:', pr.data.html_url); + core.setOutput('url', pr.data.html_url); + core.setOutput('already_existed', 'false'); + + - name: Notify owners by email + if: steps.merge.conclusion == 'success' + uses: dawidd6/action-send-mail@v3 + with: + server_address: smtp.gmail.com + server_port: 465 + secure: true + username: ${{ secrets.EMAIL_HOST_USER }} + password: ${{ secrets.EMAIL_SECRET }} + from: "Five a Day CI <${{ secrets.EMAIL_HOST_USER }}>" + to: ${{ secrets.OWNER_EMAILS }} + subject: "[Five a Day] development → testing merged — PR awaiting review" + html_body: | +

development → testing merged

+

A new merge has landed on the testing branch and a pull request to main is now awaiting review.

+ + + + + +
Merge commit${{ steps.merge.outputs.merge_msg }}
Repository${{ github.repository }}
PR status${{ steps.pr.outputs.already_existed == 'true' && 'Updated (PR already existed)' || 'Newly created' }}
+ +

+ Review PR → +

+ +
+

+ Sent automatically by the auto-merge workflow. The PR cannot be merged to main until all CI checks pass and a code owner approves. +

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c5ed88c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,127 @@ +name: CI + +on: + push: + branches: [development, testing, main] + pull_request: + branches: [testing, main] + +# Cancel in-progress runs on the same branch when new commits arrive +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + # ============================================================ + # JOB 1: Lint — Ruff (fast, no DB needed) + # ============================================================ + lint: + name: Lint + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + + - name: Install dependencies + run: uv sync --frozen --no-install-project + + - name: Ruff — check + run: uv run --no-project ruff check project/ + + - name: Ruff — format + run: uv run --no-project ruff format --check project/ + + - name: Bandit — security linter + run: PYTHONUTF8=1 uv run bandit -r project/ -c pyproject.toml + + # ============================================================ + # JOB 2: Type check — mypy (no DB needed) + # ============================================================ + typecheck: + name: Type check + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + + - name: Install dependencies + run: uv sync --frozen --no-install-project + + - name: mypy + run: uv run mypy project/ + + # ============================================================ + # JOB 3: Tests — pytest against PostgreSQL + # ============================================================ + test: + name: Tests + runs-on: ubuntu-latest + + services: + postgres: + image: postgres:16 + env: + POSTGRES_DB: fiveaday_test + POSTGRES_USER: fiveaday_user + POSTGRES_PASSWORD: fiveaday_test_pw + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + + - name: Install dependencies + run: uv sync --frozen --no-install-project + + - name: Run tests + working-directory: project + env: + DJANGO_SETTINGS_MODULE: project.settings_test + POSTGRES_DB: fiveaday_test + POSTGRES_USER: fiveaday_user + POSTGRES_PASSWORD: fiveaday_test_pw + TEST_DB_HOST: localhost + DJANGO_SECRET_KEY: ci-test-secret-key-not-for-production + LOGIN_USERNAME: ci_admin + LOGIN_PASSWORD: ci_test_password + DJANGO_ALLOWED_HOSTS: localhost,127.0.0.1 + run: | + uv run python -m pytest tests/ -v --tb=short -n auto \ + --cov=core --cov=students --cov=billing --cov=comms \ + --cov-report=xml --cov-report=term-missing + + - name: Upload coverage to Codecov + if: always() + uses: codecov/codecov-action@v4 + with: + files: project/coverage.xml + fail_ci_if_error: false + token: ${{ secrets.CODECOV_TOKEN }} + + - name: Upload coverage artifact + if: always() + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: project/coverage.xml + retention-days: 7 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..ffdabf3 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,36 @@ +name: CodeQL Security Analysis + +on: + push: + branches: [main, testing, development] + pull_request: + branches: [main] + schedule: + - cron: '30 4 * * 1' # Every Monday at 04:30 UTC + +permissions: + actions: read + contents: read + security-events: write + +jobs: + analyze: + name: Analyze Python + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: python + queries: security-and-quality + + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: /language:python diff --git a/.github/workflows/notify-production.yml b/.github/workflows/notify-production.yml new file mode 100644 index 0000000..8a687e9 --- /dev/null +++ b/.github/workflows/notify-production.yml @@ -0,0 +1,81 @@ +name: Notify production + +# Fires whenever a new commit lands on main (production). Sends an email to +# hellofiveaday@gmail.com so the team knows a production deploy is ready. + +on: + push: + branches: [main] + +permissions: + contents: read + +jobs: + notify: + name: Send production notification + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 2 # Need HEAD and HEAD~1 for diff summary + + - name: Gather commit info + id: info + run: | + SHA=${{ github.sha }} + SHORT_SHA=$(git rev-parse --short "$SHA") + MESSAGE=$(git log -1 --format="%s" "$SHA") + AUTHOR=$(git log -1 --format="%an" "$SHA") + FILES_CHANGED=$(git diff --stat HEAD~1 HEAD | tail -1 || echo "unknown") + + { + echo "short_sha=$SHORT_SHA" + echo "message=$MESSAGE" + echo "author=$AUTHOR" + echo "files_changed=$FILES_CHANGED" + } >> "$GITHUB_OUTPUT" + + - name: Send email + uses: dawidd6/action-send-mail@v3 + with: + server_address: smtp.gmail.com + server_port: 465 + secure: true + username: ${{ secrets.EMAIL_HOST_USER }} + password: ${{ secrets.EMAIL_SECRET }} + from: "Five a Day CI <${{ secrets.EMAIL_HOST_USER }}>" + to: hellofiveaday@gmail.com + subject: "[Five a Day] New commit on main — production deploy ready" + html_body: | +

New commit on main (production)

+

A new commit has landed on the main branch. Production deploy is ready when you are.

+ + + + + + + +
Commit${{ steps.info.outputs.message }}
Author${{ steps.info.outputs.author }}
SHA${{ steps.info.outputs.short_sha }}
Files changed${{ steps.info.outputs.files_changed }}
Repository${{ github.repository }}
+ +

+ View commit → +

+ +
+

Next steps

+
    +
  1. Verify CI is green on this commit
  2. +
  3. Deploy to Cloud Run: +
    gcloud builds submit --tag $IMAGE .
    +            gcloud run deploy fiveaday --image=$IMAGE --region=europe-southwest1
    +
  4. +
  5. Run migrations if the schema changed: +
    gcloud run jobs execute fiveaday-migrate --region=europe-southwest1 --wait
    +
  6. +
+ +

+ Sent automatically by the notify-production workflow on every push to main. +

diff --git a/project/project/settings.py b/project/project/settings.py index f8508c6..6c149b4 100644 --- a/project/project/settings.py +++ b/project/project/settings.py @@ -15,7 +15,7 @@ # NOTA: Al cambiar la versión, actualizar también en: # - readme.md (badge y texto) # - pyproject.toml (campo version) -APP_VERSION = os.getenv("APP_VERSION", "1.0.4") +APP_VERSION = os.getenv("APP_VERSION", "1.0.5") # ============================================================================ # SECURITY SETTINGS diff --git a/pyproject.toml b/pyproject.toml index 5dc29f2..3d78a11 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "five-a-day" -version = "1.0.4" +version = "1.0.5" description = "Five a Day management software" readme = "README.md" requires-python = ">=3.12" diff --git a/readme.md b/readme.md index 996ff73..0856185 100644 --- a/readme.md +++ b/readme.md @@ -9,7 +9,7 @@

- Version + Version Python Django PostgreSQL @@ -31,17 +31,20 @@ Built to centralize student records, automate billing cycles, and streamline par | Environment | Version | Status | |-------------|---------|--------| -| **Production** | v0.0.0 | ![undeployed](https://img.shields.io/badge/undeployed-red) | -| **Testing (QA)** | v1.0.1t | ![ready](https://img.shields.io/badge/ready_to_deploy-blue) | -| **Development** | v1.0.0 | ![active](https://img.shields.io/badge/active-brightgreen) | +| **Production** | v1.0.4 | ![ready](https://img.shields.io/badge/ready_to_deploy-blue) | +| **Testing (QA)** | v1.0.4 | ![ready](https://img.shields.io/badge/ready_to_deploy-blue) | +| **Development** | v1.0.4 | ![active](https://img.shields.io/badge/active-brightgreen) | | | | |---|---| -| **Documentation** | This README, [DEPLOYMENT.md](DEPLOYMENT.md), [HTTPS.md](docs/HTTPS.md), [UV.md](docs/UV.md), per-app READMEs, [CLAUDE.md](CLAUDE.md) | +| **Documentation** | This README, [DEPLOYMENT.md](DEPLOYMENT.md), [GITHUB.md](docs/GITHUB.md), [HTTPS.md](docs/HTTPS.md), [UV.md](docs/UV.md), [CELERY.md](docs/CELERY.md), per-app READMEs, [CLAUDE.md](CLAUDE.md) | | Version | Date | Description | |---------|------|-------------| -| **v1.0.1t** | 2026-04-14 | QA/testing environment: `/testing/` dashboard, database seeding, backlog with email, error reporting, HTTPS guide, access control via `QA_TESTING_USERNAME` | +| **v1.0.4** | 2026-04-15 | GitHub Actions CI/CD pipeline (lint, typecheck, tests, CodeQL, Dependabot), auto-merge `development` → `testing` with 24h delay + auto-PR to `main`, email notifications, branch protection rules, inspirational quote generator, GCP deployment plan, cleaned legacy Render config, `make version x.y.z` + `make pc-run` with auto version bump | +| v1.0.3 | 2026-04-14 | Test coverage raised to 70% — 294 tests total (40+ new tests) across auth views, comms services, dashboard, schedule, management, and apps modules | +| v1.0.2 | 2026-04-13 | Replaced Poetry with UV, full developer tooling (Ruff, mypy, bandit, pip-audit, pre-commit, pytest-cov, pytest-xdist, pytest-randomly) | +| v1.0.1t | 2026-04-14 | QA/testing environment: `/testing/` dashboard, database seeding, backlog with email, error reporting, HTTPS guide, access control via `QA_TESTING_USERNAME` | | v1.0.0 | 2026-04-11 | Security hardening, query optimization (Case/When aggregates, N+1 fixes), GCP config, transaction safety | | v1.0.0 | 2026-04-10 | Multi-app architecture, service layer, 132 tests, frontend cleanup, full documentation | | v0.30.2 | 2025-03-14 | History system, GDPR for adults, Docker Compose workflow | @@ -133,6 +136,18 @@ Built to centralize student records, automate billing cycles, and streamline par - [For developers: how the QA environment works](#for-developers-how-the-qa-environment-works) - [Access control for /testing/](#access-control-for-testing) - [GCP deployment plan](#gcp-deployment-plan) + - [CI/CD \& GitHub Actions](#cicd--github-actions) + - [Pipeline Overview](#pipeline-overview) + - [Branch Strategy](#branch-strategy) + - [Workflows](#workflows) + - [Automated Flows](#automated-flows) + - [Branch Protection — `main`](#branch-protection--main) + - [Branch Protection — `testing`](#branch-protection--testing) + - [Public Repository Hardening](#public-repository-hardening) + - [Required GitHub Secrets](#required-github-secrets) + - [Email Notifications](#email-notifications) + - [Dependabot](#dependabot) + - [CodeQL Security Scanning](#codeql-security-scanning) - [Contributing](#contributing) - [Development Workflow](#development-workflow) - [Code Conventions](#code-conventions) @@ -143,8 +158,86 @@ Built to centralize student records, automate billing cycles, and streamline par ## Version History & Roadmap -

-v1.0.1t — QA Testing Environment (current, testing branch) +
+v1.0.4 — CI/CD Pipeline, GCP Migration Plan, Quote Generator (current) + +**GitHub Actions CI/CD** (new — see [docs/GITHUB.md](docs/GITHUB.md)) + +- `ci.yml` — three parallel jobs on every push/PR: Ruff + Bandit lint, mypy type check, pytest against PostgreSQL 16 service container with coverage uploaded to Codecov +- `auto-merge.yml` — hourly cron that merges `development` → `testing` after 24h of inactivity and CI passing, then auto-creates a PR `testing` → `main` +- `codeql.yml` — weekly Python security analysis (OWASP Top 10, Django-specific queries) +- `notify-production.yml` — emails `hellofiveaday@gmail.com` on every push to `main` with commit info and deploy instructions +- Owner email notifications when `development` → `testing` merge lands + PR opened to `main` +- `dependabot.yml` — grouped weekly Python and GitHub Actions updates targeting `development` +- `CODEOWNERS` — auto-request reviews from both owner accounts + +**GCP migration plan** (new — see [DEPLOYMENT.md](DEPLOYMENT.md)) + +- Full Cloud Run + Cloud SQL architecture documented +- Three environments: local Docker (dev), Compute Engine e2-micro free tier (testing), Cloud Run + Cloud SQL (production) +- Cost estimate: ~$15-27/month for production, $0/month for testing +- Celery replacement strategy using Cloud Scheduler + Cloud Run Jobs +- Cleaned legacy Render config (`render.yaml` removed, commented nginx and pgAdmin services removed from docker-compose) + +**Dashboard enhancement** + +- Inspirational quote generator on `/home` — fetches two daily quotes from `zenquotes.io`, stores them in a 48h cookie, rotates daily (day 0 shows quote 1, day 1+ shows quote 2), graceful fallback to the default Spanish subtitle on API failure + +**Developer tooling** + +- `make version x.y.z` — positional argument (replaces `V=x.y.z`) with confirmation guard before writing +- `make pc-run` — renamed from `pre-commit-run`; after a clean pass, prompts to auto-increment the patch version in `pyproject.toml` and `project/settings.py` + +**Bug fixes** + +- Celery worker and beat containers added to docker-compose with correct permissions and health checks +- Several payment and enrollment issues fixed + +
+ +
+v1.0.3 — Test Coverage Expansion (70%) + +**Testing** + +- Test count raised from 252 to **294** (40+ new tests) +- Coverage raised to **70%** across `core`, `students`, `billing`, `comms` +- New test files: `test_auth_views.py`, `test_comms_services.py`, `test_dashboard.py`, `test_schedule_views.py`, `test_management_views.py`, `test_app_forms.py` +- Additional parametrized test cases for email-form views and error pages + +**Coverage tooling** + +- `coverage.svg` badge now reflects the improved coverage +- `make coverage-badge` command streamlines badge regeneration + +
+ +
+v1.0.2 — UV Migration & Developer Tooling + +**Dependency management** + +- Replaced Poetry with UV (see [docs/UV.md](docs/UV.md)) +- `uv.lock` replaces `poetry.lock` +- All Make commands updated to use `uv run` + +**Developer tooling** + +- **Ruff** — unified lint + format (replaces flake8, isort, black) +- **mypy** with `django-stubs` — static type checking +- **bandit** — Python security linter +- **pip-audit** — dependency CVE scanning +- **pytest-xdist** — parallel test execution (`-n auto`) +- **pytest-randomly** — randomized test order with reproducible seeds +- **pytest-cov** — coverage reports (HTML + XML + terminal) +- **pre-commit** hooks — Ruff, mypy, bandit on every commit + +All tools configured in `pyproject.toml` — single source of truth. + +
+ +
+v1.0.1t — QA Testing Environment **Testing infrastructure** - QA Docker Compose overlay (`docker-compose.testing.yml`) — Gunicorn, `DEBUG=False`, separate DB volume @@ -1433,6 +1526,196 @@ After deployment, Cloud Run provides a URL like `https://fiveaday-testing-xxxxx. --- +## CI/CD & GitHub Actions + +The project runs a fully automated CI/CD pipeline on GitHub Actions. Every push is tested, every merge is audited, and production is reached only through a protected pull request. The full configuration reference is in [docs/GITHUB.md](docs/GITHUB.md) — this section is the overview. + +### Pipeline Overview + +```text +Push to development + │ + ▼ +CI runs (lint + typecheck + tests) + CodeQL + │ + │ hourly cron + ▼ +Auto-merge check + • development ahead of testing? + • last commit ≥ 24 h old? + • CI passing on that commit? + │ all yes + ▼ +git merge development → testing +(commit: "YYYY-MM-DD - ") + │ + ├── CI re-runs on testing + └── PR created: testing → main + │ + ▼ +Email to owners (OWNER_EMAILS) + │ + ▼ +Manual review + Code Owner approval + │ + ▼ +Merge to main (protected — all checks required) + │ + ▼ +Email to hellofiveaday@gmail.com +(production deploy ready) +``` + +### Branch Strategy + +| Branch | Purpose | Protected | Direct push | +|---------------|------------------------------------------|--------------------------|--------------------------| +| `main` | Production. Every commit is deployable. | Full protection | No (PR + review only) | +| `testing` | Staging. Auto-merged from development. | Minimal (no force/delete)| Only from auto-merge flow| +| `development` | Active development. Day-to-day work. | None | Yes | + +Feature branches off `development` are welcome for non-trivial work, but the expected flow is: work on `development` → wait 24 h → auto-promoted to `testing` → manual merge to `main`. + +### Workflows + +| Workflow | File | Triggers | Purpose | +|----------|------|----------|---------| +| **CI** | [`ci.yml`](.github/workflows/ci.yml) | Push to `development`/`testing`/`main`; PRs to `testing`/`main` | Three parallel jobs — **Lint** (Ruff + Bandit), **Type check** (mypy), **Tests** (pytest + PostgreSQL 16 service container + Codecov upload) | +| **Auto-merge** | [`auto-merge.yml`](.github/workflows/auto-merge.yml) | Hourly cron + manual dispatch | Merges `development` → `testing` when conditions pass, creates PR to `main`, emails owners | +| **CodeQL** | [`codeql.yml`](.github/workflows/codeql.yml) | Push to `main`/`testing`/`development`; PRs to `main`; Monday 04:30 UTC | Python static security analysis (OWASP Top 10, Django-specific queries) | +| **Notify production** | [`notify-production.yml`](.github/workflows/notify-production.yml) | Push to `main` | Emails `hellofiveaday@gmail.com` with commit info and `gcloud` deploy instructions | +| **Dependabot** | [`dependabot.yml`](.github/dependabot.yml) | Weekly (Mondays 08:00 Madrid) | Grouped Python and GitHub Actions updates targeting `development` | + +Concurrent CI runs on the same branch cancel each other automatically — new pushes always produce a fresh run. + +### Automated Flows + +**1. You push to `development`** + +- CI triggers immediately (lint, typecheck, tests run in parallel, ~2-4 min) +- CodeQL triggers immediately (weekly scan also runs independently) +- The hourly auto-merge cron checks this commit every hour until it is ≥ 24 h old with passing CI, then promotes to `testing` + +**2. Auto-merge fires** + +- Creates a `--no-ff` merge commit on `testing` titled `YYYY-MM-DD - ` +- Pushes to `testing` (which triggers CI on `testing`) +- Opens PR `testing → main` if one is not already open (title matches the merge commit) +- Sends an HTML email to `OWNER_EMAILS` with a "Review PR" button + +**3. You review and merge the PR** + +- All required checks must pass (Lint, Type check, Tests, CodeQL alerts, Code Owner approval) +- You cannot approve your own PR — the second owner account approves +- On merge, `main` is updated + +**4. Production notification fires** + +- `notify-production.yml` sends an email to `hellofiveaday@gmail.com` +- Email contains commit info, file-change summary, and the exact `gcloud` commands to deploy to Cloud Run + +### Branch Protection — `main` + +Configure at **Settings → Branches → Add ruleset**, target `main`: + +**Required status checks** (names must match CI job names exactly): + +| Check | Workflow | +|-------|----------| +| `Lint` | ci.yml | +| `Type check` | ci.yml | +| `Tests` | ci.yml | +| `Analyze Python` | codeql.yml | + +**Protection rules** (every item below enabled): + +| Rule | Setting | +|------|---------| +| Require a pull request before merging | ✓ | +| Required approvals | **1** (higher if you add collaborators) | +| Dismiss stale reviews when new commits are pushed | ✓ | +| Require review from Code Owners | ✓ | +| Require status checks to pass | ✓ | +| Require branches to be up to date before merging | ✓ | +| Require conversation resolution before merging | ✓ | +| Require signed commits | ✓ (strongly recommended for a public repo) | +| Require linear history | ✓ (enforces squash/rebase merges) | +| Restrict who can push to matching branches | ✓ | +| Do not allow bypassing the above settings | ✓ (admins follow the same rules) | +| Allow force pushes | ✗ | +| Allow deletions | ✗ | + +### Branch Protection — `testing` + +`testing` needs direct pushes from the auto-merge workflow, so PR requirements are **not** enforced. Apply only safety rails: + +| Rule | Setting | +|------|---------| +| Require a pull request before merging | ✗ | +| Allow force pushes | ✗ | +| Allow deletions | ✗ | +| Require status checks to pass (optional) | ✓ — lets CI block a broken auto-merge from polluting `testing` further | + +### Public Repository Hardening + +Because this repository is **public**, extra care is taken to prevent accidental secret leaks, abuse of the CI, and unreviewed contributions: + +| Control | Where | Why | +|---------|-------|-----| +| **GitHub Secret Scanning** | Settings → Code security | Free for public repos — detects committed secrets across history | +| **Push Protection** | Settings → Code security | Free for public repos — blocks pushes that contain secrets before they land | +| **CodeQL** | `codeql.yml` + Settings → Code security | Free for public repos — weekly security analysis | +| **Dependabot alerts + security updates** | Settings → Code security | Free for public repos — fixes known CVEs in dependencies | +| **Require 2FA for all contributors** | Organization settings (if in an org) | Prevents compromised account pushes | +| **Restrict fork PRs from running CI with secrets** | Settings → Actions → Fork PR workflows: require approval for first-time contributors | Prevents secret exfiltration via malicious PRs from forks | +| **Actions allow-list** | Settings → Actions → Allow specific actions | Prevents supply-chain attacks — pin to verified creators only | +| **Workflow permissions default: read-only** | Settings → Actions → Workflow permissions | Individual workflows explicitly request `write` where needed | +| **Block workflows from approving PRs** | Settings → Actions → Allow GitHub Actions to create and approve pull requests: **only allow create, not approve** | Humans must approve, even automated PRs | +| **SECURITY.md** | Root of the repo | Public disclosure policy so researchers know how to report vulnerabilities privately | +| **License file** | Root of the repo | Required for a public repo — defines what others can legally do with the code | + +The `.env` file is gitignored and **never** committed. Production secrets live in GCP Secret Manager (see [DEPLOYMENT.md](DEPLOYMENT.md)), not in the repository or in GitHub Secrets. GitHub Secrets are used only for CI operations (sending notification emails, uploading coverage). + +### Required GitHub Secrets + +Configure at **Settings → Secrets and variables → Actions**: + +| Secret | Required by | Purpose | +|--------|-------------|---------| +| `GH_PAT` | auto-merge.yml | Fine-grained Personal Access Token. Pushes to `testing` and creates PRs *while triggering downstream CI* (which the default `GITHUB_TOKEN` cannot do). Permissions: Contents RW, Pull requests RW, Checks R, Metadata R | +| `EMAIL_HOST_USER` | auto-merge.yml, notify-production.yml | Gmail address used to send notification emails | +| `EMAIL_SECRET` | auto-merge.yml, notify-production.yml | Gmail App Password — can be the same one the application uses for transactional email | +| `OWNER_EMAILS` | auto-merge.yml | Comma-separated recipient list for the `development → testing` merge notification | +| `CODECOV_TOKEN` | ci.yml | Optional — only needed for private repos. Public repos push coverage anonymously | + +**Rotate `GH_PAT` annually.** Without it, the auto-merge falls back to the default `GITHUB_TOKEN`, which cannot trigger CI on PRs it creates — breaking the pipeline silently. + +### Email Notifications + +| Event | Recipient | Sent by | +|-------|-----------|---------| +| `development → testing` merged + PR opened to `main` | `OWNER_EMAILS` (secret) | auto-merge.yml | +| New commit on `main` (production ready to deploy) | `hellofiveaday@gmail.com` (hardcoded) | notify-production.yml | + +Both use Gmail SMTP via the `dawidd6/action-send-mail@v3` action. Emails include HTML formatting, links to the commit/PR, and actionable next steps. + +### Dependabot + +Dependabot opens **weekly PRs on `development`** (Mondays, 08:00 Europe/Madrid) for: + +- **Python packages** — minor and patch updates grouped into a single PR. Django major version bumps are intentionally ignored (require manual upgrade planning). +- **GitHub Actions** — updates to `actions/*`, `astral-sh/setup-uv`, `dawidd6/action-send-mail`, etc. + +PRs are labelled `dependencies` + `python` or `github-actions` for easy filtering. The normal 24 h cycle carries merged updates to `testing` and then to `main`. + +### CodeQL Security Scanning + +Runs on every push and PR to `main`, plus a full scan every Monday at 04:30 UTC. Uses the `security-and-quality` query suite — covers OWASP Top 10, CWE Top 25, and Django-specific queries (SQL injection, path traversal, hardcoded credentials, insecure deserialization, etc.). + +Results appear in **Security → Code scanning alerts**. A new alert on `main` does not auto-block future merges unless branch protection is configured to require the CodeQL check. + +--- + ## Contributing ### Development Workflow diff --git a/uv.lock b/uv.lock index a0e1530..ba23172 100644 --- a/uv.lock +++ b/uv.lock @@ -695,7 +695,7 @@ wheels = [ [[package]] name = "five-a-day" -version = "1.0.4" +version = "1.0.5" source = { editable = "." } dependencies = [ { name = "celery" }, From b43651543524a948b6d66619ebe736251319beb5 Mon Sep 17 00:00:00 2001 From: Joaquin Hernandez Martinez Date: Wed, 15 Apr 2026 14:36:22 +0200 Subject: [PATCH 2/8] v1.0.6 - Added documentation skill for agentic documentation and reviewed current documentation (updating most of it), and fixed CI/CD pipeline, adding aditional logic --- .claude/skills/update-readme/SKILL.md | 460 ++++++++++++++++++++++++ .env.testing.example | 92 ----- .github/workflows/auto-merge.yml | 81 ++++- .github/workflows/notify-production.yml | 40 ++- .gitignore | 7 +- CLAUDE.md | 24 +- DEPLOYMENT.md | 3 +- Makefile | 10 +- readme.md => README.md | 400 +++++++++++++-------- project/comms/README.md | 8 +- project/core/README.md | 19 +- project/project/settings.py | 6 +- pyproject.toml | 2 +- uv.lock | 2 +- 14 files changed, 880 insertions(+), 274 deletions(-) create mode 100644 .claude/skills/update-readme/SKILL.md delete mode 100644 .env.testing.example rename readme.md => README.md (81%) diff --git a/.claude/skills/update-readme/SKILL.md b/.claude/skills/update-readme/SKILL.md new file mode 100644 index 0000000..e8602a3 --- /dev/null +++ b/.claude/skills/update-readme/SKILL.md @@ -0,0 +1,460 @@ +--- +name: update-readme +description: Use when the user says their work is done and they want the project documentation updated to reflect the staged changes. Despite the name, this skill updates the full documentation set — the top-level README.md, every per-app README, CLAUDE.md, DEPLOYMENT.md, and every file under docs/. Inspects the staged diff, routes changes to the correct docs, applies per-file checklists, and sweeps for stale references across all of them. +--- + +# update-readme + +The user invokes this skill when they have finished a unit of work and want the documentation brought up to date with what's in the staged area. + +**Scope.** This skill touches EVERY documentation file in the repo — not just `README.md`. The name is historical; treat it as `update-docs`. The documentation set is: + +| File | Authoritative for | +|------|-------------------| +| `README.md` (root) | Project overview, status, version history, tech stack, directory layout, testing, security, CI/CD, QA, contributing | +| `CLAUDE.md` | Rules and conventions future AI agents must follow. Gotchas. README-maintenance checklist. | +| `DEPLOYMENT.md` | GCP deployment — all 3 environments, costs, commands, free tier notes | +| `docs/GITHUB.md` | Full CI/CD reference — workflows, branch protection, secrets, public-repo hardening | +| `docs/HTTPS.md` | HTTPS setup (Docker Nginx + Cloud Run) | +| `docs/UV.md` | UV package manager guide | +| `docs/CELERY.md` | Celery worker/beat reference | +| `docs/TODO.md` | Open tasks log | +| `project/core/README.md` | Models, views, URLs, middleware, templates/static for the `core` app | +| `project/students/README.md` | Models, forms, admin, URLs for the `students` app | +| `project/billing/README.md` | Models, services, constants, exports, admin, URLs for the `billing` app | +| `project/comms/README.md` | EmailService, email functions, Celery tasks, management commands for `comms` | +| `project/ENROLLMENT_PAYMENT_SYSTEM.md` | Canonical enrollment/payment business rules (pricing, discounts, schedule) | + +**`.env.example` does not exist in this repo — do not create it.** The `.env` template lives inline in `README.md` under the heading **`.env template`** (a fenced `bash` code block inside the Development & Docker section). Every time you edit the app's env var surface, update that code block. + +Everything under `.venv/`, `.pytest_cache/`, `node_modules/`, `.git/` is **out of scope**. You **may read `.env`** solely to extract variable names and section comments when the user explicitly asks you to refresh the README's `.env template` code block — strip every value before writing anything user-visible. Never read `.env.testing` or any other `.env*`. + +--- + +## Step 1 — Inspect what changed + +Do not guess. Run these first and read the output: + +```bash +git status --porcelain # staged + unstaged +git diff --cached --stat # summary of staged changes +git diff --cached # full staged diff (use Grep on it if very large) +git log -10 --oneline # recent commits for version/date context +git log HEAD --format='%s%n%b' -1 # full current commit message (if any) +grep '^version' pyproject.toml # current version from pyproject +grep 'APP_VERSION' project/project/settings.py | head -1 # fallback default +git remote -v # owner/repo for CI badges +``` + +If the working tree has **unstaged** changes that look relevant, ask the user whether to include them. Never stage files yourself without confirmation. + +--- + +## Step 2 — Route staged files to the right docs + +For every staged path, the table below tells you which docs are candidates for updates. A single staged change may touch many docs; apply the union. + +| Staged path pattern | Docs to review | +|---------------------|----------------| +| `project/core/models.py`, `project/core/views/**`, `project/core/middleware.py`, `project/core/templates/**`, `project/core/static/**`, `project/core/decorators.py`, `project/core/context_processors.py` | `project/core/README.md` **+** main README (Architecture, Directory Layout, Features by View, Design Decisions) | +| `project/students/**/*.py` (models, forms, admin, urls) | `project/students/README.md` **+** main README (ER diagram if models changed, Directory Layout) | +| `project/billing/models.py`, `project/billing/services/**`, `project/billing/constants.py`, `project/billing/exports.py`, `project/billing/admin.py`, `project/billing/urls.py` | `project/billing/README.md` **+** main README (Database Schema, Features by View → Payments) **+** `project/ENROLLMENT_PAYMENT_SYSTEM.md` if pricing, discounts, or enrollment rules changed | +| `project/comms/services/**`, `project/comms/tasks.py`, `project/comms/management/commands/**`, `project/comms/urls.py` | `project/comms/README.md` **+** main README (Features by View → Apps) | +| `project/tests/**` | Main README (Testing section — test counts, per-file tables) **+** the app README whose logic the new test covers | +| `project/project/settings*.py` | Main README (Env Variables Reference, `.env template` code block, App Versioning) **+** `DEPLOYMENT.md` if a new env var | +| Any other Python file using `os.getenv(...)` | Main README (Env Variables Reference + `.env template` code block) **+** `DEPLOYMENT.md` Secret Manager list if the var carries a secret | +| `Makefile` | Main README (Make Commands table, Contributing → Make Commands Developer Tooling) **+** `CLAUDE.md` Gotchas if a command was renamed or removed | +| `pyproject.toml` | Main README (Tech Stack → Python Dependencies, Developer Tooling) **+** `docs/UV.md` if tooling-related **+** version badge and tables if version bumped | +| `uv.lock` | Usually no doc change needed (lock-file churn) — but confirm no version mismatch | +| `Dockerfile`, `docker-compose*.yml`, `entrypoint.sh` | Main README (Development & Docker, Testing Environment) **+** `DEPLOYMENT.md` if production image layout changed | +| `.github/workflows/**` | Main README (CI/CD section) **+** `docs/GITHUB.md` (full reference) | +| `.github/dependabot.yml`, `.github/CODEOWNERS` | Main README (CI/CD section) **+** `docs/GITHUB.md` | +| `docs/HTTPS.md` | Main README (Security → Transport, Testing Environment) | +| `docs/UV.md` | Main README (Tech Stack, Contributing) | +| `docs/CELERY.md` | Main README (Tech Stack), `project/comms/README.md` | +| `docs/GITHUB.md` | Main README CI/CD section (keep them in sync — README is the overview, GITHUB.md is the reference) | +| `DEPLOYMENT.md` | Main README (Project Status → Hosting column, GCP references) | +| `CLAUDE.md` | No downstream doc changes — but re-read it to confirm your edits don't contradict the rules | + +--- + +## Step 3 — Per-file detailed checklists + +### 3.1 — `README.md` (root) + +Work through this list in order. **Do not skip a step just because nothing "looks" staged for it** — the last step greps for stale content and will catch anything the routing table missed. + +#### a. Header badges (≈lines 11-18) + +- Version badge must equal `pyproject.toml`'s version +- CI and Codecov badges must use the owner/repo from `git remote -v` +- Don't bloat the header — no more badges than the originals + any new CI workflow + +#### b. Project Status table — **three rows, exact order: Production → Testing → Development** + +| Environment | Branch | Hosting | CI Status | +|-------------|--------|---------|-----------| +| **Production** | `main` | GCP Cloud Run + Cloud SQL (PostgreSQL 16), `europe-southwest1` | CI badge for `main` | +| **Testing (QA)** | `testing` | GCP Compute Engine e2-micro (free tier), Docker Compose | CI badge for `testing` | +| **Development** | `development` | Local machine via `make up` (Docker Compose) | CI badge for `development` | + +If hosting changes (region, new managed service, etc.) update the Hosting cell. Never reorder rows. Never add rows. + +#### c. Recent Versions table — **exactly 3 rows** + +- Current version + the two previous patches +- When adding a new version, **delete the oldest row** +- Date in `YYYY-MM-DD` +- Description: dense one-liner that names every user-visible change in that version. Synthesise — don't copy commit subjects verbatim. Distill from `git log ..HEAD --format='%s%n%b'` plus the staged diff. + +#### d. Version History `
` blocks + +- Add a new `
` at the top of the section +- **Remove** the `open` attribute from the previously-open block +- Content structure must match the existing blocks: `**Subsection**` bold headings + bullet lists. Typical subsections: GitHub Actions CI/CD, GCP migration, Dashboard enhancement, Developer tooling, Testing, Bug fixes, Public-repo hardening. Only include subsections that are actually relevant to what changed. +- Older blocks stay unless they contradict current state + +#### e. Roadmap + +If a roadmap item shipped, move its content to the Version History block for the shipping version and **remove** the roadmap entry. Do not leave completed items in the roadmap. + +#### f. Tech Stack + +- If `pyproject.toml` added/removed a package, update the Python Dependencies table +- If a new developer tool was adopted (e.g. detect-secrets, commitizen), add it to Developer Tooling + +#### g. Database Schema + +- If any `project/*/models.py` changed, compare fields to the Mermaid ER diagram and the Key Constraints table +- Model renames, new fields, deleted fields, new indexes, new UniqueConstraints — all must be reflected + +#### h. Development & Docker + +- `make help` command count changed? Update "60+ commands" (check with `grep -c '^[a-z].*:$' Makefile`) +- Quick Start commands still valid? +- Env Variables Reference: any new `os.getenv(...)` in `settings.py` → add a row +- App Versioning paragraph reflects the current `make version` syntax + +#### i. Project Structure & Architecture → Directory Layout + +Update every line that's wrong: + +- `tests/` line: `pytest suite (N tests, X% coverage)` — get N from `grep -r "^def test_" project/tests/ | wc -l` and X from the latest coverage report (or Codecov) +- `core/views/` annotation: view module count must be accurate +- `Makefile` line: command count +- New top-level files or directories (e.g. `.github/`, `docs/`, `scripts/`) +- Deleted files/directories (e.g. `render.yaml`) + +Also refresh the App: core/students/billing/comms summary tables if models, view modules, or URL counts changed. + +#### j. Features by View + +- If a view was added or renamed, add/update the section +- If a feature was removed from a page, remove its bullet + +#### k. Testing + +- Total test count must match `grep -r "^def test_" project/tests/ | wc -l` +- Coverage % must match the current Codecov or local coverage report +- Per-file test tables: test counts per file must match + +#### l. Security + +- If a deployment platform was removed (e.g. Render), delete every row, subsection, and mention. **Always** run `grep -in 'render\|gcp-cloudrun'` on the entire README before claiming it's clean. +- If a security protection is now enabled (e.g. GitHub secret scanning, push protection), **move** its row from "Future Security Improvements" to the relevant active table. +- If `SECURE_*` settings changed, update the Transport Security and Security Headers tables. + +#### m. Testing Environment (QA) + +- QA access instructions still accurate? +- Any env var renames in `.env.testing`? +- The GCP deployment plan is **gone** from this section — it lives only in `DEPLOYMENT.md` now. If it reappears, delete it. + +#### n. CI/CD & GitHub Actions + +- Workflows table: one row per `.github/workflows/*.yml` file +- Pipeline Overview diagram: update if flow steps changed +- Branch Protection tables: match what's actually configured (or what the user wants configured) +- Public Repository Hardening: a new protection toggle → a new row +- Required GitHub Secrets: every secret referenced in any workflow `${{ secrets.X }}` must appear in this table. Grep: `grep -rh 'secrets\.' .github/workflows/ | sort -u`. +- Email Notifications: one row per notification email sent by any workflow + +#### o. Contributing + +- Development Workflow numbered list: current `make` commands, current commit message convention +- Make Commands (Developer Tooling) table: every tool in `pyproject.toml` dev-dependencies should have a row with its `make` command + +#### p. Table of Contents + +Every `##` and `###` and `####` heading in the body must have a corresponding ToC entry. GitHub anchor rules: + +- Lowercase +- Replace spaces with `-` +- Drop everything except `a-z 0-9 - _` +- Escape `&` as `\&` (escaped only in the ToC link text, not in the anchor itself) + +Deleted headings → delete the ToC entry. New headings → add an entry at the correct nesting depth. + +#### q. Stale-reference grep (MUST run before finishing) + +For every file or feature removed this session, grep the entire README. Examples: + +```bash +grep -n "render\|render\.yaml\|gcp-cloudrun" README.md # Render removed +grep -n "pre-commit-run" README.md # renamed to pc-run +grep -n "174 tests\|132 tests\|252 tests" README.md # old test counts +grep -n "V=1" README.md # old `make version V=x.y.z` syntax +grep -n "webcrumbs" README.md # CSS wrapper removed +``` + +Any hit → fix it. + +--- + +### 3.2 — `CLAUDE.md` + +CLAUDE.md is the rulebook for future AI agents. Check if the staged diff introduces: + +- A **new convention** (e.g. "always use `transaction.atomic()` for multi-model writes") — add to "Django Best Practices" or the relevant section +- A **new gotcha** (something non-obvious that tripped someone) — add to "Gotchas" +- A **renamed command, file, or symbol** — update the reference (e.g. `pre-commit-run` → `pc-run`, `make version V=x` → `make version x`) +- A **new tool or service** — add to "How to run" or "Developer tooling" +- A **new app** (unlikely) — add to "4 Django apps" + +The README-maintenance checklist inside CLAUDE.md (section "README maintenance") must stay in sync with the detailed Step 3.1 above. If you add/remove a step in the skill, update CLAUDE.md too. + +--- + +### 3.3 — `DEPLOYMENT.md` + +Check when the staged diff touches infrastructure, Docker, settings, secrets, or anything that would change a production deploy. + +- **Architecture diagram** — services still accurate? +- **Environments at a Glance** table — Development / Testing / Production all still correct? +- **Prerequisites** — any new required tool? +- **Initial Setup commands** — new service to enable, new secret to create? +- **Build & Deploy** — env-var list on the `gcloud run deploy` command must match every `os.getenv(...)` in `settings.py`. Missing env vars cause production bugs. +- **Celery strategy** — if Celery tasks were added/removed/renamed, update the Cloud Scheduler + Cloud Run Jobs sections +- **Custom domain** — only update if the domain mapping command changed +- **Cost estimates** — if hosting assumptions changed (e.g. `db-f1-micro` → `db-custom-1-3840`), recalculate +- **Optional Services for Future Evolution** — move an entry out of this section if it's now implemented + +--- + +### 3.4 — `docs/GITHUB.md` + +This is the CI/CD reference. README has the overview; GITHUB.md has the full walkthrough. They must not contradict each other. + +- **Pipeline Overview diagram** — matches README's +- **Every workflow** has its own subsection with: what it does, when it triggers, jobs, any quirks +- **Branch Protection** subsections (main, testing): every rule and required status check listed +- **Public Repository Hardening** — table matches README's +- **Required GitHub Secrets** — every `${{ secrets.X }}` in any workflow appears here with: required-by workflow, purpose, creation instructions where non-obvious (e.g. `GH_PAT` fine-grained token scopes) +- **Email Notifications** table — matches README's +- **Dependabot + CodeQL** sections — accurate + +--- + +### 3.5 — `docs/HTTPS.md`, `docs/UV.md`, `docs/CELERY.md` + +Update these only when the corresponding tool or process changed. These are narrow, focused docs. + +- `HTTPS.md`: if the Nginx config or the Cloud Run TLS setup changed +- `UV.md`: if UV workflow commands, lock behaviour, or dependency groups changed +- `CELERY.md`: if worker/beat setup, task queues, or Redis configuration changed + +--- + +### 3.6 — `docs/TODO.md` + +If the staged work **completes** a TODO item, remove it from this file. If it creates new follow-up work, add an entry with context. + +--- + +### 3.7 — Per-app READMEs (`project//README.md`) + +Each app README has the same structure. When the app's source changes, keep these in sync: + +| Section | Source of truth | How to verify | +|---------|----------------|---------------| +| **Models table** | `project//models.py` | Every model class must have a row with table name and purpose | +| **Views table** (core only) | `project/core/views/*.py` | One row per view module, listing every view function | +| **Service Layer** (billing only) | `project/billing/services/*.py` | Every service class + every public method must be documented | +| **Forms** | `project//forms.py` | Every ModelForm/Form subclass listed | +| **Admin** | `project//admin.py` | Every `register`ed model with custom admin behaviour | +| **URLs** | `project//urls.py` | URL pattern count ("12 URL patterns"); mention each route group | +| **Management commands** | `project//management/commands/*.py` | Every file listed with a one-line description | +| **Celery tasks** (comms only) | `project/comms/tasks.py` | Every `@shared_task` listed with a one-line description | + +If a new model/view/service/form/command/task was added in the staged diff, its app README must mention it. + +--- + +### 3.8 — `project/ENROLLMENT_PAYMENT_SYSTEM.md` + +This is the **authoritative spec** for pricing and billing rules. If the staged diff touches: + +- `project/billing/constants.py` (pricing seeds) +- `project/billing/services/pricing_service.py`, `enrollment_service.py`, or `payment_service.py` +- `SiteConfiguration` model fields +- The enrollment flow or discount logic + +then this file must be re-checked. The prices, discounts, and rules here must match the code. + +--- + +### 3.9 — The `.env template` code block in `README.md` + +`.env.example` **does not exist in this repo**. Instead, the README's Development & Docker section contains a `.env template` heading followed by a fenced `bash` code block that documents every variable the app reads. Contributors copy this block into a new `.env` file and fill in the blanks. + +This block is the authoritative env-var spec for local dev. The skill keeps it healthy. + +**Required checks:** + +1. **Code block exists and is findable** — the README must contain a heading literally `### .env template` followed by a fenced \`\`\`bash block. Verify with: + + ```bash + grep -n "^### \.env template" README.md + ``` + + If missing, flag it immediately — the Quick Start links to `#env-template` and `make setup` tells users to find this block. + +2. **Every var has an empty or safe value — no real secrets.** When the user asks you to refresh this block from `.env`, you may read `.env` to extract variable names and section-comment structure, but: + + - Every value slot must be **empty** (`KEY=`) **or a safe default** that's already public information. + - **Safe defaults** (OK to ship): `localhost`, `127.0.0.1`, `0.0.0.0`, `db`, `redis://redis:6379/0`, `fiveaday_db`, `fiveaday_user`, `5432`, `8000`, `True`/`False`, `development`/`production`/`testing`, `INFO`/`DEBUG`, `postgres`, `fiveaday` (the well-known legacy default username), `http://localhost:8000/auth/google/callback/`. + - **Never acceptable** — anything that looks like a real secret and must be stripped before writing: + - Random 50-char Django secret keys (anything with high-entropy symbols) + - `ghp_` / `ghs_` / `gho_` / `github_pat_` prefixes (GitHub tokens) + - `GOCSPX-` prefix (Google OAuth client secrets) + - 72-char base64 strings (Django's `get_random_secret_key()` output, `openssl rand -base64` output) + - 16-char lowercase-letter groups like `krqg zqeq kcxc onub` (Gmail App Passwords) + - Real email addresses of the maintainer or users (the maintainer knows theirs — the README should say `your-academy@gmail.com` or leave blank) + - Real IBANs, phone numbers, business details + - Real Google OAuth client IDs (start with digits then `-...-apps.googleusercontent.com`) + + If you see any of these when reading `.env`, **strip them** before writing to the README. Flag to the user that their `.env` contained secrets you saw. + +3. **Coverage matches the code** — every env var the app actually reads must appear in the template. Compare: + + ```bash + # Every env var referenced in Python code (most authoritative) + grep -rhoE 'os\.getenv\("[A-Z_]+"' project/ | sort -u | sed 's/os\.getenv("//' + + # Every var in the README template + grep -E '^[A-Z_]+=' README.md | cut -d= -f1 | sort -u + ``` + + The set difference tells you what's missing (code but not template) or stale (template but no longer read). Both sides should be addressed — missing → add a line under the right section, stale → remove from the template. + +4. **Coverage matches the Env Variables Reference table** — every row in the README's **Environment Variables Reference** table (the `| Variable | Description |...` table) should also appear in the template code block, unless it is explicitly marked "advanced override" (vars like `SESSION_COOKIE_AGE`, `APP_VERSION`, `GOOGLE_ALLOWED_EMAIL` can be left out of the template as long as they're in the reference table). + +5. **No duplicate keys** in the template — inside the fenced block: + + ```bash + awk '/^### \.env template/{flag=1; next} /^```bash/{capture=flag; next} /^```/{capture=0; flag=0} capture && /^[A-Z_]+=/ {sub(/=.*/,""); print}' README.md | sort | uniq -d + ``` + + must be empty. + +6. **Grouping and comments preserved** — the template uses `# ====...====` banner dividers before each section (DJANGO SETTINGS, LOGGING, DATABASE CONFIGURATION, SUPERUSER, EMAIL CONFIGURATION, CELERY / REDIS, AUTHENTICATION, GOOGLE OAUTH, ACADEMY BUSINESS INFO). A new var must be placed in the right section — never appended at the bottom. + +7. **`.gitignore` is correct** — should have `.env*` with **no** exceptions (no `!.env.example` line). Verify: + + ```bash + grep -n '\.env' .gitignore + ``` + + If `!.env.example` or `!.env.testing.example` or any other `!.env*` exception exists, remove it. + +8. **No stale `.env.example` references anywhere** — grep the full docs tree and Makefile: + + ```bash + grep -rn '\.env\.example\|\.env\.testing\.example' README.md CLAUDE.md DEPLOYMENT.md docs/ Makefile project/ + ``` + + Any hit → remove or replace with a pointer to the README template. + +9. **Legacy / deprecated vars** — if `.env` contains a var that the app no longer reads (e.g. `VERSION=0.30.2` from an old release), do not add it to the template. Flag it to the user so they can clean their real `.env`. + +10. **ToC entry** — the ToC must include `- [.env template](#env-template)` under Development & Docker. + +--- + +## Step 4 — Cross-file consistency sweep + +Certain facts appear in multiple documents. When they change, update **every** occurrence: + +| Fact | Files that reference it | +|------|-------------------------| +| **Current version (`x.y.z`)** | Main README header badge, Recent Versions top row, Version History latest `
`, `pyproject.toml`, `project/project/settings.py` APP_VERSION default | +| **Test count (`N tests`)** | Main README header (Project Status area if mentioned), Testing section top, Directory Layout `tests/` annotation, per-app READMEs if they cite a count | +| **Coverage %** | Main README Codecov badge, Testing Overview table, Directory Layout | +| **Make command count** | Main README Directory Layout (`60+ commands`), help text | +| **Python version (`3.12+`)** | README badge, `pyproject.toml` `requires-python`, Dockerfile `FROM python:3.12-slim`, `[tool.ruff] target-version = "py312"` | +| **Django version (`5.2`)** | README badge, `pyproject.toml` dependency | +| **PostgreSQL version (`16`)** | README badge, `docker-compose.yml`, `DEPLOYMENT.md` Cloud SQL create command, CI workflow service image | +| **GCP region (`europe-southwest1`)** | Project Status table, DEPLOYMENT.md (every `gcloud` example) | +| **Owner/repo (`starseeker-code/five-a-day`)** | README badges (CI, Codecov), GITHUB.md examples | +| **Env var inventory** | `settings.py` (`os.getenv(...)`), README Env Variables Reference table, `.env.example`, DEPLOYMENT.md Secret Manager list. Every name that appears in one must appear in every other (modulo internal-only vars that never ship to production). | + +If a change appears in one place, **check the others**. A version-bump commit that only updates the badge but not Version History is a broken commit. + +--- + +## Step 5 — Global stale-reference sweep + +Before reporting completion, grep the entire `docs/` tree, main README, all per-app READMEs, CLAUDE.md, and DEPLOYMENT.md for: + +- File names of any file deleted this session (e.g. `render.yaml`, old workflow files) +- Old command names (if renamed) +- Old env var names (if renamed) +- Legacy service names (e.g. "Render", "Heroku") unless they are historical in Version History +- Deprecated URL patterns +- Removed models + +Commands: + +```bash +grep -rn "" README.md CLAUDE.md DEPLOYMENT.md docs/ project/*/README.md project/ENROLLMENT_PAYMENT_SYSTEM.md +``` + +Any hit outside `.venv/` → fix. + +--- + +## Step 6 — Report back + +After saving all docs, report in under 25 lines: + +- **Files updated** — list each file with a one-sentence summary of what changed +- **Versions moved** — which versions entered/exited the Recent Versions table +- **Inconsistencies resolved** — e.g. "Main README badge said 1.0.4, pyproject said 1.0.5 — aligned to 1.0.5" +- **Cross-file syncs** — e.g. "Updated test count in main README, core/README.md, and CLAUDE.md" +- **Stale references removed** — e.g. "Removed 4 remaining `render.yaml` mentions in Security and ToC" +- **Anything you noticed but did NOT change** — sections that look stale but weren't in scope (surface these for the user to decide) +- **Files NOT touched and why** — if only `docs/CELERY.md` was in scope for the staged diff, say so explicitly + +--- + +## Guarantees + +- **Never commit.** The user may want to amend, combine, or review before committing. +- **Never stage files** yourself without user confirmation. +- **Never read `.env*` files** — they contain secrets. +- **Never invent work** — if the staged diff is purely a bug fix with no documentation implications, say "no doc changes needed" and stop. +- **Never fabricate counts** — run the grep/wc command to get the real test count, view count, etc. +- **Flag version mismatches** between `pyproject.toml`, `settings.py`, and the most recent commit message — the user likely forgot `make version`. +- **Always grep** for stale references before declaring victory. The routing table misses things; grep catches them. + +## When the staged diff is empty + +If `git diff --cached` is empty, either: +1. The user forgot to stage anything → ask +2. They want you to review the working tree instead → ask for confirmation and then `git diff` (unstaged) is the source of truth + +Do not guess which case it is. + +## When the staged diff spans multiple versions + +If the staged changes represent more than one logical version (e.g. a CI/CD refactor + a new feature + a bug fix), flag this to the user: they probably want to split the commit. Do not try to document them as a single version. diff --git a/.env.testing.example b/.env.testing.example deleted file mode 100644 index 882e1bc..0000000 --- a/.env.testing.example +++ /dev/null @@ -1,92 +0,0 @@ -# ============================================================================ -# FIVE A DAY — TESTING / QA ENVIRONMENT -# ============================================================================ -# Copy this file to .env.testing and fill in the secrets: -# cp .env.testing.example .env.testing -# -# Then start the QA environment: -# make testing-up -# make testing-seed - -# ============================================================================ -# DJANGO SETTINGS -# ============================================================================ -VERSION=1.0.1t -DJANGO_ENV=testing -DJANGO_DEBUG=False -DJANGO_SECRET_KEY='CHANGE_ME_generate_with_python_get_random_secret_key' -DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1,0.0.0.0,.run.app - -# ============================================================================ -# HTTPS & SECURITY -# ============================================================================ -SECURE_SSL_REDIRECT=False -SESSION_COOKIE_SECURE=True -CSRF_COOKIE_SECURE=True -SESSION_COOKIE_SAMESITE=Strict -CSRF_COOKIE_HTTPONLY=True -CSRF_COOKIE_SAMESITE=Strict -# CSRF_TRUSTED_ORIGINS=https://your-testing-domain.run.app - -# ============================================================================ -# LOGGING -# ============================================================================ -LOG_LEVEL=INFO -DJANGO_LOG_LEVEL=INFO - -# ============================================================================ -# DATABASE -# ============================================================================ -DATABASE=postgres -POSTGRES_DB=fiveaday_testing -POSTGRES_USER=fiveaday_tester -POSTGRES_PASSWORD='CHANGE_ME' -POSTGRES_HOST=db -POSTGRES_PORT=5432 - -# ============================================================================ -# QA LOGIN CREDENTIALS -# ============================================================================ -LOGIN_USERNAME=manitas -LOGIN_PASSWORD=CHANGE_ME - -# ============================================================================ -# QA TESTING TOOLS ACCESS -# ============================================================================ -# Only this username can see and use the /testing/ dashboard. -QA_TESTING_USERNAME=manitas - -# ============================================================================ -# SUPERUSER (Django Admin) -# ============================================================================ -DJANGO_SUPERUSER_USERNAME=qa_admin -DJANGO_SUPERUSER_EMAIL=qa@fiveaday.example.com -DJANGO_SUPERUSER_PASSWORD='CHANGE_ME' - -# ============================================================================ -# EMAIL -# ============================================================================ -EMAIL_HOST_USER=your-email@gmail.com -EMAIL_SECRET='your-gmail-app-password' -SUPPORT_EMAIL=your-support@email.com - -# ============================================================================ -# CELERY / REDIS (not available — tasks run synchronously) -# ============================================================================ -#CELERY_BROKER_URL=redis://redis:6379/0 -#CELERY_RESULT_BACKEND=redis://redis:6379/0 - -# ============================================================================ -# GOOGLE OAUTH (optional) -# ============================================================================ -#GOOGLE_CLIENT_ID= -#GOOGLE_CLIENT_SECRET= -#GOOGLE_REDIRECT_URI= - -# ============================================================================ -# ACADEMY BUSINESS INFO -# ============================================================================ -ACADEMY_IBAN=ES00 0000 0000 0000 0000 0000 -ACADEMY_IBAN_HOLDER=Nombre del titular -ACADEMY_PHONE=600 000 000 -ACADEMY_WHATSAPP=600000000 diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml index e68195b..64d9453 100644 --- a/.github/workflows/auto-merge.yml +++ b/.github/workflows/auto-merge.yml @@ -54,12 +54,54 @@ jobs: exit 0 fi + # 3. Has the version been bumped on development? + # Source of truth is pyproject.toml — it's what `make version` and `make pc-run` write. + # A version bump is REQUIRED: no version change → no merge, even with 24 h of new commits. + extract_version() { + # $1 = ref (e.g. origin/testing or origin/development) + git show "$1:pyproject.toml" 2>/dev/null \ + | grep -E '^[[:space:]]*version[[:space:]]*=' \ + | head -1 \ + | sed -E 's/.*version[[:space:]]*=[[:space:]]*"([^"]+)".*/\1/' + } + + TESTING_VERSION=$(extract_version origin/testing) + DEV_VERSION=$(extract_version origin/development) + + if [ -z "$TESTING_VERSION" ] || [ -z "$DEV_VERSION" ]; then + echo "Could not read version from pyproject.toml on one of the branches (testing='$TESTING_VERSION', development='$DEV_VERSION') — skipping." + echo "should_merge=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + echo "testing version: $TESTING_VERSION" + echo "development version: $DEV_VERSION" + + if [ "$DEV_VERSION" = "$TESTING_VERSION" ]; then + echo "Version unchanged ($DEV_VERSION) — development has new commits but no version bump. Skipping merge." + echo "Run 'make pc-run' (answer yes) or 'make version x.y.z' on development before the next cron tick to unlock the merge." + echo "should_merge=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + # Semver sort (GNU sort -V handles both "1.0.5" and "v1.0.5"). The highest value MUST be DEV_VERSION. + HIGHEST=$(printf '%s\n%s\n' "$TESTING_VERSION" "$DEV_VERSION" | sort -V | tail -1) + if [ "$HIGHEST" != "$DEV_VERSION" ]; then + echo "development version ($DEV_VERSION) is LOWER than testing ($TESTING_VERSION) — this should not happen. Refusing to merge backwards." + echo "should_merge=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + echo "Version bump confirmed: $TESTING_VERSION → $DEV_VERSION" + LAST_SHA=$(git rev-parse origin/development) LAST_MSG=$(git log origin/development -1 --format="%s") - echo "should_merge=true" >> "$GITHUB_OUTPUT" - echo "last_sha=$LAST_SHA" >> "$GITHUB_OUTPUT" - echo "last_msg=$LAST_MSG" >> "$GITHUB_OUTPUT" + echo "should_merge=true" >> "$GITHUB_OUTPUT" + echo "last_sha=$LAST_SHA" >> "$GITHUB_OUTPUT" + echo "last_msg=$LAST_MSG" >> "$GITHUB_OUTPUT" + echo "new_version=$DEV_VERSION" >> "$GITHUB_OUTPUT" + echo "old_version=$TESTING_VERSION" >> "$GITHUB_OUTPUT" - name: Check CI status on development id: ci @@ -124,6 +166,30 @@ jobs: echo "merge_msg=$MERGE_MSG" >> "$GITHUB_OUTPUT" echo "Merged with message: $MERGE_MSG" + - name: Tag the testing merge commit with the staging tag + id: tag + if: steps.merge.conclusion == 'success' + run: | + NEW_VER="${{ steps.conditions.outputs.new_version }}" + TAG="testing-v${NEW_VER}" + + # Defensive — the version-progression check should prevent collisions, + # but if the tag exists already, skip rather than clobber. + if git ls-remote --tags origin "refs/tags/$TAG" | grep -q "refs/tags/$TAG"; then + echo "Tag $TAG already exists on origin — skipping tag creation." + echo "created=false" >> "$GITHUB_OUTPUT" + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + exit 0 + fi + + # Annotated tag — richer metadata than a lightweight tag, shows up in the Tags tab. + git tag -a "$TAG" -m "Staging $TAG — auto-merged development → testing. The matching release tag v${NEW_VER} will be created on main when the PR lands." + git push origin "refs/tags/$TAG" + + echo "Created and pushed staging tag $TAG on testing HEAD." + echo "created=true" >> "$GITHUB_OUTPUT" + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + - name: Create PR testing → main id: pr if: steps.merge.conclusion == 'success' @@ -157,6 +223,12 @@ jobs: '', 'This PR was created automatically after `development` was merged into `testing`.', '', + `**Version:** \`${{ steps.conditions.outputs.old_version }}\` → \`${{ steps.conditions.outputs.new_version }}\``, + `**Staging tag (on testing):** \`${{ steps.tag.outputs.tag }}\``, + `**Release tag (on main, created after this PR merges):** \`v${{ steps.conditions.outputs.new_version }}\``, + '', + 'Merge strategy is flexible — staging and release tags are independent, so squash/rebase/merge-commit all work.', + '', '### Pre-merge checklist', '- [ ] All CI checks passing on this PR', '- [ ] Manual review completed', @@ -189,6 +261,9 @@ jobs:

A new merge has landed on the testing branch and a pull request to main is now awaiting review.

+ + + diff --git a/.github/workflows/notify-production.yml b/.github/workflows/notify-production.yml index 8a687e9..47a9a7e 100644 --- a/.github/workflows/notify-production.yml +++ b/.github/workflows/notify-production.yml @@ -8,7 +8,7 @@ on: branches: [main] permissions: - contents: read + contents: write # needs write for pushing the release tag jobs: notify: @@ -19,6 +19,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 2 # Need HEAD and HEAD~1 for diff summary + token: ${{ secrets.GH_PAT || github.token }} - name: Gather commit info id: info @@ -36,6 +37,42 @@ jobs: echo "files_changed=$FILES_CHANGED" } >> "$GITHUB_OUTPUT" + - name: Create release tag on main + id: tag + run: | + # Read the authoritative version from pyproject.toml on the just-pushed commit + VERSION=$(grep -E '^[[:space:]]*version[[:space:]]*=' pyproject.toml \ + | head -1 \ + | sed -E 's/.*version[[:space:]]*=[[:space:]]*"([^"]+)".*/\1/') + + if [ -z "$VERSION" ]; then + echo "Could not read version from pyproject.toml — skipping release tag." + echo "created=false" >> "$GITHUB_OUTPUT" + echo "tag=(skipped)" >> "$GITHUB_OUTPUT" + exit 0 + fi + + TAG="v${VERSION}" + + # Skip if the release tag already exists (e.g. a main push that doesn't bump version, + # like a direct docs fix or a re-deploy of the same version). + if git ls-remote --tags origin "refs/tags/$TAG" | grep -q "refs/tags/$TAG"; then + echo "Release tag $TAG already exists — skipping." + echo "created=false" >> "$GITHUB_OUTPUT" + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + exit 0 + fi + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + git tag -a "$TAG" -m "Release $TAG (production) — merged from testing" + git push origin "refs/tags/$TAG" + + echo "Created and pushed release tag $TAG on main HEAD." + echo "created=true" >> "$GITHUB_OUTPUT" + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + - name: Send email uses: dawidd6/action-send-mail@v3 with: @@ -52,6 +89,7 @@ jobs:

A new commit has landed on the main branch. Production deploy is ready when you are.

Version bump${{ steps.conditions.outputs.old_version }}${{ steps.conditions.outputs.new_version }}
Staging tag (on testing)${{ steps.tag.outputs.tag }} (${{ steps.tag.outputs.created == 'true' && 'newly created' || 'pre-existing' }})
Release tag (pending)v${{ steps.conditions.outputs.new_version }} — will be created on main after the PR merges
Merge commit${{ steps.merge.outputs.merge_msg }}
Repository${{ github.repository }}
PR status${{ steps.pr.outputs.already_existed == 'true' && 'Updated (PR already existed)' || 'Newly created' }}
+ diff --git a/.gitignore b/.gitignore index 4f24347..5842120 100644 --- a/.gitignore +++ b/.gitignore @@ -212,13 +212,10 @@ __marimo__/ # ============================================================================ # DOCKER & ENVIRONMENT # ============================================================================ -# Variables de entorno (¡NUNCA subir .env con contraseñas reales!) +# Variables de entorno (¡NUNCA subir ningún .env!) +# No hay excepciones: la plantilla de .env vive en README.md (sección ".env template") .env* -# Mantener el ejemplo -!.env.example -!.env.testing.example - # Volúmenes de Docker postgres_data/ redis_data/ diff --git a/CLAUDE.md b/CLAUDE.md index 93ebee3..8114f8a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -53,8 +53,8 @@ make test # Run tests (PostgreSQL) ```bash make lint # Ruff linter make format # Ruff formatter -make pre-commit-run # Run all pre-commit hooks -make test # Run 294 tests (PostgreSQL via Docker, parallel, 70% coverage) +make pc-run # Run all pre-commit hooks (dry run + auto version bump) +make test # Run 283 tests (PostgreSQL via Docker, parallel, with coverage) ``` - **UV** for dependency management (see [docs/UV.md](docs/UV.md)) @@ -126,7 +126,25 @@ All pricing flows through `billing/services/`. The single source of truth is `Si - **Celery eager mode** — without Redis, tasks run synchronously. Don't rely on task.delay() being truly async in development. - **`#webcrumbs` removed** — the old CSS scoping wrapper is gone. If you see references to it in old code, delete them. - **Template names are English** — all email templates were renamed from Spanish (e.g., `matricula_niño.html` → `enrollment_child.html`). Never create templates with Spanish names. -- **Version in two places** — `pyproject.toml` and `settings.py`. Use `make version V=x.y.z` to update both. +- **Version in two places** — `pyproject.toml` and `project/project/settings.py`. Use `make version x.y.z` (positional, with y/N confirmation) to update both. +- **APP_VERSION in `.env`** — the local `.env` may contain a legacy `APP_VERSION=0.x.y` line. Either remove the line or update it — it silently overrides the default in `settings.py` at runtime. +- **`pc-run` renamed** — the old `make pre-commit-run` target is now `make pc-run`. It also auto-bumps the patch version on a clean pass (y/N prompt) and auto-stages `uv.lock` if regenerated. + +## README maintenance (MUST do at end of every work session) + +The `README.md` must stay in sync with the code. At the end of any non-trivial change, verify and update these sections before handing off: + +1. **Header badges** — version badge must match `pyproject.toml` +2. **Project Status table** — three rows in order Production → Testing → Development, each with branch + hosting + CI badge +3. **Recent Versions table** — keep only the **last 3** versions. Entries must include: version, date (YYYY-MM-DD), a dense description mentioning every user-visible change in that version. When a new patch ships, drop the oldest row. +4. **Version History `
` blocks** — add a new `
` block for the new version; remove the `open` attribute from the previous one. Structure: `**Subsection**` headings + bullet lists. Pull subjects from `git log` for the commits in that version. +5. **Directory Layout** — if directories, tool counts, test counts, or Make command counts changed, update them here. `tests/` line must show current test count and coverage percentage. +6. **Make Commands table** — every renamed or new `make` target must appear or be updated. +7. **Contributing → Development Workflow** — if the developer flow changed (new pre-commit behavior, new commands), update the numbered list. +8. **Table of Contents** — every new section or renamed heading must have a matching ToC entry with a valid anchor (GitHub generates anchors by lowercasing, replacing spaces with `-`, dropping non-alphanumerics except `-`). +9. **Delete stale content** — if a file or service was removed (e.g. `render.yaml`, a retired workflow), remove every reference to it. Grep the README for the name first. + +**When the user invokes the `update-readme` skill**, use the staged changes (`git diff --cached`, `git status --porcelain`, `git diff --cached --stat`) to determine what changed, then apply the checklist above. Do not guess — inspect the staged diff first. ## Django Best Practices (enforced in this project) diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index a30c8c0..607676b 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -96,7 +96,8 @@ Verify with `free -h` — you should see ~2 GB in the Swap row. ```bash git clone https://github.com/YOUR_ORG/five-a-day.git cd five-a-day -cp .env.example .env.testing # Edit with testing values +# Create .env.testing and populate it using the template in README.md (section ".env template") +touch .env.testing docker compose --env-file .env.testing up -d ``` diff --git a/Makefile b/Makefile index d1566f5..e9581bf 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ help: @echo " ==========================" @echo "" @echo " Setup & Build:" - @echo " make setup Copy .env.example to .env" + @echo " make setup Create empty .env (fill in from README.md '.env template')" @echo " make build Build Docker images" @echo " make rebuild Full rebuild (no cache) + start" @echo " make rebuild-web Rebuild only the web image" @@ -120,8 +120,8 @@ help: # ============================================================================ setup: @if [ ! -f .env ]; then \ - cp .env.example .env; \ - echo "Created .env — edit it with your configuration."; \ + touch .env; \ + echo "Created empty .env. Copy the template from README.md (section '.env template') into .env and fill in the blanks."; \ else \ echo ".env already exists."; \ fi @@ -488,6 +488,10 @@ pc-run: echo "Updated version $$CURRENT with new version $$NEW"; \ fi; \ fi + @if [ -n "$$(git status --porcelain uv.lock 2>/dev/null)" ]; then \ + git add uv.lock; \ + echo "Staged updated uv.lock — next git commit will not be blocked by it"; \ + fi # ============================================================================ # PRODUCTION diff --git a/readme.md b/README.md similarity index 81% rename from readme.md rename to README.md index 0856185..dd398de 100644 --- a/readme.md +++ b/README.md @@ -9,11 +9,12 @@

- Version + Version Python Django PostgreSQL - Coverage + CI + Coverage

--- @@ -29,26 +30,19 @@ Built to centralize student records, automate billing cycles, and streamline par ### Project Status -| Environment | Version | Status | -|-------------|---------|--------| -| **Production** | v1.0.4 | ![ready](https://img.shields.io/badge/ready_to_deploy-blue) | -| **Testing (QA)** | v1.0.4 | ![ready](https://img.shields.io/badge/ready_to_deploy-blue) | -| **Development** | v1.0.4 | ![active](https://img.shields.io/badge/active-brightgreen) | +Live status for each environment is pulled from GitHub Actions — the badges below reflect the real state of CI on each branch. -| | | -|---|---| -| **Documentation** | This README, [DEPLOYMENT.md](DEPLOYMENT.md), [GITHUB.md](docs/GITHUB.md), [HTTPS.md](docs/HTTPS.md), [UV.md](docs/UV.md), [CELERY.md](docs/CELERY.md), per-app READMEs, [CLAUDE.md](CLAUDE.md) | +| Environment | Branch | Hosting | CI Status | +|-------------|--------|---------|-----------| +| **Production** | `main` | GCP Cloud Run + Cloud SQL (PostgreSQL 16), `europe-southwest1` | [![Production CI](https://github.com/starseeker-code/five-a-day/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/starseeker-code/five-a-day/actions/workflows/ci.yml?query=branch%3Amain) | +| **Testing (QA)** | `testing` | GCP Compute Engine e2-micro (free tier), Docker Compose | [![Testing CI](https://github.com/starseeker-code/five-a-day/actions/workflows/ci.yml/badge.svg?branch=testing)](https://github.com/starseeker-code/five-a-day/actions/workflows/ci.yml?query=branch%3Atesting) | +| **Development** | `development` | Local machine via `make up` (Docker Compose) | [![Development CI](https://github.com/starseeker-code/five-a-day/actions/workflows/ci.yml/badge.svg?branch=development)](https://github.com/starseeker-code/five-a-day/actions/workflows/ci.yml?query=branch%3Adevelopment) | | Version | Date | Description | |---------|------|-------------| -| **v1.0.4** | 2026-04-15 | GitHub Actions CI/CD pipeline (lint, typecheck, tests, CodeQL, Dependabot), auto-merge `development` → `testing` with 24h delay + auto-PR to `main`, email notifications, branch protection rules, inspirational quote generator, GCP deployment plan, cleaned legacy Render config, `make version x.y.z` + `make pc-run` with auto version bump | -| v1.0.3 | 2026-04-14 | Test coverage raised to 70% — 294 tests total (40+ new tests) across auth views, comms services, dashboard, schedule, management, and apps modules | -| v1.0.2 | 2026-04-13 | Replaced Poetry with UV, full developer tooling (Ruff, mypy, bandit, pip-audit, pre-commit, pytest-cov, pytest-xdist, pytest-randomly) | -| v1.0.1t | 2026-04-14 | QA/testing environment: `/testing/` dashboard, database seeding, backlog with email, error reporting, HTTPS guide, access control via `QA_TESTING_USERNAME` | -| v1.0.0 | 2026-04-11 | Security hardening, query optimization (Case/When aggregates, N+1 fixes), GCP config, transaction safety | -| v1.0.0 | 2026-04-10 | Multi-app architecture, service layer, 132 tests, frontend cleanup, full documentation | -| v0.30.2 | 2025-03-14 | History system, GDPR for adults, Docker Compose workflow | -| v0.29.0 | 2025-03-01 | Enrollment system with discounts, adult students, email automation | +| **v1.0.5** | 2026-04-15 | GitHub Actions CI/CD pipeline (lint, typecheck, tests, CodeQL, Dependabot), auto-merge `development` → `testing` with 24 h delay + auto-PR to `main`, email notifications, branch protection rules for public repo hardening, `make pc-run` auto-stages regenerated `uv.lock` | +| v1.0.4 | 2026-04-15 | Inspirational quote generator on `/home` (48 h cookie rotation), GCP deployment plan ([DEPLOYMENT.md](DEPLOYMENT.md)), Celery worker + beat containers, cleaned legacy Render config, `make version x.y.z` positional arg with confirmation guard, `make pc-run` (renamed from `pre-commit-run`) with auto version bump | +| v1.0.3 | 2026-04-14 | Test coverage raised to **70 %** — 13 new test files across auth views, comms services, app forms, constants, create payment views, exports, forms, parent views, payment views, schedule views, student forms, student views, transactions | --- @@ -81,6 +75,7 @@ Built to centralize student records, automate billing cycles, and streamline par - [Key Constraints](#key-constraints) - [Development \& Docker](#development--docker) - [Quick Start](#quick-start) + - [.env template](#env-template) - [Make Commands](#make-commands) - [Environment Configuration](#environment-configuration) - [Environment Variables Reference](#environment-variables-reference) @@ -120,8 +115,7 @@ Built to centralize student records, automate billing cycles, and streamline par - [Security Headers](#security-headers) - [Infrastructure \& Deployment](#infrastructure--deployment-1) - [Docker](#docker) - - [Render (render.yaml)](#render-renderyaml) - - [Google Cloud Run (gcp-cloudrun.yaml)](#google-cloud-run-gcp-cloudrunyaml) + - [Google Cloud Run](#google-cloud-run) - [Secrets Management](#secrets-management) - [Email Security](#email-security) - [Data Protection \& Input Validation](#data-protection--input-validation) @@ -135,7 +129,6 @@ Built to centralize student records, automate billing cycles, and streamline par - [Error pages you might see](#error-pages-you-might-see) - [For developers: how the QA environment works](#for-developers-how-the-qa-environment-works) - [Access control for /testing/](#access-control-for-testing) - - [GCP deployment plan](#gcp-deployment-plan) - [CI/CD \& GitHub Actions](#cicd--github-actions) - [Pipeline Overview](#pipeline-overview) - [Branch Strategy](#branch-strategy) @@ -158,39 +151,55 @@ Built to centralize student records, automate billing cycles, and streamline par ## Version History & Roadmap -
-v1.0.4 — CI/CD Pipeline, GCP Migration Plan, Quote Generator (current) +
+v1.0.5 — CI/CD Pipeline & Public Repo Hardening (current) **GitHub Actions CI/CD** (new — see [docs/GITHUB.md](docs/GITHUB.md)) -- `ci.yml` — three parallel jobs on every push/PR: Ruff + Bandit lint, mypy type check, pytest against PostgreSQL 16 service container with coverage uploaded to Codecov -- `auto-merge.yml` — hourly cron that merges `development` → `testing` after 24h of inactivity and CI passing, then auto-creates a PR `testing` → `main` +- `ci.yml` — three parallel jobs on every push/PR: Ruff + Bandit lint, mypy type check, pytest against a PostgreSQL 16 service container with coverage uploaded to Codecov +- `auto-merge.yml` — hourly cron that merges `development` → `testing` after 24 h of inactivity and CI passing, then auto-creates a PR `testing` → `main` - `codeql.yml` — weekly Python security analysis (OWASP Top 10, Django-specific queries) -- `notify-production.yml` — emails `hellofiveaday@gmail.com` on every push to `main` with commit info and deploy instructions -- Owner email notifications when `development` → `testing` merge lands + PR opened to `main` +- `notify-production.yml` — emails `hellofiveaday@gmail.com` on every push to `main` with commit info and `gcloud` deploy instructions +- Owner email notifications when `development` → `testing` merge lands and a PR is opened to `main` - `dependabot.yml` — grouped weekly Python and GitHub Actions updates targeting `development` - `CODEOWNERS` — auto-request reviews from both owner accounts +**Public-repo hardening** + +- Branch protection rules documented for `main` (14 protections) and `testing` (minimal) +- Secret scanning + push protection + CodeQL enabled (all free for public repos) +- Fork PR workflow restriction, read-only default workflow permissions, block-approvals-from-Actions +- `SECURITY.md` + `CODEOWNERS` + `LICENSE` required-file checklist in [docs/GITHUB.md](docs/GITHUB.md) + +**Developer tooling** + +- `make pc-run` auto-stages regenerated `uv.lock` as the final step — next `git commit` is no longer blocked by the lock file + +
+ +
+v1.0.4 — GCP Migration Plan, Quote Generator, Celery + **GCP migration plan** (new — see [DEPLOYMENT.md](DEPLOYMENT.md)) - Full Cloud Run + Cloud SQL architecture documented - Three environments: local Docker (dev), Compute Engine e2-micro free tier (testing), Cloud Run + Cloud SQL (production) - Cost estimate: ~$15-27/month for production, $0/month for testing - Celery replacement strategy using Cloud Scheduler + Cloud Run Jobs -- Cleaned legacy Render config (`render.yaml` removed, commented nginx and pgAdmin services removed from docker-compose) +- Cleaned legacy Render config — `render.yaml` removed; commented nginx and pgAdmin services removed from `docker-compose.yml` **Dashboard enhancement** -- Inspirational quote generator on `/home` — fetches two daily quotes from `zenquotes.io`, stores them in a 48h cookie, rotates daily (day 0 shows quote 1, day 1+ shows quote 2), graceful fallback to the default Spanish subtitle on API failure +- Inspirational quote generator on `/home` — fetches two daily quotes from `zenquotes.io`, stores them in a 48 h cookie, rotates daily (day 0 shows quote 1, day 1+ shows quote 2), graceful fallback to the default Spanish subtitle on API failure **Developer tooling** - `make version x.y.z` — positional argument (replaces `V=x.y.z`) with confirmation guard before writing - `make pc-run` — renamed from `pre-commit-run`; after a clean pass, prompts to auto-increment the patch version in `pyproject.toml` and `project/settings.py` -**Bug fixes** +**Celery** -- Celery worker and beat containers added to docker-compose with correct permissions and health checks +- Celery worker and beat containers added to `docker-compose.yml` with correct permissions and health checks - Several payment and enrollment issues fixed
@@ -200,15 +209,15 @@ Built to centralize student records, automate billing cycles, and streamline par **Testing** -- Test count raised from 252 to **294** (40+ new tests) +- 40+ new tests added across 13 new test files — overall suite around 280+ tests - Coverage raised to **70%** across `core`, `students`, `billing`, `comms` -- New test files: `test_auth_views.py`, `test_comms_services.py`, `test_dashboard.py`, `test_schedule_views.py`, `test_management_views.py`, `test_app_forms.py` +- New test files: `test_auth_views.py`, `test_app_form_views.py`, `test_constants.py`, `test_create_payment_views.py`, `test_exports.py`, `test_forms.py`, `test_parent_views.py`, `test_payment_views.py`, `test_schedule_views.py`, `test_student_forms.py`, `test_student_views.py`, `test_transactions.py` - Additional parametrized test cases for email-form views and error pages **Coverage tooling** -- `coverage.svg` badge now reflects the improved coverage -- `make coverage-badge` command streamlines badge regeneration +- Coverage badge pulled dynamically from Codecov (CI workflow uploads `coverage.xml` on every run) +- `make coverage-badge` retained for offline SVG generation
@@ -628,15 +637,17 @@ erDiagram git clone https://github.com/starseeker-code/five-a-day.git cd five-a-day -# Configure environment -cp .env.example .env # Edit with your values (see Environment Configuration below) +# Create the .env file — copy the template below into `.env` and fill in the blanks +touch .env ``` +Paste the template from [.env template](#env-template) into your new `.env` file and fill in the empty values. + **Docker (recommended):** ```bash make build # Build images -make up # Start PostgreSQL + Django → http://localhost:8000 +make up # Start PostgreSQL + Redis + Django + Celery → http://localhost:8000 make migrate # Apply migrations (first time only) ``` @@ -653,6 +664,87 @@ python manage.py runserver > - `DJANGO_ENV=development` — enables development behaviors (auto superuser, no collectstatic) > - `DJANGO_DEBUG=true` — enables Django debug mode, detailed error pages > - `POSTGRES_PASSWORD` — required for database connection +> - `DJANGO_SECRET_KEY` — generate with `python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'` + +### .env template + +`.env` is gitignored and never committed. The template below is the authoritative structure — copy it into your new `.env` file, then fill in the empty values with your own secrets. Defaults that are safe to keep as-is are already filled in. + +```bash +# ============================================================================ +# DJANGO SETTINGS +# ============================================================================ +DJANGO_ENV=development # production | development +DJANGO_DEBUG=True +SECURE_SSL_REDIRECT=False +# Generate with: python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())' +DJANGO_SECRET_KEY= +DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1,0.0.0.0 + +# ============================================================================ +# LOGGING +# ============================================================================ +LOG_LEVEL=INFO +DJANGO_LOG_LEVEL=INFO + +# ============================================================================ +# DATABASE CONFIGURATION +# ============================================================================ +DATABASE=postgres # sqlite | postgres +POSTGRES_DB=fiveaday_db +POSTGRES_USER=fiveaday_user +# Generate with: openssl rand -base64 32 +POSTGRES_PASSWORD= +POSTGRES_HOST=db # `db` in Docker, `localhost` outside +POSTGRES_PORT=5432 + +# ============================================================================ +# SUPERUSER (auto-created on first boot if all three are set) +# ============================================================================ +DJANGO_SUPERUSER_USERNAME= +DJANGO_SUPERUSER_EMAIL= +DJANGO_SUPERUSER_PASSWORD= + +# ============================================================================ +# EMAIL CONFIGURATION (Gmail SMTP + App Password) +# ============================================================================ +EMAIL_HOST_USER= # your-academy@gmail.com +EMAIL_SECRET= # 16-char Gmail App Password +SUPPORT_EMAIL= # where support tickets are sent +EMAIL_TEST_1= # dev test recipient 1 +EMAIL_TEST_2= # dev test recipient 2 + +# ============================================================================ +# CELERY / REDIS +# ============================================================================ +CELERY_BROKER_URL=redis://redis:6379/0 +CELERY_RESULT_BACKEND=redis://redis:6379/0 + +# ============================================================================ +# AUTHENTICATION (session-based, until the Django User model is adopted in v1.6) +# ============================================================================ +LOGIN_USERNAME=fiveaday +LOGIN_PASSWORD= + +# ============================================================================ +# GOOGLE OAUTH +# ============================================================================ +# Create at https://console.cloud.google.com/ → APIs & Services → Credentials +# Authorised redirect URI: http://localhost:8000/auth/google/callback/ +GOOGLE_CLIENT_ID= +GOOGLE_CLIENT_SECRET= +GOOGLE_REDIRECT_URI=http://localhost:8000/auth/google/callback/ + +# ============================================================================ +# ACADEMY BUSINESS INFO (prefilled in payment-reminder email forms) +# ============================================================================ +ACADEMY_IBAN= +ACADEMY_IBAN_HOLDER= +ACADEMY_PHONE= +ACADEMY_WHATSAPP= +``` + +**Note**: do not include `VERSION=` in your `.env` — it is deprecated. The app version is derived from `pyproject.toml` (and overridable via `APP_VERSION`). ### Make Commands @@ -685,8 +777,16 @@ Run `make` or `make help` for the full list. Key commands: | `make test-fast` | Stop on first failure | | `make test-k K=payment` | Run tests matching keyword | | **Versioning** | | -| `make version V=1.1.0` | Update version in pyproject.toml + settings.py | -| `make version` | Show current version locations | +| `make version 1.1.0` | Update version in `pyproject.toml` + `settings.py` (with y/N confirmation) | +| `make version` | Show current version | +| **Developer Tooling** | | +| `make lint` / `make lint-fix` | Run Ruff linter (optionally auto-fix) | +| `make format` / `make format-check` | Run Ruff formatter | +| `make mypy` | Run mypy type checker | +| `make bandit` | Run bandit security linter | +| `make audit` | `pip-audit` — scan deps for CVEs | +| `make pc-run` | Run pre-commit on all files; on clean pass, offer to auto-bump patch version; auto-stages regenerated `uv.lock` | +| `make pre-commit-install` | Install the git pre-commit hook | | **Email & Payments** | | | `make send-test-email` | Send test birthday email | | `make test-all-emails` | List all email templates | @@ -712,13 +812,16 @@ The database is **always PostgreSQL** — in Docker development, in tests, and i ### Environment Variables Reference +The table below describes every variable in the [.env template](#env-template) above, plus a few advanced overrides not included in the template. See the template for the full `.env` structure. + | Variable | Description | Required | Default | |----------|-------------|----------|---------| -| **Core** | | | | -| `DJANGO_ENV` | Environment: `development` / `production` | No | `development` | +| **Django core** | | | | +| `DJANGO_ENV` | Environment: `development` / `production` / `testing` | No | `development` | | `DJANGO_DEBUG` | Debug mode: `true` / `false` | No | `false` | | `DJANGO_SECRET_KEY` | Secret key | **Yes in production** | dev fallback | | `DJANGO_ALLOWED_HOSTS` | Comma-separated hosts | No | `localhost,127.0.0.1` | +| `SECURE_SSL_REDIRECT` | Force HTTPS redirects | No | `True` when `DEBUG=False` | | **Database** | | | | | `DATABASE` | Set to `postgres` for PostgreSQL | No | `postgres` | | `DATABASE_URL` | Full URL (Cloud deployments) | No | — | @@ -727,23 +830,36 @@ The database is **always PostgreSQL** — in Docker development, in tests, and i | `POSTGRES_PASSWORD` | Database password | **Yes** | — | | `POSTGRES_HOST` | Database host | No | `db` (Docker) | | `POSTGRES_PORT` | Database port | No | `5432` | +| **Superuser** (auto-created on first boot when all three are set) | | | | +| `DJANGO_SUPERUSER_USERNAME` | Superuser name | No | — | +| `DJANGO_SUPERUSER_EMAIL` | Superuser email | No | — | +| `DJANGO_SUPERUSER_PASSWORD` | Superuser password | No | — | | **Email** | | | | | `EMAIL_HOST_USER` | Gmail address | For email features | — | | `EMAIL_SECRET` | Gmail app password | For email features | — | | `SUPPORT_EMAIL` | Support ticket recipient | No | — | | `EMAIL_TEST_1` / `EMAIL_TEST_2` | Test email recipients | No | — | | **Auth** | | | | -| `LOGIN_USERNAME` | Admin username | No | `fiveaday` | -| `LOGIN_PASSWORD` | Admin password | No | `Fiveaday123!` | +| `LOGIN_USERNAME` | Admin username | **Yes** | — (login refused if missing) | +| `LOGIN_PASSWORD` | Admin password | **Yes** | — (login refused if missing) | +| `QA_TESTING_USERNAME` | Extra user allowed to see `/testing/` dashboard | No (QA only) | — | | `GOOGLE_CLIENT_ID` | OAuth client ID | For Google login | — | | `GOOGLE_CLIENT_SECRET` | OAuth client secret | For Google login | — | | `GOOGLE_REDIRECT_URI` | OAuth callback URL | For Google login | auto-detected | -| `GOOGLE_ALLOWED_EMAIL` | Restrict Google login | No | `EMAIL_HOST_USER` | -| **Other** | | | | -| `APP_VERSION` | Version string | No | from settings.py | -| `CELERY_BROKER_URL` | Redis URL for Celery | No | eager mode | -| `SESSION_COOKIE_AGE` | Session duration (seconds) | No | `86400` (24h) | -| `LOG_LEVEL` | Logging level | No | `DEBUG`/`INFO` | +| `GOOGLE_ALLOWED_EMAIL` | Restrict Google login to one email | No | `EMAIL_HOST_USER` | +| **Celery / Redis** | | | | +| `CELERY_BROKER_URL` | Redis URL for Celery | No | eager mode (tasks run inline) | +| `CELERY_RESULT_BACKEND` | Redis URL for results | No | same as broker | +| **Academy business info** (prefills payment-reminder email forms) | | | | +| `ACADEMY_IBAN` | Bank account for payment reminders | No | — | +| `ACADEMY_IBAN_HOLDER` | IBAN account holder | No | — | +| `ACADEMY_PHONE` | Phone for Bizum payments | No | — | +| `ACADEMY_WHATSAPP` | WhatsApp number for reminders | No | — | +| **Logging / misc** | | | | +| `LOG_LEVEL` | App log level | No | `DEBUG` in dev, `INFO` in prod | +| `DJANGO_LOG_LEVEL` | Django framework log level | No | inherits `LOG_LEVEL` | +| `APP_VERSION` | Version string override | No | from `settings.py` default | +| `SESSION_COOKIE_AGE` | Session duration (seconds) | No | `86400` (24 h) | ### App Versioning @@ -752,10 +868,12 @@ The app version is defined in **two places** and should be updated together: 1. **`pyproject.toml`** line 3: `version = "x.y.z"` — package metadata 2. **`project/settings.py`** line 17: `APP_VERSION = os.getenv("APP_VERSION", "x.y.z")` — runtime fallback -Use `make version V=1.1.0` to update both at once. The version appears in: +Use `make version x.y.z` (positional) to update both at once — it prompts `Version A will become the new version B, are you sure?` before writing. `make pc-run` also auto-bumps the patch digit on successful pre-commit if you answer `y` when asked. + +The version appears in: - `/health/` endpoint response - Support ticket emails -- Can be overridden at runtime via the `APP_VERSION` environment variable +- Can be overridden at runtime via the `APP_VERSION` environment variable (do **not** leave a legacy value like `0.x.y` in `.env` — remove the line so the default in `settings.py` takes effect) --- @@ -809,9 +927,13 @@ five-a-day/ │ │ │ ├── core/ Dashboard, Auth, Schedule, Utilities │ │ ├── models.py TodoItem, HistoryLog, FunFridayAttendance, ScheduleSlot -│ │ ├── views/ 12 view modules +│ │ ├── views/ 13 view modules (dashboard, auth, students, parents, +│ │ │ payments, management, app_forms, schedule, +│ │ │ fun_friday_attendance, todos, support, errors, +│ │ │ testing_tools) │ │ ├── constants.py DIAS_ES, MESES_ES, SCHEDULED_APPS -│ │ ├── middleware.py SimpleAuthMiddleware +│ │ ├── middleware.py SimpleAuthMiddleware, QAErrorEmailMiddleware +│ │ ├── decorators.py qa_access_required (testing env gate) │ │ ├── context_processors.py Notifications injected into all templates │ │ ├── transactions.py Optimized queryset builders │ │ ├── templates/ ALL HTML templates (base, pages, emails) @@ -831,7 +953,7 @@ five-a-day/ │ │ ├── exports.py Excel/CSV builders │ │ ├── admin.py Payment + Enrollment admin with actions │ │ ├── urls.py 20 URL patterns -│ │ └── management/commands/ generate_payments +│ │ └── management/commands/ generate_payments, seed_testdata │ │ │ ├── comms/ Communications │ │ ├── services/ EmailService + 12 email functions + PDF gen @@ -839,15 +961,39 @@ five-a-day/ │ │ ├── urls.py 10 URL patterns │ │ └── management/commands/ send_email, test_all_emails │ │ -│ ├── tests/ pytest suite (174 tests) -│ └── conftest.py Shared fixtures +│ ├── tests/ pytest suite (283 tests, 70 % coverage) +│ └── conftest.py Shared fixtures (models + authenticated_client) +│ +├── .github/ CI/CD — see docs/GITHUB.md +│ ├── workflows/ +│ │ ├── ci.yml Lint + typecheck + tests on every push/PR +│ │ ├── auto-merge.yml Hourly development → testing merge + PR to main +│ │ ├── codeql.yml Weekly Python security scan +│ │ └── notify-production.yml Email on push to main +│ ├── dependabot.yml Weekly dependency updates +│ └── CODEOWNERS Auto-request reviews from owner accounts +│ +├── docs/ +│ ├── GITHUB.md Full CI/CD + branch protection reference +│ ├── HTTPS.md HTTPS setup (Docker Nginx + Cloud Run) +│ ├── UV.md UV dependency management guide +│ ├── CELERY.md Celery worker/beat reference +│ └── TODO.md Open tasks │ -├── Dockerfile Multi-stage build -├── docker-compose.yml PostgreSQL + Django -├── Makefile 45+ commands -├── pyproject.toml Dependencies (uv/pip compatible) -├── CLAUDE.md AI development context -└── DEPLOYMENT.md GCP deployment guide +├── scripts/ Dev helpers (docker_smoke_test, etc.) +├── backups/ DB dumps from `make backup` (gitignored) +│ +├── Dockerfile Multi-stage build (builder + runtime) +├── docker-compose.yml PostgreSQL + Redis + Django + Celery worker + beat +├── docker-compose.testing.yml QA override (Gunicorn, DEBUG=False) +├── Makefile 60+ commands (`make help`) +├── pyproject.toml Dependencies (uv-managed) + tool config +├── uv.lock Reproducible dependency lock +├── entrypoint.sh Docker entrypoint (migrate, collectstatic, start) +├── .env / .env.testing Gitignored — never committed +├── CLAUDE.md AI development context (project rules) +├── DEPLOYMENT.md GCP deployment guide (all 3 environments) +└── README.md This file ``` ### App: core @@ -857,7 +1003,7 @@ Dashboard, authentication, scheduling, and shared utilities. Owns all views and | Component | Details | |-----------|---------| | **Models** | TodoItem, HistoryLog (1000-entry cap), FunFridayAttendance, ScheduleSlot | -| **Views** | 12 modules: auth, dashboard, students, parents, payments, management, app_forms, schedule, fun_friday_attendance, todos, support, errors | +| **Views** | 13 modules: auth, dashboard, students, parents, payments, management, app_forms, schedule, fun_friday_attendance, todos, support, errors, testing_tools | | **Middleware** | SimpleAuthMiddleware — session-based, protects all routes except /login/, /health/, /static/ | | **Templates** | base.html (layout), 15+ page templates, 12 email templates, error pages | | **Static** | app.css (sidebar/icons), 13 JS modules, logo | @@ -1049,8 +1195,8 @@ Standalone page with custom styling (does not extend base.html). | Metric | Value | |--------|-------| -| **Total tests** | 294 | -| **Test files** | 17 | +| **Total tests** | 283 | +| **Test files** | 19 | | **Coverage** | 70% (with `--cov-report=term-missing` on every run) | | **Runtime** | ~30 seconds (8 parallel workers via pytest-xdist) | | **Database** | PostgreSQL (same as production) — **always use `make test`** | @@ -1207,7 +1353,7 @@ Production defaults are applied automatically when `DEBUG=False` — no manual o - Django's `CsrfViewMiddleware` is active in the middleware stack. - All POST endpoints receive CSRF validation. JavaScript AJAX requests use `getCsrfToken()` (reads from cookies) and send via `X-CSRFToken` header. -- `CSRF_TRUSTED_ORIGINS` is configured per deployment (`render.yaml`, `gcp-cloudrun.yaml`). +- `CSRF_TRUSTED_ORIGINS` is configured per deployment via the `CSRF_TRUSTED_ORIGINS` env var (see [DEPLOYMENT.md](DEPLOYMENT.md)). - Only exception: `@csrf_exempt` on `/health/` endpoint (GET-only, returns `{"status": "healthy"}`). ### Transport Security (HTTPS) @@ -1244,30 +1390,27 @@ All settings are environment-controlled and only activate when `DEBUG=False`. | Health checks | Database has auth-checking healthcheck; web service uses `/health/` endpoint | | Seed script guard | `scripts/reset_seed_dev_data.py` aborts if `DJANGO_ENV=production` or `DEBUG=False` | -#### Render (render.yaml) - -| Decision | Implementation | -|----------|---------------| -| Auto-generated secrets | `DJANGO_SECRET_KEY` and `DJANGO_SUPERUSER_PASSWORD` use `generateValue: true` | -| Dashboard-only secrets | `DJANGO_SUPERUSER_USERNAME`, `DJANGO_SUPERUSER_EMAIL`, `LOGIN_USERNAME`, `LOGIN_PASSWORD`, `EMAIL_HOST_USER`, `EMAIL_SECRET` use `sync: false` (set in Render dashboard, not in YAML) | -| SSL enforced | `SECURE_SSL_REDIRECT=True`, all cookie secure flags enabled | -| Strict cookies | `SESSION_COOKIE_SAMESITE=Strict`, `CSRF_COOKIE_SAMESITE=Strict`, `CSRF_COOKIE_HTTPONLY=True` | +#### Google Cloud Run -#### Google Cloud Run (gcp-cloudrun.yaml) +Full deployment walkthrough in [DEPLOYMENT.md](DEPLOYMENT.md). Security-relevant decisions: | Decision | Implementation | |----------|---------------| -| Secret Manager | All credentials (`DJANGO_SECRET_KEY`, `LOGIN_*`, `EMAIL_SECRET`, `POSTGRES_*`, `GOOGLE_*`) loaded from GCP Secret Manager via `secretKeyRef` | -| Service account | Runs under dedicated `fiveaday-sa` service account with least-privilege IAM | -| Autoscaling | min=0, max=3 instances; startup probe with 50s timeout | +| Secret Manager | All credentials (`DJANGO_SECRET_KEY`, `LOGIN_*`, `EMAIL_SECRET`, `POSTGRES_*`, `GOOGLE_*`) injected at startup from GCP Secret Manager | +| Cloud SQL Auth Proxy | PostgreSQL connection goes through the proxy — no public IP on the database | +| Autoscaling | min=0 (cold starts acceptable) or min=1 (~$7/mo) for always-warm, max=2 instances | | Probes | Startup probe + liveness probe on `/health/` | +| TLS | Managed automatically by Cloud Run (custom domain + Google-managed certificate) | +| SSL enforced | `SECURE_SSL_REDIRECT=True`, all cookie secure flags enabled when `DEBUG=False` | +| Strict cookies | `SESSION_COOKIE_SAMESITE=Strict`, `CSRF_COOKIE_SAMESITE=Strict`, `CSRF_COOKIE_HTTPONLY=True` | ### Secrets Management | Rule | Implementation | |------|---------------| | No hardcoded credentials | `auth.py` requires `LOGIN_USERNAME`/`LOGIN_PASSWORD` env vars — refuses login if missing | -| No secrets in YAML | `render.yaml` uses `generateValue` or `sync: false`; `gcp-cloudrun.yaml` uses Secret Manager refs | +| No secrets in YAML | Production credentials live in GCP Secret Manager, injected into Cloud Run at startup — never in the repo | +| No secrets in GitHub Actions for deploy | CI uses only non-production Gmail SMTP + Codecov upload token. Production deploy runs manually with the operator's `gcloud` credentials | | No secrets in Docker image | `.dockerignore` excludes all `.env*` files | | `.gitignore` coverage | `.env*` pattern excludes all env file variants | | Production startup validation | `settings.py` raises `ValueError` if `SECRET_KEY` is the dev default and `DEBUG=False` | @@ -1463,66 +1606,7 @@ The `seed_testdata` command creates: Use `--reset` to wipe and re-seed, or `--small` for a minimal dataset (6 children only). -### GCP deployment plan - -The QA environment will be deployed on Google Cloud Platform, optimized for minimal cost: - -#### Recommended setup: Cloud Run + Cloud SQL - -| Component | GCP Service | Spec | Estimated cost | -|-----------|-------------|------|----------------| -| Application | Cloud Run | 1 vCPU, 512 MB, scales 0–2 | Free tier covers ~2M requests/month | -| Database | Cloud SQL (PostgreSQL 16) | `db-f1-micro`, 10 GB SSD | ~$8/month | -| Container images | Artifact Registry | Standard repo | Free tier (0.5 GB) | -| HTTPS | Cloud Run managed | Automatic TLS certificate | Free | -| DNS (optional) | Cloud DNS | 1 managed zone | ~$0.20/month | - -**Total estimated cost: ~$8–10/month** - -Cloud Run scales to zero when nobody is using it (no cost for idle time) and GCP provides automatic HTTPS with a `*.run.app` domain at no extra cost. The `db-f1-micro` Cloud SQL instance is the smallest available and more than enough for a QA team of 3–10 people. - -#### Why Cloud Run instead of a VM or Kubernetes - -- Kubernetes (GKE) has a management fee (~$70/month) that makes no sense for a small QA environment. -- A Compute Engine VM would cost ~$5/month but requires manual updates, SSL certificate management, and doesn't scale to zero. -- Cloud Run gives production-grade infrastructure (load balancing, HTTPS, health checks, rolling deploys) with almost no operational overhead. - -#### Deployment steps (run once during initial setup) - -```bash -# 1. Build and push the Docker image -gcloud builds submit --tag gcr.io/PROJECT_ID/fiveaday-testing - -# 2. Create the Cloud SQL instance -gcloud sql instances create fiveaday-testing \ - --tier=db-f1-micro \ - --region=europe-southwest1 \ - --database-version=POSTGRES_16 - -# 3. Create the database and user -gcloud sql databases create fiveaday_testing --instance=fiveaday-testing -gcloud sql users create fiveaday_tester --instance=fiveaday-testing --password=SECURE_PASSWORD - -# 4. Store secrets -echo -n "value" | gcloud secrets create SECRET_NAME --data-file=- - -# 5. Deploy to Cloud Run -gcloud run deploy fiveaday-testing \ - --image gcr.io/PROJECT_ID/fiveaday-testing \ - --region europe-southwest1 \ - --allow-unauthenticated \ - --set-env-vars "DJANGO_ENV=production,DJANGO_DEBUG=False" \ - --set-secrets "DJANGO_SECRET_KEY=django-secret-key:latest" - -# 6. Seed the database (one-time, via Cloud Run job or exec) -gcloud run jobs create seed-testdata \ - --image gcr.io/PROJECT_ID/fiveaday-testing \ - --command "python" \ - --args "project/manage.py,seed_testdata" \ - --region europe-southwest1 -``` - -After deployment, Cloud Run provides a URL like `https://fiveaday-testing-xxxxx.europe-southwest1.run.app` with HTTPS enabled automatically. +> **Deploying the QA environment** — see [DEPLOYMENT.md](DEPLOYMENT.md) for the full GCP plan. Testing runs on a Compute Engine e2-micro (free tier) with Docker Compose, while production uses Cloud Run + Cloud SQL. --- @@ -1544,6 +1628,7 @@ Auto-merge check • development ahead of testing? • last commit ≥ 24 h old? • CI passing on that commit? + • version bumped in pyproject.toml (dev > testing)? │ all yes ▼ git merge development → testing @@ -1594,14 +1679,23 @@ Concurrent CI runs on the same branch cancel each other automatically — new pu - CI triggers immediately (lint, typecheck, tests run in parallel, ~2-4 min) - CodeQL triggers immediately (weekly scan also runs independently) -- The hourly auto-merge cron checks this commit every hour until it is ≥ 24 h old with passing CI, then promotes to `testing` +- The hourly auto-merge cron promotes to `testing` only when **all four** conditions hold: dev is ahead of testing, the last commit is ≥ 24 h old, CI is green, **and the version in `pyproject.toml` has been bumped** (strictly higher than `testing`'s version). Without a version bump the merge is skipped even with 24 h of new commits on dev — run `make pc-run` (answer yes) or `make version x.y.z` before the next tick to unlock it. **2. Auto-merge fires** - Creates a `--no-ff` merge commit on `testing` titled `YYYY-MM-DD - ` - Pushes to `testing` (which triggers CI on `testing`) +- **Creates and pushes an annotated staging tag `testing-vX.Y.Z`** on the new testing merge commit - Opens PR `testing → main` if one is not already open (title matches the merge commit) -- Sends an HTML email to `OWNER_EMAILS` with a "Review PR" button +- Sends an HTML email to `OWNER_EMAILS` with version bump, staging tag, and a "Review PR" button + +**2b. You merge the PR → release tag on main** + +- `notify-production.yml` reads `version` from `pyproject.toml` on `main`'s new HEAD +- **Creates and pushes an annotated release tag `vX.Y.Z`** on that commit (skipped if tag already exists) +- Sends an HTML email to `hellofiveaday@gmail.com` with the release tag and `gcloud` deploy steps + +The two tag namespaces (`testing-vX.Y.Z` and `vX.Y.Z`) are fully independent — the `testing → main` PR can use any merge strategy (merge commit, squash, or rebase) because the release tag is derived from `pyproject.toml`, not from commit SHA continuity. **3. You review and merge the PR** @@ -1723,19 +1817,20 @@ Results appear in **Security → Code scanning alerts**. A new alert on `main` d ```bash # First-time setup uv sync --no-install-project # Install all dependencies (UV — see docs/UV.md) -make pre-commit-install # Install pre-commit hooks (Ruff + mypy + bandit) -make up # Start Docker (PostgreSQL + Django) +make pre-commit-install # Install the git pre-commit hook +make up # Start Docker (PostgreSQL + Redis + Django + Celery) ``` -1. Create a feature branch from `development` +1. Work on `development` (or a short-lived branch off `development`) 2. Make changes following the conventions below -3. Run `make lint` — Ruff linting must pass -4. Run `make mypy` — mypy type checking must pass -5. Run `make test` — all 294 tests must pass (PostgreSQL via Docker, parallel, with coverage) -6. Run `make check` — no Django system check issues -7. Create a pull request with clear description of changes +3. Run `make pc-run` — Ruff + mypy + bandit all pass, offers to auto-bump the patch version on success, and auto-stages `uv.lock` if regenerated +4. Run `make test` — all 283 tests must pass (PostgreSQL via Docker, parallel, with coverage) +5. `git commit` with a message like `v1.0.6 - Short description` (version comes first — conventions match every other commit in the project) +6. `git push origin development` +7. CI runs automatically on your push (see [CI/CD](#cicd--github-actions)) +8. ~24 h later, the auto-merge pipeline promotes your commit to `testing` and opens a PR to `main` for your review -Pre-commit hooks run **Ruff** (lint + format), **mypy** (type checking), and **bandit** (security) automatically on every commit. +Pre-commit hooks run **Ruff** (lint + format), **mypy** (type checking), and **bandit** (security) automatically on every `git commit`. If a hook modifies files (e.g. mypy regenerates `uv.lock`), the commit aborts — running `make pc-run` once resolves this by staging the regenerated lock file. ### Make Commands (Developer Tooling) @@ -1749,7 +1844,8 @@ Pre-commit hooks run **Ruff** (lint + format), **mypy** (type checking), and **b | **pytest-xdist** | Parallel test execution | Built into `make test` (`-n auto`) | | **pytest-randomly** | Randomized test ordering | Built into `make test` (seed printed) | | **pytest-cov** | Coverage reporting + badge | `make test`, `make coverage-badge` | -| **pre-commit** | Git hooks: ruff, mypy, bandit | `make pre-commit-install` | +| **pre-commit** | Git hooks: ruff, ruff-format, mypy, bandit | `make pre-commit-install` (first-time), `make pc-run` (dry-run all hooks + auto bump) | +| **make version** | Bump version in both `pyproject.toml` and `settings.py` | `make version x.y.z` (positional, with `y/N` confirmation) | All tools are configured in `pyproject.toml` and installed as dev dependencies via `uv sync`. diff --git a/project/comms/README.md b/project/comms/README.md index aa13302..a08218d 100644 --- a/project/comms/README.md +++ b/project/comms/README.md @@ -14,7 +14,7 @@ Generic email sending service with HTML template rendering and inline images. - `send_bulk_emails(template_name, emails_data, ...)` — sends multiple emails with the same template - `email_service` — singleton instance used throughout the project -Templates live in `core/templates/emails/` and extend `emails/base_email.html`. +Templates live in `core/templates/emails/` and extend `emails/base_email.html`. There are currently **14 email templates**: `happy_birthday`, `welcome_student`, `enrollment_child`, `enrollment_adult`, `fun_friday`, `payment_reminder`, `receipt_quarterly_child`, `receipt_adult`, `receipt_enrollment`, `vacation_closure`, `tax_certificate`, `monthly_report`, `newsletter`, plus the shared `base_email`. ### Email Functions (`comms/services/email_functions.py`) @@ -24,7 +24,7 @@ Convenience functions for each email type. Each wraps `email_service.send_email( | -------- | -------- | ------- | | `send_birthday_email` | `happy_birthday` | Daily cron / manual | | `send_welcome_email` | `welcome_student` | On student creation | -| `send_enrollment_confirmation_email` | `enrollment_child` | On enrollment | +| `send_enrollment_confirmation_email` | `enrollment_child` / `enrollment_adult` | On enrollment | | `send_fun_friday_email` | `fun_friday` | Weekly manual | | `send_payment_reminder_email` | `payment_reminder` | Monthly manual | | `send_quarterly_receipt_email` | `receipt_quarterly_child` | Quarterly manual | @@ -34,6 +34,8 @@ Convenience functions for each email type. Each wraps `email_service.send_email( | `send_monthly_report` | `monthly_report` | Monthly manual | | `generate_tax_certificate_pdf` | (HTML to PDF) | Called by tax certificate | +The **newsletter** and **enrollment receipt** templates do not have dedicated convenience functions — they are triggered directly from the `/apps/newsletter/` and receipt form views in `core/views/app_forms.py`, which call `email_service.send_email()` inline with per-recipient context. + ## Celery Tasks (`comms/tasks.py`) All tasks have retry logic (3 retries, exponential backoff): @@ -63,7 +65,7 @@ python manage.py send_email --tax-certificate --year 2024 ### `test_all_emails` ```bash -python manage.py test_all_emails # Send all 11 test emails +python manage.py test_all_emails # Send one test of each email template python manage.py test_all_emails --only fun_friday,birthday python manage.py test_all_emails --list # List available templates python manage.py test_all_emails --to admin@test.com diff --git a/project/core/README.md b/project/core/README.md index 94ed9de..f732db4 100644 --- a/project/core/README.md +++ b/project/core/README.md @@ -13,12 +13,12 @@ The `core` app is the "everything else" app — it owns the dashboard, authentic ## Views (core/views/) -The monolithic `views.py` was split into 12 focused modules: +The monolithic `views.py` was split into 13 focused modules: | Module | Views | Description | | ------ | ----- | ----------- | | `auth.py` | `login_view`, `logout_view`, `google_oauth_redirect`, `google_oauth_callback` | Session-based auth + Google OAuth | -| `dashboard.py` | `home`, `all_info` | Dashboard with stats (single `Case/When` aggregate query), todos, birthdays; database view | +| `dashboard.py` | `home`, `all_info` | Dashboard with stats (single `Case/When` aggregate query), todos, birthdays, inspirational quote from zenquotes.io (48 h cookie); database view | | `schedule.py` | `schedule_view`, `save_schedule_slot`, `fun_friday_view` | Weekly schedule grid + Fun Friday list (single attendance query for both weeks, filters from loaded students) | | `fun_friday_attendance.py` | `toggle_fun_friday_this_week`, `add/remove_fun_friday_attendance` | AJAX attendance toggles | | `todos.py` | `create_todo`, `complete_todo`, `history_list` | Todo CRUD + history pagination API | @@ -26,19 +26,26 @@ The monolithic `views.py` was split into 12 focused modules: | `parents.py` | `ParentCreateView` | Parent creation CBV | | `payments.py` | `payments_list`, `create_payment`, `quick_complete_payment`, etc. | Payment CRUD + AJAX APIs. Stats use single `Case/When` aggregate (1 query instead of 8). | | `management.py` | `gestion_view`, `update_site_config`, `create_teacher`, `create_group` | Admin config panel | -| `app_forms.py` | `fun_friday_form`, `payment_reminder_form`, etc. (10 views) | Email app form views | +| `app_forms.py` | `fun_friday_form`, `payment_reminder_form`, `newsletter_form`, `receipt_enrollment_form`, etc. | Email app form views (10+ forms, all prefill from `ACADEMY_*` env vars where relevant) | | `support.py` | `submit_support_ticket` | Support ticket email API | | `errors.py` | `handler400-500`, `health_check` | Error pages + health endpoint | +| `testing_tools.py` | `testing_tools_view`, `seed_testdata_ajax`, `submit_backlog`, `toggle_error_reporting` | **QA-only** dashboard at `/testing/` — database seeding, backlog reporting, error-reporting toggle. All gated by `qa_access_required` decorator. | ## URL Patterns (core/urls.py) -Routes for: login/logout, dashboard, schedule, todos, history, support, error test pages. +Routes for: login/logout, dashboard, schedule, todos, history, support, `/testing/` QA dashboard, error test pages. Student, payment, management, and email app routes live in `students/urls.py`, `billing/urls.py`, and `comms/urls.py` respectively, but their views are still in `core/views/`. -## Middleware +## Middleware & Decorators -**SimpleAuthMiddleware** — session-based auth that protects all URLs except `/login/`, `/health/`, `/static/`, `/media/`, and `/auth/google/*` (including `/callback/`). Credentials come from `LOGIN_USERNAME`/`LOGIN_PASSWORD` env vars (required; no hardcoded fallbacks). +- **`SimpleAuthMiddleware`** (`middleware.py`) — session-based auth that protects all URLs except `/login/`, `/health/`, `/static/`, `/media/`, and `/auth/google/*` (including `/callback/`). Credentials come from `LOGIN_USERNAME`/`LOGIN_PASSWORD` env vars (required; no hardcoded fallbacks). +- **`QAErrorEmailMiddleware`** (`middleware.py`) — in the QA environment, catches unhandled exceptions and emails them to `SUPPORT_EMAIL` with the full traceback. Toggleable via the `/testing/` dashboard. +- **`qa_access_required`** (`decorators.py`) — reusable gate for `/testing/` views and endpoints. Returns 404 (not 403) unless `DJANGO_ENV=testing`, `DEBUG=False`, and the session user matches `QA_TESTING_USERNAME`. + +## Management Commands + +- **`seed_testdata`** — populates the QA database with 3 teachers, 5 groups, 6 parents, 12 child students, 3 adult students, 1 inactive student, active enrollments, payments in various states, schedule slots, todo items, and history log entries. Flags: `--reset` (wipe first), `--small` (6 children only). Also callable from the `/testing/` dashboard via AJAX. ## Templates diff --git a/project/project/settings.py b/project/project/settings.py index 6c149b4..60feb82 100644 --- a/project/project/settings.py +++ b/project/project/settings.py @@ -12,10 +12,10 @@ # ============================================================================ # APP VERSION # ============================================================================ -# NOTA: Al cambiar la versión, actualizar también en: -# - readme.md (badge y texto) +# NOTA: Usa `make version x.y.z` para actualizar ambos sitios a la vez: # - pyproject.toml (campo version) -APP_VERSION = os.getenv("APP_VERSION", "1.0.5") +# - README.md (badge y tabla de versiones — gestionado por la skill update-readme) +APP_VERSION = os.getenv("APP_VERSION", "1.0.6") # ============================================================================ # SECURITY SETTINGS diff --git a/pyproject.toml b/pyproject.toml index 3d78a11..02303a3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "five-a-day" -version = "1.0.5" +version = "1.0.6" description = "Five a Day management software" readme = "README.md" requires-python = ">=3.12" diff --git a/uv.lock b/uv.lock index ba23172..42a4ac3 100644 --- a/uv.lock +++ b/uv.lock @@ -695,7 +695,7 @@ wheels = [ [[package]] name = "five-a-day" -version = "1.0.5" +version = "1.0.6" source = { editable = "." } dependencies = [ { name = "celery" }, From efe1ec06e6609cd01559147594ef1845efaa5890 Mon Sep 17 00:00:00 2001 From: Joaquin Hernandez Martinez Date: Wed, 15 Apr 2026 15:21:39 +0200 Subject: [PATCH 3/8] v1.0.7 - Fixed pipeline issues and made it work, as well as handled config and pipeline colliding, and fixed some small issues --- .github/workflows/ci.yml | 8 ++++ CLAUDE.md | 5 +- Makefile | 24 ++++++++-- README.md | 80 +++++++++++++++++++++++++++---- project/core/README.md | 4 +- project/core/static/favicon.ico | Bin 0 -> 70564 bytes project/core/templates/base.html | 26 ++++++++++ project/core/views/dashboard.py | 17 +++++-- project/project/settings.py | 2 +- project/project/settings_test.py | 10 ++++ project/pytest.ini | 2 + project/static/favicon.ico | Bin 0 -> 70564 bytes pyproject.toml | 2 +- uv.lock | 2 +- 14 files changed, 161 insertions(+), 21 deletions(-) create mode 100644 project/core/static/favicon.ico create mode 100644 project/static/favicon.ico diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c5ed88c..0fe9bea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,6 +58,14 @@ jobs: run: uv sync --frozen --no-install-project - name: mypy + env: + # django-stubs plugin imports project.settings at load time. Our settings.py + # raises ValueError when DEBUG=False AND SECRET_KEY is the dev default, so we + # set DJANGO_DEBUG=True to bypass that check during static analysis. + DJANGO_SETTINGS_MODULE: project.settings + DJANGO_DEBUG: "True" + DJANGO_SECRET_KEY: mypy-static-analysis-dummy-key + PYTHONPATH: project run: uv run mypy project/ # ============================================================ diff --git a/CLAUDE.md b/CLAUDE.md index 8114f8a..f1ff2fb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -126,9 +126,12 @@ All pricing flows through `billing/services/`. The single source of truth is `Si - **Celery eager mode** — without Redis, tasks run synchronously. Don't rely on task.delay() being truly async in development. - **`#webcrumbs` removed** — the old CSS scoping wrapper is gone. If you see references to it in old code, delete them. - **Template names are English** — all email templates were renamed from Spanish (e.g., `matricula_niño.html` → `enrollment_child.html`). Never create templates with Spanish names. -- **Version in two places** — `pyproject.toml` and `project/project/settings.py`. Use `make version x.y.z` (positional, with y/N confirmation) to update both. +- **Version in four places** — `pyproject.toml`, `project/project/settings.py` (`APP_VERSION` default), the `README.md` header badge URL, and `uv.lock` (the project's own `[[package]]` entry). `make version x.y.z` (positional, with y/N confirmation) updates the first three with `sed` and regenerates `uv.lock` via `uv lock --quiet`. Running `make version` with no argument prints the pyproject and README badge values side-by-side and warns if they've drifted. `make pc-run`'s auto patch-bump does the same four updates, then the existing `git add uv.lock` block at the end of the target stages the regenerated lockfile so the next commit isn't blocked. - **APP_VERSION in `.env`** — the local `.env` may contain a legacy `APP_VERSION=0.x.y` line. Either remove the line or update it — it silently overrides the default in `settings.py` at runtime. - **`pc-run` renamed** — the old `make pre-commit-run` target is now `make pc-run`. It also auto-bumps the patch version on a clean pass (y/N prompt) and auto-stages `uv.lock` if regenerated. +- **mypy CI job needs `DJANGO_DEBUG=True`** — `django-stubs` imports `project.settings` at load time for the Django plugin. Without `DJANGO_DEBUG=True` + a dummy `DJANGO_SECRET_KEY`, the production guard at the top of `settings.py` raises `ValueError: DJANGO_SECRET_KEY debe ser cambiado en producción`. The CI `mypy` step sets both, plus `PYTHONPATH=project`, as env vars. Any new static-analysis job that imports settings will need the same. +- **Test settings disable production security redirects** — `settings_test.py` explicitly sets `SECURE_SSL_REDIRECT = False`, `SECURE_HSTS_SECONDS = 0`, `SESSION_COOKIE_SECURE = False`, `CSRF_COOKIE_SECURE = False`. Don't remove them — the Django test client speaks HTTP against `testserver`, and inheriting `SECURE_SSL_REDIRECT=True` from `settings.py` (which kicks in when `DEBUG=False`) turns every test request into a 301 to `https://testserver/...`. The overrides keep the test settings self-contained regardless of how CI configures `DJANGO_DEBUG`. +- **WhiteNoise warning is filtered** — `pytest.ini` has `filterwarnings = ignore:No directory at:UserWarning` to silence the once-per-request warning from WhiteNoise middleware when `STATIC_ROOT` (`staticfiles/`) doesn't exist. That directory only exists after `collectstatic` runs (production only), so the warning is noise in tests. Don't add `collectstatic` to the test command. ## README maintenance (MUST do at end of every work session) diff --git a/Makefile b/Makefile index e9581bf..07509b3 100644 --- a/Makefile +++ b/Makefile @@ -363,20 +363,35 @@ endif version: @CURRENT=$$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/'); \ + BADGE=$$(grep -oE 'version-v[0-9]+(\.[0-9]+)*-brightgreen' README.md | head -1 | sed -E 's/version-v(.*)-brightgreen/\1/'); \ NEW="$(_VERSION_ARG)"; \ if [ -z "$$NEW" ]; then \ echo "Usage: make version x.y.z"; \ echo ""; \ - echo "Current version: $$CURRENT"; \ + echo "Current version:"; \ + echo " pyproject.toml: $$CURRENT"; \ + echo " README.md badge: $$BADGE"; \ + if [ -n "$$BADGE" ] && [ "$$CURRENT" != "$$BADGE" ]; then \ + echo ""; \ + echo " WARNING: pyproject and README badge are out of sync."; \ + fi; \ exit 1; \ fi; \ read -p "Version $$CURRENT will become the new version $$NEW, are you sure? [y/N] " confirm; \ if [ "$$confirm" = "y" ] || [ "$$confirm" = "yes" ]; then \ sed -i 's/^version = ".*"/version = "'"$$NEW"'"/' pyproject.toml; \ sed -i 's/APP_VERSION = os.getenv("APP_VERSION", ".*")/APP_VERSION = os.getenv("APP_VERSION", "'"$$NEW"'")/' project/project/settings.py; \ + sed -i -E 's|version-v[0-9]+(\.[0-9]+)*-brightgreen|version-v'"$$NEW"'-brightgreen|' README.md; \ + uv lock --quiet; \ echo "Version updated to $$NEW in:"; \ echo " - pyproject.toml"; \ - echo " - project/settings.py"; \ + echo " - project/project/settings.py"; \ + echo " - README.md (badge URL)"; \ + echo " - uv.lock (regenerated via 'uv lock')"; \ + echo ""; \ + echo "NOTE: the Recent Versions table, Version History details block, and per-app"; \ + echo " READMEs were NOT changed automatically — run the 'update-readme' skill"; \ + echo " after staging your work to refresh them."; \ else \ echo "Cancelled."; \ fi @@ -485,7 +500,10 @@ pc-run: NEW="$$MAJOR.$$MINOR.$$((PATCH + 1))"; \ sed -i 's/^version = ".*"/version = "'"$$NEW"'"/' pyproject.toml; \ sed -i 's/APP_VERSION = os.getenv("APP_VERSION", ".*")/APP_VERSION = os.getenv("APP_VERSION", "'"$$NEW"'")/' project/project/settings.py; \ - echo "Updated version $$CURRENT with new version $$NEW"; \ + sed -i -E 's|version-v[0-9]+(\.[0-9]+)*-brightgreen|version-v'"$$NEW"'-brightgreen|' README.md; \ + uv lock --quiet; \ + echo "Updated version $$CURRENT with new version $$NEW (pyproject.toml, settings.py, README badge, uv.lock)"; \ + echo "Reminder: Recent Versions + Version History in README were NOT touched - run '/update-readme' skill to refresh them."; \ fi; \ fi @if [ -n "$$(git status --porcelain uv.lock 2>/dev/null)" ]; then \ diff --git a/README.md b/README.md index dd398de..bc9c590 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@

- Version + Version Python Django PostgreSQL @@ -40,9 +40,9 @@ Live status for each environment is pulled from GitHub Actions — the badges be | Version | Date | Description | |---------|------|-------------| -| **v1.0.5** | 2026-04-15 | GitHub Actions CI/CD pipeline (lint, typecheck, tests, CodeQL, Dependabot), auto-merge `development` → `testing` with 24 h delay + auto-PR to `main`, email notifications, branch protection rules for public repo hardening, `make pc-run` auto-stages regenerated `uv.lock` | -| v1.0.4 | 2026-04-15 | Inspirational quote generator on `/home` (48 h cookie rotation), GCP deployment plan ([DEPLOYMENT.md](DEPLOYMENT.md)), Celery worker + beat containers, cleaned legacy Render config, `make version x.y.z` positional arg with confirmation guard, `make pc-run` (renamed from `pre-commit-run`) with auto version bump | -| v1.0.3 | 2026-04-14 | Test coverage raised to **70 %** — 13 new test files across auth views, comms services, app forms, constants, create payment views, exports, forms, parent views, payment views, schedule views, student forms, student views, transactions | +| **v1.0.7** | 2026-04-15 | Favicon + Open Graph / Twitter Card / `theme-color` / `apple-touch-icon` metadata on every page (multi-resolution `favicon.ico` generated from `logo.png`, overridable per-page blocks); `SECURE_SSL_REDIRECT`/HSTS/secure cookies disabled in `settings_test.py` so the CI test suite stops getting 301-redirected by the Django test client; WhiteNoise `No directory at: staticfiles/` warning silenced via `pytest.ini` `filterwarnings`; dashboard quote fetcher now uses `follow_redirects=True` against `zenquotes.io/api/quotes` (no trailing slash) and logs failures instead of swallowing them; CI mypy job now sets `DJANGO_DEBUG=True` + dummy `DJANGO_SECRET_KEY` so django-stubs can import `settings.py` without tripping the production secret-key guard; `make version` + `make pc-run` now also rewrite the README version badge, regenerate `uv.lock`, and warn when `pyproject.toml` and the badge drift apart | +| v1.0.6 | 2026-04-15 | New `update-readme` Claude skill under `.claude/skills/update-readme/SKILL.md` that routes staged changes across the whole documentation set (main README, `CLAUDE.md`, `DEPLOYMENT.md`, `docs/`, per-app READMEs); sweeping README restructure — renamed `readme.md` → `README.md`, reorganized sections, expanded env-var reference; `.env.testing.example` removed (its contents now live inline in the README's `.env template` code block, with `.gitignore` tightened so no `.env*` file can be committed); `auto-merge.yml` and `notify-production.yml` workflow refinements; per-app README tune-ups for `core` and `comms` | +| v1.0.5 | 2026-04-15 | GitHub Actions CI/CD pipeline (lint, typecheck, tests, CodeQL, Dependabot), auto-merge `development` → `testing` with 24 h delay + auto-PR to `main`, email notifications, branch protection rules for public repo hardening, `make pc-run` auto-stages regenerated `uv.lock` | --- @@ -70,6 +70,7 @@ Live status for each environment is pulled from GitHub Actions — the badges be - [Frontend](#frontend) - [Infrastructure \& Deployment](#infrastructure--deployment) - [Python Dependencies](#python-dependencies) + - [Developer Tooling](#developer-tooling) - [Database Schema](#database-schema) - [ER Diagram](#er-diagram) - [Key Constraints](#key-constraints) @@ -106,6 +107,7 @@ Live status for each environment is pulled from GitHub Actions — the badges be - [Model Tests](#model-tests) - [Service Tests](#service-tests) - [View Tests](#view-tests) + - [Additional Test Files (v1.0.0+)](#additional-test-files-v100) - [Migrations](#migrations) - [Security](#security) - [Authentication](#authentication) @@ -128,7 +130,7 @@ Live status for each environment is pulled from GitHub Actions — the badges be - [How to report a problem](#how-to-report-a-problem) - [Error pages you might see](#error-pages-you-might-see) - [For developers: how the QA environment works](#for-developers-how-the-qa-environment-works) - - [Access control for /testing/](#access-control-for-testing) + - [Access control for `/testing/`](#access-control-for-testing) - [CI/CD \& GitHub Actions](#cicd--github-actions) - [Pipeline Overview](#pipeline-overview) - [Branch Strategy](#branch-strategy) @@ -143,6 +145,7 @@ Live status for each environment is pulled from GitHub Actions — the badges be - [CodeQL Security Scanning](#codeql-security-scanning) - [Contributing](#contributing) - [Development Workflow](#development-workflow) + - [Make Commands (Developer Tooling)](#make-commands-developer-tooling) - [Code Conventions](#code-conventions) - [Adding a Feature](#adding-a-feature) - [License](#license) @@ -151,8 +154,64 @@ Live status for each environment is pulled from GitHub Actions — the badges be ## Version History & Roadmap -

-v1.0.5 — CI/CD Pipeline & Public Repo Hardening (current) +
+v1.0.7 — Favicon, Social Metadata & CI Test Fixes (current) + +**Social sharing & branding** + +- Multi-resolution `favicon.ico` (16/32/48/64/128/256) generated from `project/static/images/logo.png` — dropped in both `project/static/` and `project/core/static/` so both STATICFILES_DIRS paths serve it +- `base.html` now includes full social-sharing metadata: ``, ``, `` (matches the violet palette), `apple-touch-icon`, full Open Graph set (`og:type`, `og:site_name`, `og:title`, `og:description`, `og:image`, `og:image:alt`, `og:url`, `og:locale`), and Twitter Card summary tags +- Every content field is wrapped in an overridable Django block (`meta_description`, `og_title`, `og_description`, `og_image`, `twitter_title`, `twitter_description`, `twitter_image`) so per-page templates can tailor link previews without touching `base.html` + +**Test-suite fixes** + +- `settings_test.py` now explicitly sets `SECURE_SSL_REDIRECT = False`, `SECURE_HSTS_SECONDS = 0`, `SESSION_COOKIE_SECURE = False`, `CSRF_COOKIE_SECURE = False` — the CI environment runs with `DJANGO_DEBUG=False`, which activated the production SSL redirect and turned every test request into a 301 to `https://testserver/...`. The test settings are now self-contained and correct regardless of `DJANGO_DEBUG`. +- `pytest.ini` adds `filterwarnings = ignore:No directory at:UserWarning` to silence the 142 WhiteNoise warnings that were emitted once per test request (the `staticfiles/` directory only exists after `collectstatic`, which isn't run before tests) + +**Dashboard reliability** + +- Zenquotes fetch in `core/views/dashboard.py` now targets `https://zenquotes.io/api/quotes` (no trailing slash — the old URL was getting 301-redirected) with `follow_redirects=True` as a guard against future URL changes +- Silent `except Exception: pass` replaced with proper `logger.warning(...)` calls — failures are still non-fatal but now visible in logs + +**CI tooling** + +- `mypy` job in `ci.yml` now sets `DJANGO_SETTINGS_MODULE=project.settings`, `DJANGO_DEBUG=True`, a dummy `DJANGO_SECRET_KEY`, and `PYTHONPATH=project` — `django-stubs` imports `settings.py` at load time, which previously raised the production secret-key guard +- `make version x.y.z` now also updates the README version badge via `sed`, regenerates `uv.lock` via `uv lock --quiet`, and prints a reminder to run the `update-readme` skill afterwards; running `make version` with no arg now shows both `pyproject.toml` and the README badge side-by-side and warns if they've drifted +- `make pc-run`'s auto patch-bump now also rewrites the README badge and regenerates `uv.lock` — the existing `git add uv.lock` tail stages the refreshed lockfile automatically + +
+ +
+v1.0.6 — Documentation Skill & Doc Overhaul + +**Documentation agent** + +- New `update-readme` Claude skill at `.claude/skills/update-readme/SKILL.md` — routes staged files to the right docs (main README, CLAUDE.md, DEPLOYMENT.md, docs/, per-app READMEs), applies per-file checklists, and sweeps for stale references across the full documentation tree + +**README overhaul** + +- `readme.md` → `README.md` rename (case-sensitive file systems matter on GCP) +- Major reorganization of sections; expanded Environment Variables Reference; tightened Recent Versions table to 3 rows; populated Developer Tooling and Make Commands tables +- `.env template` is now the single authoritative source for local env-var structure, lives inline in the README as a fenced `bash` block + +**Secrets hygiene** + +- Removed `.env.testing.example` (its content now lives only inline in the README `.env template` block) +- `.gitignore` tightened: `.env*` matches everything, no `!.env.example` exception, no `.env*.example` carve-outs + +**CI workflow refinements** + +- `auto-merge.yml` — improved commit detection and PR creation for the `development` → `testing` → `main` cascade +- `notify-production.yml` — richer production deployment notification email with commit info and next-step `gcloud` commands + +**Per-app docs** + +- `project/core/README.md` and `project/comms/README.md` touched up to match post-refactor structure + +
+ +
+v1.0.5 — CI/CD Pipeline & Public Repo Hardening **GitHub Actions CI/CD** (new — see [docs/GITHUB.md](docs/GITHUB.md)) @@ -777,8 +836,8 @@ Run `make` or `make help` for the full list. Key commands: | `make test-fast` | Stop on first failure | | `make test-k K=payment` | Run tests matching keyword | | **Versioning** | | -| `make version 1.1.0` | Update version in `pyproject.toml` + `settings.py` (with y/N confirmation) | -| `make version` | Show current version | +| `make version 1.1.0` | Update version in `pyproject.toml`, `settings.py`, the README badge, and regenerate `uv.lock` (with y/N confirmation); reminds you to run the `update-readme` skill to refresh Version History | +| `make version` | Show current version from `pyproject.toml` + README badge; warns if they've drifted | | **Developer Tooling** | | | `make lint` / `make lint-fix` | Run Ruff linter (optionally auto-fix) | | `make format` / `make format-check` | Run Ruff formatter | @@ -803,8 +862,9 @@ The project supports three environments, controlled by `DJANGO_ENV` and `DJANGO_ | Environment | `DJANGO_ENV` | `DJANGO_DEBUG` | Database | Static Files | Use Case | |------------|-------------|---------------|----------|-------------|----------| | **Production** | `production` | `false` | PostgreSQL (Cloud SQL) | WhiteNoise + collectstatic | Live deployment | -| **Development** | `development` | `true` | PostgreSQL (Docker) | Django dev server | Local coding | | **Testing** | (via settings_test.py) | `false` | PostgreSQL (Docker) | Simple storage | `make test` | +| **Development** | `development` | `true` | PostgreSQL (Docker) | Django dev server | Local coding | + > **Defaults are production-safe**: `DJANGO_DEBUG` defaults to `false` and `DJANGO_ENV` defaults to `development`. In production, always set `DJANGO_ENV=production` and ensure `DJANGO_SECRET_KEY` is a strong random value. diff --git a/project/core/README.md b/project/core/README.md index f732db4..09dfe04 100644 --- a/project/core/README.md +++ b/project/core/README.md @@ -51,7 +51,7 @@ Student, payment, management, and email app routes live in `students/urls.py`, ` All templates live in `core/templates/`: -- `base.html` — main layout (sidebar, header, support modal, Tailwind CDN config) +- `base.html` — main layout (sidebar, header, support modal, Tailwind CDN config). Also carries the site-wide `` metadata: favicon + apple-touch-icon, `theme-color` (violet `#6d28d9`), meta description/author, full Open Graph set, and Twitter Card tags. Every content field is wrapped in a Django block (`meta_description`, `og_title`, `og_description`, `og_image`, `twitter_title`, `twitter_description`, `twitter_image`) so per-page templates can tailor link previews. - `home.html`, `login.html`, `schedule.html`, `fun_friday.html`, etc. - `payments/` — payment list, create, detail - `apps/` — email form views + `_email_preview.html` partial @@ -60,6 +60,8 @@ All templates live in `core/templates/`: ## Static Files +- `favicon.ico` — multi-resolution (16/32/48/64/128/256) icon generated from `images/logo.png`; referenced from `base.html` as both `rel="icon"` and `rel="shortcut icon"` +- `images/logo.png` — 500×500 PNG; reused as `apple-touch-icon` and Open Graph image - `css/app.css` — sidebar transitions, Material Symbols icon font settings - `js/base.js` — notification/history dropdowns (loaded on every page) - `js/support.js` — support ticket modal diff --git a/project/core/static/favicon.ico b/project/core/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..7979c4028f9d23153d0cdfdb4e27275a60fbf864 GIT binary patch literal 70564 zcmag_V~{0Gur`XWwvB09(>AAVThq2}W7@WD+qP}nw!7DT_qV^F_ncEvQI#1_R8;-Q zh|GL43jhECKmw4F0sjpmKo=+gVER82(f{gtPyhf47y!V?_|GkOxD7U$DMM}OxGT*Dn!T<@7zbn6$ z3xW)TBAF53ImZP!$T( zs-4|yvN|CFtW)3+A%2IZgcJ5Q-Et!oniVF>~h8Px>>^0$LL|sMm4pHKiRqIe|CWSTWGf6UsiuY zSD&ly4M}xd|0Imui*B=GzT((nCMU_0MUdH-(jCkCoBxKcBj5cqHFlL3b;_j<%!|Ju zk;e+RA)%f%)WTqh&~>djdiW>EA|DgS-&9x+;r#i*hePo4_f_AgI`prcxUe{nuWPKi zjyBeS_3$N6?(h*eX^An5Q1iG+iF*h+d*;3r`8$Hx%i&ahwaQ?E7A{=wyd@0!&mfCd#Ul z&hp$NHgyOUu4gH%46VK>PRjCbsRD*qA4Z!KdS^tipq)92Dh6+{qAY(QqM@96RM0frEDK}yX89NIsKY>pLwV61M;9C`%;I<60tAApvk<9^DtGf zG&-pH+>EY+jn)X6r{Fz*92ovaw%Q+4_iu0mJVw=8XMD@SB7hgo-;9jh08nXZ>RFeh zgXbE#ED7yT+TlPjOWRb7L*F!9jaJdZl!7u%se8eOUxrStN(F?I?FDXH)2F@4O9PNg z*A{j#f~oAreisUA=3V|OYr{;0My?ktSt)@j=)fuREz($>mG!wY)IB_zdif3K$OO#c zTR->AtDDJJ&km}@F#4qr%Ok(NjuG?{*b3!V+8D^+6^CtvFn2~U#goy56&n} z;1@INeeMzp@Wb4hh_{khR^r zTk44l=^XCbw>_q18Xf~Qte};O%LWN;cXHj$=Xc=H#4BK!;3>{Yi-48eN+flOSlj*r zA>hk+V@xZrg4Vq9p4OKwgQFX*vYr}m_0PGjlP4pck!p}=hSf%G3rhXRXNGXAqU}n~ zD*R}da=4?Fv6Q%&Ab_xY7~(z0;{5Z*AlR%uf5`+`XbeWJv#*#|oRwxdzpu~H2VcJ* z8qMK#nX2ZFyxdQ4b?kIMijm3jVfx?YN8eimDqKjLxOacwu>}z9jSefc3_F_FGW%2F zAp(5ucQ%=gW~ZMkFPj(J398C|s(69L#@}yvRP~!#zvBCGr(AgA8o@!HWv@10Uo#?L zxmrlb68rOor^e{1<(R$pGrEU^1@ESu=CSRp=E`0lOF?6+xY9rsds6hiD_o?lvv1E; z(PZ@XYvPXe2AA@%Mi0f=k7)`i+1<^#A7wt+J@H!R=#zo?&g zXC%p@cT#(~4Lr_?^-WCjDo`-Im*gbCHMFc?x0*h#`3z*^xfuX7DM3Nbsx#w4I_;Tx zlaR&CBgY<@x0{FPzzaI}IE}=K%MCy!}wvcVxRQx*$?2hl|v0M;o+}!HVj9t`OZjh!>#G<_kU(PR6NVjCS( z*`slPreI{}lm23=r}Ay=sqULCRUGZ**+ZAUZiagwy!i7)2<+$I{{6iH+G>nt7r1Sk z;Akt}Ka4f88@m)mk}~J%(45(EJC6c;xb!21uetF2^~Gn#>;;~HaM}HcZ9hF}#m|@Z z(Usxt9UnfrCT$c~s|n=;ePAw`6hb;D+`=@_U{Tgf=~pT&)>+h*W$IYy7&`J3iUM8{ zH{_5r(VKu#M^kd*Ou4WtGIRrWJ#MiCW+|-P@A;hKX$|q9EcBpo6=L;~Rd5Sk+sj#d zbv)_w9Dg+DW!9<+n#5&*{^L^2!tQUcN}QUY+HgP!0d!VJ0P&j5Z=2z>#pr~cJYvmSP*$E(5M!EgS( zWK$3ludWsw6lyu9q};$m-8{wV6dQqCj@ zmIb5?GGcLnCPDajCn*qw7A;>iWd%wj_gddZlmsz?1`itau3jG7*x0b2Zs|v1`}Xd( z*lae_Mt*wthzS3SZg2P5YM_-}by5Evth-qmToOV=zPiL(PPm84LYyFH#-`%;m2=li zfd^q%oyP$~ulIWuJj(NBSo`~PsS{}XI7{vY?M4*DMYU>-Rrdm zoU_Vu`_C)i=bw{gNg)COXq2F!IPho4t~~XGvjVm1z~cN^ujQ6H(4KB|1+AkgL3DJK zb_8kR<&w!&=6d$Ej1+NPkocVxA~IBP6zDwg1Q}*77N;GrT^9-%(RsN3xt*Qk)|II& zuFu_A;O;IE__+(vJGA=R4S|OrFmmIs$J)~0jNh62>1lMjI| z%qgTWJp}8vLo#bP8Dhyq?Mh=Y1|e5MMTwmkl#`0cgAj>fabPe#ISADl-<1sg<3Jad zlFknYFu`ScLONF!NeWK%#f|zADTwfPh<^stU~ed0d<~+ICO7pddQsOZxig!sLXg<%7%V?tD*52y&-+RA~@G+$3Uu}(VBDVQf?At2uuR=l^ zf2s1yt%HMKZX+Ah!UN+`w};92B_(VA_Xz|5FZo80@0lqbvdEX)(P0xd)h+91)wd-0AX8I(DSHQTK z7F)fR)0pv^C@^~ft0QIiaqsIW!m^-0BPb#%AWM+G9aUMx^Dj_E<9Vm|2%7g~#I1zV zhT&7z41)1XmJc=a?#6ky!0OU#By~Bsl!C&|hLMV|577Rk+2S{IoYp$QB*vkM(t^2; z{1QWI=S&zm%o*aQ5TlFW58g#%CiPI@(R=Ta(BLc{4I((oC9CDY*jDp0TS=%#{TB2t zw$u_Nv06~2#CQ7To)BRf1I*|k@=NQ$qZn1NLfm#Wj1|b_F&}K6R1MWc# zA5#~gn`SK5KLmP**RoMpF+B)f{7=0tMyYJuvs)#Vaoyt}3w8{?F3_Le=4amz1|-dN zbdE{)Aa-qAn>@K7H6C3JUtN|b9v;BB4h zrLl(er|d*9W*ul-(Zm+Mrb{E6WhpY9g5OF8Tz0Wv74JzvYKwI9OGQ`R<@jLW!1UC3 zc=f-_&&A;148oRr1Q(e?qrD>;g+m=>iWW_5y4t>Jmq@`4>k2}F^!L`#xpCg4$7WK{ z*8nGg=Rhc!M|ZM=tPnWnrX{_7hdRl z@La&vvhVH@X;0=aZtl>zU#{4E%u`wM7LMU$v+B0B2M0ZWu*+=+zHl!?3X8Z94M~s* z+fr4-dMILbT{Up_UULkJp-+VlOZ;;pLGe;<)edmpdN942mtIPOL1gm?Y-@T!r<=GO zFs-_;HKon*BAG`5Ojj7Rc@8CY;&eW^P75!?g_;dIKbEOAKU3UxHL3>zQs$hL%=%!T zg!#NflRUOA+b=WBI-iDT)ldV_nKn_G{uxF=fPzA9GH)P=nq^7HAl3a%I`ad#Wa^rlNcqtsbpH-8UFF*fnTAh= zNMr!!Y`5^u`ZZmNGz%4NO&*>R5c-URwn!%d_JYYMPU+e9mc}B-6?X2>8~#jC$g!Pv zbjaj0YV+(WZx<5%4B-97{?1T%P*j-x^&NBh*#V!v9-2?b9c1-h&gy>-%e3xU6d=1& zKypWQN}Z+YUke$0Y>8v@AgQVvoax{L!vr3n_h9VV156R8tgMrpurmDkOxmE(LKx-4 zZosJ{4lXHiONI)=Wp7L5HocedJNYMcSRvAAjaKD84lf<6O=fRFB5c{PqRxv(Dp97U zukxG|Y_a7vczIn`#!gyc9*)jKfQl9yIm_(H7o7YcIv6`G^JUvnI5wb!Ub36oj66&& z(vxs~eXeqTZxdsd*ijIdj&vSdC=| z(q@mSIv0_yF>vrd;2+PJsH%UO-3Fs{kd>%}1P*cGfY8vo;Qqn_>yreOUSFjxn9vdP zgr7lnywMZrpSK5K^vQA)$-Y9@vPt=)O36?$uuIjJ8u&`+n-nWnDK}ORI?bmH3JQID z*{D}aLEPSK1y2OGO-QZ)fo{PVYeE8lTi|oFyzbb+EKDmPRT?Mk)1g-S+ZrEBbguv&kn{*E83@_lI`ek>ms{IAoVrp4_W;9GbsLpDk_9RHf_jzv(lOM> zwileFc2K*PZ~BP|m!>=8MUU2gwgoMu`VGr_Eq}r`^XIqg7qJ41_l$vQ_Fq0%b?J;a z7b_LcCvH!|YbQF)z&I_WMpchbOhY`qG za6`I$v=udGJwIG?UMm6Z4*DNfPaS7(8w64atww7hRTFv5SN|+5r!?WPK}4nyVj+Ia zG&>b$+1!9~PVY3n2XbS$Rjwo#56wH-$%YdWmoW;iR^n~@70Y7BgHAEY3@1|Po6%zi{~i|G|aQ=Db?ri z!kF%1jz_*LB1Bm@`P3cq3<^C|Bu)NWeZc+}1R=&x79%qR*eLE0=NR&{73WW2bab@h z_!4YhUTO+_2|x@XN|VH(Xv!yfLJP){5uTSINl7i;?@g>#VGrLoRb3FSe*JDR;GYu=9am7h;L=z*iR#`JMG&IEeuJ%C)??e4zrux zyKj^5A&mAgFs}n|kB`+@uRpRhPHwv=e7PvPP|isL%wuE6I&yMy4Xe0#!;i?3BJeDD zYRduw5|5seGRQ&hg3^MxT zS@|LRFi6+~(Q~ZTl;q^(%hMJaV)baTT7hS;X#$-w1mBGCdnw;$w{P#%bGtJQ4Gm%n ziIbAJ8S@5Me={mVz5|iXF+>w63?(Cnco4%2(yTO7=p%ZP?As=zhO30j%SlGD+|m!R zuIH(^qY}9;n)juoG&x3QU?dX~U{J)x3gb%zh~f$>aP(z18AtlpN6k1|1VAokk$zPO zfO2thCG6hL8q&@|%%NT1+#n(gfe$hcMMc3+VMPufI`9(2QkIsMzOr&~R2q#$?W?Gg z2MF}tTwOUg+M>=rn!38WwjJt+&cbk7U>(lrGoG=?CVqVXyo`*9r6eW_yXTleArUxN z1A&BRpvLPSPoICxW|M#oKh^-NcN{%1NEjd(vJ`_nO^YPc|3`Wy@E@g)XH4LV$Au8 zX$%Oswmg!_N;unUNg6dp5$_Oa*o{1YFrrAmdBoUV!ilMHO2xx!rt3X zE-ok2PliW5Q^-AZx&8l{J^V=?5WztCy8t2p=spza7a!CsLIq=Aq4vtYt@mR+!f?IZ z3E&OneLUlzV*}(x5;v^g&qqAu29&AyPoit+A{T4&v)?jwIX|fXG-cu)eOw}&!g_qY zlJzU|w%n&tgF^n0F$_JeY+~(W1AjI&1=xlRlcCXI4CT+rJCxz-7t{7bF!ch`KV@ArBkgjNVyv;XKR`|yk37IhYmOCK)MiQk_MIwb}u z^vgsU8g5fvA^nF}VG{rev|aiuuxkC_!H{AVxUDslfyllaP;y_MROR=Xw%z?yH2`0E ziso}rs$Me8%wLcPs!V!WWCN}NW}+Y#>kbMo690uCX{!$_#IYAIPT(Mu$xe5I#&;kN zWU8RK>yP#I3URk`Z~SOw>dgb6A7r)@%+Hx=M~K>tA}VHQy3SikFtVfBN`>+t_NV3I z0b%?}zSc~UV4|tLSxv}l@k10Kgqy-t4k_)-EASeid>p zJDq0t_$04hE~3s<_1Y)H$A4qcqe!KSX=}IC-o93V@PQ1~AEz{mSYA1Zhj3H;oyFYB zeB8<&x5E&Hjl!Rl&R2PJ)BY}@*?!Yp+vX!8e~pbT!u6?v-V_Y*M;EY%aA^wDTs*4i z1bA#^8@zme6J_|=_~d<5o9SWQmC#hZt?HayX=#uzMHkLzydC^ZiJ0!LY7Dvh*|&Qc zm&@nvGPoSSv4P5m-Bt6^C2R2BPGoPdslMFJ;7X?%%qo_(h~X*Rbe?93ZkDQGR9v$O zzonBZ)w<@0<-mz<+NpW6ly~NN}0`^x7ja#F|CJxYQ6oa`TxcAWZp1%7elklL@jgm;5WWF_dH1 zm)*(D+nmLV$K->#83R+E|8{+D%SAcl@ApAxKo}x)yyhvpUaS6Ao5RbM{Oudkm^E<7 z)+m~wQJwyN=OY~Ce2^B!NGUZjpcZ+r!Fh%174WR=wLK zu;uK!)p3|98rjuvnCvxD=dr_5)CS3IH(maoN?bKF;CdnPgk-m@j6t_DwaXF2%n!1{HejF^41*yd_pQHbF-v<>{@hJ95txSx zh74n0M_duIGOBU;<>iXr^Rl=q%y0+)T8oAfc^=;f(S606%UU^TM~Dv1Uw`$nc;I+o zvE~Bo*UB9@sDk|mIug1g^h>3}bvmKO5%0=fNo<{6c6w6j&q|apY zr3RO5ORDZPP-G-cp;bKpQv9*fZ_JqU^pty*HCQ~7ux6G160CC#-`5qE3C{@A*|OxD z;grp`3HX1(uw_efLw`cueswwN^zYo;L56B|@Tso32MBP=`oCex{!}HK|K8lgN9Na^ z^&b_jZn}a#sGltaXHIhPbQW#0 zSl!M9PBHJ2+rh(Af>y^?^{_82yKX&;T6;_t%;D716?GuH6D`R-t578llO zR9s?W9?PGHx3E?E#d~1MsnIJy>MFq>(^6O6s|-5Yr&+J8Tv|c|s+K;z5{vW^5zLyrzO)rRDJV_z9J-@U|G(n72oX&tH8ERKbdb#nae5m?c5nN15d z9y+1-4b@p=l-XCD|c$lE4B3rc|uz0(X#wrze} z@xa30OR!V%C<07|0$TukuDFi#g266vDCF{zi;Gn+5?E^iRk0MY(By@Khx*_`9EbU{ zQodJM=CyEcB7%xVcYrHSs0!se;V*sLbDDnGrcjZS zOZT2N1QGdk@bj@K>~>v!I5?b{?zLK6JxUHg1$;z? zXfB!T3b8%J@@*>Vwz*zQtmYHsS0%+ID5zHNDM*1Mngro8XMS{LxU9;mHdNoeL%)45 z43XDYZrxC#OQ-?ks!6H^TBvq|C7NBN3pGNLsZ=FV+41jbdI z(!@mf;yK0P3l(hXLhtF&Syc~ez6>D@_Ed-hfahsKWrJ|9^^`j;BvOH)B`?5J-x)|AL;@+95v$!shtcww6;yN} zlK;^q4jSK4VYyJbavo`<_|A7skP27vdUoB!`M%mjOlW?7cePW z-A}(N2P$FhWMW7GttrP8^6?#TMLc8-Xde{21Mv*m?^B)_N$n2)G|dYMEEl{RTKGx8 zKVgPKq5ilsGdqUpo3f@dZU|oz#pjTJ_dHllgpRq54pz8Is;^X4iK5D*n0$nLC)Gr- z!?d3=#R{KNd@*VP@0)2H)~kJg)hSKVrl7gb-mCe3RQ)Kj9EdwhLd(YHng@ zciNwP;AK2LCx}mna}zV8!z0=y?I~r5oY8LUuDNNANqLp}7;iYTJ&AV;o~uxFD!4tl zm2B}jy)p=2ZYODa5Z{^EfeBj8;s33zxb%97XdD9t-T^akx>%7ytLPN`t&Sdtdgop; z<|uQ=^IIVC@n;iJLdZzJx*mqL-{@3enpMursy@dqkugN2%uroX_k)=)Xiy_+%Y=UaJ1VAl3W#;qvET~9Uu%v<`K_aZ z`pRHynZ-U!p8ixz&nt0o#L`sJ$50^-^Z3ma{NS@1ss4&oi^nV6ikYj$lR=LXOJ=#YaV4xnI=v6unm96Ib z688LyVX9KEj>v6wit?e-k0Q3?^vDK_jneLWjiK|xwW_FB=0V&l_75~~#Y=n#^%TF) zXbENG>oRXn_hs*C&gaHlP0vwR5ygbwDjv`2o%{&F{E{Ec*MBvqoJD?a?^9C*1Q~&< zd4bh2B38O+m`k1PxGwJ1lT+gydZ_Ho-h0Qr5-ZQ=b_RL;=0DZBDJEY(ghmOFml`2s z*$17Cz$1qaugjn4)Mn@7cS%#2pi)ZuTf6JY+LNKKOxV?RaD7LWCT*PU?~_np&KJPT zkYLZvA(%h{7|c;o`s$D6Mom_D>os0n`Wkl5u@crulW=8d$V@sD!{_5Ka>0!AoY0oi z-K>49&@4=vx3%^7_n$8mErQ466bqfdMCsI*GqaT$oC06YdbML?7w?0WzNuSyvc8v1 zH&z!M_=ft;Oet^r7d8E>>15pE8sYh&Er&#A)S_T%aW<#N~pjU5+ z19-hzsf{Kf$Y*?KWq&J-ZihSU5n}ISLIgGJO9u&D3&}hs4U(zd{Co3XMl&F>v_LJr9W4;0ufr;WC7g|~Ur(?;2 z<>7oZ#=CMn!>I+Y=`^2nZI2Wr+N;jCoIdIKKOs0o`*e_jco^tKNQ@by)~-1wf&l_s zLlDODyB@m#qU)hT<^tD2>ka!)F2RWQ#My@?+bg_X=XSoq|LF%`Rrcd2c&!LEFZBl% zUdC(>>g7enKIwB8*}LUbC7#j^Kaf761~nrKekNIqTy8B%Qz(j<4k=OQ39!4o#%)^1 zui1s=6nY2SadN@v2?};@rz$(Nkeo%g)ZIv7*m@lWW;u(v1!iW);g8*cVbR9EY#zaC1^3 zafz|nv#SQ2MzZlIj_I==g@@g8s(lLl(IDVH z84!2x{PMGoK2{8Bh+?@$f5Zqoc z?}pFBMt$gCC56-j#rBnRZPYUUPO;tvfhdpVF^yf>+C>)I&Um!p#R7wrEh#X|Dr#?8 z*yGdLM0R{UmH=sQ6wXEpyiMe#edbVHc!EVf9HmN{lA{P1dydA~OC-Bs!jEOC;eDzELCcP@Cd}OH0dG(ou&xs{L&e3y^yI z3zCq7VFgcIxt*EB0r9yq!qQtfvJFP!^f_=~q_BKF#=%zX2_Ph&q zV&7&1K248DrtIAOS0Dh@(9qx%Sao4j_+{?)$cGg?iS3usJpmLV-da{l3cHA67D%kPJfoP> zpuwu^2UboC|4!1ld^szfb;D_BZ!4 z;?YUKBI|sMjrBRSwB#uKV41wMG%i9OXV%P;>*A57(4Fr;C>k+K%Zh(YsLJS^0-}ak zr(_U)a`8JdFQQ!2P!&Vm+)eRGO2yUH)tV_zav|pt#45}2i4tTRsNo|1N(Tv@{{xkj z%tH#LX(_~q4GS&TYp}|;N*m!*V0;aU0fO(iZ?*G?pM|yxrETUs+ zDb`~$pHqUpt+Cq%)R`RkmvQ3QuCfY9oz$qb`0He9T4KBDsw^uz9V(=uT2aBg7-h@s zi4#DoeAQyqcnkX`d;!&&uXD;9V;58NG0N9lu~UB-QG0&+e@VkV&bwav-&k%oz6Af% zQT+ebspzn?39viwF0Q1I748pg7>r= zgSM7dynn09o6VO!6=J6g!djcDF_-?vdKy=j4Z|Tphf)QOp&TW#z_d56KjO1I0;e`< z#lR!KwW3({_@rHTGu?iCA3Mz;^N@Vbgb6?9LiwKZ{~ttgTI4S~Fo(TyhH>@yulZ2E zuiCw^Q~(}l1w9mK^D->}WTkuA^Tq=0=U^s+;1zzfW5X0!^pKCC9{h^`!Vd})V^2hU zY}LQ@isrv6fUGV=E{oLbD#J(Ui)es1W&T74NYew$QUNU}aEOjg>^KpnQqU5lvvB|i z9YlfUf2%g@EO~m4cZ=C{e~nJWj71;%g$m-$R~b#ql?@rIoSmT}XxXu*rru+ot5O9| zf>V}lO;A3>Uw{pmps6yGK5UujZ@TNaay?$QlJ6e<-4d=#_v1K4b(%#K0|V9$70(5V zXl^F%qm+wov?b9Zj9nz6fe^s**Y5(D?a&ML~Qz;Ec(T1d;W zDRTZDS(9%Yh|WI!1>|~$-JH6xDG6~(01H~zA|qb*W)&C*dgg$-0-FWdDLksSSq9xx zQ*&j@aTp6^tOg`<8O^NNfIUmnjJit_0HahMb?CW=1uay3Oh6#XS%6Jc4vEZ>v|^&ul`hFW2?AMG!!uVH zWUUJhdnVH$s%F|Y%r0pD3x`+XxJ4!cTZ+?g76KGRO~>~L+g}_D3|fot{k$CVtM8s} z(yxQ1zi<-W8khNdjYBL_m;NQQ1eYwP%=p5xjg2XPX+5PFxBMr>fw{?ha}3|c;9Olz}7=nZ0MDIYVp?5jaFuvNw! zmbT|VRHkF+I>!q$9+g})(6v7`yW$hm@^>Nu$@0E)3VM+doDT({9`N`(U*3>Y|5ehkFbP&k!GH{ofx2I+APm|s)$MuoFxp-u z3Sksxz9-zQ4zqeIc=`4W0D-rs+;qoWCdFGfE^_$If(^-c5C!z%B;nL*^CD?ysXW`( z`fM};BVK`p*>e5t^>ww$BT?uFRhaHPeBYMzLtF-gCDm|F{$pl{#zwe=|D^?BGO1FZ z^_d%4v{$jMx_tU}(k4>y8KMY`!Gc7d-G_<^a&VKM9|cY=gyz7ZhIo2>3Uk!*d`S7t zdltJ3Gs&8~_aR}K0y?dhC}p|4oe?<-^WwoJmJlJA%V{?yN*j)=FIVI_6TwEyHqHN_ z&ReMfPVtDJ2-T16@6^%!>h=CU&YS&8?g4|~sy{G7KYEYfJ^eHy(l8u;XQ1h1u_<%? zN-i3?M^h6DtkLLTfyGZEPBaoG=|jsh$|fxWg?LAEkZBJ4Ssox}5@`3!SAmPf0`K96 zG^*7H@V^=bC3H`zACmS@>~SErfrAF9%;@DZak*Ord97wvT_*}KmhJL&yl|H$F{K16 z#{WX%oM8OTC^pI@;Xj3ig#{)=fu%9#nJm5LpN9VNq`!&^S;PR{)xl$*w4sZOTz+`_ z%QN^FvH-}-+CzG~T2nQ%1Oyu1MST|K@>-S2VoZnKVRP%O=VeHJEB({*bpr!`Po!`*b7JQXQ0}kyOGy;4iUa zRZ5~IA&&cM^TJtmVL2b)&tXd5)^DoHVt&n7dq(J0)HkD=w+`vsF%KljCPOf>sQn4TX9m)^e46y4L6g$+HS?*R^M;mn<0w{Eo*=--s^0nVdW)9OzQgE8f6RUV22b&;(hz*Ig z3qA?uZ$$g@l!_OrDP#H)gU&GW?ny!m_aT#)Sk%ch?qz66tIO2>W!q`%`AYw3jeXaD zsatX4n_T(?vX{76iX>bqdWE7(Htni3*>_LcbJw1rQwhx%2RcmrYVa9p&V|!KA`0_+ z;Meja9LIZ$F@5|B^D+(x-87o#d)syy8uT>~l!@0t>+TKtOql8e(b0Jrv-)<6;YM`h z0;6ZCP7DO84_teHPAB~FnLz~#IAp2Lcjg9|d%)w(AsiCwl86__nrNo@qYYi@976Pi z*=4oz6}8MH)`Jm`q|ZS>tx`i{8HQcp%rZLOv~{{KMy|_>`)TAQYugs4w?nd(NjsKx zpxaiY@^>E1$SU*wHphM;8MlYmS0q|X(nB*Fw~&0X$iD@HX5tqrJ4o(+P1~6YmC2-r zOD5$A+EE_Rs;R%i+RblI2O{8^ZvWe3XkMiBuV~ix*z(y_)(XbTWnPM`9GPvVb|v`f zSO4ouobFGYrWx4f(#HI~NQQaL2=;{lTz}3k<+y zlYvrfvziT4uH`;o8D^RBE+#YjOj8}JlkUT2+<*4TUON_wVXWD6HWq-VT9nBrfDe@1 zn!lT)I2CvMzW!%{z@O92M)@C4z?twHiB;$t$>%Nc3pbdKct*F!K3|2$qk-$V-leeF z_a6cA`px*CBCwU|cm1GTo^#4?U3h<@t~nq{_;`FTPMirIfdxDfv|4!JQ;oc@AD!{Y zP4sBA+`v(6R>vhqLfv%lMO)`bo1fa9cdRSsJckMxY{3oHaeNghqY$bCQJ-x} z3`;>NW!H6V2=8>|?Y72qt9Xi@CXPUJ2~khoSE&u3aI*8Pb0Y+UgBPCCAA-^nN+wA+ zSGqQ@Ydb;@`}-$rL2sZ??@}8%EsRAT)`Z~@mco<-S%s5MOwRpTCrji>jGl@8xOF@- zsOkEbp070pt*i`4q$SE_ZW($>ltB=>acWkA^3LySkCX4hogVF=5K0-ZL6+u{^T5P^ z_zW`MG%pLMNm0K#bg?{BB*ep1r#79WM*HR`jU~s0I&prEG~Q!j0^bU<;}A(Kp|^Q; zk%6mjbS}By+B*09u26`(&p|Y5)APK_z&q*f?RV*sv-+XnKY506WZ+_r1c~L`xY#r5 zW1qr&?JpZvhX-|(xlfxJqa0h6JVvSHG6E6lL@n?<%pFP5F=>YpRwKM$J?5`Z4eMN9 zTe)hPIW?0AI%Zi0M@iaaiu`9!l@nlaH{7o0_&W;o&N_t@acN|p-mq#j$W#)BLC7RB zbpc@IX;KtH66V5K&3~LWJJ4=X7xq%+`LDMQ-{*OTF8Okl0NarM8wYqC-r99V6MeJ-1(ZOPCII^ zqu=w5?^#?AQ)nsd>4+D_6ej45jq73H+=Wsq-RTB55b3~-_9$99ilP?ASCqze{I#7p zubKZ`>pcUIDYEvQkiez3#)3kKNO_3sAPn}O0w|bF5i|=yXAmtBwextj6@2r3jajLj zuf-tzTyRCt$A!sDb0Xa7V^dV3ks>1%GZUyjY&@Cft+#YJ@GhaXa>n{;xk}w!o$B^j zn(_vH_M&oMY7RXF1L$m5l5Yd##u%3q(Q=(B$(97D)}I>omkHpri*y zSDv}?G8KyCDKzmi&a~dfP4ALz%+MzRyDqqWgYPvg*|@Uvc#pwB#!T7>PddSce7_J7 zk>CfY;traR6qSe-{1CvIjYKkc$g4 zHf$9N#DhLlLg!p0iZRL!&6Z`Un570nf%jcuo`0>n@!srcJ~j4c60FLjq8Nw~Nv6S) zD4BpVjz`yTEVuX1uY5(WGGMjKl?C|Z@7NBc_HOY>_A3}To?D-|%IhQ684{9_!NyzQ zuadImq~O2K)KW*@PHGcM!t1wtV?;8ls0jR2h+S^%G$2yI52<8&t?Fu)NZ#i{Ix0zv1Cj{qbH600^OP*h7G185MhKwRSf`z4>)&NCvRE3FU5`Zbxu+E zWRc>#GsE&w&&V=Klejf>JaFM9uyf|VQVcnKmazm@vA;GaKc3D;7eh15xV$_*r<&I^ zT^7)r<(Du~vk>9YzVdNxs0^XB*-BVM&|YucC%*bI8SB1+9TphoCPh9n7({q8C0nBv z;lacq_HoPPZLP&jt!3u4y$o>aL=&p1Mgtexf(HX`|6mhag8>U16BHGsHZE5e`i?dj zOqOi*9y=q8e?}5@sBe^t?_Azmh4x??TR5@5bE)`ns!NF8g zPFi@PDo??7akVX0B3r8`a%AMr@*8`_+l7jIH&)+gfWQxTuC|W0(N^o(hRimuX9gEq zX&OZ$f{>^H&`K}f1PoBn00(X7=Iwr&){AeKjYb|RrgUxTpO{k^5NW0LM*3q-w$PDi znc*wih3$8!5g0T>Wr`JUJ6i{h3#MPW!06I#ET;Iyu$g!cX$aVB31QCY^!Sf^Iy%j; z#tql>?*HVU(Oh`com6x5I6HMYdyE0k-T1G+XoypwVUZ-}=B>%HntO{j#?4$P1T;F1 z0%BZC26i(&pH{_qXGFHx=d4STh8ir{Gs5BL>*i_eu5aX3U?IYJ5Lv7DXw(mTpb9PD zPYB}|Ntd$EZAt=VsYX8uxD4lWat{$b?8<$u%EwlKH^Sm%g!=P$#C$_}D4 z#9azQqIsr&ghqZpQmdmv5zMe!PQcggb=C4eMB;%NLT7USizEH)7_W87z5|GV%eC!E zQlF22GKAB?QNFn!-QjJH%t?gY3k;gJMglaOc$>_ouHK6;kuNy4Wr*-?8TKB~Y-^zW zKZGrN>$Fkf_ZXSgus!jRMNsyYTIXHv4ugQ?G|-niRTc`LWhRE1&=`aXBb9O$ptFru zwJhkxL6tFv%4qDzz2Ok!r|QN@9Q36U?++^`1B9<#_H3=4ym z{v=JTe9ya@7+tU;8DNd85T?X1x?-g~L1OZ|og@2^eQ_O0BqS|FkmJoP=*slQgB+?R ze>8l%?|N5i`3nieIK1pR_4}RAy=kQaCAF@mMhy4qimkmJKVrpA%CQ(K*`{99Jfe20UMf5_>D%Tc>7) zS5xRq&y{jqgrwf5qLy*Z$5hWcGXg4AA-)o@%B*xFmbJZhtR6`K>O=3P*f#zO_kw)i zKZxovIACxRq=b?6;1IG#%&O0)q-fNeY!g8ljky)wp)lz&D?tfrgIsm&wTN<@iip?U zbhN%U8OBbC(xfPW#tfSYT2c;fBUo}Wg{|Ayg`y<>e2jAko+l#!f#4`#(5ip=Nz}Zw zA=JsLm2TfRfv`+$i+(8`6uj}`61bU*8%yl3hP{A| z32eYw=GmLTF0W)i}I=B$I)X)J2j$OU-zs!*mcjfYBJmtA%;T4bqMuOAzlc_E7=t}JR z-d1qA`yC@;;?0_hIkehbnK=}6&ciuAvcT!n6u#d>6Fu3LUCksrH)(P`Cx)3z*Y?ZF zL12b|uKK4toot$4pyC`nPCso=Ft6ZC577qta68!tiQ>jss``oA@X%*C@3Fy8s*USV zC-9)`v>%8NRMX^JW`jq+2m41OgQ_Ffh`REl0p*lp-*s^hoFx0QncC3U^%jqy0gvf0 zn~j1Y%`b7T2`)6eJFUFBO~KMeE|^(lz*_q62Q?AzpD}iH37u~@d(}Dl*7}PmxOxZ> z7|6s{@I8-{D|I02rOl4;$j2oOuq#A&P){BJDSX;dP8H;A5$-%3TfMe+eMuYX29QcS zlo$6AT6~H^UisN9wprPL&LYVe9!SjS2ZaeFMG?6#-D2%Bo_bJUHd7KIEpV{zuVH}$ zQ1VUJK2i4EICG}r8_SQT7?EDA3Vk?@$z^ByzZPY8V)`sEGwFjfQ`th?o*~N z*wNbF_yh=Tt<&XY2NZFv4eiCA`rqIZtH1S;v;!dhg;AT@&OZe+U&K5BTxICF$0lLy zvA1xsiQ~9};>LdD2YjlK0zvmMi!g0D(Tjz(PUv8{@PsnPm1#<7v+6ZXFIJ|>Py=`Iau;aKV(QX zDMH4##EVRDNKBIDDOec%wR7V9=r8UA`5>FTXOQn64Nxg(Xr>MXNU+SI(h}{O$c*;H zde$2}&v{Rf4es>S-UwL`j2`}++kg)bj~l3Irgxtpj_-)1dqRG`7U^oxY4eE;clV7O zA48wJr2!p-Y*g1t8fg~ZMdxex-xiyjHFxgYHzwkg#@I1Q$JHvb@+d>T%Yn1h3b`mF zkRzI~tgz~fK6%gTa4M3!uaAB_)LBT3)D0O+eCtnx4r=4t{JidfY}4WtW_m#Kk^i=9 zbc(b``1j}*nn+FDf=PIqz+o6*vTm5zu5KS#-=sY&C+~1FM)nWE+OfyrNZOHxi?<7b zk)|<&htPGQ664yLLdXb=3>r$$o3x1h zLklK{ zk{*O%d22viu!J#M-a?dXk&3mG>#3nXq!4RlN_tk10wU4DwV=q#&MMKA8((I4KXUSw zK--xK0_I(R+nwl5_cU1Dk_g$b34G5?H-6p`s);y?*J%RYM5zzN0|Ya_!DR3{`W`mZ z*G{>E;X(N1xS|qa;etMf`cjKzk=~8ONU3Mg<7U}cp+WbX-X|9Ao`Q}Z6kyaX5zG39LGBDg~-^FnQT|ulXx+ouzZq{IW0RFNdfD1px zzqId!PUd~lyPayvaQCG5(XgbwN&LH210l~3ZPOhMMAVKvIQS4cA%H;*Ltdkq(aN_r zJ5i{{=)oL8WsbIq#JG4t@Hl72%t1$~N0+443t_(tC)>vXGt>$zNt}jvd2J16@U^zs zj}^!$tYlyR_W8HoTJCjI<&K}qrzsuR&68H7teIMI5S0Tf7d)F!5|CN}zN~2q8xGn2 zfl$h|oam@Y@y2La^M#?mCyt|o2gH&|C7M}UO*aV`9CIJx(`_|z!*E5>D0j#KlHAPN z<^`iJtsHcvg7WaBaKb2zJ1NUgwd2SnGI|E8ZNZ~fHlkibnKJnOvc%x>bCZ92jKCNv zLlM*(Q@N(3@y|p6ZPNjmaURymuwk;)F9?Rbs|#7cAY=7`EMoz#!?W=ysiV~{lo(e^ zT7NN5C|dHs#?#qKfmRzuWSB6TQBLTn(LaPl&g4UM%TG0z<}G@W16HL3O*$))E3yU1upA|7eje&UvSJYkGMm7pvnBgj=h~ z;ml7rdu2RyWwF+y|KZtouo^X%CY%UUl5zn=(m-+l%rMh**1LN}ybPc{ShUQ}UVhW_ zayiJl!zs_74v17_iaI?_US3Tp`$x%%?zB0n|{7Hc)|PVXQVlS3mGBz zc4S)$u*`RUfqZHzBXVPLMRoQjH*rn=zAq1TB2T&-@tp&=Y>_;LZ07qu+Dc-$h%XAvDUQsJp|h=sDe|vSW_t_i}Rms z3jTrVn1l%CJQ#RYG=wac)*BZ;DdDI@PStFUQGjv9em>#f<%H)_o z9-fPOSb2T%qv9rOYJD&|Rl7FKel?25%)~frZH3I!cyI-oq(_=ip`kzId_yfq z9F6o2Jabj$#n8utR>1+Iimc`J!st4Q%qh-^9cA z2-c@SSiHP~cWf9KbV=sAt(%sb$P`IrVI}Kvw;tUX6Uk$bd3(6^*=@%$DE4B?Dr+m9F31?(imWq3{e<|BTsT}NNP{tPJoh?et#_enq`9szTj;b%LZQfT82l=*!HyD!xy zj^H}b>2={vwawjQd)ExTgo=A5!HklBSKHBm7#i7<^{;5q>Y2#I5d|F0oDNAh5h`|$}{M4UB94Sr_3O46hn-r-Z z8&wA5D{?Ty4~6&Fb@ta?di0&!jG%w?z%!wng44l7s=3SZv!(|7%BOL1oBbIdMb(Fv z<7hb7zN5UCm#nTfST4v)lJ*Fp2cbsg!yIySzvF=lQtdz1EP3Bhg#*=+>j? z9F7(2jmkzQogxM)KPzZ-80jg^y>kGsIErM{Ece5Q-_ScVc4U?#>-g=JJI6QAY45Qq z$O=XF{;e8_`0NZ)4apA6Y5kK`) zc30FQ!EO|gF?aFEwyVli#mG3m%A9+1?HTj=~Cba(IWEDUD6f1~I! z&c1N6h)=tERW>ef1;|0Ckpm?WRpB>oBbMbCA^Ymrt3J|XWTd=xqZR(wm1-N`umNiMt#9+0WcsDz}@8j8x>sHm^_NQ zKBK%=>h|4e&D5B`CrcIf^6?lK@g~Owq4Ag#Mn^8&eOc7^JQWr*cYPPX$o_0Lv++*+ zq9n$~Hx=$wec!6D1=x=AA<}%)aa1>IiKv8%7NSkp8d}{CWZsHBV zw4L%F^d8xl?da1Bu@RO++G|VDIeK)9i;39s)hQvt3{~(p&rYNc`PGX&P+&j>fS})Z zjducLQAR&ao^z!&8lU@)Y}{_*va1PeTmc;_Oec53F^532We**1&>YEB6iMoJai`tG zAa*JHphaG!;!p$5Xn~KZ-v7Syd^*ThA8EZR@PiRT%%R8WCYy>N0lbA0B4%jOZHio` zC9^(w3-v7D^xB8!eugF!MG9@3mZ`oLa{TUIwHW3g z@cIUHFOwi5&TWo|7oPvRi_N;D+@az*O-C@#Ir!vL* zdK6WO6c!x~M`;J7I;4*qjrvsyy9QgB%`l+dHh?iS{r%xU(^TSBkHtQ6dW}P)sIc5O z{v9>%2PVN~sdJFQIM}b`tESYcB*yGZAg*Y7n%3=0P>62(tR$AZusE)KP~nU0cqVr37|eGnm+wC_YWQ<8v-0OP5akOdB?PT z2UIm2|A2V@iK!}+EMG(}>hC#)O(Kqp8Qar(Ju1VGK2SkHsnVilGx#D|FAjVI8jhv< zsRvavQNqfob&QOydV}cj8#50m-mFSMx7ZybLv?dz5&|C-F7185oS@RffKuR+< za1la;N#Z67;!)^{ozF>cmlpMv-(Bz^5f+vtfEUA!W^6Tb6uo(ko#@8FLr6KBhmr&5 zaBi=OG!&uxhLx~$d{KVOdV$Kl$M-z}MduZ+?hFY!9a`{L;9ZL0vDEnv^NFrp*Ka(4 zo<1VqU;WruK9PFGut$V~l1SXjfP4ARzg2#Y4|bqlW5bgEBQ)!n;2qcl%|n!(w}-sJ||zhjC{as%!jdld9#h3@mO4(4T8qgrY0jF#6nL(-RQD zo6N}+4iE~dMP3a?;uPI=??c@WGgtBrIoi~}x%!?p>Z&ouZJF=9Uqoo=t-2?syU%sF zsQf7#&FTl;_5ju3p)Lqr{H$hu7M}YMrI3lb7~+m6Xj_BjzM+_sME+f#<++`atPJEl zQnvN!x~8YPH+ct%t9!fn^HDK<7aaY@EyZGogxkT&pUA-+gP5C$T$+w)CHdhmvhSHM z&!oq$-7F3}3W;mC+R;e*fIfr`&rRj)D#(lPnbvMk>cUz&#m~Ef zzHU%N*&;;E3WlH{@gVHF+tkH$s$!woKZuCs4V9knV1Rb#srSNUn~Ao-_hN?BisVmT zOm)O16Cdr<<~9$DJuxj(84c`hUd!cIX?%Ziee>_D5*G~q0K1ak*r_-9vf7>O!AR@e$VVJ zdW`acCLfMjLpN_8axLJj;47HDDjD!UW|boYnk~mvHATBaMC|+J;E8-C@bHq?EI6;E z!_csj$`J)cuga&qMy$sYH#m&fnQ-IGLvcqye&pTirof-`T86V}nJ zG0R4dpk)NKceMZbSQ>p;a~O_7{8EmX#*N{HUQ)EVL-S3GIEk+S^bVhd;=mO|IMRFD zrIS?CO9*Y^^5tbbl#HYD2{n zRuhg7(v?bDwz~I9R4G2oXiYcfKj!+2$#`e(AUGA?OTXvqKfQ~HGdc{8JRR3|vXHkv zkS)1FWtjTqHcyLbK_?`!fH!<{Nq6cbaaa$n)no7=KdPv_ZX5cD#8ahp74*~7+KJW( zH6We6&K3Vr0%1GS`2{{RWGnQFRuqTgvhw0MG2CluQHuhQd zU}zaOzK^i9EaQ|HtHHg3X*8%AbZ^N!T;? z#V#R7d#chblQCBM_iLPIHn&+#T0c4dbypMRpba-sgB9>lJ&Qn(xcC8&w|49)9wb*W z1FE}y11(dyZ*d8sc*eA_kq=7$AWBM5cpn?2K%x`Ih=lm29;q@Yn{T}Eu9`er++u($f{$r8aQ5crPZu$zHTKnGt!Xl{XJF=?0 zO~5}_>)$0%-2oayc2Q5JWK>A!8Gq|fjklPN=%^ntbybI}EY|(GyPN9?9bdZj*WE0# zFBXckr9a???5B{lx%Bkaev+&|xas932U04fs$_C53Hri?}BFs2r2?etJs zb=c9@pb>S~Gik*9Kt~kFkD@=Ytt4_dPB*YPGkLkH7E_mrqCfpEGPoGgZYZTuHk?Ic zoz2&d=OiJ>u-__CpT*eGoR^fCQZgg5Vzq`FhIc|xK{ab&FpKQ+3BEO!Y%~I%PulFn5;hImkE{2ppQkd?JUN_m zTIuopomvL`DM>2mP8|*- z&gB@a927}<5sAT48a9) zZ&QKN%+_ZMC55L)s_~h#HBybsb(*ztKGR_H?@98{6BYtWK2i9HDI&9H)D`0GVYi^v zm0$nj103`?F@x2@Ta$*OHg9<>{4w*O%=<@}gn}sh9Tkh~=ni@-KjOBM@Q;aTx;tm> z;nECwjG7zjcImJ;xWsT_(9lt*q*@FSXWrtwUZbo_#V79E`j!D2OMynidms-wdFnmiZ7I1x3@ClV}v@DrDq|7E$u+B88lO z_hw^M%1o`(_By)qeOgY)obWfiKIsnM^oOJAJALLa7X@xNt3W)`?WZ{7c101W)X^1G0eH z(aR%RHjOwhK(Y&CAEl}jVft3nvd)eRp)S4-HD*3|s&+ z1jOlEG+4P9df!Z0VD`fKYpkjQmphwkw8uK_Y81zk>@_WKL!6)Dbsk8?N+6Y8Yx_qC9VZsl7yIweOiQ;VNetJ&v1 z6x4;XI1`mqoOr>KaN^h@#f`|cfXh2Tqib5*B&(Qb8m&Do2)`@m+5Ea@q?z~8<*Mq3 zFkp+3PVdVfp-BeQZ2^3_N9M9t)~TdTq&Rt~l^vV(75;#g^}F;ky=I`ks@7VX?-R}I zNo+C~qx0_qK@?i=uDO#I-JT03qG0#&L(#;_?{C7?qKinau-JBiTve>6O(o+G|In)r zcDD|}ZsvOYKDx3{cVrH&EX6cd0xdxIJ(>b}%apZkfDo(W%6z*jM(;ahPX2>B?vfG} zt>`#EU*GJ;XYB1~XF<(K%VhkXVb#u2+rpS3Ll69hDo9Mxsl)1K!)-Lof@GK%o5@|I9VNp{1C%^+6r)&F9-whxxZTj#8$CZjVjb z(RZ88`^&mZ5Z9`w0a4-|mE4`*S)K$_@8@z61#%vXUsl%oW6LaGz;+PtXs?c60jCJu zb^cE&{G zj)2jMBevmBynGmc5b`3%H!H<|;sKeIq(Oh~%!%N9Mm}EBT~Kyl3>6$bN1q^rk%nTS zeaf!wR}C9?tE)z4@HhPhrH0;ypEhE!a|C=B2|wq?zF_vz6^uu-G6QI5(=DdQzTAU0 z95Iv+CEt&Mncq*m2F$9g9d1@APq*?gq}MMfXu`{bsrxs%;#EZ64jAQ6SOND6b(8@_ zpMlRd;j@*|VxDmZeUC)qDVpi@zs|>Tb~KIhXC+ng7h6BbC1!6-@~u`>E5B7h?*f|@ zb8SJcL{cUNz#Og?h6nDshu(X=x2OHiFOS#oo^P*)GxmNPL)`R{=eGywQA0Knzm;V%+@t6xozrFcx6Z;Y zIuB6C*r%fkmBu*D#u8P+PHCb=(rn8fqv#L5+4=}D=4xnYw9ZP!44W{{yug0hxpi=6 zyTO6^;+t;UHLdYQ7sU#}#7WmM8)y0H(PPO{I6Ow_!_ruS?-G@okZNmddxs0p7N>nW4f%LPvLS zv+-q}?Z2oj*`n_<{v$u~Bf2ucnwwKP8AoJHNj;S`jG)Zg;&QXY_2qi23x8;6ASx`+ zxXtp|(!%1{b!LGYbMpJOuLGx1e~@up=;YFkt_r3btu-s0_BzE( zhP`}v38U)TX+t@}m?8BUbX2YrY2!J2xx8F`)>%IDi6msQ6y&KnUi%FiaNnv+uRpsu zenf-PdqX2o>&G@Ml8e!tv}2N-Gb4o7>34C``gll6 zuDmey;&jvO{rR0da7;?XYl?l%DjSjD zM}-^v2{VMLobNVzsS~Y5;|4n-evomTkVxbjuBrXjiK6zFUIkTbsfh@hL9DWK-xnTx zwb{WI{54mQIV&!c0&~dtv0`XC72v-iJ&8q-eGFYTzoT~6#+=bZk}=?Y|9h~J7`Ef^;WeEbWyYsEBt-0aTf1`1Ohh=Y;M`KySaex|H zTBq@&2e)6}_E2ve1`b)-6;)OaT|6+Jom%<#v@}@@jM$}Go9I6A&*$Wjbi@Y#-Ytt~ z`AIDw9jLA@2qaeRtTR47JUl!;KIUN=83h*1f|c0arnA_qAgi0mTV-Z%1cu0K?7zIc zB*h}!q#YlRXzJ?X+vtcO=AU;m4tcM~6Yz2>+t9Bym~KRXZOU7-x^2zP%~{~AuTL_* zbx_zNt(+G|OxLNYM`)|4_)wh8O>j4&4u+_);eBTrPX1-JP$tiGe~~Vjf+GDe!e8gH zN``%WbbrJ2E7>~B&50B>(-KFHh8E8?vmXA(qLs~`G#WVDqns)n*%M3*1O)M7=W^@h zAK}J{UYm0&ax>i0%p(U|ZNCx8SaVdoTB@psb;M}d*j(98O3Y^S_;1Y2%$^h#XS;)3 z^OzB6Ct1A{Sc8*XC-ByT@!vgVW7A-vLI*Rz z7n%>c`83Pxfk<rt;zMUu+OC~sAs z6c4{$a-Drn`m{>R%F1%dtH()XXFqneso`G`#zaS_%*-gXZCow?cuKaRj^#_AH1BXK zv&PeHbGYJ}V|(1&?fp9dE(SfwWgl4JVI>Iezu8O<$K zEj=7JI9QyyB;r2ferUnaK63J=t(rI{k>mMp#`TaY_t!Si zt%@i1`g=~$wY>+P^x|g4{4hZ|xr>13y_xRL^nTh@?Y$!iLX)8`4Ez7`(7>gUn(yqC zNCTI@y(vv-3_}ZGojdIMs119-d=K;oP`o=92&03G7sggh7Un%lPWLxO;1v{X_*4L%DL(g_DG;gRgNaF!g!Fh+>$C!aQy} zn%9Gh$bsJ*`Jx+yj38sJVwlC*BdiSl?2hk`Rbc+23yp(SBNKxK;`m%ePGnWuy+X2H zY##Fhq9V0%^65Qr{KDgjNJ$4^WPZ7 zgIeqA-Klb2^tFujJC5#@pufl*_0jvyR=~Kq#T}IijO{BBHU$XLwN7c%wE&*qt)=MR z(T@>gA!+s`!;_)^l8KY3*2PzKRr9g3dAP-`qJWd^kDDFdud3giLM00{DR4DXR;1JU zBUL)B0jcc~z$g%!%Z@c=4J;`7e+2H&q(8ax?l;tCWx_8kNK?|2q*X!-Ql?ZtCSV}W z8F`$rN{JnW*^u%E*DF|C@@0;t<-7gy&_o&--npCMn|@c2S`(%Qh_j;G2Bfpbx#+meNPJ8|yjSyW}!TxvpH$r7-REP*$;1wJUP6*P(ylxF9Ymd|s=V3~@ z=i%?&q^mz|)kkm1#Fm>Dx!~ca>tjGAlR*6TxQ^ciMYZsMeuL9K_vftnAGb42QxF`O zFvDE8-e^(Fo8QJuQpJ@FLy(gUPHC1BJKXx{sKuk%cet?=(l_)a*_UY8LR?rsisZ`H z{3Xh#kx_gJMN`}7nzZ^fC04Kt06CfV)Rr^idNJWMpJbmh?mQ2jFG$F-Ba9;~ z8Sso+44XCebm9aOErW%CsCof7`@t#ikw=t>{DF#Cm>J&|C&lT|18^1>7M{E zj7Iwb%MT`jTN9k+_#V*p!Kp+n56(;vRBmHZQzGGVGFnQb|~@Kq~CmGD#2YEM!Q*8s+PO3u(0JwKP~P; z(CZPr&nkg0#16yQ$WOr2%{T?CrtSYko6a&qq7>X5x!h_Yx1hk|Xw1b9f=;l`5b!81 z9YtGa_^)iI+gu`K*-o@|obY@>qm0Ew5z(-oE0Br5(7xEpS4GYToT2c@z^7 zDuCz0REJV;qm#$5X*o^pBYY7@RM28-5bQ7|Xd%gJ&RCGsp@D|4CjPFpr8HPr0jr8d zVd~;v8O&rXR2neR(p5^D0$Ykfc1x1mMC$>I4C@Lho5=Wn4`zkKf&(tp-^td^=>-6B z7ymNu5a;XWEJ614bknpM2EYIzwRiOvy6?^Dm2a8Y*?(KscKav|c1C=unN++T2FMbV z_}kX$ejzu3yObp*sVP*tU~88^Rf;!-oUQhF6f21#6$hWCTkZbW0+T>I0MvLi@>=HO zz0BlL+g$7?OH>6JjiLZ9oKWa}{# zdL<8(wJI%*7lJUUE`mzH(K!6XX7;maznJXi7)n-_1E(!@mh^ld-pVzM*@=M_V(qga zhT(*P4-#`7Y$;8-0+Q?CQ_16DFA9?_gJiEkUnY#edO~r)5ZbJFB1tR|P$=A0(p`RF zNslD!$7;F6S}#mj+JIF+PB(efG!s3FEiH_igo74L4qbz3ffuCJ{j>SGn)2z{1zTk> z0qQ?v8A;G??x7zozX4DQ1f-CNh0NAp;}znL&r&ZZIAb89Vg7bdh5#VE`~HB&uG>tQ zg!GM#H&WH{c~OEREv-dLT*zv#TN`I{p3FXYR|!@ZoBxdzqb>SxskJf@!Yo5Dc|sIriM2ePqpdQPY5beE*7w3#%$e z{zcjMC{fCdBatLJ78XUmUDI@%=FnZNX1QKfV`4VP=%o;(P&Swo@*0n-#DkZ;uDSQ}b z^(rWM&_7k@gd&NycF>&VLtwOmd;nm*DmqfV1nV{C{4r<&e$=EyaNlBG@BW&PgY{H+CY`+F??rPx z^$_+XJE9RX7;i~@rGaXH_@ilvQw;a3 zuEq0JAIQ1Es(H)S#wM8`H)=sb@k-Tp7zwanO{eWSwpyH4L-LEz2VjFL&>83TqdReZ;f<6S4hh z1Yq>YMcEGfP*%h%adj^1hV)$xP&Ql-{+P@RULAN}JQ+b{Sf?Btlsa1fA0!AgYeAtP z)6iguaqzLq%aA|E4FLry5PHzTF}MrvZ9RS#ZJTqvZb=-TSYoh0Tg&ykejaW5&1oX+ zG!(gq3KkAnRbIUK4*kdnC#IBegb42dR$ufzS4X02BB`p-kl#VE%lHw@>qxsnm0TA^ z`^3-nGTRhNQ6%C;gi9sr2pbfABR*Bqt`8$(lOgQqfzzu9k6dWJNiVD&N7?AVl7ZwhbKR?epWmdb!Ws@j8m0V9j1}JQt5A={?&!_ZeaJF4z zd|TOwU!Q+5XQ3s-pkB(NL7Ad}fEc(fs6Z=9Dav{cau59WEu_z^zXFBQ}!F5(;5hko4;C_k` ztAQ_wIcJ&WHXeoYKSuWj+|qeLog=4x?T)_U{Ghtu&8M1KVpq^KzuRjur4O?4+>3Op z+WyWfV&@ZeG1M3?t7MN8`Nl?aqSdKpN|zoVA8Ycyw=_geyLKi7MY*&Ynq|w@H*w%G z@83WfLdMXlK`=Sit{qqirUV&iRu~({(Z z2X=569uAf4AXTihjeNatL%y>+NM#&H2+}tUY%;zT=Nvbjd%e|!UXDb3^*q(?Js#MB z2lC!~XY>1cv+|xcdnrjb6)jj!9J44ZK%xMubEIo%O238OW5$C3aB{S85YQo6*x>zr z$^;pKQi%oixnQ=)$2=HLv_k)8nELEp=J1|T;COyeMBlDU(7YEbOMiDiu$;_Ri%&qXbo$cBZ${>yeni2$B95}hG`$M;SsWbGwiuAZ#b?^QLGWtBF zGwm_@9U~>gR=G>$psK19-|38qmD*Q9JVeb!ODSL~{7pKRXH`nEHj*0gt)^beaKll-l;nY4oEw1z$x=*_+)d#~+RLtM|qEX$Gr zs#?+fP#s-Xv}2PSV6UCqr^(WLjG+pT>`<>u?fR&y7>aP}b_}Mq!stx@$m&}wg!Kix z6-j}GoYULoR+AluEv`{mNuSBv<}BB1lOJtMO4I-wXf=_uV+5sX`XL?s`(2nWV32gh zb^88j_K?@f_V(^zxXu4nj)u1UU4}#`n3?S*cl;K4>&((!(vs--!cLrC;N-y8Q1GMn zAMS4l-so>fMO<@geWb+>(wd3b?;#bn7nAB*GlbqQ$AEX8!KPuL-gGgYF}3C zXF>zy<2sDNVxtF>3b3!WF@d857e$ticXOCMP1$^X!8q>5M2;7_`|y%-ycZ`42}AH~ zdqK-3zmOK)6$ADY8xIE zu`jke$)Y`0UmZz>vy)|{en*U3smSAB_wMC3JxY=#$^i5ayn{u48nBa~ zW08fy%tV4u2%*&4<;@;K+#FmjnY%0Y=Yak3j```Ur@I1UfgZKy+eQDWD*0i)E08HDFu${Le!p%Pcd(=0=%h9aLX=tsCZsE4x%67+hy>IR zl*(UuQt!Z0g^~PLfK^bq(=G*>xsEJ8`I6DZ1t&>Ajm?q8yT;jwu1yzd%yr-A6&tc* zxo{zDf5~1}SA6qrR|Ng~+n3)3k?SPXERs?se8&DROoYrCt@)&Bo!{<+&XgoZX%9M9 zU8}Ga)C=MDNr*6W-hk&zrSiEhW)Acev=Or~I}+kj^p@)qoh1DTxq+g7*d+K*KWZ9} z8Hcv}Iu}WPjyWQZG;?$jIz?Ub*~}?cS~i(rJiT<8wN;f^_`81HZ9GboDELP9BFGr! zq*<|ulqMKSBQpIHJe^4noIo=f_jVo^n~fOX#{~G&(io{ zdIuSPUB-im2KFU4XN{@y3Fk0z5~LVGbPSoT7(iJkIDsW@UeWH#`1_DJh0oEewjIKO zc7i)-+kF*_erDIuQS%qG`9eP#ry=Y=j_lM}L8GpC`;2q#gt4R3b=B-#_9Ggz9$Htd z%&3jrejR+7G#9A!Xa2m2|6-i`ehpfe!0jT2q*2c~Yim#u9cz}uDi<5CyWFy+Juzn| z9Q>}o*=@eUYjaq2nc!Ey9(j8vOajd6B>NC6sfdkooRV7?<7PHtePYh$3;0(*JN>cL z?ebV7;YM614bf5wM(E7r!bNlw?yc+EuE4#1?dv$0R`%U|Iy-KHX2%nepi*!*+sn!zw|cg9o1(@)+D+#1b??C*BSN@RVW%y-VcEV?2By|8w^mJ%=r z6gyLeifj_6(VpuLLq3YN%P0>y7NKzkkzkV)lazNF>gbjaVzX!eY}|eVLsTOvSO`P` zWn#JBeDo}`MxIly&m4w4Y59nTBGhJO#*v?ijee_-E2+5+M}4Z;2-*Mi%q`|FdWOp) zBtmdHF{Z?b3(J96w-kE<_AS{|hFwp}b+HQaN6$9!WVh&E-h;*h>d{K)&^0PmQ~1X< zoc_pvl9q?sKOV$B_qP*}CsGgtqVKo8`WW+qa_CCb@}L-KJouO${LutJik<{16G4+y z5ESeKK;*vXCrgVcF3LkYZiDo-7Voe1>jWc|b%H%2UQc19D(3^s%tEo_ER`-Ga9_c) zJ3T9FpBZt{h8boazwqGC*={G-Y~R?G&RUx9z*7dq`~ZYT;CwAd{>goW)8;WIvt*m)f}fM59&q#vlk%c-$|_kC*yHKMlN8 zTxQ~tKk|>%b2Uoe5Zcty{EkEm{G_8yaOg;G=iJbh3@a-pbx+ef_(11+xtzcJSswFm z2v47Ac?XffuVCc8hDZ$zW(oT{4M)oMty&ESo9U!<8Np?8R{_w+g52030^-1(1N z8pd4BXCg|BdUDb4`~Lu}KvTbb-d;QIyYGJ8c)<+>48i8C?H;_ZpS$fLBx6YnG{a^d zi5EeT$B#z9G6dh%F$0PTEvW^*(*>Tj;M`;^F|D)1Ju9wy9T*o$0FO$I8Uoy@i`Cw+ov1Pt?2ipR}tHU20~F80E#`x9G2-^@XwpxKkI^QCu=)>%Ehw?A*xtn{Jxir4BI9R z#D3rUQWzqRG>JK)Bh>YUwqqR|f7bWqUlRiVOz8#+DU2U9fwDVO9l?UVtN5S_@bR6S zMt-yS@r|i9VC|V=1eB*EI!c^ilRwLhRy8C>wa6(zwlE{iCpjZxj$MTynZ~e$Mj7qm zi$#{^Cg<#h7vA*W`qwdU5bQdrqBYTX5{tz);WXjXDF)kF`)QuIGu}TRA*XGzyGL_Pp!zT z{3&AnHUI>)$@DMzCxMg(ICVY_KG{MKEk)oNW5JvfpCgm61aLQA;j~88*WPL4#-aiH+=m?mx z0@&oUdY>m&6bv^&+XSlxnq;*ml7hBPrGhE}wzNPBrU~b|&7PNW%fC8q;h+znopj%6 zEtoW%0d-jO7r1zbHqH3xzuj~H9LVZ%Ry0u->1`#%oW7Seyib6t*JNvMmaJhti&&q_SO)4PN~I%Xge|P)GQElotQ`oC7fL z>B%Rbv_GhL?h00!`;)XhY-xueAoH}O&pJB(R4-Zf{4pcb^NS~$a$dvIMZY32ENDPM z%|cVq)&D(yyjPXEJ{x$7DVu7J2|A@YWy-OmOy{hZZ8vf)OyA(~i4!O0F}DBK5M-Ig zqifzc^;hlDjwb2&oJwDFBnT=@E=aI|>6lw15}AQx#{KN9mm3@FSY3UiI_>bgV%6^t zc%de@={7kfBv8&Gf&z{J510muiGkHX*s6nFg*Fgn0ik(g6ts$grg`J&Fx>sM4pf|2 z3*ztJa`EeXpMd8sCm*ggsjF|IO~dLml5>9bw~se>D$4tDchSf9{C=ys-*z3y8AubV z4xe~MeBm|+-Qo4!=r_KH6--ZB+~y8y7vwtTD!1jlX@?HIW0@~8EGL%w3D#c^Af>}a z`f0CshbcRIT*hwJLgPN@Fe?WAZO)GXUmnqt$;|)D8z)U>VBcTC2EKL_IVId3G|&dT zLsz&-!muE%ae>Zknn~iM_7Tqu&UB)xzjmbzAYg=9L<@_#Szp;PcW3G3ca~%P7cPD= z^{4ZGo_@!KAm8Q<6llTSow*MQXWh)xmA z894+PVlXOnL|6;hN1wVCZI|MlfhK4Q)s1lH#rH>uS~jkK_}HNz0>Fn$cJ_vqFH$3u zMu?o68qKdAe(+6DIruk0009A?M;PNQ)6t@Wjyo&9ntA<$r|&--pRaB>m_7r1-EaL6 ziU!J*2AhyUxb#C@53$}EfFd~fTc^EwrNsh9xCdI= zG#^L>tP7l8au?ObCtvyANf*TzIE^RlonDOn1Pu*eH!{njH?QhyQ<;u{h)N$d`*0*5 z-oFe>@0@0WjMmzzzvVxdTys1*cjm3f-HOcsQZnHng@@xtO+CZz1ML7Y{r@Yk0)$mV z(-B~D1=3K0!#`9jOei2{+B}XPg`zo33j#I^5aI%8S34xCBo%y~_0el{%04>$?q^Q= z3g8d|P;VLvkwk>7n-gxFnwag>4C{(ijCQGJeaqx$?9(@2dg#>OEcm9yk7d55biGX7 z*qHe92D{#*V!e+L`M|erZWECK(%tdbs`fcoHJvo}v+#IL(Zgu^9*e%I-WUoLJpD+dS=_YrPRVbYs$8x-!oD5Rl? zvQ(7Dhy^0UKeJfn856g;{2}ukJ$VBf2-1S}Z++Uaz?f8h=fp4%(C<;Bvq&;&51oUp5p%5PNgL-#&lxX`g| zh(FPT;02vPd|ea=MT1}rkV!pgC4dv6DCXOM(jX$~7wp}qTKP*y-u}*|%}Zi!biI*5 zc+HDjTfO@KF6zV&^r*9umn^da8-V#PKv(u7Xl_3Fg%dWP+;izKY#aW?J@0Lpv#b9u zt7^xBPa(=FxLuvi)Nh_R^>>@x^l<1uFRhQ^iSu5LH zRr_aUDi=(>we8Qzvv6nTSGD~!+08Q->{p+Q8h05_q4 zVFW4)OyTcrqZx?ILJB@mk_E1$;t-XZMW`18-V$%4pTmduOdb5!gK_bjo`5t_$n?X- zKlav-eh-7Ud{Oopd`2$OYcm~0njVKW3fr-h8O$o z{_GUeao%f=zd+c@#KT<{OF(9uoTLat$#fO>)jjb2{*r!s_j#{B=WQclKQHQmD6Zv|8vG~d*P+XL`;uO zi`0?VR7Bq2<;_^8W14O!Eec3;$c){3dET0}I&gAFmFRk^X;xNZIdLw5%W4*kA<^D5atRG?HDQGwmPl z_R_I?P79Nd>sRS;z0Cn2iY>c@q!$Cgefa0PC(o{t6>3M7%N;~*e|JXQ0ah7jQU~A* z1)h>RrvgF1WloZhu*&QaK{f~NZpXqa{(J1B&=LD5A=kZ9l<>mE->4heSTfd~I_vh! z-{^X-&Pv!D`2kS`b5pp0bQA=YK+qfTnf6Im%gWqtEqV9pOI|wu#B<3l4_`AiRiTot zbyw*>9VIMx2VEwp5LkJ{)rTShXa>*&z@M0a)8+@|x^mg~+*S3m$Mmi)C%BWl02JGV zL&Jd-|CMix3b;mnKn{Z9j<11XCjQ1{p^iLRBowTIsw`M6B!j&g9Jdu6br+ zoB6y8sYWCHRapG{|KpXP=OaVz0~W1OUZ8++jwwylAv7w))I>H!D@icONyOvF{Qi#D zX5BS*hc^Iw`0MIakqfYW>zkAR%jgbqYeD&gQ^x+6G=Vg`zA4$RY++(=0FarO2qZ*e zY>zq1gYNm@XI)nL@iJ0&fQ*zS(f0X)RKyqfHYj!r1fV5h4pZEzz-LN-pPqyt3D&ze z)Dw`ESKm}mo9ddvt<8YI-(I=6cgAWPEUCAbPrbc?!f+Cm6^i*FfXIjW5>3jW#dOkJ;k@O0A zfCDc-J=xahm_uTf{f^Yy-$BMJxTh$1$dOix+UClXmgsPSMItev&Z2qm~y$$mqF(cfE^q1UKCu$!O1uQ#pC?wSwepCHP&_n8ZU%0d9HBwT|J zzKYuw7H^JrRCV_rd)(OcJ4^NZ)9UIs|8$C%=)5|oT}_R^rXUzpNU$+xHf_1W$ZVrG zkBbsC<(g0j)As~?;p3gBfd+(PjlAVh z%tzEUH9~z8k!Nn(bl*UK>qAJ+mXcxC4gh)irnI+$0a*HPqA|fIiPnlJjRwP#oKUK1 z5OJZ~Uc5jinlG5^Olz9-pUXS(jBsel7<^-4IF!pEdxFw?1>;zv1QJe z5{)Rn7t?5HN{P7bhlaPvs5UBsA;5YVZKkMXHC9m3pQuco`TE2yhMj_`aBv(oqC~9$vNl)yCW{d-} z9YJ|6hHCn!$0<;fTqN&@$7=4Ue^Q5LOm-Rxcf@D7ZbM$==bi)&y1)8Vd_|8oukjX5p6 zTL_lO@HVA^S-?iF6VQz>$Zt|Y&{F`9D6K5Qh>d3wr13LtEgA1wEXe^@FO10!OWG#8 zuY>-kCAW?!jBoK#9&NiRFI(&4bVIGQlt2Q!yO&6Pgpz;eb6^bX`R2o*gs2GEJV%I? zGudIODRhvIY-e=Q!>zqOPfdDy!smD>4kD?;?Ql9VG5QL-vHI1`lSh8xz^&a*#g;M% zltmN^m~CF&mY9x2f?_D~7xpR?+l3|ATvF;1t?a63TkqN%?p`Q10)SJBn^jmfF?svt z5mpXNnGv0dvA2IT782Emze~tA4x>YKq_Ql}00fRZ@PtMI10oez48IdXA~rC;D+kl>)Qy4Jk5n_)Jpl2K@VrmnS=-^ETDQJxDO| zTbIGcR)q#@adcK$OV!@T?{)E~c-|fVxuI%N)%$NlLJjmppaK?9Nrm}zQGegW?LA1) zg{i&pD_E*|S#Xh!kyx5{Hi@}HPrsSfr$EJ=2-R(okLeqgJDfG{@=g=#kZ=<4xjQ{{ zalh;i=UUwkNS48K6i)fkRDoua5(~gp8Sq6OseC%(SXoM0r=p@GuiEDcn*W?6)Egbp z`s~#&{NbyfcKoFaKjazz_Kwdt%}1jD=TY)UucCKlMxe+br5Lt^2osHk{-hoh%&>PM z09#7Pb4!XSr^y9hy;sd%9obY@r=h8dm}aSZY!6<6s09GK-1gxX#HsiVwd{R;$KFOI z6Bb8n;G&_F0qQy6C&0C$(y|G$vA4+wmTCXWQNiQf7mp3Dps(Jt|Dg7z?{EF?7AzTn z??C%SJoNVM6(4lGy0ujm9TaI#ynOPQi$4wH?k0J~8-F}rR(0GQ2wy5i!d6^Ez`;_W z1*Rfr25hYQCW7bUcuoui6!pQ9L|M$CG@tmYJTqXY<99x9UZDdKv+(BH&)oXa^J-w= z{{^1hz~U2L`QS3w-%#Wxo>re ztPnOWT?oZ8osrxEa?l+!&-9`-r|GI*)gCTjDU~FsOAvM>vO*&gmPc0x3=c5Mj!1!H zN)>Y$jke55Kl|H34d3u(kt3&l=`_!`F3rX(j+60t%pw576ByV_45k!i+_nhB@oNYG zgzte6ew)_8i;(Ypyr#SD@rTBZTCmJ>QWPQS$LDJ&d6@giRD1w1B+)zFk`((p$8*AXbgynkU z1rQRTaxM|QrLBhmz`TOUsVZn4!sTxtc=3cIQ#ab5ZrqzeVrxsssG>j3xu3@(;Cfl?K|qEA zWfWTn(Zw!K9QbCK0%Y&~m< zMKhG)CSt@#LaOpl0(^bf0x%?=Wfxze0}$DLD<%S0W(bzc7r;z1qhT19{|d^$b^f!> z(-tsQMwqq*0CwMq!s0KE+d}srOLwbo1eTr|#s-=wYz|V9GHUBqcSitt5wC68Q|h3R zUUx@58ZP|v@v_f{?mSE%qn0!ugeu7e@0{R-;U)3?a_1Fd{e5qkwr9|L(-T_lioIz( z8qEn`3*6ti!?fu@49P^r{eDHc(jMPq!N6f90%HX&BoG=sSyvA^IiN8Bn?fvFbK0w2 zGwy6`touU~G>7D;SX6q%2aCqR({ZkB3e*B%xjfh%nTPr~^7>zH^uo*aBkD2FvmENM zT#1T6-<2}Fo?*QY0p7Uv4Qu9z$13wV*Q;zFnXj*3{w63g@vy-sr2KxJ|GzR6^eXeUO``9fy63WAtAWRD24!xPp$sDq}aCTN5@G>YH4Y(3Vskq;IzA`MkMju|q_q5uYxk98(DB5!zZ?3nN#qq4d+}wOP-Z;cLnYxS zhV{)IJ1~gY1@o?3+JEv>f!w_EwT;q{JMX< zRkJAP_Sqx?^ZtRNA4@LX@cm0|z(0i$py>90^2*C+1l8FK^E?Mi+vYY>Brr8zse(QI z?yf)4 zZHdomy7w^LX>6oT_1iY%48iADNaTG#`mQce6Mh<=!Fp!^Fs{d2#?vkP2-_7o*A`Tu zVuueXmK!ok1m>w&Gy>bXx^+AJu9cz?Xy40j()!6oZ~gHz#-N)h8fdV6E%}QPzwgr% z$;39)4D4wcPo0|L*T&A=oy39>LSqtL62qO>IALJ*-&fe5k1HYcB(Q5pDV)DSfKj%j zY_(QUsIi}V<(2W7nrz|u$dLt_;}!&={Fo$7l#+;1G@< zzz#)|-9)26adrX#@wz+1B=J82yvP5dCt{px`r!lUtt5ZhI zqp>lR6E7~{C5t`0B$ERmVRd6!L(y0VY{sx*EL~5&mM~l&vHlqVa|3GVuZN%c)%=gU zB2IK4#;r{v3C0CVW9h*3wHsobW(<>aO5+S%2~sHtgaGAhZ5jZuj*m?;Q5$q+elte> z&0;LEd)sFV9RN5MZ_GxhGdGlPWrnZ{VP!l|*bw1bdVoyca502E{HurJc(0Joi}W<}|Ru{O4Qkq^fk>vNi^WcS;ioF5>{WAcC*t~w%qcI*l z`Oe9A#`AqX-PP;LZ>9WTDfSh3alJ>jT*F zsy&pJd$1V85`hWrA}ZX~ypGJt`N)v}9{A+%UkGyTmJGP+@NWPrHu-o8g98Dt!R|d*2GJ(7l!UW;p7Qk zf&Z|Ucc@TY1DSxkH_kpzM0?#RxWxnP^i|jZz4mV?)GNdf!qj0430>@xM4181jQB{w zl3H8H`XEdN3^0w0$_fPal(8_0zF+taQqU-UhLr{x>K|y1(lA7o$81Z_pY_yEAJ0B8 z-7rS3uQ`C9vH@6Q2r0oDu9vd2@29heyOk#B{!^}Ah=bUNNCQVVl022dP zlhCNTq%CeSoJ>}9NU)@c%IcVu!7?I#7bu}j3snYzWmOyqkL^R8f`ul3(ZYAiSd* z4-DK18*$}m5_z!ou8dFzS|BJ+1riWlBU_4Tb`A>+#q`?}|Heq2;_ER@ER;y6jBxyc zZP$gCCDqjyfDUfqT)(UR%%+@1b|!YR(g#~uS_75Xsx6H0s!uJ-C?}u-MMS2HY5AVR z;&0bxS|9z#v7_3S=nSlelNhq)z}P=?EMLs=}S@O&RQaDvGP1t@~y05zGWkhNEo z*baB|!r%=f^Ia6=EDrKmzH(?9ii|EO18L|o1cGj_9R3F=5@h&QbDriytQjns+){}= zRz0c7uoNZ;g<>C={~+LD*@7THBizCRqjZdX|4?tE=+fiE2@W_88C;4p2na;+qf!8Y zQ4R%Qf&$HT>*vTiPR$1@^?Pt3BII81~r;gsfkn}Ymr@X8qWqrTYGynTv z)n`G1RBagzJ*MUw3fVY?FSDv4Jv>;Bu)Q9a|B5IXfbWU0c_NrwjRyv%agE>bWoFNIa0+Co0>S=`DL3pN7<6T4JY9KX%TtQb$Xm zH61+-==3q6e9|>L6I4h2Mx>XVH;qmov;M~Y%c5iez9UTMUqjlc0OP+yK`(;6_X3SV z(9rOnv+uYEX*oGGt1ubu@tAf)HXwEb7Iy7$Tf~KdtuLWk5LUNwa0e2U&={CSfyP3G z#=$5GVdWopN$R7QK)t-w5$e3~LsS5ILeq>H#yR&E+2{R~kCAfc? zF2BVyrV%9ln2}zD$mvTB1GY{se`3+L>J|P?h+5{n^&X`p;CH~XNw#ZVpYZE$r(=K3 zU?2%13%DP<_naNBc^V(P;h8`5>!{HG;e(=^XS)|eG{{NNQR0ypL2+=SyIBzCG@CsZ z^G!)Xqaz%~*GNk;^8^eXw$O7FcE>4GWA~*MrA)U)-|H;~?lf*>IvId;Q#j?K*c} z5OI%#ULJVD1Di%bbSPnEBbV**?$#HM8aFPuY}$!ixRt?q+6wk8i}{s-@W54N5a9qw zW0yGI4|jfC8391xOW=+Tlq+nfF?3{K2F4^vf)jo*mmtD^S70dHNt3#a@{Yt&f7;j+XijrmckV?L?Qbb-j+XAL zgm9uIljT-&p)KsM2qj05dS-O)r?CsLqD^J-Sx)A1>K9E_nca64M3fJ6XY{wK$@K&!<_=x zA8C8AB*U+$1(bDmeM0AcaWxv1riKO$^`R0|32PT61F(`^zlk)-+h4n}&!Xt37j?7d zV`waZPOn`uOHrvM%H?auq85sML~Ju5w+ip*+i(BBNIaek>i4_!JpefI&X=yN?d)pZ zC^HK_I{SoMzO`-l(%ENK4wUh-NWg}-w|DL1DR;*Jf<6F2r*zrdj;?*|r2ky-fMGJO zg}$F}Lr8k6qo#S*MRglbo!wA(P@P^+|=e@zv(XnfyAsC&qU{sPoMsZsUDmV zzQ$coURSpDM|{Va>(wyE`)QjF41)YHtcqC=I8%YxPe(*-df(HJcoL64VNoh^bKi?H zNP#yT^&^oLsb#7K@UA96G{)>9KVm=QR*VL54(jO3|54!;Drc0+mT|4c(O=OjO zewKG1N}@jVy}VDf;+Dl}l*d4Jasc0c#nI8nNrp_WnwOaW``@2uB&A=w@y0&!?)>kn z6y4hfx|yO*JW!KBA1jtcwVr`ei>uKmH0$3 z^#w4PX}kKJPDd{KteU;uYJ>Ot!~cZSK9{)u#u(JCJwveUFx2J`|DMdv`_GOvBg@NT zedfM3Z`*gq9(9x(PA?4;nRWksb!2M8x<6ji)-V(l(0NX24Dp~|5t-ve~1kB4`}XO6i`jAq#XzCQV8;L zez{1#X6KW4{BhBRkCTE99pboKkF!qCY&kCJs)@BcIvCY5Oh=0ObQz8YDyU-=>iy^s z`(A+&ghMOP4KB2^A%TbxMlF=JMdJ|uE>9$6g!)3G;yr;*Ps>?8wH{eerk@yl(n+RC zsj;q+)feXTt?9-8$*XsjIUSGg>yY`!J1jT4jN0Jlsh6kx)fO_~+RgU)*TY(0K*vow zfK3`VD%kgi=Qqvw-TX;e-D@M|=B3g&fR&q4D5?lz8PHN1mHSO{)InZDzT+|8_WrSt z9lOV(ZQ;1 zp4d-!e|CGN)3q0)t>DQV5NRn27G+Te%|Uh0Ee4&i$zJ#TUF$m5Fzp1A9y?b2>d#MX zsy7~Sr&~F2hm7<@0Gexv7=ctH|5cc_ha3T|wW8&es`l15YC7i}`RMProoOVH*JB#) zx`2`a=mA$xKl7I??(QhM)YB7pyJ7m*W)0xbkcM!PeSFp5w(srI*Ah}F@C7g&sAnjw z+T7r{Rxt@CP%GOsreN?}-~*www`op6DXqeKU-D8*IJF^A&kP3*7kC7yl8yNYD;lN1 zIJElidmTzNY<$7^hYjro!-D!%l>aWOd8L40LVokNoLo$SM3L2? z1!NU>qRb8YX1nD^zwf{IJrALIyWqL09F4#smpsxtH++|ex+*Jo>h?ToNyftNND3|s z#Z4kIWCG?v%JMj8w54Ot;I5DM_~##X`qT{n_y!_S39E;a0r(cAG|-t%b!6C#VS3pa zak#)peZBnj((8V?p|BqY7pGVdN))nq!d)8ud&39@t#LFrYG!fM7i38P;ox6NAW#ad z!rZ)`<_4O7n{NwMbCE`j%tG8kvBr*hcF@dn7^?Gh67k?{l0Sdjb0^F+=cQ5{h7Z@@ zH%FkSj^3tIj8GK~>3n%mINFp|&^FP()prJ-KJSwg z@5Wd@H8nH>$K85Quep8pd#F2E@pF;)B_R~!A`qx(jDM5p3We{IC@Ofw;&$i47vqaw z{>?)t{ATfz4#b~;NaqiYk^$%e#kl}xdeAcS0hdK#4xfJG;;Z}i_vssbV#$7io5v1S zY&}Qk)iC`y&YQOFFu^AU4ynZ54rc;~#6M*INu%NemR&1Ljszk*MQk{Gro7} z7pO35pa5ktx-f8C^MqaaQ8IVyeiJ4vnR}p9Df(SP1KkLW)Pu}SKc}w<-gzMIe6>^H zb)(h}4V8b8!5501Dk@A_Ps*4kG)i#Pc3aN6=l$GXe;iYv&Q6;;m5m)cR{Sn?Pp_^m z_MDQdtl2v2OH6wZ2ZxUnRARva>dN%v2iPdY97&=Ui!`@PAK0AnkX57)j-h*e0=GogQ_*T$=1-YXa2ZH%0|x7^V^$sGaS~dD@){e(NK(i(Y!5Y2vYMKb20s9|9!rf1V^Q;OxoL(WOmzT2v9KN&HrW^tBFrUV?;#40gyJEZp=`M0P_P(yR6=*)f6+n2 zF`Jxw;?$KHf#Txto_73LCEovM(rVwp54t5&7BkbuQH%tKa}?T^QRT=CLBk=QM)MR$ zp;E0KqC8#6y7PT*(QNE_8|*W2$&{W6*W6w)w|wX?Nm;++Gm+XI+(;B$6zf0}C(*=d zBcwtJ11+c`7El@i;DHCUv+WDnl}SxMdeeKB@CZwuz-pmn0M-D$eOC=R^D8^%_S*yS3{mF?JtBN%rY)LRmQ+O9af(h;vVGnIRb6lW@xE~fwEmQ)_^b_- z48R%!E~uvZrjR0A021};mSO4@OLQ=EIDug^P=ruJ!!$U()irvn3oib+XatIjf8opf9~$-ACY7nKxbibf zb3ui<2Gq;5jVKAWEPuV1HvBsd2*LlB4hRv7+7X)dYe2IBUv+xXyB1ozTrq0TaZ5&% zKY87F=il+ew`h*k^v}m57pU^S+vajYD%ANYXbUino`+FYR*G<4W^?8(nsf8>zZ-O- zF|Oeh(vlHa1(XcH{}^Gpu3okMdD6H_O>YsNhj~7tF-wNzAj}D1M)A80*ZZc|Ar#A! z(u!&Jq3r(0uyf8lCe$)a5tKChe9B3CRFHQcC}Z7a#B~)Td5VKgar76}92l~9idxz$ zL71DsPDdFPybujsNk@RkX7Vyc|wR zG_^&X&J&856(r2Q#Q>pVFYeUgzRhvguMzOJs85t=R?45FD#;hS^o@P?@ZhpHUj6LZ z!+(e8rKYC%=#=IU|KHC)AGq-Sv1RS^|5X`b%x02sotHLtN^4L8i>g47O$1a1B4Boj zB($0+8G!#W>M#^QnU5ft>nY*mu5Su{80;3=!=bp|Y9SoEg{qo_*m;3+Ll`JB4doc`8seI3d)~p=7DB`VLKrK5it(sxF7muKZ}~ahs*bro}WM3hBT1p0I!Wq57r6%Q|M=RP8Mg( z$|@z$^3Kd(pEzOvcB9x^$^oo0O5K3#ND}c2Pj`USf^uTx!oywHpg3N8b@w`x7sTbP zbc`tw@r_`Z5KakF zB%~kUbVd9 zdvkNw{;_y$x2S@C+LPssM@TZCdw8QS8?Sr>z%}9Mn%Oxhyr)Q31dRo1Auu)PkVM3d2^ST|7`85$!_5eC zEJ>PCcxNbNMB@_Wc^Mj@(07`rys*FOGs2FY8_i*noM-f8)hFOC* zr<^&koW1(6t@E2+WY7G!6N}$BFS^0KAGm|{i~gYO=baJ%(IaZ-mDefUsN8L%`1h2FIBJTURNH0^*RH%uX-*q^kr_hhO7xX6){P?(BbaA71S z1kJo}tSCqztcxfa(=jJO%iZpyr_Q?VE;8gOcea>$c%OLNHpFcP%SjyR2W3Y(xp^)9 z?GG33R8BvEhGKKM@7%eCVE`pBU=>j^0RIE%x<*FQ_42hl?x?Q)GILpP zVILDU9mJk#3)VQUyg2&GDC`y%xm{h%pD^x%bPk`4x-T6QtIQA1 zOIJsTO)*vvr);WDD*<3ZoOy(J8PGB>rJo}lI%pZQdud_ys*Umyf|Fl&`tyWE?dImG zdpz$4H1J*aHY9&@^N_IxU4oJ)@SRaI0RI!xP`!Nj@|%bDp51+CZ!5OFbbYNfwh@}u zZ({Fa;(tj;@V7m0IN;Ef4AFQ9({3XHlZn`UYdMUHAh>Vy3odvcUt-a-XVr4=#bXq7 z;wUW5w2gf@OU9UoeMtr9ABjFj#`~ME0T(_FLJjAa7D;*qIaAEv%dj)jCkh0YIOGKL zhTv#BnUtAoGypy%CwKcTXI8RE67@nAca>!m;M$SH zJLJ=Gt@XvF%J%m|sS5A|#{1#UpH}Z&e8V663-)|(7TI3q@)B$Q)D-=fVpVuQMo+-F z@!=vgvjtFvM^l0`d>5$v_J~ za+|~$!(=1stppkZ*M}&}0$14+X1{jBV0>?AWWV1h_tQnm1NZ@e`)Pp~I=Jku^&$Xd^urC5l35{waF)VX7bIec`q3nPXO7otMN?QJKqN>s)2MxnAT`CQH z7nGQQYZ*nl?UUDC=M3xOXZB-}w3YW2wgn5rbfaLQG~VhuW5v;1CXUA-7}tu>C}i3} zb!M^1?=tk_iSOc}vmQOtVc%LN}NbF8v_$ae(xCs-7Ok~$`%kfl|N zn_2C3gE#xbbj zdo|-;hZ=;T(hvy(3CRTeT;v|x`23R&#G@8HeZs~O{mHaQY<7Poaw-x;D3%?C(dY={ z|Fs1({1Xk60`1UV#E)!pNpzP7&MV{zmYa;IQ$R`jr!%7Ke}z&AuokdXm#-eVGcfPM zzh2fW8rdgsvjQaoIcEE$20QFzTRvo7U0_!r<`9+Cj8d9Wfy{97H}piNO#?XVCxQ^1ZJ5Ex-uWP%dbSm+(WQUOoqq-lkCj9F^)-~$+uVxF zTt-tJ>foVkP6<_thn0SlBVQA;7Fh4cdi!#}qrerke-+Jjo9l~bs+CkRY^t@0)|QN$ zs)6KrQ@>}@4k>#WOPDy^4Al+A%vXD>Fl|Me5(n}@#EaYfCPGH|MG=zKefAPo4W)9) z8iFZ3rBW7_`C__d-vLhiC|jwx?`0*<07jOaV5X}Msd7{>O?pM&mCUj$uq}a7g8IE&X&F-*EL1r)*N6>6W@tISrnU@O zC>(dr3;hBNjCe3HLr%e`QBb6kSWbj?L2Syb{wKZ=X8psR--sfofZ~9bHT9j8@a-sh z0IP`=)PDTWFS)mOmFPYJL|*yI(-vb)2Lc4AD9NFjWz($o<|#sIlhL^4=3O%*4_9&c z!$iw~V@a`|k84Dk0h^(Ow)t;KC*RV_A=(XG&}JOBnak-AWE6T)^3sxSIng%4js*k+ zD3s=v%nj@EM&rW*LT%FRCY}>I{w1MSe+8XGx#bLu5Rcg;02L?*1UC5M%#xxwVMszU zbY;{#Hwq?ry%rFHw-=Kr8?J)#C!}ZJv?%KjA_F#?vgxM#dCRi)a3Fg4j&v~rSR@gY zuoh7=0ILBb4Q{w>pch{}KNpVb&(bezTOD7u_069w`r!Tq%{|xO z4LS#V+1VRZlI(^g@Q<-A-AgEeIYx86%A6mn1}u#d3bJKLBNm$24h7-5k+NUB_Efm0 zUoC!u=Gc~(&*|@H-bmYo2EOipJ=yCY9~QiWQh2bIQ8EClv+LK_%MY$7UlP5DVKE+VL^wca%6~wAyC=zN(X3qCHF7$wST^`cyVfb*|o0+Y_ z2CZWN8mYLhIY*)5kDUPDEqkbD>zhB(DNV_UdJG#rF8sl{rI34U>ygn|*Pg^RIz=oD z2MPwju>6pqgBOQ-e#rDwz?h)I?^e+$9o_oG*ooJ_{@gVU!-1geIDpCEYfdwLEa?72 zSwxa-cXmw6v-@8`$WynY?s-@VK;4p3o?vaD6mNY$kixF7(dGZ@k{b^mlv(^@lE6eu z22qvGNF{`n7NL-gGKj{Q%Q%_kc0c{0?%JdOg@3#p&%=Dd8^_&hjX0@3vw(?ohYWJe zc9yk+v22oDfz#(=_WSWf^X6r)T8^?|Xl^E2m6CGnGnFty5G_#f`#;z>fdd zFc*j*4D%UbuCS0d40QlBMTuW*2gbH$bdD4b4gt;?CtB{e!w%P3xcI{zK5Z;mrn}FV zN+n-Zz#!suY!(T=xV3A_sCySadC-pGXN@Jn61{hApkx5PBXH50?Wn$9&b<1rzRefj zI;m39#snvu3%|?cz>r8sq7l|<(Y8f8_{Zle+b~Dv_norPIq+xc(_Tj7VfyRRX*Fun zxB#DX)6|KNEl}dcECHHbR|%z=CT%phe5;Euct7kZ(w4>4Kp;7wRt^XNi$HY0%L5P`$pM<$ z76}-Cp?mkgzq_QSZrbhB3MIo}TDbdWWcgEFuH8v2oDSrfu7i-uv{cAE9P$kQx?Y1Q8G!F0`;EKNmzUpiSl@j9`CgWFY(z*B2&pu)SRl3LEJ||{+A@~) z%Rc9gJ7(hxPD+nRPk4Gp}Fcr2`iW@P|!bIO;=@b6-xpH^^%FExqIPjkRD#%{) zJ_~-2clvD6HM7;tzYg4?5KnN3mobPY7(|nd;jng~reC+&VBC!9VvZA!>;iMb-o4h%pRe0APy zuBv*0+9+CttXypSwL;b6f+#tL0iZF=ut7>3{>P=%lmfM>(DqBCMvV#zrN4qCNl5*T zjJ@%rvhsrt&AYvtxKl-4PX^4+R#wK^kB_-?A6-28phJu|VCEhgp@9I0}9u>YD0JwazpJ z^-%F&>7b6n@o!&sOaFMQ*pb<+mNWiyOY8D-<}_>H`%RqlBzPXG@)NKr<&7}p>i;dfAKUB7MERoA{9L!P7^Q(p-brQKRkY3D;Kr&w9yz(8f#~!~ zV(lU|_p8YVkL%@p{8yfD)m)bo`^*qS7ZXQXz2OluQl5Ib#<(6*f34gX{9h@ZzQflHGo2& z&(|>PqDj9iSLDh{>h#AQy<22kGb}o#lhLy`Kl`XJivvs-OitA%^z^B5);dg*PNfv1 zD9^*WBw00Ju?wSisonGHPcY6lE`N`o1yD2&v!;$4T$X=pYL(MISov75#7H0<3WB-; zINMJ{L~~V1T*1Er8hPE-6?fF^_t<0MxKuN{BupNxuz-t3MlZNCp84SZ^B8SCokANU zA}=@r4!j0h^Sz$|%lUKdUJsk8%_Sny>Y-!+R)XS!opbG-Bg$HGQ_5}L2Ru&*A~`B| zo2iy4b7MzYj>v_xyzEgUE}8IjanUXJTpCMeM8AFImGR!$$b)_2$-N*5z_mGi?q-h~ zao$P)G_HEn@_hK>ca%z{NW<{qWZH}wWK2T?O7G%|G`WN~&b@A|`-&7A@7ci}a=-7zaoO!{&eL$>n9 zho#p*q4S0MBP0Ht>nvz1_k}AdmBez&Xg=?>gDAf;TDkwVWao)_ysnZa;Hsfi2K=@U zD4U;HUKZ^mGTnkv;FL16%TYmu6^&xD?#mV_4jOUEgm-Wk?`iR4$?(6o-pXs{`hV>c zP3$GRGXV=gMJgQI(Hu4U0qW|)Apt!Yp>#T}mvpxm``puMBYZ*9X2F|9zXZn}XOT(u z0gfiZFk+$Hb8m}xY9X_lDnkM`13W-Sv3w_n5C48QjjkN@*Ibu(K#b=G2RP>!hXa}| z-BPIWmr-T+soMC2%5CoK&~PVdD7g1}^2B&o@X0{}vSU33UnFBA{+`_9`bEnVg2J2t zG$=xL`PUuZlwZ37+<)435OKAVzTc_hj8y{l{@k899KUOERe$L~WeaCtNpp)Eqb?oDZ<%8Vc2dWjg)8c4On?7^WPs zpdTAKX`FcT;;Z}gCiZEzVtce*aP`9rN#~_71_aBpO+ukQp#d3sCdZFLVG05zbKf}b zr-u$bLT;bg`P_bw6W)E2RvAn@0>^|P#skWMxUPJOZ}UipzICKU#=RNnx?$$pbEIVp zi?&WGi?>ICG3e}gW0N;-edAbwQAQD=xOfZIfr5Mg(EXiH?-ysFF1H-fi}+dP2SRJs zsVie*EElO^Y8FXI*2Xnw7e}qC8@s;;X!tM)HqpWm$Bt$ez2!CF7^9m;@~DWHT?m z;h>hwZhPUa3$FW<`F#&kR}Ja39(n6=;=>#6s2TuH;{b3@t(A1kK~Cg?GNpb-#$sO} z$!HQWJR@p)1yGz%fMg!{TFxMZitoH|8bYZc8&+yi_Xu&L|mx9$NYSAa z|8HKml23yZr;It2MdJj@l5DQZ{OghKj}IPk*~HIG%-_S}#~UA8*D;Y7v%Cw+INw0{ zfd<=BSi)<~xsMG`jsJ^T)Y#X)jL|@Q^T8E2_UY&I>#HN>KdWN))+_r0o9Y@-UR)>J zym7tlil3{z8%XI%4}^|8JP}@u{sd4)OFD%KBPgh+Xm=1Oh{mQDH{S1b$25co_t|~? z3&N_tUFrx}n&ad_5SmgM_&UaOW8z&e9cXs+)Y9k8=Y5HF0;-}^iX^d!Z^dY9y7jq3 ze`XeY4+|H)j>bA*)no2@#i`!ockTJuhcU(}!Okkem{u7@M2AYy)-ziEv(Ll}aQs2x z)bI3ue&Q$@fR!N?{`iV{=grCG9_W<1JLAjwpLDffes%J*KOAw@RoTWK5`IhK{z8&& z&c%N{Jq~b+@&av3rm17|<{jWQ=!*Hw+2|efy2}b} zd;^S^U|f{3!Wy_|Ixmy&*qntlK+7eP=ap>-jz!z{F+)2WK-bL(Lx5y*<7abu`?(mz zsSUmsfm8`c)$sgRH+}fxA^pP7?&}M;z6s>i)My?Xb=Dj^T5(cF)VPG#QmLt=EJu0J zO``cWogi%&9XsWq<4pCWlnMAwC>em20Ly>)bBFX93kNm)v2K=ffBt^!;_ZiC`lqw5 znRrbe7kPb8iyxMD2Y!CZtylCT{ECP#GzpaE)M70{bj~8;sQ&43a}4KxS=YZn7WzDW z({{ZG8?PM+Es*`{laB6(%>i|FE5_=^SWE+;mU1JYaui*0TM>vV64KmF7pVKQ-TY8elnK{$V& z=#iJ6{NB#Do;S}>f(r|;#}gWx%%X0&@b9U?+CCjbprk@B93^?8GaX7CxkLJ-=S}F4 z?s@2!qtxj!V)sT*J!i3J#$J6|OJS1Gimz!Z0n^iIp}6{7huwV|Q~*?I%kPjlnFE>^ zUh|1!eXsRpufTG8aVyb>ce=elVzDi+#^-DZ-SqFZXV%q)u{LMPFST##d_=1l*nR?A zAVDy_K_u`7n1c(=Ja~N7*oR*5Sf8`9vN8))QbAop0-=*SIDGcheMXt%R-y?IK8Gf~ zcfa!n;#7~%>*~dpQ^k}OCjql*7(?XxclaHH!ZQkTVI`~-B?Is+D3Wn-p{0tH;FT;E zlMKWiJazry)tWR=-&Iluz&HbGF|ts5r*4)y{*kFj6k4%dSa|La&c1F~!m{^OexQi5 zL1pAC`1-iZ_jrLb!>}Oh#9lyQ4lWc?W}+u0itt|e*vR<}vU;C?o!cRY{w_<0-;<>y zuFoY#{kqq#m(IjBvoiMIH=-f*1SISI-}Y?TY}cuzAeC@>QZ!`jhoxN;y(_g!+jeptg_PsyvRd={5 zrZXxTxY6=|`&9!z36Bn&jY?QKN@c+B0JGrJ-+VI_C;oh%zIm4_#%~O;7hej~Gs{SX zE*4(nh8LamH`4>qL+DV9@2lgn!^)|Z2*4KsNNPo5ZH7iXe0a~VUmP#4#p|7(%9V2# zQ(8$*Bfug~za>UOE9kw?Z4UsD2bSmuu13jhX-RO>TZ<>3a70Fba6>dQe@ovL(y?t8 z39ODRjW6o)vofyv!uaPH@d3!<)0eRADhWl`Vz4Y(dK&UvQ{7E8+xlXNTa1eTy+ zn4a%#f8;CmGH?rJG!#mLSS(VDk2N79XlX}dL3#^Aiq%^n#f4bA_s!3f$e_zT5y$Rg zqO|}^S=ONBU>paEw8f?8;CsiAfDmOTcKb_L&VG+Yu%TIM+w)Z~+Hpv8=5JRvVjE#P z>=x|twTmsnTEdVq_r20@w+HU%x9`0VmK7F%3EvGR1F)JX-k_<*Mmw(LEeU1~Qn{RT zq^1sVEhHf=mOOQfi%!U^kb5SglMO;ceKVG*rjp`+=92lrcsHV4nc(6v3l|&p+X=l_c?& zcn_bPcilE|U}q>IbU*|#n@uc~4%I|=gH7iiKlj;R?!1zi-XsyFJ)o(}qwm?4O#?=>2b$QdTRVPBs_pSz zkNu{6z!Jhx+GwhpPR5ek|NT%h0ILDBqu1BV_bru6d4qne_~2KMh~1HXG= zM4i9vDJzTp%b0z1wG*q5G7!wRS*Mr#TtWQtm6ZRMM>-@DwQOpClF`BY+;S*N1}m-m zENdXv3JM{}wEXQ~1S0k-rIAqK81k|r+EpF%7M_G_bVZs0F4TqwAic)kGncT+3vwdr zflbVe1no;5W74vr{mQndc0RD^F|4)s@DoPqLtRA_c69Z@Syyd5AaHJo2a@7oZtN9C zQBFZPnXlXnwmkR5_g32PkFkG!y?W=)JL8Pf-5?Me>9jb{FGuu){nLl7to*keP%{9N zi(4OYL{`}98Q@H!Hn5i$L(KupFb&ryab$t8uX)DFq)H-GXk zc1icJh|TX(K(Bjo>SL~|`kTdL)WRZ$X4F$T9gB)`hjic2JZ1mwLRxU?eqGZj8Gu!d z`x_c`U1KAwibVcWLF~R#C?812;R`D1r$yU|d6S5`s5*dIWbecpL|)Z&{d z-`89c9{2&a3`{n!Qz-L&D+-N-nY{c2h|R!`&^#p{6ki9{CT4`5jUB1_An)otwJ4=H zXFw<&OXzY(cZ{Dpbt+bzzRuHcK-ZlpyYKcF=kk@)Y=|-iLQ&?EoXn`0%{Fkn<{KAG zKB#udxUdq|7)l0URe&^KLiAamFW8zB*k9?KmbM^7_}ZeJbrI40Iq@e<9~+8-^u!w| zvH-t4|E6EmGW#N1_-39Tuqep~^209^k=M<6P4(X`Sywo&&9R@In{yxgGTV0l7u*lS zk8deqt~Lt&0kfPDFK8WmGs>bk`^4y$d%mk3-gUr8`IiF<@j3Z=B_TzVTVA>-Pq#WD z8zkmf+#*t7OD3aU;HhMyeS4Be3RB;sLA*3UNF2>4tIUFe!a0xCIeq zkuC<}Bb^<$LwbCu50Fmx+$#W}Uxn++%7I@JXI8`$IZ06Ij!^A$`rOaYJf?ePp+iq7 z8kz0SKk+L7IIyS3))opq{FIP0yStv=;}yc#&r$r)A`aDor{W3US=`Zn;>(SVPXpAu zEAkIQrDULFuj@W+pL*cMIpR*TMOIN?5p8wQKyVdf$v=#Z?Di4?a#yOsH2jom{A&Ou z4`5YLNXliS_FI`C=(czni;*OwkvMH}^Qj$D;~zq?Pt*bE@%%#BuU68dD!|%Gxp^GX ziy}i*Ye_s2OfkoOSNDGzm;}5L55S*4AQ!%nl3P%;3k zK@a}<8RI^0q59~Be)r4mo_yy^H+$|zm;C86+_|w!ucvd(?w3xhDVJSV9;ftpP$;P* zP^&{TYC)G2bGno4EpzZnp5xoDtuSb8wdW15N*J)9dDx3Wuy^eU<{?q%Lx509$D=-X z!K5i)Zv~_tZ$?M^>RZ$v1bLhhC<-sxF15KQk2>>ql3FQ zKXd$i6F!7v$9@!88U|dLX`j5~yTH13k|d`Y>KapDq0>u7eLH1n%gu_rFN- z-66ej{xioP<>!J&t7DmsgRIn6ln|D+a#Yv0hexmE^DW_fq2vLq9ztEd;y-#&?D;xE zTM>;!_qMHY;*TOkb0@|Yi~O89%D)-Ciy2K}5B-k`O--TgQ>yk&FQ3qgh7&)z9ui-5qg6Ygg}gw4e0+Y9~Y7f|3XDKZ57xo|Z}x8ywD^Bx?upWb^J_jVnro{g_bP)i?U(rHi7; z4enODl7QlrXeAls4O2?_Nx#q_(uxbKvhf!W_ z0ojDQdeDQl`(Ci1m<;`<=P4Qi?D{KZ{nrjktg8PdG>{h>t{(EAnXk-EH#Pa|i>`g~ zec(XS1(Sb!pbRp5Fk0E)V!fXiWb0e{?0rNI#Y|^3&CqH1@r61il&~hSd>L##)35t_ zf^X&XJX4@)9W*jjVqW`KR>F@4ctfO8n&J(De-DW*>(4^Lof3sPRwjC}J{W<<#yVDP z;=$4;{yc1-!^%z>ecg`ZM&B}c#dlsZ20tv;$s4c8U|TtnMUzzF+Gj!&83uJYIx8f= zeFO033jfvsCD-$Z1%CDo0Q#66{$9JGyYZ7TmE0<$+*ZNl0?NdHyWF-Zq)*$Mf0;UN z$(S?r3Wh-`O!$cz09^dxLO$kamksYrhDNjqd`)G}t7y&U1~+GJzB!%g=}f#~@MjbB zpt<)AuT#R>2YxsBy-eKV-`8&fJB}xLPa#i$kSH)sz$Gn^u@|-HHF?d0j;5~2j48Lg zasF4ua~q*f{h-Xop9t151AsALS{=P(s;WA->0isNA^U+BwHy@)LctBRc8Nt@sYNGs z$f7`bpLjX%)sE=gj~DMc>BFXLUd@NkLm&kg(84Z{oOmF0gq|_0>68kqdedfq{tef> zdHDO^IHtCQHR{pf;yr$s+XnXO+U%_|zi$Zsa&#limBijmgfx2|OEI>Y>iG(YV7NbIdPu zEGTOMnh20858X72V|TuAS<8#o^;5@vvZ^-~UTYJCQe87jZt8d1{mC^U1jL0%fT#)l_Wosj zz_0wQmQv-B4o^T+rGaUoP%TI5z_o#brGe*BeTP(vJ{E|`(7>)98sW~-sV$$L+$}RF zU;V+sA1%lv!`9tg;^z@jB}m?LYs)w6=eBo^1%OXr^cV`$zrO(}xTDM7h$U}gWk5~R zCIHIqKq{poN>k(nGM0lpw6t_uL0eQuH*!P;i|{xDnSsEwg%UPY+EvkNT5(EwYxOUx zH{ax#zkXQ%LL)S?`jQb?zYIVTDpSt1u(Y5)myFfwL?|*U)XxMM$sPK8SWZ}nxZ^U^dmRKxjbhY`NZ}@>Sk9J0#P&@>)t+T`yq6*# z1cFP;P3m4w&8C9GV<(Nc{r~DesDHTR39P3EfMC}MD2gt>(iD81&1anjSLJC}CooY) ziId=dMHwioH`THhbol^_LBC`qPN^RSqGA#x!)DkxKHvN|N19!_J^8^6A1$;uiPQ4_ zi($r9x4m`Zn}x-!@ZPV3CW?usCID6ku+oH612mYN!bM{06}8>N4^q$#il~DR-}B1R zcTYKeanV2&j}Jo(qQWZ0(hjzCAu~R+u8A~-YCb=RF~HYM16Ao2rsm-FG}dWW*R=BY zzRE}!3|}OdQN+(_0*=&L3k#rAb$4ER*8!~nfPdZvfV)oGvT;ZtT8|+rKB3I%A4h>v zCY9{N`jt7SL_{Txly^oe6`OfgWYk-FTrQu!Yunrjjyh+l)91&xA zhI8IDqnu<^uItso_g?z=U@z0PLv9+10Hr>FJ~;><(HkX}Q)=yng&dj^6Oj zjr+uE1`}H)TD!#;iOuw^`X*vz#MiWojf*Y~Y3TAg+bq{_x4^F?L~5=gkh5zCn%ob( z$=l%zh0#L&d-do|oqN)-dmagN{^s7!1|x~5l+JV6*B-rwX$p5dg&LjbgH|JT~ zJ-AbKX5*LReuYA5?KU}o{tWu;R^pQl+J`|684k_3SdH6iZsuh$Vf5`4y0dJPRylXU zJ?~GLyX<4jx{RsOQ#mFEi!OU=^o@zG1-(*nRAx@*aTDFAoK`X(aJ z+3J~d`>72smR?i@)Ztw)d(onb7f0Uu>cn|P51@EsPT2Xbp^03d>yln_v=StuFqz4@ z7tmmV&$934?Y5??_xsI#kOs9j-P+=NIc}TVcdr$F&WU-wc8I9*xS|3)t6fRU{lq1~ zgWbJ9zV?QvCo~t1H;ZP?T!AL3R0E-nyZ^VZ++bUfDga_zt@gYt&Yro$nadiTkP$eB zreUl&bc;(XHxfJlv$x*pK6lqOGT% z#@xiky|60{3PWeD<)P zw+L>7l0rM2frebKrJ2GI;rEs{2Jzna>)ZF=KlQtL@3o2pXry*8Wq z?>z{R$>IB3nvg9flrima`p8HB5SNvQL_3^KKsxbcu2=iTBd2^2*z~b!_MP`OO|N&q zNTalelzFmmK^*6#X%6XT?6xJO%G`}5!fZ-*vxo$E}(c-`S%7ruX` zA-43UGc6@@2Fx^b?t8cyO*098d1#q&0TwysOBX+ed;q6z`Q-Tii+6luWcFvTpFHyV zU*IEqjNH(?f6Z)5Q3839_m!5Sf^58fTn%{Sx1%l^i0&&sU%e^$5iLY)`?$16Xh}3t znsCw$LNz~z&x2(_yaqhy_nZB>I@z_|zxs+zHUt+77-cF5qFP}llW0k`6eK4_B3q{V ziJ^OyXEuCp!WOsgi|?gKXj&5}?yP5Rd*5j_e(jaDULOZ?Af=|#Bh1uUl!{30MJ4xy z>>F${xNrA1cb+-yk&AK1Ueo-1!E^HjVW4yr6e#dzwkmvNsi;yB#n0mL$J4pb;gMrH ztkwmZC#hChnWPCIs`nu)o>~0v&)Mbbz84Og`f&f;mVfIL4B0*IB{oRNWOZe}@5Z&+ z;lHZt9R82d&3}6Nlr0-Dc}RX{l%zx2zncmPcLEDJ0hnxWg6WTu?@GzAJ0o!M*zVTQ!Z}>Ysbzdss?a zUUguS3>SZD+ehkqyIWroh`98K#I_?sX^b)3Vmi`I!0u*>1Iv&yhIP6!*HIggP0sCn zV8i3^vkY(OHRIt$LCoUP1+EBT!U84}#rcIjy4L2i7(JE$>x1hTwdtc2|v)nc0mpl1Gnkx zZ}`L+BOW?G%n=wP5YBom7=#hQzLyS$Q4?x1n`~8;-SnC>hClM_rlxN;()H%O5FN1x z9pZ>8O}wsLG^kQKTebr}jyVBXN3hNqfT9B!C-EdR`>50u8GKsmnJ^Ob2q46;N)uQVHQPmCRRdbovhe-pJhCb&YJ&o8#uR z`wNeAgGE`Ja?qMn>ISk8+weCNws_>%cpSRWsnPfJVnOnNKuJJmLWF9NT)uhz$0vM% zuZJ0ZJofCZ9y_>-_x+tOGg1+sa>}7y&z{rH->r-E>E5-4e16Cx_0jz2#56hN$-E~4)~-eG@y za4zxbFIwr0Q#zal3xHW5m?U|Y*F1=+TD|e{SMGUdydLU}ceN(I-yknPl>r8Mmhk`- z<5fHEcuI7c1^6}0oZf66bDeq5duoAHEW&-tfJr)#y-{9)$JEz_q^v!+yfR+y54fj7 z4&2%ex`d}(YOyG1%Hf(tENyd+3TzSt3#2cTeqS;`55F+A&7^nx~3VP((nIltaR=ICb=(BP005`U~S@A`lxOad;I( zDaagTf|h&nx*A4LV@*xJm8sFUM`B*>1&&`1panYET0#sQIiM|;95n0V=NsR<@Wb8i zPklb-5EX4XOwo=GTXW5bpx|XiU$J2|?d)?-xFA-XsP}ykp36F7XP4^%eFg2398D(U zWS;)y+SJFp9hiE1w+AkG`G6y#h@cU+ZGCZVu<@=caD`+6<4%-xLW{eIesu2_)8fkZk!envQ7TyYj-F)K0>Kc?YzO{9k~{d=}fCukrEI54_t~*h zZuqluZZeAvX7nV@r)r}DyL7D2CqDjTF?mFD%PayixkTxvBY5y$-UaMI0g+HIycRb2xHAqgNVK>Zq)_?2v69?$@+TD1c-#yxOY8Lk~{S%FUG&iffyC zM3F3QzJ2=aw5d3(D;0os(g0|oBj8I7c0+I*Ew|6b?>i<+iRmX{-TB|merSnCbUUV0ljcMj{4Ba}aV*`QO zAV?I1F=P?2wS%d4sd{Qx8y zu=vp9jyujmhT!V(M$D{0L?tYAgJ2N>8>xYcdNIPic+EGS9Aa zf+&%andd!vn;SPyK>5DDis}H+?ngsAUBg)Yc8IchZ^(;@ezbS)`Irv2`*zwCYLBtn^7>J&> zDK83h)6IaT3cSU*&$C)@?$GU?vSZX!8loglDxf+^T&5Yu>sFMwB=fP~XW^@O$zuQ% zChhRtn3E`@f&h~m93T*oT)vGi%z?@S1DAqhGhvl;!70}~;9&dIwOUB0UKGsN@4vB_ zzkA?SAJ+c^Z}?Sa1{$vhAn<8IDc{U7qSOM(g3g=%dk!X|ke00K3YCUF?FalKO3H@> z($|#ult2K4D5_KdF)Oh}V{!QmL z)Hhl9`gXQ#R2hpU^BDdyO)xMQ#4&?H$};^ojp$f~&{}h3lIt#r*z#Za{Udp7881c? zm+g}vNlr9VT1AP#$qWpqGg-qTo9$|nih8;HgC+|r-nn#|3afiF>=0|-947LZMG$9;$VpYr1R1rn8(beyf7th{g`ji;0+@ES&;lWb5nh}Qp z)5_x%d~Q)?ht9Hmos5FUbPgWwVCr8!(wun&$6uZ<6JUxyvV}<7EP4r^&CrR z$)Y+Ua~kPz0q97+vpGNiy<6Ts_QUW)`2KPKau^*N0U7r?e3{dfI3TqKNrU)t#~l~D zb<(YQlei=}q&P+O*1gAbQA-p^NuZk%A^?Z{fEK`AR;A~2)bXEFG?1%`LLyPqnA-_H z8ks;lGy*oKo8fyN2Gc^`L8<|U&jgcmn1Ni6CgP7j?5?tkw}(vRG6s@bS`ei@*7=5@ zk@@)Cjujg~B`#@Sqix8u#nMi8%?oMXjpj2&>hFO=|B#Hjy?(_g2Z5H_bR{A|!ADYe z?`qa}GXQw0c&+$7gmWHGYdL+y1Lx;S)!kgiiIRbiv#8yz246UObZU>v=_A$nea1V^ ztUhN(bQyR;E6yys(En`VtmMm+@ETFw2-Y#IGX@}xy8s9S2eqIv2tYT{gX|aQzPtBL ztN65}xEMI+GqnmpYDK8#8h|^?xuykR9EW`Y8Pf50Nm}-|WfWW%0nQZBQsCLFUCr;# zvIY0z@yI|lKS$RPd>xe(pJk;FG>Bs7vev{J)|P4Rx?yJhzrX21Q`--}*2A zJ?fp+-6fEBjz8T<=GAOIJ1(XjCY7(sdEX6cU*GFj1jxOo4176y4eXphbDMu3S}Q9@ zgFqAEP^!BXT0G6r4oDScG3?740RXSV8Ui7z1@k<{(-s^=K>Ff)z7U8o)ZG=vs-?z0 zy)${+`E!-*y^Rv+&)7sbInt@-Hcfi7-t>rg+RdSVhif^YN=qIk8e%5qudi!@)Zzw~ zo}Lz?V$ar9XE)qL_*w}opi~KStJ#XH2&-a2q+&-j05EzQh3Vm-6YM2GPQ`)+lNw{s z&pWy=*?g==T^VSi%6Y%hJGSq1L_Lt(PW}9$-eTMH=_dK5q~WvKr7(3^i=u&&aG z*BJwVmL7jnaN?jDdQad`0t}eIx<0@d0GRgOpK$s#?08efl8azsYR>P_mC%bRq6lzs z@%IYUa=UF9qiIsL>0?tepDlW)mTkNh(OL>h8Kxx}=S5oEhNkqFo z^WCg;`~;N@(|a-IXBYg80C_Dn;{ygW>%{3h4@}b5$V3hfc+59a%v1%M5l)$~l|>`e z?UEbeH4d*0Hzaetm?TakZG2pC_V9n7pI2?q*}9qsl7K}mUG9}%kQ(~%dk(2S-O*8< z*HTA8(~e+4tH-l{3!i0mO1kU60l=hG7@89&hDkcfaAuZ|Rx}?r?%;wL`@I)L*oLdg z1AfE12h-7yaV`$bGU2xQ-RKuWNby}(*aIs9jV%3meeTR{pSmvLircA2!IxUw%7L8f z2H$GC6`yt4bJOXN$a3i`b9oz8zYRiV7hb4 zxwUNYu)Ld7gi^{N3Y03Mg$Igy)T04dJial8MZYC55J&1pCNYDWH-M3*xHmewo_($0d`lvdLL|KHx3fX7i>3AnEAo*9isn$dWqkz`qxWy_X9 z5EQNrS&=NsvRRg83G$M~Trq2oa3zFXWHBNK2@8an<%koLgupss*aQd$b0k3o97sIa z5|%96vaI{Iq|w~nb?jF?GqS9VZE*!z^#6UfW_r4-tE;PCy?Ryk-qIqxWQCm71Qwjf^htGmv4W!cwPK6zm-b>B!0#x8Kyg$sGyw==EBDse z^3q1jL-L&(!wcAJZ~4|3*gK$#B$Bg2a?F&gc-@E<2nw+vP=OOL9AnU~jCAOObnor=zw&u=Y4I$bU1Y`dLjX9s|D`MX$Kl>D zgkdjNPy|B|VT{M9L75y)Q-}lu3cw?vH+(=HTHDajs>c%(&6Gy~Q=%{_BEY5QRxBJV z^UHMQUWa#@r~33#0>R=P(1=1){S*q&0pejCHzF`fp<(9=SLFmMRgk6b#Ke+U&vlHBY-o@atDfToKs72`*rg0U(DqkPzY~5ar-F3`K@W%dZA=c75W(*41~&blwZEHS!tc^Zuae zvfo7PeOD#a-Z0WILC8%M;UYF}BTiM2F}Q1V81!Cv=Z=4qE7f3*8tEV)Dv(GZK_VOo z=1}6!{39*anGG#z*I>{#{z?>&MFG&DN>#aD4%rb>7%@l~_n`@;8;+@a;-j{my*(t@ z*hTiN-wQj=ytU<%|MXDX4X>wWUJf=YSynP%8(#WaC}!`zK8!jHRVWzKT%OcbW=DB$ zmF18)&tY=K1++ub2iqR~)?1%@3Mm5v(Zp~Pi(JAe7Eb8a7P`yKX~~sYp0%MNwMXOo zC$0KvFW&PU(vpbCMIHb{jN<|$hN65VF$UUy0FD9Xhz%e$;}X0I8vl6Qg>jBGfvBA8 zWhxV4bDF0mZDoI#_c1;Hyq)vSaro}5I{^Tl(|rOS{z=lCT7u`sKpm*R>A=(1bCa-_5dyLG^!iG2DkH2{0#y{tZ$Tga9 zoS*cDFx~|65k>uFB-VrD@8nZlK zxoGdLwSl{x!gH%Fp`ozo?T^943%ZTGF%;=~Jj6&m zN0}o60Eoxy19(*cun3MnI}8(_bYPo3BzBL)bNW-Spz-Aoq##qOwA>uB(p<$gyPi2M z1R4>&8=Sj-_3G6d^R|7m%At5l8N^-yS#lygy1Reb6K`$VvLz|6$ZY?8Kh(u3Z#Fxi z^tpHCrI%i6BR$LVcKrJ-W5Fwdk-5LQ<58a12q(T)^V?0r7`Ry_nmP!&nhxX+?~~8; zxalwP-Q=Y~dg+0yDpuXj_G_I4EADZn>528huXSg?0)lmQzbA4wN3Nkil2LR@*fqnA zUq5bhcLa`p4G7z*s^Q8}l@N#<{cRC4vFUa13wJg){ycFf5b)<+4HI`PKYI^C)XxoS zaBRmB5qBTu8Bz{n-hl1jUG~~m3~lEd++c_}aFie5aa(AEX(N(56jKgB<}7n&U$VsX z4Yzg!vAyN}tP02qsvmgj-r(ooJcgtA$RWqnST!4Rjk;Wo__qjnAqCmW`rDv%Yc_TN zdVyO4huq(+6Loc$YAZ(S{?36(!H_Zf>SJwR8hAh7gDi(Ydbd$(ZAHoens#3LHa9~Z z)S}kbu3v7SD0frQZ}jGFYJaM!G3^daz{l$YNc!pf>hYUy@EbMvYS;%3@IElCv|X^i z`NID@oTs47?93NFa$j+2;^ZAJ>R%v)$_Ntp0O@GzI^&U(H*DBo%q7o1$AkdL&N18g zr;+kHJo9L3&A|upY$-S84dycFS>OM?uFR%ztbd;Zt4JYmyXm#z&@87Kb*9{fS^saU zxyzv$ORveXduqoM2mbH}`*RO(3;xL5Ql2gC)XKnB?3S9RFJ8bFKLS_|C%Ou-7C}R{ zjxXJ|{WCwvoCD@Kb5Q`mdg(GLkeYU}LP33i@4;NVer&&o%=U=*#jFI&t!7ojF`O~Q!Z44a=YojV)D5M8ZRGAP3ImP>c7Mi02UT+gVZ)l z0FzW86#|%xRV~Segl=>{B!6lJtBxtVnq>p9u$Vn!6$oJIk1ST*|5y*kClaEdBpH6_ zJFS;&O>Gc6xW>ZbgC$!4k@s3UXAMwt45U3D^Y%B-&qb%KEB%ktZ!xz@&%yGrc%Le= zxm$S_$KEN~(!%d^1)FU-pe)12qvh>rX=nQly$9tvn`c6^$jf1VJGd?Sz1SlCK`}3f z01!g3V#Nw|<;s=7?RJY`FbJGZr>Lr`f_}fB$sm>KQ^ng{<(4^5s(Hxo_j?pYVcp%` z(lO=4r&37dGXsMjCJmFiy1J=)8;L|ybrUGP z#hSk84*MMGb25j_Jq>0kxH-Pz=SUp0k}C`5ORB1>$~6q*$1azvP}4LSJOm2?I5o_O z8@T|$+qQ52%a-e|yH0!h$<6oaar&cy(eB{%3NERvtkjapZEW#wyc z@AxvtM8Gyt+t%tro^qqT@T+Zu>Xm`B!|g4Y-ZkmEzbEHP z_EdM6u#BM_(&PeCr+rV98~~8(fZcxOu&!7z%A24$`z!G5B04%+Nba3N-4N+0)Rea* z4}A6=E32q}7TW30b8{eB($dyS`Cu{t(mx=2_y&UMXl_1h*dU2qSy)J12^i}t^ovD- zzP_$_K?RKlwgNz1Zf^9KsqLjrO$RD~NODL>h#l_4OK$Oz-kr^7?9#%4xMpgrl@adC9J9OpDM?&?VtU$DQG4(KOrbmRTV=oujVHu|1I~xS z;&>BDYG)p`wVtG4tOFPjo=s|AdKS~omKfCnN=QPj>TmyE$+ycoZzYtXnSB_`r0m^_0!q{Z8&wroDauB66J94U26wmWxh(cpe2K;)&S zU_;N&Q7(!TUDb1kon1;3ScH{1RXM@T?wFor6h%O}7qBwKe3KQ10Ueiu>Q-;$xnt z6$t&%)n$3;Tv}0qxJ-xCA4oUhDZH@i&Q4z4Y@XXA@pmschqUQ6R(^(YN#0iKg43Dt?Zc9xPSmP zH8sgUY|av2Tf;)Oc3FxUT=RtnVdBZ3ZYfa%evin9;<`)>(Kj4-smFfa#U`+V?1Ng4 zYVr6L-i^&!5grUuVzFXFc8ojx-U~-3k4-*KuVTZFtI!F+;~1U6`RIqilJEUOI&67U z2j*2?{6>;s-eK!yOpCc=(UtU45!G3OuuvW0Th6H_HetO9c)TPRSei>6_Z`ta;sP^s zMeF|6FfvC0%VvO9_s}L(A_`efBR*4;Jc4-*zdpU5?VB*)I+b)pIE^1ZJh_CBHw;=R~*PJhoaY9>t=>yt*rFxUNgs)848ZCXa}A( zyfhNaT5xvnrKhL&W1$xH_3~(#$@&K#Zz>LfI05T%EU!Q@eK~5{RoS8xXSZ42{IL+^ z*l_T#L~w9$eN~N~vGG^q;cS`t6~DC~&fo*b>}+v0L1xOS7!DqZUb0}^m3r2&&O3-x z{Tu#fa=z__+6Xw=(n9j75Lf59rHL7VQwVz+7aWm4w%Cq7|6>BsVT$xGldx@X@%b91 zrNtJ@)~S_*PoifK58byT-ikC5qk(GF6As_DkBy~xPkYSni#*D)6%3h<-h|~eD36jH z9fhjp`WNyVsq_^H8%)fRv<$B%1bUsUGR?T#Dtf?jb+DcGDjVXM?dGO+JBl*4$x_x$ zI&`b{9EiEyT_0jx9M;6f0$ELJp0?bC{MAvIT!Z{wU0Yilwx{{_p6la>vWiAZ0uGdFvN6sYoV%%&g%h9$z99^NV;m>t4ab z!;=XpwP~>0@0CC>{z-R!4f8zLw|y*}5m5sBD$V$9YRYo`=&S>ULWMukRx7EiFE8)4 zeEL*8HZuV~E05h1CNCM-q$=n*h%RoIkBhiCzFrMD3h*Gz@o3>fJ`nN+*3$2Kb0@!lKc(%pT-QFRdd^Fp=e}y^qW%}p$&SsAL+ph}_iGsTwa_s-3Fl!sSsrog z=u~`5Basnh3E;#}XQT9O9y8+#MMX9RlwPn_QC23vxNqJ{Hb9e2gse)pZcO}}Lx4u# zHhg=LRfTTW06u&5AX^72OLQR>D>w_KtBlFuveV6tP5u(f9#}M9*wwv7`lW!De>jap zBW4=e-nq)3K8}budWEIyZq=cTOk(?elFhex;`5ESN|4%baQ;%??vq<%lhL><_mKu@ z-*E{KjIiUv$y<5a0WU2XDtqkj?+;nhmfff_6OLW_$>Dg6+d!o;(IbHBzXRVWxi}-C zQ#-v@9n}QF;ZJ*MvDiUl1=bUibWkej#!Pf{em>`EFGH7U#n--CjqV#ZhHfT^G%^nu z7gH#1q9T`EQ#c%-@kK@Y%Y`-OB;)JFS({We=sbohFmt{PDOD0J)6ZcnhhYbN`&d=g zt%*jTC^ydql9#%H&2#=c)6L#j>kf>q1NZ+J0Kl&tKGOdd6Z5g_^sD4}$G~`nu{yqU zR@&CDAJOD+En8uF1)gg3=j2CsEW_RsiL<|o%-P7StgA}JfwK?mcDA-AGIW_uvy54iJ{b*e zJ7{uo5< z;q{`^-N{Kf__e%TAZ_Zvk#2v`+F*P*+;^`xlZtknscVeiPVN4E38D6OD;t~Si4HG_ z{FKA^b7+={bD9{yap{CyGJwGpJNf$l%*`BHA+n(gSIKeJ^qAqCe@+i%i1N#WvI^L~ z+Wab2BP24ASl;bINn8atd-`Gh_bfeX8kMS_>Uw*V#kI7w*nK3CVQ0h=!{*z#@9mBY zHvsyLE!v%u6B4euSJk{}xwmHfnfvTB>{(rN{X@Ef^hH>1kNzmt zHBJr=Tx+B=ejv;Gv;2r&4gonVc3&9~^7`do`SNls$@51`g9Rry?4c$wosouG8L+jE z#rUU{?EHt%zn0>3F#xt>r1FqsykPCdQNLCfXXNN_NiaE`ISt+&)&O*uQD}9#L%n-L zLqmqIW1~h2&_5o_sn#c+An}RnqBK7eWT4p;>JFSgCWgA$<9h(XnjX+LU%U@u6wtd{ z-GAZwDo21{liafJA3m<0H`b0(lXsf_eGpdtcj@|v+cjVf4m2a+>(tcLQzY_SjO;Ud zc`@%U%fv}7EM$lh7eXV@fyza(6Z`T~xEItKy)>EWG2KnH>ihmxy_Kn}*Yq4co(ZDb z^b8l%2?RoknVFen#OcPX5NQi|p&=Y?;q7)hoRX2TpQDwO6I%DiebLfoA(L|dj6Ewz z_WO$yz81wrEa$bVED1Kf3a0AXEr%{NP#xYkv( z8sicZA$DMH3Q+aQ4>h}uJv{w#SXkJ0c-NH>JpM;d&*gL0NW?t}iSWkPPQLom;H*0V z_F$}l(-?mq}@35Zf>3LR?vDjf-hxDc@G|0fT;2__#KpL`}cGmClb#((T zH8ixNWGfz^IGfV0-7a!5ooE5+RA!0e1eo%wM;=j?q7$#anHttQF#2k8Ch$L zHK26PlwfXdKBb_fG+cV8rM2~#ZVYai zllu<_#IhGTx4j@5w_9d(&=;&HCm$6H+>^>%wD}81__zv?!?1$$=>Y=FA;!l#q@PAM--XOG|ep^*=skfj9VA9SQZ393?P!DvyU73ppGuW%|R58dhD# z4c?s7Rmw;5w^PCEwH2MQ{4Ct&=4NlL^lHkWzy~aU8rbFW${(;J7_L7k_AyKFS@TN6_FQvy0PRv**D&*VH!keb7Mk|V$@3Zr&czCS zPv^8CTS60g;^*(re)O;+;igUX3t8>k-%7cG5+S~HfaeAV{Sh>PV|{r}vSPvBb>-r` zcVh-m$~zWf-4SIyZ=PCPkI-{VuPl2qQ5+L-`gf{%UL*5Ym$GuOU}97P!XJ+PsXbaH zkBHF`7OYqlwo{-vW(D*&v#k0NlTAN^1)x$m`BgW$)Ho6SqTTax$)_2hA+dw-vMd<-l*qssZKQ zg8K8wk($GK}MJ3BkMnU_I9LC0UCqRJHorrFYU z%587fu5l|nHn9PYTYGv~FzN*k;G)XPO2gEYl+Wk-H3GQ3&Z(_%WqDp(zj590htg1x zP~tv>#@NFc4h{-()$`G%GBG#5P!Z_U2kgqkq)}_!GIEm! zHi+|HFQ5Yar`K&IaVT0n;me;CC@mswZayy%cP{t&p64Iq!v~SFvt#W%)K*R|ZW8y{ zmz-22)Bnu+<^M`!sD1^0=%PKFf#@7Yjd~x^qQ0}XGe0I3K&rW1&MyrV%}4($JZVDC zFMIbLXemq2e_8l_xP*0p%&$Pu z!qE;F`bpf?E=&J>oVe-n&0-c_R85mes6>kOOSQM5c_s6dJ>>r->c=ZrR7H~?{% block title %}Five a Day{% endblock %} {% load static %} + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/project/project/settings.py b/project/project/settings.py index 967c73a..1a77557 100644 --- a/project/project/settings.py +++ b/project/project/settings.py @@ -15,7 +15,7 @@ # NOTA: Usa `make version x.y.z` para actualizar ambos sitios a la vez: # - pyproject.toml (campo version) # - README.md (badge y tabla de versiones — gestionado por la skill update-readme) -APP_VERSION = os.getenv("APP_VERSION", "1.0.9") +APP_VERSION = os.getenv("APP_VERSION", "1.0.10") # ============================================================================ # SECURITY SETTINGS @@ -133,7 +133,7 @@ TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", - "DIRS": [], + "DIRS": [BASE_DIR / "templates"], "APP_DIRS": True, "OPTIONS": { "context_processors": [ diff --git a/project/static/apple-touch-icon.png b/project/static/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..18031cb65444aff9c5355dbd462737500ec8d11d GIT binary patch literal 18091 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 UdU-~n{Qv*}07*qoM6N<$f)y4D-v9sr literal 70564 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 literal 0 HcmV?d00001 diff --git a/project/templates/admin/base_site.html b/project/templates/admin/base_site.html new file mode 100644 index 0000000..7875e8c --- /dev/null +++ b/project/templates/admin/base_site.html @@ -0,0 +1,24 @@ +{% extends "admin/base.html" %} +{% load i18n static %} + +{% block title %}{{ title }} | Five a Day Admin{% endblock %} + +{% block extrastyle %} +{{ block.super }} + +{% endblock %} + +{% block branding %} + +{% endblock %} + +{% block nav-global %}{% endblock %} diff --git a/project/templates/admin/index.html b/project/templates/admin/index.html new file mode 100644 index 0000000..d2b0d9c --- /dev/null +++ b/project/templates/admin/index.html @@ -0,0 +1,93 @@ +{% extends "admin/base_site.html" %} +{% load i18n log static %} + +{% block title %}{% trans 'Panel de administración' %} | {{ block.super }}{% endblock %} + +{% block content %} +
+ +
+ +
+

¡Bienvenido/a al panel de gestión!

+

+ Desde aquí puedes gestionar estudiantes, familias, matrículas, pagos, + horarios y comunicaciones de Five a Day English Academy. + Usa el menú de la izquierda o selecciona una sección de abajo. +

+
+
+ + {% if app_list %} + {% for app in app_list %} +
+
Release tag${{ steps.tag.outputs.tag }} (${{ steps.tag.outputs.created == 'true' && 'newly created' || 'already existed — no version bump on this push' }})
Commit${{ steps.info.outputs.message }}
Author${{ steps.info.outputs.author }}
SHA${{ steps.info.outputs.short_sha }}
+ + {% for model in app.models %} + + {% if model.admin_url %} + + {% else %} + + {% endif %} + + + {% endfor %} +
+ + {{ app.verbose_name }} + +
{{ model.verbose_name_plural|capfirst }}{{ model.verbose_name_plural|capfirst }} + {% if model.add_url %} + {% trans 'Añadir' %} + {% endif %} + {% if model.admin_url %} + {% if model.view_only %} + {% trans 'Ver' %} + {% else %} + {% trans 'Editar' %} + {% endif %} + {% endif %} +
+ + {% endfor %} + {% else %} +

{% trans "No tienes permisos para ver o editar nada." %}

+ {% endif %} + + +{% endblock %} + +{% block sidebar %} + +{% endblock %} diff --git a/project/templates/admin/login.html b/project/templates/admin/login.html new file mode 100644 index 0000000..bfe01d4 --- /dev/null +++ b/project/templates/admin/login.html @@ -0,0 +1,67 @@ +{% extends "admin/base_site.html" %} +{% load i18n static %} + +{% block extrastyle %} +{{ block.super }} + +{% endblock %} + +{% block bodyclass %}login{% endblock %} + +{% block usertools %}{% endblock %} +{% block nav-sidebar %}{% endblock %} +{% block breadcrumbs %}{% endblock %} +{% block content_title %}{% endblock %} + +{% block content %} +
+ + + {% if user.is_authenticated %} +

+ {% blocktranslate trimmed %} + Has iniciado sesión como {{ username }}, pero no tienes permiso para acceder a esta página. + ¿Deseas entrar con una cuenta diferente? + {% endblocktranslate %} +

+ {% endif %} + + {% if form.errors and not form.non_field_errors %} +

+ {% if form.errors.items|length == 1 %} + {% trans "Por favor, corrige el error." %} + {% else %} + {% trans "Por favor, corrige los errores." %} + {% endif %} +

+ {% endif %} + + {% if form.non_field_errors %} + {% for error in form.non_field_errors %} +

{{ error }}

+ {% endfor %} + {% endif %} + +
+ {% csrf_token %} +
+ {{ form.username.errors }} + + {{ form.username }} +
+
+ {{ form.password.errors }} + + {{ form.password }} + +
+
+ +
+
+
+{% endblock %} diff --git a/pyproject.toml b/pyproject.toml index 7198ac0..b9048d6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "five-a-day" -version = "1.0.9" +version = "1.0.10" description = "Five a Day management software" readme = "README.md" requires-python = ">=3.12" @@ -9,7 +9,7 @@ authors = [ ] dependencies = [ "django>=5.2.5,<6", - "pandas>=2.3.1,<3", + "pandas>=2.3.1,<4", "django-filter>=25.1,<26", "djangorestframework>=3.16.1,<4", "markdown>=3.8.2,<4", @@ -23,7 +23,7 @@ dependencies = [ "httpx>=0.28.1,<1", "python-dotenv>=1.2.1,<2", "psycopg2-binary>=2.9.9,<3", - "gunicorn>=21.2.0,<23", + "gunicorn>=21.2.0,<24", "whitenoise>=6.11.0,<7", "dj-database-url>=3.0.1,<4", "openpyxl>=3.1.5,<4", diff --git a/uv.lock b/uv.lock index bd16390..e4e789a 100644 --- a/uv.lock +++ b/uv.lock @@ -2,8 +2,15 @@ version = 1 revision = 3 requires-python = ">=3.12" resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version < '3.13'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.13' and sys_platform == 'win32'", + "python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] [[package]] @@ -695,7 +702,7 @@ wheels = [ [[package]] name = "five-a-day" -version = "1.0.9" +version = "1.0.10" source = { editable = "." } dependencies = [ { name = "celery" }, @@ -752,11 +759,11 @@ requires-dist = [ { name = "django-storages", specifier = ">=1.14.6,<2" }, { name = "djangorestframework", specifier = ">=3.16.1,<4" }, { name = "gspread", specifier = ">=6.2.1,<7" }, - { name = "gunicorn", specifier = ">=21.2.0,<23" }, + { name = "gunicorn", specifier = ">=21.2.0,<24" }, { name = "httpx", specifier = ">=0.28.1,<1" }, { name = "markdown", specifier = ">=3.8.2,<4" }, { name = "openpyxl", specifier = ">=3.1.5,<4" }, - { name = "pandas", specifier = ">=2.3.1,<3" }, + { name = "pandas", specifier = ">=2.3.1,<4" }, { name = "psycopg2-binary", specifier = ">=2.9.9,<3" }, { name = "python-dotenv", specifier = ">=1.2.1,<2" }, { name = "whitenoise", specifier = ">=6.11.0,<7" }, @@ -876,14 +883,14 @@ wheels = [ [[package]] name = "gunicorn" -version = "22.0.0" +version = "23.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "packaging" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1e/88/e2f93c5738a4c1f56a458fc7a5b1676fc31dcdbb182bef6b40a141c17d66/gunicorn-22.0.0.tar.gz", hash = "sha256:4a0b436239ff76fb33f11c07a16482c521a7e09c1ce3cc293c2330afe01bec63", size = 3639760, upload-time = "2024-04-16T22:58:19.218Z" } +sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031, upload-time = "2024-08-10T20:25:27.378Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/97/6d610ae77b5633d24b69c2ff1ac3044e0e565ecbd1ec188f02c45073054c/gunicorn-22.0.0-py3-none-any.whl", hash = "sha256:350679f91b24062c86e386e198a15438d53a7a8207235a78ba1b53df4c4378d9", size = 84443, upload-time = "2024-04-16T22:58:15.233Z" }, + { url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029, upload-time = "2024-08-10T20:25:24.996Z" }, ] [[package]] @@ -1286,49 +1293,54 @@ wheels = [ [[package]] name = "pandas" -version = "2.3.3" +version = "3.0.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, { name = "python-dateutil" }, - { name = "pytz" }, - { name = "tzdata" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846, upload-time = "2025-09-29T23:19:48.856Z" }, - { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618, upload-time = "2025-09-29T23:39:08.659Z" }, - { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212, upload-time = "2025-09-29T23:19:59.765Z" }, - { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693, upload-time = "2025-09-29T23:20:14.098Z" }, - { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002, upload-time = "2025-09-29T23:20:26.76Z" }, - { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" }, - { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722, upload-time = "2025-09-29T23:20:54.139Z" }, - { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671, upload-time = "2025-09-29T23:21:05.024Z" }, - { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807, upload-time = "2025-09-29T23:21:15.979Z" }, - { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872, upload-time = "2025-09-29T23:21:27.165Z" }, - { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371, upload-time = "2025-09-29T23:21:40.532Z" }, - { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333, upload-time = "2025-09-29T23:21:55.77Z" }, - { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120, upload-time = "2025-09-29T23:22:10.109Z" }, - { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991, upload-time = "2025-09-29T23:25:04.889Z" }, - { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227, upload-time = "2025-09-29T23:22:24.343Z" }, - { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056, upload-time = "2025-09-29T23:22:37.762Z" }, - { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189, upload-time = "2025-09-29T23:22:51.688Z" }, - { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912, upload-time = "2025-09-29T23:23:05.042Z" }, - { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160, upload-time = "2025-09-29T23:23:28.57Z" }, - { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" }, - { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635, upload-time = "2025-09-29T23:25:52.486Z" }, - { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079, upload-time = "2025-09-29T23:26:33.204Z" }, - { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049, upload-time = "2025-09-29T23:27:15.384Z" }, - { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638, upload-time = "2025-09-29T23:27:51.625Z" }, - { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834, upload-time = "2025-09-29T23:28:21.289Z" }, - { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925, upload-time = "2025-09-29T23:28:58.261Z" }, - { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071, upload-time = "2025-09-29T23:32:27.484Z" }, - { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504, upload-time = "2025-09-29T23:29:31.47Z" }, - { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702, upload-time = "2025-09-29T23:29:54.591Z" }, - { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535, upload-time = "2025-09-29T23:30:21.003Z" }, - { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" }, - { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" }, - { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" }, + { name = "tzdata", marker = "sys_platform == 'emscripten' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/99/b342345300f13440fe9fe385c3c481e2d9a595ee3bab4d3219247ac94e9a/pandas-3.0.2.tar.gz", hash = "sha256:f4753e73e34c8d83221ba58f232433fca2748be8b18dbca02d242ed153945043", size = 4645855, upload-time = "2026-03-31T06:48:30.816Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/b0/c20bd4d6d3f736e6bd6b55794e9cd0a617b858eaad27c8f410ea05d953b7/pandas-3.0.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:232a70ebb568c0c4d2db4584f338c1577d81e3af63292208d615907b698a0f18", size = 10347921, upload-time = "2026-03-31T06:46:33.36Z" }, + { url = "https://files.pythonhosted.org/packages/35/d0/4831af68ce30cc2d03c697bea8450e3225a835ef497d0d70f31b8cdde965/pandas-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:970762605cff1ca0d3f71ed4f3a769ea8f85fc8e6348f6e110b8fea7e6eb5a14", size = 9888127, upload-time = "2026-03-31T06:46:36.253Z" }, + { url = "https://files.pythonhosted.org/packages/61/a9/16ea9346e1fc4a96e2896242d9bc674764fb9049b0044c0132502f7a771e/pandas-3.0.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aff4e6f4d722e0652707d7bcb190c445fe58428500c6d16005b02401764b1b3d", size = 10399577, upload-time = "2026-03-31T06:46:39.224Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a8/3a61a721472959ab0ce865ef05d10b0d6bfe27ce8801c99f33d4fa996e65/pandas-3.0.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef8b27695c3d3dc78403c9a7d5e59a62d5464a7e1123b4e0042763f7104dc74f", size = 10880030, upload-time = "2026-03-31T06:46:42.412Z" }, + { url = "https://files.pythonhosted.org/packages/da/65/7225c0ea4d6ce9cb2160a7fb7f39804871049f016e74782e5dade4d14109/pandas-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f8d68083e49e16b84734eb1a4dcae4259a75c90fb6e2251ab9a00b61120c06ab", size = 11409468, upload-time = "2026-03-31T06:46:45.2Z" }, + { url = "https://files.pythonhosted.org/packages/fa/5b/46e7c76032639f2132359b5cf4c785dd8cf9aea5ea64699eac752f02b9db/pandas-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:32cc41f310ebd4a296d93515fcac312216adfedb1894e879303987b8f1e2b97d", size = 11936381, upload-time = "2026-03-31T06:46:48.293Z" }, + { url = "https://files.pythonhosted.org/packages/7b/8b/721a9cff6fa6a91b162eb51019c6243b82b3226c71bb6c8ef4a9bd65cbc6/pandas-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:a4785e1d6547d8427c5208b748ae2efb64659a21bd82bf440d4262d02bfa02a4", size = 9744993, upload-time = "2026-03-31T06:46:51.488Z" }, + { url = "https://files.pythonhosted.org/packages/d5/18/7f0bd34ae27b28159aa80f2a6799f47fda34f7fb938a76e20c7b7fe3b200/pandas-3.0.2-cp312-cp312-win_arm64.whl", hash = "sha256:08504503f7101300107ecdc8df73658e4347586db5cfdadabc1592e9d7e7a0fd", size = 9056118, upload-time = "2026-03-31T06:46:54.548Z" }, + { url = "https://files.pythonhosted.org/packages/bf/ca/3e639a1ea6fcd0617ca4e8ca45f62a74de33a56ae6cd552735470b22c8d3/pandas-3.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b5918ba197c951dec132b0c5929a00c0bf05d5942f590d3c10a807f6e15a57d3", size = 10321105, upload-time = "2026-03-31T06:46:57.327Z" }, + { url = "https://files.pythonhosted.org/packages/0b/77/dbc82ff2fb0e63c6564356682bf201edff0ba16c98630d21a1fb312a8182/pandas-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d606a041c89c0a474a4702d532ab7e73a14fe35c8d427b972a625c8e46373668", size = 9864088, upload-time = "2026-03-31T06:46:59.935Z" }, + { url = "https://files.pythonhosted.org/packages/5c/2b/341f1b04bbca2e17e13cd3f08c215b70ef2c60c5356ef1e8c6857449edc7/pandas-3.0.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:710246ba0616e86891b58ab95f2495143bb2bc83ab6b06747c74216f583a6ac9", size = 10369066, upload-time = "2026-03-31T06:47:02.792Z" }, + { url = "https://files.pythonhosted.org/packages/12/c5/cbb1ffefb20a93d3f0e1fdcda699fb84976210d411b008f97f48bf6ce27e/pandas-3.0.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5d3cfe227c725b1f3dff4278b43d8c784656a42a9325b63af6b1492a8232209e", size = 10876780, upload-time = "2026-03-31T06:47:06.205Z" }, + { url = "https://files.pythonhosted.org/packages/98/fe/2249ae5e0a69bd0ddf17353d0a5d26611d70970111f5b3600cdc8be883e7/pandas-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c3b723df9087a9a9a840e263ebd9f88b64a12075d1bf2ea401a5a42f254f084d", size = 11375181, upload-time = "2026-03-31T06:47:09.383Z" }, + { url = "https://files.pythonhosted.org/packages/de/64/77a38b09e70b6464883b8d7584ab543e748e42c1b5d337a2ee088e0df741/pandas-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a3096110bf9eac0070b7208465f2740e2d8a670d5cb6530b5bb884eca495fd39", size = 11928899, upload-time = "2026-03-31T06:47:12.686Z" }, + { url = "https://files.pythonhosted.org/packages/5e/52/42855bf626868413f761addd574acc6195880ae247a5346477a4361c3acb/pandas-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:07a10f5c36512eead51bc578eb3354ad17578b22c013d89a796ab5eee90cd991", size = 9746574, upload-time = "2026-03-31T06:47:15.64Z" }, + { url = "https://files.pythonhosted.org/packages/88/39/21304ae06a25e8bf9fc820d69b29b2c495b2ae580d1e143146c309941760/pandas-3.0.2-cp313-cp313-win_arm64.whl", hash = "sha256:5fdbfa05931071aba28b408e59226186b01eb5e92bea2ab78b65863ca3228d84", size = 9047156, upload-time = "2026-03-31T06:47:18.595Z" }, + { url = "https://files.pythonhosted.org/packages/72/20/7defa8b27d4f330a903bb68eea33be07d839c5ea6bdda54174efcec0e1d2/pandas-3.0.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:dbc20dea3b9e27d0e66d74c42b2d0c1bed9c2ffe92adea33633e3bedeb5ac235", size = 10756238, upload-time = "2026-03-31T06:47:22.012Z" }, + { url = "https://files.pythonhosted.org/packages/e9/95/49433c14862c636afc0e9b2db83ff16b3ad92959364e52b2955e44c8e94c/pandas-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b75c347eff42497452116ce05ef461822d97ce5b9ff8df6edacb8076092c855d", size = 10408520, upload-time = "2026-03-31T06:47:25.197Z" }, + { url = "https://files.pythonhosted.org/packages/3b/f8/462ad2b5881d6b8ec8e5f7ed2ea1893faa02290d13870a1600fe72ad8efc/pandas-3.0.2-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1478075142e83a5571782ad007fb201ed074bdeac7ebcc8890c71442e96adf7", size = 10324154, upload-time = "2026-03-31T06:47:28.097Z" }, + { url = "https://files.pythonhosted.org/packages/0a/65/d1e69b649cbcddda23ad6e4c40ef935340f6f652a006e5cbc3555ac8adb3/pandas-3.0.2-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5880314e69e763d4c8b27937090de570f1fb8d027059a7ada3f7f8e98bdcb677", size = 10714449, upload-time = "2026-03-31T06:47:30.85Z" }, + { url = "https://files.pythonhosted.org/packages/47/a4/85b59bc65b8190ea3689882db6cdf32a5003c0ccd5a586c30fdcc3ffc4fc/pandas-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b5329e26898896f06035241a626d7c335daa479b9bbc82be7c2742d048e41172", size = 11338475, upload-time = "2026-03-31T06:47:34.026Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c4/bc6966c6e38e5d9478b935272d124d80a589511ed1612a5d21d36f664c68/pandas-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:81526c4afd31971f8b62671442a4b2b51e0aa9acc3819c9f0f12a28b6fcf85f1", size = 11786568, upload-time = "2026-03-31T06:47:36.941Z" }, + { url = "https://files.pythonhosted.org/packages/e8/74/09298ca9740beed1d3504e073d67e128aa07e5ca5ca2824b0c674c0b8676/pandas-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:7cadd7e9a44ec13b621aec60f9150e744cfc7a3dd32924a7e2f45edff31823b0", size = 10488652, upload-time = "2026-03-31T06:47:40.612Z" }, + { url = "https://files.pythonhosted.org/packages/bb/40/c6ea527147c73b24fc15c891c3fcffe9c019793119c5742b8784a062c7db/pandas-3.0.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:db0dbfd2a6cdf3770aa60464d50333d8f3d9165b2f2671bcc299b72de5a6677b", size = 10326084, upload-time = "2026-03-31T06:47:43.834Z" }, + { url = "https://files.pythonhosted.org/packages/95/25/bdb9326c3b5455f8d4d3549fce7abcf967259de146fe2cf7a82368141948/pandas-3.0.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0555c5882688a39317179ab4a0ed41d3ebc8812ab14c69364bbee8fb7a3f6288", size = 9914146, upload-time = "2026-03-31T06:47:46.67Z" }, + { url = "https://files.pythonhosted.org/packages/8d/77/3a227ff3337aa376c60d288e1d61c5d097131d0ac71f954d90a8f369e422/pandas-3.0.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:01f31a546acd5574ef77fe199bc90b55527c225c20ccda6601cf6b0fd5ed597c", size = 10444081, upload-time = "2026-03-31T06:47:49.681Z" }, + { url = "https://files.pythonhosted.org/packages/15/88/3cdd54fa279341afa10acf8d2b503556b1375245dccc9315659f795dd2e9/pandas-3.0.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:deeca1b5a931fdf0c2212c8a659ade6d3b1edc21f0914ce71ef24456ca7a6535", size = 10897535, upload-time = "2026-03-31T06:47:53.033Z" }, + { url = "https://files.pythonhosted.org/packages/06/9d/98cc7a7624f7932e40f434299260e2917b090a579d75937cb8a57b9d2de3/pandas-3.0.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0f48afd9bb13300ffb5a3316973324c787054ba6665cda0da3fbd67f451995db", size = 11446992, upload-time = "2026-03-31T06:47:56.193Z" }, + { url = "https://files.pythonhosted.org/packages/9a/cd/19ff605cc3760e80602e6826ddef2824d8e7050ed80f2e11c4b079741dc3/pandas-3.0.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6c4d8458b97a35717b62469a4ea0e85abd5ed8687277f5ccfc67f8a5126f8c53", size = 11968257, upload-time = "2026-03-31T06:47:59.137Z" }, + { url = "https://files.pythonhosted.org/packages/db/60/aba6a38de456e7341285102bede27514795c1eaa353bc0e7638b6b785356/pandas-3.0.2-cp314-cp314-win_amd64.whl", hash = "sha256:b35d14bb5d8285d9494fe93815a9e9307c0876e10f1e8e89ac5b88f728ec8dcf", size = 9865893, upload-time = "2026-03-31T06:48:02.038Z" }, + { url = "https://files.pythonhosted.org/packages/08/71/e5ec979dd2e8a093dacb8864598c0ff59a0cee0bbcdc0bfec16a51684d4f/pandas-3.0.2-cp314-cp314-win_arm64.whl", hash = "sha256:63d141b56ef686f7f0d714cfb8de4e320475b86bf4b620aa0b7da89af8cbdbbb", size = 9188644, upload-time = "2026-03-31T06:48:05.045Z" }, + { url = "https://files.pythonhosted.org/packages/f1/6c/7b45d85db19cae1eb524f2418ceaa9d85965dcf7b764ed151386b7c540f0/pandas-3.0.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:140f0cffb1fa2524e874dde5b477d9defe10780d8e9e220d259b2c0874c89d9d", size = 10776246, upload-time = "2026-03-31T06:48:07.789Z" }, + { url = "https://files.pythonhosted.org/packages/a8/3e/7b00648b086c106e81766f25322b48aa8dfa95b55e621dbdf2fdd413a117/pandas-3.0.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ae37e833ff4fed0ba352f6bdd8b73ba3ab3256a85e54edfd1ab51ae40cca0af8", size = 10424801, upload-time = "2026-03-31T06:48:10.897Z" }, + { url = "https://files.pythonhosted.org/packages/da/6e/558dd09a71b53b4008e7fc8a98ec6d447e9bfb63cdaeea10e5eb9b2dabe8/pandas-3.0.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d888a5c678a419a5bb41a2a93818e8ed9fd3172246555c0b37b7cc27027effd", size = 10345643, upload-time = "2026-03-31T06:48:13.7Z" }, + { url = "https://files.pythonhosted.org/packages/be/e3/921c93b4d9a280409451dc8d07b062b503bbec0531d2627e73a756e99a82/pandas-3.0.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b444dc64c079e84df91baa8bf613d58405645461cabca929d9178f2cd392398d", size = 10743641, upload-time = "2026-03-31T06:48:16.659Z" }, + { url = "https://files.pythonhosted.org/packages/56/ca/fd17286f24fa3b4d067965d8d5d7e14fe557dd4f979a0b068ac0deaf8228/pandas-3.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4544c7a54920de8eeacaa1466a6b7268ecfbc9bc64ab4dbb89c6bbe94d5e0660", size = 11361993, upload-time = "2026-03-31T06:48:19.475Z" }, + { url = "https://files.pythonhosted.org/packages/e4/a5/2f6ed612056819de445a433ca1f2821ac3dab7f150d569a59e9cc105de1d/pandas-3.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:734be7551687c00fbd760dc0522ed974f82ad230d4a10f54bf51b80d44a08702", size = 11815274, upload-time = "2026-03-31T06:48:22.695Z" }, + { url = "https://files.pythonhosted.org/packages/00/2f/b622683e99ec3ce00b0854bac9e80868592c5b051733f2cf3a868e5fea26/pandas-3.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:57a07209bebcbcf768d2d13c9b78b852f9a15978dac41b9e6421a81ad4cdd276", size = 10888530, upload-time = "2026-03-31T06:48:25.806Z" }, + { url = "https://files.pythonhosted.org/packages/cb/2b/f8434233fab2bd66a02ec014febe4e5adced20e2693e0e90a07d118ed30e/pandas-3.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:5371b72c2d4d415d08765f32d689217a43227484e81b2305b52076e328f6f482", size = 9455341, upload-time = "2026-03-31T06:48:28.418Z" }, ] [[package]] @@ -1670,15 +1682,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, ] -[[package]] -name = "pytz" -version = "2026.1.post1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/56/db/b8721d71d945e6a8ac63c0fc900b2067181dbb50805958d4d4661cf7d277/pytz-2026.1.post1.tar.gz", hash = "sha256:3378dde6a0c3d26719182142c56e60c7f9af7e968076f31aae569d72a0358ee1", size = 321088, upload-time = "2026-03-03T07:47:50.683Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/99/781fe0c827be2742bcc775efefccb3b048a3a9c6ce9aec0cbf4a101677e5/pytz-2026.1.post1-py2.py3-none-any.whl", hash = "sha256:f2fd16142fda348286a75e1a524be810bb05d444e5a081f37f7affc635035f7a", size = 510489, upload-time = "2026-03-03T07:47:49.167Z" }, -] - [[package]] name = "pyyaml" version = "6.0.3" From 2e948bb2857302648751c8b0ecd6794710fc79ad Mon Sep 17 00:00:00 2001 From: Joaquin Hernandez Martinez Date: Wed, 22 Apr 2026 14:13:15 +0200 Subject: [PATCH 8/8] v1.0.11 - Testing env fixes, CI hardening (Docker build/publish/Trivy/Scorecard/dep-review), static files cleanup --- .github/workflows/ci.yml | 112 +++- .github/workflows/dependabot-auto-merge.yml | 33 ++ .github/workflows/dependency-review.yml | 26 + .github/workflows/scorecard.yml | 49 ++ CLAUDE.md | 1 + Dockerfile | 2 + Makefile | 349 ++++-------- README.md | 286 ++++++---- docker-compose.testing.yml | 14 +- docker-compose.yml | 13 + project/core/context_processors.py | 1 + project/core/static/css/admin_custom.css | 526 ++++++++++++++++++ project/{ => core}/static/css/email.css | 0 project/core/static/images/divider.svg | 239 ++++++++ .../static/images/logo_white_bg.png | Bin project/core/static/js/login_effects.js | 69 +++ project/core/static/js/login_seasonal.js | 0 project/core/templates/login.html | 177 ++++-- project/project/settings.py | 5 +- project/static/apple-touch-icon.png | Bin 18091 -> 0 bytes project/static/css/admin_custom.css | 296 ---------- project/static/favicon-32x32.png | Bin 1552 -> 0 bytes project/static/favicon.ico | Bin 600 -> 0 bytes project/static/images/logo.png | Bin 120109 -> 0 bytes project/templates/admin/base_site.html | 4 + project/templates/admin/index.html | 303 ++++++++-- project/templates/admin/login.html | 14 +- pyproject.toml | 2 +- uv.lock | 2 +- 29 files changed, 1797 insertions(+), 726 deletions(-) create mode 100644 .github/workflows/dependabot-auto-merge.yml create mode 100644 .github/workflows/dependency-review.yml create mode 100644 .github/workflows/scorecard.yml create mode 100644 project/core/static/css/admin_custom.css rename project/{ => core}/static/css/email.css (100%) create mode 100644 project/core/static/images/divider.svg rename project/{ => core}/static/images/logo_white_bg.png (100%) create mode 100644 project/core/static/js/login_effects.js create mode 100644 project/core/static/js/login_seasonal.js delete mode 100644 project/static/apple-touch-icon.png delete mode 100644 project/static/css/admin_custom.css delete mode 100644 project/static/favicon-32x32.png delete mode 100644 project/static/favicon.ico delete mode 100644 project/static/images/logo.png diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6c185de..1bfbec4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,14 @@ jobs: - name: Bandit — security linter run: PYTHONUTF8=1 uv run bandit -r project/ -c pyproject.toml + - name: pip-audit — dependency CVE scan + run: uv run pip-audit + + - name: Hadolint — Dockerfile lint + uses: hadolint/hadolint-action@v3.1.0 + with: + dockerfile: Dockerfile + # ============================================================ # JOB 2: Type check — mypy (no DB needed) # ============================================================ @@ -150,7 +158,7 @@ jobs: - name: Upload coverage to Codecov if: always() - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: files: project/coverage.xml fail_ci_if_error: false @@ -163,3 +171,105 @@ jobs: name: coverage-report path: project/coverage.xml retention-days: 7 + + # ============================================================ + # JOB 4: Docker build — validates Dockerfile builds cleanly + # ============================================================ + docker-build: + name: Docker build + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build image (no push) + uses: docker/build-push-action@v6 + with: + context: . + push: false + tags: five-a-day:ci + cache-from: type=gha + cache-to: type=gha,mode=max + + # ============================================================ + # JOB 5: Trivy — filesystem CVE scan → GitHub Security tab + # ============================================================ + trivy: + name: Trivy CVE scan + runs-on: ubuntu-latest + permissions: + security-events: write + + steps: + - uses: actions/checkout@v4 + + - name: Trivy — scan Python dependencies and filesystem + uses: aquasecurity/trivy-action@v0.35.0 + with: + scan-type: fs + scan-ref: . + format: sarif + output: trivy-results.sarif + severity: HIGH,CRITICAL + + - name: Upload results to GitHub Security tab + if: always() + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: trivy-results.sarif + + # ============================================================ + # JOB 6: Publish to GHCR + Trivy image scan + # Runs only on push to main or testing (not on PRs). + # Depends on test + docker-build passing first. + # ============================================================ + docker-publish: + name: Publish image & scan + runs-on: ubuntu-latest + needs: [test, docker-build] + if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/testing') + permissions: + contents: read + packages: write + security-events: write + + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push to GHCR + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: | + ghcr.io/${{ github.repository }}:${{ github.ref_name }} + ghcr.io/${{ github.repository }}:sha-${{ github.sha }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Trivy — scan Docker image for OS-level CVEs + uses: aquasecurity/trivy-action@v0.35.0 + with: + image-ref: ghcr.io/${{ github.repository }}:${{ github.ref_name }} + format: sarif + output: trivy-image-results.sarif + severity: HIGH,CRITICAL + + - name: Upload image scan to GitHub Security tab + if: always() + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: trivy-image-results.sarif diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml new file mode 100644 index 0000000..462d30a --- /dev/null +++ b/.github/workflows/dependabot-auto-merge.yml @@ -0,0 +1,33 @@ +name: Dependabot auto-merge + +# Automatically merges Dependabot PRs once CI passes. +# Only acts on minor/patch updates — major version bumps are left for manual review. +# Requires "Allow auto-merge" enabled in repo Settings → General → Pull Requests. + +on: pull_request + +permissions: + contents: write + pull-requests: write + +jobs: + auto-merge: + name: Enable auto-merge + runs-on: ubuntu-latest + if: github.actor == 'dependabot[bot]' + + steps: + - name: Fetch Dependabot metadata + id: metadata + uses: dependabot/fetch-metadata@v2 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Enable auto-merge for minor/patch updates + if: | + steps.metadata.outputs.update-type == 'version-update:semver-minor' || + steps.metadata.outputs.update-type == 'version-update:semver-patch' + run: gh pr merge --auto --merge "$PR_URL" + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000..9295d94 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,26 @@ +name: Dependency review + +# Runs on every PR and blocks merges that introduce a dependency with a known +# HIGH or CRITICAL CVE. Complements pip-audit (which checks all installed deps) +# by specifically flagging what the PR diff adds or changes. +# GitHub-native — no token or third-party service required. + +on: pull_request + +permissions: + contents: read + pull-requests: write + +jobs: + dependency-review: + name: Dependency review + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Dependency review + uses: actions/dependency-review-action@v4 + with: + fail-on-severity: high + comment-summary-in-pr: always diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 0000000..5747b79 --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,49 @@ +name: OSSF Scorecard + +# Grades the repo's supply-chain security posture (branch protection, dependency +# pinning, CI, secret scanning, etc.) and publishes results to the GitHub +# Security tab. Free for public repositories. +# https://securityscorecards.dev + +on: + branch_protection_rule: + schedule: + - cron: '0 6 * * 1' # Every Monday at 06:00 UTC + push: + branches: [main] + +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + security-events: write + id-token: write + contents: read + actions: read + + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Run Scorecard analysis + uses: ossf/scorecard-action@v2.4.0 + with: + results_file: scorecard-results.sarif + results_format: sarif + publish_results: true + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: scorecard-results + path: scorecard-results.sarif + retention-days: 5 + + - name: Upload results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: scorecard-results.sarif diff --git a/CLAUDE.md b/CLAUDE.md index 347b24e..c08e041 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -128,6 +128,7 @@ All pricing flows through `billing/services/`. The single source of truth is `Si - **Template names are English** — all email templates were renamed from Spanish (e.g., `matricula_niño.html` → `enrollment_child.html`). Never create templates with Spanish names. - **Version in four places** — `pyproject.toml`, `project/project/settings.py` (`APP_VERSION` default), the `README.md` header badge URL, and `uv.lock` (the project's own `[[package]]` entry). `make version x.y.z` (positional, with y/N confirmation) updates the first three with `sed` and regenerates `uv.lock` via `uv lock --quiet`. Running `make version` with no argument prints the pyproject and README badge values side-by-side and warns if they've drifted. `make pc-run`'s auto patch-bump does the same four updates, then the existing `git add uv.lock` block at the end of the target stages the regenerated lockfile so the next commit isn't blocked. - **APP_VERSION in `.env`** — the local `.env` may contain a legacy `APP_VERSION=0.x.y` line. Either remove the line or update it — it silently overrides the default in `settings.py` at runtime. +- **`load_dotenv(override=False)` — Docker env vars take precedence** — `settings.py` calls `load_dotenv(..., override=False)`. This means environment variables already set in the process (i.e. injected by Docker Compose's `environment:` block) are NOT overridden by the `.env` file. This is intentional: it allows compose overlays (`docker-compose.testing.yml`) to inject different credentials without the volume-mounted `.env` file silently overwriting them. If you need the `.env` file to win over Docker's env vars, you'd need `override=True` — but don't do that, it breaks multi-environment setups. - **`pc-run` renamed** — the old `make pre-commit-run` target is now `make pc-run`. It also auto-bumps the patch version on a clean pass (y/N prompt) and auto-stages `uv.lock` if regenerated. - **mypy CI job needs `DJANGO_DEBUG=True`** — `django-stubs` imports `project.settings` at load time for the Django plugin. Without `DJANGO_DEBUG=True` + a dummy `DJANGO_SECRET_KEY`, the production guard at the top of `settings.py` raises `ValueError: DJANGO_SECRET_KEY debe ser cambiado en producción`. The CI `mypy` step sets both, plus `PYTHONPATH=project`, as env vars. Any new static-analysis job that imports settings will need the same. - **Test settings disable production security redirects** — `settings_test.py` explicitly sets `SECURE_SSL_REDIRECT = False`, `SECURE_HSTS_SECONDS = 0`, `SESSION_COOKIE_SECURE = False`, `CSRF_COOKIE_SECURE = False`. Don't remove them — the Django test client speaks HTTP against `testserver`, and inheriting `SECURE_SSL_REDIRECT=True` from `settings.py` (which kicks in when `DEBUG=False`) turns every test request into a 301 to `https://testserver/...`. The overrides keep the test settings self-contained regardless of how CI configures `DJANGO_DEBUG`. diff --git a/Dockerfile b/Dockerfile index f30eb92..332d5b0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,7 @@ ENV PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 # Install system deps needed to compile Python packages +# hadolint ignore=DL3008 RUN apt-get update && apt-get install -y --no-install-recommends \ gcc \ postgresql-client \ @@ -42,6 +43,7 @@ ENV PYTHONUNBUFFERED=1 \ PATH="/app/.venv/bin:$PATH" # Install only runtime system deps +# hadolint ignore=DL3008 RUN apt-get update && apt-get install -y --no-install-recommends \ postgresql-client \ libpq-dev \ diff --git a/Makefile b/Makefile index 93f074e..57ffbf7 100644 --- a/Makefile +++ b/Makefile @@ -3,15 +3,13 @@ # ============================================================================ # Docker and Django shortcuts. Run `make` or `make help` for usage. -.PHONY: help setup build up down restart stop start rebuild dev logs logs-web \ - logs-db ps stats shell bash migrate makemigrations createsuperuser \ - collectstatic check dbshell backup restore reset-db test test-local \ - test-verbose test-coverage test-unit test-integration test-cov-gate test-models test-services test-views \ - clean clean-all health url send-test-email generate-payments \ - testing-up testing-down testing-logs testing-seed testing-reset \ - testing-rebuild testing-shell testing-health \ - sync lint lint-fix format format-check pre-commit-install pc-run \ - mypy bandit audit coverage-badge \ +.PHONY: help setup build up down restart stop start rebuild dev logs \ + ps stats shell bash migrate makemigrations createsuperuser \ + collectstatic check dbshell backup restore reset-db \ + test test-cov-gate \ + clean clean-all health url generate-payments generate-payments-dry \ + sync lint format pre-commit-install pc-run \ + mypy bandit audit coverage-badge check-deploy \ celery-logs celery-restart celery-status celery-test-task # ============================================================================ @@ -23,98 +21,80 @@ help: @echo " ==========================" @echo "" @echo " Setup & Build:" - @echo " make setup Create empty .env (fill in from README.md '.env template')" - @echo " make build Build Docker images" - @echo " make rebuild Full rebuild (no cache) + start" - @echo " make rebuild-web Rebuild only the web image" + @echo " make setup Create empty .env" + @echo " make build Build Docker images" + @echo " make rebuild Full rebuild (no cache) + start" + @echo " make rebuild SERVICE=x Rebuild only a specific service" @echo "" @echo " Docker Lifecycle:" - @echo " make up Start all services (detached)" - @echo " make down Stop and remove containers" - @echo " make restart Restart all services" - @echo " make restart-web Restart only web" - @echo " make restart-db Restart only database" - @echo " make stop Stop without removing" - @echo " make start Start stopped containers" - @echo " make dev Start in foreground (logs visible)" - @echo " make dev-build Build + start in foreground" + @echo " make up Start all services (detached)" + @echo " make down Stop and remove containers" + @echo " make restart Restart all services" + @echo " make restart SERVICE=x Restart a specific service" + @echo " make stop Stop without removing" + @echo " make stop SERVICE=x Stop a specific service" + @echo " make start Start stopped containers" + @echo " make start SERVICE=x Start a specific service" + @echo " make dev Start in foreground (logs visible)" + @echo " make dev BUILD=1 Build + start in foreground" @echo "" @echo " Monitoring:" - @echo " make logs Tail logs (all services)" - @echo " make logs-web Tail web logs only" - @echo " make logs-db Tail database logs only" - @echo " make ps Show running services" - @echo " make stats Show resource usage" - @echo " make health Full health check (Django + DB)" - @echo " make url Show access URLs" + @echo " make logs Tail all logs" + @echo " make logs SERVICE=x Tail logs for a specific service" + @echo " make ps Show running services" + @echo " make stats Show resource usage" + @echo " make health Full health check (Django + DB)" + @echo " make url Show access URLs" @echo "" @echo " Django:" - @echo " make shell Django shell inside container" - @echo " make bash Bash shell inside container" - @echo " make migrate Apply all migrations" - @echo " make makemigrations Create migrations (all apps)" - @echo " make createsuperuser Create Django superuser" - @echo " make collectstatic Collect static files" - @echo " make check Run Django system checks" + @echo " make shell Django shell inside container" + @echo " make bash Bash shell inside container" + @echo " make migrate Apply all migrations" + @echo " make makemigrations Create migrations (all apps)" + @echo " make createsuperuser Create Django superuser" + @echo " make collectstatic Collect static files" + @echo " make check Run Django system checks" @echo "" @echo " Database:" - @echo " make dbshell PostgreSQL interactive shell" - @echo " make backup Dump DB to backups/" - @echo " make restore FILE=x Restore from SQL file" - @echo " make reset-db Drop and recreate DB (destructive!)" + @echo " make dbshell PostgreSQL interactive shell" + @echo " make backup Dump DB to backups/" + @echo " make restore FILE=x Restore from SQL file" + @echo " make reset-db Drop and recreate DB (destructive!)" @echo "" @echo " Testing:" - @echo " make test Run all tests (Docker)" - @echo " make test-local Run all tests (local, no Docker)" - @echo " make test-verbose Run all tests with verbose output" - @echo " make test-coverage Run tests with coverage report" - @echo " make test-unit Run only unit tests" - @echo " make test-integration Run only integration tests" - @echo " make test-models Run only model tests" - @echo " make test-services Run only service tests" - @echo " make test-views Run only view tests" - @echo " make test-fast Run tests, stop on first failure" - @echo "" - @echo " Email:" - @echo " make send-test-email Send a test birthday email" - @echo " make test-all-emails Send one test of each email template" + @echo " make test Run all tests (Docker + coverage)" + @echo " make test unit Run only unit tests" + @echo " make test integration Run only integration tests" + @echo " make test coverage All tests + HTML coverage report" + @echo " make test K= Filter by keyword (e.g. K=payment)" + @echo " make test ARGS='...' Pass raw pytest flags through" @echo "" @echo " Payments:" @echo " make generate-payments Generate current month" @echo " make generate-payments-dry Preview without creating" @echo "" - @echo " Testing / QA Environment:" - @echo " make testing-up Start QA environment (production-like)" - @echo " make testing-down Stop QA environment" - @echo " make testing-rebuild Full rebuild of QA environment" - @echo " make testing-logs Tail QA logs" - @echo " make testing-seed Populate QA database with test data" - @echo " make testing-reset Wipe QA database and re-seed" - @echo " make testing-shell Django shell in QA container" - @echo " make testing-health Health check for QA environment" - @echo "" @echo " Celery (async tasks):" - @echo " make celery-logs Tail Celery worker + beat logs" - @echo " make celery-restart Restart worker + beat containers" - @echo " make celery-status Show Celery worker status" - @echo " make celery-test-task Send a debug task to verify Celery works" + @echo " make celery-logs Tail Celery worker + beat logs" + @echo " make celery-restart Restart worker + beat containers" + @echo " make celery-status Show Celery worker status" + @echo " make celery-test-task Send a debug task to verify Celery works" @echo "" @echo " Developer Tooling:" - @echo " make sync Install all deps (including dev) via uv" - @echo " make lint Run Ruff linter" - @echo " make lint-fix Run Ruff linter with auto-fix" - @echo " make format Format code with Ruff" - @echo " make format-check Check formatting (no changes)" - @echo " make mypy Run mypy type checker" - @echo " make bandit Run bandit security linter" - @echo " make audit Audit dependencies for vulnerabilities" - @echo " make coverage-badge Generate coverage.svg badge from last test run" - @echo " make pre-commit-install Install pre-commit hooks" - @echo " make pc-run Run pre-commit on all files" + @echo " make sync Install all deps (including dev) via uv" + @echo " make lint Check code with Ruff (read-only)" + @echo " make lint FIX=1 Lint and auto-fix issues" + @echo " make format Format code with Ruff" + @echo " make format DRY=1 Check formatting without applying changes" + @echo " make mypy Run mypy type checker" + @echo " make bandit Run bandit security linter" + @echo " make audit Audit dependencies for vulnerabilities" + @echo " make coverage-badge Generate coverage.svg badge" + @echo " make pre-commit-install Install pre-commit hooks" + @echo " make pc-run Run pre-commit on all files" @echo "" @echo " Cleanup:" - @echo " make clean Remove stopped containers + prune" - @echo " make clean-all Remove everything including volumes" + @echo " make clean Remove stopped containers + prune" + @echo " make clean-all Remove everything including volumes" @echo "" # ============================================================================ @@ -141,49 +121,38 @@ up: down: docker compose down -restart: - docker compose restart - -restart-web: - docker compose restart web +# Rebuild with no cache. Without SERVICE rebuilds everything; with SERVICE=web +# only that service is stopped/rebuilt/started. +rebuild: + @if [ -z "$(SERVICE)" ]; then \ + docker compose down; \ + docker compose build --no-cache; \ + docker compose up -d; \ + echo "Rebuilt and started: http://localhost:8000"; \ + else \ + docker compose stop $(SERVICE); \ + docker compose build --no-cache $(SERVICE); \ + docker compose up -d $(SERVICE); \ + echo "Rebuilt service: $(SERVICE)"; \ + fi -restart-db: - docker compose restart db +restart: + docker compose restart $(SERVICE) stop: - docker compose stop + docker compose stop $(SERVICE) start: - docker compose start - -rebuild: - docker compose down - docker compose build --no-cache - docker compose up -d - @echo "Rebuilt and started: http://localhost:8000" - -rebuild-web: - docker compose stop web - docker compose build --no-cache web - docker compose up -d web + docker compose start $(SERVICE) dev: - docker compose up --remove-orphans - -dev-build: - docker compose up --build --remove-orphans + docker compose up $(if $(BUILD),--build,) --remove-orphans # ============================================================================ # MONITORING # ============================================================================ logs: - docker compose logs -f - -logs-web: - docker compose logs -f web - -logs-db: - docker compose logs -f db + docker compose logs -f $(SERVICE) ps: docker compose ps @@ -268,74 +237,50 @@ reset-db: # ============================================================================ # TESTING # ============================================================================ -# Tests use PostgreSQL by default (requires `make up` for the DB container). -# Set TEST_DB_ENGINE=sqlite to fall back to SQLite for quick local runs. +# All tests run inside Docker against PostgreSQL (same engine as production). +# +# Usage: +# make test all tests with coverage +# make test unit tests/unit/ only +# make test integration tests/integration/ only +# make test coverage all tests + HTML report (htmlcov/) +# make test K=payment filter by keyword +# make test ARGS='--lf' pass any raw pytest flag through + +ifeq ($(firstword $(MAKECMDGOALS)),test) + _SUITE := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)) + ifneq ($(_SUITE),) + $(eval $(_SUITE):;@:) + endif +endif -# Run all tests inside Docker (uses the container's PostgreSQL) test: docker compose exec web uv sync --frozen --no-install-project --quiet - docker compose exec -e DJANGO_SETTINGS_MODULE=project.settings_test -e TEST_DB_HOST=db web python -m pytest project/tests/ -v --tb=short -n auto --cov=core --cov=students --cov=billing --cov=comms --cov-report=term-missing - -# Pre-commit coverage gate: same command as `make test` but fails fast if -# coverage drops below 75%. Invoked by the pytest-coverage pre-commit hook -# and safe to run manually. Output is terse (-q) so commit feedback stays -# readable; full coverage report still prints at the end. + @SUITE="$(_SUITE)"; \ + TEST_PATH="project/tests/"; \ + EXTRA=""; \ + case "$$SUITE" in \ + unit) TEST_PATH="project/tests/unit/" ;; \ + integration) TEST_PATH="project/tests/integration/" ;; \ + coverage) EXTRA="--cov-report=html" ;; \ + esac; \ + [ -n "$(K)" ] && EXTRA="$$EXTRA -k $(K)"; \ + docker compose exec \ + -e DJANGO_SETTINGS_MODULE=project.settings_test \ + -e TEST_DB_HOST=db \ + web python -m pytest $$TEST_PATH -v --tb=short -n auto \ + --cov=core --cov=students --cov=billing --cov=comms \ + --cov-report=term-missing $$EXTRA $(ARGS) + +# Pre-commit coverage gate: fails if coverage drops below 75%. +# Invoked by the pytest-coverage pre-commit hook; safe to run manually. test-cov-gate: @docker compose exec web uv sync --frozen --no-install-project --quiet @docker compose exec -e DJANGO_SETTINGS_MODULE=project.settings_test -e TEST_DB_HOST=db web python -m pytest project/tests/ -q --tb=line -n auto --cov=core --cov=students --cov=billing --cov=comms --cov-fail-under=75 -# Run tests locally against the Docker PostgreSQL (default) -test-local: - cd project && TEST_DB_HOST=localhost python -m pytest tests/ -v --tb=short - -# Run tests locally with SQLite (no Docker needed) -test-sqlite: - cd project && TEST_DB_ENGINE=sqlite python -m pytest tests/ -v --tb=short - -# Verbose output with full tracebacks -test-verbose: - cd project && TEST_DB_HOST=localhost python -m pytest tests/ -v --tb=long -s - -# Coverage report -test-coverage: - cd project && TEST_DB_HOST=localhost python -m pytest tests/ --cov=core --cov=students --cov=billing --cov=comms --cov-report=term-missing --cov-report=html - @echo "HTML report: project/htmlcov/index.html" - -# Run only unit tests (direct calls, no HTTP stack) -test-unit: - cd project && TEST_DB_HOST=localhost python -m pytest tests/unit/ -v --tb=short - -# Run only integration tests (Django test client through the middleware chain) -test-integration: - cd project && TEST_DB_HOST=localhost python -m pytest tests/integration/ -v --tb=short - -# Run specific test modules -test-models: - cd project && TEST_DB_HOST=localhost python -m pytest tests/unit/test_models.py -v --tb=short - -test-services: - cd project && TEST_DB_HOST=localhost python -m pytest tests/unit/test_services.py -v --tb=short - -test-views: - cd project && TEST_DB_HOST=localhost python -m pytest tests/integration/test_views.py -v --tb=short - -# Stop on first failure -test-fast: - cd project && TEST_DB_HOST=localhost python -m pytest tests/ -x -v --tb=short - -# Run tests matching a keyword (usage: make test-k K=payment) -test-k: - cd project && TEST_DB_HOST=localhost python -m pytest tests/ -v -k "$(K)" --tb=short - # ============================================================================ -# EMAIL & PAYMENTS +# PAYMENTS # ============================================================================ -send-test-email: - docker compose exec web python project/manage.py send_email --template happy_birthday --test - -test-all-emails: - docker compose exec web python project/manage.py test_all_emails --list - generate-payments: docker compose exec web python project/manage.py generate_payments @@ -363,15 +308,12 @@ clean-all: # ============================================================================ # VERSIONING # ============================================================================ -# App version is defined in two places: -# 1. pyproject.toml -> version = "x.y.z" -# 2. project/settings.py -> APP_VERSION fallback = "x.y.z" -# This command updates both at once. -# +# App version is defined in three places: +# 1. pyproject.toml -> version = "x.y.z" +# 2. settings.py -> APP_VERSION fallback = "x.y.z" +# 3. README.md -> badge URL # Usage: make version x.y.z -# Capture `make version x.y.z` - treat the version number as a goal with an empty recipe -# so Make doesn't complain about a missing target named "x.y.z". ifeq ($(firstword $(MAKECMDGOALS)),version) _VERSION_ARG := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)) ifneq ($(_VERSION_ARG),) @@ -414,47 +356,6 @@ version: echo "Cancelled."; \ fi -# ============================================================================ -# TESTING / QA ENVIRONMENT -# ============================================================================ -# These targets use docker-compose.testing.yml on top of the base file. -# The QA environment mimics production: DEBUG=False, Gunicorn, HTTPS cookies. - -TESTING_COMPOSE = docker compose -f docker-compose.yml -f docker-compose.testing.yml - -testing-up: - $(TESTING_COMPOSE) up -d --remove-orphans - @echo "QA environment started: http://localhost:8000" - @echo "Login with credentials from .env.testing (LOGIN_USERNAME / LOGIN_PASSWORD)" - -testing-down: - $(TESTING_COMPOSE) down - -testing-rebuild: - $(TESTING_COMPOSE) down - $(TESTING_COMPOSE) build --no-cache - $(TESTING_COMPOSE) up -d - @echo "QA environment rebuilt: http://localhost:8000" - -testing-logs: - $(TESTING_COMPOSE) logs -f web - -testing-seed: - $(TESTING_COMPOSE) exec web python project/manage.py seed_testdata - -testing-reset: - $(TESTING_COMPOSE) exec web python project/manage.py seed_testdata --reset - -testing-shell: - $(TESTING_COMPOSE) exec web python project/manage.py shell - -testing-health: - @echo "=== QA Services ===" - @$(TESTING_COMPOSE) ps - @echo "" - @echo "=== Health endpoint ===" - @curl -sf http://localhost:8000/health/ 2>/dev/null || echo "(not reachable)" - # ============================================================================ # CELERY (async tasks + scheduled jobs) # ============================================================================ @@ -477,16 +378,10 @@ sync: uv sync --no-install-project lint: - uv run --no-project ruff check project/ - -lint-fix: - uv run --no-project ruff check --fix project/ + uv run --no-project ruff check $(if $(FIX),--fix,) project/ format: - uv run --no-project ruff format project/ - -format-check: - uv run --no-project ruff format --check project/ + uv run --no-project ruff format $(if $(DRY),--check,) project/ mypy: uv run mypy project/ diff --git a/README.md b/README.md index f998199..2c48b78 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,6 @@

- Version - Python - Django - PostgreSQL CI Coverage

@@ -23,127 +19,175 @@ Built to centralize student records, automate billing cycles, and streamline par ### Project Status +

+ Version +  |  + CI main +  |  + Coverage +  |  + OSSF Scorecard +  |  + Dependabot +

+ + | Environment | Branch | Hosting | CI Status | |-------------|--------|---------|-----------| | **Production** | `main` | [https://example.com/](...) | [![Production CI](https://github.com/starseeker-code-public/five-a-day/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/starseeker-code-public/five-a-day/actions/workflows/ci.yml?query=branch%3Amain) | | **Testing (QA)** | `testing` | [https://example.com/](...) | [![Testing CI](https://github.com/starseeker-code-public/five-a-day/actions/workflows/ci.yml/badge.svg?branch=testing)](https://github.com/starseeker-code-public/five-a-day/actions/workflows/ci.yml?query=branch%3Atesting) | -| **Development** | `development` | [Local Docker development: http://localhost:8000/](http://localhost:8000/) | [![Development CI](https://github.com/starseeker-code-public/five-a-day/actions/workflows/ci.yml/badge.svg?branch=development)](https://github.com/starseeker-code-public/five-a-day/actions/workflows/ci.yml?query=branch%3Adevelopment) | +| **Development** | `development` | [Docker in local](http://localhost:8000/) | [![Development CI](https://github.com/starseeker-code-public/five-a-day/actions/workflows/ci.yml/badge.svg?branch=development)](https://github.com/starseeker-code-public/five-a-day/actions/workflows/ci.yml?query=branch%3Adevelopment) | + | Version | Date | Description | |---------|------|-------------| -| **v1.0.10** | 2026-04-21 | Branded admin theme, white-bg favicon, social meta | -| v1.0.9 | 2026-04-16 | Test suite restructure to unit/integration, 96% coverage, CI gates | -| v1.0.8 | 2026-04-15 | README trim, `docs/` purge, flaky CI test removed | +| **v1.0.11** | 2026-04-22 | Testing env fixes, CI hardening, static files cleanup | +| v1.0.10 | 2026-04-21 | Branded admin theme, white-bg favicon, social meta | +| v1.0.9 | 2026-04-16 | Test suite restructure, 96% coverage, CI gates | --- ## Table of Contents -- [Project Status](#project-status) -- [Version History \& Roadmap](#version-history--roadmap) - - [Roadmap](#roadmap) - - [v1.1 — Waiting List \& Group Capacity](#v11--waiting-list--group-capacity) - - [v1.2 — Google Sheets Integration](#v12--google-sheets-integration) - - [v1.3 — PDF Invoice Generation](#v13--pdf-invoice-generation) - - [v1.4 — Celery + Redis Deployment](#v14--celery--redis-deployment) - - [v1.5 — Expense Tracking](#v15--expense-tracking) - - [v1.6 — Multi-User Permissions](#v16--multi-user-permissions) - - [v1.7 — Advanced Reporting \& Analytics](#v17--advanced-reporting--analytics) - - [v1.8 — SMS Notifications (Twilio)](#v18--sms-notifications-twilio) - - [v1.9 — Parent Portal](#v19--parent-portal) - - [v1.10 — Audit Log \& Security Hardening](#v110--audit-log--security-hardening) - - [v1.11 — Stripe Payment Integration](#v111--stripe-payment-integration) - - [v1.12 — Mobile Optimization \& PWA](#v112--mobile-optimization--pwa) -- [Tech Stack](#tech-stack) - - [Backend](#backend) - - [Frontend](#frontend) - - [Infrastructure \& Deployment](#infrastructure--deployment) - - [Python Dependencies](#python-dependencies) - - [Developer Tooling](#developer-tooling) -- [Database Schema](#database-schema) - - [ER Diagram](#er-diagram) - - [Key Constraints](#key-constraints) -- [Development \& Docker](#development--docker) - - [Quick Start](#quick-start) - - [.env template](#env-template) - - [Make Commands](#make-commands) - - [Environment Configuration](#environment-configuration) - - [Environment Variables Reference](#environment-variables-reference) - - [App Versioning](#app-versioning) -- [Project Structure \& Architecture](#project-structure--architecture) - - [Architecture Overview](#architecture-overview) - - [App Dependency Flow](#app-dependency-flow) - - [Directory Layout](#directory-layout) - - [App: core](#app-core) - - [App: students](#app-students) - - [App: billing](#app-billing) - - [App: comms](#app-comms) - - [Design Decisions](#design-decisions) -- [Features by View](#features-by-view) - - [Home (Dashboard)](#home-dashboard) - - [Students](#students) - - [Student Create](#student-create) - - [Student Detail \& Update](#student-detail--update) - - [Payments](#payments) - - [Schedule](#schedule) - - [Fun Friday](#fun-friday) - - [Apps (Email Tools)](#apps-email-tools) - - [Management](#management) - - [Database (All Info)](#database-all-info) - - [Login](#login) -- [Testing](#testing) - - [Testing Overview](#testing-overview) - - [Unit Tests](#unit-tests) - - [Integration Tests](#integration-tests) - - [Coverage Report](#coverage-report) -- [Migrations](#migrations) -- [Security](#security) - - [Authentication](#authentication) - - [Session \& Cookie Configuration](#session--cookie-configuration) - - [CSRF Protection](#csrf-protection) - - [Transport Security (HTTPS)](#transport-security-https) - - [Security Headers](#security-headers) - - [Infrastructure \& Deployment](#infrastructure--deployment-1) - - [Docker](#docker) - - [Google Cloud Run](#google-cloud-run) - - [Secrets Management](#secrets-management) - - [Email Security](#email-security) - - [Data Protection \& Input Validation](#data-protection--input-validation) - - [Logging \& Monitoring](#logging--monitoring) - - [Future Security Improvements](#future-security-improvements) -- [Testing Environment (QA)](#testing-environment-qa) - - [What is the testing environment?](#what-is-the-testing-environment) - - [How to access it](#how-to-access-it) - - [What you can test](#what-you-can-test) - - [How to report a problem](#how-to-report-a-problem) - - [Error pages you might see](#error-pages-you-might-see) - - [For developers: how the QA environment works](#for-developers-how-the-qa-environment-works) - - [Access control for `/testing/`](#access-control-for-testing) -- [CI/CD \& GitHub Actions](#cicd--github-actions) - - [Pipeline Overview](#pipeline-overview) - - [Branch Strategy](#branch-strategy) - - [Workflows](#workflows) - - [Automated Flows](#automated-flows) - - [Branch Protection — `main`](#branch-protection--main) - - [Branch Protection — `testing`](#branch-protection--testing) - - [Public Repository Hardening](#public-repository-hardening) - - [Required GitHub Secrets](#required-github-secrets) - - [Email Notifications](#email-notifications) - - [Dependabot](#dependabot) - - [CodeQL Security Scanning](#codeql-security-scanning) -- [Contributing](#contributing) - - [Development Workflow](#development-workflow) - - [Make Commands (Developer Tooling)](#make-commands-developer-tooling) - - [Code Conventions](#code-conventions) - - [Adding a Feature](#adding-a-feature) -- [License](#license) +- [Five a Day eVolution](#five-a-day-evolution) + - [Project Status](#project-status) + - [Table of Contents](#table-of-contents) + - [Version History \& Roadmap](#version-history--roadmap) + - [Roadmap](#roadmap) + - [v1.1 — Waiting List \& Group Capacity](#v11--waiting-list--group-capacity) + - [v1.2 — Google Sheets Integration](#v12--google-sheets-integration) + - [v1.3 — PDF Invoice Generation](#v13--pdf-invoice-generation) + - [v1.4 — Celery + Redis Deployment](#v14--celery--redis-deployment) + - [v1.5 — Expense Tracking](#v15--expense-tracking) + - [v1.6 — Multi-User Permissions](#v16--multi-user-permissions) + - [v1.7 — Advanced Reporting \& Analytics](#v17--advanced-reporting--analytics) + - [v1.8 — SMS Notifications (Twilio)](#v18--sms-notifications-twilio) + - [v1.9 — Parent Portal](#v19--parent-portal) + - [v1.10 — Audit Log \& Security Hardening](#v110--audit-log--security-hardening) + - [v1.11 — Stripe Payment Integration](#v111--stripe-payment-integration) + - [v1.12 — Mobile Optimization \& PWA](#v112--mobile-optimization--pwa) + - [Tech Stack](#tech-stack) + - [Backend](#backend) + - [Frontend](#frontend) + - [Infrastructure \& Deployment](#infrastructure--deployment) + - [Python Dependencies](#python-dependencies) + - [Developer Tooling](#developer-tooling) + - [Database Schema](#database-schema) + - [ER Diagram](#er-diagram) + - [Key Constraints](#key-constraints) + - [Development \& Docker](#development--docker) + - [Quick Start](#quick-start) + - [.env template](#env-template) + - [Make Commands](#make-commands) + - [Environment Configuration](#environment-configuration) + - [Environment Variables Reference](#environment-variables-reference) + - [App Versioning](#app-versioning) + - [Project Structure \& Architecture](#project-structure--architecture) + - [Architecture Overview](#architecture-overview) + - [App Dependency Flow](#app-dependency-flow) + - [Directory Layout](#directory-layout) + - [App: core](#app-core) + - [App: students](#app-students) + - [App: billing](#app-billing) + - [App: comms](#app-comms) + - [Design Decisions](#design-decisions) + - [Features by View](#features-by-view) + - [Home (Dashboard)](#home-dashboard) + - [Students](#students) + - [Student Create](#student-create) + - [Student Detail \& Update](#student-detail--update) + - [Payments](#payments) + - [Schedule](#schedule) + - [Fun Friday](#fun-friday) + - [Apps (Email Tools)](#apps-email-tools) + - [Management](#management) + - [Database (All Info)](#database-all-info) + - [Login](#login) + - [Testing](#testing) + - [Testing Overview](#testing-overview) + - [Unit Tests](#unit-tests) + - [Integration Tests](#integration-tests) + - [Coverage Report](#coverage-report) + - [Migrations](#migrations) + - [Security](#security) + - [Authentication](#authentication) + - [Session \& Cookie Configuration](#session--cookie-configuration) + - [CSRF Protection](#csrf-protection) + - [Transport Security (HTTPS)](#transport-security-https) + - [Security Headers](#security-headers) + - [Infrastructure \& Deployment](#infrastructure--deployment-1) + - [Docker](#docker) + - [Google Cloud Run](#google-cloud-run) + - [Secrets Management](#secrets-management) + - [Email Security](#email-security) + - [Data Protection \& Input Validation](#data-protection--input-validation) + - [Logging \& Monitoring](#logging--monitoring) + - [Future Security Improvements](#future-security-improvements) + - [Testing Environment (QA)](#testing-environment-qa) + - [What is the testing environment?](#what-is-the-testing-environment) + - [How to access it](#how-to-access-it) + - [What you can test](#what-you-can-test) + - [How to report a problem](#how-to-report-a-problem) + - [Error pages you might see](#error-pages-you-might-see) + - [For developers: how the QA environment works](#for-developers-how-the-qa-environment-works) + - [Access control for `/testing/`](#access-control-for-testing) + - [CI/CD \& GitHub Actions](#cicd--github-actions) + - [Pipeline Overview](#pipeline-overview) + - [Branch Strategy](#branch-strategy) + - [Workflows](#workflows) + - [Automated Flows](#automated-flows) + - [Branch Protection — `main`](#branch-protection--main) + - [Branch Protection — `testing`](#branch-protection--testing) + - [Public Repository Hardening](#public-repository-hardening) + - [Required GitHub Secrets](#required-github-secrets) + - [Email Notifications](#email-notifications) + - [Dependabot](#dependabot) + - [CodeQL Security Scanning](#codeql-security-scanning) + - [Contributing](#contributing) + - [Development Workflow](#development-workflow) + - [Make Commands (Developer Tooling)](#make-commands-developer-tooling) + - [Code Conventions](#code-conventions) + - [Adding a Feature](#adding-a-feature) + - [License](#license) --- ## Version History & Roadmap -
-v1.0.10 — Branded Admin Theme, White-Bg Favicon & Social Meta (current) +
+v1.0.11 — Testing Environment Fixes, CI Hardening & Static File Cleanup (current) + +**Testing environment** + +- `docker-compose.testing.yml`: added explicit `POSTGRES_DB`, `POSTGRES_USER`, `POSTGRES_PASSWORD` overrides to both `db` and `web` services — the base `docker-compose.yml` uses `.env` credentials while the overlay uses `.env.testing` credentials; without these overrides the `db` container initialised with dev credentials while the `web` container tried to connect with testing credentials +- `settings.py`: `load_dotenv(override=True)` → `override=False` — Docker `environment:` values now take precedence over the volume-mounted `.env` file; `override=True` was silently overwriting credentials injected by the compose overlay +- `core/context_processors.py`: added `hasattr(request, "session")` guard before `request.session.get("username")` — prevents `AttributeError` 500 errors in admin views and error-handler requests that bypass `SessionMiddleware` + +**Static files** + +- `STATICFILES_DIRS = [BASE_DIR / "static"]` removed from `settings.py`; all static assets now live under `project/core/static/` (served via `APP_DIRS=True`) — no separate `STATICFILES_DIRS` needed +- Moved to `project/core/static/`: `css/admin_custom.css`, `css/email.css`, `images/logo_white_bg.png` +- Deleted legacy `project/static/` assets: `apple-touch-icon.png`, `favicon-32x32.png`, `favicon.ico`, `images/logo.png` + +**CI/CD — new jobs and workflows** + +- `ci.yml` lint job: added `pip-audit` CVE scan and Hadolint Dockerfile lint +- New CI job — **Docker build**: validates `Dockerfile` builds cleanly on every push/PR (with GHA cache) +- New CI job — **Trivy filesystem scan**: scans Python deps + filesystem for HIGH/CRITICAL CVEs; uploads SARIF to GitHub Security tab +- New CI job — **Docker publish**: on push to `main`/`testing`, builds and pushes image to GHCR (`ghcr.io/starseeker-code-public/five-a-day:` + `sha-`), then runs Trivy image scan +- `codecov-action` upgraded v4 → v5 +- New `dependabot-auto-merge.yml`: automatically merges Dependabot minor/patch PRs once CI passes +- New `dependency-review.yml`: blocks PRs that introduce a HIGH/CRITICAL CVE dependency +- New `scorecard.yml`: OSSF Scorecard supply-chain security grading (weekly + on push to `main`); results published to GitHub Security tab + +**Admin** + +- `#nav-sidebar` right padding set to `1rem` in `admin_custom.css` + +
+ +
+v1.0.10 — Branded Admin Theme, White-Bg Favicon & Social Meta **Social sharing & branding** @@ -532,9 +576,9 @@ Progressive Web App support: installable on mobile, offline-capable dashboard, p | Technology | Version | Purpose | |-----------|---------|---------| -| Python | 3.12+ | Runtime | -| Django | 5.2.5 | Web framework | -| PostgreSQL | 16 (Alpine) | Database (production, development, and testing) | +| [![Python](https://img.shields.io/badge/Python-3.12+-3776ab?style=flat-square&logo=python&logoColor=white)](https://python.org) | 3.12+ | Runtime | +| [![Django](https://img.shields.io/badge/Django-5.2-092e20?style=flat-square&logo=django&logoColor=white)](https://djangoproject.com) | 5.2.5 | Web framework | +| [![PostgreSQL](https://img.shields.io/badge/PostgreSQL-16-336791?style=flat-square&logo=postgresql&logoColor=white)](https://postgresql.org) | 16 (Alpine) | Database (production, development, and testing) | | Celery | 5.5.3 | Async task queue (eager mode without Redis, full async with Redis in v1.4) | | Celery Beat | (bundled with Celery) | Scheduled task execution (birthday emails, payment generation — v1.4) | | Redis | 7 (Alpine) | Message broker for Celery (planned, v1.4) | @@ -1102,10 +1146,13 @@ five-a-day/ │ ├── .github/ CI/CD — see docs/GITHUB.md │ ├── workflows/ -│ │ ├── ci.yml Lint + typecheck + tests on every push/PR -│ │ ├── auto-merge.yml Hourly development → testing merge + PR to main -│ │ ├── codeql.yml Weekly Python security scan -│ │ └── notify-production.yml Email on push to main +│ │ ├── ci.yml Lint + typecheck + tests + Docker build + CVE scan on every push/PR +│ │ ├── auto-merge.yml Hourly development → testing merge + PR to main +│ │ ├── codeql.yml Weekly Python security scan +│ │ ├── notify-production.yml Email on push to main +│ │ ├── dependabot-auto-merge.yml Auto-merge Dependabot minor/patch PRs +│ │ ├── dependency-review.yml Block PRs introducing HIGH/CRITICAL CVEs +│ │ └── scorecard.yml OSSF Scorecard supply-chain security (weekly) │ ├── dependabot.yml Weekly dependency updates │ └── CODEOWNERS Auto-request reviews from owner accounts │ @@ -1122,7 +1169,7 @@ five-a-day/ ├── Dockerfile Multi-stage build (builder + runtime) ├── docker-compose.yml PostgreSQL + Redis + Django + Celery worker + beat ├── docker-compose.testing.yml QA override (Gunicorn, DEBUG=False) -├── Makefile 75+ commands (`make help`) +├── Makefile 45+ commands (`make help`) ├── pyproject.toml Dependencies (uv-managed) + tool config ├── uv.lock Reproducible dependency lock ├── entrypoint.sh Docker entrypoint (migrate, collectstatic, start) @@ -1817,10 +1864,13 @@ Feature branches off `development` are welcome for non-trivial work, but the exp | Workflow | File | Triggers | Purpose | |----------|------|----------|---------| -| **CI** | [`ci.yml`](.github/workflows/ci.yml) | Push to `development`/`testing`/`main`; PRs to `testing`/`main` | Three parallel jobs — **Lint** (Ruff + Bandit), **Type check** (mypy), **Tests** (pytest + PostgreSQL 16 service container + Codecov upload) | +| **CI** | [`ci.yml`](.github/workflows/ci.yml) | Push to `development`/`testing`/`main`; PRs to `testing`/`main` | Six jobs — **Lint** (Ruff + Bandit + pip-audit + Hadolint), **Type check** (mypy), **Tests** (pytest + PostgreSQL 16 + Codecov), **Docker build** (validates Dockerfile), **Trivy** (filesystem CVE scan → Security tab), **Docker publish** (GHCR push + image scan, on `main`/`testing` only) | | **Auto-merge** | [`auto-merge.yml`](.github/workflows/auto-merge.yml) | Hourly cron + manual dispatch | Merges `development` → `testing` when conditions pass, creates PR to `main`, emails owners | | **CodeQL** | [`codeql.yml`](.github/workflows/codeql.yml) | Push to `main`/`testing`/`development`; PRs to `main`; Monday 04:30 UTC | Python static security analysis (OWASP Top 10, Django-specific queries) | | **Notify production** | [`notify-production.yml`](.github/workflows/notify-production.yml) | Push to `main` | Emails `hellofiveaday@gmail.com` with commit info and `gcloud` deploy instructions | +| **Dependabot auto-merge** | [`dependabot-auto-merge.yml`](.github/workflows/dependabot-auto-merge.yml) | Pull request (Dependabot only) | Enables auto-merge for minor/patch Dependabot PRs once CI passes | +| **Dependency review** | [`dependency-review.yml`](.github/workflows/dependency-review.yml) | Pull request | Blocks PRs that introduce a HIGH/CRITICAL CVE dependency | +| **OSSF Scorecard** | [`scorecard.yml`](.github/workflows/scorecard.yml) | Push to `main`; weekly Monday 06:00 UTC; branch protection rule changes | Grades supply-chain security posture; uploads SARIF to GitHub Security tab | | **Dependabot** | [`dependabot.yml`](.github/dependabot.yml) | Weekly (Mondays 08:00 Madrid) | Grouped Python and GitHub Actions updates targeting `development` | Concurrent CI runs on the same branch cancel each other automatically — new pushes always produce a fresh run. @@ -1911,6 +1961,8 @@ Because this repository is **public**, extra care is taken to prevent accidental | **GitHub Secret Scanning** | Settings → Code security | Free for public repos — detects committed secrets across history | | **Push Protection** | Settings → Code security | Free for public repos — blocks pushes that contain secrets before they land | | **CodeQL** | `codeql.yml` + Settings → Code security | Free for public repos — weekly security analysis | +| **OSSF Scorecard** | `scorecard.yml` + Settings → Code security | Free for public repos — weekly supply-chain security grading (branch protection, dependency pinning, CI, secret scanning) | +| **Dependency review** | `dependency-review.yml` | Blocks PRs that introduce a new HIGH/CRITICAL CVE dependency — catches supply-chain attacks before they merge | | **Dependabot alerts + security updates** | Settings → Code security | Free for public repos — fixes known CVEs in dependencies | | **Require 2FA for all contributors** | Organization settings (if in an org) | Prevents compromised account pushes | | **Restrict fork PRs from running CI with secrets** | Settings → Actions → Fork PR workflows: require approval for first-time contributors | Prevents secret exfiltration via malicious PRs from forks | diff --git a/docker-compose.testing.yml b/docker-compose.testing.yml index 9295041..c31105c 100644 --- a/docker-compose.testing.yml +++ b/docker-compose.testing.yml @@ -12,10 +12,17 @@ services: db: - # Override: use a separate volume so QA data never touches dev data + # Override: use a separate volume so QA data never touches dev data, + # and use dedicated testing credentials that match .env.testing. volumes: - testing_postgres_data:/var/lib/postgresql/data/pgdata - ./backups:/backups + environment: + POSTGRES_DB: fiveaday_testing + POSTGRES_USER: fiveaday_tester + POSTGRES_PASSWORD: 'TestingQA2026!SecurePassword' + healthcheck: + test: ["CMD-SHELL", "PGPASSWORD=TestingQA2026!SecurePassword psql -h 127.0.0.1 -U fiveaday_tester -d fiveaday_testing -c '\\q'"] web: # Use the QA-specific env file instead of the default .env @@ -34,8 +41,11 @@ services: project.wsgi:application environment: - # Ensure the entrypoint collects static files DJANGO_ENV: testing + POSTGRES_DB: fiveaday_testing + POSTGRES_USER: fiveaday_tester + POSTGRES_PASSWORD: 'TestingQA2026!SecurePassword' + POSTGRES_HOST: db volumes: testing_postgres_data: diff --git a/docker-compose.yml b/docker-compose.yml index 10f515a..e4e64b7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -74,6 +74,13 @@ services: CELERY_RESULT_BACKEND: redis://redis:6379/0 POSTGRES_HOST: db + # Live-mount source code so template/CSS/Python changes are visible + # without a full rebuild. The anonymous .venv volume keeps the + # container's installed packages isolated from the host. + volumes: + - .:/app + - /app/.venv + # Wait for PostgreSQL + Redis before starting depends_on: db: @@ -122,6 +129,9 @@ services: CELERY_BROKER_URL: redis://redis:6379/0 CELERY_RESULT_BACKEND: redis://redis:6379/0 POSTGRES_HOST: db + volumes: + - .:/app + - /app/.venv working_dir: /app/project command: celery -A project.celery worker -l info -Q celery,emails --concurrency=2 depends_on: @@ -149,6 +159,9 @@ services: CELERY_BROKER_URL: redis://redis:6379/0 CELERY_RESULT_BACKEND: redis://redis:6379/0 POSTGRES_HOST: db + volumes: + - .:/app + - /app/.venv working_dir: /app/project command: celery -A project.celery beat -l info --pidfile=/tmp/celerybeat.pid --schedule=/tmp/celerybeat-schedule depends_on: diff --git a/project/core/context_processors.py b/project/core/context_processors.py index aa59f18..800545c 100644 --- a/project/core/context_processors.py +++ b/project/core/context_processors.py @@ -37,6 +37,7 @@ def today_notifications(request): show_testing_tools = ( settings.IS_TESTING_ENV and settings.QA_TESTING_USERNAME + and hasattr(request, "session") and request.session.get("username") == settings.QA_TESTING_USERNAME ) diff --git a/project/core/static/css/admin_custom.css b/project/core/static/css/admin_custom.css new file mode 100644 index 0000000..b2fb0ef --- /dev/null +++ b/project/core/static/css/admin_custom.css @@ -0,0 +1,526 @@ +/* Five a Day eVolution — Admin custom styles + Lavender palette, forced light mode, rounded UI, card-based index. + Loaded via admin/base_site.html extrastyle block. */ + +/* ==================================================================== + FORCE LIGHT MODE — no dark/light toggle + ==================================================================== */ +html { + color-scheme: only light; +} + +/* ==================================================================== + COLOR THEME — lavender / violet palette (matches main app primary-*) + ==================================================================== */ +:root { + --primary: #8b5cf6; /* violet-500 */ + --secondary: #7c3aed; /* violet-600 */ + --accent: #c4b5fd; /* violet-300 */ + --primary-fg: #ffffff; + + --body-fg: #1e1b4b; + --body-bg: #faf8ff; + --body-quiet-color: #7c3aed; + --body-loud-color: #5b21b6; + + --header-color: #ffffff; + --header-branding-color: #ddd6fe; + --header-bg: #6d28d9; + --header-link-color: #ede9fe; + + --breadcrumbs-fg: #ddd6fe; + --breadcrumbs-link-fg: #ffffff; + --breadcrumbs-bg: #7c3aed; + + --link-fg: #7c3aed; + --link-hover-color: #4c1d95; + --link-selected-fg: #5b21b6; + + --hairline-color: #ede9fe; + --border-color: #ddd6fe; + + --error-fg: #dc2626; + --message-success-bg: #d1fae5; + --message-warning-bg: #fef3c7; + --message-error-bg: #fee2e2; + + --darkened-bg: #f5f3ff; + --selected-bg: #ede9fe; + --selected-row: #f5f3ff; + + --button-fg: #ffffff; + --button-bg: #8b5cf6; + --button-hover-bg: #7c3aed; + --default-button-fg: #ffffff; + --default-button-bg: #7c3aed; + --default-button-hover-bg: #6d28d9; + --close-button-bg: #a78bfa; + --close-button-hover-bg: #8b5cf6; + --delete-button-bg: #dc2626; + --delete-button-hover-bg: #b91c1c; + + --object-tools-fg: #ffffff; + --object-tools-bg: #6d28d9; + --object-tools-hover-bg: #5b21b6; +} + +/* Hide the built-in dark/light theme toggle — we force light mode only */ +.theme-toggle { + display: none !important; +} + +/* ==================================================================== + FONT — match the main application + ==================================================================== */ +body, #header, #nav-sidebar, input, select, textarea, button { + font-family: 'Trebuchet MS', ui-sans-serif, system-ui, sans-serif; +} + +/* ==================================================================== + HEADER + ==================================================================== */ +#header { + background: linear-gradient(135deg, #5b21b6 0%, #7c3aed 60%, #8b5cf6 100%); + border-bottom: 3px solid #c4b5fd; + padding: 8px 16px; +} + +#branding h1, #branding h1 a, #branding h1 a:visited { + color: #ffffff; + font-size: 1.1rem; + font-weight: 700; + letter-spacing: 0.02em; + display: flex; + align-items: center; + gap: 10px; + text-decoration: none; +} + +#branding h1 .admin-logo { + border-radius: 6px; + background: #fff; + padding: 2px; + height: 36px; + width: 36px; + object-fit: contain; +} + +/* ==================================================================== + NAVIGATION SIDEBAR + ==================================================================== */ +#nav-sidebar { + padding-right: 1rem; +} + +#nav-sidebar .module caption { + background: #6d28d9; + color: #ffffff; +} + +/* ==================================================================== + MODULE / TABLE HEADERS — rounded containers + ==================================================================== */ +.module { + border-radius: 10px; + overflow: hidden; + border: 1px solid #ede9fe; +} + +.module h2, +.module caption, +.inline-group h2 { + background: linear-gradient(90deg, #7c3aed, #8b5cf6); + color: #ffffff; +} + +/* ==================================================================== + FIELDSETS + ==================================================================== */ +fieldset.module h2 { + background: #ede9fe; + color: #4c1d95; + border-left: 4px solid #8b5cf6; +} + +/* ==================================================================== + TABLES — rounded, lavender header + ==================================================================== */ +#result_list { + border-radius: 10px; + overflow: hidden; + border: 1px solid #ddd6fe; +} + +#result_list thead th { + background: #ede9fe; + color: #4c1d95; + border-bottom: 2px solid #c4b5fd; + font-weight: 700; +} + +#result_list tbody tr:nth-child(even) td, +#result_list tbody tr:nth-child(even) th { + background: #faf8ff; +} + +#result_list tbody tr:hover td, +#result_list tbody tr:hover th { + background: #f5f3ff; +} + +/* ==================================================================== + SEARCH BAR — rounded pill + ==================================================================== */ +#changelist-search input[type="text"], +#changelist-search input[type="search"], +.search-row input[type="text"], +.search-row input[type="search"] { + border-radius: 24px; + border: 1.5px solid #ddd6fe; + padding: 7px 16px; + background: #faf8ff; + transition: border-color 0.2s, box-shadow 0.2s; +} + +#changelist-search input[type="text"]:focus, +#changelist-search input[type="search"]:focus, +.search-row input:focus { + border-color: #8b5cf6; + box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.12); + outline: none; +} + +#changelist-search input[type="submit"], +.search-row input[type="submit"] { + border-radius: 20px; + background: #8b5cf6; + color: #fff; + border: none; + padding: 7px 16px; + cursor: pointer; + font-weight: 600; + transition: background 0.15s; +} + +#changelist-search input[type="submit"]:hover { + background: #7c3aed; +} + +/* ==================================================================== + BUTTONS + ==================================================================== */ +.button, +input[type="submit"], +input[type="button"], +.submit-row input, +a.button { + border-radius: 8px; +} + +/* ==================================================================== + DASHBOARD WELCOME BANNER + ==================================================================== */ +.five-a-day-welcome { + background: linear-gradient(135deg, #f5f3ff 0%, #ede9fe 100%); + border: 1px solid #ddd6fe; + border-left: 5px solid #8b5cf6; + border-radius: 12px; + padding: 1.25rem 1.5rem; + margin-bottom: 2rem; + display: flex; + align-items: flex-start; + gap: 1rem; +} + +.five-a-day-welcome .welcome-logo { + width: 56px; + height: 56px; + object-fit: contain; + border-radius: 8px; + flex-shrink: 0; +} + +.five-a-day-welcome .welcome-text h2 { + color: #4c1d95; + font-size: 1.1rem; + font-weight: 700; + margin: 0 0 0.35rem; + background: none; + padding: 0; +} + +.five-a-day-welcome .welcome-text p { + color: #5b21b6; + margin: 0; + font-size: 0.88rem; + line-height: 1.55; +} + +/* ==================================================================== + ADMIN INDEX — SECTIONS + ==================================================================== */ +.five-a-day-section { + margin-bottom: 2.5rem; +} + +.five-a-day-section-header { + display: flex; + align-items: center; + gap: 0.65rem; + margin-bottom: 1rem; + padding-bottom: 0.55rem; + border-bottom: 2px solid #ddd6fe; +} + +.five-a-day-section-header .section-icon { + font-size: 1.25rem; + line-height: 1; +} + +.five-a-day-section-header h2 { + font-size: 0.98rem; + font-weight: 700; + color: #4c1d95; + margin: 0; + text-transform: none; + background: none; + padding: 0; +} + +.five-a-day-section-header .section-desc { + margin-left: auto; + color: #7c3aed; + font-size: 0.76rem; + font-style: italic; +} + +/* ==================================================================== + ADMIN INDEX — MODEL CARDS + ==================================================================== */ +.five-a-day-model-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); + gap: 13px; +} + +.model-card { + background: #ffffff; + border: 1px solid #e4d4fe; + border-left: 4px solid #8b5cf6; + border-radius: 12px; + padding: 1rem 1.1rem 0.85rem; + display: flex; + flex-direction: column; + gap: 0.3rem; + box-shadow: 0 1px 4px rgba(139, 92, 246, 0.07); + transition: box-shadow 0.2s, transform 0.15s; +} + +.model-card:hover { + box-shadow: 0 4px 18px rgba(139, 92, 246, 0.15); + transform: translateY(-2px); +} + +.model-card .card-icon { + font-size: 1.75rem; + line-height: 1; + margin-bottom: 0.15rem; +} + +.model-card .card-name { + font-weight: 700; + color: #4c1d95; + font-size: 0.93rem; + line-height: 1.2; +} + +.model-card .card-desc { + color: #6d28d9; + font-size: 0.77rem; + flex: 1; + line-height: 1.45; +} + +.model-card .card-actions { + display: flex; + gap: 0.45rem; + margin-top: 0.65rem; + flex-wrap: wrap; +} + +.model-card .card-btn { + display: inline-flex; + align-items: center; + gap: 3px; + padding: 4px 11px; + border-radius: 20px; + font-size: 0.73rem; + font-weight: 600; + text-decoration: none; + transition: background 0.15s; + line-height: 1.4; +} + +.model-card .card-btn.view-btn { + background: #ede9fe; + color: #6d28d9; +} + +.model-card .card-btn.view-btn:hover { + background: #ddd6fe; + color: #4c1d95; +} + +.model-card .card-btn.add-btn { + background: #8b5cf6; + color: #ffffff; +} + +.model-card .card-btn.add-btn:hover { + background: #7c3aed; +} + +/* ==================================================================== + LOGIN PAGE — standalone card, no Django chrome + ==================================================================== */ +body.login { + background: linear-gradient(160deg, #f5f3ff 0%, #ede9fe 45%, #ddd6fe 100%); + min-height: 100vh; + margin: 0; +} + +body.login #header, +body.login #nav-sidebar, +body.login div[role="navigation"], +body.login #nav-breadcrumbs { + display: none !important; +} + +body.login #container { + display: flex; + align-items: center; + justify-content: center; + min-height: 100vh; + padding: 2rem 1rem; + box-sizing: border-box; +} + +body.login #main { + width: 100%; + max-width: 420px; +} + +body.login #content-main { + background: #ffffff; + border-radius: 14px; + box-shadow: 0 8px 40px rgba(76, 29, 149, 0.15), + 0 2px 8px rgba(109, 40, 217, 0.1); + padding: 2.5rem 2.25rem; +} + +body.login .login-brand { + text-align: center; + margin-bottom: 2rem; + padding-bottom: 1.5rem; + border-bottom: 1px solid #ede9fe; +} + +body.login .login-brand img { + width: 80px; + height: 80px; + object-fit: contain; + border-radius: 10px; + margin: 0 auto 0.75rem; + display: block; +} + +body.login .login-brand h1 { + color: #4c1d95; + font-size: 1.35rem; + font-weight: 700; + margin: 0 0 0.2rem; +} + +body.login .login-brand .login-subtitle { + color: #7c3aed; + font-size: 0.85rem; + margin: 0; +} + +body.login .form-row { + margin-bottom: 1rem; +} + +body.login label { + color: #4c1d95; + font-weight: 600; + font-size: 0.85rem; + display: block; + margin-bottom: 4px; +} + +body.login input[type="text"], +body.login input[type="password"] { + width: 100%; + border: 1.5px solid #ddd6fe; + border-radius: 8px; + padding: 9px 12px; + font-size: 0.95rem; + font-family: 'Trebuchet MS', sans-serif; + transition: border-color 0.2s, box-shadow 0.2s; + box-sizing: border-box; + background: #faf8ff; +} + +body.login input[type="text"]:focus, +body.login input[type="password"]:focus { + border-color: #8b5cf6; + box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.12); + outline: none; + background: #ffffff; +} + +body.login .submit-row { + margin-top: 1.5rem; + padding: 0; + background: transparent; + border: none; + text-align: center; + overflow: visible; + border-radius: 0; +} + +body.login input[type="submit"] { + width: 100%; + background: linear-gradient(135deg, #8b5cf6, #7c3aed); + color: #ffffff; + border: none; + border-radius: 8px; + padding: 11px; + font-size: 1rem; + font-weight: 600; + cursor: pointer; + font-family: 'Trebuchet MS', sans-serif; + transition: background 0.2s, transform 0.1s, box-shadow 0.2s; + box-shadow: 0 2px 8px rgba(139, 92, 246, 0.25); +} + +body.login input[type="submit"]:hover { + background: linear-gradient(135deg, #7c3aed, #6d28d9); + box-shadow: 0 4px 14px rgba(139, 92, 246, 0.35); + transform: translateY(-1px); +} + +body.login input[type="submit"]:active { + transform: translateY(0); +} + +body.login .errornote { + background: #fee2e2; + border: 1px solid #fca5a5; + border-radius: 7px; + color: #b91c1c; + padding: 0.6rem 1rem; + font-size: 0.9rem; + margin-bottom: 1rem; +} diff --git a/project/static/css/email.css b/project/core/static/css/email.css similarity index 100% rename from project/static/css/email.css rename to project/core/static/css/email.css diff --git a/project/core/static/images/divider.svg b/project/core/static/images/divider.svg new file mode 100644 index 0000000..a4aeeb2 --- /dev/null +++ b/project/core/static/images/divider.svg @@ -0,0 +1,239 @@ + + +Created by potrace 1.15, written by Peter Selinger 2001-2017 + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/project/static/images/logo_white_bg.png b/project/core/static/images/logo_white_bg.png similarity index 100% rename from project/static/images/logo_white_bg.png rename to project/core/static/images/logo_white_bg.png diff --git a/project/core/static/js/login_effects.js b/project/core/static/js/login_effects.js new file mode 100644 index 0000000..a5c0a67 --- /dev/null +++ b/project/core/static/js/login_effects.js @@ -0,0 +1,69 @@ +/* login_effects.js — text & button animations for the login card */ +(function () { + 'use strict'; + + /* ── Inject all animation CSS ───────────────────────────── */ + var styleEl = document.createElement('style'); + styleEl.textContent = + + /* Bienvenida: quill reveal left-to-right */ + '@keyframes bridgertonBloom {' + + ' 0% { opacity:0; clip-path:inset(0 100% 0 0); filter:blur(5px); }' + + ' 18% { opacity:1; }' + + ' 100% { opacity:1; clip-path:inset(0 0% 0 0); filter:blur(0); }' + + '}' + + '.fx-heading { animation:bridgertonBloom 1.4s cubic-bezier(.23,1,.32,1) both; }' + + + /* Quote: fast spring reveal */ + '@keyframes quoteReveal {' + + ' from { opacity:0; transform:translateY(8px); }' + + ' to { opacity:1; transform:translateY(0); }' + + '}' + + '.fx-quote { animation:quoteReveal .3s cubic-bezier(.34,1.56,.64,1) both; }' + + + /* Quote: violet glow pulse */ + '@keyframes quoteGlow {' + + ' 0% { text-shadow:0 0 0 rgba(139,92,246,0); }' + + ' 35% { text-shadow:0 0 14px rgba(139,92,246,.55),0 0 30px rgba(124,58,237,.18); }' + + ' 100% { text-shadow:0 0 0 rgba(139,92,246,0); }' + + '}' + + '.fx-quote-glow { animation:quoteGlow 1.4s ease-in-out; }' + + + ''; + + document.head.appendChild(styleEl); + + /* ── Timing (ms) ────────────────────────────────────────── */ + var HEADING_MS = 1400; // bridgertonBloom duration + var QUOTE_MS = 300; // quoteReveal duration + var QUOTE_DELAY = 1000; // quote appears 1s after page load + + /* ── Sequence ───────────────────────────────────────────── */ + function run() { + var heading = document.querySelector('.card-heading'); + var quote = document.getElementById('login-quote'); + + /* Step 1 — Bienvenida, immediately on load */ + if (heading) heading.classList.add('fx-heading'); + + /* Step 2 — Quote, right after Bienvenida finishes */ + setTimeout(function () { + if (!quote) return; + quote.classList.add('fx-quote'); + + /* Quote glow fires once the reveal completes */ + setTimeout(function () { + if (!quote) return; + quote.style.opacity = '1'; + quote.classList.add('fx-quote-glow'); + }, QUOTE_MS); + + }, QUOTE_DELAY); + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', run); + } else { + run(); + } +})(); diff --git a/project/core/static/js/login_seasonal.js b/project/core/static/js/login_seasonal.js new file mode 100644 index 0000000..e69de29 diff --git a/project/core/templates/login.html b/project/core/templates/login.html index d8e0210..6b4384b 100644 --- a/project/core/templates/login.html +++ b/project/core/templates/login.html @@ -5,6 +5,59 @@ Acceso · Five a Day + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-

Five a Day

-

Sistema de Gestión Escolar

+ + Five a Day + +

Donde el inglés es tuyo

Bienvenida

-

Accede para gestionar tu academia

+

Accede para gestionar tu academia

{% if messages %} {% for message in messages %} @@ -236,14 +316,16 @@

Five a Day

{% if google_oauth_available %} - - - - + + + + Iniciar sesión con Google -
o con usuario y contraseña
+ {% endif %} @@ -272,5 +354,22 @@

Five a Day

+ + diff --git a/project/project/settings.py b/project/project/settings.py index 1a77557..a1d392d 100644 --- a/project/project/settings.py +++ b/project/project/settings.py @@ -5,7 +5,7 @@ import dj_database_url from dotenv import load_dotenv -load_dotenv(Path(__file__).resolve().parent.parent.parent / ".env", override=True) +load_dotenv(Path(__file__).resolve().parent.parent.parent / ".env", override=False) BASE_DIR = Path(__file__).resolve().parent.parent @@ -15,7 +15,7 @@ # NOTA: Usa `make version x.y.z` para actualizar ambos sitios a la vez: # - pyproject.toml (campo version) # - README.md (badge y tabla de versiones — gestionado por la skill update-readme) -APP_VERSION = os.getenv("APP_VERSION", "1.0.10") +APP_VERSION = os.getenv("APP_VERSION", "1.0.11") # ============================================================================ # SECURITY SETTINGS @@ -275,7 +275,6 @@ # ============================================================================ STATIC_URL = "/static/" STATIC_ROOT = BASE_DIR.parent / "staticfiles" -STATICFILES_DIRS = [BASE_DIR / "static"] # Configuración de WhiteNoise para producción STORAGES = { diff --git a/project/static/apple-touch-icon.png b/project/static/apple-touch-icon.png deleted file mode 100644 index 18031cb65444aff9c5355dbd462737500ec8d11d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18091 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 c-_IZ3OyKPHU@zb07 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 + {% endblock %} +{% comment %}Empty this block to disable Django admin's dark-mode CSS variable overrides.{% endcomment %} +{% block dark-mode-vars %}{% endblock %} + {% block branding %}

diff --git a/project/templates/admin/index.html b/project/templates/admin/index.html index d2b0d9c..f5a650c 100644 --- a/project/templates/admin/index.html +++ b/project/templates/admin/index.html @@ -6,57 +6,294 @@ {% block content %}
+
-

¡Bienvenido/a al panel de gestión!

+

¡Bienvenidas, Silvia y Penelope! 👋

- Desde aquí puedes gestionar estudiantes, familias, matrículas, pagos, - horarios y comunicaciones de Five a Day English Academy. - Usa el menú de la izquierda o selecciona una sección de abajo. + Desde aquí podéis gestionar todo lo de la academia: alumnos, familias, + matrículas, pagos, horarios y más. Usad las tarjetas de abajo para + ir directamente a lo que necesitéis.

- {% if app_list %} - {% for app in app_list %} -
- - + +
+
+ 📊 +

Datos

+ Alumnos, familias, pagos y configuración de la academia +
+
+ {% for app in app_list %} + {% if app.app_label == "students" or app.app_label == "billing" or app.app_label == "core" %} {% for model in app.models %} -
- {% if model.admin_url %} - - {% else %} - - {% endif %} - - + {% if model.add_url %} + + Añadir + {% endif %} + + + {% endif %} {% endfor %} -
- - {{ app.verbose_name }} - -
{{ model.verbose_name_plural|capfirst }}{{ model.verbose_name_plural|capfirst }} + {% if model.object_name != "SiteConfiguration" and model.object_name != "EnrollmentType" and model.object_name != "StudentParent" %} +
+ + {% if model.object_name == "Student" %}🧑‍🎓 + {% elif model.object_name == "Parent" %}👨‍👩‍👧 + {% elif model.object_name == "Teacher" %}👩‍🏫 + {% elif model.object_name == "Group" and app.app_label == "students" %}👥 + {% elif model.object_name == "StudentParent" %}🔗 + {% elif model.object_name == "Payment" %}💳 + {% elif model.object_name == "Enrollment" %}📋 + {% elif model.object_name == "EnrollmentType" %}📝 + {% elif model.object_name == "SiteConfiguration" %}⚙️ + {% elif model.object_name == "HistoryLog" %}📜 + {% elif model.object_name == "TodoItem" %}✅ + {% elif model.object_name == "ScheduleSlot" %}🗓️ + {% elif model.object_name == "FunFridayAttendance" %}🎉 + {% else %}📦 + {% endif %} + +
+ {% if model.object_name == "Student" %}Alumnos + {% elif model.object_name == "Parent" %}Padres y Tutores + {% elif model.object_name == "Teacher" %}Profesores + {% elif model.object_name == "Group" and app.app_label == "students" %}Grupos + {% elif model.object_name == "StudentParent" %}Vínculos alumno–familiar + {% elif model.object_name == "Payment" %}Pagos + {% elif model.object_name == "Enrollment" %}Matrículas + {% elif model.object_name == "EnrollmentType" %}Tipos de matrícula + {% elif model.object_name == "SiteConfiguration" %}Configuración global + {% elif model.object_name == "HistoryLog" %}Historial de actividad + {% elif model.object_name == "TodoItem" %}Tareas pendientes + {% elif model.object_name == "ScheduleSlot" %}Horarios + {% elif model.object_name == "FunFridayAttendance" %}Fun Friday + {% else %}{{ model.verbose_name_plural|capfirst }} + {% endif %} +
+
+ {% if model.object_name == "Student" %} + Ficha completa de cada alumno: datos personales, grupo y estado de matrícula. + {% elif model.object_name == "Parent" %} + Datos de contacto de los familiares y responsables de los alumnos. + {% elif model.object_name == "Teacher" %} + Ficha de cada profesor y monitor de la academia. + {% elif model.object_name == "Group" and app.app_label == "students" %} + Grupos y clases de la academia: nombre, nivel y horario. + {% elif model.object_name == "StudentParent" %} + Relaciones entre alumnos y sus padres o tutores. + {% elif model.object_name == "Payment" %} + Registro de todos los pagos: cuotas mensuales, matrículas y recibos. + {% elif model.object_name == "Enrollment" %} + Matrículas activas e históricas de cada alumno por año académico. + {% elif model.object_name == "EnrollmentType" %} + Modalidades de inscripción disponibles en la academia. + {% elif model.object_name == "SiteConfiguration" %} + Precios, cuotas, IVA y ajustes generales del sistema. Solo hay un registro. + {% elif model.object_name == "HistoryLog" %} + Registro automático de las acciones realizadas en el sistema. + {% elif model.object_name == "TodoItem" %} + Lista de tareas y recordatorios del equipo de la academia. + {% elif model.object_name == "ScheduleSlot" %} + Configuración de las franjas horarias del cuadrante semanal. + {% elif model.object_name == "FunFridayAttendance" %} + Registro de asistencia a las actividades especiales de los viernes. + {% else %} + Gestión de {{ model.verbose_name_plural }}. + {% endif %} +
+
+ {% if model.admin_url %} + 📋 Ver + {% endif %} {% if model.add_url %} - {% trans 'Añadir' %} + + Añadir {% endif %} +
+
+ {% endif %} + {% endfor %} + {% endif %} + {% endfor %} + + + + +
+
+ 🗂️ +

Datos extra

+ Configuración avanzada y tablas de referencia +
+
+ {% for app in app_list %} + {% if app.app_label == "students" or app.app_label == "billing" %} + {% for model in app.models %} + {% if model.object_name == "SiteConfiguration" or model.object_name == "EnrollmentType" or model.object_name == "StudentParent" %} +
+ + {% if model.object_name == "SiteConfiguration" %}⚙️ + {% elif model.object_name == "EnrollmentType" %}📝 + {% elif model.object_name == "StudentParent" %}🔗 + {% endif %} + +
+ {% if model.object_name == "SiteConfiguration" %}Configuración global + {% elif model.object_name == "EnrollmentType" %}Tipos de matrícula + {% elif model.object_name == "StudentParent" %}Vínculos alumno–familiar + {% endif %} +
+
+ {% if model.object_name == "SiteConfiguration" %} + Precios, cuotas, IVA y ajustes generales del sistema. Solo hay un registro. + {% elif model.object_name == "EnrollmentType" %} + Modalidades de inscripción disponibles en la academia. + {% elif model.object_name == "StudentParent" %} + Relaciones entre alumnos y sus padres o tutores. + {% endif %} +
+
{% if model.admin_url %} - {% if model.view_only %} - {% trans 'Ver' %} - {% else %} - {% trans 'Editar' %} - {% endif %} + 📋 Ver {% endif %} -
+ {% endif %} + {% endfor %} +
+
+ + + {% for app in app_list %}{% if app.app_label == "gsheets" %} +
+
+ 📗 +

Google Sheets

+ Integración con hojas de cálculo de Google
- {% endfor %} - {% else %} -

{% trans "No tienes permisos para ver o editar nada." %}

+
+ {% for model in app.models %} +
+ + {% if model.object_name == "AccessCredentials" %}🔑 + {% else %}📄{% endif %} + +
+ {% if model.object_name == "AccessCredentials" %}Credenciales Google + {% else %}{{ model.verbose_name_plural|capfirst }}{% endif %} +
+
+ {% if model.object_name == "AccessCredentials" %} + Token de acceso para la integración con Google Sheets. + {% else %} + Gestión de {{ model.verbose_name_plural }}. + {% endif %} +
+
+ {% if model.admin_url %} + 📋 Ver + {% endif %} + {% if model.add_url %} + + Añadir + {% endif %} +
+
+ {% endfor %} +
+
+ {% endif %}{% endfor %} + + + {% for app in app_list %}{% if app.app_label == "auth" %} +
+
+ 🔐 +

Seguridad

+ Usuarios y permisos de acceso al panel +
+
+ {% for model in app.models %} +
+ + {% if model.object_name == "User" %}👤 + {% elif model.object_name == "Group" %}🔐 + {% else %}🛡️{% endif %} + +
+ {% if model.object_name == "User" %}Usuarios + {% elif model.object_name == "Group" %}Grupos de permisos + {% else %}{{ model.verbose_name_plural|capfirst }}{% endif %} +
+
+ {% if model.object_name == "User" %} + Cuentas de acceso al panel de administración. + {% elif model.object_name == "Group" %} + Roles y conjuntos de permisos para los usuarios del panel. + {% else %} + Gestión de {{ model.verbose_name_plural }}. + {% endif %} +
+
+ {% if model.admin_url %} + 📋 Ver + {% endif %} + {% if model.add_url %} + + Añadir + {% endif %} +
+
+ {% endfor %} +
+
+ {% endif %}{% endfor %} + + + {% for app in app_list %} + {% if app.app_label != "students" and app.app_label != "billing" and app.app_label != "core" and app.app_label != "gsheets" and app.app_label != "auth" %} +
+
+ 🔧 +

Otros — {{ app.verbose_name }}

+
+
+ {% for model in app.models %} +
+ 📦 +
{{ model.verbose_name_plural|capfirst }}
+
Gestión de {{ model.verbose_name_plural }}.
+
+ {% if model.admin_url %} + 📋 Ver + {% endif %} + {% if model.add_url %} + + Añadir + {% endif %} +
+
+ {% endfor %} +
+
{% endif %} + {% endfor %}

{% endblock %} diff --git a/project/templates/admin/login.html b/project/templates/admin/login.html index bfe01d4..112b31a 100644 --- a/project/templates/admin/login.html +++ b/project/templates/admin/login.html @@ -3,7 +3,7 @@ {% block extrastyle %} {{ block.super }} - +{% comment %}Do NOT load admin/css/login.css — it conflicts with admin_custom.css.{% endcomment %} {% endblock %} {% block bodyclass %}login{% endblock %} @@ -15,16 +15,17 @@ {% block content %}
+ {% if user.is_authenticated %}

- {% blocktranslate trimmed %} - Has iniciado sesión como {{ username }}, pero no tienes permiso para acceder a esta página. + {% blocktranslate trimmed with username=user.get_username %} + Estás conectado como {{ username }}, pero no tienes permiso para acceder a esta página. ¿Deseas entrar con una cuenta diferente? {% endblocktranslate %}

@@ -46,8 +47,9 @@

Five a Day eVolution

{% endfor %} {% endif %} -
+ {% csrf_token %} +
{{ form.username.errors }} @@ -57,11 +59,11 @@

Five a Day eVolution

{{ form.password.errors }} {{ form.password }} -
+
{% endblock %} diff --git a/pyproject.toml b/pyproject.toml index b9048d6..b2eae09 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "five-a-day" -version = "1.0.10" +version = "1.0.11" description = "Five a Day management software" readme = "README.md" requires-python = ">=3.12" diff --git a/uv.lock b/uv.lock index e4e789a..8a0c1e2 100644 --- a/uv.lock +++ b/uv.lock @@ -702,7 +702,7 @@ wheels = [ [[package]] name = "five-a-day" -version = "1.0.10" +version = "1.0.11" source = { editable = "." } dependencies = [ { name = "celery" },