bourahima-coulibaly commited on
Commit ·
e8013a0
1
Parent(s): 2e85219
app
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .github/workflows/python-app.yml +45 -0
- .streamlit/config.toml +5 -0
- .vscode/settings.json +5 -0
- CVGenius.egg-info/PKG-INFO +16 -0
- CVGenius.egg-info/SOURCES.txt +18 -0
- CVGenius.egg-info/dependency_links.txt +1 -0
- CVGenius.egg-info/requires.txt +7 -0
- CVGenius.egg-info/top_level.txt +3 -0
- MANIFEST.in +2 -0
- README copy.md +70 -0
- __init__.py +0 -0
- __pycache__/__init__.cpython-311.pyc +0 -0
- __pycache__/app.cpython-311.pyc +0 -0
- __pycache__/conftest.cpython-311-pytest-8.2.2.pyc +0 -0
- app.py +35 -0
- auth/__init__.py +0 -0
- auth/__pycache__/__init__.cpython-311.pyc +0 -0
- auth/__pycache__/authentification.cpython-311.pyc +0 -0
- auth/authentification.py +34 -0
- auth/config.yaml +21 -0
- configuration/__init__.py +0 -0
- configuration/__pycache__/__init__.cpython-311.pyc +0 -0
- configuration/__pycache__/config.cpython-311.pyc +0 -0
- configuration/config.py +53 -0
- conftest.py +0 -0
- image.png +0 -0
- images/background.jpg +0 -0
- images/doc1.png +0 -0
- images/doc2.png +0 -0
- images/logo.png +0 -0
- requirements.txt +12 -0
- scr/__init__.py +0 -0
- scr/__pycache__/__init__.cpython-311.pyc +0 -0
- scr/__pycache__/__init__.cpython-312.pyc +0 -0
- scr/__pycache__/documentation.cpython-311.pyc +0 -0
- scr/__pycache__/logs.cpython-311.pyc +0 -0
- scr/__pycache__/logs.cpython-312.pyc +0 -0
- scr/__pycache__/models.cpython-311.pyc +0 -0
- scr/__pycache__/models.cpython-312.pyc +0 -0
- scr/__pycache__/utils.cpython-311.pyc +0 -0
- scr/__pycache__/utils.cpython-312.pyc +0 -0
- scr/documentation.py +71 -0
- scr/logs.py +46 -0
- scr/models.py +243 -0
- scr/utils.py +26 -0
- setup.py +41 -0
- tests/__init__.py +0 -0
- tests/__pycache__/__init__.cpython-311.pyc +0 -0
- tests/__pycache__/test_app.cpython-311-pytest-8.2.2.pyc +0 -0
- tests/__pycache__/test_app.cpython-312-pytest-8.2.1.pyc +0 -0
.github/workflows/python-app.yml
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Python application
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
push:
|
| 5 |
+
branches: [ "main" ]
|
| 6 |
+
pull_request:
|
| 7 |
+
branches: [ "main" ]
|
| 8 |
+
|
| 9 |
+
permissions:
|
| 10 |
+
contents: read
|
| 11 |
+
|
| 12 |
+
jobs:
|
| 13 |
+
build:
|
| 14 |
+
runs-on: ubuntu-latest
|
| 15 |
+
|
| 16 |
+
steps:
|
| 17 |
+
- uses: actions/checkout@v4
|
| 18 |
+
|
| 19 |
+
- name: Set up Python 3.10
|
| 20 |
+
uses: actions/setup-python@v3
|
| 21 |
+
with:
|
| 22 |
+
python-version: "3.10"
|
| 23 |
+
|
| 24 |
+
- name: Install dependencies
|
| 25 |
+
run: |
|
| 26 |
+
python -m pip install --upgrade pip
|
| 27 |
+
pip install flake8 pytest
|
| 28 |
+
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
| 29 |
+
|
| 30 |
+
- name: Install project in editable mode
|
| 31 |
+
run: |
|
| 32 |
+
pip install -e .
|
| 33 |
+
|
| 34 |
+
- name: Set PYTHONPATH
|
| 35 |
+
run: |
|
| 36 |
+
echo "PYTHONPATH=${{ github.workspace }}" >> $GITHUB_ENV
|
| 37 |
+
|
| 38 |
+
- name: Lint with flake8
|
| 39 |
+
run: |
|
| 40 |
+
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
| 41 |
+
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
| 42 |
+
|
| 43 |
+
- name: Test with pytest
|
| 44 |
+
run: |
|
| 45 |
+
pytest
|
.streamlit/config.toml
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[theme]
|
| 2 |
+
primaryColor="#ef9462"
|
| 3 |
+
backgroundColor="#e1e2e2"
|
| 4 |
+
secondaryBackgroundColor="#d3d4d4"
|
| 5 |
+
textColor="#080101"
|
.vscode/settings.json
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"DockerRun.DisableAutoGenerateConfig": true,
|
| 3 |
+
"taipyStudio.gUI.elementsFilePaths": [],
|
| 4 |
+
"CodeGPT.apiKey": "CodeGPT Plus Beta"
|
| 5 |
+
}
|
CVGenius.egg-info/PKG-INFO
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.1
|
| 2 |
+
Name: CVGenius
|
| 3 |
+
Version: 0.1.0
|
| 4 |
+
Summary: A comprehensive CV management application
|
| 5 |
+
Home-page: https://github.com/coulibaly-b/CVGenius
|
| 6 |
+
Author: Ibrahim COULIBALY
|
| 7 |
+
Author-email: icoulbi4@gmail.com
|
| 8 |
+
Classifier: Development Status :: 3 - Alpha
|
| 9 |
+
Classifier: Intended Audience :: Developers
|
| 10 |
+
Classifier: License :: OSI Approved :: MIT License
|
| 11 |
+
Classifier: Programming Language :: Python :: 3.10
|
| 12 |
+
Classifier: Programming Language :: Python :: 3.9
|
| 13 |
+
Classifier: Programming Language :: Python :: 3.8
|
| 14 |
+
Classifier: Programming Language :: Python :: 3.7
|
| 15 |
+
Requires-Python: >=3.7
|
| 16 |
+
Provides-Extra: dev
|
CVGenius.egg-info/SOURCES.txt
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MANIFEST.in
|
| 2 |
+
README.md
|
| 3 |
+
setup.py
|
| 4 |
+
CVGenius.egg-info/PKG-INFO
|
| 5 |
+
CVGenius.egg-info/SOURCES.txt
|
| 6 |
+
CVGenius.egg-info/dependency_links.txt
|
| 7 |
+
CVGenius.egg-info/requires.txt
|
| 8 |
+
CVGenius.egg-info/top_level.txt
|
| 9 |
+
auth/__init__.py
|
| 10 |
+
auth/authentification.py
|
| 11 |
+
auth/config.yaml
|
| 12 |
+
configuration/__init__.py
|
| 13 |
+
configuration/config.py
|
| 14 |
+
scr/__init__.py
|
| 15 |
+
scr/documentation.py
|
| 16 |
+
scr/logs.py
|
| 17 |
+
scr/models.py
|
| 18 |
+
scr/utils.py
|
CVGenius.egg-info/dependency_links.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
|
CVGenius.egg-info/requires.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
pytest
|
| 2 |
+
pyyaml
|
| 3 |
+
|
| 4 |
+
[dev]
|
| 5 |
+
pytest-cov
|
| 6 |
+
flake8
|
| 7 |
+
black
|
CVGenius.egg-info/top_level.txt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
auth
|
| 2 |
+
configuration
|
| 3 |
+
scr
|
MANIFEST.in
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
include README.md
|
| 2 |
+
include auth/*.yaml # or any other data files
|
README copy.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: CvGeniusAI
|
| 3 |
+
emoji: 📉
|
| 4 |
+
colorFrom: red
|
| 5 |
+
colorTo: red
|
| 6 |
+
sdk: streamlit
|
| 7 |
+
sdk_version: 1.35.0
|
| 8 |
+
app_file: app.py
|
| 9 |
+
pinned: false
|
| 10 |
+
license: apache-2.0
|
| 11 |
+
---
|
| 12 |
+
|
| 13 |
+
# CV Genius
|
| 14 |
+
|
| 15 |
+
CV Genius est une application web Streamlit utilisant l'IA pour assister les utilisateurs dans leur processus de candidature. Elle offre des fonctionnalités telles que le scoring de CV, la génération de lettres de motivation et l'amélioration de CV.
|
| 16 |
+
|
| 17 |
+

|
| 18 |
+
[Lien vers l'application](https://extia-cvgenius.streamlit.app/)
|
| 19 |
+
|
| 20 |
+
## Fonctionnalités
|
| 21 |
+
|
| 22 |
+
- Scoring CV/Offre d'emploi
|
| 23 |
+
- Génération de lettre de motivation
|
| 24 |
+
- Amélioration de CV
|
| 25 |
+
- Complétion de mail
|
| 26 |
+
|
| 27 |
+
## Prérequis
|
| 28 |
+
|
| 29 |
+
- Python 3.8+
|
| 30 |
+
- Dépendances du fichier `requirements.txt`
|
| 31 |
+
- Jeton d'API Hugging Face
|
| 32 |
+
|
| 33 |
+
## Installation
|
| 34 |
+
|
| 35 |
+
1. Cloner le dépôt :
|
| 36 |
+
|
| 37 |
+
```bash
|
| 38 |
+
git clone https://huggingface.co/spaces/bourahima/CvGeniusAI
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
+
2. Installez les dépendances :
|
| 42 |
+
|
| 43 |
+
```bash
|
| 44 |
+
pip install -r requirements.txt
|
| 45 |
+
```
|
| 46 |
+
|
| 47 |
+
3. Configurer le jeton API :
|
| 48 |
+
Créez un fichier `.env` à la racine du projet avec :
|
| 49 |
+
|
| 50 |
+
```txt
|
| 51 |
+
huggingface_api_key=YOUR_API_TOKEN
|
| 52 |
+
```
|
| 53 |
+
|
| 54 |
+
## Utilisation
|
| 55 |
+
|
| 56 |
+
1. Lancer l'application :
|
| 57 |
+
|
| 58 |
+
```bash
|
| 59 |
+
streamlit run app.py
|
| 60 |
+
```
|
| 61 |
+
|
| 62 |
+
2. Suivre les instructions dans l'interface utilisateur.
|
| 63 |
+
|
| 64 |
+
## Contribution
|
| 65 |
+
|
| 66 |
+
1. Forker le dépôt
|
| 67 |
+
2. Créer une branche (`git checkout -b feature/nouvelle-fonctionnalite`)
|
| 68 |
+
3. Commiter les changements (`git commit -am 'Ajoute une nouvelle fonctionnalité'`)
|
| 69 |
+
4. Pousser la branche (`git push origin feature/nouvelle-fonctionnalite`)
|
| 70 |
+
5. Ouvrir une Pull Request
|
__init__.py
ADDED
|
File without changes
|
__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (237 Bytes). View file
|
|
|
__pycache__/app.cpython-311.pyc
ADDED
|
Binary file (1.65 kB). View file
|
|
|
__pycache__/conftest.cpython-311-pytest-8.2.2.pyc
ADDED
|
Binary file (237 Bytes). View file
|
|
|
app.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
from auth.authentification import authenticate_user
|
| 3 |
+
from ui.navigation.render_navigation import render_navigation
|
| 4 |
+
from ui.home.render_home import render_home
|
| 5 |
+
from ui.infos.render_infos import render_infos
|
| 6 |
+
from ui.todos.render_todos import render_todos
|
| 7 |
+
from configuration.config import setup_page_config
|
| 8 |
+
from scr.documentation import documentations
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def main(run_setup=True, test_mode=False):
|
| 12 |
+
if run_setup:
|
| 13 |
+
setup_page_config()
|
| 14 |
+
|
| 15 |
+
is_authenticated = authenticate_user()
|
| 16 |
+
|
| 17 |
+
if test_mode:
|
| 18 |
+
return is_authenticated
|
| 19 |
+
|
| 20 |
+
if is_authenticated:
|
| 21 |
+
documentations()
|
| 22 |
+
selected_page = render_navigation()
|
| 23 |
+
|
| 24 |
+
if selected_page == "Home":
|
| 25 |
+
render_home()
|
| 26 |
+
elif selected_page == "Infos":
|
| 27 |
+
render_infos()
|
| 28 |
+
elif selected_page == "To Do's":
|
| 29 |
+
render_todos()
|
| 30 |
+
else:
|
| 31 |
+
st.error("Authentication failed")
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
if __name__ == "__main__":
|
| 35 |
+
main()
|
auth/__init__.py
ADDED
|
File without changes
|
auth/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (242 Bytes). View file
|
|
|
auth/__pycache__/authentification.cpython-311.pyc
ADDED
|
Binary file (1.98 kB). View file
|
|
|
auth/authentification.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import streamlit_authenticator as stauth
|
| 3 |
+
import yaml
|
| 4 |
+
from yaml.loader import SafeLoader
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def load_config():
|
| 8 |
+
with open('auth/config.yaml') as file:
|
| 9 |
+
config = yaml.load(file, Loader=SafeLoader)
|
| 10 |
+
return config
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def authenticate_user():
|
| 14 |
+
config = load_config()
|
| 15 |
+
authenticator = stauth.Authenticate(
|
| 16 |
+
config['credentials'],
|
| 17 |
+
config['cookie']['name'],
|
| 18 |
+
config['cookie']['key'],
|
| 19 |
+
config['cookie']['expiry_days'],
|
| 20 |
+
config['pre-authorized']
|
| 21 |
+
)
|
| 22 |
+
|
| 23 |
+
name, authentication_status, username = authenticator.login()
|
| 24 |
+
|
| 25 |
+
if authentication_status is False:
|
| 26 |
+
st.error('Username/password is incorrect')
|
| 27 |
+
elif authentication_status is None:
|
| 28 |
+
st.warning('Please enter your username and password')
|
| 29 |
+
elif authentication_status:
|
| 30 |
+
authenticator.logout('Logout', 'sidebar')
|
| 31 |
+
st.sidebar.markdown(f"Bienvenue, {name} ! 👋")
|
| 32 |
+
return True
|
| 33 |
+
|
| 34 |
+
return False
|
auth/config.yaml
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
credentials:
|
| 2 |
+
usernames:
|
| 3 |
+
jsmith:
|
| 4 |
+
email: jsmith@gmail.com
|
| 5 |
+
failed_login_attempts: 0 # Will be managed automatically
|
| 6 |
+
logged_in: False # Will be managed automatically
|
| 7 |
+
name: John Smith
|
| 8 |
+
password: abc # Will be hashed automatically
|
| 9 |
+
rbriggs:
|
| 10 |
+
email: rbriggs@gmail.com
|
| 11 |
+
failed_login_attempts: 0 # Will be managed automatically
|
| 12 |
+
logged_in: False # Will be managed automatically
|
| 13 |
+
name: Rebecca Briggs
|
| 14 |
+
password: def # Will be hashed automatically
|
| 15 |
+
cookie:
|
| 16 |
+
expiry_days: 30
|
| 17 |
+
key: some_signature_key # Must be string
|
| 18 |
+
name: some_cookie_name
|
| 19 |
+
pre-authorized:
|
| 20 |
+
emails:
|
| 21 |
+
- melsby@gmail.com
|
configuration/__init__.py
ADDED
|
File without changes
|
configuration/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (251 Bytes). View file
|
|
|
configuration/__pycache__/config.cpython-311.pyc
ADDED
|
Binary file (1.92 kB). View file
|
|
|
configuration/config.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
from PIL import Image
|
| 3 |
+
import os
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def setup_page_config():
|
| 7 |
+
logo_path = os.path.join(os.getcwd(), "images", "logo.png")
|
| 8 |
+
logo = Image.open(logo_path)
|
| 9 |
+
|
| 10 |
+
st.set_page_config(
|
| 11 |
+
page_title="CV Genius",
|
| 12 |
+
page_icon=logo,
|
| 13 |
+
initial_sidebar_state="collapsed",
|
| 14 |
+
layout='wide'
|
| 15 |
+
)
|
| 16 |
+
|
| 17 |
+
# Add CSS styles
|
| 18 |
+
st.markdown("""
|
| 19 |
+
<style>
|
| 20 |
+
.title {
|
| 21 |
+
font-size: 36px;
|
| 22 |
+
font-weight: bold;
|
| 23 |
+
color: #2D3E50;
|
| 24 |
+
}
|
| 25 |
+
.subtitle {
|
| 26 |
+
font-size: 24px;
|
| 27 |
+
font-weight: bold;
|
| 28 |
+
color: #4A6F8A;
|
| 29 |
+
}
|
| 30 |
+
.description {
|
| 31 |
+
font-size: 18px;
|
| 32 |
+
color: #6C8798;
|
| 33 |
+
}
|
| 34 |
+
</style>
|
| 35 |
+
""", unsafe_allow_html=True)
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def get_over_theme():
|
| 39 |
+
return {
|
| 40 |
+
"txc_inactive": "#FFFFFF",
|
| 41 |
+
"color": "#FF6300",
|
| 42 |
+
"txc_active": "#2D3E50",
|
| 43 |
+
"MENU_BACKGROUND": "#FF6300",
|
| 44 |
+
"txc_hover": "#4A6F8A",
|
| 45 |
+
"MENU_BACKGROUND_HOVER": "#4A6F8A",
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def get_menu_data():
|
| 50 |
+
return [
|
| 51 |
+
{"id": "Infos", "icon": "💡", "label": "Infos"},
|
| 52 |
+
{"icon": "🚀", "label": "To Do's"},
|
| 53 |
+
]
|
conftest.py
ADDED
|
File without changes
|
image.png
ADDED
|
images/background.jpg
ADDED
|
images/doc1.png
ADDED
|
images/doc2.png
ADDED
|
images/logo.png
ADDED
|
requirements.txt
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
streamlit==1.36.0
|
| 2 |
+
streamlit-pills==0.3.0
|
| 3 |
+
langchain==0.2.9
|
| 4 |
+
langchain-community==0.2.7
|
| 5 |
+
langchain-huggingface==0.0.3
|
| 6 |
+
pdfminer-six==20231228
|
| 7 |
+
streamlit-aggrid==0.2.2-2
|
| 8 |
+
reportlab==4.2.2
|
| 9 |
+
hydralit==1.0.14
|
| 10 |
+
python-dotenv
|
| 11 |
+
werkzeug==3.0.3
|
| 12 |
+
streamlit-authenticator==0.3.2
|
scr/__init__.py
ADDED
|
File without changes
|
scr/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (241 Bytes). View file
|
|
|
scr/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (209 Bytes). View file
|
|
|
scr/__pycache__/documentation.cpython-311.pyc
ADDED
|
Binary file (4.67 kB). View file
|
|
|
scr/__pycache__/logs.cpython-311.pyc
ADDED
|
Binary file (2.6 kB). View file
|
|
|
scr/__pycache__/logs.cpython-312.pyc
ADDED
|
Binary file (4.86 kB). View file
|
|
|
scr/__pycache__/models.cpython-311.pyc
ADDED
|
Binary file (12.4 kB). View file
|
|
|
scr/__pycache__/models.cpython-312.pyc
ADDED
|
Binary file (13 kB). View file
|
|
|
scr/__pycache__/utils.cpython-311.pyc
ADDED
|
Binary file (1.74 kB). View file
|
|
|
scr/__pycache__/utils.cpython-312.pyc
ADDED
|
Binary file (1.44 kB). View file
|
|
|
scr/documentation.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
from PIL import Image
|
| 3 |
+
import os
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def documentations():
|
| 7 |
+
st.sidebar.image(Image.open(
|
| 8 |
+
os.path.join(os.getcwd(),
|
| 9 |
+
"images",
|
| 10 |
+
"background.jpg"
|
| 11 |
+
)
|
| 12 |
+
)
|
| 13 |
+
)
|
| 14 |
+
|
| 15 |
+
st.sidebar.markdown(
|
| 16 |
+
"<div class='title'>CV Genius</div>", unsafe_allow_html=True
|
| 17 |
+
)
|
| 18 |
+
st.sidebar.markdown(
|
| 19 |
+
"<div class='subtitle'>Améliorez votre processus de candidature</div>",
|
| 20 |
+
unsafe_allow_html=True,
|
| 21 |
+
)
|
| 22 |
+
st.sidebar.markdown(
|
| 23 |
+
"""<div class='description'>Cette application utilise des modèles d'IA
|
| 24 |
+
pour vous aider à scorer votre CV, rédiger des lettres de motivation et
|
| 25 |
+
améliorer votre CV.</div>""",
|
| 26 |
+
unsafe_allow_html=True,
|
| 27 |
+
)
|
| 28 |
+
with st.sidebar.expander("Documentation", expanded=True):
|
| 29 |
+
st.write("""Welcome to the documentation page for our project.
|
| 30 |
+
Here you will find all the necessary information to get
|
| 31 |
+
started with the Hugging Face API and Streamlit settings.
|
| 32 |
+
""")
|
| 33 |
+
|
| 34 |
+
st.header("Hugging Face Access Key")
|
| 35 |
+
st.write("""To use the Hugging Face API, you need to obtain an
|
| 36 |
+
access key. Follow these steps to get your key:""")
|
| 37 |
+
st.markdown(
|
| 38 |
+
"""
|
| 39 |
+
1. Go to the [Hugging Face website](https://huggingface.co/).
|
| 40 |
+
""")
|
| 41 |
+
st.markdown("""
|
| 42 |
+
2. Sign up for an account if you don't have one already.
|
| 43 |
+
""")
|
| 44 |
+
st.markdown("3. Once logged in, go to your profile settings.")
|
| 45 |
+
st.markdown("4. Look for the 'Access Tokens' section.")
|
| 46 |
+
st.markdown("5. Generate a new access token and copy it.")
|
| 47 |
+
st.markdown("""
|
| 48 |
+
<a href=https://huggingface.co/docs/hub/security-tokens>
|
| 49 |
+
For more information</a>""", unsafe_allow_html=True)
|
| 50 |
+
|
| 51 |
+
st.header("Setting Hugging Face Access Key in Streamlit")
|
| 52 |
+
st.write("""To use the Hugging Face API in your Streamlit app,
|
| 53 |
+
you need to set the access key as an environment
|
| 54 |
+
variable. Follow these steps:""")
|
| 55 |
+
st.markdown("1. Open your application.")
|
| 56 |
+
st.markdown("""2. At the bottom of your Streamlit app, click
|
| 57 |
+
on the manage app button, then select the 3 horizontal
|
| 58 |
+
dots, go to the settings, and select 'secrets' to
|
| 59 |
+
replace the new access key.""")
|
| 60 |
+
|
| 61 |
+
# Load and display images
|
| 62 |
+
doc1_path = os.path.join(os.getcwd(), "images", "doc1.png")
|
| 63 |
+
doc1 = Image.open(doc1_path)
|
| 64 |
+
st.image(doc1, caption="Manage app", use_column_width=True)
|
| 65 |
+
|
| 66 |
+
doc2_path = os.path.join(os.getcwd(), "images", "doc2.png")
|
| 67 |
+
doc2 = Image.open(doc2_path)
|
| 68 |
+
st.image(doc2, caption="Manage app", use_column_width=True)
|
| 69 |
+
|
| 70 |
+
st.write("""Now you can use the Hugging Face API in your
|
| 71 |
+
Streamlit app without any issues.""")
|
scr/logs.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import streamlit as st
|
| 3 |
+
from werkzeug.security import generate_password_hash, check_password_hash
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
USERS_FILE = "users.json"
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def load_users():
|
| 10 |
+
try:
|
| 11 |
+
with open(USERS_FILE, "r") as f:
|
| 12 |
+
content = f.read().strip()
|
| 13 |
+
if content:
|
| 14 |
+
return json.loads(content)
|
| 15 |
+
else:
|
| 16 |
+
return {}
|
| 17 |
+
except (FileNotFoundError, json.JSONDecodeError):
|
| 18 |
+
return {}
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def save_users(users):
|
| 22 |
+
with open(USERS_FILE, "w") as f:
|
| 23 |
+
json.dump(users, f)
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def add_user(username, password):
|
| 27 |
+
users = load_users()
|
| 28 |
+
if username in users:
|
| 29 |
+
return False
|
| 30 |
+
hashed_password = generate_password_hash(password)
|
| 31 |
+
users[username] = hashed_password
|
| 32 |
+
save_users(users)
|
| 33 |
+
return True
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def check_credentials(username, password):
|
| 37 |
+
users = load_users()
|
| 38 |
+
if username not in users:
|
| 39 |
+
return False
|
| 40 |
+
return check_password_hash(users[username], password)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def logout():
|
| 44 |
+
st.session_state.logged_in = False
|
| 45 |
+
st.session_state.username = None
|
| 46 |
+
st.rerun() # Recharger l'application pour refléter les changements
|
scr/models.py
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from langchain_huggingface.llms import HuggingFaceEndpoint
|
| 2 |
+
from langchain_core.prompts import PromptTemplate
|
| 3 |
+
|
| 4 |
+
from requests.exceptions import HTTPError
|
| 5 |
+
import streamlit as st
|
| 6 |
+
from scr.utils import ModelError
|
| 7 |
+
from abc import ABC, abstractmethod
|
| 8 |
+
|
| 9 |
+
import os
|
| 10 |
+
from dotenv import load_dotenv
|
| 11 |
+
|
| 12 |
+
load_dotenv()
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
HUGGINGFACE_HUB_API_TOKEN = os.getenv("huggingface_api_key")
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class ResumeAIStrategy(ABC):
|
| 19 |
+
|
| 20 |
+
def __init__(self):
|
| 21 |
+
self.llm = HuggingFaceEndpoint(
|
| 22 |
+
repo_id="mistralai/MixTraL-8x7B-Instruct-v0.1",
|
| 23 |
+
temperature=0.001,
|
| 24 |
+
repetition_penalty=1.2,
|
| 25 |
+
max_length=10000,
|
| 26 |
+
max_new_tokens=2000,
|
| 27 |
+
huggingfacehub_api_token=HUGGINGFACE_HUB_API_TOKEN,
|
| 28 |
+
add_to_git_credential=True,
|
| 29 |
+
)
|
| 30 |
+
|
| 31 |
+
@abstractmethod
|
| 32 |
+
def generate(self, resume: str, job_advert: str) -> str:
|
| 33 |
+
pass
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class ScoreResumeJob(ResumeAIStrategy):
|
| 37 |
+
def __init__(self):
|
| 38 |
+
super().__init__()
|
| 39 |
+
|
| 40 |
+
def generate(self, resume: str, job_advert: str) -> str:
|
| 41 |
+
prompt = """Évaluez la correspondance entre le CV d'un candidat et
|
| 42 |
+
la description du poste en attribuant un score sur 100. Tenez compte
|
| 43 |
+
des éléments suivants par ordre d'importance :
|
| 44 |
+
|
| 45 |
+
1. Dernière expérience professionnelle et sa durée
|
| 46 |
+
2. Deuxième expérience professionnelle et sa durée
|
| 47 |
+
3. Présence de lacunes dans le parcours professionnel
|
| 48 |
+
4. Formations, certifications et renommée des institutions
|
| 49 |
+
5. Technologies maîtrisées
|
| 50 |
+
6. Projets réalisés
|
| 51 |
+
7. Engagement dans des actions humanitaires
|
| 52 |
+
|
| 53 |
+
Notez que la maîtrise d'un langage de programmation implique la
|
| 54 |
+
connaissance des librairies et frameworks associés. Répondez
|
| 55 |
+
au format suivant :
|
| 56 |
+
|
| 57 |
+
- Score de correspondance : [Score sur 100]
|
| 58 |
+
- Points forts : [Liste des principaux points forts du candidat]
|
| 59 |
+
- Points faibles : [Liste des principaux points faibles du candidat]
|
| 60 |
+
- Explication : [Paragraphe expliquant le score, les points forts
|
| 61 |
+
et faibles]
|
| 62 |
+
|
| 63 |
+
Exemple :
|
| 64 |
+
Score de correspondance : 85/100
|
| 65 |
+
Points forts :
|
| 66 |
+
- Expérience de 5 ans dans un rôle similaire
|
| 67 |
+
- Maîtrise des technologies requises
|
| 68 |
+
- Formation pertinente
|
| 69 |
+
|
| 70 |
+
Points faibles :
|
| 71 |
+
- Manque d'expérience avec un outil spécifique
|
| 72 |
+
- Absence de certifications professionnelles
|
| 73 |
+
|
| 74 |
+
Explication : Le candidat est bien qualifié avec une solide expérience
|
| 75 |
+
et des compétences pertinentes, bien que quelques domaines nécessitent
|
| 76 |
+
des améliorations.
|
| 77 |
+
|
| 78 |
+
CV : {resume}
|
| 79 |
+
Description du poste : {job_advert}
|
| 80 |
+
Réponse :"""
|
| 81 |
+
|
| 82 |
+
prompt_template = PromptTemplate(
|
| 83 |
+
template=prompt, input_variables=["resume", "job_advert"]
|
| 84 |
+
)
|
| 85 |
+
chain = prompt_template | self.llm
|
| 86 |
+
result = chain.invoke({"resume": resume, "job_advert": job_advert})
|
| 87 |
+
return result
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
class CoverLetterGenerator(ResumeAIStrategy):
|
| 91 |
+
def __init__(self):
|
| 92 |
+
super().__init__()
|
| 93 |
+
|
| 94 |
+
def generate(self, resume: str, job_advert: str) -> str:
|
| 95 |
+
prompt = """Générez une lettre de motivation professionnelle pour le
|
| 96 |
+
poste décrit ci-dessous, en vous basant sur le CV fourni. La lettre
|
| 97 |
+
doit inclure :
|
| 98 |
+
|
| 99 |
+
1. Une introduction accrocheuse
|
| 100 |
+
2. Vos expériences et compétences pertinentes pour le poste, en
|
| 101 |
+
utilisant des termes techniques spécifiques et des exemples
|
| 102 |
+
concrets de réalisations
|
| 103 |
+
3. Votre motivation pour le poste et l'entreprise
|
| 104 |
+
4. Une conclusion avec un appel à l'action
|
| 105 |
+
|
| 106 |
+
CV : {resume}
|
| 107 |
+
Description du poste : {job_advert}
|
| 108 |
+
|
| 109 |
+
Réponse :"""
|
| 110 |
+
|
| 111 |
+
prompt_template = PromptTemplate(
|
| 112 |
+
template=prompt, input_variables=["resume", "job_advert"]
|
| 113 |
+
)
|
| 114 |
+
chain = prompt_template | self.llm
|
| 115 |
+
result = chain.invoke({"resume": resume, "job_advert": job_advert})
|
| 116 |
+
return result.replace('`', '')
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
class ResumeImprover(ResumeAIStrategy):
|
| 120 |
+
def __init__(self):
|
| 121 |
+
super().__init__()
|
| 122 |
+
|
| 123 |
+
def generate(self, resume: str, job_advert: str) -> str:
|
| 124 |
+
prompt = """Améliorez le CV ci-dessous en mettant en avant les
|
| 125 |
+
compétences, qualifications et expériences pertinentes pour
|
| 126 |
+
le poste. L'objectif est d'obtenir un score de correspondance
|
| 127 |
+
supérieur à 95%.
|
| 128 |
+
|
| 129 |
+
Instructions :
|
| 130 |
+
- Résumé : Présentez brièvement le candidat en mettant en avant ses
|
| 131 |
+
forces principales et sa pertinence pour le poste, en utilisant
|
| 132 |
+
des mots-clés de l'offre.
|
| 133 |
+
- Compétences : Listez les compétences les plus pertinentes pour le
|
| 134 |
+
poste, en mettant en avant celles mentionnées dans l'offre d'emploi.
|
| 135 |
+
- Expérience professionnelle : Réorganisez et reformulez les
|
| 136 |
+
expériences pour souligner les réalisations pertinentes pour le
|
| 137 |
+
poste. Combinez les points si n��cessaire.
|
| 138 |
+
- Formation et certifications : Mettez en avant les formations et
|
| 139 |
+
certifications pertinentes, ajoutant des informations manquantes
|
| 140 |
+
si nécessaire.
|
| 141 |
+
- Projets : Reformulez et mettez en avant les projets pertinents,
|
| 142 |
+
incluant des projets open source valorisants si nécessaire.
|
| 143 |
+
- Informations supplémentaires : Ajoutez toute information valorisante
|
| 144 |
+
comme les implications bénévoles, récompenses, publications, etc.
|
| 145 |
+
|
| 146 |
+
Assurez-vous que les modifications sont professionnelles, concises
|
| 147 |
+
et positives. La structure du CV doit être cohérente et logique.
|
| 148 |
+
|
| 149 |
+
CV : {resume}
|
| 150 |
+
Offre d'emploi : {job_advert}
|
| 151 |
+
|
| 152 |
+
Contexte supplémentaire : Le candidat a une vaste expérience non
|
| 153 |
+
listée qui pourrait être pertinente pour le poste. Veuillez en
|
| 154 |
+
tenir compte.
|
| 155 |
+
|
| 156 |
+
Réponse :"""
|
| 157 |
+
|
| 158 |
+
prompt_template = PromptTemplate(
|
| 159 |
+
template=prompt, input_variables=["resume", "job_advert"]
|
| 160 |
+
)
|
| 161 |
+
chain = prompt_template | self.llm
|
| 162 |
+
result = chain.invoke({"resume": resume, "job_advert": job_advert})
|
| 163 |
+
return result
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
class ResumeGenerator:
|
| 167 |
+
def __init__(self, resume: str,
|
| 168 |
+
job_advert: str,
|
| 169 |
+
resumeStrategy: ResumeAIStrategy):
|
| 170 |
+
self.resume = resume
|
| 171 |
+
self.job_advert = job_advert
|
| 172 |
+
self.resumeStrategy = resumeStrategy
|
| 173 |
+
|
| 174 |
+
def generator(self) -> str:
|
| 175 |
+
try:
|
| 176 |
+
generated_text = self.resumeStrategy.generate(
|
| 177 |
+
resume=self.resume, job_advert=self.job_advert
|
| 178 |
+
)
|
| 179 |
+
return generated_text
|
| 180 |
+
except TimeoutError:
|
| 181 |
+
return """Le modèle a pris trop de temps pour répondre.
|
| 182 |
+
Veuillez réessayer plus tard."""
|
| 183 |
+
except ModelError as e:
|
| 184 |
+
return f"""Une erreur s'est produite lors de l'appel
|
| 185 |
+
au modèle : {str(e)}"""
|
| 186 |
+
except HTTPError as http_err:
|
| 187 |
+
if http_err.response.status_code == 500:
|
| 188 |
+
request_id = http_err.response.headers.get("x-request-id",
|
| 189 |
+
"N/A")
|
| 190 |
+
return f"""API problem: Internal Server Error
|
| 191 |
+
(Request ID: {request_id})"""
|
| 192 |
+
else:
|
| 193 |
+
return f"HTTP error occurred: {http_err}"
|
| 194 |
+
except Exception as e:
|
| 195 |
+
return f"Une erreur inattendue s'est produite : {str(e)}"
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
class MailCompletion:
|
| 199 |
+
def __init__(self):
|
| 200 |
+
self.llm = HuggingFaceEndpoint(
|
| 201 |
+
repo_id="mistralai/MixTraL-8x7B-Instruct-v0.1",
|
| 202 |
+
temperature=0.001,
|
| 203 |
+
max_new_tokens=1000,
|
| 204 |
+
huggingfacehub_api_token=HUGGINGFACE_HUB_API_TOKEN,
|
| 205 |
+
)
|
| 206 |
+
|
| 207 |
+
def mailcompletion(self, resume: str) -> str:
|
| 208 |
+
prompt = """Complétez le modèle de courrier électronique ci-dessous en
|
| 209 |
+
utilisant les informations du CV fourni. Remplacez les parties entre
|
| 210 |
+
crochets [] par les informations pertinentes du CV.
|
| 211 |
+
|
| 212 |
+
Format :
|
| 213 |
+
Bonjour,
|
| 214 |
+
|
| 215 |
+
J'espère que vous allez bien.
|
| 216 |
+
|
| 217 |
+
Je vous propose le profil de [Prénom du candidat], [Intitulé du poste]
|
| 218 |
+
avec [Nombre] année(s) d'expérience qui pourrait intéresser votre
|
| 219 |
+
équipe.
|
| 220 |
+
|
| 221 |
+
Vous trouverez son dossier technique en pièce jointe.
|
| 222 |
+
|
| 223 |
+
En quelques mots :
|
| 224 |
+
- Technologies & Langages : [Liste des technologies et langages de
|
| 225 |
+
programmation]
|
| 226 |
+
- Compétences métiers : [Liste des compétences métiers pertinentes]
|
| 227 |
+
- Dernier client : [Nom de l'entreprise de la dernière expérience]
|
| 228 |
+
- Disponibilité : [Date de disponibilité ou Immédiatement]
|
| 229 |
+
|
| 230 |
+
Qu'en pensez-vous ? Je reste à votre disposition pour toute
|
| 231 |
+
information complémentaire.
|
| 232 |
+
|
| 233 |
+
Excellente journée,
|
| 234 |
+
|
| 235 |
+
CV : {resume}
|
| 236 |
+
|
| 237 |
+
Réponse :"""
|
| 238 |
+
|
| 239 |
+
prompt_template = PromptTemplate(template=prompt,
|
| 240 |
+
input_variables=["resume"])
|
| 241 |
+
chain = prompt_template | self.llm
|
| 242 |
+
result = chain.invoke({"resume": resume})
|
| 243 |
+
return result
|
scr/utils.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pdfminer.high_level import extract_text
|
| 2 |
+
from io import BytesIO
|
| 3 |
+
|
| 4 |
+
from reportlab.lib.pagesizes import letter
|
| 5 |
+
from reportlab.platypus import SimpleDocTemplate, Paragraph
|
| 6 |
+
from reportlab.lib.styles import getSampleStyleSheet
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def extract_text_from_pdf(pdf_path):
|
| 10 |
+
return extract_text(pdf_path)
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def generate_pdf(text):
|
| 14 |
+
styles = getSampleStyleSheet()
|
| 15 |
+
pdf_buffer = BytesIO()
|
| 16 |
+
doc = SimpleDocTemplate(pdf_buffer, pagesize=letter)
|
| 17 |
+
elements = []
|
| 18 |
+
for line in text.split('\n'):
|
| 19 |
+
elements.append(Paragraph(line, styles['BodyText']))
|
| 20 |
+
doc.build(elements)
|
| 21 |
+
pdf_buffer.seek(0)
|
| 22 |
+
return pdf_buffer
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class ModelError(Exception):
|
| 26 |
+
pass
|
setup.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from setuptools import setup, find_packages
|
| 2 |
+
|
| 3 |
+
setup(
|
| 4 |
+
name='CVGenius',
|
| 5 |
+
version='0.1.0',
|
| 6 |
+
description='A comprehensive CV management application',
|
| 7 |
+
author='Ibrahim COULIBALY',
|
| 8 |
+
author_email='icoulbi4@gmail.com',
|
| 9 |
+
url='https://github.com/coulibaly-b/CVGenius',
|
| 10 |
+
packages=find_packages(exclude=['tests*']),
|
| 11 |
+
include_package_data=True,
|
| 12 |
+
install_requires=[
|
| 13 |
+
'pytest',
|
| 14 |
+
'pyyaml',
|
| 15 |
+
# Add other dependencies here
|
| 16 |
+
],
|
| 17 |
+
extras_require={
|
| 18 |
+
'dev': [
|
| 19 |
+
'pytest-cov',
|
| 20 |
+
'flake8',
|
| 21 |
+
'black',
|
| 22 |
+
# Add other development dependencies here
|
| 23 |
+
],
|
| 24 |
+
},
|
| 25 |
+
entry_points={
|
| 26 |
+
'console_scripts': [
|
| 27 |
+
# If you have any scripts to run
|
| 28 |
+
# 'script_name = module:function',
|
| 29 |
+
],
|
| 30 |
+
},
|
| 31 |
+
classifiers=[
|
| 32 |
+
'Development Status :: 3 - Alpha', # Adjust as appropriate
|
| 33 |
+
'Intended Audience :: Developers',
|
| 34 |
+
'License :: OSI Approved :: MIT License', # Adjust if different
|
| 35 |
+
'Programming Language :: Python :: 3.10',
|
| 36 |
+
'Programming Language :: Python :: 3.9',
|
| 37 |
+
'Programming Language :: Python :: 3.8',
|
| 38 |
+
'Programming Language :: Python :: 3.7',
|
| 39 |
+
],
|
| 40 |
+
python_requires='>=3.7',
|
| 41 |
+
)
|
tests/__init__.py
ADDED
|
File without changes
|
tests/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (243 Bytes). View file
|
|
|
tests/__pycache__/test_app.cpython-311-pytest-8.2.2.pyc
ADDED
|
Binary file (6.35 kB). View file
|
|
|
tests/__pycache__/test_app.cpython-312-pytest-8.2.1.pyc
ADDED
|
Binary file (1.18 kB). View file
|
|
|