feat(demo): add committed seed SQL and startup loader
This commit is contained in:
parent
854b18ecbd
commit
29ba40edcb
3 changed files with 327 additions and 1 deletions
54
demo/seed.sql
Normal file
54
demo/seed.sql
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
-- jobs
|
||||||
|
INSERT INTO jobs (title, company, url, source, location, is_remote, salary, match_score, status, date_found, date_posted, cover_letter, applied_at, phone_screen_at, interviewing_at, offer_at, hired_at, interview_date, rejection_stage, hired_feedback) VALUES ('UX Designer', 'Spotify', 'https://www.linkedin.com/jobs/view/1000001', 'linkedin', 'Remote', '1', '$110k–$140k', '94.0', 'approved', '2026-04-14', '2026-04-12', 'Dear Hiring Manager,
|
||||||
|
|
||||||
|
I''m excited to apply for the UX Designer role at Spotify. With five years of
|
||||||
|
experience designing for music discovery and cross-platform experiences, I''ve
|
||||||
|
consistently shipped features that make complex audio content feel effortless to
|
||||||
|
navigate. At my last role I led a redesign of the playlist creation flow that
|
||||||
|
reduced drop-off by 31%.
|
||||||
|
|
||||||
|
Spotify''s commitment to artist and listener discovery — and its recent push into
|
||||||
|
audiobooks and podcast tooling — aligns directly with the kind of cross-format
|
||||||
|
design challenges I''m most energised by.
|
||||||
|
|
||||||
|
I''d love to bring that focus to your product design team.
|
||||||
|
|
||||||
|
Warm regards,
|
||||||
|
[Your name]
|
||||||
|
', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO jobs (title, company, url, source, location, is_remote, salary, match_score, status, date_found, date_posted, cover_letter, applied_at, phone_screen_at, interviewing_at, offer_at, hired_at, interview_date, rejection_stage, hired_feedback) VALUES ('Product Designer', 'Duolingo', 'https://www.linkedin.com/jobs/view/1000002', 'linkedin', 'Pittsburgh, PA', '0', '$95k–$120k', '87.0', 'approved', '2026-04-13', '2026-04-10', 'Draft in progress — cover letter generating…', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO jobs (title, company, url, source, location, is_remote, salary, match_score, status, date_found, date_posted, cover_letter, applied_at, phone_screen_at, interviewing_at, offer_at, hired_at, interview_date, rejection_stage, hired_feedback) VALUES ('UX Lead', 'NPR', 'https://www.indeed.com/viewjob?jk=1000003', 'indeed', 'Washington, DC', '1', '$120k–$150k', '81.0', 'approved', '2026-04-12', '2026-04-08', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO jobs (title, company, url, source, location, is_remote, salary, match_score, status, date_found, date_posted, cover_letter, applied_at, phone_screen_at, interviewing_at, offer_at, hired_at, interview_date, rejection_stage, hired_feedback) VALUES ('Senior UX Designer', 'Mozilla', 'https://www.linkedin.com/jobs/view/1000004', 'linkedin', 'Remote', '1', '$105k–$130k', '81.0', 'pending', '2026-04-13', '2026-03-12', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO jobs (title, company, url, source, location, is_remote, salary, match_score, status, date_found, date_posted, cover_letter, applied_at, phone_screen_at, interviewing_at, offer_at, hired_at, interview_date, rejection_stage, hired_feedback) VALUES ('Interaction Designer', 'Figma', 'https://www.indeed.com/viewjob?jk=1000005', 'indeed', 'San Francisco, CA', '1', '$115k–$145k', '78.0', 'pending', '2026-04-11', '2026-04-09', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO jobs (title, company, url, source, location, is_remote, salary, match_score, status, date_found, date_posted, cover_letter, applied_at, phone_screen_at, interviewing_at, offer_at, hired_at, interview_date, rejection_stage, hired_feedback) VALUES ('Product Designer II', 'Notion', 'https://www.linkedin.com/jobs/view/1000006', 'linkedin', 'Remote', '1', '$100k–$130k', '76.0', 'pending', '2026-04-10', '2026-04-07', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO jobs (title, company, url, source, location, is_remote, salary, match_score, status, date_found, date_posted, cover_letter, applied_at, phone_screen_at, interviewing_at, offer_at, hired_at, interview_date, rejection_stage, hired_feedback) VALUES ('UX Designer', 'Stripe', 'https://www.linkedin.com/jobs/view/1000007', 'linkedin', 'Remote', '1', '$120k–$150k', '74.0', 'pending', '2026-04-09', '2026-04-06', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO jobs (title, company, url, source, location, is_remote, salary, match_score, status, date_found, date_posted, cover_letter, applied_at, phone_screen_at, interviewing_at, offer_at, hired_at, interview_date, rejection_stage, hired_feedback) VALUES ('UI/UX Designer', 'Canva', 'https://www.indeed.com/viewjob?jk=1000008', 'indeed', 'Remote', '1', '$90k–$115k', '72.0', 'pending', '2026-04-08', '2026-04-05', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO jobs (title, company, url, source, location, is_remote, salary, match_score, status, date_found, date_posted, cover_letter, applied_at, phone_screen_at, interviewing_at, offer_at, hired_at, interview_date, rejection_stage, hired_feedback) VALUES ('Senior Product Designer', 'Asana', 'https://www.linkedin.com/jobs/view/1000009', 'linkedin', 'San Francisco, CA', '1', '$125k–$155k', '69.0', 'pending', '2026-04-07', '2026-04-04', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO jobs (title, company, url, source, location, is_remote, salary, match_score, status, date_found, date_posted, cover_letter, applied_at, phone_screen_at, interviewing_at, offer_at, hired_at, interview_date, rejection_stage, hired_feedback) VALUES ('UX Researcher', 'Intercom', 'https://www.indeed.com/viewjob?jk=1000010', 'indeed', 'Remote', '1', '$95k–$120k', '67.0', 'pending', '2026-04-06', '2026-04-03', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO jobs (title, company, url, source, location, is_remote, salary, match_score, status, date_found, date_posted, cover_letter, applied_at, phone_screen_at, interviewing_at, offer_at, hired_at, interview_date, rejection_stage, hired_feedback) VALUES ('Product Designer', 'Linear', 'https://www.linkedin.com/jobs/view/1000011', 'linkedin', 'Remote', '1', '$110k–$135k', '65.0', 'pending', '2026-04-05', '2026-04-02', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO jobs (title, company, url, source, location, is_remote, salary, match_score, status, date_found, date_posted, cover_letter, applied_at, phone_screen_at, interviewing_at, offer_at, hired_at, interview_date, rejection_stage, hired_feedback) VALUES ('UX Designer', 'Loom', 'https://www.indeed.com/viewjob?jk=1000012', 'indeed', 'Remote', '1', '$90k–$110k', '62.0', 'pending', '2026-04-04', '2026-04-01', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO jobs (title, company, url, source, location, is_remote, salary, match_score, status, date_found, date_posted, cover_letter, applied_at, phone_screen_at, interviewing_at, offer_at, hired_at, interview_date, rejection_stage, hired_feedback) VALUES ('Senior Product Designer', 'Asana', 'https://www.asana.com/jobs/1000013', 'linkedin', 'San Francisco, CA', '1', '$125k–$155k', '91.0', 'phone_screen', '2026-04-01', '2026-03-30', NULL, '2026-04-08', '2026-04-15', NULL, NULL, NULL, '2026-04-15T14:00:00', NULL, NULL);
|
||||||
|
INSERT INTO jobs (title, company, url, source, location, is_remote, salary, match_score, status, date_found, date_posted, cover_letter, applied_at, phone_screen_at, interviewing_at, offer_at, hired_at, interview_date, rejection_stage, hired_feedback) VALUES ('Product Designer', 'Notion', 'https://www.notion.so/jobs/1000014', 'indeed', 'Remote', '1', '$100k–$130k', '88.0', 'interviewing', '2026-03-25', '2026-03-23', NULL, '2026-04-01', '2026-04-05', '2026-04-12', NULL, NULL, '2026-04-22T10:00:00', NULL, NULL);
|
||||||
|
INSERT INTO jobs (title, company, url, source, location, is_remote, salary, match_score, status, date_found, date_posted, cover_letter, applied_at, phone_screen_at, interviewing_at, offer_at, hired_at, interview_date, rejection_stage, hired_feedback) VALUES ('Design Systems Designer', 'Figma', 'https://www.figma.com/jobs/1000015', 'linkedin', 'San Francisco, CA', '1', '$130k–$160k', '96.0', 'hired', '2026-03-01', '2026-02-27', NULL, '2026-03-08', '2026-03-14', '2026-03-21', '2026-04-01', '2026-04-08', NULL, NULL, '{"factors":["clear_scope","great_manager","mission_aligned"],"notes":"Excited about design systems work. Salary met expectations."}');
|
||||||
|
INSERT INTO jobs (title, company, url, source, location, is_remote, salary, match_score, status, date_found, date_posted, cover_letter, applied_at, phone_screen_at, interviewing_at, offer_at, hired_at, interview_date, rejection_stage, hired_feedback) VALUES ('UX Designer', 'Slack', 'https://slack.com/jobs/1000016', 'indeed', 'Remote', '1', '$115k–$140k', '79.0', 'applied', '2026-03-18', '2026-03-16', NULL, '2026-03-28', NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
-- job_contacts
|
||||||
|
INSERT INTO job_contacts (job_id, direction, subject, from_addr, to_addr, received_at, stage_signal) VALUES (1, 'inbound', 'Excited to connect — UX Designer role at Spotify', 'jamie.chen@spotify.com', 'you@example.com', '2026-04-12', 'positive_response');
|
||||||
|
INSERT INTO job_contacts (job_id, direction, subject, from_addr, to_addr, received_at, stage_signal) VALUES (1, 'outbound', 'Re: Excited to connect — UX Designer role at Spotify', 'you@example.com', 'jamie.chen@spotify.com', '2026-04-13', NULL);
|
||||||
|
INSERT INTO job_contacts (job_id, direction, subject, from_addr, to_addr, received_at, stage_signal) VALUES (13, 'inbound', 'Interview Confirmation — Senior Product Designer', 'recruiting@asana.com', 'you@example.com', '2026-04-13', 'interview_scheduled');
|
||||||
|
INSERT INTO job_contacts (job_id, direction, subject, from_addr, to_addr, received_at, stage_signal) VALUES (14, 'inbound', 'Your panel interview is confirmed for Apr 22', 'recruiting@notion.so', 'you@example.com', '2026-04-12', 'interview_scheduled');
|
||||||
|
INSERT INTO job_contacts (job_id, direction, subject, from_addr, to_addr, received_at, stage_signal) VALUES (14, 'inbound', 'Pre-interview prep resources', 'marcus.webb@notion.so', 'you@example.com', '2026-04-13', 'positive_response');
|
||||||
|
INSERT INTO job_contacts (job_id, direction, subject, from_addr, to_addr, received_at, stage_signal) VALUES (15, 'inbound', 'Figma Design Systems — Offer Letter', 'offers@figma.com', 'you@example.com', '2026-04-01', 'offer_received');
|
||||||
|
INSERT INTO job_contacts (job_id, direction, subject, from_addr, to_addr, received_at, stage_signal) VALUES (15, 'outbound', 'Re: Figma Design Systems — Offer Letter (acceptance)', 'you@example.com', 'offers@figma.com', '2026-04-05', NULL);
|
||||||
|
INSERT INTO job_contacts (job_id, direction, subject, from_addr, to_addr, received_at, stage_signal) VALUES (15, 'inbound', 'Welcome to Figma! Onboarding next steps', 'onboarding@figma.com', 'you@example.com', '2026-04-08', NULL);
|
||||||
|
INSERT INTO job_contacts (job_id, direction, subject, from_addr, to_addr, received_at, stage_signal) VALUES (16, 'inbound', 'Thanks for applying to Slack', 'noreply@slack.com', 'you@example.com', '2026-03-28', NULL);
|
||||||
|
|
||||||
|
-- references_
|
||||||
|
INSERT INTO references_ (name, email, role, company, relationship, notes, tags, prep_email) VALUES ('Dr. Priya Nair', 'priya.nair@example.com', 'Director of Design', 'Acme Corp', 'former_manager', 'Managed me for 3 years on the consumer app redesign. Enthusiastic reference.', '["manager","design"]', 'Hi Priya,
|
||||||
|
|
||||||
|
I hope you''re doing well! I''m currently interviewing for a few senior UX roles and would be so grateful if you''d be willing to serve as a reference.
|
||||||
|
|
||||||
|
Thank you!
|
||||||
|
[Your name]');
|
||||||
|
INSERT INTO references_ (name, email, role, company, relationship, notes, tags, prep_email) VALUES ('Sam Torres', 'sam.torres@example.com', 'Senior Product Designer', 'Acme Corp', 'former_colleague', 'Worked together on design systems. Great at speaking to collaborative process.', '["colleague","design_systems"]', NULL);
|
||||||
|
INSERT INTO references_ (name, email, role, company, relationship, notes, tags, prep_email) VALUES ('Jordan Kim', 'jordan.kim@example.com', 'VP of Product', 'Streamline Inc', 'former_manager', 'Led the product team I was embedded in. Can speak to business impact of design work.', '["manager","product"]', NULL);
|
||||||
20
dev-api.py
20
dev-api.py
|
|
@ -51,14 +51,32 @@ IS_DEMO: bool = os.environ.get("DEMO_MODE", "").lower() in ("1", "true", "yes")
|
||||||
# Per-request DB path — set by cloud_session_middleware; falls back to DB_PATH
|
# Per-request DB path — set by cloud_session_middleware; falls back to DB_PATH
|
||||||
_request_db: ContextVar[str | None] = ContextVar("_request_db", default=None)
|
_request_db: ContextVar[str | None] = ContextVar("_request_db", default=None)
|
||||||
|
|
||||||
|
def _load_demo_seed(db_path: str, seed_file: str) -> None:
|
||||||
|
"""Load seed SQL into the demo DB if it is empty (no jobs rows yet)."""
|
||||||
|
import sqlite3 as _sqlite3
|
||||||
|
seed_path = Path(seed_file)
|
||||||
|
if not seed_path.exists():
|
||||||
|
return
|
||||||
|
con = _sqlite3.connect(db_path)
|
||||||
|
try:
|
||||||
|
count = con.execute("SELECT COUNT(*) FROM jobs").fetchone()[0]
|
||||||
|
if count == 0:
|
||||||
|
con.executescript(seed_path.read_text())
|
||||||
|
con.commit()
|
||||||
|
finally:
|
||||||
|
con.close()
|
||||||
|
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def lifespan(app: FastAPI):
|
async def lifespan(app: FastAPI):
|
||||||
"""Load .env then run pending SQLite migrations on startup."""
|
"""Load .env, run migrations, and (in demo mode) seed the demo DB."""
|
||||||
# Load .env before any runtime env reads — safe because lifespan doesn't run
|
# Load .env before any runtime env reads — safe because lifespan doesn't run
|
||||||
# when dev_api is imported by tests (only when uvicorn actually starts).
|
# when dev_api is imported by tests (only when uvicorn actually starts).
|
||||||
_load_env(PEREGRINE_ROOT / ".env")
|
_load_env(PEREGRINE_ROOT / ".env")
|
||||||
from scripts.db_migrate import migrate_db
|
from scripts.db_migrate import migrate_db
|
||||||
migrate_db(Path(DB_PATH))
|
migrate_db(Path(DB_PATH))
|
||||||
|
if IS_DEMO and (seed_file := os.environ.get("DEMO_SEED_FILE")):
|
||||||
|
_load_demo_seed(DB_PATH, seed_file)
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
254
scripts/generate_demo_seed.py
Normal file
254
scripts/generate_demo_seed.py
Normal file
|
|
@ -0,0 +1,254 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Generate demo/seed.sql — committed seed INSERT statements for the demo DB.
|
||||||
|
|
||||||
|
Run whenever seed data needs to change:
|
||||||
|
conda run -n cf python scripts/generate_demo_seed.py
|
||||||
|
|
||||||
|
Outputs pure INSERT SQL (no DDL). Schema migrations are handled by db_migrate.py
|
||||||
|
at container startup. The seed SQL is loaded after migrations complete.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from datetime import date, timedelta
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
OUT_PATH = Path(__file__).parent.parent / "demo" / "seed.sql"
|
||||||
|
|
||||||
|
TODAY = date.today()
|
||||||
|
|
||||||
|
|
||||||
|
def _dago(n: int) -> str:
|
||||||
|
return (TODAY - timedelta(days=n)).isoformat()
|
||||||
|
|
||||||
|
|
||||||
|
def _dfrom(n: int) -> str:
|
||||||
|
return (TODAY + timedelta(days=n)).isoformat()
|
||||||
|
|
||||||
|
|
||||||
|
COVER_LETTER_SPOTIFY = """\
|
||||||
|
Dear Hiring Manager,
|
||||||
|
|
||||||
|
I'm excited to apply for the UX Designer role at Spotify. With five years of
|
||||||
|
experience designing for music discovery and cross-platform experiences, I've
|
||||||
|
consistently shipped features that make complex audio content feel effortless to
|
||||||
|
navigate. At my last role I led a redesign of the playlist creation flow that
|
||||||
|
reduced drop-off by 31%.
|
||||||
|
|
||||||
|
Spotify's commitment to artist and listener discovery — and its recent push into
|
||||||
|
audiobooks and podcast tooling — aligns directly with the kind of cross-format
|
||||||
|
design challenges I'm most energised by.
|
||||||
|
|
||||||
|
I'd love to bring that focus to your product design team.
|
||||||
|
|
||||||
|
Warm regards,
|
||||||
|
[Your name]
|
||||||
|
"""
|
||||||
|
|
||||||
|
SQL_PARTS: list[str] = []
|
||||||
|
|
||||||
|
# ── Jobs ──────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
# Columns: title, company, url, source, location, is_remote, salary,
|
||||||
|
# match_score, status, date_found, date_posted, cover_letter,
|
||||||
|
# applied_at, phone_screen_at, interviewing_at, offer_at, hired_at,
|
||||||
|
# interview_date, rejection_stage, hired_feedback
|
||||||
|
JOBS: list[tuple] = [
|
||||||
|
# ---- Review queue (12 jobs — mix of pending + approved) ------------------
|
||||||
|
("UX Designer",
|
||||||
|
"Spotify", "https://www.linkedin.com/jobs/view/1000001",
|
||||||
|
"linkedin", "Remote", 1, "$110k–$140k",
|
||||||
|
94.0, "approved", _dago(1), _dago(3), COVER_LETTER_SPOTIFY,
|
||||||
|
None, None, None, None, None, None, None, None),
|
||||||
|
|
||||||
|
("Product Designer",
|
||||||
|
"Duolingo", "https://www.linkedin.com/jobs/view/1000002",
|
||||||
|
"linkedin", "Pittsburgh, PA", 0, "$95k–$120k",
|
||||||
|
87.0, "approved", _dago(2), _dago(5), "Draft in progress — cover letter generating…",
|
||||||
|
None, None, None, None, None, None, None, None),
|
||||||
|
|
||||||
|
("UX Lead",
|
||||||
|
"NPR", "https://www.indeed.com/viewjob?jk=1000003",
|
||||||
|
"indeed", "Washington, DC", 1, "$120k–$150k",
|
||||||
|
81.0, "approved", _dago(3), _dago(7), None,
|
||||||
|
None, None, None, None, None, None, None, None),
|
||||||
|
|
||||||
|
# Ghost post — date_posted 34 days ago → shadow indicator
|
||||||
|
("Senior UX Designer",
|
||||||
|
"Mozilla", "https://www.linkedin.com/jobs/view/1000004",
|
||||||
|
"linkedin", "Remote", 1, "$105k–$130k",
|
||||||
|
81.0, "pending", _dago(2), _dago(34), None,
|
||||||
|
None, None, None, None, None, None, None, None),
|
||||||
|
|
||||||
|
("Interaction Designer",
|
||||||
|
"Figma", "https://www.indeed.com/viewjob?jk=1000005",
|
||||||
|
"indeed", "San Francisco, CA", 1, "$115k–$145k",
|
||||||
|
78.0, "pending", _dago(4), _dago(6), None,
|
||||||
|
None, None, None, None, None, None, None, None),
|
||||||
|
|
||||||
|
("Product Designer II",
|
||||||
|
"Notion", "https://www.linkedin.com/jobs/view/1000006",
|
||||||
|
"linkedin", "Remote", 1, "$100k–$130k",
|
||||||
|
76.0, "pending", _dago(5), _dago(8), None,
|
||||||
|
None, None, None, None, None, None, None, None),
|
||||||
|
|
||||||
|
("UX Designer",
|
||||||
|
"Stripe", "https://www.linkedin.com/jobs/view/1000007",
|
||||||
|
"linkedin", "Remote", 1, "$120k–$150k",
|
||||||
|
74.0, "pending", _dago(6), _dago(9), None,
|
||||||
|
None, None, None, None, None, None, None, None),
|
||||||
|
|
||||||
|
("UI/UX Designer",
|
||||||
|
"Canva", "https://www.indeed.com/viewjob?jk=1000008",
|
||||||
|
"indeed", "Remote", 1, "$90k–$115k",
|
||||||
|
72.0, "pending", _dago(7), _dago(10), None,
|
||||||
|
None, None, None, None, None, None, None, None),
|
||||||
|
|
||||||
|
("Senior Product Designer",
|
||||||
|
"Asana", "https://www.linkedin.com/jobs/view/1000009",
|
||||||
|
"linkedin", "San Francisco, CA", 1, "$125k–$155k",
|
||||||
|
69.0, "pending", _dago(8), _dago(11), None,
|
||||||
|
None, None, None, None, None, None, None, None),
|
||||||
|
|
||||||
|
("UX Researcher",
|
||||||
|
"Intercom", "https://www.indeed.com/viewjob?jk=1000010",
|
||||||
|
"indeed", "Remote", 1, "$95k–$120k",
|
||||||
|
67.0, "pending", _dago(9), _dago(12), None,
|
||||||
|
None, None, None, None, None, None, None, None),
|
||||||
|
|
||||||
|
("Product Designer",
|
||||||
|
"Linear", "https://www.linkedin.com/jobs/view/1000011",
|
||||||
|
"linkedin", "Remote", 1, "$110k–$135k",
|
||||||
|
65.0, "pending", _dago(10), _dago(13), None,
|
||||||
|
None, None, None, None, None, None, None, None),
|
||||||
|
|
||||||
|
("UX Designer",
|
||||||
|
"Loom", "https://www.indeed.com/viewjob?jk=1000012",
|
||||||
|
"indeed", "Remote", 1, "$90k–$110k",
|
||||||
|
62.0, "pending", _dago(11), _dago(14), None,
|
||||||
|
None, None, None, None, None, None, None, None),
|
||||||
|
|
||||||
|
# ---- Pipeline jobs (applied → hired) ------------------------------------
|
||||||
|
("Senior Product Designer",
|
||||||
|
"Asana", "https://www.asana.com/jobs/1000013",
|
||||||
|
"linkedin", "San Francisco, CA", 1, "$125k–$155k",
|
||||||
|
91.0, "phone_screen", _dago(14), _dago(16), None,
|
||||||
|
_dago(7), _dfrom(0), None, None, None,
|
||||||
|
f"{_dfrom(0)}T14:00:00", None, None),
|
||||||
|
|
||||||
|
("Product Designer",
|
||||||
|
"Notion", "https://www.notion.so/jobs/1000014",
|
||||||
|
"indeed", "Remote", 1, "$100k–$130k",
|
||||||
|
88.0, "interviewing", _dago(21), _dago(23), None,
|
||||||
|
_dago(14), _dago(10), _dago(3), None, None,
|
||||||
|
f"{_dfrom(7)}T10:00:00", None, None),
|
||||||
|
|
||||||
|
("Design Systems Designer",
|
||||||
|
"Figma", "https://www.figma.com/jobs/1000015",
|
||||||
|
"linkedin", "San Francisco, CA", 1, "$130k–$160k",
|
||||||
|
96.0, "hired", _dago(45), _dago(47), None,
|
||||||
|
_dago(38), _dago(32), _dago(25), _dago(14), _dago(7),
|
||||||
|
None, None,
|
||||||
|
'{"factors":["clear_scope","great_manager","mission_aligned"],"notes":"Excited about design systems work. Salary met expectations."}'),
|
||||||
|
|
||||||
|
("UX Designer",
|
||||||
|
"Slack", "https://slack.com/jobs/1000016",
|
||||||
|
"indeed", "Remote", 1, "$115k–$140k",
|
||||||
|
79.0, "applied", _dago(28), _dago(30), None,
|
||||||
|
_dago(18), None, None, None, None, None, None, None),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _q(v: object) -> str:
|
||||||
|
"""SQL-quote a Python value."""
|
||||||
|
if v is None:
|
||||||
|
return "NULL"
|
||||||
|
return "'" + str(v).replace("'", "''") + "'"
|
||||||
|
|
||||||
|
|
||||||
|
_JOB_COLS = (
|
||||||
|
"title, company, url, source, location, is_remote, salary, "
|
||||||
|
"match_score, status, date_found, date_posted, cover_letter, "
|
||||||
|
"applied_at, phone_screen_at, interviewing_at, offer_at, hired_at, "
|
||||||
|
"interview_date, rejection_stage, hired_feedback"
|
||||||
|
)
|
||||||
|
|
||||||
|
SQL_PARTS.append("-- jobs")
|
||||||
|
for job in JOBS:
|
||||||
|
vals = ", ".join(_q(v) for v in job)
|
||||||
|
SQL_PARTS.append(f"INSERT INTO jobs ({_JOB_COLS}) VALUES ({vals});")
|
||||||
|
|
||||||
|
# ── Contacts ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
# (job_id, direction, subject, from_addr, to_addr, received_at, stage_signal)
|
||||||
|
CONTACTS: list[tuple] = [
|
||||||
|
(1, "inbound", "Excited to connect — UX Designer role at Spotify",
|
||||||
|
"jamie.chen@spotify.com", "you@example.com", _dago(3), "positive_response"),
|
||||||
|
(1, "outbound", "Re: Excited to connect — UX Designer role at Spotify",
|
||||||
|
"you@example.com", "jamie.chen@spotify.com", _dago(2), None),
|
||||||
|
(13, "inbound", "Interview Confirmation — Senior Product Designer",
|
||||||
|
"recruiting@asana.com", "you@example.com", _dago(2), "interview_scheduled"),
|
||||||
|
(14, "inbound", "Your panel interview is confirmed for Apr 22",
|
||||||
|
"recruiting@notion.so", "you@example.com", _dago(3), "interview_scheduled"),
|
||||||
|
(14, "inbound", "Pre-interview prep resources",
|
||||||
|
"marcus.webb@notion.so", "you@example.com", _dago(2), "positive_response"),
|
||||||
|
(15, "inbound", "Figma Design Systems — Offer Letter",
|
||||||
|
"offers@figma.com", "you@example.com", _dago(14), "offer_received"),
|
||||||
|
(15, "outbound", "Re: Figma Design Systems — Offer Letter (acceptance)",
|
||||||
|
"you@example.com", "offers@figma.com", _dago(10), None),
|
||||||
|
(15, "inbound", "Welcome to Figma! Onboarding next steps",
|
||||||
|
"onboarding@figma.com", "you@example.com", _dago(7), None),
|
||||||
|
(16, "inbound", "Thanks for applying to Slack",
|
||||||
|
"noreply@slack.com", "you@example.com", _dago(18), None),
|
||||||
|
]
|
||||||
|
|
||||||
|
SQL_PARTS.append("\n-- job_contacts")
|
||||||
|
for c in CONTACTS:
|
||||||
|
job_id, direction, subject, from_addr, to_addr, received_at, stage_signal = c
|
||||||
|
SQL_PARTS.append(
|
||||||
|
f"INSERT INTO job_contacts "
|
||||||
|
f"(job_id, direction, subject, from_addr, to_addr, received_at, stage_signal) "
|
||||||
|
f"VALUES ({job_id}, {_q(direction)}, {_q(subject)}, {_q(from_addr)}, "
|
||||||
|
f"{_q(to_addr)}, {_q(received_at)}, {_q(stage_signal)});"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ── References ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
# (name, email, role, company, relationship, notes, tags, prep_email)
|
||||||
|
REFERENCES: list[tuple] = [
|
||||||
|
("Dr. Priya Nair", "priya.nair@example.com", "Director of Design", "Acme Corp",
|
||||||
|
"former_manager",
|
||||||
|
"Managed me for 3 years on the consumer app redesign. Enthusiastic reference.",
|
||||||
|
'["manager","design"]',
|
||||||
|
"Hi Priya,\n\nI hope you're doing well! I'm currently interviewing for a few senior UX roles "
|
||||||
|
"and would be so grateful if you'd be willing to serve as a reference.\n\nThank you!\n[Your name]"),
|
||||||
|
|
||||||
|
("Sam Torres", "sam.torres@example.com", "Senior Product Designer", "Acme Corp",
|
||||||
|
"former_colleague",
|
||||||
|
"Worked together on design systems. Great at speaking to collaborative process.",
|
||||||
|
'["colleague","design_systems"]', None),
|
||||||
|
|
||||||
|
("Jordan Kim", "jordan.kim@example.com", "VP of Product", "Streamline Inc",
|
||||||
|
"former_manager",
|
||||||
|
"Led the product team I was embedded in. Can speak to business impact of design work.",
|
||||||
|
'["manager","product"]', None),
|
||||||
|
]
|
||||||
|
|
||||||
|
SQL_PARTS.append("\n-- references_")
|
||||||
|
for ref in REFERENCES:
|
||||||
|
name, email, role, company, relationship, notes, tags, prep_email = ref
|
||||||
|
SQL_PARTS.append(
|
||||||
|
f"INSERT INTO references_ "
|
||||||
|
f"(name, email, role, company, relationship, notes, tags, prep_email) "
|
||||||
|
f"VALUES ({_q(name)}, {_q(email)}, {_q(role)}, {_q(company)}, "
|
||||||
|
f"{_q(relationship)}, {_q(notes)}, {_q(tags)}, {_q(prep_email)});"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ── Write output ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
output = "\n".join(SQL_PARTS) + "\n"
|
||||||
|
OUT_PATH.write_text(output, encoding="utf-8")
|
||||||
|
print(
|
||||||
|
f"Wrote {OUT_PATH} "
|
||||||
|
f"({len(JOBS)} jobs, {len(CONTACTS)} contacts, {len(REFERENCES)} references)"
|
||||||
|
)
|
||||||
Loading…
Reference in a new issue