diff --git a/server/.env.example b/server/.env.example new file mode 100644 index 0000000..c850c8a --- /dev/null +++ b/server/.env.example @@ -0,0 +1 @@ +DATABASE_URI="sqlite:///database.db" \ No newline at end of file diff --git a/server/.gitignore b/server/.gitignore new file mode 100644 index 0000000..4d767c2 --- /dev/null +++ b/server/.gitignore @@ -0,0 +1,3 @@ +.venv/ +.env +database.db \ No newline at end of file diff --git a/server/README.md b/server/README.md new file mode 100644 index 0000000..eb861dc --- /dev/null +++ b/server/README.md @@ -0,0 +1,14 @@ +# Run +``` +fastapi dev main.py +``` + +# Best Practice +## Simple Boilerplate +https://github.com/anthonycepeda/fastapi-sqlmodel/tree/main + +## Global Boilerplate +https://github.com/zhanymkanov/fastapi-best-practices?tab=readme-ov-file#excessively-use-pydantic + +## SQLEModel-boilerplate +https://github.com/benavlabs/SQLModel-boilerplate/tree/main diff --git a/server/requirements.txt b/server/requirements.txt new file mode 100644 index 0000000..13f4297 --- /dev/null +++ b/server/requirements.txt @@ -0,0 +1,3 @@ +fastapi==0.128.6 +sqlmodel==0.0.32 +python-dotenv==1.2.1 \ No newline at end of file diff --git a/server/src/__init__.py b/server/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/src/__pycache__/__init__.cpython-311.pyc b/server/src/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..964069f Binary files /dev/null and b/server/src/__pycache__/__init__.cpython-311.pyc differ diff --git a/server/src/__pycache__/app.cpython-311.pyc b/server/src/__pycache__/app.cpython-311.pyc new file mode 100644 index 0000000..4d598fa Binary files /dev/null and b/server/src/__pycache__/app.cpython-311.pyc differ diff --git a/server/src/__pycache__/database.cpython-311.pyc b/server/src/__pycache__/database.cpython-311.pyc new file mode 100644 index 0000000..053851b Binary files /dev/null and b/server/src/__pycache__/database.cpython-311.pyc differ diff --git a/server/src/__pycache__/main.cpython-311.pyc b/server/src/__pycache__/main.cpython-311.pyc new file mode 100644 index 0000000..9b5e865 Binary files /dev/null and b/server/src/__pycache__/main.cpython-311.pyc differ diff --git a/server/src/app.py b/server/src/app.py new file mode 100644 index 0000000..bff65bb --- /dev/null +++ b/server/src/app.py @@ -0,0 +1,27 @@ +from contextlib import asynccontextmanager +from fastapi import FastAPI + +from src.database import create_db_and_tables +#TODO : best practice to manage models import +from src.questions.models import Question +from src.knowledges.models import Knowledge +from src.metrics.models import Metric + +#Test +from src.questions.controller import create_question, read_question +from src.knowledges.controller import create_knowledge, read_knowledges, delete_knowledge + +#TODO : alternative @app.on_event("startup") ? +@asynccontextmanager +async def lifespan(app: FastAPI): + #startup + create_db_and_tables() + delete_knowledge() + yield + #shutdown + +app = FastAPI(lifespan=lifespan) + +@app.get("/") +async def root(): + return {"message": "Hello World"} \ No newline at end of file diff --git a/server/src/database.py b/server/src/database.py new file mode 100644 index 0000000..c63b1ee --- /dev/null +++ b/server/src/database.py @@ -0,0 +1,17 @@ +from dotenv import load_dotenv +import os +#import secrets +from sqlmodel import Session, SQLModel, create_engine + +load_dotenv() +database_uri=os.environ.get("DATABASE_URI") + +connect_args = {"check_same_thread": False} +engine = create_engine(database_uri, echo=True, connect_args=connect_args) + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + +def get_session(): + with Session(engine) as session: + yield session \ No newline at end of file diff --git a/server/src/knowledges/__pycache__/controller.cpython-311.pyc b/server/src/knowledges/__pycache__/controller.cpython-311.pyc new file mode 100644 index 0000000..9e8776d Binary files /dev/null and b/server/src/knowledges/__pycache__/controller.cpython-311.pyc differ diff --git a/server/src/knowledges/__pycache__/crud.cpython-311.pyc b/server/src/knowledges/__pycache__/crud.cpython-311.pyc new file mode 100644 index 0000000..58bc112 Binary files /dev/null and b/server/src/knowledges/__pycache__/crud.cpython-311.pyc differ diff --git a/server/src/knowledges/__pycache__/cruds.cpython-311.pyc b/server/src/knowledges/__pycache__/cruds.cpython-311.pyc new file mode 100644 index 0000000..652e680 Binary files /dev/null and b/server/src/knowledges/__pycache__/cruds.cpython-311.pyc differ diff --git a/server/src/knowledges/__pycache__/models.cpython-311.pyc b/server/src/knowledges/__pycache__/models.cpython-311.pyc new file mode 100644 index 0000000..f942fba Binary files /dev/null and b/server/src/knowledges/__pycache__/models.cpython-311.pyc differ diff --git a/server/src/knowledges/controller.py b/server/src/knowledges/controller.py new file mode 100644 index 0000000..d9b05e7 --- /dev/null +++ b/server/src/knowledges/controller.py @@ -0,0 +1,19 @@ +from src.knowledges.models import Knowledge +from src.knowledges.crud import create_knowledge as create_knowledge_model, read_knowledges as read_knowledges_model, read_knowledge as read_knowledge_model, update_knowledge as update_knowledge_model, delete_knowledge as delete_knowledge_model + +def create_knowledge(): + connaissance = Knowledge(content="La connaissance n'est pas.", uri="https://perdu.fr") + create_knowledge_model(connaissance) + return read_knowledges()[-1] + +def read_knowledges(): + return read_knowledges_model() + +def read_knowledge(knowledge_id): + return read_knowledge_model(knowledge_id) + +def update_knowledge(): + return update_knowledge_model(2, "faculté mentale produisant une assimilation par l'esprit d'un contenu objectif préalablement traduit en signes et en idées ;", "https://fr.wikipedia.org/wiki/Connaissance#D%C3%A9finition") + +def delete_knowledge(): + return delete_knowledge_model(7) diff --git a/server/src/knowledges/crud.py b/server/src/knowledges/crud.py new file mode 100644 index 0000000..d0d2e57 --- /dev/null +++ b/server/src/knowledges/crud.py @@ -0,0 +1,42 @@ +from sqlmodel import Session, select + +from src.knowledges.models import Knowledge +from src.database import engine + +def create_knowledge(knowledge: Knowledge): + with Session(engine) as session: + session.add(knowledge) + session.commit() + +def read_knowledges(): + with Session(engine) as session: + statement = select(Knowledge) + results = session.exec(statement) + knowledges = results.all() + return knowledges + +def read_knowledge(knowledge_id: int): + with Session(engine) as session: + #statement = select(Knowledge).where(Knowledge.id == knowledge_id) + #results = session.exec(statement) + #knowledge = results.first() + knowledge = session.get(Knowledge, knowledge_id) + return knowledge + +#TODO adapt logic with args +def update_knowledge(knowledge_id: int, content: str, uri: str): + with Session(engine) as session: + knowledge = session.get(Knowledge, knowledge_id) + knowledge.content = content if content else knowledge.content + knowledge.uri = uri if uri else knowledge.uri + + session.add(knowledge) + session.commit() + session.refresh(knowledge) + +def delete_knowledge(knowledge_id: int): + with Session(engine) as session: + knowledge = session.get(Knowledge, knowledge_id) + session.delete(knowledge) + session.commit() + #TODO : delete join questions \ No newline at end of file diff --git a/server/src/knowledges/models.py b/server/src/knowledges/models.py new file mode 100644 index 0000000..d98c182 --- /dev/null +++ b/server/src/knowledges/models.py @@ -0,0 +1,9 @@ +from sqlmodel import Field, SQLModel, Relationship +#TODO : add pydantic validation + +class Knowledge(SQLModel, table=True): + id: int | None = Field(default=None, primary_key=True) + content: str = Field(index=True) + uri: str = Field(index=True) + + questions: list["Question"] = Relationship(back_populates="knowledge", cascade_delete=True) # type: ignore \ No newline at end of file diff --git a/server/src/knowledges/router.py b/server/src/knowledges/router.py new file mode 100644 index 0000000..e69de29 diff --git a/server/src/metrics/__pycache__/models.cpython-311.pyc b/server/src/metrics/__pycache__/models.cpython-311.pyc new file mode 100644 index 0000000..95351cb Binary files /dev/null and b/server/src/metrics/__pycache__/models.cpython-311.pyc differ diff --git a/server/src/metrics/crud.py b/server/src/metrics/crud.py new file mode 100644 index 0000000..6077527 --- /dev/null +++ b/server/src/metrics/crud.py @@ -0,0 +1,29 @@ +from sqlmodel import Session, select + +from src.metrics.models import Metric +from src.questions.models import Question +from src.database import engine + +def create_metric(Metric: Metric): + with Session(engine) as session: + session.add(Metric) + session.commit() + + +def read_metrics(question): + with Session(engine) as session: + statement = select(Metric).where(Metric.question_id == question.id) + results = session.exec(statement) + metrics = results.all() + return metrics + +def read_metric(metric_id: int): + with Session(engine) as session: + metric = session.get(Metric, metric_id) + return metric + +def delete_metric(metric_id: int): + with Session(engine) as session: + metric = session.get(Metric, metric_id) + session.delete(metric) + session.commit() diff --git a/server/src/metrics/models.py b/server/src/metrics/models.py new file mode 100644 index 0000000..fdafd4f --- /dev/null +++ b/server/src/metrics/models.py @@ -0,0 +1,7 @@ +from sqlmodel import Field, SQLModel +#TODO : add pydantic validation + +class Metric(SQLModel, table=True): + id: int | None = Field(default=None, primary_key=True) + question_id: int | None = Field(default=None, foreign_key="question.id", ondelete="CASCADE") + need_index: int diff --git a/server/src/metrics/router.py b/server/src/metrics/router.py new file mode 100644 index 0000000..e69de29 diff --git a/server/src/questions/__pycache__/controller.cpython-311.pyc b/server/src/questions/__pycache__/controller.cpython-311.pyc new file mode 100644 index 0000000..b8d4cf3 Binary files /dev/null and b/server/src/questions/__pycache__/controller.cpython-311.pyc differ diff --git a/server/src/questions/__pycache__/crud.cpython-311.pyc b/server/src/questions/__pycache__/crud.cpython-311.pyc new file mode 100644 index 0000000..5ce8e5d Binary files /dev/null and b/server/src/questions/__pycache__/crud.cpython-311.pyc differ diff --git a/server/src/questions/__pycache__/models.cpython-311.pyc b/server/src/questions/__pycache__/models.cpython-311.pyc new file mode 100644 index 0000000..4450c54 Binary files /dev/null and b/server/src/questions/__pycache__/models.cpython-311.pyc differ diff --git a/server/src/questions/controller.py b/server/src/questions/controller.py new file mode 100644 index 0000000..70e0aa6 --- /dev/null +++ b/server/src/questions/controller.py @@ -0,0 +1,13 @@ +from src.questions.models import Question +from src.questions.crud import create_question as create_question_model, read_questions as read_questions_model, read_question as read_question_model +from src.knowledges.models import Knowledge + +def create_question(knowledge: Knowledge): + question = Question(knowledge = knowledge, question = "Quel est le sens du mot connaissance ?") + create_question_model(question) + +def read_questions(knowledge): + return read_questions_model(knowledge) + +def read_question(question_id): + return read_question_model(question_id) \ No newline at end of file diff --git a/server/src/questions/crud.py b/server/src/questions/crud.py new file mode 100644 index 0000000..0eae63d --- /dev/null +++ b/server/src/questions/crud.py @@ -0,0 +1,42 @@ +from sqlmodel import Session, select + +from src.questions.models import Question +from src.knowledges.models import Knowledge +from src.database import engine + +def create_question(Question: Question): + with Session(engine) as session: + session.add(Question) + session.commit() + + +def read_questions(knowledge): + with Session(engine) as session: + statement = select(Question).where(Question.knowledge_id == knowledge.id) + results = session.exec(statement) + questions = results.all() + return questions + +def read_question(question_id: int): + with Session(engine) as session: + question = session.get(Question, question_id) + return question + +# #TODO adapt logic with args +# def update_question(question_id: int, content: str, uri: str): +# with Session(engine) as session: +# question = session.get(Question, question_id) +# question.content = content if content else question.content +# question.uri = uri if uri else question.uri + +# session.add(question) +# session.commit() +# session.refresh(question) + +#TODO : test +def delete_question(question_id: int): + with Session(engine) as session: + question = session.get(Question, question_id) + session.delete(question) + session.commit() + diff --git a/server/src/questions/models.py b/server/src/questions/models.py new file mode 100644 index 0000000..923ebe7 --- /dev/null +++ b/server/src/questions/models.py @@ -0,0 +1,12 @@ +from sqlmodel import Field, SQLModel, Relationship +from src.knowledges.models import Knowledge +#TODO : add pydantic validation + +class Question(SQLModel, table=True): + id: int | None = Field(default=None, primary_key=True) + question:str + + knowledge_id: int | None = Field(default=None, foreign_key="knowledge.id", ondelete="CASCADE") + knowledge: Knowledge | None = Relationship(back_populates="questions") + + metrics: list["Metrics"] = Relationship(back_populates="question", cascade_delete=True) # type: ignore diff --git a/server/src/questions/router.py b/server/src/questions/router.py new file mode 100644 index 0000000..e69de29 diff --git a/server/src/questions/service.py b/server/src/questions/service.py new file mode 100644 index 0000000..e69de29 diff --git a/server/src/questions/utils.py b/server/src/questions/utils.py new file mode 100644 index 0000000..e69de29