Blame

75dffb Damien 2026-06-15 10:02:38
Flesh out all pages — add Home Assistant, LAN Machines, GC Jobs, Known Issues; expand Infrastructure, Docker Swarm, Network/Media/Documents/Traefik pages
1
# GC Jobs
2
3
← [[Home]]
4
5
Two services on PCT 107 (debian, 192.168.2.81) for Government of Canada job hunting.
6
7
---
8
9
## gcjobs-qa (stack: `gcjobs-qa`)
10
11
Streamlit STAR interview prep assistant at `gcjobs.carr-family.org` (Authentik protected).
12
13
- **Port:** 8501 (host-mode on 192.168.2.81)
14
- **Image:** `gcjobs-qa:latest` — built locally on PCT 107
15
- **Data volume:** `gcjobs-qa_gcjobs_data``/app/data/` (resume.json, star_answers.csv, etc.)
16
- **Env:** `OPENAI_API_KEY` in compose
17
18
---
19
20
## gcjobs-filler (stack: `gcjobs-filler`)
21
22
Selenium bot that pre-fills GC Jobs (PSC) applications section-by-section and emails a summary for manual review before submitting.
23
24
- **URL:** `gcjobs-filler.carr-family.org` (no Authentik — static route in `routes.yml`)
25
- **Port:** 8000 (FastAPI, host-mode on 192.168.2.81)
26
- **Image:** `gcjobs-filler:latest` — built locally on PCT 107 from `/tank/appdata/gcjobs-filler/app/`
27
- **Compose:** `/tank/appdata/gcjobs-filler/docker-compose.yml``traefik.enable=false`, routing via `routes.yml`
28
- **Data:** `/tank/appdata/gcjobs-filler/data/` on ZFS — mounted into PCT 107 via `mp3`, then Docker bind-mounts to `/app/data`
29
- **STAR data:** gcjobs-qa answer library mounted read-only at `/app/star_data` from `gcjobs-qa_gcjobs_data` volume on PCT 107
30
- **Permissions:** data dir `chmod 777`, files `666`, screenshots dir `777` (ZFS owned root:root, container writes as unprivileged user)
31
- **Notifications:** email via Proton Bridge SMTP when pre-fill completes
32
33
---
34
35
## Bot Flow
36
37
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.
38
39
**8 sections filled:** Employee info, Résumé, Screening Questions, Work locations, Classification, Education, Languages, Employment Equity.
40
41
**On unexpected pages:** bot saves a screenshot to `/app/data/screenshots/` for debugging.
42
43
---
44
45
## Supported Job URL Formats
46
47
| Format | Example |
48
|--------|---------|
49
| Standard posting | `page1800?poster=2424673` |
50
| Direct apply link | `page1710?careerChoiceId=2424673` |
51
| Newer PSC format | `/applicant/1156524/page1820?careerChoice=2424673` |
52
53
Career ID is extracted automatically from any of these formats.
54
55
---
56
57
## Config Files
58
59
| File | Purpose |
60
|------|---------|
61
| `/tank/appdata/gcjobs-filler/data/config.yml` | GCKey creds (`[email protected]`), IMAP config, email, monitor settings |
62
| `/tank/appdata/gcjobs-filler/data/profile.yml` | Employment info, language prefs, screening Q answers + narratives |
63
64
### Key profile.yml Settings
65
66
- `employment.classification` — must match visible text in the PSC dropdown (e.g. `"EC-05"`)
67
- `screening_questions` — map question-text substrings to `'yes'`/`'no'`; bot fuzzy-matches (≥85%) dynamically per job
68
- `screening_narratives` — free-text responses for Yes answers; same substring keys
69
70
---
71
72
## Screening Question Resolution Pipeline (`app/ai_helper.py`)
73
74
1. Fuzzy match (≥85% via `rapidfuzz`) against `profile.yml` `screening_questions` → use profiled answer
75
2. If answer is `yes` and no narrative in `screening_narratives`, fuzzy-match question against STAR CSV → AI adapts that answer as narrative
76
3. No profile match → fuzzy-match (≥70%) against STAR CSV → answer `yes` + AI-adapt STAR narrative
77
4. No STAR match → ask OpenAI (`gpt-4o-mini`) yes/no from resume.json → if yes, generate narrative via `gpt-4o`
78
79
**"Section link not found for screeningQuestions"** — normal; the bot skips jobs with no screening questions.
80
81
---
82
83
## Web UI Tabs
84
85
| Tab | Purpose |
86
|-----|---------|
87
| Apply | Submit a job URL |
88
| Monitor | Watch the bot's progress |
89
| Jobs | Browse scraped job listings |
90
| History | Past applications |
91
| Profile | Employment, languages, education, EE consent, screening Qs + narratives |
92
| STAR Answers | Searchable answer library (from gcjobs-qa) |
93
| Settings | GCKey creds, IMAP, SMTP, monitor config |
94
95
**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.
96
97
---
98
99
## Monitor Filters (Settings tab)
100
101
- `keywords` — title/classification substrings
102
- `classifications` — e.g. `EC-05`
103
- `language_requirements` — e.g. `bilingual`, `english essential`; blank = all
104
105
**Jobs table** shows extracted language requirement per posting (colour-coded: purple = bilingual, blue = English essential).
106
107
---
108
109
## Rebuild & Redeploy
110
111
```bash
112
# Build (context is parent dir — Dockerfile does COPY app/ .)
113
tar -cf - -C /tank/appdata/gcjobs-filler --exclude='./data' . | \
114
pct exec 107 -- bash -c 'rm -rf /tmp/gcjobs-build && mkdir -p /tmp/gcjobs-build && tar -xf - -C /tmp/gcjobs-build'
115
pct exec 107 -- docker build -t gcjobs-filler:latest /tmp/gcjobs-build/
116
117
# Image-only change (no compose changes):
118
pct exec 108 -- docker service update --force gcjobs-filler_gcjobs-filler
119
120
# Compose changes (volumes, ports, etc.) — must pipe file in; PCT 108 can't read /tank/appdata/gcjobs-filler/ directly:
121
cat /tank/appdata/gcjobs-filler/docker-compose.yml | \
122
pct exec 108 -- bash -c 'cat > /tmp/gcjobs-filler-compose.yml'
123
pct exec 108 -- docker stack deploy -c /tmp/gcjobs-filler-compose.yml gcjobs-filler
124
```
125
126
**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.
127
128
---
129
130
## Known Issue
131
132
`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.