husseinelsaadi commited on
Commit
504df0f
·
1 Parent(s): f35697f

Flask integrated and adjusted applying

Browse files
backend/app.py CHANGED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, render_template, redirect, url_for, flash, request
2
+ from werkzeug.utils import secure_filename
3
+ import os
4
+ import sys
5
+ import json
6
+ from datetime import datetime
7
+
8
+ # Add the parent directory to sys.path to help with imports
9
+ current_dir = os.path.dirname(os.path.abspath(__file__))
10
+ parent_dir = os.path.dirname(current_dir)
11
+ sys.path.append(parent_dir)
12
+ sys.path.append(current_dir)
13
+
14
+ # Import local modules with error handling
15
+ try:
16
+ from form.JobApplicationForm import JobApplicationForm
17
+ except ImportError:
18
+ try:
19
+ from backend.form.JobApplicationForm import JobApplicationForm
20
+ except ImportError:
21
+ print("Error importing JobApplicationForm. Check the path.")
22
+ sys.exit(1)
23
+
24
+ try:
25
+ from models.database import db, Job, Application, init_db
26
+ except ImportError:
27
+ try:
28
+ from backend.models.database import db, Job, Application, init_db
29
+ except ImportError:
30
+ print("Error importing database models. Check the path.")
31
+ sys.exit(1)
32
+
33
+ try:
34
+ from models.resume_parser.resume_to_features import extract_resume_features
35
+ except ImportError:
36
+ try:
37
+ from backend.models.resume_parser.resume_to_features import extract_resume_features
38
+ except ImportError:
39
+ print("Error importing resume_to_features. Check if the function is defined in the module.")
40
+ sys.exit(1)
41
+
42
+ app = Flask(__name__)
43
+ app.config['SECRET_KEY'] = 'your-secret-key'
44
+ app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///codingo.db'
45
+ app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
46
+ app.config['UPLOAD_FOLDER'] = 'uploads/resumes'
47
+
48
+ # Create upload folder if it doesn't exist
49
+ os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
50
+
51
+ # Initialize the database with the app
52
+ init_db(app)
53
+
54
+ # Routes
55
+ @app.route('/')
56
+ def index():
57
+ return render_template('index.html')
58
+
59
+
60
+ @app.route('/jobs')
61
+ def jobs():
62
+ all_jobs = Job.query.order_by(Job.date_posted.desc()).all()
63
+ return render_template('jobs.html', jobs=all_jobs)
64
+
65
+
66
+ @app.route('/job/<int:job_id>')
67
+ def job_detail(job_id):
68
+ job = Job.query.get_or_404(job_id)
69
+ return render_template('job_detail.html', job=job)
70
+
71
+
72
+ @app.route('/apply/<int:job_id>', methods=['GET', 'POST'])
73
+ def apply(job_id):
74
+ job = Job.query.get_or_404(job_id)
75
+ form = JobApplicationForm()
76
+ form.job_id.data = job_id
77
+
78
+ if form.validate_on_submit():
79
+ # Save resume file
80
+ resume_file = form.resume.data
81
+ filename = secure_filename(
82
+ f"{form.name.data.replace(' ', '_')}_{datetime.now().strftime('%Y%m%d%H%M%S')}.{resume_file.filename.split('.')[-1]}")
83
+ resume_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
84
+ resume_file.save(resume_path)
85
+
86
+ # Extract features from resume
87
+ try:
88
+ features = extract_resume_features(resume_path)
89
+ features_json = json.dumps(features)
90
+ except Exception as e:
91
+ print(f"Error extracting features: {e}")
92
+ features_json = "{}"
93
+
94
+ # Create new application
95
+ application = Application(
96
+ job_id=job_id,
97
+ name=form.name.data,
98
+ email=form.email.data,
99
+ resume_path=resume_path,
100
+ cover_letter=form.cover_letter.data,
101
+ extracted_features=features_json
102
+ )
103
+
104
+ db.session.add(application)
105
+ db.session.commit()
106
+
107
+ flash('Your application has been submitted successfully!', 'success')
108
+ return redirect(url_for('jobs'))
109
+
110
+ return render_template('apply.html', form=form, job=job)
111
+
112
+
113
+ @app.route('/parse_resume', methods=['POST'])
114
+ def parse_resume():
115
+ if 'resume' not in request.files:
116
+ return {"error": "No file uploaded"}, 400
117
+
118
+ file = request.files['resume']
119
+ if file.filename == '':
120
+ return {"error": "No selected file"}, 400
121
+
122
+ filename = secure_filename(file.filename)
123
+ file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
124
+ file.save(file_path)
125
+
126
+ # Extract features from resume
127
+ try:
128
+ features = extract_resume_features(file_path)
129
+ response = {
130
+ "name": features.get('name', ''),
131
+ "email": features.get('email', ''),
132
+ "mobile_number": features.get('mobile_number', ''),
133
+ "skills": features.get('skills', []),
134
+ "experience": features.get('experience', [])
135
+ }
136
+ return response, 200
137
+ except Exception as e:
138
+ print(f"Error parsing resume: {e}")
139
+ return {"error": "Failed to parse resume"}, 500
140
+
141
+
142
+ if __name__ == '__main__':
143
+ print("Starting Codingo application...")
144
+ app.run(debug=True)
backend/form/JobApplicationForm.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ WTForms for the Codingo application.
3
+ """
4
+
5
+ from flask_wtf import FlaskForm
6
+ from flask_wtf.file import FileField, FileAllowed, FileRequired
7
+ from wtforms import StringField, TextAreaField, SubmitField, HiddenField, SelectField
8
+ from wtforms.validators import DataRequired, Email, Length
9
+
10
+
11
+ class JobApplicationForm(FlaskForm):
12
+ """Form for submitting a job application."""
13
+ name = StringField('Full Name', validators=[
14
+ DataRequired(),
15
+ Length(min=2, max=100, message="Name must be between 2 and 100 characters.")
16
+ ])
17
+
18
+ email = StringField('Email', validators=[
19
+ DataRequired(),
20
+ Email(message="Please enter a valid email address.")
21
+ ])
22
+
23
+ resume = FileField('Upload Resume', validators=[
24
+ FileRequired(message="Please upload your resume."),
25
+ FileAllowed(['pdf', 'docx'], 'Only PDF and DOCX files are allowed!')
26
+ ])
27
+
28
+ cover_letter = TextAreaField('Cover Letter', validators=[
29
+ DataRequired(),
30
+ Length(min=50, message="Cover letter should be at least 50 characters.")
31
+ ])
32
+
33
+ job_id = HiddenField('Job ID')
34
+
35
+ submit = SubmitField('Submit Application')
36
+
37
+ # Add more forms as needed for future features
backend/instance/codingo.db ADDED
Binary file (12.3 kB). View file
 
backend/model/resume-parser/resume_to_features.py DELETED
@@ -1,10 +0,0 @@
1
- import os
2
- from pyresparser import ResumeParser
3
-
4
- # Build absolute path to the resume file
5
- current_dir = os.path.dirname(os.path.abspath(__file__))
6
- resume_path = os.path.join(current_dir, '../../../data/resumes/Hussein El Saadi - CV.pdf')
7
-
8
- # Parse and print the extracted data
9
- data = ResumeParser(resume_path).get_extracted_data()
10
- print(data)
 
 
 
 
 
 
 
 
 
 
 
backend/models/database.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Database models for the Codingo application.
3
+ """
4
+
5
+ from flask_sqlalchemy import SQLAlchemy
6
+ from datetime import datetime
7
+
8
+ db = SQLAlchemy()
9
+
10
+
11
+ class Job(db.Model):
12
+ """Job model representing a job posting."""
13
+ id = db.Column(db.Integer, primary_key=True)
14
+ title = db.Column(db.String(100), nullable=False)
15
+ company = db.Column(db.String(100), nullable=False)
16
+ location = db.Column(db.String(100), nullable=False)
17
+ description = db.Column(db.Text, nullable=False)
18
+ requirements = db.Column(db.Text, nullable=False)
19
+ date_posted = db.Column(db.DateTime, default=datetime.utcnow)
20
+
21
+ # Relationships
22
+ applications = db.relationship('Application', backref='job', lazy=True, cascade="all, delete-orphan")
23
+
24
+ def __repr__(self):
25
+ return f"Job('{self.title}', '{self.company}')"
26
+
27
+
28
+ class Application(db.Model):
29
+ """Application model representing a job application."""
30
+ id = db.Column(db.Integer, primary_key=True)
31
+ job_id = db.Column(db.Integer, db.ForeignKey('job.id'), nullable=False)
32
+ name = db.Column(db.String(100), nullable=False)
33
+ email = db.Column(db.String(100), nullable=False)
34
+ resume_path = db.Column(db.String(255), nullable=False)
35
+ cover_letter = db.Column(db.Text, nullable=False)
36
+ extracted_features = db.Column(db.Text, nullable=True) # JSON string of extracted features
37
+ status = db.Column(db.String(50), default='applied') # applied, reviewed, shortlisted, rejected, hired
38
+ date_applied = db.Column(db.DateTime, default=datetime.utcnow)
39
+
40
+ def __repr__(self):
41
+ return f"Application('{self.name}', '{self.email}', Job ID: {self.job_id})"
42
+
43
+
44
+ # Initialize the database
45
+ def init_db(app):
46
+ """Initialize the database with the Flask app."""
47
+ db.init_app(app)
48
+
49
+ with app.app_context():
50
+ db.create_all()
51
+
52
+ # Add sample data if jobs table is empty
53
+ if Job.query.count() == 0:
54
+ sample_jobs = [
55
+ {
56
+ 'title': 'Senior Python Developer',
57
+ 'company': 'TechCorp',
58
+ 'location': 'Remote',
59
+ 'description': 'We are looking for an experienced Python developer to join our team. You will be responsible for developing and maintaining our backend services.',
60
+ 'requirements': 'Strong Python skills, experience with Flask or Django, knowledge of SQL and NoSQL databases, 5+ years of experience.'
61
+ },
62
+ {
63
+ 'title': 'Data Scientist',
64
+ 'company': 'DataInsights',
65
+ 'location': 'New York, NY',
66
+ 'description': 'Join our data science team to develop machine learning models and extract insights from large datasets.',
67
+ 'requirements': 'Masters or PhD in Computer Science, Statistics or related field. Experience with Python, PyTorch, and scikit-learn.'
68
+ },
69
+ {
70
+ 'title': 'Frontend Developer',
71
+ 'company': 'WebSolutions',
72
+ 'location': 'San Francisco, CA',
73
+ 'description': 'Develop responsive and user-friendly web applications with modern JavaScript frameworks.',
74
+ 'requirements': 'Proficiency in HTML, CSS, JavaScript, and React.js. Experience with RESTful APIs and version control systems.'
75
+ }
76
+ ]
77
+
78
+ for job_data in sample_jobs:
79
+ job = Job(**job_data)
80
+ db.session.add(job)
81
+
82
+ db.session.commit()
backend/models/resume_parser/resume_to_features.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from pyresparser import ResumeParser
3
+
4
+
5
+ def extract_resume_features(resume_path):
6
+ """
7
+ Extract features from a resume file.
8
+
9
+ Args:
10
+ resume_path (str): Path to the resume file
11
+
12
+ Returns:
13
+ dict: Dictionary containing extracted features from resume
14
+ """
15
+ try:
16
+ data = ResumeParser(resume_path).get_extracted_data()
17
+ return data
18
+ except Exception as e:
19
+ print(f"Error parsing resume: {e}")
20
+ return {
21
+ 'name': '',
22
+ 'email': '',
23
+ 'mobile_number': '',
24
+ 'skills': [],
25
+ 'experience': [],
26
+ 'no_of_pages': 0,
27
+ 'total_experience': 0
28
+ }
29
+
30
+
31
+ # Example usage (will run if script is executed directly)
32
+ if __name__ == "__main__":
33
+ # Build absolute path to the resume file
34
+ current_dir = os.path.dirname(os.path.abspath(__file__))
35
+ resume_path = os.path.join(current_dir, '../../../data/resumes/Hussein El Saadi - CV.pdf')
36
+
37
+ # Parse and print the extracted data
38
+ data = extract_resume_features(resume_path)
39
+ print(data)
backend/templates/apply.html ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Apply for {{ job.title }} - Codingo{% endblock %}
4
+
5
+ {% block hero %}
6
+ <section class="hero" style="padding: 3rem 1rem;">
7
+ <div class="container">
8
+ <div class="hero-content">
9
+ <h1>Apply for {{ job.title }}</h1>
10
+ <p>{{ job.company }} • {{ job.location }}</p>
11
+ </div>
12
+ </div>
13
+ </section>
14
+ {% endblock %}
15
+
16
+ {% block content %}
17
+ <section class="content-section">
18
+ <ul class="breadcrumbs">
19
+ <li><a href="{{ url_for('index') }}">Home</a></li>
20
+ <li><a href="{{ url_for('jobs') }}">Jobs</a></li>
21
+ <li><a href="{{ url_for('job_detail', job_id=job.id) }}">{{ job.title }}</a></li>
22
+ <li>Apply</li>
23
+ </ul>
24
+
25
+ <div class="card">
26
+ <div class="card-header">
27
+ <h2>Complete Your Application</h2>
28
+ <p>Upload your resume to auto-fill fields or complete the form manually</p>
29
+ </div>
30
+
31
+ <div class="card-body">
32
+ <form id="resumeForm" method="POST" enctype="multipart/form-data">
33
+ <div class="form-group">
34
+ <label for="resume">Upload Resume</label>
35
+ <input type="file" id="resume" name="resume" class="form-control" required>
36
+ </div>
37
+ </form>
38
+
39
+ <form method="POST" enctype="multipart/form-data">
40
+ {{ form.hidden_tag() }}
41
+ {{ form.job_id }}
42
+
43
+ <div class="form-group">
44
+ {{ form.name.label }}
45
+ {{ form.name(class="form-control", id="name", placeholder="Enter your full name") }}
46
+ </div>
47
+
48
+ <div class="form-group">
49
+ {{ form.email.label }}
50
+ {{ form.email(class="form-control", id="email", placeholder="Enter your email address") }}
51
+ </div>
52
+
53
+ <div class="form-group">
54
+ <label for="phone">Phone Number</label>
55
+ <input type="text" id="phone" class="form-control" placeholder="Enter your phone number">
56
+ </div>
57
+
58
+ <div class="form-group">
59
+ <label for="skills">Skills</label>
60
+ <textarea id="skills" class="form-control" placeholder="Skills extracted from your resume..."></textarea>
61
+ </div>
62
+
63
+ <div class="form-group">
64
+ <label for="experience">Previous Experience</label>
65
+ <textarea id="experience" class="form-control" placeholder="Experience extracted from your resume..."></textarea>
66
+ </div>
67
+
68
+ <div class="form-group">
69
+ {{ form.cover_letter.label }}
70
+ {{ form.cover_letter(class="form-control", placeholder="Enter your cover letter here...") }}
71
+ </div>
72
+
73
+ <div style="display: flex; justify-content: space-between; align-items: center; margin-top: 2rem;">
74
+ <a href="{{ url_for('job_detail', job_id=job.id) }}" class="btn btn-outline">Cancel</a>
75
+ {{ form.submit(class="btn btn-primary") }}
76
+ </div>
77
+ </form>
78
+ </div>
79
+ </div>
80
+ </section>
81
+
82
+ <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
83
+ <script>
84
+ $(document).ready(function() {
85
+ $("#resume").on("change", function () {
86
+ var formData = new FormData();
87
+ formData.append("resume", $("#resume")[0].files[0]);
88
+
89
+ $.ajax({
90
+ url: "{{ url_for('parse_resume') }}",
91
+ type: "POST",
92
+ data: formData,
93
+ processData: false,
94
+ contentType: false,
95
+ success: function (data) {
96
+ $("#name").val(data.name);
97
+ $("#email").val(data.email);
98
+ $("#phone").val(data.mobile_number);
99
+ $("#skills").val(data.skills.join(", "));
100
+ $("#experience").val(data.experience.join("\n"));
101
+ },
102
+ error: function () {
103
+ alert("Error parsing the resume. Please try again.");
104
+ }
105
+ });
106
+ });
107
+ });
108
+ </script>
109
+ {% endblock %}
backend/templates/base.html ADDED
@@ -0,0 +1,731 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>{% block title %}Codingo - AI-Powered Recruitment Platform{% endblock %}</title>
7
+ <style>
8
+ :root {
9
+ --primary: #4361ee;
10
+ --secondary: #3a0ca3;
11
+ --accent: #4cc9f0;
12
+ --light: #f8f9fa;
13
+ --dark: #212529;
14
+ --success: #2ecc71;
15
+ --warning: #f39c12;
16
+ --danger: #e74c3c;
17
+ }
18
+
19
+ * {
20
+ margin: 0;
21
+ padding: 0;
22
+ box-sizing: border-box;
23
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
24
+ }
25
+
26
+ body {
27
+ background-color: var(--light);
28
+ color: var(--dark);
29
+ line-height: 1.6;
30
+ perspective: 1000px;
31
+ overflow-x: hidden;
32
+ }
33
+
34
+ header {
35
+ background: linear-gradient(135deg, var(--primary), var(--secondary));
36
+ color: white;
37
+ padding: 1rem;
38
+ position: sticky;
39
+ top: 0;
40
+ z-index: 100;
41
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
42
+ }
43
+
44
+ .container {
45
+ width: 100%;
46
+ max-width: 1200px;
47
+ margin: 0 auto;
48
+ padding: 0 1rem;
49
+ }
50
+
51
+ .nav-container {
52
+ display: flex;
53
+ justify-content: space-between;
54
+ align-items: center;
55
+ }
56
+
57
+ .logo {
58
+ display: flex;
59
+ align-items: center;
60
+ font-size: 2rem;
61
+ font-weight: bold;
62
+ color: white;
63
+ text-decoration: none;
64
+ transition: transform 0.3s ease;
65
+ }
66
+
67
+ .logo:hover {
68
+ transform: scale(1.05);
69
+ }
70
+
71
+ .logo-part1 {
72
+ color: white;
73
+ }
74
+
75
+ .logo-part2 {
76
+ color: #FF8C42;
77
+ font-style: italic;
78
+ text-shadow: 0px 0px 10px rgba(255, 140, 66, 0.7);
79
+ animation: glow 2s ease-in-out infinite alternate;
80
+ }
81
+
82
+ @keyframes glow {
83
+ from {
84
+ text-shadow: 0 0 5px rgba(255, 140, 66, 0.7);
85
+ }
86
+ to {
87
+ text-shadow: 0 0 15px rgba(255, 140, 66, 1), 0 0 20px rgba(255, 140, 66, 0.8);
88
+ }
89
+ }
90
+
91
+ .logo-part3 {
92
+ color: var(--accent);
93
+ }
94
+
95
+ .login-buttons {
96
+ display: flex;
97
+ gap: 1rem;
98
+ }
99
+
100
+ .btn {
101
+ padding: 0.5rem 1.5rem;
102
+ border-radius: 5px;
103
+ border: none;
104
+ cursor: pointer;
105
+ font-weight: 500;
106
+ transition: all 0.3s ease;
107
+ text-decoration: none;
108
+ position: relative;
109
+ overflow: hidden;
110
+ z-index: 1;
111
+ }
112
+
113
+ .btn::before {
114
+ content: '';
115
+ position: absolute;
116
+ top: 0;
117
+ left: 0;
118
+ width: 0%;
119
+ height: 100%;
120
+ background-color: rgba(255, 255, 255, 0.1);
121
+ transition: all 0.3s ease;
122
+ z-index: -1;
123
+ }
124
+
125
+ .btn:hover::before {
126
+ width: 100%;
127
+ }
128
+
129
+ .btn-primary {
130
+ background-color: var(--accent);
131
+ color: var(--dark);
132
+ box-shadow: 0 4px 8px rgba(76, 201, 240, 0.3);
133
+ }
134
+
135
+ .btn-outline {
136
+ background-color: transparent;
137
+ border: 2px solid white;
138
+ color: white;
139
+ }
140
+
141
+ .btn:hover {
142
+ transform: translateY(-2px);
143
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
144
+ }
145
+
146
+ .hero {
147
+ background: linear-gradient(rgba(67, 97, 238, 0.8), rgba(58, 12, 163, 0.9)), url("/api/placeholder/1200/600") no-repeat center center/cover;
148
+ color: white;
149
+ text-align: center;
150
+ padding: 5rem 1rem;
151
+ position: relative;
152
+ overflow: hidden;
153
+ }
154
+
155
+ .hero::before {
156
+ content: '';
157
+ position: absolute;
158
+ top: -50%;
159
+ left: -50%;
160
+ width: 200%;
161
+ height: 200%;
162
+ background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0) 60%);
163
+ animation: rotate 30s linear infinite;
164
+ z-index: 1;
165
+ }
166
+
167
+ @keyframes rotate {
168
+ from {
169
+ transform: rotate(0deg);
170
+ }
171
+ to {
172
+ transform: rotate(360deg);
173
+ }
174
+ }
175
+
176
+ .hero-content {
177
+ display: flex;
178
+ flex-direction: column;
179
+ align-items: center;
180
+ transform-style: preserve-3d;
181
+ transition: transform 0.3s ease;
182
+ position: relative;
183
+ z-index: 2;
184
+ }
185
+
186
+ .hero h1 {
187
+ font-size: 3rem;
188
+ margin-bottom: 1.5rem;
189
+ font-weight: 700;
190
+ text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
191
+ transform: translateZ(20px);
192
+ animation: fadeIn 1s ease-out;
193
+ }
194
+
195
+ @keyframes fadeIn {
196
+ from {
197
+ opacity: 0;
198
+ transform: translateY(20px) translateZ(20px);
199
+ }
200
+ to {
201
+ opacity: 1;
202
+ transform: translateY(0) translateZ(20px);
203
+ }
204
+ }
205
+
206
+ .hero p {
207
+ font-size: 1.2rem;
208
+ max-width: 800px;
209
+ margin: 0 auto 2rem;
210
+ transform: translateZ(10px);
211
+ animation: fadeIn 1s ease-out 0.3s both;
212
+ }
213
+
214
+ .luna-avatar-container {
215
+ position: relative;
216
+ width: 250px;
217
+ height: 250px;
218
+ margin-bottom: 2rem;
219
+ perspective: 1000px;
220
+ }
221
+
222
+ .luna-avatar {
223
+ width: 100%;
224
+ height: 100%;
225
+ border-radius: 50%;
226
+ border: 4px solid var(--accent);
227
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3), 0 0 20px rgba(76, 201, 240, 0.5);
228
+ overflow: hidden;
229
+ animation: float 4s ease-in-out infinite;
230
+ position: relative;
231
+ z-index: 2;
232
+ background: linear-gradient(135deg, rgba(76, 201, 240, 0.2), rgba(58, 12, 163, 0.2));
233
+ display: flex;
234
+ justify-content: center;
235
+ align-items: center;
236
+ }
237
+
238
+ .luna-glow {
239
+ position: absolute;
240
+ width: 100%;
241
+ height: 100%;
242
+ border-radius: 50%;
243
+ background: radial-gradient(circle, rgba(76, 201, 240, 0.8) 0%, rgba(76, 201, 240, 0) 70%);
244
+ filter: blur(15px);
245
+ opacity: 0.7;
246
+ z-index: 1;
247
+ animation: pulse 4s ease-in-out infinite alternate;
248
+ }
249
+
250
+ @keyframes pulse {
251
+ 0% { transform: scale(0.9); opacity: 0.5; }
252
+ 100% { transform: scale(1.1); opacity: 0.7; }
253
+ }
254
+
255
+ @keyframes float {
256
+ 0% { transform: translateY(0px) rotateY(0deg); }
257
+ 50% { transform: translateY(-15px) rotateY(5deg); }
258
+ 100% { transform: translateY(0px) rotateY(0deg); }
259
+ }
260
+
261
+ .luna-avatar img {
262
+ width: 100%;
263
+ height: 100%;
264
+ object-fit: cover;
265
+ object-position: center -10px;
266
+ top: 0;
267
+ left: 0;
268
+ }
269
+
270
+ .hero-buttons {
271
+ display: flex;
272
+ justify-content: center;
273
+ gap: 1.5rem;
274
+ flex-wrap: wrap;
275
+ transform: translateZ(15px);
276
+ animation: fadeIn 1s ease-out 0.6s both;
277
+ }
278
+
279
+ .features {
280
+ padding: 5rem 1rem;
281
+ background-color: white;
282
+ position: relative;
283
+ z-index: 1;
284
+ }
285
+
286
+ .section-title {
287
+ text-align: center;
288
+ margin-bottom: 3rem;
289
+ transform-style: preserve-3d;
290
+ transition: transform 0.3s ease;
291
+ }
292
+
293
+ .section-title h2 {
294
+ font-size: 2.5rem;
295
+ color: var(--primary);
296
+ margin-bottom: 1rem;
297
+ position: relative;
298
+ display: inline-block;
299
+ }
300
+
301
+ .section-title h2::after {
302
+ content: '';
303
+ position: absolute;
304
+ bottom: -10px;
305
+ left: 50%;
306
+ transform: translateX(-50%);
307
+ width: 80px;
308
+ height: 3px;
309
+ background: linear-gradient(to right, var(--primary), var(--accent));
310
+ }
311
+
312
+ .section-title p {
313
+ max-width: 600px;
314
+ margin: 0 auto;
315
+ color: #666;
316
+ }
317
+
318
+ .features-grid {
319
+ display: grid;
320
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
321
+ gap: 2rem;
322
+ }
323
+
324
+ .feature-card {
325
+ background-color: white;
326
+ border-radius: 10px;
327
+ overflow: hidden;
328
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
329
+ transition: all 0.5s ease;
330
+ display: flex;
331
+ flex-direction: column;
332
+ height: 100%;
333
+ transform-style: preserve-3d;
334
+ perspective: 1000px;
335
+ }
336
+
337
+ .feature-card:hover {
338
+ transform: translateY(-10px) rotateX(5deg);
339
+ box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1);
340
+ }
341
+
342
+ .feature-icon {
343
+ background: linear-gradient(135deg, rgba(67, 97, 238, 0.1), rgba(76, 201, 240, 0.1));
344
+ padding: 2rem;
345
+ display: flex;
346
+ justify-content: center;
347
+ align-items: center;
348
+ font-size: 2.5rem;
349
+ color: var(--primary);
350
+ transition: all 0.3s ease;
351
+ }
352
+
353
+ .feature-card:hover .feature-icon {
354
+ transform: translateZ(20px);
355
+ color: var(--accent);
356
+ }
357
+
358
+ .feature-content {
359
+ padding: 1.5rem;
360
+ flex-grow: 1;
361
+ transform: translateZ(0);
362
+ transition: transform 0.3s ease;
363
+ }
364
+
365
+ .feature-card:hover .feature-content {
366
+ transform: translateZ(10px);
367
+ }
368
+
369
+ .feature-content h3 {
370
+ font-size: 1.5rem;
371
+ margin-bottom: 1rem;
372
+ color: var(--dark);
373
+ position: relative;
374
+ display: inline-block;
375
+ }
376
+
377
+ .feature-content h3::after {
378
+ content: '';
379
+ position: absolute;
380
+ bottom: -5px;
381
+ left: 0;
382
+ width: 40px;
383
+ height: 2px;
384
+ background: var(--primary);
385
+ transition: width 0.3s ease;
386
+ }
387
+
388
+ .feature-card:hover .feature-content h3::after {
389
+ width: 100%;
390
+ }
391
+
392
+ .content-section {
393
+ padding: 5rem 1rem;
394
+ background-color: white;
395
+ min-height: 50vh;
396
+ }
397
+
398
+ .cta {
399
+ background: linear-gradient(135deg, var(--secondary), var(--primary));
400
+ color: white;
401
+ padding: 5rem 1rem;
402
+ text-align: center;
403
+ position: relative;
404
+ overflow: hidden;
405
+ }
406
+
407
+ .cta::before {
408
+ content: '';
409
+ position: absolute;
410
+ top: -50%;
411
+ left: -50%;
412
+ width: 200%;
413
+ height: 200%;
414
+ background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0) 60%);
415
+ animation: rotate 20s linear infinite;
416
+ z-index: 1;
417
+ }
418
+
419
+ .cta .container {
420
+ position: relative;
421
+ z-index: 2;
422
+ }
423
+
424
+ .cta h2 {
425
+ font-size: 2.5rem;
426
+ margin-bottom: 1.5rem;
427
+ text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
428
+ }
429
+
430
+ .cta p {
431
+ max-width: 600px;
432
+ margin: 0 auto 2rem;
433
+ font-size: 1.2rem;
434
+ }
435
+
436
+ footer {
437
+ background-color: var(--dark);
438
+ color: white;
439
+ padding: 3rem 1rem;
440
+ }
441
+
442
+ .footer-grid {
443
+ display: grid;
444
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
445
+ gap: 2rem;
446
+ }
447
+
448
+ .footer-col h3 {
449
+ font-size: 1.2rem;
450
+ margin-bottom: 1.5rem;
451
+ color: var(--accent);
452
+ position: relative;
453
+ display: inline-block;
454
+ }
455
+
456
+ .footer-col h3::after {
457
+ content: '';
458
+ position: absolute;
459
+ bottom: -8px;
460
+ left: 0;
461
+ width: 30px;
462
+ height: 2px;
463
+ background: var(--accent);
464
+ }
465
+
466
+ .social-links {
467
+ display: flex;
468
+ gap: 1rem;
469
+ margin-top: 1rem;
470
+ }
471
+
472
+ .social-links a {
473
+ color: white;
474
+ background-color: rgba(255, 255, 255, 0.1);
475
+ width: 40px;
476
+ height: 40px;
477
+ border-radius: 50%;
478
+ display: flex;
479
+ justify-content: center;
480
+ align-items: center;
481
+ transition: all 0.3s ease;
482
+ }
483
+
484
+ .social-links a:hover {
485
+ background-color: var(--accent);
486
+ color: var(--dark);
487
+ transform: translateY(-3px);
488
+ }
489
+
490
+ .copyright {
491
+ margin-top: 2rem;
492
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
493
+ padding-top: 1.5rem;
494
+ text-align: center;
495
+ color: #aaa;
496
+ }
497
+
498
+ .card {
499
+ background-color: white;
500
+ border-radius: 10px;
501
+ overflow: hidden;
502
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
503
+ transition: all 0.5s ease;
504
+ margin-bottom: 2rem;
505
+ }
506
+
507
+ .card:hover {
508
+ transform: translateY(-5px);
509
+ box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1);
510
+ }
511
+
512
+ .card-header {
513
+ background: linear-gradient(135deg, var(--primary), var(--secondary));
514
+ color: white;
515
+ padding: 1.5rem;
516
+ }
517
+
518
+ .card-body {
519
+ padding: 1.5rem;
520
+ }
521
+
522
+ .job-grid {
523
+ display: grid;
524
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
525
+ gap: 2rem;
526
+ }
527
+
528
+ .job-card {
529
+ background-color: white;
530
+ border-radius: 10px;
531
+ overflow: hidden;
532
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
533
+ transition: all 0.5s ease;
534
+ height: 100%;
535
+ display: flex;
536
+ flex-direction: column;
537
+ }
538
+
539
+ .job-card:hover {
540
+ transform: translateY(-10px);
541
+ box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1);
542
+ }
543
+
544
+ .job-header {
545
+ background: linear-gradient(135deg, rgba(67, 97, 238, 0.9), rgba(58, 12, 163, 0.9));
546
+ color: white;
547
+ padding: 1.5rem;
548
+ }
549
+
550
+ .job-header h3 {
551
+ font-size: 1.5rem;
552
+ margin-bottom: 0.5rem;
553
+ }
554
+
555
+ .job-info {
556
+ display: flex;
557
+ justify-content: space-between;
558
+ font-size: 0.9rem;
559
+ opacity: 0.9;
560
+ }
561
+
562
+ .job-body {
563
+ padding: 1.5rem;
564
+ flex-grow: 1;
565
+ }
566
+
567
+ .job-description {
568
+ margin-bottom: 1.5rem;
569
+ }
570
+
571
+ .job-footer {
572
+ padding: 1rem 1.5rem;
573
+ background-color: rgba(0, 0, 0, 0.03);
574
+ display: flex;
575
+ justify-content: flex-end;
576
+ }
577
+
578
+ .form-group {
579
+ margin-bottom: 1.5rem;
580
+ }
581
+
582
+ .form-group label {
583
+ display: block;
584
+ margin-bottom: 0.5rem;
585
+ font-weight: 500;
586
+ }
587
+
588
+ .form-control {
589
+ width: 100%;
590
+ padding: 0.75rem;
591
+ border: 1px solid #ddd;
592
+ border-radius: 5px;
593
+ font-size: 1rem;
594
+ }
595
+
596
+ .form-control:focus {
597
+ border-color: var(--primary);
598
+ outline: none;
599
+ box-shadow: 0 0 0 3px rgba(67, 97, 238, 0.2);
600
+ }
601
+
602
+ textarea.form-control {
603
+ min-height: 150px;
604
+ }
605
+
606
+ .flash-messages {
607
+ margin: 1rem 0;
608
+ }
609
+
610
+ .alert {
611
+ padding: 1rem;
612
+ border-radius: 5px;
613
+ margin-bottom: 1rem;
614
+ }
615
+
616
+ .alert-success {
617
+ background-color: rgba(46, 204, 113, 0.2);
618
+ border: 1px solid var(--success);
619
+ color: #27ae60;
620
+ }
621
+
622
+ .alert-danger {
623
+ background-color: rgba(231, 76, 60, 0.2);
624
+ border: 1px solid var(--danger);
625
+ color: #c0392b;
626
+ }
627
+
628
+ /* Breadcrumbs */
629
+ .breadcrumbs {
630
+ display: flex;
631
+ padding: 1rem 0;
632
+ margin-bottom: 1rem;
633
+ list-style: none;
634
+ }
635
+
636
+ .breadcrumbs li {
637
+ display: flex;
638
+ align-items: center;
639
+ }
640
+
641
+ .breadcrumbs li:not(:last-child)::after {
642
+ content: '/';
643
+ margin: 0 0.5rem;
644
+ color: #aaa;
645
+ }
646
+
647
+ .breadcrumbs a {
648
+ color: var(--primary);
649
+ text-decoration: none;
650
+ }
651
+
652
+ .breadcrumbs a:hover {
653
+ text-decoration: underline;
654
+ }
655
+
656
+ @media screen and (max-width: 768px) {
657
+ .nav-container {
658
+ flex-direction: row;
659
+ justify-content: space-between;
660
+ }
661
+
662
+ .luna-avatar-container {
663
+ width: 180px;
664
+ height: 180px;
665
+ }
666
+
667
+ .hero h1 {
668
+ font-size: 2rem;
669
+ }
670
+
671
+ .hero p {
672
+ font-size: 1rem;
673
+ }
674
+
675
+ .job-grid {
676
+ grid-template-columns: 1fr;
677
+ }
678
+ }
679
+ </style>
680
+ </head>
681
+ <body>
682
+ <header>
683
+ <div class="container nav-container">
684
+ <a href="{{ url_for('index') }}" class="logo">
685
+ <span class="logo-part1">Cod</span><span class="logo-part2">in</span><span class="logo-part3">go</span>
686
+ </a>
687
+ <div class="login-buttons">
688
+ <a href="{{ url_for('index') }}" class="btn btn-outline">Log In</a>
689
+ <a href="{{ url_for('index') }}" class="btn btn-primary">Sign Up</a>
690
+ </div>
691
+ </div>
692
+ </header>
693
+
694
+ {% block hero %}{% endblock %}
695
+
696
+ <main>
697
+ <div class="container">
698
+ {% with messages = get_flashed_messages(with_categories=true) %}
699
+ {% if messages %}
700
+ <div class="flash-messages">
701
+ {% for category, message in messages %}
702
+ <div class="alert alert-{{ category }}">{{ message }}</div>
703
+ {% endfor %}
704
+ </div>
705
+ {% endif %}
706
+ {% endwith %}
707
+
708
+ {% block content %}{% endblock %}
709
+ </div>
710
+ </main>
711
+
712
+ <footer>
713
+ <div class="container">
714
+ <div class="footer-grid">
715
+ <div class="footer-col">
716
+ <h3>Codingo</h3>
717
+ <p>AI-powered recruitment platform that revolutionizes how companies hire technical talent.</p>
718
+ <div class="social-links">
719
+ <a href="#"><span>f</span></a>
720
+ <a href="#"><span>t</span></a>
721
+ <a href="#"><span>in</span></a>
722
+ </div>
723
+ </div>
724
+ </div>
725
+ <div class="copyright">
726
+ <p>&copy; 2025 Codingo. All rights reserved.</p>
727
+ </div>
728
+ </div>
729
+ </footer>
730
+ </body>
731
+ </html>
backend/templates/index.html CHANGED
@@ -1,585 +1,64 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Codingo - AI-Powered Recruitment Platform</title>
7
- <style>
8
- :root {
9
- --primary: #4361ee;
10
- --secondary: #3a0ca3;
11
- --accent: #4cc9f0;
12
- --light: #f8f9fa;
13
- --dark: #212529;
14
- --success: #2ecc71;
15
- --warning: #f39c12;
16
- --danger: #e74c3c;
17
- }
18
 
19
- * {
20
- margin: 0;
21
- padding: 0;
22
- box-sizing: border-box;
23
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
24
- }
25
-
26
- body {
27
- background-color: var(--light);
28
- color: var(--dark);
29
- line-height: 1.6;
30
- perspective: 1000px;
31
- overflow-x: hidden;
32
- }
33
-
34
- header {
35
- background: linear-gradient(135deg, var(--primary), var(--secondary));
36
- color: white;
37
- padding: 1rem;
38
- position: sticky;
39
- top: 0;
40
- z-index: 100;
41
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
42
- }
43
-
44
- .container {
45
- width: 100%;
46
- max-width: 1200px;
47
- margin: 0 auto;
48
- padding: 0 1rem;
49
- }
50
-
51
- .nav-container {
52
- display: flex;
53
- justify-content: space-between;
54
- align-items: center;
55
- }
56
-
57
- .logo {
58
- display: flex;
59
- align-items: center;
60
- font-size: 2rem;
61
- font-weight: bold;
62
- color: white;
63
- text-decoration: none;
64
- transition: transform 0.3s ease;
65
- }
66
-
67
- .logo:hover {
68
- transform: scale(1.05);
69
- }
70
-
71
- .logo-part1 {
72
- color: white;
73
- }
74
-
75
- .logo-part2 {
76
- color: #FF8C42;
77
- font-style: italic;
78
- text-shadow: 0px 0px 10px rgba(255, 140, 66, 0.7);
79
- animation: glow 2s ease-in-out infinite alternate;
80
- }
81
-
82
- @keyframes glow {
83
- from {
84
- text-shadow: 0 0 5px rgba(255, 140, 66, 0.7);
85
- }
86
- to {
87
- text-shadow: 0 0 15px rgba(255, 140, 66, 1), 0 0 20px rgba(255, 140, 66, 0.8);
88
- }
89
- }
90
-
91
- .logo-part3 {
92
- color: var(--accent);
93
- }
94
-
95
- .login-buttons {
96
- display: flex;
97
- gap: 1rem;
98
- }
99
-
100
- .btn {
101
- padding: 0.5rem 1.5rem;
102
- border-radius: 5px;
103
- border: none;
104
- cursor: pointer;
105
- font-weight: 500;
106
- transition: all 0.3s ease;
107
- text-decoration: none;
108
- position: relative;
109
- overflow: hidden;
110
- z-index: 1;
111
- }
112
-
113
- .btn::before {
114
- content: '';
115
- position: absolute;
116
- top: 0;
117
- left: 0;
118
- width: 0%;
119
- height: 100%;
120
- background-color: rgba(255, 255, 255, 0.1);
121
- transition: all 0.3s ease;
122
- z-index: -1;
123
- }
124
-
125
- .btn:hover::before {
126
- width: 100%;
127
- }
128
-
129
- .btn-primary {
130
- background-color: var(--accent);
131
- color: var(--dark);
132
- box-shadow: 0 4px 8px rgba(76, 201, 240, 0.3);
133
- }
134
-
135
- .btn-outline {
136
- background-color: transparent;
137
- border: 2px solid white;
138
- color: white;
139
- }
140
-
141
- .btn:hover {
142
- transform: translateY(-2px);
143
- box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
144
- }
145
-
146
- .hero {
147
- background: linear-gradient(rgba(67, 97, 238, 0.8), rgba(58, 12, 163, 0.9)), url("/api/placeholder/1200/600") no-repeat center center/cover;
148
- color: white;
149
- text-align: center;
150
- padding: 5rem 1rem;
151
- position: relative;
152
- overflow: hidden;
153
- }
154
-
155
- .hero::before {
156
- content: '';
157
- position: absolute;
158
- top: -50%;
159
- left: -50%;
160
- width: 200%;
161
- height: 200%;
162
- background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0) 60%);
163
- animation: rotate 30s linear infinite;
164
- z-index: 1;
165
- }
166
-
167
- @keyframes rotate {
168
- from {
169
- transform: rotate(0deg);
170
- }
171
- to {
172
- transform: rotate(360deg);
173
- }
174
- }
175
-
176
- .hero-content {
177
- display: flex;
178
- flex-direction: column;
179
- align-items: center;
180
- transform-style: preserve-3d;
181
- transition: transform 0.3s ease;
182
- position: relative;
183
- z-index: 2;
184
- }
185
-
186
- .hero h1 {
187
- font-size: 3rem;
188
- margin-bottom: 1.5rem;
189
- font-weight: 700;
190
- text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
191
- transform: translateZ(20px);
192
- animation: fadeIn 1s ease-out;
193
- }
194
-
195
- @keyframes fadeIn {
196
- from {
197
- opacity: 0;
198
- transform: translateY(20px) translateZ(20px);
199
- }
200
- to {
201
- opacity: 1;
202
- transform: translateY(0) translateZ(20px);
203
- }
204
- }
205
-
206
- .hero p {
207
- font-size: 1.2rem;
208
- max-width: 800px;
209
- margin: 0 auto 2rem;
210
- transform: translateZ(10px);
211
- animation: fadeIn 1s ease-out 0.3s both;
212
- }
213
-
214
- .luna-avatar-container {
215
- position: relative;
216
- width: 250px;
217
- height: 250px;
218
- margin-bottom: 2rem;
219
- perspective: 1000px;
220
- }
221
-
222
- .luna-avatar {
223
- width: 100%;
224
- height: 100%;
225
- border-radius: 50%;
226
- border: 4px solid var(--accent);
227
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3), 0 0 20px rgba(76, 201, 240, 0.5);
228
- overflow: hidden;
229
- animation: float 4s ease-in-out infinite;
230
- position: relative;
231
- z-index: 2;
232
- background: linear-gradient(135deg, rgba(76, 201, 240, 0.2), rgba(58, 12, 163, 0.2));
233
- display: flex;
234
- justify-content: center;
235
- align-items: center;
236
- }
237
-
238
- .luna-glow {
239
- position: absolute;
240
- width: 100%;
241
- height: 100%;
242
- border-radius: 50%;
243
- background: radial-gradient(circle, rgba(76, 201, 240, 0.8) 0%, rgba(76, 201, 240, 0) 70%);
244
- filter: blur(15px);
245
- opacity: 0.7;
246
- z-index: 1;
247
- animation: pulse 4s ease-in-out infinite alternate;
248
- }
249
-
250
- @keyframes pulse {
251
- 0% { transform: scale(0.9); opacity: 0.5; }
252
- 100% { transform: scale(1.1); opacity: 0.7; }
253
- }
254
-
255
- @keyframes float {
256
- 0% { transform: translateY(0px) rotateY(0deg); }
257
- 50% { transform: translateY(-15px) rotateY(5deg); }
258
- 100% { transform: translateY(0px) rotateY(0deg); }
259
- }
260
-
261
- .luna-avatar img {
262
- width: 100%;
263
- height: 100%;
264
- object-fit: cover;
265
- object-position: center -10px;
266
- top: 0;
267
- left: 0;
268
- }
269
-
270
-
271
- .hero-buttons {
272
- display: flex;
273
- justify-content: center;
274
- gap: 1.5rem;
275
- flex-wrap: wrap;
276
- transform: translateZ(15px);
277
- animation: fadeIn 1s ease-out 0.6s both;
278
- }
279
-
280
- .features {
281
- padding: 5rem 1rem;
282
- background-color: white;
283
- position: relative;
284
- z-index: 1;
285
- }
286
-
287
- .section-title {
288
- text-align: center;
289
- margin-bottom: 3rem;
290
- transform-style: preserve-3d;
291
- transition: transform 0.3s ease;
292
- }
293
-
294
- .section-title h2 {
295
- font-size: 2.5rem;
296
- color: var(--primary);
297
- margin-bottom: 1rem;
298
- position: relative;
299
- display: inline-block;
300
- }
301
-
302
- .section-title h2::after {
303
- content: '';
304
- position: absolute;
305
- bottom: -10px;
306
- left: 50%;
307
- transform: translateX(-50%);
308
- width: 80px;
309
- height: 3px;
310
- background: linear-gradient(to right, var(--primary), var(--accent));
311
- }
312
-
313
- .section-title p {
314
- max-width: 600px;
315
- margin: 0 auto;
316
- color: #666;
317
- }
318
-
319
- .features-grid {
320
- display: grid;
321
- grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
322
- gap: 2rem;
323
- }
324
-
325
- .feature-card {
326
- background-color: white;
327
- border-radius: 10px;
328
- overflow: hidden;
329
- box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
330
- transition: all 0.5s ease;
331
- display: flex;
332
- flex-direction: column;
333
- height: 100%;
334
- transform-style: preserve-3d;
335
- perspective: 1000px;
336
- }
337
-
338
- .feature-card:hover {
339
- transform: translateY(-10px) rotateX(5deg);
340
- box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1);
341
- }
342
-
343
- .feature-icon {
344
- background: linear-gradient(135deg, rgba(67, 97, 238, 0.1), rgba(76, 201, 240, 0.1));
345
- padding: 2rem;
346
- display: flex;
347
- justify-content: center;
348
- align-items: center;
349
- font-size: 2.5rem;
350
- color: var(--primary);
351
- transition: all 0.3s ease;
352
- }
353
-
354
- .feature-card:hover .feature-icon {
355
- transform: translateZ(20px);
356
- color: var(--accent);
357
- }
358
-
359
- .feature-content {
360
- padding: 1.5rem;
361
- flex-grow: 1;
362
- transform: translateZ(0);
363
- transition: transform 0.3s ease;
364
- }
365
-
366
- .feature-card:hover .feature-content {
367
- transform: translateZ(10px);
368
- }
369
-
370
- .feature-content h3 {
371
- font-size: 1.5rem;
372
- margin-bottom: 1rem;
373
- color: var(--dark);
374
- position: relative;
375
- display: inline-block;
376
- }
377
-
378
- .feature-content h3::after {
379
- content: '';
380
- position: absolute;
381
- bottom: -5px;
382
- left: 0;
383
- width: 40px;
384
- height: 2px;
385
- background: var(--primary);
386
- transition: width 0.3s ease;
387
- }
388
-
389
- .feature-card:hover .feature-content h3::after {
390
- width: 100%;
391
- }
392
-
393
- .cta {
394
- background: linear-gradient(135deg, var(--secondary), var(--primary));
395
- color: white;
396
- padding: 5rem 1rem;
397
- text-align: center;
398
- position: relative;
399
- overflow: hidden;
400
- }
401
-
402
- .cta::before {
403
- content: '';
404
- position: absolute;
405
- top: -50%;
406
- left: -50%;
407
- width: 200%;
408
- height: 200%;
409
- background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0) 60%);
410
- animation: rotate 20s linear infinite;
411
- z-index: 1;
412
- }
413
-
414
- .cta .container {
415
- position: relative;
416
- z-index: 2;
417
- }
418
-
419
- .cta h2 {
420
- font-size: 2.5rem;
421
- margin-bottom: 1.5rem;
422
- text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
423
- }
424
-
425
- .cta p {
426
- max-width: 600px;
427
- margin: 0 auto 2rem;
428
- font-size: 1.2rem;
429
- }
430
-
431
- footer {
432
- background-color: var(--dark);
433
- color: white;
434
- padding: 3rem 1rem;
435
- }
436
-
437
- .footer-grid {
438
- display: grid;
439
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
440
- gap: 2rem;
441
- }
442
-
443
- .footer-col h3 {
444
- font-size: 1.2rem;
445
- margin-bottom: 1.5rem;
446
- color: var(--accent);
447
- position: relative;
448
- display: inline-block;
449
- }
450
-
451
- .footer-col h3::after {
452
- content: '';
453
- position: absolute;
454
- bottom: -8px;
455
- left: 0;
456
- width: 30px;
457
- height: 2px;
458
- background: var(--accent);
459
- }
460
-
461
- .social-links {
462
- display: flex;
463
- gap: 1rem;
464
- margin-top: 1rem;
465
- }
466
-
467
- .social-links a {
468
- color: white;
469
- background-color: rgba(255, 255, 255, 0.1);
470
- width: 40px;
471
- height: 40px;
472
- border-radius: 50%;
473
- display: flex;
474
- justify-content: center;
475
- align-items: center;
476
- transition: all 0.3s ease;
477
- }
478
-
479
- .social-links a:hover {
480
- background-color: var(--accent);
481
- color: var(--dark);
482
- transform: translateY(-3px);
483
- }
484
-
485
- .copyright {
486
- margin-top: 2rem;
487
- border-top: 1px solid rgba(255, 255, 255, 0.1);
488
- padding-top: 1.5rem;
489
- text-align: center;
490
- color: #aaa;
491
- }
492
-
493
- @media screen and (max-width: 768px) {
494
- .nav-container {
495
- flex-direction: row;
496
- justify-content: space-between;
497
- }
498
-
499
- .luna-avatar-container {
500
- width: 180px;
501
- height: 180px;
502
- }
503
-
504
- .hero h1 {
505
- font-size: 2rem;
506
- }
507
-
508
- .hero p {
509
- font-size: 1rem;
510
- }
511
- }
512
- </style>
513
- </head>
514
- <body>
515
- <header>
516
- <div class="container nav-container">
517
- <a href="#" class="logo">
518
- <span class="logo-part1">Cod</span><span class="logo-part2">in</span><span class="logo-part3">go</span>
519
- </a>
520
- <div class="login-buttons">
521
- <a href="#" class="btn btn-outline">Log In</a>
522
- <a href="#" class="btn btn-primary">Sign Up</a>
523
- </div>
524
- </div>
525
- </header>
526
 
 
527
  <section class="hero">
528
  <div class="container">
529
  <div class="hero-content">
530
  <div class="luna-avatar-container">
531
  <div class="luna-glow"></div>
532
  <div class="luna-avatar">
533
- <img src="../static/images/LUNA.png" alt="LUNA AI Assistant">
534
  </div>
535
  </div>
536
  <h1>Meet LUNA, Your AI Recruitment Assistant</h1>
537
  <p>Revolutionize your hiring process with AI-powered candidate screening, automated interviews, and
538
  intelligent skill matching to find your perfect technical talent.</p>
539
  <div class="hero-buttons">
540
- <a href="#" class="btn btn-primary">Get Started</a>
541
  <a href="#" class="btn btn-outline">Watch Demo</a>
542
  </div>
543
  </div>
544
  </div>
545
  </section>
 
546
 
547
- <section class="features" id="features">
548
- <div class="container">
549
- <div class="section-title">
550
- <h2>Platform Features</h2>
551
- <p>Codingo streamlines every step of your technical hiring process with cutting-edge AI technology</p>
 
 
 
 
 
 
 
 
 
 
 
552
  </div>
553
- <div class="features-grid">
554
- <div class="feature-card">
555
- <div class="feature-icon">
556
- <span>🤖</span>
557
- </div>
558
- <div class="feature-content">
559
- <h3>AI CV Analysis</h3>
560
- <p>LUNA automatically analyzes resumes, matching candidates to job requirements with precision and
561
- eliminating unconscious bias.</p>
562
- </div>
563
  </div>
564
- <div class="feature-card">
565
- <div class="feature-icon">
566
- <span>🎯</span>
567
- </div>
568
- <div class="feature-content">
569
- <h3>Smart Shortlisting</h3>
570
- <p>Our 70% skill match threshold ensures you only interview candidates who meet your technical
571
- requirements.</p>
572
- </div>
573
  </div>
574
- <div class="feature-card">
575
- <div class="feature-icon">
576
- <span>🖥️</span>
577
- </div>
578
- <div class="feature-content">
579
- <h3>AI-Led Interviews</h3>
580
- <p>Structured interview sessions assess soft skills, technical knowledge, and coding abilities with
581
- real-time monitoring.</p>
582
- </div>
583
  </div>
584
  </div>
585
  </div>
@@ -589,27 +68,7 @@
589
  <div class="container">
590
  <h2>Ready to Transform Your Technical Hiring?</h2>
591
  <p>Join hundreds of companies finding their perfect tech talent faster and more efficiently with Codingo.</p>
592
- <a href="#" class="btn btn-primary">Start Free Trial</a>
593
  </div>
594
  </section>
595
-
596
- <footer>
597
- <div class="container">
598
- <div class="footer-grid">
599
- <div class="footer-col">
600
- <h3>Codingo</h3>
601
- <p>AI-powered recruitment platform that revolutionizes how companies hire technical talent.</p>
602
- <div class="social-links">
603
- <a href="#"><span>f</span></a>
604
- <a href="#"><span>t</span></a>
605
- <a href="#"><span>in</span></a>
606
- </div>
607
- </div>
608
- </div>
609
- <div class="copyright">
610
- <p>&copy; 2025 Codingo. All rights reserved.</p>
611
- </div>
612
- </div>
613
- </footer>
614
- </body>
615
- </html>
 
1
+ {% extends "base.html" %}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
+ {% block title %}Codingo - AI-Powered Recruitment Platform{% endblock %}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
+ {% block hero %}
6
  <section class="hero">
7
  <div class="container">
8
  <div class="hero-content">
9
  <div class="luna-avatar-container">
10
  <div class="luna-glow"></div>
11
  <div class="luna-avatar">
12
+ <img src="{{ url_for('static', filename='images/LUNA.png') }}" alt="LUNA AI Assistant">
13
  </div>
14
  </div>
15
  <h1>Meet LUNA, Your AI Recruitment Assistant</h1>
16
  <p>Revolutionize your hiring process with AI-powered candidate screening, automated interviews, and
17
  intelligent skill matching to find your perfect technical talent.</p>
18
  <div class="hero-buttons">
19
+ <a href="{{ url_for('jobs') }}" class="btn btn-primary">Find Jobs</a>
20
  <a href="#" class="btn btn-outline">Watch Demo</a>
21
  </div>
22
  </div>
23
  </div>
24
  </section>
25
+ {% endblock %}
26
 
27
+ {% block content %}
28
+ <section class="content-section" id="features">
29
+ <div class="section-title">
30
+ <h2>Platform Features</h2>
31
+ <p>Codingo streamlines every step of your technical hiring process with cutting-edge AI technology</p>
32
+ </div>
33
+ <div class="features-grid">
34
+ <div class="feature-card">
35
+ <div class="feature-icon">
36
+ <span>🤖</span>
37
+ </div>
38
+ <div class="feature-content">
39
+ <h3>AI CV Analysis</h3>
40
+ <p>LUNA automatically analyzes resumes, matching candidates to job requirements with precision and
41
+ eliminating unconscious bias.</p>
42
+ </div>
43
  </div>
44
+ <div class="feature-card">
45
+ <div class="feature-icon">
46
+ <span>🎯</span>
 
 
 
 
 
 
 
47
  </div>
48
+ <div class="feature-content">
49
+ <h3>Smart Shortlisting</h3>
50
+ <p>Our 70% skill match threshold ensures you only interview candidates who meet your technical
51
+ requirements.</p>
 
 
 
 
 
52
  </div>
53
+ </div>
54
+ <div class="feature-card">
55
+ <div class="feature-icon">
56
+ <span>🖥️</span>
57
+ </div>
58
+ <div class="feature-content">
59
+ <h3>AI-Led Interviews</h3>
60
+ <p>Structured interview sessions assess soft skills, technical knowledge, and coding abilities with
61
+ real-time monitoring.</p>
62
  </div>
63
  </div>
64
  </div>
 
68
  <div class="container">
69
  <h2>Ready to Transform Your Technical Hiring?</h2>
70
  <p>Join hundreds of companies finding their perfect tech talent faster and more efficiently with Codingo.</p>
71
+ <a href="{{ url_for('jobs') }}" class="btn btn-primary">Browse Available Jobs</a>
72
  </div>
73
  </section>
74
+ {% endblock %}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/templates/job_detail.html ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}{{ job.title }} - Codingo{% endblock %}
4
+
5
+ {% block content %}
6
+ <section class="content-section">
7
+ <ul class="breadcrumbs">
8
+ <li><a href="{{ url_for('index') }}">Home</a></li>
9
+ <li><a href="{{ url_for('jobs') }}">Jobs</a></li>
10
+ <li>{{ job.title }}</li>
11
+ </ul>
12
+
13
+ <div class="card">
14
+ <div class="card-header">
15
+ <h2>{{ job.title }}</h2>
16
+ <div style="display: flex; justify-content: space-between; margin-top: 0.5rem;">
17
+ <span>{{ job.company }}</span>
18
+ <span>{{ job.location }}</span>
19
+ </div>
20
+ </div>
21
+ <div class="card-body">
22
+ <div style="margin-bottom: 2rem;">
23
+ <h3 style="color: var(--primary); margin-bottom: 1rem;">Job Description</h3>
24
+ <p>{{ job.description }}</p>
25
+ </div>
26
+
27
+ <div style="margin-bottom: 2rem;">
28
+ <h3 style="color: var(--primary); margin-bottom: 1rem;">Requirements</h3>
29
+ <p>{{ job.requirements }}</p>
30
+ </div>
31
+
32
+ <div style="text-align: center; margin-top: 2rem;">
33
+ <p style="margin-bottom: 1rem;">Posted on: {{ job.date_posted.strftime('%B %d, %Y') }}</p>
34
+ <a href="{{ url_for('apply', job_id=job.id) }}" class="btn btn-primary" style="padding: 0.75rem 2.5rem;">Apply Now</a>
35
+ </div>
36
+ </div>
37
+ </div>
38
+ </section>
39
+
40
+ <section class="cta">
41
+ <div class="container">
42
+ <h2>Is This Job Not Quite Right?</h2>
43
+ <p>Check out other opportunities that might better match your skills and career goals.</p>
44
+ <a href="{{ url_for('jobs') }}" class="btn btn-primary">View All Jobs</a>
45
+ </div>
46
+ </section>
47
+ {% endblock %}
backend/templates/jobs.html ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Available Jobs - Codingo{% endblock %}
4
+
5
+ {% block hero %}
6
+ <section class="hero" style="padding: 3rem 1rem;">
7
+ <div class="container">
8
+ <div class="hero-content">
9
+ <h1>Find Your Perfect Tech Role</h1>
10
+ <p>Browse our curated selection of tech positions and let LUNA match your skills to the right opportunity.</p>
11
+ </div>
12
+ </div>
13
+ </section>
14
+ {% endblock %}
15
+
16
+ {% block content %}
17
+ <section class="content-section">
18
+ <div class="section-title">
19
+ <h2>Available Positions</h2>
20
+ <p>Discover opportunities that match your expertise and career goals</p>
21
+ </div>
22
+
23
+ <ul class="breadcrumbs">
24
+ <li><a href="{{ url_for('index') }}">Home</a></li>
25
+ <li>Jobs</li>
26
+ </ul>
27
+
28
+ <div class="job-grid">
29
+ {% if jobs %}
30
+ {% for job in jobs %}
31
+ <div class="job-card">
32
+ <div class="job-header">
33
+ <h3>{{ job.title }}</h3>
34
+ <div class="job-info">
35
+ <span>{{ job.company }}</span>
36
+ <span>{{ job.location }}</span>
37
+ </div>
38
+ </div>
39
+ <div class="job-body">
40
+ <div class="job-description">
41
+ <p>{{ job.description[:150] }}{% if job.description|length > 150 %}...{% endif %}</p>
42
+ </div>
43
+ <div>Posted: {{ job.date_posted.strftime('%B %d, %Y') }}</div>
44
+ </div>
45
+ <div class="job-footer">
46
+ <a href="{{ url_for('job_detail', job_id=job.id) }}" class="btn btn-primary">View Details</a>
47
+ </div>
48
+ </div>
49
+ {% endfor %}
50
+ {% else %}
51
+ <div class="card">
52
+ <div class="card-body">
53
+ <p>No jobs available at the moment. Please check back later.</p>
54
+ </div>
55
+ </div>
56
+ {% endif %}
57
+ </div>
58
+ </section>
59
+ {% endblock %}
backend/uploads/resumes/Hussein_El_Saadi_-_CV.pdf ADDED
Binary file (72.9 kB). View file
 
backend/uploads/resumes/Mohamad_MoallemCV-2024.pdf ADDED
Binary file (58.9 kB). View file
 
readme.md CHANGED
@@ -96,7 +96,7 @@ model = Pipeline([
96
  model.fit(df['text'], df['position'])
97
 
98
  # Save model
99
- joblib.dump(model, 'backend/model/cv_classifier.pkl')
100
 
101
  print("Model trained and saved successfully!")
102
  ```
@@ -111,19 +111,21 @@ Create a script to verify your model works correctly.
111
  import joblib
112
  import sys
113
 
 
114
  def predict_role(cv_text):
115
  # Load the trained model
116
- model = joblib.load('backend/model/cv_classifier.pkl')
117
-
118
  # Make prediction
119
  prediction = model.predict([cv_text])[0]
120
  confidence = max(model.predict_proba([cv_text])[0]) * 100
121
-
122
  return {
123
  'predicted_position': prediction,
124
  'confidence': f"{confidence:.2f}%"
125
  }
126
 
 
127
  if __name__ == "__main__":
128
  if len(sys.argv) > 1:
129
  # Get CV text from command line argument
@@ -131,7 +133,7 @@ if __name__ == "__main__":
131
  else:
132
  # Example CV text
133
  cv_text = "Experienced Python developer with 5 years of experience in Flask and AWS."
134
-
135
  result = predict_role(cv_text)
136
  print(f"Predicted Position: {result['predicted_position']}")
137
  print(f"Confidence: {result['confidence']}")
 
96
  model.fit(df['text'], df['position'])
97
 
98
  # Save model
99
+ joblib.dump(model, 'backend/models/cv_classifier.pkl')
100
 
101
  print("Model trained and saved successfully!")
102
  ```
 
111
  import joblib
112
  import sys
113
 
114
+
115
  def predict_role(cv_text):
116
  # Load the trained model
117
+ model = joblib.load('backend/models/cv_classifier.pkl')
118
+
119
  # Make prediction
120
  prediction = model.predict([cv_text])[0]
121
  confidence = max(model.predict_proba([cv_text])[0]) * 100
122
+
123
  return {
124
  'predicted_position': prediction,
125
  'confidence': f"{confidence:.2f}%"
126
  }
127
 
128
+
129
  if __name__ == "__main__":
130
  if len(sys.argv) > 1:
131
  # Get CV text from command line argument
 
133
  else:
134
  # Example CV text
135
  cv_text = "Experienced Python developer with 5 years of experience in Flask and AWS."
136
+
137
  result = predict_role(cv_text)
138
  print(f"Predicted Position: {result['predicted_position']}")
139
  print(f"Confidence: {result['confidence']}")
requirements.txt CHANGED
@@ -6,4 +6,6 @@ PyMuPDF
6
  python-docx
7
  spacy>=3.0.0
8
  nltk
9
- pyresparser
 
 
 
6
  python-docx
7
  spacy>=3.0.0
8
  nltk
9
+ pyresparser
10
+ flask_sqlalchemy
11
+ flask_wtf