GC Jobs

Home

Two services on PCT 107 (debian, 192.168.2.81) for Government of Canada job hunting.


gcjobs-qa (stack: gcjobs-qa)

Streamlit STAR interview prep assistant at gcjobs.carr-family.org (Authentik protected).

  • Port: 8501 (host-mode on 192.168.2.81)
  • Image: gcjobs-qa:latest — built locally on PCT 107
  • Data volume: gcjobs-qa_gcjobs_data/app/data/ (resume.json, star_answers.csv, etc.)
  • Env: OPENAI_API_KEY in compose

gcjobs-filler (stack: gcjobs-filler)

Selenium bot that pre-fills GC Jobs (PSC) applications section-by-section and emails a summary for manual review before submitting.

  • URL: gcjobs-filler.carr-family.org (no Authentik — static route in routes.yml)
  • Port: 8000 (FastAPI, host-mode on 192.168.2.81)
  • Image: gcjobs-filler:latest — built locally on PCT 107 from /tank/appdata/gcjobs-filler/app/
  • Compose: /tank/appdata/gcjobs-filler/docker-compose.ymltraefik.enable=false, routing via routes.yml
  • Data: /tank/appdata/gcjobs-filler/data/ on ZFS — mounted into PCT 107 via mp3, then Docker bind-mounts to /app/data
  • STAR data: gcjobs-qa answer library mounted read-only at /app/star_data from gcjobs-qa_gcjobs_data volume on PCT 107
  • Permissions: data dir chmod 777, files 666, screenshots dir 777 (ZFS owned root:root, container writes as unprivileged user)
  • Notifications: email via Proton Bridge SMTP when pre-fill completes

Bot Flow

Navigate to page1710?careerChoiceId=<id>&psrsMode=11 → PSC login (UserNumber/Password) → OTP from Proton Bridge IMAP (Folders/GC Jobs) → optional page1570 (Notice page — bot clicks Continue) → optional page1960 (Confirmation/consent — bot checks checkbox then clicks Continue) → /applicant/<appid>/page1600 → fill 8 sections → stop before Submit → email notification.

8 sections filled: Employee info, Résumé, Screening Questions, Work locations, Classification, Education, Languages, Employment Equity.

On unexpected pages: bot saves a screenshot to /app/data/screenshots/ for debugging.


Supported Job URL Formats

Format Example
Standard posting page1800?poster=2424673
Direct apply link page1710?careerChoiceId=2424673
Newer PSC format /applicant/1156524/page1820?careerChoice=2424673

Career ID is extracted automatically from any of these formats.


Config Files

File Purpose
/tank/appdata/gcjobs-filler/data/config.yml GCKey creds ([email protected]), IMAP config, email, monitor settings
/tank/appdata/gcjobs-filler/data/profile.yml Employment info, language prefs, screening Q answers + narratives

Key profile.yml Settings

  • employment.classification — must match visible text in the PSC dropdown (e.g. "EC-05")
  • screening_questions — map question-text substrings to 'yes'/'no'; bot fuzzy-matches (≥85%) dynamically per job
  • screening_narratives — free-text responses for Yes answers; same substring keys

Screening Question Resolution Pipeline (app/ai_helper.py)

  1. Fuzzy match (≥85% via rapidfuzz) against profile.yml screening_questions → use profiled answer
  2. If answer is yes and no narrative in screening_narratives, fuzzy-match question against STAR CSV → AI adapts that answer as narrative
  3. No profile match → fuzzy-match (≥70%) against STAR CSV → answer yes + AI-adapt STAR narrative
  4. No STAR match → ask OpenAI (gpt-4o-mini) yes/no from resume.json → if yes, generate narrative via gpt-4o

"Section link not found for screeningQuestions" — normal; the bot skips jobs with no screening questions.


Web UI Tabs

Tab Purpose
Apply Submit a job URL
Monitor Watch the bot's progress
Jobs Browse scraped job listings
History Past applications
Profile Employment, languages, education, EE consent, screening Qs + narratives
STAR Answers Searchable answer library (from gcjobs-qa)
Settings GCKey creds, IMAP, SMTP, monitor config

Settings password gotcha — browsers block pre-filling type="password" fields, so opening Settings shows blank boxes. The backend preserves existing passwords when an empty string is submitted — safe to save without touching password fields.


Monitor Filters (Settings tab)

  • keywords — title/classification substrings
  • classifications — e.g. EC-05
  • language_requirements — e.g. bilingual, english essential; blank = all

Jobs table shows extracted language requirement per posting (colour-coded: purple = bilingual, blue = English essential).


Rebuild & Redeploy

# Build (context is parent dir — Dockerfile does COPY app/ .)
tar -cf - -C /tank/appdata/gcjobs-filler --exclude='./data' . | \
  pct exec 107 -- bash -c 'rm -rf /tmp/gcjobs-build && mkdir -p /tmp/gcjobs-build && tar -xf - -C /tmp/gcjobs-build'
pct exec 107 -- docker build -t gcjobs-filler:latest /tmp/gcjobs-build/

# Image-only change (no compose changes):
pct exec 108 -- docker service update --force gcjobs-filler_gcjobs-filler

# Compose changes (volumes, ports, etc.) — must pipe file in; PCT 108 can't read /tank/appdata/gcjobs-filler/ directly:
cat /tank/appdata/gcjobs-filler/docker-compose.yml | \
  pct exec 108 -- bash -c 'cat > /tmp/gcjobs-filler-compose.yml'
pct exec 108 -- docker stack deploy -c /tmp/gcjobs-filler-compose.yml gcjobs-filler

Deploy gotcha: PCT 108 cannot access /tank/appdata/gcjobs-filler/ (not in its LXC mounts). Always pipe the compose file in via stdin. cp /tank/... /tmp/... on the Proxmox host writes to the host's /tmp/, not PCT 108's.


Known Issue

Client.__init__() got an unexpected keyword argument 'proxies' — AI narrative generation is broken in gcjobs-filler. Leaving screening question narratives unfilled. Cause: OpenAI client version incompatibility.