Spaces:
Build error
Build error
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,531 +1,373 @@
|
|
| 1 |
-
|
| 2 |
-
from chat_client import chat
|
| 3 |
-
from google_function import leggi_gmail
|
| 4 |
-
from google_function import scrivi_bozza_gmail
|
| 5 |
-
from google_function import leggi_calendario_google
|
| 6 |
-
from google_function import connetti_google
|
| 7 |
-
from google_function import crea_documento_google
|
| 8 |
-
import time
|
| 9 |
-
import os
|
| 10 |
-
from dotenv import load_dotenv
|
| 11 |
-
from sentence_transformers import SentenceTransformer
|
| 12 |
-
import requests
|
| 13 |
-
from langchain_community.vectorstores import Chroma
|
| 14 |
-
from langchain_community.embeddings import HuggingFaceEmbeddings
|
| 15 |
-
import json
|
| 16 |
-
from googlesearch import search
|
| 17 |
from bs4 import BeautifulSoup
|
| 18 |
-
import
|
| 19 |
-
import
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
import
|
| 23 |
-
|
| 24 |
-
import
|
| 25 |
-
from openai import OpenAI
|
| 26 |
-
|
| 27 |
-
load_dotenv()
|
| 28 |
-
EFFETTUA_LOGIN_GOOGLE = os.getenv('EFFETTUA_LOGIN_GOOGLE')=="1"
|
| 29 |
-
URL_APP_SCRIPT = os.getenv('URL_APP_SCRIPT')
|
| 30 |
-
URL_PROMPT = URL_APP_SCRIPT + '?IdFoglio=1cLw9q70BsPmxMBj9PIzgXtq6sm3X-GVBVnOB5wE8jr8'
|
| 31 |
-
URL_DOCUMENTI = URL_APP_SCRIPT + '?IdSecondoFoglio=1cLw9q70BsPmxMBj9PIzgXtq6sm3X-GVBVnOB5wE8jr8'
|
| 32 |
-
SYSTEM_PROMPT = ["Sei BonsiAI e mi aiuterai nelle mie richieste (Parla in ITALIANO)", "Esatto, sono BonsiAI. Di cosa hai bisogno?"]
|
| 33 |
-
CHAT_BOTS = {
|
| 34 |
-
"Mixtral 8x7B v0.1": {
|
| 35 |
-
"model": "mistralai/Mixtral-8x7B-Instruct-v0.1",
|
| 36 |
-
"description": "Un modello avanzato di chatbot con architettura 8x7B sviluppato da Mistral AI. Supporta fino a 30 pagine di input e costa zero",
|
| 37 |
-
"pagine_contesto": 30,
|
| 38 |
-
"richiede_api_key": False
|
| 39 |
-
},
|
| 40 |
-
"Mistral 7B v0.2": {
|
| 41 |
-
"model": "mistralai/Mistral-7B-Instruct-v0.2",
|
| 42 |
-
"description": "Una versione più leggera del modello Mistral, con architettura 7B, sviluppato da Mistral AI. Supporta fino a 8 pagine di input e costa zero",
|
| 43 |
-
"pagine_contesto": 8,
|
| 44 |
-
"richiede_api_key": False
|
| 45 |
-
},
|
| 46 |
-
"Gpt 3.5 Turbo": {
|
| 47 |
-
"model": "gpt-3.5-turbo",
|
| 48 |
-
"description": "Una versione ottimizzata e performante del modello GPT-3.5 di OpenAI. Supporta 16 Pagine di input e costa 2$ ogni 1000 Pagine",
|
| 49 |
-
"pagine_contesto": 16,
|
| 50 |
-
"richiede_api_key": True
|
| 51 |
-
},
|
| 52 |
-
"Gpt 4 Turbo": {
|
| 53 |
-
"model": "gpt-4-turbo",
|
| 54 |
-
"description": "Una versione avanzata e potenziata del famoso modello GPT-4 di OpenAI, ottimizzata per prestazioni superiori. Supporta fino a 120 Pagine di input e costa 30$ ogni 1000 Pagine",
|
| 55 |
-
"pagine_contesto": 120,
|
| 56 |
-
"richiede_api_key": True
|
| 57 |
-
},
|
| 58 |
-
"Gpt 4-o": {
|
| 59 |
-
"model": "gpt-4o",
|
| 60 |
-
"description": "L'ultimissima versione di GPT! Supporta fino a 120 Pagine di input e costa 20$ ogni 1000 Pagine (meno di GPT 4 Turbo)",
|
| 61 |
-
"pagine_contesto": 120,
|
| 62 |
-
"richiede_api_key": True
|
| 63 |
-
}
|
| 64 |
-
}
|
| 65 |
-
option_personalizzata = {'Personalizzata': {'systemRole': 'Tu sei BONSI AI, il mio assistente personale della scuola superiore del Bonsignori. Aiutami in base alle mie esigenze',
|
| 66 |
-
'systemStyle': 'Firmati sempre come BONSI AI. (scrivi in italiano)',
|
| 67 |
-
'instruction': '',
|
| 68 |
-
'tipo': '',
|
| 69 |
-
'RAG': False}
|
| 70 |
-
}
|
| 71 |
-
option_leggiemail = {'Leggi Gmail': {'systemRole': 'Tu sei BONSI AI, il mio assistente personale della scuola superiore del Bonsignori. Effettua l operazione richiesta sulla base delle seguenti email: ',
|
| 72 |
-
'systemStyle': 'Firmati sempre come BONSI AI. (scrivi in italiano)',
|
| 73 |
-
'instruction': '',
|
| 74 |
-
'tipo': 'EMAIL',
|
| 75 |
-
'RAG': False}
|
| 76 |
-
}
|
| 77 |
-
option_leggicalendar = {'Leggi Calendar': {'systemRole': 'Tu sei BONSI AI, il mio assistente personale della scuola superiore del Bonsignori. Effettua l operazione richiesta sulla base dei seguenti eventi di calendario: ',
|
| 78 |
-
'systemStyle': 'Firmati sempre come BONSI AI. (scrivi in italiano)',
|
| 79 |
-
'instruction': '',
|
| 80 |
-
'tipo': 'CALENDAR',
|
| 81 |
-
'RAG': False}
|
| 82 |
-
}
|
| 83 |
-
|
| 84 |
-
# ----------------------------------------------------------- Interfaccia --------------------------------------------------------------------
|
| 85 |
-
st.set_page_config(page_title="Bonsi A.I.", page_icon="🏫")
|
| 86 |
-
|
| 87 |
-
def init_state() :
|
| 88 |
-
if "messages" not in st.session_state:
|
| 89 |
-
st.session_state.messages = []
|
| 90 |
-
|
| 91 |
-
if "temp" not in st.session_state:
|
| 92 |
-
st.session_state.temp = 0.8
|
| 93 |
-
|
| 94 |
-
if "history" not in st.session_state:
|
| 95 |
-
st.session_state.history = [SYSTEM_PROMPT]
|
| 96 |
-
|
| 97 |
-
if "top_k" not in st.session_state:
|
| 98 |
-
st.session_state.top_k = 5
|
| 99 |
-
|
| 100 |
-
if "repetion_penalty" not in st.session_state :
|
| 101 |
-
st.session_state.repetion_penalty = 1
|
| 102 |
-
|
| 103 |
-
if "api_key" not in st.session_state :
|
| 104 |
-
st.session_state.api_key = ""
|
| 105 |
|
| 106 |
-
|
| 107 |
-
st.session_state.chat_bot = "Mixtral 8x7B v0.1"
|
| 108 |
|
| 109 |
-
|
| 110 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
st.session_state.numero_generazioni = 1
|
| 129 |
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
if "urls" not in st.session_state:
|
| 149 |
-
st.session_state.urls = [""] * 5
|
| 150 |
-
|
| 151 |
-
if "creds" not in st.session_state:
|
| 152 |
-
st.session_state.creds = None
|
| 153 |
|
| 154 |
-
|
| 155 |
-
|
| 156 |
|
| 157 |
-
|
| 158 |
-
|
|
|
|
|
|
|
|
|
|
| 159 |
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 207 |
try:
|
| 208 |
-
|
| 209 |
-
|
|
|
|
|
|
|
|
|
|
| 210 |
except:
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
def open_new_tab(url):
|
| 215 |
-
placeholder = st.empty()
|
| 216 |
-
with placeholder:
|
| 217 |
-
placeholder.empty()
|
| 218 |
-
new_tab_js = f'''<script type="text/javascript">window.open("{url}", "_blank");</script>'''
|
| 219 |
-
st.components.v1.html(new_tab_js, height=1)
|
| 220 |
-
time.sleep(0.3)
|
| 221 |
-
placeholder.empty()
|
| 222 |
-
|
| 223 |
-
def esporta_testo(tipo, ultimo_messaggio):
|
| 224 |
-
testo = st.session_state.ultimo_messaggio if ultimo_messaggio else st.session_state.tutti_messaggi
|
| 225 |
-
if tipo == 'Bozza Email':
|
| 226 |
-
url = scrivi_bozza_gmail(testo)
|
| 227 |
-
open_new_tab(url)
|
| 228 |
-
if tipo == 'Google Documenti':
|
| 229 |
-
url = crea_documento_google(testo)
|
| 230 |
-
open_new_tab(url)
|
| 231 |
-
|
| 232 |
-
def sidebar():
|
| 233 |
-
def retrieval_settings() :
|
| 234 |
-
st.markdown("# Impostazioni Prompt")
|
| 235 |
-
st.session_state.selected_option_key = st.selectbox('Azione', list(st.session_state.options.keys()))
|
| 236 |
-
st.session_state.selected_option = st.session_state.options.get(st.session_state.selected_option_key, {})
|
| 237 |
|
| 238 |
-
if st.session_state.options.get(st.session_state.selected_option_key, {})["tipo"]=='DOCUMENTO':
|
| 239 |
-
st.session_state.selected_documento_key = st.selectbox('Documento', list(st.session_state.documenti.keys()))
|
| 240 |
-
st.session_state.selected_documento = st.session_state.documenti.get(st.session_state.selected_documento_key, {})
|
| 241 |
-
st.session_state.instruction = st.session_state.selected_documento.get('instruction', '')['Testo']
|
| 242 |
-
else:
|
| 243 |
-
st.session_state.instruction = st.session_state.selected_option.get('instruction', '')
|
| 244 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 245 |
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
if st.session_state.selected_option_key == 'Decreti':
|
| 261 |
-
st.session_state.top_k = st.slider(label="Documenti da ricercare", min_value=1, max_value=20, value=4, disabled=not st.session_state.rag_enabled)
|
| 262 |
-
st.session_state.decreti_escludere = st.multiselect(
|
| 263 |
-
'Decreti da escludere',
|
| 264 |
-
['23.10.2 destinazione risorse residue pnrr DGR 1051-2023_Destinazione risorse PNRR Duale.pdf', '23.10.25 accompagnatoria Circolare Inail assicurazione.pdf', '23.10.26 circolare Inail assicurazione.pdf', '23.10.3 FAQ in attesa di avviso_.pdf', '23.11.2 avviso 24_24 Decreto 17106-2023 Approvazione Avviso IeFP 2023-2024.pdf', '23.5.15 decreto linee inclusione x enti locali.pdf', '23.6.21 Circolare+esplicativa+DGR+312-2023.pdf', '23.7.3 1° Decreto R.L. 23_24 .pdf', '23.9 Regolamento_prevenzione_bullismo_e_cyberbullismo__Centro_Bonsignori.pdf', '23.9.1 FAQ inizio anno formativo.pdf', '23.9.15 DECRETO VERIFICHE AMMINISTR 15-09-23.pdf', '23.9.4 modifica decreto GRS.pdf', '23.9.8 Budget 23_24.pdf', '24.10.2022 DECRETO loghi N.15176.pdf', 'ALLEGATO C_Scheda Supporti al funzionamento.pdf', 'ALLEGATO_ B_ Linee Guida.pdf', 'ALLEGATO_A1_PEI_INFANZIA.pdf', 'ALLEGATO_A2_PEI_PRIMARIA.pdf', 'ALLEGATO_A3_PEI_SEC_1_GRADO.pdf', 'ALLEGATO_A4_PEI_SEC_2_GRADO.pdf', 'ALLEGATO_C_1_Tabella_Fabbisogni.pdf', 'Brand+Guidelines+FSE+.pdf', 'Decreto 20797 del 22-12-2023_Aggiornamento budget PNRR.pdf', 'Decreto 20874 del 29-12-2023 Avviso IeFP PNRR 2023-2024_file unico.pdf'],
|
| 265 |
-
[])
|
| 266 |
-
st.session_state.uploaded_files = st.file_uploader("Importa file", accept_multiple_files=True)
|
| 267 |
-
st.session_state.testo_documenti = ''
|
| 268 |
-
for uploaded_file in st.session_state.uploaded_files:
|
| 269 |
-
text_doc = read_text_from_file(uploaded_file)
|
| 270 |
-
st.session_state.testo_documenti += text_doc
|
| 271 |
-
print(st.session_state.testo_documenti)
|
| 272 |
-
st.markdown("---")
|
| 273 |
-
st.markdown("# Ricerca Online")
|
| 274 |
-
st.session_state.cerca_online = st.toggle("Attivata", value=False)
|
| 275 |
-
with st.popover("Siti Specifici", disabled=not st.session_state.cerca_online,use_container_width=True):
|
| 276 |
-
st.markdown("#### Inserisci Siti Web ")
|
| 277 |
-
for i in range(5):
|
| 278 |
-
st.session_state.urls[i] = st.text_input(f"URL Sito {i+1}", placeholder='Sito Web...', help='è possibile specificare anche il link di un video Youtube, in tal caso verrà restituita la trascrizione del video')
|
| 279 |
-
st.session_state.selected_tbs = st.selectbox("Periodo:", list(st.session_state.tbs_options.keys()), disabled=(not st.session_state.cerca_online) or (st.session_state.urls[0]!=""))
|
| 280 |
-
st.session_state.tbs_value = st.session_state.tbs_options[st.session_state.selected_tbs]
|
| 281 |
-
st.session_state.numero_siti = st.slider(label="Risultati", min_value = 1, max_value=20, value=3, disabled=(not st.session_state.cerca_online) or (st.session_state.urls[0]!=""))
|
| 282 |
-
#st.session_state.suddividi_ricerca = st.toggle("Attivata", value=False)
|
| 283 |
-
st.markdown("---")
|
| 284 |
-
|
| 285 |
-
def model_settings():
|
| 286 |
-
st.markdown("# Modello")
|
| 287 |
-
st.session_state.chat_bot = st.sidebar.selectbox('Tipo', list(CHAT_BOTS.keys()))
|
| 288 |
-
if CHAT_BOTS[st.session_state.chat_bot]["richiede_api_key"] == True:
|
| 289 |
-
st.session_state.api_key = st.text_input('Api Key', type = 'password', label_visibility='collapsed', placeholder='Inserisci la chiave API')
|
| 290 |
-
st.session_state.client = OpenAI(api_key=st.session_state.api_key)
|
| 291 |
-
print('xxxxxxx')
|
| 292 |
-
st.write(CHAT_BOTS[st.session_state.chat_bot]["description"])
|
| 293 |
-
st.session_state.split = st.slider(label="Pagine Suddivisione", min_value=1, max_value=CHAT_BOTS[st.session_state.chat_bot]["pagine_contesto"], value=CHAT_BOTS[st.session_state.chat_bot]["pagine_contesto"], help='Se il documento ha 100 pagine e suddivido per 20 pagine elaborerà la risposta 5 volte. Più alto è il numero e meno volte elaborerà ma la risposta sarà più imprecisa')
|
| 294 |
-
st.session_state.numero_generazioni = st.slider(label="Generazioni", min_value = 1, max_value=10, value=1)
|
| 295 |
-
st.session_state.enable_history = st.toggle("Storico Messaggi", value=True)
|
| 296 |
-
st.session_state.temp = st.slider(label="Creatività", min_value=0.0, max_value=1.0, step=0.1, value=0.9)
|
| 297 |
-
st.session_state.max_tokens = st.slider(label="Lunghezza Output", min_value = 2, max_value=4096, step= 32, value=1024)
|
| 298 |
-
st.markdown("---")
|
| 299 |
-
|
| 300 |
-
def export_settings():
|
| 301 |
-
st.markdown("# Esportazione")
|
| 302 |
-
st.session_state.export_type = st.selectbox('Tipologia', ('Non Esportare', 'Google Documenti', 'Bozza Email'), help='Seleziona la tipologia di esportazione del testo generato')
|
| 303 |
-
st.session_state.export_all = st.toggle("Considera tutte le chat", value=False)
|
| 304 |
-
if st.button("Esporta", type="primary", use_container_width=True):
|
| 305 |
-
esporta_testo(st.session_state.export_type, st.session_state.export_all)
|
| 306 |
-
st.markdown("---")
|
| 307 |
-
|
| 308 |
-
with st.sidebar:
|
| 309 |
-
retrieval_settings()
|
| 310 |
-
model_settings()
|
| 311 |
-
if EFFETTUA_LOGIN_GOOGLE:
|
| 312 |
-
export_settings()
|
| 313 |
-
st.markdown("""> **Creato da Matteo Bergamelli **""")
|
| 314 |
-
|
| 315 |
-
def header() :
|
| 316 |
-
st.title("Bonsi A.I.", anchor=False)
|
| 317 |
-
with st.expander("Cos'è BonsiAI?"):
|
| 318 |
-
st.info("""BonsiAI Chat è un ChatBot personalizzato basato su un database vettoriale, funziona secondo il principio della Generazione potenziata da Recupero (RAG).
|
| 319 |
-
La sua funzione principale ruota attorno alla gestione di un ampio repository di documenti BonsiAI e fornisce agli utenti risposte in linea con le loro domande.
|
| 320 |
-
Questo approccio garantisce una risposta più precisa sulla base della richiesta degli utenti.""")
|
| 321 |
-
|
| 322 |
-
def chat_box() :
|
| 323 |
-
for message in st.session_state.messages:
|
| 324 |
-
with st.chat_message(message["role"]):
|
| 325 |
-
st.markdown(message["content"])
|
| 326 |
-
|
| 327 |
-
def formattaPrompt(prompt, systemRole, systemStyle, instruction):
|
| 328 |
-
if st.session_state.cerca_online:
|
| 329 |
-
systemRole += '. Ti ho fornito una lista di materiali nelle instruction. Devi rispondere sulla base delle informazioni fonrnite!'
|
| 330 |
-
input_text = f'''
|
| 331 |
-
{{
|
| 332 |
-
"input": {{
|
| 333 |
-
"role": "system",
|
| 334 |
-
"content": "{systemRole}",
|
| 335 |
-
"style": "{systemStyle} "
|
| 336 |
-
}},
|
| 337 |
-
"messages": [
|
| 338 |
-
{{
|
| 339 |
-
"role": "instructions",
|
| 340 |
-
"content": "{instruction} ({systemStyle})"
|
| 341 |
-
}},
|
| 342 |
-
{{
|
| 343 |
-
"role": "user",
|
| 344 |
-
"content": "{prompt}"
|
| 345 |
-
}}
|
| 346 |
-
]
|
| 347 |
-
}}
|
| 348 |
-
'''
|
| 349 |
-
return input_text
|
| 350 |
-
|
| 351 |
-
def gen_augmented_prompt(prompt, top_k) :
|
| 352 |
-
links = ""
|
| 353 |
-
embedding = HuggingFaceEmbeddings(model_name="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2")
|
| 354 |
-
db = Chroma(persist_directory='./DB_Decreti', embedding_function=embedding)
|
| 355 |
-
docs = db.similarity_search(prompt, k=top_k)
|
| 356 |
-
links = []
|
| 357 |
-
context = ''
|
| 358 |
-
NomeCartellaOriginariaDB = 'Documenti_2\\'
|
| 359 |
-
for doc in docs:
|
| 360 |
-
testo = doc.page_content.replace('\n', ' ')
|
| 361 |
-
context += testo + '\n\n\n'
|
| 362 |
-
reference = doc.metadata["source"].replace(NomeCartellaOriginariaDB, '') + ' (Pag. ' + str(doc.metadata["page"]) + ')'
|
| 363 |
-
links.append((reference, testo))
|
| 364 |
-
return context, links
|
| 365 |
-
|
| 366 |
-
def get_search_results_int(url):
|
| 367 |
-
result = {'title': '', 'description': '', 'url': '', 'body': ''}
|
| 368 |
-
try:
|
| 369 |
-
if "www.youtube.com" in url:
|
| 370 |
-
video_id = url.split("=")[1]
|
| 371 |
-
title = 'Video Youtube'
|
| 372 |
-
description = ''
|
| 373 |
-
transcript = YouTubeTranscriptApi.get_transcript(video_id)
|
| 374 |
-
body_content = " ".join([segment["text"] for segment in transcript])
|
| 375 |
-
print(video_id)
|
| 376 |
-
print(body_content)
|
| 377 |
-
result = {'title': title, 'description': body_content, 'url': url, 'body': body_content}
|
| 378 |
-
else:
|
| 379 |
-
response = requests.get(url)
|
| 380 |
-
soup = BeautifulSoup(response.text, 'html.parser')
|
| 381 |
-
title = soup.title.string if soup.title else "N/A"
|
| 382 |
-
description = soup.find('meta', attrs={'name': 'description'})['content'] if soup.find('meta', attrs={'name': 'description'}) else "N/A"
|
| 383 |
-
body_content = soup.find('body').get_text() if soup.find('body') else "N/A"
|
| 384 |
-
result = {'title': title, 'description': description, 'url': url, 'body': body_content}
|
| 385 |
-
except Exception as e:
|
| 386 |
-
print(f"Error fetching data from {url}: {e}")
|
| 387 |
-
return result
|
| 388 |
-
|
| 389 |
-
def get_search_results(query, top_k):
|
| 390 |
-
results = []
|
| 391 |
-
if st.session_state.urls[0] != "":
|
| 392 |
-
for i in range(5):
|
| 393 |
-
url = st.session_state.urls[i]
|
| 394 |
-
if url != "":
|
| 395 |
-
results.append(get_search_results_int(url))
|
| 396 |
-
else:
|
| 397 |
-
for url in search(query, num=top_k, stop=top_k, tbs=st.session_state.tbs_value):
|
| 398 |
-
results.append(get_search_results_int(url))
|
| 399 |
-
return results
|
| 400 |
|
| 401 |
-
def
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
| 416 |
-
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
for chunk in chat_stream :
|
| 441 |
-
if CHAT_BOTS[st.session_state.chat_bot]["model"][:3] == 'gpt':
|
| 442 |
-
if chunk.choices[0].delta and chunk.choices[0].delta.content:
|
| 443 |
-
full_response += chunk.choices[0].delta.content
|
| 444 |
-
else:
|
| 445 |
-
if chunk.token.text!='</s>' :
|
| 446 |
-
full_response += chunk.token.text
|
| 447 |
-
placeholder.markdown(full_response + "▌")
|
| 448 |
-
placeholder.markdown(full_response)
|
| 449 |
-
return full_response
|
| 450 |
-
|
| 451 |
-
def show_source(links) :
|
| 452 |
-
with st.expander("Mostra fonti") :
|
| 453 |
-
for link in links:
|
| 454 |
-
reference, testo = link
|
| 455 |
-
st.info('##### ' + reference.replace('_', ' ') + '\n\n'+ testo)
|
| 456 |
-
|
| 457 |
-
def split_text(text, chunk_size):
|
| 458 |
-
testo_suddiviso = []
|
| 459 |
-
if text == '':
|
| 460 |
-
text = ' '
|
| 461 |
-
if chunk_size < 100:
|
| 462 |
-
chunk_size = 60000
|
| 463 |
-
for i in range(0, len(text), chunk_size):
|
| 464 |
-
testo_suddiviso.append(text[i:i+chunk_size])
|
| 465 |
-
return testo_suddiviso
|
| 466 |
-
|
| 467 |
-
init_state()
|
| 468 |
-
if not st.session_state.login_effettuato and EFFETTUA_LOGIN_GOOGLE:
|
| 469 |
-
connetti_google()
|
| 470 |
-
|
| 471 |
-
if st.session_state.login_effettuato or not EFFETTUA_LOGIN_GOOGLE:
|
| 472 |
-
st_javascript("localStorage.removeItem('token');")
|
| 473 |
-
init_state()
|
| 474 |
-
sidebar()
|
| 475 |
-
header()
|
| 476 |
-
chat_box()
|
| 477 |
|
| 478 |
-
|
| 479 |
-
|
| 480 |
-
|
| 481 |
-
|
| 482 |
-
|
| 483 |
-
|
| 484 |
-
|
| 485 |
-
|
| 486 |
-
|
| 487 |
-
|
| 488 |
-
|
| 489 |
-
|
| 490 |
-
|
| 491 |
-
|
| 492 |
-
|
| 493 |
-
|
| 494 |
-
|
| 495 |
-
|
| 496 |
-
|
| 497 |
-
|
| 498 |
-
|
| 499 |
-
|
| 500 |
-
|
| 501 |
-
|
| 502 |
-
|
| 503 |
-
|
| 504 |
-
|
| 505 |
-
|
| 506 |
-
|
| 507 |
-
|
| 508 |
-
|
| 509 |
-
|
| 510 |
-
|
| 511 |
-
|
| 512 |
-
|
| 513 |
-
|
| 514 |
-
|
| 515 |
-
|
| 516 |
-
|
| 517 |
-
|
| 518 |
-
|
| 519 |
-
|
| 520 |
-
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
st.session_state.tutti_messaggi += '\n\n' + full_response
|
| 524 |
-
if st.session_state.enable_history:
|
| 525 |
-
st.session_state.history.append([prompt_originale, full_response])
|
| 526 |
-
else:
|
| 527 |
-
st.session_state.history.append(['', ''])
|
| 528 |
-
st.success('Generazione Completata')
|
| 529 |
-
payload = {"domanda": prompt_originale, "risposta": risposta_completa}
|
| 530 |
-
json_payload = json.dumps(payload)
|
| 531 |
-
response = requests.post(URL_APP_SCRIPT, data=json_payload)
|
|
|
|
| 1 |
+
from io import BytesIO
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
from bs4 import BeautifulSoup
|
| 3 |
+
from collections import namedtuple
|
| 4 |
+
import requests
|
| 5 |
+
import re
|
| 6 |
+
import pandas as pd
|
| 7 |
+
import numpy as np
|
| 8 |
+
import time
|
| 9 |
+
import streamlit as st
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
+
prezzo_al_mq = 0
|
|
|
|
| 12 |
|
| 13 |
+
class Immobiliare:
|
| 14 |
+
|
| 15 |
+
def __init__(self, url, *,
|
| 16 |
+
verbose=True,
|
| 17 |
+
min_house_cost=10_000,
|
| 18 |
+
browse_all_pages=True,
|
| 19 |
+
area_not_found=0,
|
| 20 |
+
price_not_found=np.nan,
|
| 21 |
+
floor_not_found=0,
|
| 22 |
+
car_not_found=0,
|
| 23 |
+
energy_not_found="n/a",
|
| 24 |
+
invalid_price_per_area=0,
|
| 25 |
+
wait=60):
|
| 26 |
|
| 27 |
+
self.url = url
|
| 28 |
+
self.verbose = verbose
|
| 29 |
+
self.min_house_cost = min_house_cost
|
| 30 |
+
self.browse_all_pages = browse_all_pages
|
| 31 |
+
self.wait = wait / 1000
|
| 32 |
+
|
| 33 |
+
self.area_not_found = area_not_found
|
| 34 |
+
self.price_not_found = price_not_found
|
| 35 |
+
self.floor_not_found = floor_not_found
|
| 36 |
+
self.car_not_found = car_not_found
|
| 37 |
+
self.energy_not_found = energy_not_found
|
| 38 |
+
self.invalid_price_per_area = invalid_price_per_area
|
| 39 |
+
|
| 40 |
+
def _say(self, *args, **kwargs):
|
| 41 |
+
if self.verbose:
|
| 42 |
+
print(*args, **kwargs)
|
|
|
|
| 43 |
|
| 44 |
+
def get_all_urls(self):
|
| 45 |
+
pattern = re.compile(r"\d+\/$")
|
| 46 |
+
urls_ = []
|
| 47 |
+
|
| 48 |
+
# first page
|
| 49 |
+
self._say("Processing page 1")
|
| 50 |
+
page = self._get_page(self.url)
|
| 51 |
+
|
| 52 |
+
page.seek(0)
|
| 53 |
+
soup = BeautifulSoup(page, "html.parser")
|
| 54 |
+
|
| 55 |
+
for link in soup.find_all("a"):
|
| 56 |
+
time.sleep(self.wait)
|
| 57 |
+
l = link.get("href")
|
| 58 |
+
|
| 59 |
+
if l is None:
|
| 60 |
+
continue
|
| 61 |
+
|
| 62 |
+
if "https" in l and "annunci" in l:
|
| 63 |
+
if pattern.search(l):
|
| 64 |
+
urls_.append(l)
|
| 65 |
+
|
| 66 |
+
if self.browse_all_pages:
|
| 67 |
+
for i in range(2, 10_000):
|
| 68 |
+
self._say(f"Processing page {i}")
|
| 69 |
+
curr_url = self.url + f"&pag={i}"
|
| 70 |
+
|
| 71 |
+
t = self._get_text(curr_url).lower()
|
| 72 |
+
|
| 73 |
+
if "404 not found" in t or "non è presente" in t:
|
| 74 |
+
self.urls_ = urls_
|
| 75 |
+
break
|
| 76 |
+
|
| 77 |
+
else:
|
| 78 |
+
page = self._get_page(curr_url)
|
| 79 |
+
page.seek(0)
|
| 80 |
+
soup = BeautifulSoup(page, "html.parser")
|
| 81 |
+
|
| 82 |
+
for link in soup.find_all("a"):
|
| 83 |
+
l = link.get("href")
|
| 84 |
+
|
| 85 |
+
if l is None:
|
| 86 |
+
continue
|
| 87 |
+
|
| 88 |
+
if "https" in l and "annunci" in l:
|
| 89 |
+
if pattern.search(l):
|
| 90 |
+
urls_.append(l)
|
| 91 |
+
|
| 92 |
+
self.urls_ = urls_
|
| 93 |
+
self._say("All retrieved urls in attribute 'urls_'")
|
| 94 |
+
self._say(f"Found {len(urls_)} houses matching criteria.")
|
| 95 |
+
|
| 96 |
+
@staticmethod
|
| 97 |
+
def _get_page(url):
|
| 98 |
+
req = requests.get(url, allow_redirects=False)
|
| 99 |
+
page = BytesIO()
|
| 100 |
+
page.write(req.content)
|
| 101 |
+
return page
|
| 102 |
|
| 103 |
+
@staticmethod
|
| 104 |
+
def _get_text(sub_url):
|
| 105 |
+
req = requests.get(sub_url, allow_redirects=False)
|
| 106 |
+
page = BytesIO()
|
| 107 |
+
page.write(req.content)
|
| 108 |
+
page.seek(0)
|
| 109 |
+
soup = BeautifulSoup(page, "html.parser")
|
| 110 |
+
text = soup.get_text()
|
| 111 |
+
t = text.replace("\n", "")
|
| 112 |
+
for _ in range(50):
|
| 113 |
+
t = t.replace(" ", " ")
|
| 114 |
+
return t
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
|
| 116 |
+
def _get_data(self, sub_url):
|
| 117 |
+
t = self._get_text(sub_url).lower()
|
| 118 |
|
| 119 |
+
# costo appartamento
|
| 120 |
+
cost_patterns = (
|
| 121 |
+
r"€ (\d+\.\d+\.\d+)", #if that's more than 1M €
|
| 122 |
+
r"€ (\d+\.\d+)",
|
| 123 |
+
)
|
| 124 |
|
| 125 |
+
cost = None
|
| 126 |
+
locali = None
|
| 127 |
+
for pattern in cost_patterns:
|
| 128 |
+
cost_pattern = re.compile(pattern)
|
| 129 |
+
try:
|
| 130 |
+
cost = cost_pattern.search(t)
|
| 131 |
+
locali = str(cost.group(1).replace(".", ""))[-1]
|
| 132 |
+
cost = str(cost.group(1).replace(".", ""))[:-1]
|
| 133 |
+
#cost = cost.group(1).replace(".", "")
|
| 134 |
+
break
|
| 135 |
+
except AttributeError:
|
| 136 |
+
continue
|
| 137 |
+
|
| 138 |
+
if cost is None:
|
| 139 |
+
if "prezzo su richiesta" in t:
|
| 140 |
+
self._say(f"Price available upon request for {sub_url}")
|
| 141 |
+
cost = self.price_not_found
|
| 142 |
+
else:
|
| 143 |
+
self._say(f"Can't get price for {sub_url}")
|
| 144 |
+
cost = self.price_not_found
|
| 145 |
+
|
| 146 |
+
if cost is not None and cost is not self.price_not_found:
|
| 147 |
+
if int(cost) < self.min_house_cost:
|
| 148 |
+
if "prezzo su richiesta" in t:
|
| 149 |
+
self._say(f"Price available upon request for {sub_url}")
|
| 150 |
+
cost = self.price_not_found
|
| 151 |
+
else:
|
| 152 |
+
self._say(f"Too low house price: {int(cost)}? for {sub_url}")
|
| 153 |
+
cost = self.price_not_found
|
| 154 |
+
|
| 155 |
+
# piano
|
| 156 |
+
floor_patterns = (
|
| 157 |
+
r"piano (\d{1,2})",
|
| 158 |
+
r"(\d{1,2}) piano",
|
| 159 |
+
r"(\d{1,2}) piani",
|
| 160 |
+
)
|
| 161 |
+
|
| 162 |
+
floor = None
|
| 163 |
+
for pattern in floor_patterns:
|
| 164 |
+
floor_pattern = re.compile(pattern)
|
| 165 |
+
floor = floor_pattern.search(t)
|
| 166 |
+
if floor is not None:
|
| 167 |
+
floor = floor.group(1)
|
| 168 |
+
break
|
| 169 |
+
|
| 170 |
+
if "piano terra" in t:
|
| 171 |
+
floor = 1
|
| 172 |
+
|
| 173 |
+
ultimo = "ultimo" in t
|
| 174 |
+
|
| 175 |
+
# metri quadri
|
| 176 |
+
|
| 177 |
+
area_pattern = re.compile(r"(\d+) m²")
|
| 178 |
+
try:
|
| 179 |
+
area = area_pattern.search(t)
|
| 180 |
+
area = area.group(1)
|
| 181 |
+
except AttributeError:
|
| 182 |
+
area = self.area_not_found
|
| 183 |
+
if "asta" in t:
|
| 184 |
+
self._say(f"Auction house: no area info {sub_url}")
|
| 185 |
+
else:
|
| 186 |
+
self._say(f"Can't get area info from url {sub_url}")
|
| 187 |
+
|
| 188 |
+
# classe energetica
|
| 189 |
+
energy_patterns = (
|
| 190 |
+
r"energetica (\D{1,2}) ",
|
| 191 |
+
r"energetica(\S{1,2})",
|
| 192 |
+
)
|
| 193 |
+
|
| 194 |
+
def energy_acceptable(stringlike):
|
| 195 |
+
if not stringlike.startswith(("A", "B", "C", "D", "E", "F", "G")):
|
| 196 |
+
return False
|
| 197 |
+
else:
|
| 198 |
+
if len(stringlike) == 1:
|
| 199 |
+
return True
|
| 200 |
+
else:
|
| 201 |
+
if not stringlike.endswith(
|
| 202 |
+
("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "+")
|
| 203 |
+
):
|
| 204 |
+
return False
|
| 205 |
+
else:
|
| 206 |
+
return True
|
| 207 |
+
|
| 208 |
+
energy = None
|
| 209 |
+
for i, pattern in enumerate(energy_patterns):
|
| 210 |
+
energy_pattern = re.compile(pattern)
|
| 211 |
+
energy = energy_pattern.search(t)
|
| 212 |
+
if energy is not None:
|
| 213 |
+
energy = energy.group(1).upper()
|
| 214 |
+
if energy_acceptable(energy):
|
| 215 |
+
break
|
| 216 |
+
|
| 217 |
+
if energy is None or not energy_acceptable(energy):
|
| 218 |
+
if "in attesa di certificazione" in t:
|
| 219 |
+
self._say(f"Energy efficiency still pending for {sub_url} ")
|
| 220 |
+
energy = self.energy_not_found
|
| 221 |
+
else:
|
| 222 |
+
self._say(f"Can't get energy efficiency from {sub_url}")
|
| 223 |
+
energy = self.energy_not_found
|
| 224 |
+
|
| 225 |
+
# posto auto
|
| 226 |
+
car_patterns = (
|
| 227 |
+
r"post\S auto (\d{1,2})",
|
| 228 |
+
)
|
| 229 |
+
|
| 230 |
+
car = None
|
| 231 |
+
for pattern in car_patterns:
|
| 232 |
+
car_pattern = re.compile(pattern)
|
| 233 |
+
car = car_pattern.search(t)
|
| 234 |
+
if car is not None:
|
| 235 |
+
car = car.group(1)
|
| 236 |
+
break
|
| 237 |
+
|
| 238 |
+
if car is None:
|
| 239 |
+
available_upon_request = re.compile(r"possibilit\S.{0,10}auto")
|
| 240 |
+
if available_upon_request.search(t) is not None:
|
| 241 |
+
self._say(f"Car spot/box available upon request for {sub_url}")
|
| 242 |
+
car = 0
|
| 243 |
+
else:
|
| 244 |
+
car = self.car_not_found
|
| 245 |
+
|
| 246 |
+
# €/m²
|
| 247 |
try:
|
| 248 |
+
price_per_area = round(int(cost) / int(area), 1)
|
| 249 |
+
differenza = prezzo_al_mq - price_per_area
|
| 250 |
+
vantaggio = (differenza / prezzo_al_mq) * 120
|
| 251 |
+
vantaggio = max(0, vantaggio)
|
| 252 |
+
vantaggio = int(vantaggio)
|
| 253 |
except:
|
| 254 |
+
price_per_area = self.energy_not_found
|
| 255 |
+
vantaggio = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 256 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 257 |
|
| 258 |
+
# packing the results
|
| 259 |
+
House = namedtuple(
|
| 260 |
+
"House", [
|
| 261 |
+
"Vantaggio",
|
| 262 |
+
"Prezzo_Mq",
|
| 263 |
+
"Prezzo",
|
| 264 |
+
"Superficie",
|
| 265 |
+
"Locali",
|
| 266 |
+
"Piano",
|
| 267 |
+
#"ultimo",
|
| 268 |
+
"Url"
|
| 269 |
+
#"energy",
|
| 270 |
+
#"posto_auto"
|
| 271 |
+
]
|
| 272 |
+
)
|
| 273 |
|
| 274 |
+
res = House(
|
| 275 |
+
vantaggio,
|
| 276 |
+
price_per_area,
|
| 277 |
+
cost,
|
| 278 |
+
area,
|
| 279 |
+
#ultimo,
|
| 280 |
+
locali,
|
| 281 |
+
floor,
|
| 282 |
+
sub_url
|
| 283 |
+
#energy,
|
| 284 |
+
#car
|
| 285 |
+
)
|
| 286 |
+
|
| 287 |
+
return res
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 288 |
|
| 289 |
+
def find_all_houses(self):
|
| 290 |
+
if not hasattr(self, "urls_"):
|
| 291 |
+
self.get_all_urls()
|
| 292 |
+
|
| 293 |
+
all_results = []
|
| 294 |
+
for url in self.urls_:
|
| 295 |
+
try:
|
| 296 |
+
all_results.append(self._get_data(url))
|
| 297 |
+
except:
|
| 298 |
+
print(f"offending_url='{url}'")
|
| 299 |
+
raise
|
| 300 |
+
|
| 301 |
+
self.df_ = pd.DataFrame(all_results)
|
| 302 |
+
self._say("Results stored in attribute 'df_'")
|
| 303 |
+
|
| 304 |
+
# Funzione di styling per evidenziare in rosso i valori inferiori alla variabile
|
| 305 |
+
def evidenzia_in_rosso(valore, soglia):
|
| 306 |
+
if valore < soglia:
|
| 307 |
+
return 'background-color: red; color: white'
|
| 308 |
+
return ''
|
| 309 |
+
|
| 310 |
+
st.set_page_config(layout="wide")
|
| 311 |
+
# Streamlit interface
|
| 312 |
+
|
| 313 |
+
st.title('🏠 Immobiliare A.I. ')
|
| 314 |
+
st.write("##### Il tuo assistente di intelligenza artificiale per la ricerca di occasioni immobiliari")
|
| 315 |
+
with st.expander("Informazioni"):
|
| 316 |
+
st.write("Immobiliare A.I. è la webapp che semplifica la ricerca di immobili, grazie a algoritmi avanzati che calcolano il vantaggio di ogni offerta. Trova le migliori occasioni sul mercato con analisi precise e personalizzate. Scopri l’immobile giusto per te con facilità e sicurezza!")
|
| 317 |
+
|
| 318 |
+
cerca_premuto = False
|
| 319 |
+
# Input field for 'comune'
|
| 320 |
+
with st.sidebar:
|
| 321 |
+
st.title("Filtri")
|
| 322 |
+
comune_input = st.text_input("Comune", 'lonato del garda')
|
| 323 |
+
prezzo_al_mq = st.number_input("Prezzo Medio al Mq", 2500)
|
| 324 |
+
prezzo_minimo = st.sidebar.slider("Prezzo Minimo", min_value=0, max_value=1000, value=200)
|
| 325 |
+
prezzo_massimo = st.sidebar.slider("Prezzo Massimo", min_value=0, max_value=1000, value=230)
|
| 326 |
+
|
| 327 |
+
locali = list(range(1, 21)) # Intervallo da 1 a 10
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 328 |
|
| 329 |
+
# Select slider unico per selezionare l'intervallo del numero di locali
|
| 330 |
+
locali_range = st.sidebar.select_slider(
|
| 331 |
+
"Locali",
|
| 332 |
+
options=locali,
|
| 333 |
+
value=(locali[2], locali[4]) # Valore iniziale, da 1 a 5 locali
|
| 334 |
+
)
|
| 335 |
+
|
| 336 |
+
# Dividi il range in minimo e massimo numero di locali
|
| 337 |
+
locali_minimo, locali_massimo = locali_range
|
| 338 |
+
prezzo_minimo = prezzo_minimo*1000
|
| 339 |
+
prezzo_massimo = prezzo_massimo*1000
|
| 340 |
+
cerca_premuto = st.button("Cerca", use_container_width=True, type='primary')
|
| 341 |
+
|
| 342 |
+
if cerca_premuto:
|
| 343 |
+
if comune_input:
|
| 344 |
+
comune = comune_input.replace(" ", "-")
|
| 345 |
+
|
| 346 |
+
|
| 347 |
+
url = f"https://www.immobiliare.it/vendita-case/{comune}/?prezzoMinimo={prezzo_minimo}&prezzoMassimo={prezzo_massimo}&localiMinimo={locali_minimo}&localiMassimo={locali_massimo}&random=123456"
|
| 348 |
+
#st.write(f"Seraching: {url}")
|
| 349 |
+
with st.spinner("Ricerca immobiliare in corso..."):
|
| 350 |
+
case = Immobiliare(url)
|
| 351 |
+
case.find_all_houses()
|
| 352 |
+
df = case.df_
|
| 353 |
+
df = df.sort_values(by="Prezzo_Mq", ascending=True)
|
| 354 |
+
|
| 355 |
+
st.dataframe(df, hide_index=True, use_container_width=True,
|
| 356 |
+
column_config ={
|
| 357 |
+
"Vantaggio": st.column_config.ProgressColumn(
|
| 358 |
+
"Vantaggio",
|
| 359 |
+
help="Vantaggio in %",
|
| 360 |
+
format='%f',
|
| 361 |
+
min_value=0,
|
| 362 |
+
max_value=100,
|
| 363 |
+
),
|
| 364 |
+
"Prezzo_Mq": " €/Mq",
|
| 365 |
+
"Prezzo": "Prezzo Totale",
|
| 366 |
+
"Superficie": "Superficie",
|
| 367 |
+
"Locali": "Locali",
|
| 368 |
+
"Piano": "Piano",
|
| 369 |
+
"Url": st.column_config.LinkColumn("App URL")
|
| 370 |
+
})
|
| 371 |
+
st.success("Elaborazione Completata")
|
| 372 |
+
else:
|
| 373 |
+
st.error("Per favore, inserisci il nome di un comune.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|