import os # Ensure Hugging Face writes cache to a safe writable location on Spaces os.environ["HF_HOME"] = "/tmp/huggingface" os.environ["TRANSFORMERS_CACHE"] = "/tmp/huggingface/transformers" os.environ["HUGGINGFACE_HUB_CACHE"] = "/tmp/huggingface/hub" from flask import Flask, render_template, redirect, url_for, flash, request from flask_login import LoginManager, login_required, current_user from werkzeug.utils import secure_filename import sys import json from datetime import datetime # Adjust sys.path for import flexibility current_dir = os.path.dirname(os.path.abspath(__file__)) sys.path.append(current_dir) # Import and initialize DB from backend.models.database import db, Job, Application, init_db from backend.models.user import User from backend.routes.auth import auth_bp from backend.routes.interview_api import interview_api from backend.models.resume_parser.resume_to_features import extract_resume_features # Initialize Flask app safe_instance_path = os.path.abspath("flask_instance") app = Flask( __name__, static_folder='backend/static', static_url_path='/static', template_folder='backend/templates', instance_path=safe_instance_path # ✅ force-safe location ) os.makedirs(safe_instance_path, exist_ok=True) app.config['SECRET_KEY'] = 'your-secret-key' # # Configure the database connection # # By default the application wrote its SQLite database into the # `/tmp` directory. On local machines this works, but on hosted # platforms like Hugging Face Spaces the `/tmp` directory is not # persistent across sessions. That means any data stored in # `/tmp/codingo.db` would be lost once the process restarts, and # newly created user accounts would appear to disappear immediately. # # To fix this we store the database file inside the project under # `backend/instance/codingo.db`. The `backend/instance` directory # already exists (it is created by `os.makedirs` below) and is # persisted across requests, so user registrations and other data # remain available. SQLAlchemy requires three slashes for a relative # SQLite URI (e.g. `sqlite:///relative/path.db`). Here we use four # leading slashes because the path is relative to the project # directory when using `sqlite:///backend/instance/codingo.db`. app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///codingo.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False from flask_wtf.csrf import CSRFProtect # csrf = CSRFProtect(app) # Create necessary directories os.makedirs('static/audio', exist_ok=True) os.makedirs('temp', exist_ok=True) # Initialize DB with app init_db(app) # Flask-Login setup login_manager = LoginManager() login_manager.login_view = 'auth.login' login_manager.init_app(app) @login_manager.user_loader def load_user(user_id): return db.session.get(User, int(user_id)) # Register blueprints app.register_blueprint(auth_bp) app.register_blueprint(interview_api, url_prefix="/api") def handle_resume_upload(file): """Save uploaded file temporarily, extract features, then clean up.""" if not file or file.filename == '': return None, "No file uploaded", None try: filename = secure_filename(file.filename) temp_dir = os.path.join(current_dir, 'temp') os.makedirs(temp_dir, exist_ok=True) filepath = os.path.join(temp_dir, filename) file.save(filepath) features = extract_resume_features(filepath) # Clean up try: os.remove(filepath) except: pass return features, None, filename except Exception as e: print(f"Error in handle_resume_upload: {e}") return None, str(e), None # Routes (keep your existing routes) @app.route('/') def index(): return render_template('index.html') @app.route('/jobs') def jobs(): all_jobs = Job.query.order_by(Job.date_posted.desc()).all() return render_template('jobs.html', jobs=all_jobs) @app.route('/job/') def job_detail(job_id): job = Job.query.get_or_404(job_id) return render_template('job_detail.html', job=job) @app.route('/apply/', methods=['GET', 'POST']) @login_required def apply(job_id): job = Job.query.get_or_404(job_id) if request.method == 'POST': file = request.files.get('resume') features, error, _ = handle_resume_upload(file) if error or not features: flash("Resume parsing failed. Please try again.", "danger") return render_template('apply.html', job=job) application = Application( job_id=job_id, user_id=current_user.id, name=current_user.username, email=current_user.email, extracted_features=json.dumps(features) ) db.session.add(application) db.session.commit() flash('Your application has been submitted successfully!', 'success') return redirect(url_for('jobs')) return render_template('apply.html', job=job) @app.route('/my_applications') @login_required def my_applications(): applications = Application.query.filter_by( user_id=current_user.id ).order_by(Application.date_applied.desc()).all() return render_template('my_applications.html', applications=applications) @app.route('/parse_resume', methods=['POST']) def parse_resume(): file = request.files.get('resume') features, error, _ = handle_resume_upload(file) if error: return {"error": "Error parsing resume. Please try again."}, 400 if not features: return {"error": "Failed to extract resume details."}, 400 response = { "name": features.get('name', ''), "email": features.get('email', ''), "mobile_number": features.get('mobile_number', ''), "skills": features.get('skills', []), "experience": features.get('experience', []), "education": features.get('education', []), "summary": features.get('summary', '') } return response, 200 @app.route("/interview/") @login_required def interview_page(job_id): job = Job.query.get_or_404(job_id) application = Application.query.filter_by( user_id=current_user.id, job_id=job_id ).first() if not application or not application.extracted_features: flash("Please apply for this job and upload your resume first.", "warning") return redirect(url_for('job_detail', job_id=job_id)) cv_data = json.loads(application.extracted_features) return render_template("interview.html", job=job, cv=cv_data) if __name__ == '__main__': print("Starting Codingo application...") with app.app_context(): db.create_all() # Use port from environment or default to 7860 port = int(os.environ.get('PORT', 7860)) app.run(debug=True, host='0.0.0.0', port=port)