diff --git a/LICENSE b/LICENSE index edae2b3c1..4687cced0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,9 +1,16 @@ -MIT License +Proprietary License for Auto Jobs Applier AIHawk -Copyright (c) 2024 feder-cr +1. Freedom to Use, Modify, and Distribute +The software may be freely used, modified, and distributed by anyone, provided that the source code remains accessible and open to the public. Any derivative work or modification may be released as open source but cannot be converted into proprietary software without prior written authorization from the original author. -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: +2. Prohibition of Resale and Non-Commercial Use +The software may not be sold without explicit authorization from the original author. It is intended for non-commercial use unless specific permission for commercial use is granted. -The above copyright notice and this permission notice must be included in all copies or substantial portions of the Software. +3. Copyright +Copyright remains with the original author. Any unauthorized use will be considered a copyright infringement. -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. +4. Disclaimer of Warranty +This software is provided “as is,” without warranties of any kind. The author shall not be liable for any damages arising from its use. + +5. Governing Law +This agreement shall be governed by the laws of Italy. diff --git a/README.md b/README.md index fc5d0ba74..b0e955849 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ Auto_Jobs_Applier_AIHawk steps in as a game-changing solution to these challenge ## Installation -**Confirmed succesfull runs on the following:** +**Confirmed successful runs on the following:** - Operating Systems: - Windows 10 - Ubuntu 22 diff --git a/assets/resume_schema.yaml b/assets/resume_schema.yaml index 25ddfff31..8b3bb88ad 100644 --- a/assets/resume_schema.yaml +++ b/assets/resume_schema.yaml @@ -15,7 +15,7 @@ personal_information: email: {type: string, format: email} github: {type: string, format: uri} linkedin: {type: string, format: uri} - required: [name, surname, date_of_birth, country, city, address,zip_code, phone_prefix, phone, email] + required: [name, surname, date_of_birth, country, city, address, zip_code, phone_prefix, phone, email] education_details: type: array @@ -130,4 +130,4 @@ work_preferences: willing_to_complete_assessments: {type: string, enum: [Yes, No]} willing_to_undergo_drug_tests: {type: string, enum: [Yes, No]} willing_to_undergo_background_checks: {type: string, enum: [Yes, No]} - required: [remote_work, in_person_work, open_to_relocation, willing_to_complete_assessments, willing_to_undergo_drug_tests, willing_to_undergo_background_checks] \ No newline at end of file + required: [remote_work, in_person_work, open_to_relocation, willing_to_complete_assessments, willing_to_undergo_drug_tests, willing_to_undergo_background_checks] diff --git a/data_folder/config.yaml b/data_folder/config.yaml index f114bb0eb..90674e812 100644 --- a/data_folder/config.yaml +++ b/data_folder/config.yaml @@ -47,4 +47,4 @@ job_applicants_threshold: llm_model_type: openai llm_model: 'gpt-4o-mini' -# llm_api_url: https://api.pawan.krd/cosmosrp/v1' +# llm_api_url: 'https://api.pawan.krd/cosmosrp/v1' diff --git a/main.py b/main.py index 9f3166d60..82c66f0c7 100644 --- a/main.py +++ b/main.py @@ -8,7 +8,7 @@ from selenium.webdriver.chrome.service import Service as ChromeService from webdriver_manager.chrome import ChromeDriverManager from selenium.common.exceptions import WebDriverException -from lib_resume_builder_AIHawk import Resume,StyleManager,FacadeManager,ResumeGenerator +from lib_resume_builder_AIHawk import Resume, FacadeManager, ResumeGenerator, StyleManager from src.utils import chrome_browser_options from src.llm.llm_manager import GPTAnswerer from src.aihawk_authenticator import AIHawkAuthenticator @@ -17,8 +17,8 @@ from src.job_application_profile import JobApplicationProfile from loguru import logger -# Suppress stderr -sys.stderr = open(os.devnull, 'w') +# Suppress stderr only during specific operations +original_stderr = sys.stderr class ConfigError(Exception): pass @@ -37,8 +37,8 @@ def validate_yaml_file(yaml_path: Path) -> dict: raise ConfigError(f"Error reading file {yaml_path}: {exc}") except FileNotFoundError: raise ConfigError(f"File not found: {yaml_path}") - - + + @staticmethod def validate_config(config_yaml_path: Path) -> dict: parameters = ConfigValidator.validate_yaml_file(config_yaml_path) required_keys = { @@ -67,30 +67,36 @@ def validate_config(config_yaml_path: Path) -> dict: else: raise ConfigError(f"Invalid type for key '{key}' in config file {config_yaml_path}. Expected {expected_type}.") + # Validate experience levels, ensure they are boolean experience_levels = ['internship', 'entry', 'associate', 'mid-senior level', 'director', 'executive'] for level in experience_levels: if not isinstance(parameters['experienceLevel'].get(level), bool): raise ConfigError(f"Experience level '{level}' must be a boolean in config file {config_yaml_path}") + # Validate job types, ensure they are boolean job_types = ['full-time', 'contract', 'part-time', 'temporary', 'internship', 'other', 'volunteer'] for job_type in job_types: if not isinstance(parameters['jobTypes'].get(job_type), bool): raise ConfigError(f"Job type '{job_type}' must be a boolean in config file {config_yaml_path}") + # Validate date filters date_filters = ['all time', 'month', 'week', '24 hours'] for date_filter in date_filters: if not isinstance(parameters['date'].get(date_filter), bool): raise ConfigError(f"Date filter '{date_filter}' must be a boolean in config file {config_yaml_path}") + # Validate positions and locations as lists of strings if not all(isinstance(pos, str) for pos in parameters['positions']): raise ConfigError(f"'positions' must be a list of strings in config file {config_yaml_path}") if not all(isinstance(loc, str) for loc in parameters['locations']): raise ConfigError(f"'locations' must be a list of strings in config file {config_yaml_path}") + # Validate distance approved_distances = {0, 5, 10, 25, 50, 100} if parameters['distance'] not in approved_distances: raise ConfigError(f"Invalid distance value in config file {config_yaml_path}. Must be one of: {approved_distances}") + # Ensure blacklists are lists for blacklist in ['companyBlacklist', 'titleBlacklist']: if not isinstance(parameters.get(blacklist), list): raise ConfigError(f"'{blacklist}' must be a list in config file {config_yaml_path}") @@ -99,10 +105,8 @@ def validate_config(config_yaml_path: Path) -> dict: return parameters - - @staticmethod - def validate_secrets(secrets_yaml_path: Path) -> tuple: + def validate_secrets(secrets_yaml_path: Path) -> str: secrets = ConfigValidator.validate_yaml_file(secrets_yaml_path) mandatory_secrets = ['llm_api_key'] @@ -115,10 +119,6 @@ def validate_secrets(secrets_yaml_path: Path) -> tuple: return secrets['llm_api_key'] class FileManager: - @staticmethod - def find_file(name_containing: str, with_extension: str, at_path: Path) -> Path: - return next((file for file in at_path.iterdir() if name_containing.lower() in file.name.lower() and file.suffix.lower() == with_extension.lower()), None) - @staticmethod def validate_data_folder(app_data_folder: Path) -> tuple: if not app_data_folder.exists() or not app_data_folder.is_dir(): @@ -150,7 +150,6 @@ def file_paths_to_dict(resume_file: Path | None, plain_text_resume_file: Path) - def init_browser() -> webdriver.Chrome: try: - options = chrome_browser_options() service = ChromeService(ChromeDriverManager().install()) return webdriver.Chrome(service=service, options=options) @@ -165,9 +164,9 @@ def create_and_run_bot(parameters, llm_api_key): plain_text_resume = file.read() resume_object = Resume(plain_text_resume) resume_generator_manager = FacadeManager(llm_api_key, style_manager, resume_generator, resume_object, Path("data_folder/output")) - os.system('cls' if os.name == 'nt' else 'clear') + + # Run the resume generator manager's functions resume_generator_manager.choose_style() - os.system('cls' if os.name == 'nt' else 'clear') job_application_profile_object = JobApplicationProfile(plain_text_resume) @@ -211,18 +210,14 @@ def main(collect: False, resume: Path = None): except ConfigError as ce: logger.error(f"Configuration error: {str(ce)}") logger.error(f"Refer to the configuration guide for troubleshooting: https://github.com/feder-cr/Auto_Jobs_Applier_AIHawk?tab=readme-ov-file#configuration {str(ce)}") + except FileNotFoundError as fnf: logger.error(f"File not found: {str(fnf)}") logger.error("Ensure all required files are present in the data folder.") - logger.error("Refer to the file setup guide: https://github.com/feder-cr/AIHawk_AIHawk_automatic_job_application/blob/main/readme.md#configuration") except RuntimeError as re: - logger.error(f"Runtime error: {str(re)}") - - logger.error("Refer to the configuration and troubleshooting guide: https://github.com/feder-cr/AIHawk_AIHawk_automatic_job_application/blob/main/readme.md#configuration") except Exception as e: logger.error(f"An unexpected error occurred: {str(e)}") - logger.error("Refer to the general troubleshooting guide: https://github.com/feder-cr/AIHawk_AIHawk_automatic_job_application/blob/main/readme.md#configuration") if __name__ == "__main__": main()