Hitika111 commited on
Commit
8ece11d
Β·
verified Β·
1 Parent(s): f3c7318

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +108 -79
app.py CHANGED
@@ -7,25 +7,33 @@ from utils.ai_analyzer import analyze_resume_with_jd
7
  from utils.scheduler import schedule_interview, send_rejection_email
8
  from utils.database import db, Candidate, JobDescription
9
  from datetime import datetime
 
 
10
 
11
  app = Flask(__name__)
12
  app.config['SECRET_KEY'] = os.getenv('SECRET_KEY', 'ats-hackathon-secret')
13
- app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///ats.db'
 
 
 
 
 
 
 
14
  app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
15
- app.config['UPLOAD_FOLDER'] = 'uploads'
16
  app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max
17
 
18
  ALLOWED_EXTENSIONS = {'pdf'}
19
 
20
  db.init_app(app)
21
 
 
 
 
22
  def allowed_file(filename):
23
  return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
24
 
25
- @app.before_request
26
- def create_tables():
27
- db.create_all()
28
-
29
  # ─── ROUTES ───────────────────────────────────────────────────────────────────
30
 
31
  @app.route('/')
@@ -47,81 +55,102 @@ def dashboard():
47
  @app.route('/upload', methods=['GET', 'POST'])
48
  def upload():
49
  if request.method == 'POST':
50
- job_title = request.form.get('job_title', '')
51
- job_description = request.form.get('job_description', '')
52
- threshold = int(request.form.get('threshold', 70))
53
- interview_date = request.form.get('interview_date', '')
54
- interview_time = request.form.get('interview_time', '')
55
- interview_link = request.form.get('interview_link', '')
56
-
57
- files = request.files.getlist('resumes')
58
- results = []
59
-
60
- for file in files:
61
- if file and allowed_file(file.filename):
62
- filename = secure_filename(file.filename)
63
- filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
64
- file.save(filepath)
65
-
66
- # Extract text from PDF
67
- resume_text = extract_text_from_pdf(filepath)
68
-
69
- # AI analysis
70
- analysis = analyze_resume_with_jd(resume_text, job_description, job_title)
71
-
72
- # Determine status
73
- score = analysis.get('score', 0)
74
- status = 'shortlisted' if score >= threshold else 'rejected'
75
-
76
- # Save to DB
77
- candidate = Candidate(
78
- name=analysis.get('name', 'Unknown'),
79
- email=analysis.get('email', ''),
80
- phone=analysis.get('phone', ''),
81
- score=score,
82
- status=status,
83
- job_title=job_title,
84
- skills_matched=json.dumps(analysis.get('matching_skills', [])),
85
- skills_missing=json.dumps(analysis.get('missing_skills', [])),
86
- reasoning=analysis.get('reasoning', ''),
87
- resume_filename=filename,
88
- created_at=datetime.utcnow()
89
- )
90
- db.session.add(candidate)
91
- db.session.commit()
92
-
93
- # Send automated emails
94
- if status == 'shortlisted' and analysis.get('email'):
95
- interview_datetime = f"{interview_date} {interview_time}" if interview_date else None
96
- email_sent = schedule_interview(
97
- candidate_email=analysis.get('email'),
98
- candidate_name=analysis.get('name'),
99
  job_title=job_title,
100
- interview_datetime=interview_datetime,
101
- interview_link=interview_link,
102
- candidate_id=candidate.id
103
- )
104
- if email_sent:
105
- candidate.status = 'scheduled'
106
- candidate.interview_scheduled = interview_datetime
107
- db.session.commit()
108
-
109
- elif status == 'rejected' and analysis.get('email'):
110
- send_rejection_email(
111
- candidate_email=analysis.get('email'),
112
- candidate_name=analysis.get('name'),
113
- job_title=job_title
114
  )
115
-
116
- results.append({
117
- 'name': candidate.name,
118
- 'email': candidate.email,
119
- 'score': score,
120
- 'status': candidate.status,
121
- 'id': candidate.id
122
- })
123
-
124
- return jsonify({'success': True, 'results': results, 'total': len(results)})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
 
126
  return render_template('upload.html')
127
 
 
7
  from utils.scheduler import schedule_interview, send_rejection_email
8
  from utils.database import db, Candidate, JobDescription
9
  from datetime import datetime
10
+ import logging
11
+ logger = logging.getLogger(__name__)
12
 
13
  app = Flask(__name__)
14
  app.config['SECRET_KEY'] = os.getenv('SECRET_KEY', 'ats-hackathon-secret')
15
+
16
+ # HuggingFace Spaces: use absolute /app paths so gunicorn (non-root) can write
17
+ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
18
+ UPLOAD_DIR = os.path.join(BASE_DIR, 'uploads')
19
+ DB_PATH = os.path.join(BASE_DIR, 'ats.db')
20
+ os.makedirs(UPLOAD_DIR, exist_ok=True)
21
+
22
+ app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{DB_PATH}'
23
  app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
24
+ app.config['UPLOAD_FOLDER'] = UPLOAD_DIR
25
  app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max
26
 
27
  ALLOWED_EXTENSIONS = {'pdf'}
28
 
29
  db.init_app(app)
30
 
31
+ with app.app_context():
32
+ db.create_all()
33
+
34
  def allowed_file(filename):
35
  return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
36
 
 
 
 
 
37
  # ─── ROUTES ───────────────────────────────────────────────────────────────────
38
 
39
  @app.route('/')
 
55
  @app.route('/upload', methods=['GET', 'POST'])
56
  def upload():
57
  if request.method == 'POST':
58
+ try:
59
+ job_title = request.form.get('job_title', '')
60
+ job_description = request.form.get('job_description', '')
61
+ threshold = int(request.form.get('threshold', 70))
62
+ interview_date = request.form.get('interview_date', '')
63
+ interview_time = request.form.get('interview_time', '')
64
+ interview_link = request.form.get('interview_link', '')
65
+
66
+ files = request.files.getlist('resumes')
67
+ if not files or all(f.filename == '' for f in files):
68
+ return jsonify({'success': False, 'error': 'No files received.'}), 400
69
+
70
+ results = []
71
+
72
+ for file in files:
73
+ if not file or not allowed_file(file.filename):
74
+ continue
75
+ try:
76
+ filename = secure_filename(file.filename)
77
+ filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
78
+ file.save(filepath)
79
+
80
+ resume_text = extract_text_from_pdf(filepath)
81
+ if not resume_text.strip():
82
+ results.append({'name': filename, 'email': '', 'score': 0,
83
+ 'status': 'error', 'id': None,
84
+ 'error': 'Could not extract text from PDF'})
85
+ continue
86
+
87
+ analysis = analyze_resume_with_jd(resume_text, job_description, job_title)
88
+
89
+ if 'error' in analysis and not analysis.get('name'):
90
+ results.append({'name': filename, 'email': '', 'score': 0,
91
+ 'status': 'error', 'id': None,
92
+ 'error': analysis['error']})
93
+ continue
94
+
95
+ score = int(analysis.get('score', 0))
96
+ status = 'shortlisted' if score >= threshold else 'rejected'
97
+
98
+ candidate = Candidate(
99
+ name=analysis.get('name', 'Unknown'),
100
+ email=analysis.get('email', ''),
101
+ phone=analysis.get('phone', ''),
102
+ score=score,
103
+ status=status,
 
 
 
104
  job_title=job_title,
105
+ skills_matched=json.dumps(analysis.get('matching_skills', [])),
106
+ skills_missing=json.dumps(analysis.get('missing_skills', [])),
107
+ reasoning=analysis.get('reasoning', ''),
108
+ resume_filename=filename,
109
+ created_at=datetime.utcnow()
 
 
 
 
 
 
 
 
 
110
  )
111
+ db.session.add(candidate)
112
+ db.session.commit()
113
+
114
+ if status == 'shortlisted' and analysis.get('email'):
115
+ interview_datetime = (interview_date + ' ' + interview_time).strip() if interview_date else None
116
+ email_sent = schedule_interview(
117
+ candidate_email=analysis.get('email'),
118
+ candidate_name=analysis.get('name'),
119
+ job_title=job_title,
120
+ interview_datetime=interview_datetime,
121
+ interview_link=interview_link,
122
+ candidate_id=candidate.id
123
+ )
124
+ if email_sent:
125
+ candidate.status = 'scheduled'
126
+ candidate.interview_scheduled = interview_datetime
127
+ db.session.commit()
128
+
129
+ elif status == 'rejected' and analysis.get('email'):
130
+ send_rejection_email(
131
+ candidate_email=analysis.get('email'),
132
+ candidate_name=analysis.get('name'),
133
+ job_title=job_title
134
+ )
135
+
136
+ results.append({
137
+ 'name': candidate.name,
138
+ 'email': candidate.email,
139
+ 'score': score,
140
+ 'status': candidate.status,
141
+ 'id': candidate.id
142
+ })
143
+
144
+ except Exception as file_err:
145
+ logger.error('Error processing file %s: %s', file.filename, file_err)
146
+ results.append({'name': file.filename, 'email': '', 'score': 0,
147
+ 'status': 'error', 'id': None, 'error': str(file_err)})
148
+
149
+ return jsonify({'success': True, 'results': results, 'total': len(results)})
150
+
151
+ except Exception as e:
152
+ logger.error('Upload route error: %s', e)
153
+ return jsonify({'success': False, 'error': str(e)}), 500
154
 
155
  return render_template('upload.html')
156