Intelliverse / app.py
Hitika111's picture
Update app.py
8ece11d verified
from flask import Flask, render_template, request, jsonify, redirect, url_for, flash
from werkzeug.utils import secure_filename
import os
import json
from utils.resume_parser import extract_text_from_pdf
from utils.ai_analyzer import analyze_resume_with_jd
from utils.scheduler import schedule_interview, send_rejection_email
from utils.database import db, Candidate, JobDescription
from datetime import datetime
import logging
logger = logging.getLogger(__name__)
app = Flask(__name__)
app.config['SECRET_KEY'] = os.getenv('SECRET_KEY', 'ats-hackathon-secret')
# HuggingFace Spaces: use absolute /app paths so gunicorn (non-root) can write
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
UPLOAD_DIR = os.path.join(BASE_DIR, 'uploads')
DB_PATH = os.path.join(BASE_DIR, 'ats.db')
os.makedirs(UPLOAD_DIR, exist_ok=True)
app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{DB_PATH}'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['UPLOAD_FOLDER'] = UPLOAD_DIR
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max
ALLOWED_EXTENSIONS = {'pdf'}
db.init_app(app)
with app.app_context():
db.create_all()
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
# ─── ROUTES ───────────────────────────────────────────────────────────────────
@app.route('/')
def index():
return render_template('index.html')
@app.route('/dashboard')
def dashboard():
candidates = Candidate.query.order_by(Candidate.score.desc()).all()
stats = {
'total': len(candidates),
'shortlisted': sum(1 for c in candidates if c.status == 'shortlisted'),
'rejected': sum(1 for c in candidates if c.status == 'rejected'),
'scheduled': sum(1 for c in candidates if c.status == 'scheduled'),
'avg_score': round(sum(c.score or 0 for c in candidates) / max(len(candidates), 1), 1)
}
return render_template('dashboard.html', candidates=candidates, stats=stats)
@app.route('/upload', methods=['GET', 'POST'])
def upload():
if request.method == 'POST':
try:
job_title = request.form.get('job_title', '')
job_description = request.form.get('job_description', '')
threshold = int(request.form.get('threshold', 70))
interview_date = request.form.get('interview_date', '')
interview_time = request.form.get('interview_time', '')
interview_link = request.form.get('interview_link', '')
files = request.files.getlist('resumes')
if not files or all(f.filename == '' for f in files):
return jsonify({'success': False, 'error': 'No files received.'}), 400
results = []
for file in files:
if not file or not allowed_file(file.filename):
continue
try:
filename = secure_filename(file.filename)
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
file.save(filepath)
resume_text = extract_text_from_pdf(filepath)
if not resume_text.strip():
results.append({'name': filename, 'email': '', 'score': 0,
'status': 'error', 'id': None,
'error': 'Could not extract text from PDF'})
continue
analysis = analyze_resume_with_jd(resume_text, job_description, job_title)
if 'error' in analysis and not analysis.get('name'):
results.append({'name': filename, 'email': '', 'score': 0,
'status': 'error', 'id': None,
'error': analysis['error']})
continue
score = int(analysis.get('score', 0))
status = 'shortlisted' if score >= threshold else 'rejected'
candidate = Candidate(
name=analysis.get('name', 'Unknown'),
email=analysis.get('email', ''),
phone=analysis.get('phone', ''),
score=score,
status=status,
job_title=job_title,
skills_matched=json.dumps(analysis.get('matching_skills', [])),
skills_missing=json.dumps(analysis.get('missing_skills', [])),
reasoning=analysis.get('reasoning', ''),
resume_filename=filename,
created_at=datetime.utcnow()
)
db.session.add(candidate)
db.session.commit()
if status == 'shortlisted' and analysis.get('email'):
interview_datetime = (interview_date + ' ' + interview_time).strip() if interview_date else None
email_sent = schedule_interview(
candidate_email=analysis.get('email'),
candidate_name=analysis.get('name'),
job_title=job_title,
interview_datetime=interview_datetime,
interview_link=interview_link,
candidate_id=candidate.id
)
if email_sent:
candidate.status = 'scheduled'
candidate.interview_scheduled = interview_datetime
db.session.commit()
elif status == 'rejected' and analysis.get('email'):
send_rejection_email(
candidate_email=analysis.get('email'),
candidate_name=analysis.get('name'),
job_title=job_title
)
results.append({
'name': candidate.name,
'email': candidate.email,
'score': score,
'status': candidate.status,
'id': candidate.id
})
except Exception as file_err:
logger.error('Error processing file %s: %s', file.filename, file_err)
results.append({'name': file.filename, 'email': '', 'score': 0,
'status': 'error', 'id': None, 'error': str(file_err)})
return jsonify({'success': True, 'results': results, 'total': len(results)})
except Exception as e:
logger.error('Upload route error: %s', e)
return jsonify({'success': False, 'error': str(e)}), 500
return render_template('upload.html')
@app.route('/candidate/<int:id>')
def candidate_detail(id):
candidate = Candidate.query.get_or_404(id)
skills_matched = json.loads(candidate.skills_matched or '[]')
skills_missing = json.loads(candidate.skills_missing or '[]')
return render_template('candidate_detail.html',
candidate=candidate,
skills_matched=skills_matched,
skills_missing=skills_missing)
@app.route('/api/candidates')
def api_candidates():
candidates = Candidate.query.order_by(Candidate.score.desc()).all()
return jsonify([{
'id': c.id,
'name': c.name,
'email': c.email,
'score': c.score,
'status': c.status,
'job_title': c.job_title,
'created_at': c.created_at.isoformat() if c.created_at else ''
} for c in candidates])
@app.route('/api/reschedule/<int:id>', methods=['POST'])
def reschedule(id):
candidate = Candidate.query.get_or_404(id)
data = request.json
email_sent = schedule_interview(
candidate_email=candidate.email,
candidate_name=candidate.name,
job_title=candidate.job_title,
interview_datetime=data.get('datetime'),
interview_link=data.get('link'),
candidate_id=id
)
if email_sent:
candidate.interview_scheduled = data.get('datetime')
candidate.status = 'scheduled'
db.session.commit()
return jsonify({'success': True})
return jsonify({'success': False, 'error': 'Email failed'})
if __name__ == '__main__':
os.makedirs('uploads', exist_ok=True)
app.run(debug=True, port=5000)