diff --git a/.env.template b/.env.template index 4003ba7..016240b 100644 --- a/.env.template +++ b/.env.template @@ -2,5 +2,6 @@ SECRET_KEY= USERNAME= PASS_HASH= #PASSWORD=only for testing -FABRIQUEDOC_ENDPOINT= +FABRIQUEDOC_ENDPOINT= # Use http://backend:8080 for Docker Compose, or http://127.0.0.1:8080 for local development +FABRIQUEDOC_PUBLIC_ENDPOINT= # Use http://127.0.0.1:8080 for local development, or your public IP/domain for deployment ALGORITHM=HS256 diff --git a/backend/.DS_Store b/backend/.DS_Store new file mode 100644 index 0000000..810ea86 Binary files /dev/null and b/backend/.DS_Store differ diff --git a/backend/routers/images.py b/backend/routers/images.py index bbc79f5..dee6e26 100644 --- a/backend/routers/images.py +++ b/backend/routers/images.py @@ -10,7 +10,7 @@ from models import User router = APIRouter() @router.get("/") -async def get_images(current_user: Annotated[User, Depends(get_current_active_user)]): +async def get_images(): # list all files in resources/images files = [f for f in os.listdir(f"{os.getcwd()}/resources/images") if os.path.isfile(os.path.join(f"{os.getcwd()}/resources/images", f))] # sort the files @@ -18,7 +18,7 @@ async def get_images(current_user: Annotated[User, Depends(get_current_active_us return {"images": files} @router.get("/{nom_image}") -async def get_image(nom_image: str, current_user: Annotated[User, Depends(get_current_active_user)]): +async def get_image(nom_image: str): return FileResponse(f"{os.getcwd()}/resources/images/{nom_image}") @router.post("/") diff --git a/backend/styles/autohebergement/instagram-fullscreen/atelier1-fullscreen-autohebergement-1080-1350-jaune.png b/backend/styles/autohebergement/instagram-fullscreen/atelier1-fullscreen-autohebergement-1080-1350-jaune.png new file mode 100644 index 0000000..567c819 Binary files /dev/null and b/backend/styles/autohebergement/instagram-fullscreen/atelier1-fullscreen-autohebergement-1080-1350-jaune.png differ diff --git a/backend/styles/autohebergement/instagram-fullscreen/atelier1-fullscreen-autohebergement-1080-1350-jaune.svg b/backend/styles/autohebergement/instagram-fullscreen/atelier1-fullscreen-autohebergement-1080-1350-jaune.svg new file mode 100644 index 0000000..cb872b0 --- /dev/null +++ b/backend/styles/autohebergement/instagram-fullscreen/atelier1-fullscreen-autohebergement-1080-1350-jaune.svg @@ -0,0 +1,156 @@ + + + +ATELIER #1 INTRODUCTION À L'AUTOHÉBERGEMENT15 AVRIL 2025 À 9H00 JEVALIDE.CA/AUTO1 diff --git a/backend/styles/autohebergement/linkedin/banniere-autohebergement-1920-jaune.png b/backend/styles/autohebergement/linkedin/banniere-autohebergement-1920-jaune.png new file mode 100644 index 0000000..3fe89ca Binary files /dev/null and b/backend/styles/autohebergement/linkedin/banniere-autohebergement-1920-jaune.png differ diff --git a/backend/styles/autohebergement/linkedin/banniere-autohebergement-1920-jaune.svg b/backend/styles/autohebergement/linkedin/banniere-autohebergement-1920-jaune.svg new file mode 100644 index 0000000..c9a81cc --- /dev/null +++ b/backend/styles/autohebergement/linkedin/banniere-autohebergement-1920-jaune.svg @@ -0,0 +1,136 @@ + + + +Intro autohébergement15 avril 2025 à 9h00 jevalide.ca/auto1 diff --git a/backend/styles/rituels-numeriques/slide169/slides-rituels-numeriques-1920-1080-jaune-top.png b/backend/styles/rituels-numeriques/slide169/slides-rituels-numeriques-1920-1080-jaune-top.png new file mode 100644 index 0000000..1ec1f79 Binary files /dev/null and b/backend/styles/rituels-numeriques/slide169/slides-rituels-numeriques-1920-1080-jaune-top.png differ diff --git a/backend/styles/rituels-numeriques/slide169/slides-rituels-numeriques-1920-1080-jaune-top.svg b/backend/styles/rituels-numeriques/slide169/slides-rituels-numeriques-1920-1080-jaune-top.svg new file mode 100644 index 0000000..5cba18e --- /dev/null +++ b/backend/styles/rituels-numeriques/slide169/slides-rituels-numeriques-1920-1080-jaune-top.svg @@ -0,0 +1,152 @@ + + + +RITUELSNUMÉRIQUES diff --git a/frontend/app_tab1.py b/frontend/app_tab1.py index 250cf47..8b49450 100644 --- a/frontend/app_tab1.py +++ b/frontend/app_tab1.py @@ -1,6 +1,8 @@ # Importation des bibliothèques nécessaires import mdformat # Pour formater le texte Markdown import streamlit as st # Pour créer l'interface utilisateur web +import requests # Pour effectuer des requêtes HTTP +import re # Pour les expressions régulières # Importation du texte de démonstration depuis un autre fichier from demo_text import demo_text @@ -9,22 +11,76 @@ from demo_text import demo_text def app_tab1(): # Fonction de rappel pour le bouton de formatage def button1_callback(): + # Récupère le texte Markdown actuel + markdown_text = st.session_state['markdown'] + + # Vérifie si l'utilisateur veut ajouter des nouvelles pages + if st.session_state.get('add_newpage_checkbox', False): + header_level = st.session_state['header_level_select'] + + # 1. Insérer \newpage avant le niveau d'en-tête spécifié + lines = markdown_text.split('\n') + new_lines = [] + for line in lines: + if line.startswith('#' * header_level + ' '): + new_lines.append('\\newpage') + new_lines.append(line) + markdown_text = '\n'.join(new_lines) + + # 2. Supprimer les \newpage trop proches, sauf s'il y a une image + final_lines = [] + i = 0 + while i < len(new_lines): + if new_lines[i] == '\\newpage': + # Vérifier si la prochaine \newpage est trop proche + next_newpage_index = -1 + for j in range(i + 1, min(i + 11, len(new_lines))): # Vérifier jusqu'à 10 lignes plus loin + if new_lines[j] == '\\newpage': + next_newpage_index = j + break + + if next_newpage_index != -1: + # Vérifier s'il y a une image entre les deux \newpage + image_found = False + for k in range(i + 1, next_newpage_index): + if '!' in new_lines[k] and '[' in new_lines[k] and ']' in new_lines[k] and '(' in new_lines[k] and ')' in new_lines[k]: + image_found = True + break + + if not image_found: + # Supprimer la \newpage actuelle si la prochaine est trop proche et pas d'image + i += 1 # Passer la \newpage actuelle + continue # Ne pas l'ajouter à final_lines + + final_lines.append(new_lines[i]) + i += 1 + + markdown_text = '\n'.join(final_lines) + # Formate le texte Markdown stocké dans la session et active la numérotation - st.session_state['markdown'] = mdformat.text(st.session_state['markdown'], + st.session_state['markdown'] = mdformat.text(markdown_text, options={"number": True}) # Création d'une zone de texte pour entrer le contenu Markdown - st.session_state['content'] = st.text_area( + st.text_area( # Texte d'instruction pour l'utilisateur "Entre ton texte ici. Les images sont dans ./images/", # Texte par défaut (démonstration) - demo_text, + st.session_state.get('markdown', demo_text), # Hauteur de la zone de texte en pixels height=450, # Clé unique pour identifier cet élément dans la session key='markdown' ) + # Menu déroulant pour choisir le niveau d'en-tête + st.selectbox( + "Insérer une nouvelle page avant les en-têtes de niveau :", + options=[1, 2, 3, 4, 5, 6], + index=1, # Par défaut, niveau 2 + key='header_level_select' + ) + # Création d'un bouton pour formater le texte st.button( # Texte affiché sur le bouton diff --git a/frontend/app_tab2.py b/frontend/app_tab2.py index 365fbba..07c8d44 100644 --- a/frontend/app_tab2.py +++ b/frontend/app_tab2.py @@ -1,6 +1,8 @@ # Importation de la bibliothèque Streamlit # Streamlit est utilisé pour créer des applications web interactives en Python import streamlit as st +import re # Pour les expressions régulières +import requests # Pour effectuer des requêtes HTTP # Définition de la fonction principale pour l'onglet 2 de l'application def app_tab2(): @@ -12,4 +14,29 @@ def app_tab2(): # st.markdown() est utilisé pour rendre du texte formaté en Markdown # st.session_state est un dictionnaire qui conserve les données entre les rechargements de page # ['content'] fait référence à la clé où le contenu Markdown est stocké - st.markdown(st.session_state['content']) \ No newline at end of file + + markdown_content = st.session_state['markdown'] + fabriquedoc_public_endpoint = st.session_state['fabriquedoc_public_endpoint'] + bearer_token = st.session_state['bearer_token'] + + # Regex pour trouver les images Markdown: ![alt text](path/to/image.ext) + # Capture le chemin complet de l'image (y compris les répertoires si présents) + image_pattern = re.compile(r'!\[.*?\]\((.*?)\)') + + def replace_image_path(match): + image_path = match.group(1) + # Si le chemin est déjà une URL complète, ne rien faire + if image_path.startswith('http://') or image_path.startswith('https://'): + return match.group(0) + + # Construire l'URL complète de l'image + # On suppose que le chemin dans le markdown est juste le nom du fichier ou un chemin relatif simple + # et que le backend sert les images via /images/{nom_fichier} + image_name = image_path.split('/')[-1] # Prend juste le nom du fichier + full_image_url = f"{fabriquedoc_public_endpoint}/images/{image_name}" + return f"![{match.group(0).split('](')[0][2:]}]({full_image_url})" + + # Remplacer tous les chemins d'images relatifs par des URLs complètes + processed_markdown_content = image_pattern.sub(replace_image_path, markdown_content) + + st.markdown(processed_markdown_content) \ No newline at end of file diff --git a/frontend/demo_text.py b/frontend/demo_text.py index 66c66cf..4f992fd 100644 --- a/frontend/demo_text.py +++ b/frontend/demo_text.py @@ -48,8 +48,8 @@ Ceci est centré ## Images -- ![Texte alternatif](url-de-l-image.jpg) -- [![Image avec lien](url-de-l-image-miniature.jpg)](url-de-l-image-complete.jpg) +- ![Logo](/images/logo-complet-noir.png) +- [![Logo](/images/logo-complet-noir.png)](https://jevalide.ca) ## Citations diff --git a/frontend/main.py b/frontend/main.py index 2324b5c..446a9e7 100644 --- a/frontend/main.py +++ b/frontend/main.py @@ -32,7 +32,11 @@ def init_session_state(): if 'fabriquedoc_endpoint' not in st.session_state: load_dotenv() # Charge les variables d'environnement depuis un fichier .env # Définit l'URL du backend, avec une valeur par défaut si non spécifiée - st.session_state['fabriquedoc_endpoint'] = os.environ.get("FABRIQUEDOC_ENDPOINT", "http://127.0.0.1:8080") + # Définit l'URL du backend. Utilise 'http://backend:8080' pour Docker Compose, + # ou 'http://127.0.0.1:8080' pour un développement local sans Docker Compose. + st.session_state['fabriquedoc_endpoint'] = os.environ.get("FABRIQUEDOC_ENDPOINT", "http://backend:8080") + + st.session_state['fabriquedoc_public_endpoint'] = os.environ.get("FABRIQUEDOC_PUBLIC_ENDPOINT", "http://127.0.0.1:8080") # Initialise d'autres variables de session avec des valeurs par défaut for key, default_value in [('options', ""), ('bearer_token', ""), ('logged_in', False)]: