From 24a836de3c49f74ecf0a15d8ffd9c45cd10054bd Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Fri, 30 May 2025 12:58:09 +0000 Subject: [PATCH 01/36] Ajouter a.txt --- a.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 a.txt diff --git a/a.txt b/a.txt new file mode 100644 index 0000000..e69de29 From b31a0a8def713fbc204a5666d4577323ca03af1e Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Fri, 30 May 2025 13:05:53 +0000 Subject: [PATCH 02/36] Ajouter test.txt --- test.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test.txt diff --git a/test.txt b/test.txt new file mode 100644 index 0000000..e69de29 From e2210cd9ca4090ef308f0efca43b731592f6fd22 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sat, 31 May 2025 17:04:15 +0000 Subject: [PATCH 03/36] Supprimer a.txt --- a.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 a.txt diff --git a/a.txt b/a.txt deleted file mode 100644 index e69de29..0000000 From d61c9ee44ae0871b43133360c9d8b19587afa31f Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sat, 31 May 2025 17:04:20 +0000 Subject: [PATCH 04/36] Supprimer test.txt --- test.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 test.txt diff --git a/test.txt b/test.txt deleted file mode 100644 index e69de29..0000000 From 44ee3724d8cc4b07f44866436526057af3a80af5 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sat, 31 May 2025 17:04:40 +0000 Subject: [PATCH 05/36] Ajouter main.py --- main.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 main.py diff --git a/main.py b/main.py new file mode 100644 index 0000000..da371f2 --- /dev/null +++ b/main.py @@ -0,0 +1,21 @@ +from fastapi import FastAPI +import mariadb +app = FastAPI() + +@app.get("/api/portfolio") +def read_portfolio(): +  try: +    conn = mariadb.connect( +      user="root", +      password="motdepasse", +      host="localhost", +      port=3306, +      database="portfolio" +    ) +    cursor = conn.cursor() +    cursor.execute("SELECT * FROM portfolio;") +    result = cursor.fetchall() +    conn.close() +    return {"data": result} +  except mariadb.Error as e: +    return {"error": str(e)} \ No newline at end of file From 9adc90de2902a90e4ce2ead82bf9a6c21ac49746 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sat, 31 May 2025 17:05:42 +0000 Subject: [PATCH 06/36] Actualiser main.py --- main.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/main.py b/main.py index da371f2..72da5be 100644 --- a/main.py +++ b/main.py @@ -4,18 +4,18 @@ app = FastAPI() @app.get("/api/portfolio") def read_portfolio(): -  try: -    conn = mariadb.connect( -      user="root", -      password="motdepasse", -      host="localhost", -      port=3306, -      database="portfolio" -    ) -    cursor = conn.cursor() -    cursor.execute("SELECT * FROM portfolio;") -    result = cursor.fetchall() -    conn.close() -    return {"data": result} -  except mariadb.Error as e: -    return {"error": str(e)} \ No newline at end of file + try: + conn = mariadb.connect( + user="root", + password="motdepasse", + host="localhost", + port=3306, + database="portfolio" + ) + cursor = conn.cursor() + cursor.execute("SELECT * FROM portfolio;") + result = cursor.fetchall() + conn.close() + return {"data": result} + except mariadb.Error as e: + return {"error": str(e)} \ No newline at end of file From 8caa17af41e720bb86c3d78d68913bd8f3a865a2 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sat, 31 May 2025 17:06:15 +0000 Subject: [PATCH 07/36] Ajouter requirements.txt --- requirements.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..63875dc --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +fastapi +uvicorn +mariadb \ No newline at end of file From 21544407661f743017bc5e5d1de9a41dd1e12285 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sun, 1 Jun 2025 15:47:47 +0200 Subject: [PATCH 08/36] add of .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d0ee3b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +venv/ +__pycache__/ +*.pyc From 7a89aee31f26bf1a689aa210b2fc11bf4af790ea Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sun, 1 Jun 2025 13:48:10 +0000 Subject: [PATCH 09/36] Ajouter test.txt --- test.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test.txt diff --git a/test.txt b/test.txt new file mode 100644 index 0000000..e69de29 From 8304940dafa8894ff200b97eeab25e706eb0baa3 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Fri, 29 Aug 2025 18:40:08 +0200 Subject: [PATCH 10/36] update requierements.txt, add of db connection function and root --- .env | 4 ++++ main.py | 61 ++++++++++++++++++++++++++++++++---------------- requirements.txt | 3 ++- test.txt | 0 4 files changed, 47 insertions(+), 21 deletions(-) create mode 100644 .env delete mode 100644 test.txt diff --git a/.env b/.env new file mode 100644 index 0000000..75d0e3d --- /dev/null +++ b/.env @@ -0,0 +1,4 @@ +DB_USER="portfolio_user" +DB_PASS="USER@gio53" +DB_HOST="localhost" +DB_NAME="portfolio" \ No newline at end of file diff --git a/main.py b/main.py index 72da5be..4e54c7a 100644 --- a/main.py +++ b/main.py @@ -1,21 +1,42 @@ -from fastapi import FastAPI -import mariadb -app = FastAPI() +import os +from fastapi import FastAPI, HTTPException +from fastapi.middleware.cors import CORSMiddleware +import MySQLdb +from dotenv import load_dotenv -@app.get("/api/portfolio") -def read_portfolio(): - try: - conn = mariadb.connect( - user="root", - password="motdepasse", - host="localhost", - port=3306, - database="portfolio" - ) - cursor = conn.cursor() - cursor.execute("SELECT * FROM portfolio;") - result = cursor.fetchall() - conn.close() - return {"data": result} - except mariadb.Error as e: - return {"error": str(e)} \ No newline at end of file +load_dotenv() + +app = FastAPI() + +origins = [ + "http://localhost", + "http://localhost:5173", +] +app.add_middleware( + CORSMiddleware, + allow_origins=origins, + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + + +def get_db_connection(): + try: + conn = MySQLdb.connect( + user=os.getenv("DB_USER"), + passwd=os.getenv("DB_PASS"), + host=os.getenv("DB_HOST"), + db=os.getenv("DB_NAME"), + charset='utf8mb4', + cursorclass=MySQLdb.cursors.DictCursor + ) + return conn + except MySQLdb.Error as e: + print(f"Erreur de connexion à la base de données : {e}") + raise HTTPException(status_code=500, detail="Erreur interne du serveur lors de la connexion à la base de données.") + + +@app.get("/") +def read_root(): + return {"status": "API Portfolio fonctionnelle"} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 63875dc..83cca42 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ fastapi uvicorn -mariadb \ No newline at end of file +mysqlclient +python-dotenv \ No newline at end of file diff --git a/test.txt b/test.txt deleted file mode 100644 index e69de29..0000000 From 0c6c69fcf3d948a27183fef270ca3a779465961a Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Fri, 29 Aug 2025 17:25:40 +0000 Subject: [PATCH 11/36] Supprimer .env --- .env | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 .env diff --git a/.env b/.env deleted file mode 100644 index 75d0e3d..0000000 --- a/.env +++ /dev/null @@ -1,4 +0,0 @@ -DB_USER="portfolio_user" -DB_PASS="USER@gio53" -DB_HOST="localhost" -DB_NAME="portfolio" \ No newline at end of file From 05c7d8cc5a1334b17e12f0911543c0a890ae6ba4 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Fri, 29 Aug 2025 19:32:13 +0200 Subject: [PATCH 12/36] add of .env in .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index d0ee3b1..cbbb8d1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ venv/ __pycache__/ *.pyc +.env \ No newline at end of file From c03a1e136d58f90bdd6194e14cc8177437997168 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Fri, 29 Aug 2025 19:41:37 +0200 Subject: [PATCH 13/36] test --- main.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/main.py b/main.py index 4e54c7a..2e6fe29 100644 --- a/main.py +++ b/main.py @@ -35,8 +35,6 @@ def get_db_connection(): except MySQLdb.Error as e: print(f"Erreur de connexion à la base de données : {e}") raise HTTPException(status_code=500, detail="Erreur interne du serveur lors de la connexion à la base de données.") - - @app.get("/") def read_root(): return {"status": "API Portfolio fonctionnelle"} \ No newline at end of file From 16f6048479006b0f3169d227565436a7acce0602 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Fri, 29 Aug 2025 19:47:17 +0200 Subject: [PATCH 14/36] test .sh --- main.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index 2e6fe29..fa6198c 100644 --- a/main.py +++ b/main.py @@ -35,6 +35,8 @@ def get_db_connection(): except MySQLdb.Error as e: print(f"Erreur de connexion à la base de données : {e}") raise HTTPException(status_code=500, detail="Erreur interne du serveur lors de la connexion à la base de données.") + + @app.get("/") def read_root(): - return {"status": "API Portfolio fonctionnelle"} \ No newline at end of file + return {"status": "API Portfolio fonctionnelle !"} \ No newline at end of file From f7a09291af08cf41cd1bce978708aeb5f16f43ca Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Fri, 29 Aug 2025 23:00:22 +0200 Subject: [PATCH 15/36] add of skills router --- routers/skills.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 routers/skills.py diff --git a/routers/skills.py b/routers/skills.py new file mode 100644 index 0000000..08e7936 --- /dev/null +++ b/routers/skills.py @@ -0,0 +1,19 @@ +from fastapi import APIRouter, HTTPException +import MySQLdb +from ..main import get_db_connection + +router = APIRouter() + +@router.get("/api/skills") +def read_experience_tasks(): + conn = get_db_connection() + try: + with conn.cursor() as cursor: + sql = "SELECT * FROM skills;" + cursor.execute(sql) + result = cursor.fetchall() + return {"data": result} + except MySQLdb.Error as e: + raise HTTPException(status_code=500, detail=f"Erreur base de données: {e}") + finally: + conn.close() \ No newline at end of file From 1175c0030b4916e0c99706f9f1528d991d747dfd Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Fri, 29 Aug 2025 23:03:33 +0200 Subject: [PATCH 16/36] correction of skills router --- main.py | 10 +++++++++- routers/skills.py | 4 ++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/main.py b/main.py index fa6198c..5ba2dd9 100644 --- a/main.py +++ b/main.py @@ -39,4 +39,12 @@ def get_db_connection(): @app.get("/") def read_root(): - return {"status": "API Portfolio fonctionnelle !"} \ No newline at end of file + return {"status": "API Portfolio fonctionnelle !"} + + + +app.include_router( + skills.router, + prefix="/api/skills", + tags=["Skills"] +) \ No newline at end of file diff --git a/routers/skills.py b/routers/skills.py index 08e7936..bee70a3 100644 --- a/routers/skills.py +++ b/routers/skills.py @@ -4,8 +4,8 @@ from ..main import get_db_connection router = APIRouter() -@router.get("/api/skills") -def read_experience_tasks(): +@router.get("/") +def read_skills(): conn = get_db_connection() try: with conn.cursor() as cursor: From 4e14592fdb106651e46ed83bbaf19ca701d35671 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Fri, 29 Aug 2025 23:08:56 +0200 Subject: [PATCH 17/36] fix of skills router --- main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/main.py b/main.py index 5ba2dd9..8ac6cc8 100644 --- a/main.py +++ b/main.py @@ -3,6 +3,7 @@ from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware import MySQLdb from dotenv import load_dotenv +from routers import skills load_dotenv() From 9a74817a080baaad21898640eaca887f188c31d9 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Fri, 29 Aug 2025 23:14:52 +0200 Subject: [PATCH 18/36] fix skills router --- database.py | 21 +++++++++++++++++++++ main.py | 23 ----------------------- routers/skills.py | 2 +- 3 files changed, 22 insertions(+), 24 deletions(-) create mode 100644 database.py diff --git a/database.py b/database.py new file mode 100644 index 0000000..1a78c13 --- /dev/null +++ b/database.py @@ -0,0 +1,21 @@ +import os +import MySQLdb +from fastapi import HTTPException +from dotenv import load_dotenv + +load_dotenv() + +def get_db_connection(): + try: + conn = MySQLdb.connect( + user=os.getenv("DB_USER"), + passwd=os.getenv("DB_PASS"), + host=os.getenv("DB_HOST"), + db=os.getenv("DB_NAME"), + charset='utf8mb4', + cursorclass=MySQLdb.cursors.DictCursor + ) + return conn + except MySQLdb.Error as e: + print(f"Erreur de connexion à la base de données : {e}") + raise HTTPException(status_code=500, detail="Erreur interne du serveur lors de la connexion à la base de données.") \ No newline at end of file diff --git a/main.py b/main.py index 8ac6cc8..e800499 100644 --- a/main.py +++ b/main.py @@ -1,12 +1,8 @@ import os from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware -import MySQLdb -from dotenv import load_dotenv from routers import skills -load_dotenv() - app = FastAPI() origins = [ @@ -21,29 +17,10 @@ app.add_middleware( allow_headers=["*"], ) - -def get_db_connection(): - try: - conn = MySQLdb.connect( - user=os.getenv("DB_USER"), - passwd=os.getenv("DB_PASS"), - host=os.getenv("DB_HOST"), - db=os.getenv("DB_NAME"), - charset='utf8mb4', - cursorclass=MySQLdb.cursors.DictCursor - ) - return conn - except MySQLdb.Error as e: - print(f"Erreur de connexion à la base de données : {e}") - raise HTTPException(status_code=500, detail="Erreur interne du serveur lors de la connexion à la base de données.") - - @app.get("/") def read_root(): return {"status": "API Portfolio fonctionnelle !"} - - app.include_router( skills.router, prefix="/api/skills", diff --git a/routers/skills.py b/routers/skills.py index bee70a3..bb739f1 100644 --- a/routers/skills.py +++ b/routers/skills.py @@ -1,6 +1,6 @@ from fastapi import APIRouter, HTTPException import MySQLdb -from ..main import get_db_connection +from ..database import get_db_connection router = APIRouter() From 4144586b5ccdf758a62f761201e5db92881f4687 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Fri, 29 Aug 2025 23:16:46 +0200 Subject: [PATCH 19/36] fix skills router --- routers/skills.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/skills.py b/routers/skills.py index bb739f1..0157fd3 100644 --- a/routers/skills.py +++ b/routers/skills.py @@ -1,6 +1,6 @@ from fastapi import APIRouter, HTTPException import MySQLdb -from ..database import get_db_connection +from database import get_db_connection router = APIRouter() From bd97946caa8639d4bf49364484c818f98ed5abbd Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Fri, 29 Aug 2025 23:21:50 +0200 Subject: [PATCH 20/36] fix skills router --- database.py | 13 ++++++++----- routers/skills.py | 12 ++++++------ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/database.py b/database.py index 1a78c13..a674b6f 100644 --- a/database.py +++ b/database.py @@ -1,11 +1,12 @@ +# database.py import os import MySQLdb -from fastapi import HTTPException from dotenv import load_dotenv load_dotenv() def get_db_connection(): + conn = None try: conn = MySQLdb.connect( user=os.getenv("DB_USER"), @@ -13,9 +14,11 @@ def get_db_connection(): host=os.getenv("DB_HOST"), db=os.getenv("DB_NAME"), charset='utf8mb4', - cursorclass=MySQLdb.cursors.DictCursor + cursorclass=MySQLdb.cursors.DictCursor ) - return conn + yield conn except MySQLdb.Error as e: - print(f"Erreur de connexion à la base de données : {e}") - raise HTTPException(status_code=500, detail="Erreur interne du serveur lors de la connexion à la base de données.") \ No newline at end of file + print(f"Erreur de base de données : {e}") + finally: + if conn: + conn.close() \ No newline at end of file diff --git a/routers/skills.py b/routers/skills.py index 0157fd3..fca89cb 100644 --- a/routers/skills.py +++ b/routers/skills.py @@ -1,12 +1,14 @@ -from fastapi import APIRouter, HTTPException +from fastapi import APIRouter, HTTPException, Depends import MySQLdb from database import get_db_connection router = APIRouter() @router.get("/") -def read_skills(): - conn = get_db_connection() +def read_skills(conn = Depends(get_db_connection)): + if not conn: + raise HTTPException(status_code=503, detail="Service de base de données indisponible.") + try: with conn.cursor() as cursor: sql = "SELECT * FROM skills;" @@ -14,6 +16,4 @@ def read_skills(): result = cursor.fetchall() return {"data": result} except MySQLdb.Error as e: - raise HTTPException(status_code=500, detail=f"Erreur base de données: {e}") - finally: - conn.close() \ No newline at end of file + raise HTTPException(status_code=500, detail=f"Erreur lors de la requête: {e}") \ No newline at end of file From d284ce534acd170d7f93c4b9ac1d227d4abb6bd6 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Fri, 29 Aug 2025 23:25:25 +0200 Subject: [PATCH 21/36] fix skills router --- database.py | 3 +-- routers/skills.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/database.py b/database.py index a674b6f..a9ec1d0 100644 --- a/database.py +++ b/database.py @@ -13,8 +13,7 @@ def get_db_connection(): passwd=os.getenv("DB_PASS"), host=os.getenv("DB_HOST"), db=os.getenv("DB_NAME"), - charset='utf8mb4', - cursorclass=MySQLdb.cursors.DictCursor + charset='utf8mb4' ) yield conn except MySQLdb.Error as e: diff --git a/routers/skills.py b/routers/skills.py index fca89cb..53d624e 100644 --- a/routers/skills.py +++ b/routers/skills.py @@ -10,7 +10,7 @@ def read_skills(conn = Depends(get_db_connection)): raise HTTPException(status_code=503, detail="Service de base de données indisponible.") try: - with conn.cursor() as cursor: + with conn.cursor(MySQLdb.cursors.DictCursor) as cursor: sql = "SELECT * FROM skills;" cursor.execute(sql) result = cursor.fetchall() From 6902f844fb75affb1e521f34af31cf612c4ded04 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Fri, 29 Aug 2025 23:27:30 +0200 Subject: [PATCH 22/36] fix skills router --- database.py | 13 +++++-------- routers/skills.py | 12 ++++++++---- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/database.py b/database.py index a9ec1d0..e9d5883 100644 --- a/database.py +++ b/database.py @@ -1,4 +1,4 @@ -# database.py +# database.py (sans générateur) import os import MySQLdb from dotenv import load_dotenv @@ -6,7 +6,6 @@ from dotenv import load_dotenv load_dotenv() def get_db_connection(): - conn = None try: conn = MySQLdb.connect( user=os.getenv("DB_USER"), @@ -15,9 +14,7 @@ def get_db_connection(): db=os.getenv("DB_NAME"), charset='utf8mb4' ) - yield conn - except MySQLdb.Error as e: - print(f"Erreur de base de données : {e}") - finally: - if conn: - conn.close() \ No newline at end of file + return conn + except Exception as e: + print(f"ERREUR CRITIQUE DE CONNEXION DB : {e}") + return None \ No newline at end of file diff --git a/routers/skills.py b/routers/skills.py index 53d624e..bf9c062 100644 --- a/routers/skills.py +++ b/routers/skills.py @@ -1,14 +1,16 @@ -from fastapi import APIRouter, HTTPException, Depends +# routers/skills.py (sans générateur) +from fastapi import APIRouter, HTTPException import MySQLdb from database import get_db_connection router = APIRouter() @router.get("/") -def read_skills(conn = Depends(get_db_connection)): +def read_skills(): + conn = get_db_connection() if not conn: raise HTTPException(status_code=503, detail="Service de base de données indisponible.") - + try: with conn.cursor(MySQLdb.cursors.DictCursor) as cursor: sql = "SELECT * FROM skills;" @@ -16,4 +18,6 @@ def read_skills(conn = Depends(get_db_connection)): result = cursor.fetchall() return {"data": result} except MySQLdb.Error as e: - raise HTTPException(status_code=500, detail=f"Erreur lors de la requête: {e}") \ No newline at end of file + raise HTTPException(status_code=500, detail=f"Erreur lors de la requête: {e}") + finally: + conn.close() \ No newline at end of file From d75a5590ccfba060ca67a258720961722560017f Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sun, 31 Aug 2025 00:21:39 +0200 Subject: [PATCH 23/36] add of experiences router and experienceTasks router --- main.py | 14 ++++++++++++-- routers/experienceTasks.py | 24 ++++++++++++++++++++++++ routers/experiences.py | 24 ++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 routers/experienceTasks.py create mode 100644 routers/experiences.py diff --git a/main.py b/main.py index e800499..d228b7b 100644 --- a/main.py +++ b/main.py @@ -1,7 +1,7 @@ import os from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware -from routers import skills +from routers import skills, experiences, experienceTasks app = FastAPI() @@ -24,5 +24,15 @@ def read_root(): app.include_router( skills.router, prefix="/api/skills", - tags=["Skills"] + tags=["Skills"], +) +app.include_router( +experiences.router, + prefix="/api/experiences", + tags=["Experiences"] +) +app.include_router( + experienceTasks.router, + prefix="/api/experienceTasks", + tags=["ExperienceTasks"] ) \ No newline at end of file diff --git a/routers/experienceTasks.py b/routers/experienceTasks.py new file mode 100644 index 0000000..716d8cd --- /dev/null +++ b/routers/experienceTasks.py @@ -0,0 +1,24 @@ +# routers/skills.py (sans générateur) +from fastapi import APIRouter, HTTPException +import MySQLdb +from database import get_db_connection + +router = APIRouter() + + +@router.get("/") +def read_experienceTasks(): + conn = get_db_connection() + if not conn: + raise HTTPException(status_code=503, detail="Service de base de données indisponible.") + + try: + with conn.cursor(MySQLdb.cursors.DictCursor) as cursor: + sql = "SELECT * FROM experience_tasks;" + cursor.execute(sql) + result = cursor.fetchall() + return {"data": result} + except MySQLdb.Error as e: + raise HTTPException(status_code=500, detail=f"Erreur lors de la requête: {e}") + finally: + conn.close() \ No newline at end of file diff --git a/routers/experiences.py b/routers/experiences.py new file mode 100644 index 0000000..551cb27 --- /dev/null +++ b/routers/experiences.py @@ -0,0 +1,24 @@ +# routers/skills.py (sans générateur) +from fastapi import APIRouter, HTTPException +import MySQLdb +from database import get_db_connection + +router = APIRouter() + + +@router.get("/") +def read_experiences(): + conn = get_db_connection() + if not conn: + raise HTTPException(status_code=503, detail="Service de base de données indisponible.") + + try: + with conn.cursor(MySQLdb.cursors.DictCursor) as cursor: + sql = "SELECT * FROM experiences;" + cursor.execute(sql) + result = cursor.fetchall() + return {"data": result} + except MySQLdb.Error as e: + raise HTTPException(status_code=500, detail=f"Erreur lors de la requête: {e}") + finally: + conn.close() \ No newline at end of file From 900a17eac5e40597df33bc8e07e8edcf9e1e28fd Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sun, 31 Aug 2025 18:22:58 +0200 Subject: [PATCH 24/36] add of projects router --- main.py | 7 ++++++- routers/projects.py | 48 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 routers/projects.py diff --git a/main.py b/main.py index d228b7b..d200679 100644 --- a/main.py +++ b/main.py @@ -1,7 +1,7 @@ import os from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware -from routers import skills, experiences, experienceTasks +from routers import skills, experiences, experienceTasks, projects app = FastAPI() @@ -35,4 +35,9 @@ app.include_router( experienceTasks.router, prefix="/api/experienceTasks", tags=["ExperienceTasks"] +) +app.include_router( + projects.router, + prefix="/api/projects", + tags=["Projects"] ) \ No newline at end of file diff --git a/routers/projects.py b/routers/projects.py new file mode 100644 index 0000000..00c7d05 --- /dev/null +++ b/routers/projects.py @@ -0,0 +1,48 @@ +# routers/skills.py (sans générateur) +from fastapi import APIRouter, HTTPException +import MySQLdb +from database import get_db_connection + +router = APIRouter() + + +@router.get("/") +def read_projects(): + conn = get_db_connection() + if not conn: + raise HTTPException(status_code=503, detail="Service de base de données indisponible.") + + try: + with conn.cursor(MySQLdb.cursors.DictCursor) as cursor: + sql = """ + SELECT + p.id, + p.title, + p.description, + p.color, + p.nb_image, + p.image_name, + GROUP_CONCAT(DISTINCT s.name ORDER BY s.name SEPARATOR ', ') AS skills + FROM + projects p + LEFT JOIN + project_skills ps ON p.id = ps.project_id + LEFT JOIN + skills s ON ps.skill_id = s.id + GROUP BY + p.id; + """ + cursor.execute(sql) + result = cursor.fetchall() + + for project in result: + if project['skills']: + project['skills'] = project['skills'].split(', ') + else: + project['skills'] = [] + + return {"data": result} + except MySQLdb.Error as e: + raise HTTPException(status_code=500, detail=f"Erreur lors de la requête: {e}") + finally: + conn.close() \ No newline at end of file From cec4fb3234bf72fec4ab485c78eb0a2dcaeffea0 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sun, 31 Aug 2025 21:40:21 +0200 Subject: [PATCH 25/36] debug error 404 --- main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/main.py b/main.py index d200679..8ff8b0f 100644 --- a/main.py +++ b/main.py @@ -8,6 +8,7 @@ app = FastAPI() origins = [ "http://localhost", "http://localhost:5173", + "http://192.168.2.210:8081", ] app.add_middleware( CORSMiddleware, From 6e20a308ab96423d39f8a997bd33ad7107fe4983 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sun, 31 Aug 2025 21:49:31 +0200 Subject: [PATCH 26/36] debug error 404 --- main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/main.py b/main.py index 8ff8b0f..d200679 100644 --- a/main.py +++ b/main.py @@ -8,7 +8,6 @@ app = FastAPI() origins = [ "http://localhost", "http://localhost:5173", - "http://192.168.2.210:8081", ] app.add_middleware( CORSMiddleware, From 2c68620c668e075da6c28cea06694c2c3e75eb97 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Tue, 2 Sep 2025 22:59:56 +0200 Subject: [PATCH 27/36] add of school attribute in projects --- routers/projects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/projects.py b/routers/projects.py index 00c7d05..9c668ae 100644 --- a/routers/projects.py +++ b/routers/projects.py @@ -19,7 +19,7 @@ def read_projects(): p.id, p.title, p.description, - p.color, + p.school, p.nb_image, p.image_name, GROUP_CONCAT(DISTINCT s.name ORDER BY s.name SEPARATOR ', ') AS skills From 0d2e1374edfb453bca5b2d02605ee489bb9b4b15 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Wed, 3 Sep 2025 14:31:04 +0200 Subject: [PATCH 28/36] add of longProjects and update of projects table --- main.py | 13 ++++-- routers/longProjects.py | 49 +++++++++++++++++++++++ routers/{projects.py => shortProjects.py} | 5 +-- 3 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 routers/longProjects.py rename routers/{projects.py => shortProjects.py} (93%) diff --git a/main.py b/main.py index d200679..8fbc98f 100644 --- a/main.py +++ b/main.py @@ -1,7 +1,7 @@ import os from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware -from routers import skills, experiences, experienceTasks, projects +from routers import skills, experiences, experienceTasks, shortProjects, longProjects app = FastAPI() @@ -37,7 +37,12 @@ app.include_router( tags=["ExperienceTasks"] ) app.include_router( - projects.router, - prefix="/api/projects", - tags=["Projects"] + shortProjects.router, + prefix="/api/shortProjects", + tags=["ShortProjects"] +) +app.include_router( + longProjects.router, + prefix="/api/longProjects", + tags=["LongProjects"] ) \ No newline at end of file diff --git a/routers/longProjects.py b/routers/longProjects.py new file mode 100644 index 0000000..69adca5 --- /dev/null +++ b/routers/longProjects.py @@ -0,0 +1,49 @@ +from fastapi import APIRouter, HTTPException +import MySQLdb +from database import get_db_connection + +router = APIRouter() + + +@router.get("/") +def read_longProjects(): + conn = get_db_connection() + if not conn: + raise HTTPException(status_code=503, detail="Service de base de données indisponible.") + + try: + with conn.cursor(MySQLdb.cursors.DictCursor) as cursor: + sql = """ + SELECT + p.id, + p.title, + p.long_description, + p.school, + p.date, + p.repo_link, + p.nb_image, + p.image_name, + GROUP_CONCAT(DISTINCT s.name ORDER BY s.name SEPARATOR ', ') AS skills + FROM + projects p + LEFT JOIN + project_skills ps ON p.id = ps.project_id + LEFT JOIN + skills s ON ps.skill_id = s.id + GROUP BY + p.id; + """ + cursor.execute(sql) + result = cursor.fetchall() + + for project in result: + if project['skills']: + project['skills'] = project['skills'].split(', ') + else: + project['skills'] = [] + + return {"data": result} + except MySQLdb.Error as e: + raise HTTPException(status_code=500, detail=f"Erreur lors de la requête: {e}") + finally: + conn.close() \ No newline at end of file diff --git a/routers/projects.py b/routers/shortProjects.py similarity index 93% rename from routers/projects.py rename to routers/shortProjects.py index 9c668ae..16da9bb 100644 --- a/routers/projects.py +++ b/routers/shortProjects.py @@ -1,4 +1,3 @@ -# routers/skills.py (sans générateur) from fastapi import APIRouter, HTTPException import MySQLdb from database import get_db_connection @@ -7,7 +6,7 @@ router = APIRouter() @router.get("/") -def read_projects(): +def read_shortProjects(): conn = get_db_connection() if not conn: raise HTTPException(status_code=503, detail="Service de base de données indisponible.") @@ -18,7 +17,7 @@ def read_projects(): SELECT p.id, p.title, - p.description, + p.short_description, p.school, p.nb_image, p.image_name, From e97d7def89072357eaa8be93f4b36f81e6470b16 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Wed, 3 Sep 2025 21:41:52 +0200 Subject: [PATCH 29/36] change date in projects instead of beginning_year and end_year, --- routers/longProjects.py | 2 ++ routers/shortProjects.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/routers/longProjects.py b/routers/longProjects.py index 69adca5..80bb5d7 100644 --- a/routers/longProjects.py +++ b/routers/longProjects.py @@ -23,6 +23,8 @@ def read_longProjects(): p.repo_link, p.nb_image, p.image_name, + p.beginning_year, + p.end_year, GROUP_CONCAT(DISTINCT s.name ORDER BY s.name SEPARATOR ', ') AS skills FROM projects p diff --git a/routers/shortProjects.py b/routers/shortProjects.py index 16da9bb..fecc2ed 100644 --- a/routers/shortProjects.py +++ b/routers/shortProjects.py @@ -21,6 +21,8 @@ def read_shortProjects(): p.school, p.nb_image, p.image_name, + p.beginning_year, + p.end_year, GROUP_CONCAT(DISTINCT s.name ORDER BY s.name SEPARATOR ', ') AS skills FROM projects p From 15dce53e86646292a32b5c8d600e7c1e5707710a Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Thu, 4 Sep 2025 23:43:57 +0200 Subject: [PATCH 30/36] remove of nb_image in shortProjects --- routers/shortProjects.py | 1 - 1 file changed, 1 deletion(-) diff --git a/routers/shortProjects.py b/routers/shortProjects.py index fecc2ed..be5aead 100644 --- a/routers/shortProjects.py +++ b/routers/shortProjects.py @@ -19,7 +19,6 @@ def read_shortProjects(): p.title, p.short_description, p.school, - p.nb_image, p.image_name, p.beginning_year, p.end_year, From 510eb13d8ca5aaede0653b7506b162881bd368db Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sun, 14 Sep 2025 12:39:39 +0200 Subject: [PATCH 31/36] add of short_description in longProjects.py --- routers/longProjects.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/routers/longProjects.py b/routers/longProjects.py index 80bb5d7..718f271 100644 --- a/routers/longProjects.py +++ b/routers/longProjects.py @@ -4,9 +4,8 @@ from database import get_db_connection router = APIRouter() - -@router.get("/") -def read_longProjects(): +@router.get("/{project_id}") +def read_long_project(project_id: int): conn = get_db_connection() if not conn: raise HTTPException(status_code=503, detail="Service de base de données indisponible.") @@ -18,8 +17,8 @@ def read_longProjects(): p.id, p.title, p.long_description, + p.short_description, p.school, - p.date, p.repo_link, p.nb_image, p.image_name, @@ -32,20 +31,23 @@ def read_longProjects(): project_skills ps ON p.id = ps.project_id LEFT JOIN skills s ON ps.skill_id = s.id - GROUP BY - p.id; + WHERE p.id = %s + GROUP BY p.id; """ - cursor.execute(sql) - result = cursor.fetchall() + cursor.execute(sql, (project_id,)) + result = cursor.fetchone() - for project in result: - if project['skills']: - project['skills'] = project['skills'].split(', ') - else: - project['skills'] = [] + if not result: + raise HTTPException(status_code=404, detail="Projet non trouvé.") + + if result['skills']: + result['skills'] = result['skills'].split(', ') + else: + result['skills'] = [] + + return result - return {"data": result} except MySQLdb.Error as e: raise HTTPException(status_code=500, detail=f"Erreur lors de la requête: {e}") finally: - conn.close() \ No newline at end of file + conn.close() From 5ff02c0ff5152dd094baec733ec8cbb765867138 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 3 Jun 2026 11:35:09 +0200 Subject: [PATCH 32/36] initialize backend with FastAPI and Docker setup --- .env.example | 7 ++++ Dockerfile | 13 +++++++ app/database.py | 38 +++++++++++++++++++ app/main.py | 9 +++++ app/models/experience.py | 45 ++++++++++++++++++++++ app/models/experience_task.py | 39 +++++++++++++++++++ app/models/project.py | 70 +++++++++++++++++++++++++++++++++++ app/models/project_skill.py | 22 +++++++++++ app/models/skill.py | 28 ++++++++++++++ app/routers/experiences.py | 17 +++++++++ app/routers/projects.py | 32 ++++++++++++++++ app/routers/skills.py | 13 +++++++ app/schemas/experience.py | 20 ++++++++++ app/schemas/project.py | 33 +++++++++++++++++ app/schemas/skill.py | 17 +++++++++ database.py | 20 ---------- docker-compose.yml | 13 +++++++ main.py | 48 ------------------------ requirements.txt | 7 ++-- routers/experienceTasks.py | 24 ------------ routers/experiences.py | 24 ------------ routers/longProjects.py | 53 -------------------------- routers/shortProjects.py | 48 ------------------------ routers/skills.py | 23 ------------ 24 files changed, 420 insertions(+), 243 deletions(-) create mode 100644 .env.example create mode 100644 Dockerfile create mode 100644 app/database.py create mode 100644 app/main.py create mode 100644 app/models/experience.py create mode 100644 app/models/experience_task.py create mode 100644 app/models/project.py create mode 100644 app/models/project_skill.py create mode 100644 app/models/skill.py create mode 100644 app/routers/experiences.py create mode 100644 app/routers/projects.py create mode 100644 app/routers/skills.py create mode 100644 app/schemas/experience.py create mode 100644 app/schemas/project.py create mode 100644 app/schemas/skill.py delete mode 100644 database.py create mode 100644 docker-compose.yml delete mode 100644 main.py delete mode 100644 routers/experienceTasks.py delete mode 100644 routers/experiences.py delete mode 100644 routers/longProjects.py delete mode 100644 routers/shortProjects.py delete mode 100644 routers/skills.py diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..033963b --- /dev/null +++ b/.env.example @@ -0,0 +1,7 @@ +DB_HOST= +DB_PORT=3306 + +DB_NAME= + +DB_USER= +DB_PASSWORD= \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..fb67acd --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM python:3.12-slim + +WORKDIR /app + +COPY requirements.txt . + +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +EXPOSE 8000 + +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"] \ No newline at end of file diff --git a/app/database.py b/app/database.py new file mode 100644 index 0000000..0913d01 --- /dev/null +++ b/app/database.py @@ -0,0 +1,38 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +from sqlalchemy.orm import declarative_base + +from dotenv import load_dotenv +import os + +load_dotenv() + +DATABASE_URL = ( + f"mysql+pymysql://" + f"{os.getenv('DB_USER')}:" + f"{os.getenv('DB_PASSWORD')}@" + f"{os.getenv('DB_HOST')}:" + f"{os.getenv('DB_PORT')}/" + f"{os.getenv('DB_NAME')}" +) + +engine = create_engine( + DATABASE_URL, + pool_pre_ping=True +) + +SessionLocal = sessionmaker( + autocommit=False, + autoflush=False, + bind=engine +) + +Base = declarative_base() + + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() \ No newline at end of file diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000..bbc5cc3 --- /dev/null +++ b/app/main.py @@ -0,0 +1,9 @@ +from fastapi import FastAPI + +from app.routers import projects, experiences, skills + +app = FastAPI() + +app.include_router(projects.router) +app.include_router(experiences.router) +app.include_router(skills.router) diff --git a/app/models/experience.py b/app/models/experience.py new file mode 100644 index 0000000..07f3a43 --- /dev/null +++ b/app/models/experience.py @@ -0,0 +1,45 @@ +from sqlalchemy import ( + Column, + Integer, + String +) + +from sqlalchemy.orm import relationship + +from app.database import Base + + +class Experience(Base): + __tablename__ = "experiences" + + id = Column( + Integer, + primary_key=True, + index=True + ) + + company = Column( + String(255), + nullable=False + ) + + location = Column( + String(255), + nullable=False + ) + + duration = Column( + String(255), + nullable=False + ) + + role = Column( + String(255), + nullable=False + ) + + tasks = relationship( + "ExperienceTask", + back_populates="experience", + cascade="all, delete-orphan" + ) \ No newline at end of file diff --git a/app/models/experience_task.py b/app/models/experience_task.py new file mode 100644 index 0000000..ee854db --- /dev/null +++ b/app/models/experience_task.py @@ -0,0 +1,39 @@ +from sqlalchemy import ( + Column, + Integer, + String, + ForeignKey +) + +from sqlalchemy.orm import relationship + +from app.database import Base + + +class ExperienceTask(Base): + __tablename__ = "experience_tasks" + + id = Column( + Integer, + primary_key=True, + index=True + ) + + description = Column( + String(255), + nullable=False + ) + + experience_id = Column( + Integer, + ForeignKey( + "experiences.id", + ondelete="CASCADE" + ), + nullable=False + ) + + experience = relationship( + "Experience", + back_populates="tasks" + ) \ No newline at end of file diff --git a/app/models/project.py b/app/models/project.py new file mode 100644 index 0000000..43a7937 --- /dev/null +++ b/app/models/project.py @@ -0,0 +1,70 @@ +from sqlalchemy import ( + Column, + Integer, + String, + Text, + Boolean +) + +from sqlalchemy.orm import relationship + +from app.database import Base +from app.models.project_skill import project_skills + + +class Project(Base): + __tablename__ = "projects" + + id = Column(Integer, primary_key=True, index=True) + + title = Column( + String(255), + nullable=False + ) + + nb_image = Column( + Integer, + default=1 + ) + + short_description = Column( + Text, + nullable=False + ) + + image_name = Column( + String(255), + nullable=False, + default="empty" + ) + + school = Column( + Boolean, + nullable=False + ) + + long_description = Column( + Text, + nullable=False + ) + + repo_link = Column( + String(255), + nullable=True + ) + + beginning_year = Column( + Integer, + nullable=False + ) + + end_year = Column( + Integer, + nullable=True + ) + + skills = relationship( + "Skill", + secondary=project_skills, + back_populates="projects" + ) \ No newline at end of file diff --git a/app/models/project_skill.py b/app/models/project_skill.py new file mode 100644 index 0000000..c048a15 --- /dev/null +++ b/app/models/project_skill.py @@ -0,0 +1,22 @@ +from sqlalchemy import Table, Column, Integer, ForeignKey + +from app.database import Base + +project_skills = Table( + "project_skills", + Base.metadata, + + Column( + "project_id", + Integer, + ForeignKey("projects.id", ondelete="CASCADE"), + primary_key=True + ), + + Column( + "skill_id", + Integer, + ForeignKey("skills.id", ondelete="CASCADE"), + primary_key=True + ) +) \ No newline at end of file diff --git a/app/models/skill.py b/app/models/skill.py new file mode 100644 index 0000000..5bd83c7 --- /dev/null +++ b/app/models/skill.py @@ -0,0 +1,28 @@ +from sqlalchemy import Column, Integer, String +from sqlalchemy.orm import relationship + +from app.database import Base +from app.models.project_skill import project_skills + + +class Skill(Base): + __tablename__ = "skills" + + id = Column(Integer, primary_key=True, index=True) + + name = Column( + String(255), + nullable=False, + unique=True + ) + + type = Column( + String(255), + nullable=False + ) + + projects = relationship( + "Project", + secondary=project_skills, + back_populates="skills" + ) \ No newline at end of file diff --git a/app/routers/experiences.py b/app/routers/experiences.py new file mode 100644 index 0000000..98984c9 --- /dev/null +++ b/app/routers/experiences.py @@ -0,0 +1,17 @@ +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.orm import Session, joinedload + +from app.database import get_db +from app.models.experience import Experience +from app.schemas.experience import ExperienceResponse + +router = APIRouter(prefix="/experiences", tags=["Experiences"]) + + +@router.get("/", response_model=list[ExperienceResponse]) +def get_experiences(db: Session = Depends(get_db)): + return ( + db.query(Experience) + .options(joinedload(Experience.tasks)) + .all() + ) diff --git a/app/routers/projects.py b/app/routers/projects.py new file mode 100644 index 0000000..424a7c5 --- /dev/null +++ b/app/routers/projects.py @@ -0,0 +1,32 @@ +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.orm import Session, joinedload + +from app.database import get_db +from app.models.project import Project +from app.schemas.project import ProjectCard, ProjectPage + +router = APIRouter(prefix="/projects", tags=["Projects"]) + + +@router.get("/", response_model=list[ProjectCard]) +def get_projects(db: Session = Depends(get_db)): + return ( + db.query(Project) + .options(joinedload(Project.skills)) + .all() + ) + + +@router.get("/{project_id}", response_model=ProjectPage) +def get_project(project_id: int, db: Session = Depends(get_db)): + project = ( + db.query(Project) + .options(joinedload(Project.skills)) + .filter(Project.id == project_id) + .first() + ) + + if not project: + raise HTTPException(status_code=404, detail="Project not found") + + return project \ No newline at end of file diff --git a/app/routers/skills.py b/app/routers/skills.py new file mode 100644 index 0000000..4f3393d --- /dev/null +++ b/app/routers/skills.py @@ -0,0 +1,13 @@ +from fastapi import APIRouter, Depends +from sqlalchemy.orm import Session + +from app.database import get_db +from app.models.skill import Skill +from app.schemas.skill import SkillResponse + +router = APIRouter(prefix="/skills", tags=["Skills"]) + + +@router.get("/", response_model=list[SkillResponse]) +def get_skills(db: Session = Depends(get_db)): + return db.query(Skill).all() \ No newline at end of file diff --git a/app/schemas/experience.py b/app/schemas/experience.py new file mode 100644 index 0000000..68fcdc2 --- /dev/null +++ b/app/schemas/experience.py @@ -0,0 +1,20 @@ +from pydantic import BaseModel + +class ExperienceTaskResponse(BaseModel): + id: int + description: str + + class Config: + from_attributes = True + +class ExperienceResponse(BaseModel): + id: int + company: str + location: str + duration: str + role: str + tasks: list[ExperienceTaskResponse] + + class Config: + from_attributes = Trues + diff --git a/app/schemas/project.py b/app/schemas/project.py new file mode 100644 index 0000000..cd56f1d --- /dev/null +++ b/app/schemas/project.py @@ -0,0 +1,33 @@ +from pydantic import BaseModel +from app.schemas.skill import ProjectSkill + + +class ProjectCard(BaseModel): + id: int + title: str + short_description: str + image_name: str + school: bool + beginning_year: int + end_year: int | None + skills: list[ProjectSkill] + + class Config: + from_attributes = True + + +class ProjectPage(BaseModel): + id: int + title: str + short_description: str + long_description: str + repo_link: str | None + nb_image: int + image_name: str + school: bool + beginning_year: int + end_year: int | None + skills: list[ProjectSkill] + + class Config: + from_attributes = True \ No newline at end of file diff --git a/app/schemas/skill.py b/app/schemas/skill.py new file mode 100644 index 0000000..501959f --- /dev/null +++ b/app/schemas/skill.py @@ -0,0 +1,17 @@ +from pydantic import BaseModel + +class SkillResponse(BaseModel): + id: int + name: str + type: str + + class Config: + from_attributes = True + + +class ProjectSkill(BaseModel): + id: int + name: str + + class Config: + from_attributes = True \ No newline at end of file diff --git a/database.py b/database.py deleted file mode 100644 index e9d5883..0000000 --- a/database.py +++ /dev/null @@ -1,20 +0,0 @@ -# database.py (sans générateur) -import os -import MySQLdb -from dotenv import load_dotenv - -load_dotenv() - -def get_db_connection(): - try: - conn = MySQLdb.connect( - user=os.getenv("DB_USER"), - passwd=os.getenv("DB_PASS"), - host=os.getenv("DB_HOST"), - db=os.getenv("DB_NAME"), - charset='utf8mb4' - ) - return conn - except Exception as e: - print(f"ERREUR CRITIQUE DE CONNEXION DB : {e}") - return None \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..fc8fbec --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +version: "3.8" + +services: + backend: + build: . + container_name: portfolio-backend + restart: unless-stopped + env_file: + - .env + ports: + - "8000:8000" + volumes: + - .:/app \ No newline at end of file diff --git a/main.py b/main.py deleted file mode 100644 index 8fbc98f..0000000 --- a/main.py +++ /dev/null @@ -1,48 +0,0 @@ -import os -from fastapi import FastAPI, HTTPException -from fastapi.middleware.cors import CORSMiddleware -from routers import skills, experiences, experienceTasks, shortProjects, longProjects - -app = FastAPI() - -origins = [ - "http://localhost", - "http://localhost:5173", -] -app.add_middleware( - CORSMiddleware, - allow_origins=origins, - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], -) - -@app.get("/") -def read_root(): - return {"status": "API Portfolio fonctionnelle !"} - -app.include_router( - skills.router, - prefix="/api/skills", - tags=["Skills"], -) -app.include_router( -experiences.router, - prefix="/api/experiences", - tags=["Experiences"] -) -app.include_router( - experienceTasks.router, - prefix="/api/experienceTasks", - tags=["ExperienceTasks"] -) -app.include_router( - shortProjects.router, - prefix="/api/shortProjects", - tags=["ShortProjects"] -) -app.include_router( - longProjects.router, - prefix="/api/longProjects", - tags=["LongProjects"] -) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 83cca42..7823962 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ -fastapi -uvicorn -mysqlclient +fastapi +uvicorn[standard] +sqlalchemy +pymysql python-dotenv \ No newline at end of file diff --git a/routers/experienceTasks.py b/routers/experienceTasks.py deleted file mode 100644 index 716d8cd..0000000 --- a/routers/experienceTasks.py +++ /dev/null @@ -1,24 +0,0 @@ -# routers/skills.py (sans générateur) -from fastapi import APIRouter, HTTPException -import MySQLdb -from database import get_db_connection - -router = APIRouter() - - -@router.get("/") -def read_experienceTasks(): - conn = get_db_connection() - if not conn: - raise HTTPException(status_code=503, detail="Service de base de données indisponible.") - - try: - with conn.cursor(MySQLdb.cursors.DictCursor) as cursor: - sql = "SELECT * FROM experience_tasks;" - cursor.execute(sql) - result = cursor.fetchall() - return {"data": result} - except MySQLdb.Error as e: - raise HTTPException(status_code=500, detail=f"Erreur lors de la requête: {e}") - finally: - conn.close() \ No newline at end of file diff --git a/routers/experiences.py b/routers/experiences.py deleted file mode 100644 index 551cb27..0000000 --- a/routers/experiences.py +++ /dev/null @@ -1,24 +0,0 @@ -# routers/skills.py (sans générateur) -from fastapi import APIRouter, HTTPException -import MySQLdb -from database import get_db_connection - -router = APIRouter() - - -@router.get("/") -def read_experiences(): - conn = get_db_connection() - if not conn: - raise HTTPException(status_code=503, detail="Service de base de données indisponible.") - - try: - with conn.cursor(MySQLdb.cursors.DictCursor) as cursor: - sql = "SELECT * FROM experiences;" - cursor.execute(sql) - result = cursor.fetchall() - return {"data": result} - except MySQLdb.Error as e: - raise HTTPException(status_code=500, detail=f"Erreur lors de la requête: {e}") - finally: - conn.close() \ No newline at end of file diff --git a/routers/longProjects.py b/routers/longProjects.py deleted file mode 100644 index 718f271..0000000 --- a/routers/longProjects.py +++ /dev/null @@ -1,53 +0,0 @@ -from fastapi import APIRouter, HTTPException -import MySQLdb -from database import get_db_connection - -router = APIRouter() - -@router.get("/{project_id}") -def read_long_project(project_id: int): - conn = get_db_connection() - if not conn: - raise HTTPException(status_code=503, detail="Service de base de données indisponible.") - - try: - with conn.cursor(MySQLdb.cursors.DictCursor) as cursor: - sql = """ - SELECT - p.id, - p.title, - p.long_description, - p.short_description, - p.school, - p.repo_link, - p.nb_image, - p.image_name, - p.beginning_year, - p.end_year, - GROUP_CONCAT(DISTINCT s.name ORDER BY s.name SEPARATOR ', ') AS skills - FROM - projects p - LEFT JOIN - project_skills ps ON p.id = ps.project_id - LEFT JOIN - skills s ON ps.skill_id = s.id - WHERE p.id = %s - GROUP BY p.id; - """ - cursor.execute(sql, (project_id,)) - result = cursor.fetchone() - - if not result: - raise HTTPException(status_code=404, detail="Projet non trouvé.") - - if result['skills']: - result['skills'] = result['skills'].split(', ') - else: - result['skills'] = [] - - return result - - except MySQLdb.Error as e: - raise HTTPException(status_code=500, detail=f"Erreur lors de la requête: {e}") - finally: - conn.close() diff --git a/routers/shortProjects.py b/routers/shortProjects.py deleted file mode 100644 index be5aead..0000000 --- a/routers/shortProjects.py +++ /dev/null @@ -1,48 +0,0 @@ -from fastapi import APIRouter, HTTPException -import MySQLdb -from database import get_db_connection - -router = APIRouter() - - -@router.get("/") -def read_shortProjects(): - conn = get_db_connection() - if not conn: - raise HTTPException(status_code=503, detail="Service de base de données indisponible.") - - try: - with conn.cursor(MySQLdb.cursors.DictCursor) as cursor: - sql = """ - SELECT - p.id, - p.title, - p.short_description, - p.school, - p.image_name, - p.beginning_year, - p.end_year, - GROUP_CONCAT(DISTINCT s.name ORDER BY s.name SEPARATOR ', ') AS skills - FROM - projects p - LEFT JOIN - project_skills ps ON p.id = ps.project_id - LEFT JOIN - skills s ON ps.skill_id = s.id - GROUP BY - p.id; - """ - cursor.execute(sql) - result = cursor.fetchall() - - for project in result: - if project['skills']: - project['skills'] = project['skills'].split(', ') - else: - project['skills'] = [] - - return {"data": result} - except MySQLdb.Error as e: - raise HTTPException(status_code=500, detail=f"Erreur lors de la requête: {e}") - finally: - conn.close() \ No newline at end of file diff --git a/routers/skills.py b/routers/skills.py deleted file mode 100644 index bf9c062..0000000 --- a/routers/skills.py +++ /dev/null @@ -1,23 +0,0 @@ -# routers/skills.py (sans générateur) -from fastapi import APIRouter, HTTPException -import MySQLdb -from database import get_db_connection - -router = APIRouter() - -@router.get("/") -def read_skills(): - conn = get_db_connection() - if not conn: - raise HTTPException(status_code=503, detail="Service de base de données indisponible.") - - try: - with conn.cursor(MySQLdb.cursors.DictCursor) as cursor: - sql = "SELECT * FROM skills;" - cursor.execute(sql) - result = cursor.fetchall() - return {"data": result} - except MySQLdb.Error as e: - raise HTTPException(status_code=500, detail=f"Erreur lors de la requête: {e}") - finally: - conn.close() \ No newline at end of file From b76566aea46b0b2fd877480b1a83f8181985ed40 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 3 Jun 2026 11:38:02 +0200 Subject: [PATCH 33/36] update .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index cbbb8d1..fed54ed 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -venv/ __pycache__/ *.pyc +*.pyo +*.pyd .env \ No newline at end of file From 916b6f7ca299854c138ae66ac4acf1c39abc8834 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 3 Jun 2026 11:39:14 +0200 Subject: [PATCH 34/36] update .gitignore --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index fed54ed..991c3c8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,7 @@ __pycache__/ *.pyc *.pyo *.pyd -.env \ No newline at end of file + +.env + +.idea From 91c1e1a621052d1b5c4cabe2fefe06ee66cc5bcd Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 3 Jun 2026 14:06:12 +0200 Subject: [PATCH 35/36] correct missing import --- app/models/experience.py | 2 ++ app/schemas/experience.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/models/experience.py b/app/models/experience.py index 07f3a43..215b150 100644 --- a/app/models/experience.py +++ b/app/models/experience.py @@ -1,3 +1,5 @@ +from app.models.experience_task import ExperienceTask + from sqlalchemy import ( Column, Integer, diff --git a/app/schemas/experience.py b/app/schemas/experience.py index 68fcdc2..8b19059 100644 --- a/app/schemas/experience.py +++ b/app/schemas/experience.py @@ -16,5 +16,5 @@ class ExperienceResponse(BaseModel): tasks: list[ExperienceTaskResponse] class Config: - from_attributes = Trues + from_attributes = True From 505355769bc1c9888a1260addf25f6e8a95db1ca Mon Sep 17 00:00:00 2001 From: Giovanni Date: Mon, 15 Jun 2026 11:13:01 +0200 Subject: [PATCH 36/36] add /api prefix to all routers --- app/main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/main.py b/app/main.py index bbc5cc3..57d7fcb 100644 --- a/app/main.py +++ b/app/main.py @@ -4,6 +4,6 @@ from app.routers import projects, experiences, skills app = FastAPI() -app.include_router(projects.router) -app.include_router(experiences.router) -app.include_router(skills.router) +app.include_router(projects.router, prefix="/api") +app.include_router(experiences.router, prefix="/api") +app.include_router(skills.router, prefix="/api")