diff --git a/.env.template b/.env.template index 3c405f4..cde3d60 100644 --- a/.env.template +++ b/.env.template @@ -1 +1,2 @@ -BACKEND_URL= \ No newline at end of file +BACKEND_URL= +FLASK_DEBUG= \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index d01dd77..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,40 +0,0 @@ -# .gitlab-ci.yml - -stages: - - dockerize - - deployment - -build-push-docker-image-job: - stage: dockerize - # Specify a Docker image to run the job in. - image: docker:20-dind - # Specify an additional image 'docker:dind' ("Docker-in-Docker") that - # will start up the Docker daemon when it is brought up by a runner. - before_script: - - docker login -u "$DOCKER_REGISTRY_USER" -p "$DOCKER_REGISTRY_PASSWORD" $DOCKER_REGISTRY_URL # Instructs GitLab to login to its registry - services: - - name: docker:20-dind - alias: docker - command: ["--tls=false"] - script: - - echo "Building..." # MAKE SURE NO SPACE ON EITHER SIDE OF = IN THE FOLLOWING LINE - - export CONTAINER_FULL_IMAGE_NAME_WITH_TAG=$IMAGE_NAME_WITH_REGISTRY_PREFIX/my-build-image:$COMMIT_HASH - - docker build --network=host -f ./Dockerfile --pull -t built-image-name . - - docker tag built-image-name "$CONTAINER_FULL_IMAGE_NAME_WITH_TAG" - - docker push "$CONTAINER_FULL_IMAGE_NAME_WITH_TAG" - - echo "$CONTAINER_FULL_IMAGE_NAME_WITH_TAG" - - echo "Deploying on CapRover..." - - docker run --network=host caprover/cli-caprover:2.2.3 caprover deploy --caproverUrl "$CAPROVER_URL" --caproverPassword "$CAPROVER_PASSWORD" -a "$CAPROVER_APP" -i "$CONTAINER_FULL_IMAGE_NAME_WITH_TAG" - only: - - main - variables: - DOCKER_DRIVER: overlay2 - DOCKER_REGISTRY_USER: ${CI_REGISTRY_USER} - DOCKER_REGISTRY_PASSWORD: ${CI_REGISTRY_PASSWORD} - DOCKER_REGISTRY_URL: ${CI_REGISTRY} - IMAGE_NAME_WITH_REGISTRY_PREFIX: ${CI_REGISTRY_IMAGE} - COMMIT_HASH: ${CI_COMMIT_SHA} - CAPROVER_URL: ${CAPROVER_URL} - CAPROVER_PASSWORD: ${CAPROVER_PASSWORD} - CAPROVER_APP: ${CAPROVER_APP} - diff --git a/.woodpecker.yaml b/.woodpecker.yaml deleted file mode 100644 index ac075bd..0000000 --- a/.woodpecker.yaml +++ /dev/null @@ -1,19 +0,0 @@ -steps: - - name: docker - image: plugins/docker - commands: - - docker login docker.io -u $${DOCKERHUB_USERNAME} -p $${DOCKERHUB_PASSWORD} - - docker build --rm=true -f Dockerfile -t $${CI_COMMIT_REF} . - - docker tag $${CI_COMMIT_REF} $${DOCKERHUB_USERNAME}/$${CI_REPO_NAME}:latest - - docker push $${DOCKERHUB_USERNAME}/$${CI_REPO_NAME}:latest - secrets: [ dockerhub_username, dockerhub_password ] - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - name: caprover - image: plugins/docker - commands: - - docker login docker.io -u $${DOCKERHUB_USERNAME} -p $${DOCKERHUB_PASSWORD} - - docker run --network=host caprover/cli-caprover:2.2.3 caprover deploy --caproverUrl "$${CAPROVER_URL}" --caproverPassword "$${CAPROVER_PASSWORD}" -a "$${CI_REPO_NAME}" -i docker.io/$${DOCKERHUB_USERNAME}/$${CI_REPO_NAME} - secrets: [ dockerhub_username, dockerhub_password, caprover_url, caprover_password ] - volumes: - - /var/run/docker.sock:/var/run/docker.sock diff --git a/Dockerfile b/Dockerfile index 592ea31..7e54dd8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ -FROM python:3.10-slim +FROM python:3.13-slim-bookworm -EXPOSE 8051 +EXPOSE 5000 WORKDIR /app @@ -10,9 +10,14 @@ RUN pip install -r requirements.txt # Copy the app's code COPY config.py ./ -COPY main.py ./ +COPY app.py ./ COPY ressources ./ressources +COPY templates ./templates +COPY static ./static + +# Set the environment variable for Flask +ENV FLASK_APP=app.py +ENV FLASK_RUN_HOST=0.0.0.0 # Set the entrypoint to run the app -ENTRYPOINT [ "streamlit", "run" ] -CMD [ "main.py", "--server.port=8051", "--server.headless", "true", "--server.fileWatcherType", "none", "--browser.gatherUsageStats", "false"] +CMD ["flask", "run", "--port=5000"] \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..d3e81ac --- /dev/null +++ b/app.py @@ -0,0 +1,35 @@ +from flask import Flask, render_template, request, jsonify +from flaskext.markdown import Markdown +from markupsafe import Markup +import requests +import os +from config import settings + +app = Flask(__name__) +Markdown(app) + +def correct_text(text): + url = f"{settings.BACKEND_URL}/corriger" + response = requests.post(url, json={"text": text}) + if response.status_code != 200: + return {"error": f"Erreur lors de la requête au serveur: {response.status_code}"} + else: + return {"text": response.json()["text"]} + +@app.route('/') +def index(): + with open("ressources/header.md", "r") as f: + header = f.read() + with open("ressources/footer.md", "r") as f: + footer = f.read() + return render_template('index.html', header=header, footer=footer) + +@app.route('/correct', methods=['POST']) +def correct(): + text = request.form['text'] + result = correct_text(text) + return jsonify(result) + +if __name__ == '__main__': + debug_mode = os.getenv('FLASK_DEBUG', 'False').lower() == 'true' + app.run(host='localhost', debug=debug_mode) \ No newline at end of file diff --git a/config.py b/config.py index d0ddaaf..77aa02a 100644 --- a/config.py +++ b/config.py @@ -1,17 +1,9 @@ -from typing import Optional +import os +from dotenv import load_dotenv -from pydantic import BaseModel -from pydantic_settings import BaseSettings -from dotenv import find_dotenv - -LOGGER_NAME = "point-median-frontend" - - -class Settings(BaseSettings): - BACKEND_URL: str = "http://localhost:8000" - - class Config: - env_file = find_dotenv() +load_dotenv() +class Settings: + BACKEND_URL = os.getenv("BACKEND_URL") settings = Settings() diff --git a/docker-run.sh b/docker-run.sh index bdf6552..1269038 100644 --- a/docker-run.sh +++ b/docker-run.sh @@ -1,4 +1,27 @@ -docker stop point-median-frontend -docker rm point-median-frontend -# Ce programme sert à lancer le job_dispatcher dans un docker localement pour tester -docker run -p 8051:8051 --name point-median-frontend --env-file .env --network host local/point-median-frontend +#!/bin/bash + +# Name of the Docker network and application +NETWORK_NAME="point-median-network" +APP_NAME="point-median-frontend" + +# Create the Docker network if it doesn't exist +if ! docker network inspect $NETWORK_NAME >/dev/null 2>&1; then + echo "Creating Docker network: $NETWORK_NAME" + docker network create $NETWORK_NAME +else + echo "Docker network $NETWORK_NAME already exists" +fi + +# Stop and remove the existing container if it exists +docker stop $APP_NAME >/dev/null 2>&1 +docker rm $APP_NAME >/dev/null 2>&1 +# Run the new container +echo "Starting $APP_NAME container" +docker run -d \ + -p 5000:5000 \ + --name $APP_NAME \ + --env-file .env \ + --network $NETWORK_NAME \ + local/$APP_NAME + +echo "Container started. You can access the application at http://localhost:5000" diff --git a/main.py b/main.py deleted file mode 100644 index 60c9450..0000000 --- a/main.py +++ /dev/null @@ -1,34 +0,0 @@ -import streamlit as st -import requests -from config import settings -import streamlit.components.v1 as components - - -def correct_text(text): - url = f"{settings.BACKEND_URL}/corriger" - response = requests.post(url, json={"text": text}) - if response.status_code != 200: - st.error("Erreur lors de la requête au serveur: {response.status_code}") - return "" - else: - return response.json()["text"] - - -def main(): - st.title("Application Point Médian") - - with open("ressources/header.md", "r") as f: - st.markdown(f.read(), unsafe_allow_html=False) - - text = st.text_area("Entre le texte à corriger", placeholder="Écris ton texte ici", height=200) - if st.button("Corriger"): - corrected_text = correct_text(text) - st.text_area("Texte corrigé:", value=corrected_text, height=200) - with open("ressources/formulaire_courriel.html", "r") as f: - components.html(f.read(), height=350, scrolling=True) - with open("ressources/footer.md", "r") as f: - st.markdown(f.read(), unsafe_allow_html=False) - - -if __name__ == "__main__": - main() diff --git a/requirements.txt b/requirements.txt index 0b87f22..b6eedae 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -pydantic~=2.3.0 -pydantic-settings==2.0.3 -streamlit~=1.26.0 -requests~=2.31.0 -python-dotenv~=1.0.0 +Flask==2.3.2 +Flask-Markdown==0.3 +MarkupSafe==2.1.3 +requests +python-dotenv \ No newline at end of file diff --git a/ressources/formulaire_courriel.html b/ressources/formulaire_courriel.html index 0072f35..958c4b6 100644 --- a/ressources/formulaire_courriel.html +++ b/ressources/formulaire_courriel.html @@ -3,15 +3,10 @@

Abonne-toi pour ne rien manquer

-

-

- -

- - -

+

+

@@ -20,7 +15,6 @@

-

diff --git a/ressources/header.md b/ressources/header.md index 24fa811..2e2a256 100644 --- a/ressources/header.md +++ b/ressources/header.md @@ -1,2 +1,3 @@ -Cette application convertit le texte entré dans la boîte en forme inclusive abrégée avec le point régulier . vers le point médian ·. -Les séparateurs / et - et les formes avec les parenthèses sont aussi convertis. +Cette application convertit le texte entré dans la boîte en forme inclusive abrégée avec le point régulier `.` vers le point médian `·`. + +Les séparateurs `/` et `-` et les formes avec les parenthèses sont aussi convertis. diff --git a/static/fonts/Cinzel/Cinzel-Bold.ttf b/static/fonts/Cinzel/Cinzel-Bold.ttf new file mode 100644 index 0000000..6200673 Binary files /dev/null and b/static/fonts/Cinzel/Cinzel-Bold.ttf differ diff --git a/static/fonts/Cinzel/Cinzel-Regular.ttf b/static/fonts/Cinzel/Cinzel-Regular.ttf new file mode 100644 index 0000000..c92b3db Binary files /dev/null and b/static/fonts/Cinzel/Cinzel-Regular.ttf differ diff --git a/static/fonts/Cinzel/Cinzel-SemiBold.ttf b/static/fonts/Cinzel/Cinzel-SemiBold.ttf new file mode 100644 index 0000000..cd59bfc Binary files /dev/null and b/static/fonts/Cinzel/Cinzel-SemiBold.ttf differ diff --git a/static/fonts/FiraSans/FiraSans-Light.ttf b/static/fonts/FiraSans/FiraSans-Light.ttf new file mode 100644 index 0000000..663d1de Binary files /dev/null and b/static/fonts/FiraSans/FiraSans-Light.ttf differ diff --git a/static/fonts/FiraSans/FiraSans-Medium.ttf b/static/fonts/FiraSans/FiraSans-Medium.ttf new file mode 100644 index 0000000..001ebe7 Binary files /dev/null and b/static/fonts/FiraSans/FiraSans-Medium.ttf differ diff --git a/static/fonts/FiraSans/FiraSans-Regular.ttf b/static/fonts/FiraSans/FiraSans-Regular.ttf new file mode 100644 index 0000000..6f80647 Binary files /dev/null and b/static/fonts/FiraSans/FiraSans-Regular.ttf differ diff --git a/static/fonts/FiraSans/FiraSans-SemiBold.ttf b/static/fonts/FiraSans/FiraSans-SemiBold.ttf new file mode 100644 index 0000000..0c93b7e Binary files /dev/null and b/static/fonts/FiraSans/FiraSans-SemiBold.ttf differ diff --git a/static/script.js b/static/script.js new file mode 100644 index 0000000..af1b203 --- /dev/null +++ b/static/script.js @@ -0,0 +1,25 @@ +$(document).ready(function() { + $('#correct-button').click(function() { + var text = $('#input-text').val(); + $.ajax({ + url: '/correct', + method: 'POST', + data: { text: text }, + success: function(response) { + if (response.error) { + alert(response.error); + } else { + $('#output-text').val(response.text); + } + }, + error: function() { + alert('Une erreur est survenue lors de la correction du texte.'); + } + }); + }); + + // Load email form + $.get('ressources/formulaire_courriel.html', function(data) { + $('#email-form').html(data); + }); +}); \ No newline at end of file diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..2e03893 --- /dev/null +++ b/static/style.css @@ -0,0 +1,214 @@ +:root { + --bg-color: #001233; + --text-color: #c5c5c5; + --accent-color: #daa520; + --secondary-color: #791cf8; + --tertiary-color: #daa520; + --link-color: #954df9; + --link-hover-color: #daa520; + --code-bg-color: #1d1d1d; + --code-text-color: #c1bdbd; +} + +@font-face { + font-family: 'Fira Sans'; + src: url(/static/fonts/FiraSans/FiraSans-Light.ttf) format("truetype"); + font-weight: 300; + font-style: normal +} + +@font-face { + font-family: 'Fira Sans'; + src: url(/static/fonts/FiraSans/FiraSans-Regular.ttf) format("truetype"); + font-weight: 400; + font-style: normal +} + +@font-face { + font-family: 'Fira Sans'; + src: url(/static/fonts/FiraSans/FiraSans-Medium.ttf) format("truetype"); + font-weight: 500; + font-style: normal +} + +@font-face { + font-family: 'Fira Sans'; + src: url(/static/fonts/FiraSans/FiraSans-SemiBold.ttf) format("truetype"); + font-weight: 600; + font-style: normal +} + +@font-face { + font-family: 'Cinzel'; + src: url(/static/fonts/Cinzel/Cinzel-Regular.ttf) format("truetype"); + font-weight: 400; + font-style: normal +} + +@font-face { + font-family: 'Cinzel'; + src: url(/static/fonts/Cinzel/Cinzel-SemiBold.ttf) format("truetype"); + font-weight: 600; + font-style: normal +} + +@font-face { + font-family: 'Cinzel'; + src: url(/static/fonts/Cinzel/Cinzel-Bold.ttf) format("truetype"); + font-weight: 700; + font-style: normal +} + +.container { + width: 100%; + max-width: 100%; + margin: 0 auto; + padding: 0 20px; + box-sizing: border-box; +} + +@media screen and (min-width: 768px) { + .container { + width: 80%; + max-width: 1200px; + } +} + +body { + margin: 0; + padding: 0; +} + +/* Adjust the existing body styles */ +body { + background-color: var(--bg-color); + color: var(--text-color); + font-family: 'Fira Sans', sans-serif; + font-weight: 300; + font-size: 18px; + line-height: 1.6; + background-image: radial-gradient(circle at 10% 20%, #8a2be20d 0%, transparent 20%), radial-gradient(circle at 90% 80%, #32cd320d 0%, transparent 20%); +} + +/* Adjust padding for elements inside the container */ +.container > * { + padding-left: 0; + padding-right: 0; +} + +strong { + font-weight: 600 +} + +h1 { + font-family: 'Cinzel', serif; + font-weight: 800; + color: var(--accent-color); + text-shadow: 2px 2px 4px #00000080; + letter-spacing: 1px; + font-size: 2.5em +} + +h2, h3 { + font-family: 'Cinzel', serif; + font-weight: 600; + color: var(--accent-color); + text-shadow: 1px 1px 3px #0000004d; + letter-spacing: .5px +} + +h4, h5 { + font-family: 'Cinzel', serif; + font-weight: 400; + color: var(--accent-color); + text-shadow: 1px 1px 3px #0000004d; + letter-spacing: .5px +} + +h2 { + font-size: 2em +} + +h3 { + font-size: 1.75em +} + +h4 { + font-size: 1.5em +} + +h5 { + font-size: 1.25em +} + +a { + color: var(--link-color); + text-decoration: none; + transition: all .3s ease +} + +a:hover { + color: var(--link-hover-color); + text-decoration: underline +} + +textarea { + width: 100%; + padding: 15px 20px; + margin: 15px; + background-color: var(--code-bg-color); + color: var(--code-text-color); + border: 1px solid var(--accent-color); + border-radius: 5px; + font-family: 'Fira Sans', monospace; + font-size: 16px; + resize: vertical; + line-height: 1.5; + box-sizing: border-box; +} + +textarea:focus { + outline: none; + border-color: var(--secondary-color); + box-shadow: 0 0 0 2px rgba(121, 28, 248, 0.2); +} + +button { + padding: 12px 24px; + background-color: var(--secondary-color); + color: var(--text-color); + border: none; + border-radius: 30px; + cursor: pointer; + font-family: 'Fira Sans', sans-serif; + font-size: 18px; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 1px; + transition: all 0.3s ease; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + margin: 10px 0; + display: inline-block; + min-width: 150px; +} + +button:hover { + background-color: var(--tertiary-color); + color: var(--bg-color); + transform: translateY(-2px); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2); +} + +button:active { + transform: translateY(1px); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); +} + +button:focus { + outline: none; + box-shadow: 0 0 0 3px rgba(218, 165, 32, 0.4); +} + +.markdown-content { + margin-bottom: 20px; +} \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..d4274c2 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,31 @@ + + + + + + Application Point Médian + + + +
+

Application Point Médian

+ +
+ {{ header | markdown }} +
+ + + + + +
+ +
+ {{ footer | markdown }} +
+
+ + + + + \ No newline at end of file