From 0f1a07da49397321c7e3abfa5bf96f0cc3be8443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pelletier?= Date: Sun, 11 May 2025 21:51:32 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80=20Add=20feature:=20Frontend=20app?= =?UTF-8?q?=20working=20stub=20=F0=9F=9A=80=20Add=20feature:=20Ruff=20lint?= =?UTF-8?q?ing=20=F0=9F=9A=80=20Add=20feature:=20Logging?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/config.py | 15 ++++ backend/app/models.py | 14 +++- backend/app/routers/analysis_router.py | 12 +++- backend/app/routers/convert_router.py | 10 +-- backend/app/routers/export_router.py | 6 +- backend/app/routers/generate_router.py | 8 +-- backend/app/routers/import_router.py | 20 ++++-- backend/main.py | 7 +- frontend/app.py | 62 ++++++++++++++++ frontend/requirements.txt | 2 + requirements.txt | 1 + ruff.toml | 99 ++++++++++++++++++++++++++ 12 files changed, 234 insertions(+), 22 deletions(-) create mode 100644 backend/app/config.py create mode 100644 frontend/app.py create mode 100644 requirements.txt create mode 100644 ruff.toml diff --git a/backend/app/config.py b/backend/app/config.py new file mode 100644 index 0000000..093c753 --- /dev/null +++ b/backend/app/config.py @@ -0,0 +1,15 @@ +import logging + +from app.models import AvailableSource, AvailableSourcesResponse + +logger = logging.getLogger("base_logger") + +available_sources = AvailableSourcesResponse( + sources=[ + AvailableSource( + display_name="LinkedIn Shares", + name="linkedin_shares", + format="csv", + ), + ], +) diff --git a/backend/app/models.py b/backend/app/models.py index 9b640b9..7e9d08e 100644 --- a/backend/app/models.py +++ b/backend/app/models.py @@ -1,4 +1,4 @@ -from typing import Dict +from typing import Dict, List from pydantic import BaseModel @@ -7,6 +7,7 @@ class AnalysisRequest(BaseModel): analysis_type: str filters: Dict + class AnalysisResponse(BaseModel): result: str @@ -41,8 +42,19 @@ class GenerateResponse(BaseModel): class ImportRequest(BaseModel): + type: str data: str class ImportResponse(BaseModel): status: str + + +class AvailableSource(BaseModel): + display_name: str + name: str + format: str + + +class AvailableSourcesResponse(BaseModel): + sources: List[AvailableSource] diff --git a/backend/app/routers/analysis_router.py b/backend/app/routers/analysis_router.py index a2b9656..c10e70c 100644 --- a/backend/app/routers/analysis_router.py +++ b/backend/app/routers/analysis_router.py @@ -1,14 +1,20 @@ +from app.config import logger +from app.models import AnalysisRequest, AnalysisResponse from fastapi import APIRouter -from app.models import AnalysisResponse, AnalysisRequest - analysis_router = APIRouter(prefix="/analysis", tags=["Analysis"]) + @analysis_router.post("/", response_model=AnalysisResponse) def analysis_data(request: AnalysisRequest): """ - Analysis imported data (e.g., sentiment, keywords, or patterns). + Analyse imported data (e.g., sentiment, keywords, or patterns). """ + logger.info( + f"Analyse imported data " + f"on {request.analysis_type} " + f"with filters {request.filters}" + ) # Placeholder for analysis logic analysis = ... return {"analysis": analysis} diff --git a/backend/app/routers/convert_router.py b/backend/app/routers/convert_router.py index 39a6e85..4024fef 100644 --- a/backend/app/routers/convert_router.py +++ b/backend/app/routers/convert_router.py @@ -1,18 +1,20 @@ +from app.config import logger +from app.models import ConversionRequest, ConversionResponse from fastapi import APIRouter -from app.models import ConversionResponse, ConversionRequest - convert_router = APIRouter(prefix="/convert", tags=["Convert"]) + @convert_router.post("/", response_model=ConversionResponse) def convert_data(request: ConversionRequest): """ - Convert data from one format to another (e.g., JSON to CSV, text to XML). + Convert data from a source to normalized JSON """ + logger.info(f"Converting {request.source_type} data to normalized JSON") # Example conversion logic (replace with actual implementation) converted_data = ... return { "converted_data": converted_data, - "status": "success" + "status": "success", } diff --git a/backend/app/routers/export_router.py b/backend/app/routers/export_router.py index a7018c9..5c30d0b 100644 --- a/backend/app/routers/export_router.py +++ b/backend/app/routers/export_router.py @@ -1,13 +1,15 @@ +from app.config import logger +from app.models import ExportRequest, ExportResponse from fastapi import APIRouter -from app.models import ExportRequest, ExportResponse - export_router = APIRouter(prefix="/export", tags=["Export"]) + @export_router.post("/", response_model=ExportResponse) def export_data(request: ExportRequest): """ Export analysed data (e.g., as JSON, CSV, or PDF). """ + logger.info(f"Exporting data as {request.format}") exported_data_url = ... return {"url": exported_data_url} diff --git a/backend/app/routers/generate_router.py b/backend/app/routers/generate_router.py index c5b60bd..7b380c3 100644 --- a/backend/app/routers/generate_router.py +++ b/backend/app/routers/generate_router.py @@ -1,7 +1,7 @@ +from app.config import logger +from app.models import GenerateRequest, GenerateResponse from fastapi import APIRouter -from app.models import GenerateResponse, GenerateRequest - generate_router = APIRouter(prefix="/generate", tags=["Generate"]) @@ -11,5 +11,5 @@ def generate_content(request: GenerateRequest): Generate new content (e.g., text, images, or summaries). """ # Placeholder for generation logic (e.g., LLM, AI model) - response = ... - return response + logger.info(f"Generating content for request: {request.prompt}") + return ... diff --git a/backend/app/routers/import_router.py b/backend/app/routers/import_router.py index a1fe6f6..bcc9546 100644 --- a/backend/app/routers/import_router.py +++ b/backend/app/routers/import_router.py @@ -1,7 +1,7 @@ +from app.config import available_sources, logger +from app.models import AvailableSourcesResponse, ImportRequest, ImportResponse from fastapi import APIRouter -from app.models import ImportRequest, ImportResponse - import_router = APIRouter(prefix="/import", tags=["Import"]) @@ -10,5 +10,17 @@ def import_data(request: ImportRequest): """ Import data (e.g., text, files, or structured data). """ - response = ... - return response + logger.info(f"Receiver importation request: {request.type}") + return ... + + +@import_router.get( + "/available_sources", response_model=AvailableSourcesResponse +) +def get_available_sources(): + """ + Get available sources from database + :return: Available sources in an AvailableSourcesResponse object + """ + logger.info("Get available sources from database") + return available_sources diff --git a/backend/main.py b/backend/main.py index 7f98564..e3df413 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,10 +1,9 @@ -from fastapi import FastAPI -from app.routers.convert_router import convert_router -from app.routers.import_router import import_router from app.routers.analysis_router import analysis_router +from app.routers.convert_router import convert_router from app.routers.export_router import export_router from app.routers.generate_router import generate_router - +from app.routers.import_router import import_router +from fastapi import FastAPI app = FastAPI(title="Retro API", description="Retro content management system") diff --git a/frontend/app.py b/frontend/app.py new file mode 100644 index 0000000..eeb7e6f --- /dev/null +++ b/frontend/app.py @@ -0,0 +1,62 @@ +import os + +import dotenv +import requests +import streamlit as st + +dotenv.load_dotenv() + +# Set the backend API base URL +BACKEND_URL = os.environ.get("BACKEND_URL") + +# Database variables +available_sources = requests.get( + f"{BACKEND_URL}/import/available_sources" +).json() + +# Application tabs +tabs = st.tabs(["Conversion", "Analysis", "Data", "Settings", "Dashboard"]) + +# Application core +with tabs[0]: + st.header("Conversion de source") + with st.form("conversion_form"): + input_file = st.file_uploader("Télécharge un fichier à convertir") + input_text = st.text_area("Saisis du texte à convertir") + source_type = st.selectbox( + label="Sélectionne la source", + options=[ + source.get("name") + for source in available_sources.get("sources") + ], + format_func=lambda x: { + source.get("name"): source.get("display_name") + for source in available_sources.get("sources") + }.get(x), + ) + if input_text: + source_data = input_text + elif input_file: + source_data = input_file + else: + st.error("Tu dois fournir un fichier ou du texte") + submitted = st.form_submit_button("Convertir") + if submitted: + response = requests.post( + f"{BACKEND_URL}/convert", + json={"source_type": source_type, "source_data": source_data}, + ) + st.success("Conversion Result:") + st.write(response.json()) + +with tabs[1]: + st.header("Importer des données") + +with tabs[2]: + st.header("Analyser des données") + +with tabs[3]: + st.header("Exporter des données") + +with tabs[4]: + st.header("Générer une publication") diff --git a/frontend/requirements.txt b/frontend/requirements.txt index 917c82a..0546f1a 100644 --- a/frontend/requirements.txt +++ b/frontend/requirements.txt @@ -1,2 +1,4 @@ streamlit +requests +python-dotenv diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..ede3eb4 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +ruff \ No newline at end of file diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000..8b18102 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,99 @@ +# This configuration file is based on PEP8 Style Guide for Python + +# Exclude a variety of commonly ignored directories. +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".ipynb_checkpoints", + ".mypy_cache", + ".nox", + ".pants.d", + ".pyenv", + ".pytest_cache", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + ".vscode", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "site-packages", + "venv", +] + +# Same as PEP8. +line-length = 79 +indent-width = 4 + +[lint] +preview = true +select = ["F", # Pyflakes + "E", # pycodestyle + "I", # isort + "N", # pep8-naming + "FBT", # flake8-boolean-trap + "B", # flake8-bugbear + "A", # flake8-builtins + "COM", # flake8-commas + "C4", # flake8-comprehensions + "DTZ", # flake8-datetimez + "EM", # flake8-errmsg + "EXE", # flake8-executable + "ISC", # flake8-implicit-str-concat + "ICN", # flake8-import-conventions + "LOG", # flake8-logging + "G", # flake8-logging-format + "PIE", # flake8-pie + "PYI", # flake8-pyi + "Q", # flake8-quotes + "RSE", # flake8-raise + "RET", # flake8-return + "SLOT", # flake8-slots + "SIM", # flake8-simplify + "TID", # flake8-tidy-imports + "TCH", # flake8-type-checking + "ARG", # flake8-unused-arguments + "PD", # pandas-vet + "TRY", # tryceratops + "PERF", # Perflint + "RUF"] # Ruff-specific rules +ignore = [ + "COM812" +] + +# Allow fix for all enabled rules (when `--fix`) is provided. +fixable = ["ALL"] +unfixable = [] + +# Allow unused variables when underscore-prefixed. +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + +[format] +line-ending = "auto" # Automatically detect the appropriate line ending. +# quote-style = "double" # Don't work when is flake8-quotes (Q) activated | Use double quotes for strings. +indent-style = "space" # Indent with spaces, rather than tabs. +skip-magic-trailing-comma = false # Respect magic trailing commas. + + +# Enable auto-formatting of code examples in docstrings. Markdown, +# reStructuredText code/literal blocks and doctests are all supported. +# +# This is currently disabled by default, but it is planned for this +# to be opt-out in the future. +docstring-code-format = false + +# Set the line length limit used when formatting code snippets in +# docstrings. +# +# This only has an effect when the `docstring-code-format` setting is +# enabled. +docstring-code-line-length = "dynamic" \ No newline at end of file