Spaces:
Sleeping
Sleeping
File size: 19,218 Bytes
504df0f 2ae57cb 504df0f 9d4b413 504df0f 2ae57cb 504df0f 2ae57cb 504df0f 2ae57cb 9d4b413 2ae57cb 504df0f 49fbb3a 2ae57cb 504df0f 3c4bd31 504df0f 2ae57cb 504df0f 2ae57cb 504df0f 2ae57cb 504df0f 2ae57cb 504df0f 1e8e58c 504df0f 2ae57cb 3c4bd31 504df0f 2ae57cb 504df0f 49fbb3a 504df0f 49fbb3a 5f99f0f 981366b 5f99f0f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 | from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
import json
db = SQLAlchemy()
# ✅ Import User model here, before Job and Application use it
from backend.models.user import User
class Job(db.Model):
__tablename__ = 'jobs'
id = db.Column(db.Integer, primary_key=True)
role = db.Column(db.String(100), nullable=False)
description = db.Column(db.Text, nullable=False)
seniority = db.Column(db.String(50), nullable=False)
skills = db.Column(db.Text, nullable=False)
company = db.Column(db.String(100), nullable=False)
date_posted = db.Column(db.DateTime, default=datetime.utcnow)
# Number of interview questions for this job. Recruiters can set this
# value when posting a job. Defaults to 3 to preserve existing
# behaviour where the interview consists of three questions. The
# interview API uses this field to determine when to stop asking
# follow‑up questions. See backend/routes/interview_api.py for
# details.
num_questions = db.Column(db.Integer, nullable=False, default=3)
recruiter_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
recruiter = db.relationship('User', backref='posted_jobs')
@property
def skills_list(self):
"""Return a list of skills parsed from the JSON string stored in ``skills``.
The ``skills`` column stores a JSON encoded list of skills (e.g. '["Python", "Flask"]').
In templates it is convenient to work with a Python list so that skills can be joined
or iterated over. If parsing fails for any reason an empty list is returned.
"""
try:
# Import json lazily to avoid circular imports at module import time.
import json as _json
return _json.loads(self.skills) if self.skills else []
except Exception:
return []
def __repr__(self):
return f"<Job {self.role} at {self.company}>"
class Application(db.Model):
__tablename__ = 'applications'
id = db.Column(db.Integer, primary_key=True)
job_id = db.Column(db.Integer, db.ForeignKey('jobs.id'), nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
name = db.Column(db.String(100), nullable=False)
email = db.Column(db.String(100), nullable=False)
resume_path = db.Column(db.String(255), nullable=True)
cover_letter = db.Column(db.Text, nullable=True)
extracted_features = db.Column(db.Text, nullable=True)
status = db.Column(db.String(50), default='applied')
date_applied = db.Column(db.DateTime, default=datetime.utcnow)
interview_log = db.Column(db.Text)
user = db.relationship('User', backref='applications')
# Set up a relationship back to the Job so that templates can access
# ``application.job`` directly. Without this relationship you'd need to
# query the Job model manually in the route or template, which is less
# convenient and can lead to additional database queries.
job = db.relationship('Job', backref='applications', lazy='joined')
def __repr__(self):
return f"Application('{self.name}', '{self.email}', Job ID: {self.job_id})"
def get_profile_data(self):
try:
return json.loads(self.extracted_features) if self.extracted_features else {}
except:
return {}
def init_db(app):
db.init_app(app)
with app.app_context():
# Create any missing tables. SQLAlchemy does not automatically add
# columns to existing tables, so we call create_all() first to ensure
# new tables (like ``applications`` or ``jobs``) are present.
db.create_all()
# Dynamically add the ``num_questions`` column to the ``jobs`` table
# if it is missing. When deploying an updated application against an
# existing database, the new field will not appear until we run an
# explicit ALTER TABLE. Inspect the current table schema and add
# ``num_questions`` with a default of 3 if it doesn't exist. This
# logic is idempotent: the ALTER TABLE statement runs only when
# necessary.
from sqlalchemy import inspect
inspector = inspect(db.engine)
try:
columns = [col['name'] for col in inspector.get_columns('jobs')]
if 'num_questions' not in columns:
# SQLite supports adding new columns via ALTER TABLE. The
# default value of 3 matches the default declared in the
# Job model. If you are using a different database backend,
# verify that this syntax is supported.
db.session.execute('ALTER TABLE jobs ADD COLUMN num_questions INTEGER NOT NULL DEFAULT 3')
db.session.commit()
except Exception:
# If inspection fails (e.g. the table does not exist yet), rely on
# SQLAlchemy's create_all() to create a fresh schema with the
# ``num_questions`` column.
pass
# Seed professional demo data (recruiter + applicant + a curated set of
# jobs) so the platform always looks polished and the full demo flow
# works after every restart. On Hugging Face the SQLite DB lives in
# the ephemeral /tmp directory and is wiped on each restart, so this
# idempotent seeding repopulates a clean, consistent dataset every time.
seed_demo_data()
# Known demo accounts used for the marketing demo. Passwords are intentionally
# simple here because these are throwaway demo accounts on an ephemeral DB.
DEMO_RECRUITER_EMAIL = 'hr@codingo.ai'
DEMO_APPLICANT_EMAIL = 'candidate@codingo.ai'
DEMO_PASSWORD = 'codingo123'
def seed_demo_data():
"""Idempotently seed a demo recruiter, a demo applicant and a curated set
of professional job listings.
Safe to call on every startup: each entity is only created when missing,
and jobs are only inserted when the ``jobs`` table is empty. Any failure
is rolled back and swallowed so seeding can never block app startup.
"""
try:
# --- Demo recruiter (owns the seeded jobs so they appear in the HR
# dashboard, which filters by recruiter_id) ---
recruiter = User.query.filter_by(email=DEMO_RECRUITER_EMAIL).first()
if recruiter is None:
recruiter = User(
username='Codingo HR',
email=DEMO_RECRUITER_EMAIL,
role='recruiter',
)
recruiter.set_password(DEMO_PASSWORD)
db.session.add(recruiter)
db.session.commit()
# --- Demo applicant (job-seeker role is 'unemployed') ---
applicant = User.query.filter_by(email=DEMO_APPLICANT_EMAIL).first()
if applicant is None:
applicant = User(
username='Demo Candidate',
email=DEMO_APPLICANT_EMAIL,
role='unemployed',
)
applicant.set_password(DEMO_PASSWORD)
db.session.add(applicant)
db.session.commit()
# --- Jobs: only seed when there are none, to avoid duplicates and to
# never clobber jobs a recruiter may have posted at runtime ---
if Job.query.count() == 0:
demo_jobs = [
{
'role': 'Data Scientist',
'seniority': 'Mid-level',
'company': 'NorthBridge Analytics',
'skills': ['Python', 'SQL', 'Pandas', 'scikit-learn',
'Machine Learning', 'Statistics', 'Data Visualization'],
'description': (
"We are looking for a Data Scientist to turn raw data into "
"actionable insight. You will design and evaluate machine "
"learning models, run statistical analyses, and build clear "
"visualizations that guide product and business decisions. "
"You will collaborate closely with engineering and product "
"teams to take models from prototype to production."
),
},
{
'role': 'Data Engineer',
'seniority': 'Senior',
'company': 'Cloudbyte Systems',
'skills': ['Python', 'SQL', 'Apache Spark', 'Airflow',
'ETL', 'AWS', 'Data Warehousing'],
'description': (
"Join us as a Senior Data Engineer to design, build and "
"maintain the data pipelines that power our analytics and "
"machine learning platforms. You will own scalable ETL "
"workflows, optimize our data warehouse, and ensure data "
"quality and reliability across the organization."
),
},
{
'role': 'Machine Learning Engineer',
'seniority': 'Mid-level',
'company': 'Vantix AI',
'skills': ['Python', 'TensorFlow', 'PyTorch', 'Machine Learning',
'MLOps', 'Model Deployment', 'Docker'],
'description': (
"As a Machine Learning Engineer you will take models from "
"research into reliable, production-grade services. You will "
"train and fine-tune models, build robust deployment and "
"monitoring pipelines, and work with data scientists to "
"ship features that delight our users."
),
},
{
'role': 'NLP Engineer',
'seniority': 'Senior',
'company': 'Lingua Labs',
'skills': ['Python', 'NLP', 'Transformers', 'spaCy',
'PyTorch', 'LLMs', 'Hugging Face'],
'description': (
"We are hiring a Senior NLP Engineer to build state-of-the-art "
"language understanding systems. You will develop models for "
"text classification, information extraction and conversational "
"AI, fine-tune large language models, and deploy them at scale "
"to power our products."
),
},
{
'role': 'Computer Vision Engineer',
'seniority': 'Mid-level',
'company': 'VisionWorks AI',
'skills': ['Python', 'OpenCV', 'Deep Learning', 'PyTorch',
'Image Processing', 'CNNs', 'Model Optimization'],
'description': (
"Join our team as a Computer Vision Engineer to develop "
"image and video understanding systems. You will design and "
"train deep learning models for detection, segmentation and "
"recognition, optimize them for real-time performance, and "
"integrate them into our production pipeline."
),
},
]
for spec in demo_jobs:
db.session.add(Job(
role=spec['role'],
description=spec['description'],
seniority=spec['seniority'],
skills=json.dumps(spec['skills']),
company=spec['company'],
recruiter_id=recruiter.id,
num_questions=4,
))
db.session.commit()
# --- Pre-seed completed demo interviews for the demo candidate so the
# HR dashboard is always populated even after a restart (the SQLite
# DB is ephemeral on Hugging Face). Idempotent per (candidate, job):
# a row is only created when one does not already exist. ---
demo_interviews = [
{
'role': 'Data Scientist',
'skills': ['Python', 'SQL', 'Pandas', 'scikit-learn',
'Machine Learning', 'Data Visualization'],
'experience': [
'Data Analyst at BrightData (2 years) — built dashboards and predictive models',
'Machine Learning Intern at NorthBridge Analytics — customer churn prediction',
],
'education': ['BSc in Computer Science, USAL'],
'qa_log': [
{
'question': "Hi, I'm LUNA, your AI recruiter. Can you tell me about your background and what drew you to data science?",
'answer': "I have around three years working with data. I started as a data analyst building dashboards, then moved into machine learning where I built churn and forecasting models in Python with scikit-learn. I love data science because it turns messy data into decisions that actually move the business.",
'evaluation': {'score': 'Excellent', 'feedback': 'Clear, specific background with concrete tools and real impact on the business.'},
},
{
'question': "How do you prevent a model from overfitting?",
'answer': "I use cross-validation to get an honest estimate of performance, keep the model as simple as the data allows, and apply regularization like L1 or L2. I also use more training data when possible, early stopping for neural nets, and I always compare training versus validation error to catch overfitting early.",
'evaluation': {'score': 'Excellent', 'feedback': 'Covers cross-validation, regularization, and monitoring train/validation gap accurately.'},
},
{
'question': "Walk me through a data science project you are proud of and the impact it had.",
'answer': "At NorthBridge I built a customer churn model. I engineered features from usage logs, trained a gradient boosting model, and reached about 0.86 ROC-AUC. The retention team used the risk scores to target outreach and we reduced monthly churn by roughly 12 percent.",
'evaluation': {'score': 'Good', 'feedback': 'Solid end-to-end project with a measurable result; could add more on validation and deployment.'},
},
{
'question': "What are your salary expectations? Are you looking for a full-time or part-time role, and do you prefer remote or on-site work?",
'answer': "I'm looking for a full-time role and I'm flexible on salary within the market range for a mid-level data scientist. I'm happy with hybrid or on-site, and comfortable fully remote as well.",
'evaluation': {'score': 'Excellent', 'feedback': 'Clear, reasonable and flexible answer on logistics.'},
},
],
},
{
'role': 'Data Engineer',
'skills': ['Python', 'SQL', 'Apache Spark', 'Airflow', 'ETL', 'AWS'],
'experience': [
'Data Engineer at Cloudbyte Systems (3 years) — built batch and streaming pipelines',
'Backend Developer — automated ETL jobs and data quality checks',
],
'education': ['BSc in Software Engineering, USAL'],
'qa_log': [
{
'question': "Hi, I'm LUNA, your AI recruiter. Tell me about your experience and why data engineering?",
'answer': "I've spent three years as a data engineer building pipelines in Python and Spark, orchestrated with Airflow on AWS. I enjoy data engineering because reliable, well-modeled data is what makes everything else — analytics and machine learning — actually work.",
'evaluation': {'score': 'Excellent', 'feedback': 'Strong, specific experience with the exact stack the role needs.'},
},
{
'question': "How do you design a reliable ETL pipeline?",
'answer': "I make each step idempotent so re-runs are safe, add schema validation and data quality checks early, and design for incremental loads instead of full reloads. I orchestrate with Airflow, add retries and alerting on failures, and keep raw data so I can reprocess if logic changes.",
'evaluation': {'score': 'Excellent', 'feedback': 'Idempotency, validation, incremental loads and observability — a complete, senior answer.'},
},
{
'question': "Describe a time you optimized a slow data pipeline.",
'answer': "A nightly Spark job was taking over four hours. I found it was shuffling too much data, so I repartitioned on the join key, cached a reused dataframe, and switched some wide transformations to broadcast joins. It dropped to about forty minutes.",
'evaluation': {'score': 'Good', 'feedback': 'Concrete optimization with a real result; could mention profiling and cost trade-offs.'},
},
{
'question': "What are your salary expectations? Are you looking for a full-time or part-time role, and do you prefer remote or on-site work?",
'answer': "Full-time, and I'm open on compensation within the senior data engineer range. I prefer hybrid but I'm fully comfortable working remotely.",
'evaluation': {'score': 'Excellent', 'feedback': 'Direct and flexible on role type, pay and location.'},
},
],
},
]
for spec in demo_interviews:
job = Job.query.filter_by(role=spec['role'], recruiter_id=recruiter.id).first()
if job is None:
continue
existing = Application.query.filter_by(user_id=applicant.id, job_id=job.id).first()
if existing is not None:
continue
db.session.add(Application(
job_id=job.id,
user_id=applicant.id,
name=applicant.username,
email=applicant.email,
status='interviewed',
extracted_features=json.dumps({
'skills': spec['skills'],
'experience': spec['experience'],
'education': spec['education'],
}),
interview_log=json.dumps(spec['qa_log'], ensure_ascii=False),
))
db.session.commit()
except Exception as exc:
# Never let seeding break startup.
db.session.rollback()
print(f"Demo data seeding skipped due to error: {exc}")
|