diff --git a/Controller/gemini_pipeline.py b/Controller/gemini_pipeline.py new file mode 100644 index 00000000..b68852f4 --- /dev/null +++ b/Controller/gemini_pipeline.py @@ -0,0 +1,40 @@ +''' +MIT License + +Copyright (c) 2024 MD NAZMUL HAQUE, KISHAN KUMAR GANGULY, RAVI + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +''' + +from PyPDF2 import PdfFileReader +import numpy as np +import os +import google.generativeai as genai + +API_KEY = "AIzaSyBvVbnHMiCIN143GgN6P1u4w7iBbGucb0E" #add gemini API key +def get_gemini_feedback(pdf_path): + if pdf_path: + genai.configure(api_key=API_KEY) + model = genai.GenerativeModel("gemini-1.5-flash") + sample_pdf = genai.upload_file(pdf_path) + prompt = "critique the following resume on the basis of its conciseness, use of action words and numbers. Give suggestions on the 1. structure and design section, then the 2. education section, then the 3. experiences section, then the 4. skills section and finally the 5. projects section. At first give a paragraph summarizing your suggestions. Then, give these suggestions on these five sections in the form of five paragraphs and label them Section 1, Section 2, Section 3, Section 4 and Section 5 respectively, each separated by a line. Make sure each paragraph is atleast 50-70 words long." + try: + response = model.generate_content( + [prompt, sample_pdf], + generation_config=genai.types.GenerationConfig( + # Only one candidate for now. + candidate_count=1, + max_output_tokens=40000, + temperature=0.8, + ), + ) + return response.text + + except Exception as e: + print(f"An error occurred: {e}") + + return None diff --git a/UnitTesting/selenium/login_test.py b/UnitTesting/selenium/login_test.py new file mode 100644 index 00000000..30a45338 --- /dev/null +++ b/UnitTesting/selenium/login_test.py @@ -0,0 +1,170 @@ +import unittest +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import Select, WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +import time +import HtmlTestRunner + +class LoginTestCase(unittest.TestCase): + + @classmethod + def setUpClass(cls): + # Initialize the Firefox WebDriver + cls.driver = webdriver.Firefox() + cls.driver.maximize_window() + cls.driver.implicitly_wait(10) + + def setUp(self): + # Navigate to the login page before each test + self.driver.get("http://127.0.0.1:5000/login") # Replace with the actual login page URL + + def test_login_with_valid_credentials_admin(self): + """Test case for logging in with valid admin credentials.""" + driver = self.driver + + # Fill in Username + username_field = driver.find_element(By.NAME, "username") + username_field.send_keys("abcdefgh") # Replace with a valid admin username in your database + + # Fill in Password + password_field = driver.find_element(By.NAME, "password") + password_field.send_keys("abcdefgh") # Replace with a valid password in your database + + # Select User Role from dropdown + user_role_dropdown = Select(driver.find_element(By.NAME, "usertype")) + user_role_dropdown.select_by_visible_text("Admin") + + # Submit the form + submit_button = driver.find_element(By.NAME, "submit") + submit_button.click() + + # Wait for the page to load + time.sleep(3) + + # Check for server errors + self._check_for_errors() + + # Verify if redirected to the admin dashboard page URL + expected_url = "http://127.0.0.1:5000/admin?data=abcdefgh" # Replace with the actual admin dashboard URL + current_url = driver.current_url + self.assertEqual(current_url, expected_url, "Login was successful and redirected to admin dashboard page.") + + def test_login_with_valid_credentials_student(self): + """Test case for logging in with valid student credentials.""" + driver = self.driver + + # Fill in Username + username_field = driver.find_element(By.NAME, "username") + username_field.send_keys("12345678") # Replace with a valid student username in your database + + # Fill in Password + password_field = driver.find_element(By.NAME, "password") + password_field.send_keys("12345678") # Replace with a valid password in your database + + # Select User Role from dropdown + user_role_dropdown = Select(driver.find_element(By.NAME, "usertype")) + user_role_dropdown.select_by_visible_text("Student") + + # Submit the form + submit_button = driver.find_element(By.NAME, "submit") + submit_button.click() + + # Wait for the page to load + time.sleep(3) + + # Check for server errors + self._check_for_errors() + + # Verify if redirected to the student dashboard page URL + expected_url = "http://127.0.0.1:5000/student?data=12345678" # Replace with the actual student dashboard URL + current_url = driver.current_url + self.assertEqual(current_url, expected_url, "Login was successful and redirected to student dashboard page.") + + def test_login_with_invalid_credentials(self): + """Test case for logging in with invalid credentials.""" + driver = self.driver + + # Fill in Username + username_field = driver.find_element(By.NAME, "username") + username_field.send_keys("invalid_user") + + # Fill in Password + password_field = driver.find_element(By.NAME, "password") + password_field.send_keys("WrongPassword!") + + # Select User Role from dropdown + user_role_dropdown = Select(driver.find_element(By.NAME, "usertype")) + user_role_dropdown.select_by_visible_text("Admin") + + # Submit the form + submit_button = driver.find_element(By.NAME, "submit") + submit_button.click() + + # Wait for the page to load + time.sleep(3) + + # Check for server errors + self._check_for_errors() + + # Verify if still on the login page + current_url = driver.current_url + self.assertEqual(current_url, "http://127.0.0.1:5000/login", "Page did not forward after invalid login attempt.") + + # Verify if an error message is displayed + error_message = driver.find_element(By.CLASS_NAME, "alert-danger") + self.assertTrue(error_message.is_displayed(), "Error message is not displayed for invalid login.") + + def test_login_with_blank_fields(self): + """Test case for logging in with blank fields.""" + driver = self.driver + + # Leave fields blank and submit + username_field = driver.find_element(By.NAME, "username") + password_field = driver.find_element(By.NAME, "password") + + username_field.clear() + password_field.clear() + + # Select User Role from dropdown + user_role_dropdown = Select(driver.find_element(By.NAME, "usertype")) + user_role_dropdown.select_by_visible_text("Admin") + + # Submit the form + submit_button = driver.find_element(By.NAME, "submit") + submit_button.click() + + # Wait for the page to load + time.sleep(3) + + # Check for server errors + self._check_for_errors() + + # Verify if still on the login page + current_url = driver.current_url + self.assertEqual(current_url, "http://127.0.0.1:5000/login", "Page did not forward after submitting blank fields.") + + def _check_for_errors(self): + # Check for a generic internal server error message in

+ h1_elements = self.driver.find_elements(By.TAG_NAME, "h1") + h1_texts = [h1.text for h1 in h1_elements] + # Check if any

contains the term "error" + internal_error_found = any("error" in h1.lower() for h1 in h1_texts) + # Assert that an internal server error message was not found + self.assertFalse(internal_error_found, "Internal server error detected in the response.") + + @classmethod + def tearDownClass(cls): + # Quit the driver after all tests are done + cls.driver.quit() + +# Run the tests +if __name__ == "__main__": + output_file = "login_test_report.html" + runner = HtmlTestRunner.HTMLTestRunner( + output='.', # Specify the output directory + report_name='login_test_report', # Set the report name (without extension) + report_title='Login Test Report', # Title for the report + descriptions='Unit test results' # Description for the report + ) + runner.run(unittest.TestLoader().loadTestsFromTestCase(LoginTestCase)) diff --git a/UnitTesting/selenium/login_test_report___main__.LoginTestCase_2024-10-28_19-46-46.html b/UnitTesting/selenium/login_test_report___main__.LoginTestCase_2024-10-28_19-46-46.html new file mode 100644 index 00000000..8d51e861 --- /dev/null +++ b/UnitTesting/selenium/login_test_report___main__.LoginTestCase_2024-10-28_19-46-46.html @@ -0,0 +1,92 @@ + + + + Login Test Report + + + + + +
+
+
+

Login Test Report

+

Start Time: 2024-10-28 19:46:46

+

Duration: 55.76 s

+

Summary: Total: 4, Pass: 4

+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
__main__.LoginTestCaseStatus
test_login_with_blank_fields + Pass + +
test_login_with_invalid_credentials + Pass + +
test_login_with_valid_credentials_admin + Pass + +
test_login_with_valid_credentials_student + Pass + +
+ Total: 4, Pass: 4 -- Duration: 55.76 s +
+
+
+
+ + + + + h1_elements = self.driver.find_elements(By.TAG_NAME, "h1") + h1_texts = [h1.text for h1 in h1_elements] + # Check if any

contains the term "error" + internal_error_found = any("error" in h1.lower() for h1 in h1_texts) + # Assert that an internal server error message was not found + self.assertFalse(internal_error_found, "Internal server error detected in the response.") + + + @classmethod + def tearDownClass(cls): + # Quit the driver after all tests are done + cls.driver.quit() + +# Run the tests +if __name__ == "__main__": + output_file = "signup_test_report.html" + runner = HtmlTestRunner.HTMLTestRunner( + output='.', # Specify the output directory + report_name='signup_test_report', # Set the report name (without extension) + report_title='Signup Test Report', # Title for the report + descriptions='Unit test results' # Description for the report + ) + runner.run(unittest.TestLoader().loadTestsFromTestCase(SignupTestCase)) + #unittest.main() \ No newline at end of file diff --git a/UnitTesting/selenium/signup_test_report___main__.SignupTestCase_2024-10-28_18-33-26.html b/UnitTesting/selenium/signup_test_report___main__.SignupTestCase_2024-10-28_18-33-26.html new file mode 100644 index 00000000..8d285d46 --- /dev/null +++ b/UnitTesting/selenium/signup_test_report___main__.SignupTestCase_2024-10-28_18-33-26.html @@ -0,0 +1,92 @@ + + + + Signup Test Report + + + + + +
+
+
+

Signup Test Report

+

Start Time: 2024-10-28 18:33:26

+

Duration: 54.25 s

+

Summary: Total: 4, Pass: 4

+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
__main__.SignupTestCaseStatus
test_signup_with_blank_fields + Pass + +
test_signup_with_duplicate_username + Pass + +
test_signup_with_short_password + Pass + +
test_signup_with_unique_username + Pass + +
+ Total: 4, Pass: 4 -- Duration: 54.25 s +
+
+
+
+ + + +', methods=['GET', 'POST']) def get_job_application_status(status): data_received = request.args.get('data') + print("faul ", data_received) user = find_user(str(data_received), database) if status: @@ -178,30 +211,29 @@ def add_job_application(): @app.route('/student/update_job_application',methods=['GET','POST']) def update_job_application(): if request.method == 'POST': + job_id = request.form['job_id'] company = request.form['company'] location = request.form['location'] jobposition = request.form['jobposition'] salary = request.form['salary'] status = request.form['status'] - user_id = request.form['user_id'] + user_name = session['user_name'] # Perform the update operation - update_job_application_by_id( company, location, jobposition, salary, status, database) # Replace this with your method to update the job + update_job_application_by_id( job_id, company, location, jobposition, salary, status, database) # Replace this with your method to update the job flash('Job Application Updated!') # Redirect to a success page or any relevant route after successful job update - return redirect(url_for('student', data=user_id)) + return redirect(url_for('student', data=user_name)) -@app.route('/student/delete_job_application/', methods=['POST']) -def delete_job_application(company): +@app.route('/student/delete_job_application', methods=['POST']) +def delete_job_application(): if request.method == 'POST': - user_id = request.form['user_id'] - # Perform the deletion operation - delete_job_application_by_company(company,database) # Using the function to delete by company name - + job_id = request.args.get('job_id') + user_name = request.args.get('user_name') + delete_job_application_by_job_id(job_id,database) flash('Job Application Deleted!') - # Redirect to a success page or any relevant route after successful deletion - return redirect(url_for('student', data=user_id)) # Redirect to the student page or your desired route + return redirect(url_for('student', data=user_name)) @app.route('/student/add_New',methods=['GET','POST']) def add_New(): @@ -247,6 +279,7 @@ def job_profile_analyze(): if request.method == 'POST': job_profile = request.form['job_profile'] skills = extract_skills(job_profile) + print("\n\n\n\n\n",skills) skills_text = ', '.join(skills) return render_template('job_profile_analyze.html', skills_text=skills_text, job_profile=job_profile) return render_template('job_profile_analyze.html', skills_text='', job_profile='') @@ -270,14 +303,13 @@ def upload(): user = request.form['user_id'] user = find_user(str(user),database) - print('Userrrrrr', user) - return render_template("home.html", data=data, upcoming_events=upcoming_events, user=user) @app.route('/student/analyze_resume', methods=['GET']) def view_ResumeAna(): - return render_template('resume_analyzer.html') + resumes = get_resumes_by_user_name(session['user_name'], database) + return render_template('resume_analyzer.html', resumes=resumes) @app.route('/student/companiesList', methods=['GET']) def view_companies_list(): @@ -286,7 +318,7 @@ def view_companies_list(): @app.route('/student/analyze_resume', methods=['POST']) def analyze_resume(): - jobtext = request.form['jobtext'] + jobtext = request.form['job_description'] os.chdir(os.getcwd()+"/Controller/resume/") output = resume_analyzer(jobtext, str(os.listdir(os.getcwd())[0])) os.chdir("..") @@ -303,42 +335,59 @@ def display(): user = request.form['user_id'] user = find_user(str(user),database) return render_template('home.html', user=user, data=data, upcoming_events=upcoming_events) +def section_strip(section, section_name): + if section_name in section: + section = section.replace(section_name+"**", "", 1).strip() + # Remove asterisks from the end + section = section.rstrip('*').strip() + return section +@app.route('/student/resume_AI_analyzer/', methods=['GET']) +def resume_AI_analyzer(): + resume_dir = os.path.join(os.getcwd(), 'Controller', 'resume') -@app.route('/student/chat_gpt_analyzer/', methods=['GET']) -def chat_gpt_analyzer(): - files = os.listdir(os.getcwd()+'/Controller/resume') - pdf_path = os.getcwd()+'//Controller/resume/'+files[0] - text_path = os.getcwd()+'//Controller/resume_txt/'+files[0][:-3]+'txt' - with open(text_path, 'w'): - pass - pdf_to_text(pdf_path, text_path) - suggestions = chatgpt(text_path) - flag = 0 - final_sugges_send = [] - final_sugges = "" - - # Initialize an empty string to store the result - result_string = "" - - # Iterate through each character in the original string - for char in suggestions: - # If the character is not a newline character, add it to the result string - if char != '\n': - final_sugges += char - sections = final_sugges.split("Section") - for section in sections: - section = section.strip() # Remove leading and trailing whitespace - # if section: # Check if the section is not empty (e.g., due to leading/trailing "Section") - # print("Section:", section) - sections = sections[1:] - section_names = ['Education', 'Experience','Skills', 'Projects'] - sections[0] = sections[0][3:] - sections[1] = sections[1][3:] - sections[2] = sections[2][3:] - sections[3] = sections[3][3:] - return render_template('chat_gpt_analyzer.html', suggestions=sections, pdf_path=pdf_path, section_names = section_names) + files = os.listdir(resume_dir) + if not files: + return jsonify({"error": "No resume files found."}), 404 + + pdf_file = files[0] + pdf_path = os.path.join(resume_dir, pdf_file) + + if not os.path.exists(pdf_path): + return jsonify({"error": f"PDF file '{pdf_file}' does not exist."}), 404 + + suggestions = get_gemini_feedback(pdf_path) + print(suggestions) + if suggestions: + final_sugges = "" + + # Iterate through each character in the original string + for char in suggestions: + # If the character is not a newline character, add it to the result string + if char != '\n': + final_sugges += char + + sections = final_sugges.split("Section") + section_names = ['Structure and Design', 'Education', 'Experiences','Skills', 'Projects'] + + for index, section in enumerate(sections): + section = section.strip() # Remove leading and trailing whitespace + if index: + print("before:",section) + section = section_strip(section, section_names[index-1]) + sections[index] = section + # if section: # Check if the section is not empty (e.g., due to leading/trailing "Section") + # print("Section:", section) + sections = sections[1:] + sections[0] = sections[0][3:] + sections[1] = sections[1][3:] + sections[2] = sections[2][3:] + sections[3] = sections[3][3:] + sections[4] = sections[4][3:] + return render_template('gemini_analyzer.html', suggestions=sections, pdf_path=pdf_path, section_names = section_names) + else: + return jsonify({"error": f"No suggestion was generated"}), 404 @app.route('/student/job_search') def job_search(): @@ -346,14 +395,26 @@ def job_search(): @app.route('/student/job_search/result', methods=['POST']) def search(): - job_role = request.form['job_role'] - adzuna_url = f"https://api.adzuna.com/v1/api/jobs/us/search/1?app_id=575e7a4b&app_key=35423835cbd9428eb799622c6081ffed&what_phrase={job_role}" + job_title = urllib.parse.quote(request.form['job_title']) + location = urllib.parse.quote(request.form['location']) + minSalary = request.form['minSalary'] + maxSalary = request.form['maxSalary'] + job_type = request.form['job_type'] + company = urllib.parse.quote(request.form['company']) + query = "what_phrase="+job_title + if(len(minSalary)>0): query+="&salary_min="+minSalary + if(len(maxSalary)>0): query+="&salary_max="+maxSalary + if(job_type=="full_time"): query+="&full_time=1" + if(job_type=="part_time"): query+="&part_time=1" + if(len(company)>0): query+="&company="+company + + adzuna_url = f"https://api.adzuna.com/v1/api/jobs/gb/search/1?app_id=575e7a4b&app_key=35423835cbd9428eb799622c6081ffed&"+query try: response = requests.get(adzuna_url) if response.status_code == 200: data = response.json() jobs = data.get('results', []) - return render_template('job_search_results.html', jobs=jobs) + return render_template('job_search.html', jobs=jobs) else: return "Error fetching job listings" except requests.RequestException as e: diff --git a/database.db b/database.db index e8f517b7..24f90c44 100644 Binary files a/database.db and b/database.db differ diff --git a/database.sqbpro b/database.sqbpro new file mode 100644 index 00000000..574ebc62 --- /dev/null +++ b/database.sqbpro @@ -0,0 +1,6 @@ +
DELETE FROM resumes +WHERE id NOT IN ( + SELECT MIN(id) + FROM resumes + GROUP BY username, fileName +);
diff --git a/dbutils.py b/dbutils.py index b75cac7d..6d3b203d 100644 --- a/dbutils.py +++ b/dbutils.py @@ -10,6 +10,8 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ''' import sqlite3 +import os + def create_tables(db): conn = sqlite3.connect(db) @@ -33,6 +35,16 @@ def create_tables(db): status TEXT ) ''') + cursor.execute(''' + CREATE TABLE IF NOT EXISTS resumes ( + id INTEGER NOT NULL, + username TEXT NOT NULL, + fileName TEXT NOT NULL, + PRIMARY KEY(id AUTOINCREMENT), + UNIQUE(username, fileName), + FOREIGN KEY(username) REFERENCES client(username) + ) + ''') conn.commit() conn.close() @@ -47,6 +59,16 @@ def add_client(value_set,db): conn.commit() conn.close() +def get_user_by_username_role(username, usertype, db): + conn = sqlite3.connect(db) + print('Data==>', username, usertype) + cursor = conn.cursor() + # Querying the 'client' table + cursor.execute("SELECT * FROM client WHERE username = ? AND usertype=?", (username,usertype)) + rows = cursor.fetchone() + conn.close() + return rows + def find_user(data,db): conn = sqlite3.connect(db) @@ -69,34 +91,33 @@ def add_job(data,db): conn.commit() conn.close() -def get_job_applications(db): +def get_job_applications(user_name, db): conn = sqlite3.connect(db) cursor = conn.cursor() - cursor.execute("SELECT * FROM jobs") + cursor.execute("SELECT * FROM jobs WHERE user_name=?",(user_name,)) rows = cursor.fetchall() # Use fetchall() to get all rows conn.close() - print('rows ->>>', rows) return rows -def update_job_application_by_id(company, location, jobposition, salary, status,db): +def update_job_application_by_id(job_id, company, location, jobposition, salary, status,db): conn = sqlite3.connect(db) cursor = conn.cursor() # Update the 'jobs' table based on jobid - cursor.execute("UPDATE jobs SET company_name=?, location=?, job_position=?, salary=?, status=? WHERE company_name=?", - (company, location, jobposition, salary, status, company)) + cursor.execute("UPDATE jobs SET company_name=?, location=?, job_position=?, salary=?, status=? WHERE id=?", + (company, location, jobposition, salary, status, job_id)) conn.commit() conn.close() -def delete_job_application_by_company(company_name,db): +def delete_job_application_by_job_id(job_id,db): conn = sqlite3.connect(db) cursor = conn.cursor() # Delete the job application from the 'jobs' table based on the company name - cursor.execute("DELETE FROM jobs WHERE company_name=?", (company_name,)) + cursor.execute("DELETE FROM jobs WHERE id=?", (job_id,)) conn.commit() conn.close() @@ -110,5 +131,22 @@ def get_job_applications_by_status(db, status): print('rows ->>>', rows) return rows +def get_resumes_by_user_name(user_name, db): + conn = sqlite3.connect(db) + cursor = conn.cursor() + + cursor.execute("SELECT name FROM sqlite_master WHERE type='table';") + tables = cursor.fetchall() + print("Tables:", tables) + db_path = os.path.abspath(db) + print(f"Connecting to database: {db_path}") + + cursor.execute("SELECT * FROM resumes WHERE username = ?", (user_name,)) + rows = cursor.fetchall() # Use fetchall() to get all rows + conn.close() + print('rows ->>>', rows) + return rows + + diff --git a/jobDataAPI.py b/jobDataAPI.py new file mode 100644 index 00000000..a56fc2bc --- /dev/null +++ b/jobDataAPI.py @@ -0,0 +1,101 @@ +import requests +import urllib.parse + +class JobDataAPI: + def __init__(self, api_url, app_id, app_key): + """ + Initialize the JobDataAPI class with an API URL, app ID, and app key. + :param api_url: Base URL of the API + :param app_id: Application ID for authentication + :param app_key: Application key for authentication + """ + self.api_url = api_url + self.app_id = app_id + self.app_key = app_key + + def getJobDataByPref(self, preferences): + """ + Get job data based on user preferences by making an API request. + :param preferences: Dictionary containing user preferences (e.g., location, industry) + :return: Response data from the API containing job data matching preferences + """ + params = { + "app_id": self.app_id, + "app_key": self.app_key, + **preferences + } + response = requests.get(self.api_url, params=params) + if response.status_code == 200: + return response.json() + else: + response.raise_for_status() + + def getSalaryHistogram(self): + """ + Get salary histogram data for jobs by making an API request. + :return: Response data from the API containing salary histogram + """ + # Assuming the API supports this endpoint for salary histogram data + response = requests.get(f"{self.api_url}/salary_histogram", params={"app_id": self.app_id, "app_key": self.app_key}) + if response.status_code == 200: + return response.json() + else: + response.raise_for_status() + + def getHistoricalSalary(self, job_title): + """ + Get historical salary data for a specific job title by making an API request. + :param job_title: Job title for which historical salary data is needed + :return: Response data from the API containing historical salary data + """ + # Assuming the API supports this endpoint for historical salary data + response = requests.get(f"{self.api_url}/historical_salary", params={"app_id": self.app_id, "app_key": self.app_key, "job_title": job_title}) + if response.status_code == 200: + return response.json() + else: + response.raise_for_status() + + def getJobGeoData(self, location): + """ + Get geographical data for jobs in a specific location by making an API request. + :param location: Location for which job geographical data is needed + :return: Response data from the API containing geographical job data + """ + params = { + "app_id": self.app_id, + "app_key": self.app_key, + "location": location + } + response = requests.get(self.api_url, params=params) + if response.status_code == 200: + return response.json() + else: + response.raise_for_status() + + def getTopCompanies(self, limit=10): + """ + Get the top companies by the number of job postings by making an API request. + :param limit: Number of top companies to return + :return: Response data from the API containing top companies and their job posting counts + """ + # Assuming the API supports this endpoint for top companies data + response = requests.get(f"{self.api_url}/top_companies", params={"app_id": self.app_id, "app_key": self.app_key, "limit": limit}) + if response.status_code == 200: + return response.json() + else: + response.raise_for_status() + +# Example usage +app_id = "575e7a4b" +app_key = "35423835cbd9428eb799622c6081ffed" +api_url = "https://api.adzuna.com/v1/api/jobs/us/search/1?" + +api = JobDataAPI(api_url, app_id, app_key) + + +params = {'location0': 'New York', 'category': 'Tech', 'what_phrase': 'Software Engineer'} +encoded_params = urllib.parse.urlencode(params, quote_via=urllib.parse.quote) +print(encoded_params) + +job_data = api.getJobDataByPref(encoded_params) +print(job_data) diff --git a/login_utils.py b/login_utils.py index ee6add2b..11247144 100644 --- a/login_utils.py +++ b/login_utils.py @@ -60,6 +60,7 @@ def login_user(app,user, remember=False, duration=None, force=False, fresh=True) """ print("###USER",user) session["user_id"] = user[0] + session["user_name"] = user[1] session["type"] = user[4] session["_fresh"] = fresh session["_id"] = get_session_identifier() diff --git a/requirements.txt b/requirements.txt index 16ac244d..471065d0 100644 Binary files a/requirements.txt and b/requirements.txt differ diff --git a/signup_test_report.html/TestResults___main__.SignupTestCase_2024-10-27_11-13-41.html b/signup_test_report.html/TestResults___main__.SignupTestCase_2024-10-27_11-13-41.html new file mode 100644 index 00000000..9452fced --- /dev/null +++ b/signup_test_report.html/TestResults___main__.SignupTestCase_2024-10-27_11-13-41.html @@ -0,0 +1,124 @@ + + + + Unittest Results + + + + + +
+
+
+

Unittest Results

+

Start Time: 2024-10-27 11:13:41

+

Duration: 24.54 s

+

Summary: Total: 4, Pass: 2, Fail: 1, Error: 1

+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
__main__.SignupTestCaseStatus
test_signup_with_blank_fields + Error + + +

NoSuchElementException: Message: Could not locate element with visible text: ; For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#no-such-element-exception +

Traceback (most recent call last): + File "E:\WolfTrack5.0\UnitTesting\selenium\signup_test.py", line 105, in test_signup_with_blank_fields + user_type_dropdown.select_by_visible_text("") # Assuming there is a blank option or handle error accordingly + File "E:\WolfTrack5.0\test_env\lib\site-packages\selenium\webdriver\support\select.py", line 137, in select_by_visible_text + raise NoSuchElementException(f"Could not locate element with visible text: {text}") +selenium.common.exceptions.NoSuchElementException: Message: Could not locate element with visible text: ; For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#no-such-element-exception + +

+
test_signup_with_duplicate_username + Pass + +
test_signup_with_short_password + Fail + + +

AssertionError: 'http://127.0.0.1:5000/login' != 'http://127.0.0.1:5000/signup' +- http://127.0.0.1:5000/login +? ^^ - ++ http://127.0.0.1:5000/signup +? ^^ ++ + : Page did not forward after short password attempt.

Traceback (most recent call last): + File "E:\WolfTrack5.0\UnitTesting\selenium\signup_test.py", line 90, in test_signup_with_short_password + self.assertEqual(current_url, "http://127.0.0.1:5000/signup", "Page did not forward after short password attempt.") +AssertionError: 'http://127.0.0.1:5000/login' != 'http://127.0.0.1:5000/signup' +- http://127.0.0.1:5000/login +? ^^ - ++ http://127.0.0.1:5000/signup +? ^^ ++ + : Page did not forward after short password attempt. +

+
test_signup_with_unique_username + Pass + +
+ Total: 4, Pass: 2, Fail: 1, Error: 1 -- Duration: 24.54 s +
+
+
+
+ + + +{{section_names[3]}}

+
+
+
+
{{section_names[4]}}
+

{{suggestions[4]}}

+
+
+
diff --git a/templates/gemini_analyzer.html b/templates/gemini_analyzer.html new file mode 100644 index 00000000..a2f357bb --- /dev/null +++ b/templates/gemini_analyzer.html @@ -0,0 +1,19 @@ +{% extends "layout/init.html" %} + +{% block content %} +

Suggestions

+
+
+ {% for i in range(section_names|length) %} +
+
+
+
{{ section_names[i] }}
+

{{ suggestions[i] }}

+
+
+
+ {% endfor %} +
+
+{% endblock %} diff --git a/templates/home.html b/templates/home.html index deed3290..566fd882 100755 --- a/templates/home.html +++ b/templates/home.html @@ -1,306 +1,241 @@ -{% extends "layout/base.html" %} +{% extends "layout/init.html" %} {% block content %} - - - - - Job Listings - - - - - - - - - - -
- -
-

Job Application Portal - -
-
- -
-
- -

-
- - - - - - - - - - - - - {% for row in jobapplications %} - - - - - - - - - -
CompanyLocationJob PositionSalaryStatusEditDelete
{{ row[1] }}{{ row[2] }}{{ row[3] }}{{ row[4] }}{{ row[5] }} - - ✎ - - - - ❌ - -
+ {% endif %} + +
+

Applied Jobs

+ + + + + + + + + + + + + + + + {% for row in jobapplications %} + + + + + + + + + + + + + + + + + {% endfor %} + +
#CompanyLocationJob PositionSalaryStatusEditDelete
{{ loop.index }}{{ row[1] }}{{ row[2] }}{{ row[3] }}{{ row[4] }}{{ row[5] }}
+ + +
- - - -{% endblock content %} \ No newline at end of file + + + +{% endblock %} diff --git a/templates/index copy.html b/templates/index copy.html new file mode 100644 index 00000000..045c99ca --- /dev/null +++ b/templates/index copy.html @@ -0,0 +1,124 @@ + + + + + + WolfTrack + + + + + + + + + + + + + + + + + + + +
+ + +
+
+
+
+ 100x100 +
+ +
+
Login
+
+
Signup
+
+
+
+
+
+ + + + diff --git a/templates/index.html b/templates/index.html index 045c99ca..c649b6b6 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,124 +1,67 @@ - - - - - - WolfTrack - - - - - - - - - - - - - - - - +{% extends "layout/skeleton.html" %} + +{% block content %} + +
+
+ +
+

Welcome to WolfTrack

+

Your gateway to job opportunities, resume evaluation, and career development at NC State University.

+
- + +
+ +
+
+
+ +

Job Searching

+

Explore thousands of job opportunities that fit your skills and preferences. WolfTrack helps you find the best jobs, internships to boost your career.

+ Search Jobs +
+
+
-
- + +
+
+
+ +

Resume Evaluation

+

Get your resume evaluated to improve your chances of landing your dream job. Our advanced AI tools provide feedback to make your resume stand out.

+ Evaluate Resume +
+
+
-
-
-
-
- 100x100 + +
+
+
+ +

Resume Suggestions

+

Receive personalized suggestions to optimize your resume for specific job opportunities and industries, leveraging the latest AI-powered tools.

+ Get Suggestions
+
+
+
-
-
Login
-
-
Signup
+ +
+
+
+
+ +

Skill Check

+

Assess your skills and identify areas of improvement. With WolfTrack, you can discover and develop the skills that are in demand by top employers.

+ Take Skill Assessment
- - - - +
+{% endblock %} \ No newline at end of file diff --git a/templates/job_profile_analyze copy.html b/templates/job_profile_analyze copy.html new file mode 100644 index 00000000..db6ac3f3 --- /dev/null +++ b/templates/job_profile_analyze copy.html @@ -0,0 +1,86 @@ + + + + + Job Skills Extractor + + + +
+

Job Skills Extractor

+
+ + +
+ +
+ + {% if skills_text %} +
+ + +
+ {% endif %} +
+ + diff --git a/templates/job_profile_analyze.html b/templates/job_profile_analyze.html index db6ac3f3..7cf66a2b 100644 --- a/templates/job_profile_analyze.html +++ b/templates/job_profile_analyze.html @@ -1,86 +1,45 @@ - - - - - Job Skills Extractor - - - -
-

Job Skills Extractor

-
- - -
- -
- - {% if skills_text %} -
- - -
- {% endif %} -
- - +{% extends "layout/init.html" %} + +{% block content %} +
+
+
+
+
+

Job Skills Extractor

+
+
+
+
+ + +
+ +
+ +
+
+ + {% if skills_text %} +
+
Extracted Skills
+
+ {% for skill in skills_text.split(', ') %} +
+
+
+
{{ skill }}
+
+
+
+ {% endfor %} +
+
+ {% endif %} +
+
+
+
+
+ +{% endblock %} \ No newline at end of file diff --git a/templates/job_search copy.html b/templates/job_search copy.html new file mode 100644 index 00000000..a1be6c93 --- /dev/null +++ b/templates/job_search copy.html @@ -0,0 +1,86 @@ + + + + + + Job Search + + + + + +

Job Search

+
+
+
+ +
+ + + diff --git a/templates/job_search.html b/templates/job_search.html index a1be6c93..6b3d8d9a 100644 --- a/templates/job_search.html +++ b/templates/job_search.html @@ -1,86 +1,206 @@ - - +{% extends "layout/init.html" %} - - - Job Search +{% block title %}Job Search{% endblock %} - - + } + + document.getElementById('searchInput').addEventListener('keyup', function() { + let filter = this.value.toUpperCase(); + + rows.forEach(row => { + let rowText = row.innerText || row.textContent; + row.style.display = rowText.toUpperCase().includes(filter) ? '' : 'none'; + }); - -

Job Search

-
-
-
- -
- + currentPage = 1; // Reset to first page + displayTableRows(); + updatePagination(); + }); - + displayTableRows(); + updatePagination(); +}); + +{% endblock %} diff --git a/templates/layout/base.html b/templates/layout/base.html index 8bf5356f..bffc58e4 100644 --- a/templates/layout/base.html +++ b/templates/layout/base.html @@ -234,7 +234,7 @@ >