""" HR-AI Interview Simulation V2 - Full Premium UI Complete Flask Application with Premium Frontend Deployed: December 2024 """ import os from flask import Flask, request, jsonify, send_from_directory from flask_cors import CORS from groq import Groq import json import uuid import re from datetime import datetime from PyPDF2 import PdfReader # Configuration API_KEY = os.getenv('GROQ_API_KEY', '') GROQ_MODEL = 'llama-3.3-70b-versatile' # Get the directory where app.py is located BASE_DIR = os.path.dirname(os.path.abspath(__file__)) STATIC_DIR = os.path.join(BASE_DIR, 'static') print(f"BASE_DIR: {BASE_DIR}") print(f"STATIC_DIR: {STATIC_DIR}") print(f"Static dir exists: {os.path.exists(STATIC_DIR)}") if os.path.exists(STATIC_DIR): print(f"Static contents: {os.listdir(STATIC_DIR)}") app = Flask(__name__, static_folder=STATIC_DIR, static_url_path='/static') CORS(app) client = None if API_KEY: client = Groq(api_key=API_KEY) sessions = {} def get_or_create_session(session_id): if session_id not in sessions: sessions[session_id] = { 'candidate_profile': None, 'interview_questions': [], 'interview_responses': [], 'interview_start_time': None, 'interview_end_time': None } return sessions[session_id] def extract_text_from_pdf(pdf_file): try: reader = PdfReader(pdf_file) return "".join(p.extract_text() or "" for p in reader.pages) except: return "" def extract_json_from_response(text): match = re.search(r'```(?:json)?\s*([\s\S]*?)\s*```', text) if match: try: return json.loads(match.group(1)) except: pass if '{' in text: try: return json.loads(text[text.find('{'):text.rfind('}')+1]) except: pass return None def generate_content_with_groq(prompt): if not client: return None try: resp = client.chat.completions.create( messages=[{"role": "system", "content": "Return only valid JSON."}, {"role": "user", "content": prompt}], model=GROQ_MODEL, temperature=0.7, max_tokens=4096 ) data = extract_json_from_response(resp.choices[0].message.content) return json.dumps(data) if data else None except Exception as e: print(f"Groq Error: {e}") return None # Serve main page - redirect to signup first @app.route('/') def home(): return send_from_directory(STATIC_DIR, 'signup.html') # Serve dashboard @app.route('/dashboard') @app.route('/index.html') def dashboard(): return send_from_directory(STATIC_DIR, 'index.html') # Serve login page @app.route('/login') @app.route('/login.html') def login(): return send_from_directory(STATIC_DIR, 'login.html') # Serve signup page @app.route('/signup') @app.route('/signup.html') def signup(): return send_from_directory(STATIC_DIR, 'signup.html') # Serve static assets - CSS and JS @app.route('/assets/') def serve_assets(filename): assets_dir = os.path.join(STATIC_DIR, 'assets') return send_from_directory(assets_dir, filename) # Catch-all for any other static files in root @app.route('/') def serve_static(filename): # Check if file exists in static directory file_path = os.path.join(STATIC_DIR, filename) if os.path.exists(file_path): return send_from_directory(STATIC_DIR, filename) # For any non-existent file, redirect to dashboard instead of 404 return send_from_directory(STATIC_DIR, 'index.html') # API Endpoints @app.route('/upload_resume', methods=['POST']) def upload_resume(): session_id = request.headers.get('X-User-Session-Id', str(uuid.uuid4())) session = get_or_create_session(session_id) if 'resume' not in request.files: return jsonify({'error': 'No resume file'}), 400 file = request.files['resume'] text = extract_text_from_pdf(file) if not text.strip(): return jsonify({'error': 'Cannot extract text from PDF'}), 400 prompt = f'''Analyze this resume and extract information. Return JSON with these exact fields: - name: candidate's full name (string) - email: email address (string) - experience: work experience summary as a simple string like "5 years in software development" or "2 roles (3 months, 2 months)" - key_skills: array of skill strings like ["Python", "JavaScript", "React"] - inferred_position: best matching job title (string) Return ONLY valid JSON: {{"name":"","email":"","experience":"","key_skills":[],"inferred_position":""}} Resume text: {text[:8000]}''' resp = generate_content_with_groq(prompt) if resp: profile = json.loads(resp) if not isinstance(profile.get('key_skills'), list): profile['key_skills'] = [] # Ensure experience is a string if isinstance(profile.get('experience'), dict): exp = profile['experience'] profile['experience'] = f"{exp.get('years', '')} years" if 'years' in exp else str(exp) elif isinstance(profile.get('experience'), list): profile['experience'] = ', '.join(str(e) for e in profile['experience']) session['candidate_profile'] = profile return jsonify({'message': 'Success', 'candidate_profile': profile, 'session_id': session_id}), 200 return jsonify({'error': 'AI failed'}), 500 @app.route('/setup_interview', methods=['POST']) def setup_interview(): session_id = request.headers.get('X-User-Session-Id') if not session_id or session_id not in sessions: return jsonify({'error': 'Invalid session'}), 400 session = sessions[session_id] data = request.get_json() position = data.get('position_role', '') profile = session.get('candidate_profile') if not position or not profile: return jsonify({'error': 'Position and profile required'}), 400 skills = ", ".join(profile.get('key_skills', [])) experience = profile.get('experience', 'N/A') # Generate in-depth concept questions based on resume skills prompt = f'''Generate 15 IN-DEPTH interview questions for {profile.get('name','Candidate')} applying for "{position}". Candidate Skills: {skills} Experience: {experience} Generate questions that test DEEP understanding of concepts. Questions should be: - Based on the candidate's actual skills from their resume - Testing theoretical knowledge, architecture decisions, best practices - Asking about real-world scenarios and problem-solving approaches - NO coding/programming challenges - only conceptual questions Question Distribution: - 10 Technical questions (in-depth concepts about their listed skills like {skills}) - 3 Soft Skills questions (teamwork, leadership, conflict resolution) - 2 Communication questions (explaining technical concepts, stakeholder management) Example good technical questions: - "Explain the difference between SQL and NoSQL databases and when you would choose each" - "What are the SOLID principles and how have you applied them?" - "Describe microservices architecture and its trade-offs vs monolithic" - "How does garbage collection work in [language]?" Return JSON: {{"questions":[{{"id":"q1","question":"...","tags":["technical"]}}]}}''' resp = generate_content_with_groq(prompt) if resp: result = json.loads(resp) session['interview_questions'] = result.get('questions', []) session['interview_responses'] = [] session['interview_start_time'] = datetime.now().isoformat() return jsonify({'message': 'Generated', 'questions': result.get('questions', []), 'is_coding_role': False}), 200 return jsonify({'error': 'Failed'}), 500 @app.route('/submit_answer', methods=['POST']) def submit_answer(): session_id = request.headers.get('X-User-Session-Id') if not session_id or session_id not in sessions: return jsonify({'error': 'Invalid session'}), 400 session = sessions[session_id] data = request.get_json() qid = data.get('question_id') answer = data.get('response_text', '') duration = data.get('duration', '00:00') q = next((x for x in session['interview_questions'] if x['id'] == qid), None) if not q: return jsonify({'error': 'Question not found'}), 404 prompt = f'''Evaluate strictly: Q: {q['question']} A: {answer} Return: {{"technicalScore":85,"communicationScore":90,"relevanceScore":88,"feedback":"..."}}''' resp = generate_content_with_groq(prompt) if resp: ev = json.loads(resp) ev['score'] = round((ev.get('technicalScore',0)+ev.get('communicationScore',0)+ev.get('relevanceScore',0))/3) session['interview_responses'].append({'question_id':qid,'question':q['question'],'tags':q.get('tags',[]),'response':answer,'duration':duration,'evaluation':ev}) return jsonify({'message': 'Evaluated', 'evaluation': ev}), 200 return jsonify({'error': 'Failed'}), 500 @app.route('/get_assessment', methods=['GET']) def get_assessment(): session_id = request.headers.get('X-User-Session-Id') if not session_id or session_id not in sessions: return jsonify({'error': 'Invalid session'}), 400 session = sessions[session_id] if not session.get('interview_responses'): return jsonify({'error': 'No responses'}), 400 profile = session['candidate_profile'] responses = session['interview_responses'] avg = sum(r['evaluation']['score'] for r in responses) / len(responses) summary = "\n".join([f"Q: {r['question'][:60]}... Score: {r['evaluation']['score']}%" for r in responses[:5]]) prompt = f'''Assessment for {profile.get('name','Candidate')}. Avg: {avg:.1f}%. Questions: {len(responses)}. {summary} Return: {{"overallScore":85,"recommendation":"Recommended","keyStrengths":["..."],"areasForImprovement":["..."],"detailedScores":{{"technicalSkills":85,"communication":80,"softSkills":78}}}}''' resp = generate_content_with_groq(prompt) if resp: a = json.loads(resp) a['detailedQuestionAnalysis'] = [{'question':r['question'],'score':r['evaluation']['score'],'technicalScore':r['evaluation'].get('technicalScore',0),'communicationScore':r['evaluation'].get('communicationScore',0),'relevanceScore':r['evaluation'].get('relevanceScore',0)} for r in responses] return jsonify({'message': 'Generated', 'assessment': a}), 200 return jsonify({'assessment': {'overallScore': round(avg), 'recommendation': 'Recommended' if avg >= 70 else 'Needs Improvement', 'keyStrengths': [], 'areasForImprovement': [], 'detailedScores': {'technicalSkills': round(avg), 'communication': round(avg), 'softSkills': round(avg)}}}), 200 @app.route('/log_security', methods=['POST']) def log_security(): return jsonify({'message': 'Logged'}), 200 if __name__ == '__main__': port = int(os.environ.get('PORT', 7860)) app.run(host='0.0.0.0', port=port, debug=False)