Merge branch 'hotfix/2.1.7'

This commit is contained in:
Colin Maudry
2025-11-11 08:39:58 +01:00
5 changed files with 134 additions and 122 deletions
+5 -1
View File
@@ -1,6 +1,6 @@
# decp.info # decp.info
> v2.1.6 > v2.1.7
Outil d'exploration et de téléchargement des données essentielles de la commande publique. Outil d'exploration et de téléchargement des données essentielles de la commande publique.
@@ -38,6 +38,10 @@ Ne pas oublier de mettre à jour les fichier .env.
## Notes de version ## Notes de version
##### 2.1.7 (11 novembre 2025)
- Remplacement du formulaire de contact par une adresse email
##### 2.1.6 (15 octobre 2025) ##### 2.1.6 (15 octobre 2025)
- Stabilisation de la vue marché - Stabilisation de la vue marché
+1 -1
View File
@@ -1,7 +1,7 @@
[project] [project]
name = "decp.info" name = "decp.info"
description = "Interface d'exploration et d'analyse des marchés publics français." description = "Interface d'exploration et d'analyse des marchés publics français."
version = "2.1.6" version = "2.1.7"
requires-python = ">= 3.10" requires-python = ">= 3.10"
authors = [ authors = [
{ name = "Colin Maudry", email = "colin+decp@maudry.com" } { name = "Colin Maudry", email = "colin+decp@maudry.com" }
+6
View File
@@ -90,6 +90,12 @@ app.layout = html.Div(
], ],
className="logo", className="logo",
), ),
html.Div(
id="announcements",
children=html.P(
"️ Le formulaire de contact a été remplacé par une adresse email"
),
),
html.Div( html.Div(
[ [
dcc.Link( dcc.Link(
+5
View File
@@ -142,6 +142,11 @@ td[data-dash-column="objet"] {
margin: 0 0 20px 20px; margin: 0 0 20px 20px;
} }
/* Annonces */
#announcements {
margin-top: 25px;
}
/* Menu de navigation */ /* Menu de navigation */
.navbar { .navbar {
+117 -120
View File
@@ -1,11 +1,7 @@
import os
import re import re
import smtplib
import time import time
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from dash import Input, Output, State, callback, dcc, html, register_page from dash import dcc, html, register_page
from flask import request from flask import request
from src.utils import meta_content from src.utils import meta_content
@@ -25,51 +21,52 @@ layout = html.Div(
className="container", className="container",
children=[ children=[
html.H2("Contact", id="contact"), html.H2("Contact", id="contact"),
html.P( # html.P(
"Votre message arrivera directement dans ma boîte mail, et je reviendrai vers vous rapidement." # "Votre message arrivera directement dans ma boîte mail, et je reviendrai vers vous rapidement."
), # ),
html.Div( # html.Div(
[ # [
html.Label("Votre nom"), # html.Label("Votre nom"),
dcc.Input( # dcc.Input(
id="input-name", # id="input-name",
type="text", # type="text",
style={"width": "100%", "padding": "8px", "margin": "8px 0"}, # style={"width": "100%", "padding": "8px", "margin": "8px 0"},
), # ),
html.Label("Votre adresse email"), # html.Label("Votre adresse email"),
dcc.Input( # dcc.Input(
id="input-email", # id="input-email",
type="email", # type="email",
style={"width": "100%", "padding": "8px", "margin": "8px 0"}, # style={"width": "100%", "padding": "8px", "margin": "8px 0"},
), # ),
html.Label("Message"), # html.Label("Message"),
dcc.Textarea( # dcc.Textarea(
id="input-message", # id="input-message",
style={ # style={
"width": "100%", # "width": "100%",
"height": 200, # "height": 200,
"padding": "8px", # "padding": "8px",
"margin": "8px 0", # "margin": "8px 0",
}, # },
), # ),
html.Button( # html.Button(
"Envoyer ", # "Envoyer ",
id="submit-button", # id="submit-button",
n_clicks=0, # n_clicks=0,
style={"marginTop": "10px"}, # style={"marginTop": "10px"},
), # ),
html.Div( # html.Div(
id="output-message", style={"marginTop": "10px", "color": "green"} # id="output-message", style={"marginTop": "10px", "color": "green"}
), # ),
], # ],
style={ # style={
"maxWidth": "600px", # "maxWidth": "600px",
"margin": "auto", # "margin": "auto",
"padding": "20px", # "padding": "20px",
"lineHeight": "20px", # "lineHeight": "20px",
}, # },
), # ),
dcc.Markdown(""" dcc.Markdown("""
- Email : [colin+decp@maudry.com](mailto:colin+decp@maudry.com)
- Bluesky : [@col1m.bsky.social](https://bsky.app/profile/col1m.bsky.social) - Bluesky : [@col1m.bsky.social](https://bsky.app/profile/col1m.bsky.social)
- Mastodon : [col1m@mamot.fr](https://mamot.fr/@col1m) - Mastodon : [col1m@mamot.fr](https://mamot.fr/@col1m)
- LinkedIn : [colinmaudry](https://www.linkedin.com/in/colinmaudry/) - LinkedIn : [colinmaudry](https://www.linkedin.com/in/colinmaudry/)
@@ -97,74 +94,74 @@ def sanitize_email(email):
return re.match(r"[^@]+@[^@]+\.[^@]+", email) return re.match(r"[^@]+@[^@]+\.[^@]+", email)
@callback( # @callback(
Output("output-message", "children"), # Output("output-message", "children"),
Input("submit-button", "n_clicks"), # Input("submit-button", "n_clicks"),
State("input-name", "value"), # State("input-name", "value"),
State("input-email", "value"), # State("input-email", "value"),
State("input-message", "value"), # State("input-message", "value"),
prevent_initial_call=True, # prevent_initial_call=True,
) # )
def send_email(n_clicks, form_name, form_email, form_message): # def send_email(n_clicks, form_name, form_email, form_message):
if not all([form_name, form_email, form_message]): # if not all([form_name, form_email, form_message]):
return html.Div( # return html.Div(
"Veuillez s'il vous plaît remplir tous les champs.", style={"color": "red"} # "Veuillez s'il vous plaît remplir tous les champs.", style={"color": "red"}
) # )
#
client_ip = request.remote_addr # client_ip = request.remote_addr
if is_rate_limited(): # if is_rate_limited():
wait_time = int(RATE_LIMIT_WINDOW - (time.time() - rate_limit_store[client_ip])) # wait_time = int(RATE_LIMIT_WINDOW - (time.time() - rate_limit_store[client_ip]))
return html.Div( # return html.Div(
f"⏳ J'ai mis en place une protection contre le spam, et vous m'avez écrit il y a moins de 5 minutes. " # f"⏳ J'ai mis en place une protection contre le spam, et vous m'avez écrit il y a moins de 5 minutes. "
f"Veuillez s'il vous plaît attendre {wait_time} secondes avant de renvoyer un message.", # f"Veuillez s'il vous plaît attendre {wait_time} secondes avant de renvoyer un message.",
style={"color": "black"}, # style={"color": "black"},
) # )
#
try: # try:
# Configuration du serveur SMTP # # Configuration du serveur SMTP
smtp_server = os.getenv("SENDER_SERVER_DOMAIN") # smtp_server = os.getenv("SENDER_SERVER_DOMAIN")
smtp_port = 587 # smtp_port = 587
login_email = os.getenv("LOGIN_EMAIL") # login_email = os.getenv("LOGIN_EMAIL")
from_email = os.getenv("FROM_EMAIL") # from_email = os.getenv("FROM_EMAIL")
to_email = os.getenv("TO_EMAIL", from_email) # to_email = os.getenv("TO_EMAIL", from_email)
login_password = os.getenv("LOGIN_PASSWORD") # login_password = os.getenv("LOGIN_PASSWORD")
#
print( # print(
smtp_server, # smtp_server,
smtp_port, # smtp_port,
login_email, # login_email,
from_email, # from_email,
to_email, # to_email,
login_password, # login_password,
sep="\n", # sep="\n",
) # )
#
# Création de l'email # # Création de l'email
email = MIMEMultipart() # email = MIMEMultipart()
email["From"] = from_email # email["From"] = from_email
email["To"] = to_email # email["To"] = to_email
email["Subject"] = f"[decp.info] Message de {form_name}" # email["Subject"] = f"[decp.info] Message de {form_name}"
#
body = f""" # body = f"""
Nom : {form_name} # Nom : {form_name}
Email : {form_email} # Email : {form_email}
#
Message : # Message :
{form_message} # {form_message}
""" # """
email.attach(MIMEText(body, "plain")) # email.attach(MIMEText(body, "plain"))
#
# Send email # # Send email
server = smtplib.SMTP(smtp_server, smtp_port) # server = smtplib.SMTP(smtp_server, smtp_port)
server.starttls() # server.starttls()
server.login(login_email, login_password) # server.login(login_email, login_password)
server.sendmail(from_email, to_email, email.as_string()) # server.sendmail(from_email, to_email, email.as_string())
server.quit() # server.quit()
#
return html.Div("✅ Envoi réussi", style={"color": "green"}) # return html.Div("✅ Envoi réussi", style={"color": "green"})
#
except Exception as e: # except Exception as e:
print(e) # print(e)
return html.Div( # return html.Div(
f"❌ Échec de l'envoi du message : {str(e)}", style={"color": "red"} # f"❌ Échec de l'envoi du message : {str(e)}", style={"color": "red"}
) # )