Améliorations recherche
This commit is contained in:
parent
731d9bce6d
commit
ad0c34fcff
10 changed files with 317 additions and 305 deletions
|
@ -1,2 +1,5 @@
|
|||
TYPESENSE_API_KEY=
|
||||
TYPESENSE_DATA_DIR=
|
||||
TYPESENSE_API_KEY=RANDOM_STRING_HERE
|
||||
TYPESENSE_DATA_DIR=/data
|
||||
TYPESENSE_HOST=localhost
|
||||
TYPESENSE_PORT=8108
|
||||
TYPESENSE_PROTOCOL=http
|
||||
|
|
|
@ -53,8 +53,9 @@ Tu peux obtenir une sauvegarde des données de tes réseaux sociaux. Je t'ai mis
|
|||
|
||||
- Clone le projet avec Git
|
||||
- Configure ta clé API en copiant `.env.template` dans `.env` et en y mettant une clé API de ton choix
|
||||
- Configure les autres variables d'environnement dans `.env`.
|
||||
- Exécute le fichier `docker-compose.yml` avec Docker Compose pour installer le moteur de recherche TypeSense
|
||||
- Connecte-toi à l'application en lançant run_streamlit_app.py et en allant au http://localhost:8501
|
||||
- Connecte-toi à l'application en lançant run_streamlit_app.py et en allant au http://localhost:8501.
|
||||
- Si tout fonctionne, tu vas accéder à l'interface de recherche
|
||||
|
||||
## Mettre les fichiers au bon endroit
|
||||
|
|
30
import_data/10_install_nlp_models.py
Normal file
30
import_data/10_install_nlp_models.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
import spacy
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
def download_spacy_model(model_name):
|
||||
print(f"Downloading and installing spaCy model: {model_name}")
|
||||
try:
|
||||
subprocess.check_call([sys.executable, "-m", "spacy", "download", model_name])
|
||||
print(f"Successfully installed {model_name}")
|
||||
except subprocess.CalledProcessError:
|
||||
print(f"Error installing {model_name}. Please make sure you have the necessary permissions.")
|
||||
|
||||
# Download and install English model
|
||||
download_spacy_model("en_core_web_sm")
|
||||
|
||||
# Download and install French model
|
||||
download_spacy_model("fr_core_news_sm")
|
||||
|
||||
# Load the models to verify installation
|
||||
try:
|
||||
nlp_en = spacy.load("en_core_web_sm")
|
||||
print("English model loaded successfully")
|
||||
except:
|
||||
print("Error loading English model")
|
||||
|
||||
try:
|
||||
nlp_fr = spacy.load("fr_core_news_sm")
|
||||
print("French model loaded successfully")
|
||||
except:
|
||||
print("Error loading French model")
|
|
@ -1,97 +0,0 @@
|
|||
altair==5.5.0
|
||||
annotated-types==0.7.0
|
||||
attrs==24.2.0
|
||||
av==13.1.0
|
||||
beautifulsoup4==4.12.3
|
||||
blinker==1.9.0
|
||||
blis==1.0.1
|
||||
cachetools==5.5.0
|
||||
catalogue==2.0.10
|
||||
certifi==2024.8.30
|
||||
charset-normalizer==3.4.0
|
||||
click==8.1.7
|
||||
cloudpathlib==0.20.0
|
||||
coloredlogs==15.0.1
|
||||
confection==0.1.5
|
||||
contourpy==1.3.1
|
||||
ctranslate2==4.5.0
|
||||
cycler==0.12.1
|
||||
cymem==2.0.10
|
||||
en_core_web_sm @ https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.8.0/en_core_web_sm-3.8.0-py3-none-any.whl#sha256=1932429db727d4bff3deed6b34cfc05df17794f4a52eeb26cf8928f7c1a0fb85
|
||||
faster-whisper==1.1.0
|
||||
filelock==3.16.1
|
||||
flatbuffers==24.3.25
|
||||
fonttools==4.55.0
|
||||
fr_core_news_sm @ https://github.com/explosion/spacy-models/releases/download/fr_core_news_sm-3.8.0/fr_core_news_sm-3.8.0-py3-none-any.whl#sha256=7d6ad14cd5078e53147bfbf70fb9d433c6a3865b695fda2657140bbc59a27e29
|
||||
fsspec==2024.10.0
|
||||
gitdb==4.0.11
|
||||
GitPython==3.1.43
|
||||
huggingface-hub==0.26.3
|
||||
humanfriendly==10.0
|
||||
idna==3.10
|
||||
Jinja2==3.1.4
|
||||
jsonschema==4.23.0
|
||||
jsonschema-specifications==2024.10.1
|
||||
kiwisolver==1.4.7
|
||||
langcodes==3.5.0
|
||||
langdetect==1.0.9
|
||||
language_data==1.3.0
|
||||
marisa-trie==1.2.1
|
||||
markdown-it-py==3.0.0
|
||||
markdownify==0.11.6
|
||||
MarkupSafe==3.0.2
|
||||
matplotlib==3.9.3
|
||||
mdurl==0.1.2
|
||||
mpmath==1.3.0
|
||||
murmurhash==1.0.11
|
||||
narwhals==1.15.0
|
||||
numpy==2.0.2
|
||||
onnxruntime==1.20.1
|
||||
packaging==24.2
|
||||
pandas==2.2.3
|
||||
pillow==11.0.0
|
||||
plotly==5.24.1
|
||||
preshed==3.0.9
|
||||
protobuf==5.29.0
|
||||
pyarrow==17.0.0
|
||||
pydantic==2.10.2
|
||||
pydantic_core==2.27.1
|
||||
pydeck==0.9.1
|
||||
Pygments==2.18.0
|
||||
pyparsing==3.2.0
|
||||
python-dateutil==2.9.0.post0
|
||||
python-dotenv==1.0.1
|
||||
pytz==2024.2
|
||||
PyYAML==6.0.2
|
||||
referencing==0.35.1
|
||||
requests==2.31.0
|
||||
rich==13.9.4
|
||||
rpds-py==0.21.0
|
||||
setuptools==75.6.0
|
||||
shellingham==1.5.4
|
||||
six==1.16.0
|
||||
smart-open==7.0.5
|
||||
smmap==5.0.1
|
||||
soupsieve==2.6
|
||||
spacy==3.8.2
|
||||
spacy-language-detection==0.2.1
|
||||
spacy-legacy==3.0.12
|
||||
spacy-loggers==1.0.5
|
||||
srsly==2.4.8
|
||||
streamlit==1.40.2
|
||||
sympy==1.13.3
|
||||
tenacity==9.0.0
|
||||
thinc==8.3.2
|
||||
tokenizers==0.21.0
|
||||
toml==0.10.2
|
||||
tornado==6.4.2
|
||||
tqdm==4.67.1
|
||||
typer==0.14.0
|
||||
typesense==0.21.0
|
||||
typing_extensions==4.12.2
|
||||
tzdata==2024.2
|
||||
urllib3==2.2.3
|
||||
wasabi==1.1.3
|
||||
weasel==0.4.1
|
||||
wrapt==1.17.0
|
||||
xmltodict==0.13.0
|
|
@ -1,4 +1,4 @@
|
|||
import utils.config as config
|
||||
from import_data.utils import config
|
||||
|
||||
wordpress_names = config.WORDPRESS_NAMES.split(",")
|
||||
|
||||
|
@ -13,5 +13,5 @@ reseau_social_data = [{"nom": "LinkedIn",
|
|||
{"nom": "FacebookBusiness",
|
||||
"repertoires": ["posts"]},
|
||||
{"nom": "Podcast",
|
||||
"repertoires": ["shownotes", "audio"]}
|
||||
"repertoires": ["shownotes", "audio", "feeds"]}
|
||||
]
|
||||
|
|
|
@ -6,10 +6,10 @@ load_dotenv()
|
|||
|
||||
client = typesense.Client({
|
||||
'nodes': [{
|
||||
'host': 'localhost',
|
||||
'port': '8108',
|
||||
'protocol': 'http'
|
||||
'host': os.getenv('TYPESENSE_HOST','localhost'),
|
||||
'port': os.getenv('TYPESENSE_PORT','8108'),
|
||||
'protocol': os.getenv('TYPESENSE_PROTOCOL','http'),
|
||||
}],
|
||||
'api_key': os.getenv('TYPESENSE_API_KEY'),
|
||||
'connection_timeout_seconds': 2
|
||||
'connection_timeout_seconds': 10
|
||||
})
|
||||
|
|
40
search_app_ui/rechercher_documents.py
Normal file
40
search_app_ui/rechercher_documents.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
import streamlit as st
|
||||
from import_data.utils.typesense_client import client
|
||||
|
||||
def rechercher_documents(cette_requete,
|
||||
ces_filtres=None,
|
||||
facette_par=None,
|
||||
query_by="texte,embedding",
|
||||
sort_by="_text_match:desc,creation_timestamp:desc",
|
||||
nb_buckets=10,
|
||||
prefix=False,
|
||||
per_page=10,
|
||||
page=1):
|
||||
parametres_recherche = {
|
||||
'q': cette_requete,
|
||||
'query_by': query_by,
|
||||
'sort_by': sort_by.replace(
|
||||
"_text_match",
|
||||
f"_text_match(buckets: {nb_buckets})"),
|
||||
"exclude_fields": "embedding",
|
||||
"prefix": str(prefix).lower(),
|
||||
'per_page': per_page,
|
||||
'page': page
|
||||
}
|
||||
|
||||
if ces_filtres:
|
||||
parametres_recherche['filter_by'] = ces_filtres
|
||||
|
||||
if facette_par:
|
||||
parametres_recherche['facet_by'] = facette_par
|
||||
|
||||
st.write("Search parameters:", parametres_recherche)
|
||||
|
||||
all_results = []
|
||||
try:
|
||||
results = client.collections['social_media_posts'].documents.search(parametres_recherche)
|
||||
all_results.extend(results['hits'])
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
st.error(f"Error during search: {str(e)}") # Error handling
|
19
search_app_ui/recuperer_reseaux.py
Normal file
19
search_app_ui/recuperer_reseaux.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
import streamlit as st
|
||||
|
||||
from import_data.utils.typesense_client import client
|
||||
|
||||
|
||||
def recuperer_reseaux():
|
||||
search_parameters = {
|
||||
'q': '*',
|
||||
'query_by': 'network',
|
||||
'facet_by': 'network',
|
||||
'per_page': 0
|
||||
}
|
||||
try:
|
||||
results = client.collections['social_media_posts'].documents.search(search_parameters)
|
||||
networks = [facet['value'] for facet in results['facet_counts'][0]['counts']]
|
||||
return networks
|
||||
except Exception as e:
|
||||
st.error(f"Erreur lors de la récupération des réseaux : {str(e)}")
|
||||
return ['Facebook', 'Instagram', 'Threads', 'LinkedIn', 'WordPress'] # Valeurs par défaut en cas d'erreur
|
|
@ -1,134 +1,27 @@
|
|||
import streamlit as st
|
||||
import typesense
|
||||
from datetime import datetime, time
|
||||
import pandas as pd
|
||||
import plotly.express as px
|
||||
from dotenv import load_dotenv
|
||||
import os
|
||||
|
||||
from import_data.utils.typesense_client import client
|
||||
from search_app_ui.rechercher_documents import rechercher_documents
|
||||
from search_app_ui.recuperer_reseaux import recuperer_reseaux
|
||||
|
||||
# Configurer la page en mode large
|
||||
st.set_page_config(layout="wide")
|
||||
|
||||
# Forcer le thème sombre
|
||||
st.markdown("""
|
||||
<style>
|
||||
/* Fond principal */
|
||||
.stApp {
|
||||
background-color: #0e1117;
|
||||
color: #fafafa;
|
||||
}
|
||||
/* Barre latérale */
|
||||
.css-1d391kg {
|
||||
background-color: #262730;
|
||||
}
|
||||
/* Boutons */
|
||||
.stButton>button {
|
||||
color: #fafafa;
|
||||
background-color: #262730;
|
||||
border-color: #fafafa;
|
||||
}
|
||||
/* Champs de texte */
|
||||
.stTextInput>div>div>input {
|
||||
color: #fafafa;
|
||||
background-color: #262730;
|
||||
}
|
||||
/* Boîte de sélection */
|
||||
.stSelectbox>div>div>select {
|
||||
color: #fafafa;
|
||||
background-color: #262730;
|
||||
}
|
||||
/* Sélection multiple */
|
||||
.stMultiSelect>div>div>select {
|
||||
color: #fafafa;
|
||||
background-color: #262730;
|
||||
}
|
||||
/* Saisie de date */
|
||||
.stDateInput>div>div>input {
|
||||
color: #fafafa;
|
||||
background-color: #262730;
|
||||
}
|
||||
</style>
|
||||
""", unsafe_allow_html=True)
|
||||
# Load and apply the CSS
|
||||
def load_css(file_name):
|
||||
with open(file_name) as f:
|
||||
st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)
|
||||
|
||||
# Ajouter ce CSS pour créer une zone de résultats défilable
|
||||
st.markdown("""
|
||||
<style>
|
||||
.scrollable-results {
|
||||
height: 400px;
|
||||
overflow-y: scroll;
|
||||
border: 1px solid #ccc;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
</style>
|
||||
""", unsafe_allow_html=True)
|
||||
# Load the CSS file
|
||||
load_css('style.css') # If the CSS file is in a subdirectory, adjust the path accordingly
|
||||
|
||||
# Charger les variables d'environnement
|
||||
load_dotenv()
|
||||
|
||||
# Initialiser le client Typesense
|
||||
client = typesense.Client({
|
||||
'nodes': [{
|
||||
'host': 'localhost',
|
||||
'port': '8108',
|
||||
'protocol': 'http'
|
||||
}],
|
||||
'api_key': os.getenv('TYPESENSE_API_KEY'),
|
||||
'connection_timeout_seconds': 2
|
||||
})
|
||||
|
||||
|
||||
def rechercher_documents(cette_requete, ces_filtres=None, facette_par=None):
|
||||
parametres_recherche = {
|
||||
'q': cette_requete,
|
||||
'query_by': 'texte,embedding',
|
||||
'sort_by': '_text_match(buckets: 10):desc,creation_timestamp:desc',
|
||||
"exclude_fields": "embedding",
|
||||
"prefix": "false",
|
||||
'per_page': 10,
|
||||
'page': 1
|
||||
}
|
||||
|
||||
if ces_filtres:
|
||||
parametres_recherche['filter_by'] = ces_filtres
|
||||
|
||||
if facette_par:
|
||||
parametres_recherche['facet_by'] = facette_par
|
||||
|
||||
st.write("Search parameters:", parametres_recherche)
|
||||
|
||||
all_results = []
|
||||
try:
|
||||
while True:
|
||||
results = client.collections['social_media_posts'].documents.search(parametres_recherche)
|
||||
all_results.extend(results['hits'])
|
||||
if len(all_results) >= results['found']:
|
||||
break
|
||||
parametres_recherche['page'] += 1
|
||||
results['hits'] = all_results
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
st.error(f"Error during search: {str(e)}") # Error handling
|
||||
|
||||
|
||||
# Récupérer dynamiquement les réseaux depuis Typesense
|
||||
def get_networks():
|
||||
search_parameters = {
|
||||
'q': '*',
|
||||
'query_by': 'network',
|
||||
'facet_by': 'network',
|
||||
'per_page': 0
|
||||
}
|
||||
try:
|
||||
results = client.collections['social_media_posts'].documents.search(search_parameters)
|
||||
networks = [facet['value'] for facet in results['facet_counts'][0]['counts']]
|
||||
return networks
|
||||
except Exception as e:
|
||||
st.error(f"Erreur lors de la récupération des réseaux : {str(e)}")
|
||||
return ['Facebook', 'Instagram', 'Threads', 'LinkedIn', 'WordPress'] # Valeurs par défaut en cas d'erreur
|
||||
|
||||
|
||||
# Interface utilisateur Streamlit
|
||||
st.title('Recherche dans tes contenus publiés sur le web')
|
||||
|
||||
|
@ -147,7 +40,7 @@ date_fin = col2.date_input('Date de fin', value=datetime.now())
|
|||
# Filtre de réseau social et de langue
|
||||
|
||||
col3, col4 = st.columns(2)
|
||||
reseaux = get_networks()
|
||||
reseaux = recuperer_reseaux()
|
||||
reseaux_selectionnes = col3.multiselect('Sélectionnez les réseaux sociaux', reseaux,
|
||||
default=reseaux[0] if reseaux else None)
|
||||
langues = [('fr', 'Français'), ('en', 'English')]
|
||||
|
@ -156,105 +49,187 @@ langue_selectionnees = col4.multiselect('Sélectionnez la langue',
|
|||
format_func=lambda x: x,
|
||||
default='Français')
|
||||
|
||||
# Filtre sur le nombre de mots
|
||||
nombre_mots = st.slider('Nombre de mots minimum', min_value=0, max_value=1000, value=100, step=10)
|
||||
|
||||
# Convertir les étiquettes en codes de langage
|
||||
selected_lang_codes = [code for code, label in langues if label in langue_selectionnees]
|
||||
|
||||
# Filtre sur le nombre de mots
|
||||
nombre_mots = st.slider('Nombre de mots minimum', min_value=0, max_value=1000, value=100, step=10)
|
||||
# Nouvelle section pour les options de recherche avancées
|
||||
st.sidebar.header("Options de recherche avancées")
|
||||
|
||||
# Option pour activer/désactiver les options avancées
|
||||
show_advanced_options = st.sidebar.checkbox("Activer les options avancées")
|
||||
|
||||
if show_advanced_options:
|
||||
# Champs de recherche
|
||||
query_by = st.sidebar.multiselect(
|
||||
"Champs de recherche",
|
||||
["texte", "embedding"],
|
||||
default=["texte", "embedding"]
|
||||
)
|
||||
|
||||
# Tri
|
||||
sort_options = [
|
||||
"_text_match:desc", "creation_timestamp:desc",
|
||||
"_text_match:asc", "creation_timestamp:asc",
|
||||
]
|
||||
nb_buckets = st.sidebar.number_input("Nombre de buckets pour le tri", min_value=1, value=10)
|
||||
sort_by = st.sidebar.multiselect(
|
||||
"Trier par",
|
||||
sort_options,
|
||||
default=["_text_match:desc", "creation_timestamp:desc"]
|
||||
)
|
||||
|
||||
# Préfixe
|
||||
prefix = st.sidebar.checkbox("Activer la recherche par préfixe", value=False)
|
||||
|
||||
# Pagination
|
||||
per_page = st.sidebar.slider("Résultats par page", min_value=1, max_value=100, value=10)
|
||||
page = st.sidebar.number_input("Page", min_value=1, value=1)
|
||||
|
||||
else:
|
||||
# Valeurs par défaut si les options avancées ne sont pas affichées
|
||||
query_by = ["texte", "embedding"]
|
||||
sort_by = ["_text_match:desc", "creation_timestamp:desc"]
|
||||
prefix = False
|
||||
nb_buckets = 10
|
||||
per_page = 10
|
||||
page = 1
|
||||
|
||||
if st.button('Rechercher'):
|
||||
# Préparer les filtres
|
||||
debut_datetime = datetime.combine(date_debut, time.min)
|
||||
fin_datetime = datetime.combine(date_fin, time.max)
|
||||
filtre_date = f"creation_timestamp:[{int(debut_datetime.timestamp())}..{int(fin_datetime.timestamp())}]"
|
||||
filtre_reseau = f"network:[{' '.join(reseaux_selectionnes)}]" if reseaux_selectionnes else None
|
||||
filtre_langue = f"langue:[{' '.join(selected_lang_codes)}]" if selected_lang_codes else None
|
||||
filtre_reseau = f"network:=[{', '.join(reseaux_selectionnes)}]" if reseaux_selectionnes else None
|
||||
filtre_langue = f"langue:=[{', '.join(selected_lang_codes)}]" if selected_lang_codes else None
|
||||
filtre_mots = f"nombre_de_mots:[{nombre_mots}..10000]" if nombre_mots > 0 else None
|
||||
|
||||
filtres = ' && '.join(filter(None, [filtre_date, filtre_reseau, filtre_langue, filtre_mots]))
|
||||
liste_filtres = []
|
||||
if filtre_date:
|
||||
liste_filtres.append(filtre_date)
|
||||
if filtre_reseau:
|
||||
liste_filtres.append(filtre_reseau)
|
||||
if filtre_langue:
|
||||
liste_filtres.append(filtre_langue)
|
||||
if filtre_mots:
|
||||
liste_filtres.append(filtre_mots)
|
||||
|
||||
filtres = ' && '.join(liste_filtres) if liste_filtres else None
|
||||
# Effectuer la recherche pour tous les résultats
|
||||
tous_resultats = rechercher_documents(requete, ces_filtres=filtres, facette_par='network')
|
||||
nombre_total_resultats = tous_resultats['found']
|
||||
tous_resultats = rechercher_documents(
|
||||
requete,
|
||||
ces_filtres=filtres,
|
||||
facette_par='network',
|
||||
query_by=','.join(query_by),
|
||||
sort_by=','.join(sort_by),
|
||||
nb_buckets=nb_buckets,
|
||||
prefix=prefix,
|
||||
per_page=per_page,
|
||||
page=page
|
||||
)
|
||||
if tous_resultats:
|
||||
nombre_total_resultats = tous_resultats['found']
|
||||
# Afficher le nombre total de résultats
|
||||
st.subheader(f"Trouvé {nombre_total_resultats} résultats parmi {total_documents } documents indexés")
|
||||
else:
|
||||
nombre_total_resultats = 0
|
||||
st.subheader("Aucun résultat trouvé")
|
||||
|
||||
# Afficher le nombre total de résultats
|
||||
st.subheader(f"Trouvé {nombre_total_resultats} résultats parmi {total_documents } documents indexés")
|
||||
|
||||
# Affichage des résultats (100 maximum)
|
||||
st.subheader("Résultats de la recherche")
|
||||
|
||||
for hit in tous_resultats['hits'][:100]: # Limite à 100 résultats
|
||||
col1, col2 = st.columns([1, 4])
|
||||
|
||||
with col1:
|
||||
st.markdown(f"**{hit['document']['network']}**")
|
||||
st.markdown(
|
||||
f"**{datetime.fromtimestamp(hit['document']['creation_timestamp']).strftime('%Y-%m-%d %H:%M:%S')}**")
|
||||
st.markdown(f"**{hit['document']['nombre_de_mots']} mots**")
|
||||
# Score
|
||||
st.markdown(f"**Score: {hit["hybrid_search_info"]['rank_fusion_score']}**")
|
||||
# Étiquettes de couleur pour les facettes
|
||||
st.markdown(f"""
|
||||
<span style="background-color: #007bff; color: white; padding: 2px 6px; border-radius: 10px;">
|
||||
{hit['document']['langue']}
|
||||
</span>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
with col2:
|
||||
# Boîte de texte pour le contenu
|
||||
st.text_area("Contenu", hit['document']['texte'], height=150)
|
||||
|
||||
# URI en dessous
|
||||
if 'uri' in hit['document']:
|
||||
st.markdown(f"[Lien vers le post original]({hit['document']['uri']})")
|
||||
|
||||
st.markdown("---")
|
||||
|
||||
# Afficher les facettes
|
||||
if 'facet_counts' in tous_resultats:
|
||||
facettes_reseau = {facette['value']: facette['count'] for facette in
|
||||
tous_resultats['facet_counts'][0]['counts']}
|
||||
st.subheader("Résultats par Réseau")
|
||||
|
||||
# Graphique en camembert pour montrer la distribution des résultats par réseau social
|
||||
fig = px.pie(values=list(facettes_reseau.values()), names=list(facettes_reseau.keys()),
|
||||
title="Distribution par Réseau")
|
||||
# Ce graphique montre la proportion de résultats pour chaque réseau social
|
||||
st.plotly_chart(fig)
|
||||
|
||||
# Distribution temporelle par réseau et par mois
|
||||
if nombre_total_resultats > 0:
|
||||
st.subheader("Résultats au fil du temps par réseau (agrégation mensuelle)")
|
||||
# Affichage des résultats (100 maximum)
|
||||
st.subheader("Résultats de la recherche")
|
||||
|
||||
df_temporel = pd.DataFrame({
|
||||
'date': [datetime.fromtimestamp(hit['document']['creation_timestamp']) for hit in tous_resultats['hits']],
|
||||
'network': [hit['document']['network'] for hit in tous_resultats['hits']]
|
||||
})
|
||||
for hit in tous_resultats['hits'][:100]: # Limite à 100 résultats
|
||||
col1, col2 = st.columns([1, 4])
|
||||
|
||||
df_temporel['mois'] = df_temporel['date'].dt.to_period('M')
|
||||
df_temporel = df_temporel.groupby(['mois', 'network']).size().reset_index(name='count')
|
||||
df_temporel['mois'] = df_temporel['mois'].dt.to_timestamp()
|
||||
with col1:
|
||||
st.markdown(f"**{hit['document']['network']}**")
|
||||
st.markdown(
|
||||
f"**{datetime.fromtimestamp(hit['document']['creation_timestamp']).strftime('%Y-%m-%d %H:%M:%S')}**")
|
||||
st.markdown(f"**{hit['document']['nombre_de_mots']} mots**")
|
||||
# Score
|
||||
if "texte" in query_by and "embedding" in query_by:
|
||||
score = hit["hybrid_search_info"]['rank_fusion_score']
|
||||
score_type = "Hybrid"
|
||||
elif "texte" in query_by:
|
||||
score = hit["text_match_info"]['score']
|
||||
score_type = "Text"
|
||||
elif "embedding" in query_by:
|
||||
score = hit["vector_distance"]
|
||||
score_type = "Vector"
|
||||
else:
|
||||
score = "N/A"
|
||||
score_type = "Unknown"
|
||||
|
||||
# Graphique linéaire pour montrer l'évolution du nombre de posts par réseau au fil du temps
|
||||
fig = px.line(df_temporel, x='mois', y='count', color='network',
|
||||
title="Distribution temporelle par réseau (agrégation mensuelle)")
|
||||
fig.update_layout(xaxis_title="Mois", yaxis_title="Nombre de posts")
|
||||
fig.update_xaxes(tickformat="%B %Y")
|
||||
# Ce graphique permet de visualiser les tendances de publication pour chaque réseau social au fil des mois
|
||||
st.plotly_chart(fig)
|
||||
st.markdown(f"**{score_type} Score: {score}**")
|
||||
# Étiquettes de couleur pour les facettes
|
||||
st.markdown(f"""
|
||||
<span style="background-color: #007bff; color: white; padding: 2px 6px; border-radius: 10px;">
|
||||
{hit['document']['langue']}
|
||||
</span>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# Graphique à barres empilées pour montrer la répartition des posts par réseau pour chaque mois
|
||||
fig_bar = px.bar(df_temporel, x='mois', y='count', color='network',
|
||||
title="Distribution temporelle par réseau (barres empilées, agrégation mensuelle)")
|
||||
fig_bar.update_layout(xaxis_title="Mois", yaxis_title="Nombre de posts")
|
||||
fig_bar.update_xaxes(tickformat="%B %Y")
|
||||
# Ce graphique permet de comparer facilement le volume de posts entre les différents réseaux sociaux pour chaque mois
|
||||
st.plotly_chart(fig_bar)
|
||||
with col2:
|
||||
# Boîte de texte pour le contenu
|
||||
st.text_area("Contenu", hit['document']['texte'], height=150)
|
||||
|
||||
st.subheader("Tableau récapitulatif mensuel")
|
||||
df_pivot = df_temporel.pivot(index='mois', columns='network', values='count').fillna(0)
|
||||
df_pivot['Total'] = df_pivot.sum(axis=1)
|
||||
df_pivot = df_pivot.reset_index()
|
||||
df_pivot['mois'] = df_pivot['mois'].dt.strftime('%B %Y')
|
||||
# Ce tableau fournit un résumé détaillé du nombre de posts par réseau social pour chaque mois
|
||||
st.dataframe(df_pivot)
|
||||
# URI en dessous
|
||||
if 'uri' in hit['document']:
|
||||
st.markdown(f"[Lien vers le post original]({hit['document']['uri']})")
|
||||
# Affichage du hit brut
|
||||
st.subheader("Données brutes")
|
||||
st.json(hit, expanded=False)
|
||||
|
||||
st.markdown("---")
|
||||
|
||||
# Afficher les facettes
|
||||
if 'facet_counts' in tous_resultats:
|
||||
facettes_reseau = {facette['value']: facette['count'] for facette in
|
||||
tous_resultats['facet_counts'][0]['counts']}
|
||||
st.subheader("Résultats par Réseau")
|
||||
|
||||
# Graphique en camembert pour montrer la distribution des résultats par réseau social
|
||||
fig = px.pie(values=list(facettes_reseau.values()), names=list(facettes_reseau.keys()),
|
||||
title="Distribution par Réseau")
|
||||
# Ce graphique montre la proportion de résultats pour chaque réseau social
|
||||
st.plotly_chart(fig)
|
||||
|
||||
# Distribution temporelle par réseau et par mois
|
||||
if nombre_total_resultats > 0:
|
||||
st.subheader("Résultats au fil du temps par réseau (agrégation mensuelle)")
|
||||
|
||||
df_temporel = pd.DataFrame({
|
||||
'date': [datetime.fromtimestamp(hit['document']['creation_timestamp']) for hit in tous_resultats['hits']],
|
||||
'network': [hit['document']['network'] for hit in tous_resultats['hits']]
|
||||
})
|
||||
|
||||
df_temporel['mois'] = df_temporel['date'].dt.to_period('M')
|
||||
df_temporel = df_temporel.groupby(['mois', 'network']).size().reset_index(name='count')
|
||||
df_temporel['mois'] = df_temporel['mois'].dt.to_timestamp()
|
||||
|
||||
# Graphique linéaire pour montrer l'évolution du nombre de posts par réseau au fil du temps
|
||||
fig = px.line(df_temporel, x='mois', y='count', color='network',
|
||||
title="Distribution temporelle par réseau (agrégation mensuelle)")
|
||||
fig.update_layout(xaxis_title="Mois", yaxis_title="Nombre de posts")
|
||||
fig.update_xaxes(tickformat="%B %Y")
|
||||
# Ce graphique permet de visualiser les tendances de publication pour chaque réseau social au fil des mois
|
||||
st.plotly_chart(fig)
|
||||
|
||||
# Graphique à barres empilées pour montrer la répartition des posts par réseau pour chaque mois
|
||||
fig_bar = px.bar(df_temporel, x='mois', y='count', color='network',
|
||||
title="Distribution temporelle par réseau (barres empilées, agrégation mensuelle)")
|
||||
fig_bar.update_layout(xaxis_title="Mois", yaxis_title="Nombre de posts")
|
||||
fig_bar.update_xaxes(tickformat="%B %Y")
|
||||
# Ce graphique permet de comparer facilement le volume de posts entre les différents réseaux sociaux pour chaque mois
|
||||
st.plotly_chart(fig_bar)
|
||||
|
||||
st.subheader("Tableau récapitulatif mensuel")
|
||||
df_pivot = df_temporel.pivot(index='mois', columns='network', values='count').fillna(0)
|
||||
df_pivot['Total'] = df_pivot.sum(axis=1)
|
||||
df_pivot = df_pivot.reset_index()
|
||||
df_pivot['mois'] = df_pivot['mois'].dt.strftime('%B %Y')
|
||||
# Ce tableau fournit un résumé détaillé du nombre de posts par réseau social pour chaque mois
|
||||
st.dataframe(df_pivot)
|
||||
|
|
41
search_app_ui/style.css
Normal file
41
search_app_ui/style.css
Normal file
|
@ -0,0 +1,41 @@
|
|||
/* Fond principal */
|
||||
.stApp {
|
||||
background-color: #0e1117;
|
||||
color: #fafafa;
|
||||
}
|
||||
|
||||
/* Barre latérale */
|
||||
.css-1d391kg {
|
||||
background-color: #262730;
|
||||
}
|
||||
|
||||
/* Boutons */
|
||||
.stButton>button {
|
||||
color: #fafafa;
|
||||
background-color: #262730;
|
||||
border-color: #fafafa;
|
||||
}
|
||||
|
||||
/* Champs de texte */
|
||||
.stTextInput>div>div>input {
|
||||
color: #fafafa;
|
||||
background-color: #262730;
|
||||
}
|
||||
|
||||
/* Boîte de sélection */
|
||||
.stSelectbox>div>div>select {
|
||||
color: #fafafa;
|
||||
background-color: #262730;
|
||||
}
|
||||
|
||||
/* Sélection multiple */
|
||||
.stMultiSelect>div>div>select {
|
||||
color: #fafafa;
|
||||
background-color: #262730;
|
||||
}
|
||||
|
||||
/* Saisie de date */
|
||||
.stDateInput>div>div>input {
|
||||
color: #fafafa;
|
||||
background-color: #262730;
|
||||
}
|
Loading…
Add table
Reference in a new issue