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 # Configurer la page en mode large st.set_page_config(layout="wide") # Forcer le thème sombre st.markdown(""" """, unsafe_allow_html=True) # Ajouter ce CSS pour créer une zone de résultats défilable st.markdown(""" """, unsafe_allow_html=True) # 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') # Indiquer le nombre total de documents indexés dans Typesense collections = client.collections.retrieve() total_documents = sum(collection['num_documents'] for collection in collections) # Champ de recherche requete = st.text_input('Entrez votre requête de recherche') # Filtre de plage de dates col1, col2 = st.columns(2) date_debut = col1.date_input('Date de début', value=datetime.now() - pd.DateOffset(years=1)) 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_selectionnes = col3.multiselect('Sélectionnez les réseaux sociaux', reseaux, default=reseaux[0] if reseaux else None) langues = [('fr', 'Français'), ('en', 'English')] langue_selectionnees = col4.multiselect('Sélectionnez la langue', options=[label for code, label in langues], format_func=lambda x: x, default='Français') # 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) 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_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])) # 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'] # 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""" {hit['document']['langue']} """, 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)") 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)