Add bid tracker
This commit is contained in:
@@ -1,21 +1,23 @@
|
||||
# CLAUDE.md — Website Checker: Complete Technical Knowledge Base
|
||||
|
||||
This document captures the full architecture, design decisions, database schema, module inventory,
|
||||
feature set, and operational notes for the **Website Checker** desktop application.
|
||||
This document is the authoritative technical reference for the **Website Checker**
|
||||
desktop application. Keep it current whenever code changes are made.
|
||||
|
||||
---
|
||||
|
||||
## 1. Application Overview
|
||||
|
||||
Website Checker is a Python/Tkinter desktop application for shift-based website monitoring.
|
||||
Regular users log in, view websites assigned to their shift, open each site, and mark it checked.
|
||||
Administrators manage users, websites, shifts, credentials, and run reports.
|
||||
Website Checker is a Python/Tkinter desktop application for shift-based website
|
||||
monitoring backed by a remote MySQL database. Regular users log in, view websites
|
||||
assigned to their shift, open each site, and mark it checked. Administrators manage
|
||||
users, websites, shifts, credentials, email reports, and AI-powered document analysis.
|
||||
|
||||
- **UI:** Python `tkinter` + `ttk`
|
||||
- **Database:** Remote MySQL 5.7+ / MariaDB 10.3+ via `mysql-connector-python`
|
||||
- **Entry point:** `app.py` → `class App(tk.Tk)`
|
||||
- **Default window:** 1100×700, minimum 960×620
|
||||
- **Default theme:** Light (toggleable to Dark)
|
||||
- **Platform:** Windows desktop (primary); macOS/Linux supported with limitations
|
||||
|
||||
---
|
||||
|
||||
@@ -28,21 +30,23 @@ website_checker/
|
||||
├── models.py All database access (data layer)
|
||||
├── requirements.txt
|
||||
├── CLAUDE.md This file
|
||||
├── README.md Setup and deployment guide
|
||||
├── USER_MANUAL.md End-user documentation
|
||||
├── utils/
|
||||
│ ├── config_crypto.py Windows DPAPI encryption for config.ini credentials
|
||||
│ ├── crypto.py Fernet credential encryption (website passwords)
|
||||
│ ├── export.py CSV + Excel export
|
||||
│ ├── scheduler.py Daily email report background daemon
|
||||
│ ├── scheduler.py Daily email report background daemon + SMTP helpers
|
||||
│ └── ui_helpers.py ThemeManager, COLOURS proxy, DateEntry, widgets
|
||||
└── views/
|
||||
├── admin_dashboard_view.py Admin home — KPI cards + per-user progress table
|
||||
├── admin_log_view.py Activity log treeview
|
||||
├── admin_shifts_view.py Shift CRUD + PDF export
|
||||
├── admin_users_view.py User CRUD
|
||||
├── admin_shifts_view.py Shift CRUD + PDF export + inactive filter toggle
|
||||
├── admin_users_view.py User CRUD + secure password reset dialog
|
||||
├── admin_websites_view.py Website CRUD + visibility + credentials (collapsible)
|
||||
├── ai_summary_view.py AI document analysis via Groq API (NEW)
|
||||
├── ai_summary_view.py AI document analysis via Groq API
|
||||
├── change_password_view.py Self-service password change (all roles)
|
||||
├── email_settings_view.py SMTP / scheduled report configuration
|
||||
├── email_settings_view.py SMTP / scheduled report config (3-mode security)
|
||||
├── login_view.py Login form with rate-limiting countdown
|
||||
├── reports_view.py Shift Detail / Unchecked / Summary / Chart tabs
|
||||
├── settings_view.py DB connection settings dialog
|
||||
@@ -71,9 +75,11 @@ smtp_host=smtp.example.com
|
||||
smtp_port=587
|
||||
smtp_user=sender@example.com
|
||||
smtp_password=dpapi:<base64-blob> ; encrypted
|
||||
use_tls=true
|
||||
security=starttls ; starttls | ssl | none
|
||||
use_tls=true ; legacy compat field, derived from security
|
||||
recipients=admin@example.com
|
||||
send_time=18:00
|
||||
last_sent_date=2025-01-01 ; ISO date, prevents re-send after restart
|
||||
|
||||
[groq]
|
||||
api_key=dpapi:<base64-blob> ; encrypted
|
||||
@@ -92,6 +98,7 @@ UTF-8 log file in working directory. All INFO/WARNING/ERROR from every module.
|
||||
|
||||
All tables use InnoDB/utf8mb4. Created by `initialize_database()` on first launch.
|
||||
Safe ALTER TABLE migrations run automatically for columns added post-deployment.
|
||||
New tables (ai_criteria, ai_analysis_log) verified with information_schema check and logged.
|
||||
|
||||
### users
|
||||
| Column | Type | Notes |
|
||||
@@ -111,7 +118,7 @@ Safe ALTER TABLE migrations run automatically for columns added post-deployment.
|
||||
|---|---|---|
|
||||
| id | INT PK AUTO | |
|
||||
| name | VARCHAR(200) | |
|
||||
| url | TEXT | |
|
||||
| url | TEXT | Validated by _validate_and_normalise_url (http/https only) |
|
||||
| check_type | ENUM('daily','weekly') | weekly = once per ISO week |
|
||||
| visibility | ENUM('all','assigned') | assigned = only users in website_users table |
|
||||
| note | TEXT | Shown on user dashboard card |
|
||||
@@ -130,9 +137,10 @@ Safe ALTER TABLE migrations run automatically for columns added post-deployment.
|
||||
One row per user per site per day (upserted). `user_note` optional.
|
||||
|
||||
### activity_log
|
||||
`action` codes: LOGIN, LOGOUT, SESSION_TIMEOUT, ACCOUNT_LOCKED, CREATE/UPDATE/DELETE_USER,
|
||||
CHANGE_PASSWORD, CHANGE_PASSWORD_FAIL, RESET_PASSWORD, CREATE/UPDATE/DELETE_WEBSITE,
|
||||
ADD/REMOVE_CREDENTIAL, CREATE/UPDATE/DELETE_SHIFT, CHECK_WEBSITE, UPDATE_NOTE,
|
||||
`action` codes: LOGIN, LOGOUT, SESSION_TIMEOUT, ACCOUNT_LOCKED,
|
||||
CREATE/UPDATE/DELETE_USER, CHANGE_PASSWORD, CHANGE_PASSWORD_FAIL, RESET_PASSWORD,
|
||||
CREATE/UPDATE/DELETE_WEBSITE, ADD/REMOVE_CREDENTIAL,
|
||||
CREATE/UPDATE/DELETE_SHIFT, CHECK_WEBSITE, UPDATE_NOTE,
|
||||
EXPORT_CSV, EXPORT_EXCEL, EXPORT_SHIFT_PDF, UPDATE_EMAIL_SETTINGS,
|
||||
CREATE/UPDATE/DELETE_AI_CRITERION.
|
||||
|
||||
@@ -146,15 +154,16 @@ E.g. "23456" = Mon–Fri. Queried with `LOCATE(DAYOFWEEK(CURDATE()), days_of_wee
|
||||
### website_users (junction): (website_id, user_id) PK — for visibility='assigned'
|
||||
|
||||
### ai_criteria
|
||||
Stores evaluation criteria used by the AI to assess opportunity alignment.
|
||||
Stores evaluation criteria used by the AI to assess solicitation alignment.
|
||||
| Column | Type | Notes |
|
||||
|---|---|---|
|
||||
| id | INT PK AUTO | |
|
||||
| title | VARCHAR(200) | Short label |
|
||||
| description | TEXT | Full criterion text sent to the AI |
|
||||
| title | VARCHAR(200) | Short label shown in treeview |
|
||||
| description | TEXT | Full criterion text sent to the AI prompt |
|
||||
| is_active | TINYINT(1) | Inactive criteria excluded from AI prompt |
|
||||
| sort_order | INT | Controls display and prompt ordering |
|
||||
| created_by | INT FK users SET NULL | |
|
||||
| created_at / updated_at | DATETIME | |
|
||||
|
||||
### ai_analysis_log
|
||||
Persists every AI analysis result for history and audit purposes.
|
||||
@@ -173,16 +182,24 @@ Persists every AI analysis result for history and audit purposes.
|
||||
|
||||
## 5. Module Details
|
||||
|
||||
### app.py
|
||||
- App(tk.Tk) — root window; manages login state, shell, sidebar, session timeout
|
||||
- IDLE_TIMEOUT_MS = 1,800,000 (30 min); IDLE_WARNING_MS = 60,000 (1 min warning)
|
||||
- _reset_idle_timer throttled: Motion events fire at most once per _IDLE_THROTTLE_S=5.0 seconds
|
||||
- _idle_last_reset: monotonic timestamp used for throttle check
|
||||
- Admin login → scheduler.start(); logout → scheduler.stop()
|
||||
- Theme toggle destroys and rebuilds the entire shell (content panels re-instantiated)
|
||||
|
||||
### config.py
|
||||
- DB_CONFIG loaded from config.ini at import; placeholders if file missing
|
||||
- get_connection() → pool (pool_size=5)
|
||||
- initialize_database() → idempotent DDL + ALTER TABLE migrations
|
||||
- initialize_database() → idempotent DDL + ALTER TABLE migrations + table confirmation logs
|
||||
- load_config() / save_config() → decrypt/encrypt DPAPI fields transparently
|
||||
- migrate_plaintext_config() → one-time migration; encrypts any plain-text creds on startup
|
||||
- config_exists() → True if host, database, user are set
|
||||
- reload_db_config() → re-reads config.ini, resets pool
|
||||
|
||||
### utils/config_crypto.py *(NEW)*
|
||||
### utils/config_crypto.py
|
||||
Windows DPAPI-based encryption for config.ini sensitive values.
|
||||
- encrypt_value(plaintext) → "dpapi:<base64>" string
|
||||
- decrypt_value(stored) → plaintext; plain-text pass-through for legacy values
|
||||
@@ -201,6 +218,14 @@ Password helpers:
|
||||
Rate-limit constants: MAX_FAILED_ATTEMPTS=5, LOCKOUT_MINUTES=15
|
||||
Password strength: PW_MIN_LENGTH=8, requires upper + digit + special char
|
||||
|
||||
Key functions:
|
||||
- get_today_checks(user_id) — shift-aware; respects visibility and check_type
|
||||
- get_unchecked_report(target_date, user_id) — shift-scoped; only sites user was expected to check
|
||||
- get_summary_report(date_from, date_to) — shift-scoped total_sites per user per day
|
||||
- get_all_criteria() / get_active_criteria() — no LEFT JOIN; simple SELECT from ai_criteria
|
||||
- save_ai_analysis() / get_ai_analysis_history() / get_ai_analysis_detail()
|
||||
- delete_user() — guards: self-delete prevention + last-admin prevention
|
||||
|
||||
### utils/crypto.py
|
||||
- PBKDF2-HMAC-SHA256, 100,000 iterations, 32-byte salt from config.ini [crypto]
|
||||
- encrypt(plaintext) → "enc:" + base64(Fernet token)
|
||||
@@ -218,73 +243,97 @@ Password strength: PW_MIN_LENGTH=8, requires upper + digit + special char
|
||||
### utils/scheduler.py
|
||||
- Daemon thread; polls every 60 seconds
|
||||
- Sends HTML email once per day when now >= send_time
|
||||
- smtp_password is DPAPI-encrypted on save (encrypt_value) / decrypted on load (decrypt_value)
|
||||
- last_sent_date in-memory (resets on restart)
|
||||
- start() / stop() called from app.py on login/logout (admin only)
|
||||
- smtp_password DPAPI-encrypted on save / decrypted on load
|
||||
- last_sent_date persisted to config.ini [email] last_sent_date — survives restarts
|
||||
- start() / stop() called from app.py on admin login/logout
|
||||
- start() guards against double-start: joins existing thread before spawning new one
|
||||
- _make_smtp_server(host, port, security) — centralised connection with explicit ehlo() calls
|
||||
(required on Windows for reliable STARTTLS/SSL handshake)
|
||||
- security field: "starttls" (port 587) | "ssl" (port 465) | "none" (port 25)
|
||||
- test_smtp_connection() — 4-step diagnostic: DNS → TCP → TLS → Auth
|
||||
- send_test_email() — sends real HTML test message through full pipeline
|
||||
- Email report excludes users with total_sites=0 (no shift today); adds footnote count
|
||||
|
||||
### views/email_settings_view.py
|
||||
- Security mode: 3 radio buttons (STARTTLS/SSL·TLS/None) replacing old Use STARTTLS checkbox
|
||||
- Selecting a security mode auto-fills the default port
|
||||
- Loads legacy use_tls bool from config.ini and maps to security field on first load
|
||||
- 🔌 Test Connection — step-by-step diagnostic output in status label
|
||||
- 📧 Send Test Email — real email send; all buttons disabled during operation
|
||||
- Save preserves last_sent_date in config.ini
|
||||
|
||||
### views/ai_summary_view.py
|
||||
AI-powered document analysis panel. Accessible from the sidebar for both admin and user roles.
|
||||
AI-powered document analysis panel. Accessible from sidebar for both roles.
|
||||
|
||||
Key internals:
|
||||
- Supported file types: .txt, .md, .csv, .pdf, .docx, .doc, .xlsx, .xls
|
||||
- Groq API key and model selection are **visible only to admins** (user role sees neither)
|
||||
- Regular users use the stored API key transparently
|
||||
- Groq API key and model selection visible only to admins
|
||||
- Regular users use stored API key transparently
|
||||
- Background thread for AI calls (prevents UI freeze)
|
||||
- Extraction prompt focused on procurement/solicitation fields:
|
||||
Solicitation Number/Type, Set-Aside, Description, Work Site, Pre-Proposal Conference,
|
||||
POC, Square Footage, Driving Distance from office (2815 Hartland Rd, Falls Church VA),
|
||||
Last Day for Questions, Due Date
|
||||
- Overall summary covers: Scope of Work, Contract Period, Proposal Submission Requirements,
|
||||
Key Deadlines & Action Items
|
||||
- Groq API key stored DPAPI-encrypted in config.ini [groq] section
|
||||
- Notebook layout: ✨ Analyze tab + 🕑 History tab
|
||||
- Extraction prompt: 12 labeled fields per document + 4-section Overall Summary
|
||||
- Driving distance calculated from 2815 Hartland Rd, Falls Church VA (AI estimate)
|
||||
- max_tokens=4096; temperature=0.2; MAX_CHARS_PER_FILE=14,000
|
||||
- **Evaluation Criteria panel** (collapsible): admin can add/edit/delete criteria;
|
||||
users see read-only list. Active criteria are appended to the AI prompt as an
|
||||
alignment evaluation section. The AI outputs RECOMMENDATION: PURSUE/PASS/UNCLEAR
|
||||
which is parsed and displayed as a colour-coded verdict banner.
|
||||
- **History tab**: every analysis is saved to ai_analysis_log (with verdict + criteria
|
||||
snapshot). History treeview is filterable by verdict. Double-click restores full
|
||||
output in the Analyze tab. Admins see all users; regular users see only their own.
|
||||
- CriterionDialog has a live character counter with 500-char soft limit guidance.
|
||||
- URL open and health-check probe both guarded by _validate_and_normalise_url.
|
||||
|
||||
.doc reading (legacy binary Word format) — 3-tier fallback:
|
||||
Evaluation Criteria panel (collapsible):
|
||||
- Admin: Add/Edit/Delete controls; CriterionDialog with live character counter (soft limit 500)
|
||||
- Users: read-only treeview
|
||||
- Active criteria appended to AI prompt; AI outputs RECOMMENDATION: PURSUE/PASS/UNCLEAR
|
||||
- Verdict parsed by _parse_verdict() and shown as colour-coded banner (green/red/amber)
|
||||
|
||||
History tab:
|
||||
- Every analysis saved to ai_analysis_log with verdict + criteria snapshot
|
||||
- save_ai_analysis() called in _on_success() — never blocks the result display
|
||||
- Filterable by verdict radio buttons (All/PURSUE/PASS/UNCLEAR/none)
|
||||
- Double-click or View Result restores full output in Analyze tab
|
||||
- Admins see all users' history; regular users see only their own
|
||||
- Auto-refreshes on tab switch via <<NotebookTabChanged>>
|
||||
|
||||
.doc reading (legacy binary Word) — 3-tier fallback:
|
||||
1. win32com (Word COM automation — requires MS Word installed)
|
||||
2. docx2txt (pure Python)
|
||||
3. Raw ASCII scrape from binary
|
||||
|
||||
URL safety: _validate_and_normalise_url() rejects non-http/https schemes.
|
||||
|
||||
### views/admin_websites_view.py
|
||||
- Website CRUD dialog now has a **collapsible credentials section** (hidden by default)
|
||||
- "🔑 Show Credentials" / "🔒 Hide Credentials" toggle button
|
||||
- Auto-expands when editing a site that already has saved credentials
|
||||
- "+ Add Credential" auto-expands the section if collapsed
|
||||
- **Bug fixed:** `_on_visibility_change` previously used `before=self.creds_container`
|
||||
which crosses a widget parent boundary causing a TclError; corrected to `before=self._cred_body`
|
||||
- Website CRUD dialog with collapsible credentials section (hidden by default)
|
||||
- _on_visibility_change uses before=self._cred_body (not creds_container — same parent)
|
||||
- _validate_and_normalise_url() called in _save() before DB write
|
||||
- URL validation: prepends https:// if no scheme; rejects javascript:/data:/file: etc.
|
||||
|
||||
### views/admin_users_view.py
|
||||
- 🔑 Reset Password button generates 16-char cryptographically random password
|
||||
- _PasswordResetDialog: masked field, 👁 reveal, 📋 copy (2s flash), 120s auto-close,
|
||||
30s clipboard wipe, countdown timer
|
||||
- Logs RESET_PASSWORD to activity_log
|
||||
|
||||
### views/admin_shifts_view.py
|
||||
- "Show inactive" checkbox toggles visibility of inactive shifts in treeview
|
||||
- _load_shifts filters by _show_inactive BooleanVar; no extra DB call
|
||||
- _export_pdf(): reportlab A4; one section per active shift
|
||||
|
||||
### views/user_dashboard_view.py
|
||||
Key internals:
|
||||
- _health_cache: {website_id: ("ok"|"slow"|"down", ms)} — daemon threads, HEAD request, 6s timeout
|
||||
- Dot colours: green (<=3000ms), amber (>3000ms), red (error)
|
||||
- Mousewheel: Enter/Leave scoped; unbind_all in destroy()
|
||||
- Keyboard shortcuts: self.bind() stored in _shortcut_ids; unbound in destroy()
|
||||
- Notifications: after(60_000) loop; plyer first, fallback to borderless Toplevel toast
|
||||
- **Credentials popup no longer opens automatically on link click**
|
||||
- "🔑 Credentials" button appears on each site card only if the site has saved credentials
|
||||
- CredentialsPopup: 📋 copy button for username (2s flash); 📋 copy button for password
|
||||
(2s flash + clipboard auto-cleared after 15s for security); 👁 toggle to reveal password
|
||||
- _health_cache: {website_id: ("ok"|"slow"|"restricted"|"down", ms)}
|
||||
- ok: 2xx/3xx ≤3000ms; slow: 2xx/3xx >3000ms; restricted: 4xx; down: 5xx/network error
|
||||
- Notifications: single consolidated reminder per shift end-time group (not per site)
|
||||
- Groups unchecked sites by end_time; fires one notification listing all sites
|
||||
- _notified_sites key: (frozenset(names), end_t)
|
||||
- Keyboard shortcuts: stored in _shortcut_ids; unbound in destroy()
|
||||
- Credentials popup: 📋 copy (username 2s flash; password 2s flash + 15s clipboard wipe)
|
||||
|
||||
### views/reports_view.py
|
||||
- Tabs: Shift Detail / Unchecked / Summary / Chart
|
||||
- All date fields use DateEntry (no manual text entry)
|
||||
- Summary report: shift-scoped total_sites per user per day (not global count)
|
||||
- Unchecked report: shift-scoped; only sites user was expected to check that day
|
||||
- Chart: matplotlib Agg + FigureCanvasTkAgg; bars green≥100% / amber≥50% / red<50%
|
||||
|
||||
### views/login_view.py
|
||||
- check_login_allowed(username) called before authenticate()
|
||||
- Locked: form disabled; 1-second countdown; re-enables when timer reaches 0
|
||||
- Not-yet-locked failed attempt: shows "N attempt(s) remaining before lockout"
|
||||
|
||||
### views/admin_shifts_view.py
|
||||
- _export_pdf(): reportlab A4 document; one section per active shift; user+website tables
|
||||
|
||||
### views/reports_view.py
|
||||
- All date fields use DateEntry (no manual text entry)
|
||||
- Chart: matplotlib Agg + FigureCanvasTkAgg; bars green>=100% / amber>=50% / red<50%
|
||||
|
||||
---
|
||||
|
||||
## 6. Authentication & Security
|
||||
@@ -296,10 +345,14 @@ Key internals:
|
||||
| Config credential protection | Windows DPAPI (CryptProtectData) — user-account-scoped |
|
||||
| Encrypted fields | DB user, DB password, SMTP password, Groq API key |
|
||||
| Login rate limiting | 5 attempts → 15-min lockout in DB |
|
||||
| Session timeout | 30-min idle; 1-min warning; any mouse/key event resets |
|
||||
| Session timeout | 30-min idle; 1-min warning; Motion events throttled to 1/5s |
|
||||
| Password policy | 8+ chars, uppercase, digit, special character |
|
||||
| Self-service change | Requires current password; strength meter; same-as-current guard |
|
||||
| Clipboard security | Password copy auto-clears clipboard after 15 seconds |
|
||||
| URL safety | _validate_and_normalise_url rejects non-http/https schemes |
|
||||
| Admin self-delete | Blocked in delete_user() |
|
||||
| Last-admin deletion | Blocked in delete_user() |
|
||||
| Password reset display | _PasswordResetDialog: 120s auto-close, 30s clipboard wipe |
|
||||
|
||||
### DPAPI Migration
|
||||
`migrate_plaintext_config()` is called automatically on every startup (in `_init_db`).
|
||||
@@ -316,13 +369,21 @@ On first run after this feature was added, it encrypts any existing plain-text v
|
||||
|
||||
### visibility
|
||||
- all: every user in shift sees it
|
||||
- assigned: only users in website_users table see it
|
||||
- assigned: only users in website_users table see it (enforced in both shift and legacy paths)
|
||||
|
||||
### get_today_checks resolution
|
||||
1. Check if user has active shifts today (DAYOFWEEK match)
|
||||
2. If yes: union of shift websites filtered by check_type + visibility
|
||||
2. If yes: shift websites filtered by check_type + visibility (website_users respected)
|
||||
3. If no: all active websites filtered by visibility + check_type (legacy fallback)
|
||||
|
||||
### Unchecked report scope
|
||||
Uses shift membership to determine "expected" sites per user per day.
|
||||
CROSS JOIN replaced with shift-aware subquery — no false positives for off-shift users.
|
||||
|
||||
### Summary report total_sites
|
||||
Correlated subquery counts shift-assigned sites per user per check_date day-of-week.
|
||||
A user in a 3-site shift shows 3 as total_sites, not the global count.
|
||||
|
||||
### Soft deletes
|
||||
Websites and Shifts: is_active=0. Users: hard-delete (admin can deactivate first).
|
||||
|
||||
@@ -336,6 +397,7 @@ Websites and Shifts: is_active=0. Users: hard-delete (admin can deactivate first
|
||||
| app.py | IDLE_WARNING_MS | 60,000 (1 min) |
|
||||
| app.py | APP_VERSION | "1.0.0" |
|
||||
| app.py | VERSION_CHECK_ENABLED | False |
|
||||
| app.py | App._IDLE_THROTTLE_S | 5.0 (seconds between Motion-event timer resets) |
|
||||
| models.py | MAX_FAILED_ATTEMPTS | 5 |
|
||||
| models.py | LOCKOUT_MINUTES | 15 |
|
||||
| models.py | PW_MIN_LENGTH | 8 |
|
||||
@@ -345,9 +407,9 @@ Websites and Shifts: is_active=0. Users: hard-delete (admin can deactivate first
|
||||
| ai_summary_view.py | MAX_CHARS_PER_FILE | 14,000 |
|
||||
| ai_summary_view.py | _OFFICE_ADDRESS | "2815 Hartland Road, Falls Church, VA 22043, USA" |
|
||||
| ai_summary_view.py | CriterionDialog._DESC_SOFT_LIMIT | 500 (chars, soft guidance only) |
|
||||
| app.py | App._IDLE_THROTTLE_S | 5.0 (seconds between Motion-event timer resets) |
|
||||
| admin_users_view.py | _PasswordResetDialog._AUTO_CLOSE_S | 120 (seconds before auto-close) |
|
||||
| admin_users_view.py | _PasswordResetDialog._CLIP_CLEAR_S | 30 (seconds before clipboard wipe) |
|
||||
| utils/scheduler.py | (timeout in _make_smtp_server) | 15 seconds |
|
||||
|
||||
---
|
||||
|
||||
@@ -370,9 +432,8 @@ Websites and Shifts: is_active=0. Users: hard-delete (admin can deactivate first
|
||||
|
||||
6. Weekly site logic uses YEARWEEK(..., 1) (ISO week, Monday start).
|
||||
|
||||
7. The email scheduler `last_sent_date` is now persisted to `config.ini`
|
||||
`[email] last_sent_date` (ISO format). A restart after the configured
|
||||
`send_time` will not re-send the report on the same day.
|
||||
7. The email scheduler last_sent_date is persisted to config.ini [email] last_sent_date
|
||||
(ISO format). A restart after the configured send_time will not re-send that day.
|
||||
|
||||
8. All log messages use ASCII only (hyphens not em-dashes/arrows) to prevent
|
||||
cp1252 UnicodeEncodeError on Windows consoles.
|
||||
@@ -381,8 +442,19 @@ Websites and Shifts: is_active=0. Users: hard-delete (admin can deactivate first
|
||||
If config.ini is copied to a different machine or user account, credentials
|
||||
cannot be decrypted. Users must re-enter credentials via the Settings dialog.
|
||||
|
||||
10. The AI summary view fetches credentials from the DB at card render time (not lazily).
|
||||
Avoid having hundreds of sites with credentials as this adds DB round-trips per render.
|
||||
10. Theme toggle destroys and rebuilds the entire shell. Any unsaved in-memory
|
||||
state in the active panel is lost. AI summary output is not preserved across
|
||||
theme toggles (it is in ai_analysis_log; retrieve from History tab).
|
||||
|
||||
11. _make_smtp_server calls ehlo() explicitly before and after starttls() — this is
|
||||
required on Windows. Removing these calls breaks SMTP on many servers.
|
||||
|
||||
12. The security field in config.ini supersedes use_tls. On existing deployments
|
||||
without a security field, load_email_config() derives it from use_tls for
|
||||
backward compatibility.
|
||||
|
||||
13. get_all_criteria() does NOT join users table. The creator column is excluded
|
||||
intentionally — the treeview doesn't show it and the join is wasted I/O.
|
||||
|
||||
---
|
||||
|
||||
@@ -403,8 +475,8 @@ pywin32>=306
|
||||
docx2txt>=0.8
|
||||
```
|
||||
|
||||
stdlib used: tkinter, csv, smtplib, urllib.request, configparser, threading, calendar,
|
||||
datetime, base64, re, tempfile
|
||||
stdlib used: tkinter, csv, smtplib, urllib.request, urllib.parse, configparser,
|
||||
threading, calendar, datetime, base64, re, tempfile, socket
|
||||
|
||||
---
|
||||
|
||||
@@ -429,7 +501,28 @@ My Shift · 🤖 AI Summary · Change Password · Sign Out
|
||||
|
||||
---
|
||||
|
||||
## 13. Deployment Notes
|
||||
## 13. SMTP Configuration Reference
|
||||
|
||||
| Security Mode | Protocol | Default Port | Use When |
|
||||
|---|---|---|---|
|
||||
| STARTTLS | Plain → TLS upgrade | 587 | Office 365, Exchange, most corporate |
|
||||
| SSL/TLS | TLS from first byte | 465 | Gmail direct, some providers |
|
||||
| None | Unencrypted | 25 | Internal relay servers only |
|
||||
|
||||
Connection flow in _make_smtp_server:
|
||||
- STARTTLS: SMTP() → ehlo() → starttls() → ehlo()
|
||||
- SSL: SMTP_SSL() → ehlo()
|
||||
- None: SMTP() → ehlo()
|
||||
|
||||
test_smtp_connection() diagnostic steps:
|
||||
1. DNS — getaddrinfo(); fails = hostname wrong or no network
|
||||
2. TCP — create_connection(); fails = firewall blocking the port
|
||||
3. TLS — _make_smtp_server(); fails = wrong security mode or port
|
||||
4. Auth — server.login(); fails = wrong username/password or App Password needed
|
||||
|
||||
---
|
||||
|
||||
## 14. Deployment Notes
|
||||
|
||||
- Python 3.9+ required. 3.12 tested on Windows.
|
||||
- tkinter bundled on Windows/macOS; Linux: apt install python3-tk
|
||||
@@ -439,30 +532,41 @@ My Shift · 🤖 AI Summary · Change Password · Sign Out
|
||||
- pywin32 required for DPAPI encryption (Windows only). Install: pip install pywin32
|
||||
- Microsoft Word recommended for .doc file support in AI Summary (falls back to docx2txt)
|
||||
- Groq API key required for AI Summary feature — free at https://console.groq.com
|
||||
- Gmail users: enable 2FA, then create an App Password at myaccount.google.com/apppasswords
|
||||
- config.ini is machine- and user-account-specific. Do not share or copy between machines.
|
||||
|
||||
---
|
||||
|
||||
## 14. AI Summary Feature — Setup & Prompt Details
|
||||
## 15. AI Summary Feature — Setup & Prompt Details
|
||||
|
||||
### Setup (Admin)
|
||||
### Setup (Admin only)
|
||||
1. Obtain a free Groq API key at https://console.groq.com
|
||||
2. Navigate to **🤖 AI Summary** in the sidebar
|
||||
2. Navigate to 🤖 AI Summary in the sidebar
|
||||
3. Enter the API key and select a model
|
||||
4. Click **💾 Save Settings** — key is stored DPAPI-encrypted in config.ini
|
||||
4. Click 💾 Save Settings — key stored DPAPI-encrypted in config.ini [groq]
|
||||
|
||||
### Supported Models
|
||||
- llama-3.3-70b-versatile (default, recommended)
|
||||
- llama-3.1-8b-instant
|
||||
- llama-3.1-8b-instant (faster, smaller)
|
||||
- gemma2-9b-it
|
||||
- mixtral-8x7b-32768
|
||||
|
||||
### Prompt Strategy
|
||||
The prompt instructs the AI to extract 12 labeled fields per document:
|
||||
Solicitation Number, Type, Set-Aside, Description/Scope, Work Site, Pre-Proposal Conference,
|
||||
POC, Square Footage, **Driving Distance from 2815 Hartland Rd Falls Church VA** (calculated by AI),
|
||||
Extraction: 12 labeled fields per document:
|
||||
Solicitation Number, Type, Set-Aside, Description/Scope, Work Site,
|
||||
Pre-Proposal Conference, POC, Square Footage,
|
||||
Driving Distance from 2815 Hartland Rd Falls Church VA (AI estimate),
|
||||
Last Day for Questions, Due Date, Other requirements.
|
||||
|
||||
Then produces an Overall Summary with 4 sections:
|
||||
Overall Summary: 4 sections:
|
||||
A. Scope of Work · B. Contract Period · C. Proposal Submission Requirements · D. Key Deadlines
|
||||
|
||||
The driving distance/time is an AI estimate using major highways. Actual times may vary with traffic.
|
||||
Alignment evaluation (when criteria active):
|
||||
- Each criterion assessed as MEETS / DOES NOT MEET / PARTIALLY MEETS
|
||||
- RECOMMENDATION: PURSUE | PASS | UNCLEAR on its own line (machine-read by _parse_verdict)
|
||||
- Executive Summary: 2-3 sentences
|
||||
|
||||
### History & Audit
|
||||
- Every analysis is auto-saved to ai_analysis_log (verdict + criteria snapshot at time of run)
|
||||
- update_criterion() logs full description text in activity_log.detail for audit trail
|
||||
- Admins see all users' analyses; users see only their own
|
||||
Reference in New Issue
Block a user