#*jN"ddlZeejdrejddddlmZmZmZmZm Z m Z m Z m Z m Z ddlmZdd lmZddlZddlZdd lmZeed d Zd e_dejd<dejd<ddZejddkr edZedZedZdZ e!ddgdZ"e!ddge!d dgd!Z#e!d"dgd#Z$e!d$dgd%Z%e!d&dge d'Z&e!d&dge d(Z'e!d)d*ge d+Z(e!d)d,ge d-Z)e!d.dge d/Z*e!d.dgd0Z+e!d1d*gd2Z,e!d1d,gd3Z-e!d4dgd5Z.e!d6d7Z/e!d8d9Z0e!d:d;e1fd<Z2d=e1fd>Z3d;e1fd?Z4e!d@d;e1fdAZ5e!dBd;e1fdCZ6e!dDdgdEZ7e!dFdgd;e1fdGZ8e!d:d*gd;e1fdHZ9e!dIdgdJe1dKe1fdLZ:e!d:d,gd;e1fdMZ;e!dNdgd;e1fdOZe!dPd,gdQe=fdSZ?e!dTdUZ@e!dVdWZAe!dXdYZBe!dZd[e=fd\ZCe!d]d^ZDe!d_d`e1fdaZEe!dZd*gd[e=fdbZFe!dcdgd[e=fddZGe!dedgdfZHe!dgd*gdhe=fdiZIe!dgd,gdhe=fdjZJe!d8dge!dkdgdlZKe!dme!dndoZLe!dpdqZMe!drdsZNe!dtduZOe!dvdwZPe!dxdgdyZQe!dzdgd{ZRe!d|dgd}ZSe!d~dge dZTe!ddge dZUe!ddge dZVe!ddge dZWe!ddZXe!dd=e1fdZYe!ddZZe!ddZ[e!ddZ\e!ddZ]e!ddZ^e!ddZ_e!ddZ`e!ddZae!ddZbe!ddZce!ddge dZde!ddge dZee!ddge dZfe!ddge dZge!ddge dZhe!ddge dZie!ddge dZje!dd*ge dZke!dd,ge dZle!ddZmedkrendddSdS)N reconfigureutf-8replace)encodingerrors) Flaskrender_templatesend_from_directoryjsonifyrequest make_responsesessionredirect send_file)database_logicwraps)logger ../frontend) static_foldertemplate_folder/mediatrack_secret_key_2025_change_in_productionTSESSION_COOKIE_HTTPONLYLaxSESSION_COOKIE_SAMESITE,c~ddl}ddlfd}||dd}|dS)uAvvia un thread demone che fa push_offline_inserts ogni N secondi (default 5 min). Gira solo quando la rete è disponibile. Silenzioso — non blocca mai l'app.rNcddl}|d ddlm}|dz dz }d}|r=|dd d}|r)dd lm }|}|dkrtd |d n#t$rYnwxYw|) NrT get_app_rootlauncher_cachenetwork_status.jsonrronline)push_offline_insertsz[sync] Push periodico: z prodotti sincronizzati) timesleepapp.config_loaderr"existsloads read_textgetlauncher_src.shared_db_syncr'print Exception)r(r"flagr&r'n_jsoninterval_secondss >C:\PYTHON\MyICR_Suite\app\modules\mediatrack\backend\server.py _sync_loopz(_start_periodic_sync.._sync_loop!s  2 ) ::::::#|~~(88;PP;;==_"[[)I)IJJNNxY]^^FTPPPPPP,,..A1uuRRRRSSS     JJ' ( ( ( )sBB22 B?>B?Tz periodic-synctargetdaemonname) threadingjsonThreadstart)r5r<r7tr4s` @r6_start_periodic_syncrAsi$#######))))))(  4oNNAGGIIIIIMYICR_DEV_MODE1cvddl}ddlfd}||dddS)zEsegue query leggere sui parquet principali in background al boot per azzerare la latenza del primo accesso reale (diritti/emesso nella scheda prodotto).rNcvd ddlm}|dSdD]e} |d|dt jd|d5#t $r$}t jd|d |Yd}~^d}~wwxYwdS#t $r"}t jd |Yd}~dSd}~wwxYw) Nr db_manager)imdbdirittiemessozSELECT 1 FROM "z " LIMIT 1z [prewarm] z OK: zrewarm] errore generale: )r)app.database.connectionrI execute_queryrinfor1warning)rIviewer(s r6_warmz_prewarm_duckdb.._warmCs, 1  > : : : : : :!5 = ==,,-Nt-N-N-NOOOK 6T 6 6 67777 ===N#;#;#;#;#;<<<<<<<<=  = =  > > > N<<< = = = = = = = = = >sEB B 1AB  B#B=B BB B8B33B8Tzduckdb-prewarmr8)r<r(r>r?)r<rTr(s @r6_prewarm_duckdbrU>sf > > > > >E$5EFFLLNNNNNrBcddl}ddlddlddlmdt ddffd fd}||d d dS) zJCopia in locale tutti gli allegati referenziati nel DB (fallback offline).rNPath file_pathattachments_dirreturncL|}|jdd}|j|S)z4Mappa un path di rete al corrispondente path locale.rN)partsjoinpath)rYrZpr]rXs r6_attachment_local_pathz1_sync_attachments.._attachment_local_path^s/ DOO ''//rBc  d ddlm}|dz dz }|ddt j}d\}}}|D]} |}|s|d z }' ||}|r:|j|jkr|d z }|j dd t|t||d z }tj d |d |d |d dS#t$r"} tjd| Yd} ~ dSd} ~ wwxYw)N-rr!local_db attachmentsT)parentsexist_ok)rrrrz[attachments] sync: z copiati, u già aggiornati, z non raggiungibilizttachments] errore sync: )r)r*r"mkdirrget_all_allegati_pathsr+statst_mtimeparentcopy2strrrPr1rQ)r"rZpathscopiedskippedmissingsrcsrc_pathdstrSrXr`shutilr(s r6_syncz _sync_attachments.._synces 2 > 6 6 6 6 6 6*lnnz9MIO  ! !$ ! > > >"9;;E'. $FGW  499((qLG,,S/BB::<<CHHJJ$78==??;S$S$SqLG    === S]]CHH555!  Kwvwwww\cwww x x x x x > > > N<<< = = = = = = = = = >sD9E E?E::E?Tzattachments-syncr8)r<r(rupathlibrXrmr>r?)r<rvrXr`rur(s @@@@r6_sync_attachmentsrxYs""""""""""""0#00000000>>>>>>>>2E$5GHHNNPPPPPrBctjdp1tjdptjS)z6Ritorna il nome utente Windows della macchina correnteUSERNAMEUSER)osenvironr.socket gethostnamerBr6get_windows_userrs7 :>>* % % W)?)? W6CUCWCWWrBc<tfd}|S)zHDecorator no-op: accesso libero, utente identificato da Windows usernamec|i|SNr)argskwargsfs r6decorated_functionz*login_required..decorated_functionsq$!&!!!rBr)rrs` r6login_requiredrs2 1XX""""X" rBz/api/auth/checkGET)methodscDtdtdddS)z9Sempre autenticato: restituisce l'utente Windows correnteTuser)usernameruolo) authenticatedr)r rrrBr6api_check_authrs9 (**    rBz/api/auth/logoutPOSTz /api/logoutc$tddiS)uONo-op: non esiste più il login, mantiene compatibilità con eventuali chiamatesuccessT)r rrBr6 api_logoutrs It$ % %%rBz/api/network-statuscF ddlm}ddl}ddl}|dz }d}|dz }|r=||dd d}d}|d z }|rF|j }|j | d }|stj nd} t|| |d S#t$rtdddd cYSwxYw) zJStato rete: online/offline + data ultimo sync + prodotti offline pendenti.rr!Nr#Tr$rr%r&zshared_db_pull.jsonz%d/%m/%Y %H:%M)r&pending_offline last_sync)r*r"r=datetimer+r,r-r.rirj fromtimestampstrftimerget_pending_offline_countr r1) r"r4_dt cache_dirr&r2r pull_cachetspendings r6api_network_statusrs`R222222-------- LNN%55 00 ;;== W[[!A!ABBFFxQUVVF !66      R""+B 22266??@PQQIDJQ.:<<PUTc  tj}tj||dd|d|dd|dd|dpd\}}t ||d S#t $r+}t d t|d d fcYd}~Sd}~wwxYw) NrrrattivorrrrrFrr)r rraggiorna_contestor.r r1rm) id_contestorrrrSs r6api_update_contestors A!! 2  HHVR HH% & & HHXq ! ! HHXq ! ! HH- . . 6$   C2#66777 AAA53q66::;;S@@@@@@@AsB$B'' C1 CCCDELETEc tj|\}}t||dS#t$r+}tdt |ddfcYd}~Sd}~wwxYw)NrFrr)relimina_contestor r1rm)rrrrSs r6api_delete_contestorsA 1+>>C2#66777 AAA53q66::;;S@@@@@@@As(+ A  AA A z/api/event-typesc tj}td|iS#t$r/}t d|tddidfcYd}~Sd}~wwxYw)zNGET: Recupera tutti i tipi evento (accessibile a tutti gli utenti autenticati) eventTypesz [ERROR] Errore get event types: rzErrore recupero tipi eventorN)rget_all_event_typesr r1r0) event_typesrSs r6api_get_event_typesr sF$8::  k2333 FFF 444555!>?@@#EEEEEEEF#& A$AAAc F tj}td||d}|d}|dd}|d}|dd}|d }td |d |d ||r|st d dddfSt j|||||||dd\}}} td|d||rt d|| dSt d |ddfS#t$rU} td| ddl} | t d t| ddfcYd} ~ Sd} ~ wwxYw)zPOST: Crea un nuovo tipo eventoz[DEBUG] Dati ricevuti: nome_evento colore_hexiconaz fa-calendar descrizionefunzione_specialenonecampi_visibiliz[DEBUG] nome_evento: z , colore: z , icona: FzNome e colore sono obbligatorir visibilitaentrambi)rrrrrrrzEBUG] Risultato: success=z , message=T)rridz"[ERROR] Errore create event type: rNr) r r=r0r.r rcreate_event_typer1 traceback print_excrm) rrrrrrrrrid_tipo_eventorSrs r6api_create_event_typers%A| ...///hh}-- XXl++ -00hh}--  HH%8&AA"233 YkYYZYYRWYYZZZ _* _u7WXXYY[^^ ^+9+K#!#/)xx j99, , , (. HGHHwHHIII  Ft~VVWW Wuw??@@#E E AAA 616677753q66::;;S@@@@@@@ As,C EAE-E F  A FF F z%/api/event-types/c . tj}tj||d|d|d|d|d|d|d\}}|rt d |d St d |d d fS#t $r0}td|t d dd dfcYd}~Sd}~wwxYw)z&PUT: Aggiorna un tipo evento esistenterrrrrrr)rrrrrrrrTrFrrz"[ERROR] Errore update event type: z Errore aggiornamento tipo eventorN)r r=rupdate_event_typer.r r1r0)rrrrrSs r6api_update_event_typerCs;]|);)//xx --((7##//"hh':;;88$455xx --      Ft@@AA Auw??@@#E E ]]] 616677753UVVWWY\\\\\\\\]s$CCC D$%D DDc tj|\}}|rtd|dStd|ddfS#t$r0}t d|tddddfcYd }~Sd }~wwxYw) z*DELETE: Elimina (disattiva) un tipo eventoTrFrrz"[ERROR] Errore delete event type: zErrore eliminazione tipo eventorN)rdelete_event_typer r1r0)rrrrSs r6api_delete_event_typer^s \);NKK  Ft@@AA Auw??@@#E E \\\ 616677753TUUVVX[[[[[[[[\s"*AA A; %A60A;6A;z/api/client-logc tjpi}|dd}|di}tjd|d|t ddiS#t $rt ddid fcYSwxYw) z6Riceve log dal frontend JS e li scrive nel log server.rrextraz[JS] z | rTFr)r r=r.rrPr r1)rrrs r6api_client_logrps+|!rhhub!!"%% +C++E++,,,d|$$$ +++e}%%s****+sA$A''BBz/api/filter-datac tjdddk}tjdd}t j||}t j}t j}t j}t||||dS#t$r/}td|tdd id fcYd }~Sd }~wwxYw) zCEndpoint API per recuperare i dati per i menu a tendina dei filtri. includi_gemmafalsetrueanno_min_gemmar) distributorscontextsbuyers tipologiez+Errore nel recuperare i dati per i filtri: rz&Impossibile caricare i dati dei filtrirN) r rr.lowerrget_distributori_uniciget_contesti_uniciget_buyers_uniciget_tipologie_unicher r1r0)rr distributorirrrrSs r6api_get_filter_datar}sQ (('BBHHJJfT  ))*:B??%<]N[[ !466022"799 ( "      QQQ ?A??@@@!IJKKSPPPPPPPQsB7B:: C3$C.(C3.C3z /api/productsc&tjdd}tjdd}tjdd}tjdd}tjdd}tjddd k}tjd d}tjd d} t jd |d |d|d|t j||||||||}t jdt|t|S#t$r} t j d| d|d|d|d |d|d|d|ddl } t j | tdt| idfcYd} ~ Sd} ~ wwxYw)z;Endpoint API per recuperare la lista dei prodotti filtrata.titolor distributorecontesto tipo_evento codice_imdbrrrr tipologiaz[api/products] contesto=z tipo_evento=z titolo=z gemma=z[api/products] risultati=z[api/products] Errore: z | params: titolo=z distrib=z contesto=z imdb=z anno=rNrr)r rr.rrrPrget_dati_griglia_filtratilenr r1rrdebug format_excrm) rrrrrrrrproductsrSrs r6api_get_productsrs\  h + +F<##NB77L| B//H,""="55K,""="55KL$$_g>>DDFF&PM\%%&6;;N   b11I / x _ep} A A A!;FLRZ\ikvyGIRT_`` ?H ??@@@x   /// ]q]]F]]Wc]]qy]]KV]]an]]vA]]KY]] ^ ^ ^ Y))++,,,Q())3....... /s A-F HA5H H Hz/api/product/media_idctd||dr|dd}tj|}|t ddidfStj|\}}}d}|r|dnd}|rd dl}tj}|j |_ | } | d |f| } || rt| }t |||d Stj|\} }}| t dd idfSt | ||d S) zCEndpoint API per recuperare i dati completi di un singolo prodotto.z ==> Richiesta dettaglio per ID: G_NrzProdotto Gemma non trovatorrz4SELECT * FROM OmdbData WHERE codice_imdb = ? LIMIT 1) anagraficalogs omdb_datazProdotto non trovato)r0 startswithrget_prodotto_gemma_dettaglior get_storia_prodottor.sqlite3_connRow row_factorycursorexecutefetchoneclosedict) rgemma_id gemma_product_r r r_sqconcurrowrs r6api_get_product_detailsrs 7X 7 78884  \ABB<&CHMM  G%ABCCSH H$7AA 4 :GQm'' 666T  & ! ! ! ! &((C!gCO**,,C KKNQ\P^ _ _ _,,..C IIKKK & II mTPYZZ[[[#1"DX"N"NJi!78993>> *dSS T TTrBrcddlm}|dS |d|f}|js t |jddndS#t $r"}tjd|Yd}~dSd}~wwxYw)uQBridge: codice_imdb → prod OnAir via imdb.parquet. Ritorna None se non trovato.rrHNzBSELECT codice AS prod FROM imdb WHERE riferimento_imdb = ? LIMIT 1produ[bridge imdb→prod] ) rNrIrOemptyrmilocr1rrQ)rrIdfrSs r6_get_prod_from_imdbr$s222222t  % % P N  /1h@s271:f%&&&D@ 2q22333ttttts?A A8A33A8c |drtjd|ddt dt dt dzdd}|js:|jd d}|r#t|r|ndSdSd dl }tj }|j |_ | }|d |f|}||r|d r|d ndS) zQLegge codice_imdb da SQLite (MediaTrack) o gemma.parquet (Gemma) per un media_id.rA_ID = 'rN'' A_COD_IMDBwherecolumnsrz:SELECT codice_imdb FROM AnagraficaMedia WHERE id_media = ?r)r r _gemma_dfrchrr!r"rmstriprrrrrrrr)rgdfvalrrrrs r6_get_codice_imdb_for_mediar2s64  J&NXabb\11#b''3r773r77?KKNNN    y =(1+l+C<#c((.."2"2<33 <t"$$'jjll PS[R]^^^llnn %(IS-?Is=!!TIrBz&/api/product//dirittic8ddlm}ddl}|tddidfSt |}|stdgdSt |}|stdgdS |d|f}|||d d }td ||d S#t$rA}tj d |tdt|idfcYd}~Sd}~wwxYw)NrrHr Database parquet non disponibilerT) not_in_onairrowsaV SELECT CAST(decr AS VARCHAR) AS decr, CAST(scad AS VARCHAR) AS scad, ragsoc_distr, perc, pass_cons_tot, pass_eff_tot, causale FROM diritti WHERE prod = ? ORDER BY decr DESC recordsorientF)r5r r6z[diritti query] )rNrIpandasr r2r$rOr+notnullto_dictr1rrrm)rrIpdrr r#r6rSs r6api_get_product_dirittir>sh222222!CDEEsJJ,X66K ;b99::: { + +D ;b99:::/  % % ' W  xx 2--55Y5GGtTJJKKK /// +++,,,Q())3......./s'A&C D6DDDz%/api/product//emessoczddlm}ddlm}ddl}|t ddidfSt |}|st dgddSt|}|st dgddStj d d  d k}| }||j d z |j|j} |r|d |f} n|d||f} | || dd} t d|| | |dS#t*$rA} t-jd| t dt1| idfcYd} ~ Sd} ~ wwxYw)NrrH)daterr4rT)r5r6totalallrra SELECT CAST(data_emissione AS VARCHAR) AS data_emissione, rete, CAST(ora_inizio AS VARCHAR) AS ora_inizio, fascia, audience, share FROM emesso WHERE prodotto = ? ORDER BY data_emissione DESC a SELECT CAST(data_emissione AS VARCHAR) AS data_emissione, rete, CAST(ora_inizio AS VARCHAR) AS ora_inizio, fascia, audience, share FROM emesso WHERE prodotto = ? AND data_emissione >= ? ORDER BY data_emissione DESC r7r8F)r5r r6filteredcutoffz[emesso query] )rNrIrr@r:r r2r$r rr.rtodayyearmonthday isoformatrOr+r;r<r1rrrm) rrIr@r=rr all_datarFrEr#r6rSs r6api_get_product_emessorLs!222222!CDEEsJJ,X66K Gb1EEFFF { + +D Gb1EEFFF|w//55776AH JJLLE T%*q.%+uy 9 9 C C E EF"/  !)) +  BB)) + ! !Bxx 2--55Y5GG!4(lf      /// *q**+++Q())3......./s*BE// F:96F5/F:5F:z/api/omdb/purge-stubsc tj}td|d|ddS#t$r+}tdt |ddfcYd}~Sd}~wwxYw) z:Elimina i record OmdbData stub (tutti i campi chiave N/A).Tz Eliminati z record stub OmdbData.)rdeletedrFrrN)rpurge_omdb_stubsr r1rm)rNrSs r6api_purge_omdb_stubsrPZsC 1334GHtU\HtHtHtuuvvv CCC5SVV<<==sBBBBBBBCs), A! AA!A!z*/api/product//import-omdbc tjd}|stddddfS t d|d|t j||\}}t d|d ||rtd |dd fStd|dd fS#t$rU}t d |ddl}| tdt|dd fcYd}~Sd}~wwxYw)zS Endpoint API per importare i dati da OMDB e salvarli direttamente nel DB. imdb_idFzID IMDb non fornito.rrz'[DEBUG] Importazione OMDB per media_id=z , imdb_id=z[DEBUG] Risultato: successo=z , messaggio=Trz+[ERROR] Eccezione in api_import_omdb_data: rN) r r=r.r r0rimporta_e_salva_da_omdbr1rrrm)rrRsuccesso messaggiorSrs r6api_import_omdb_datarWgs` ly))G S55KLLMMsRR C UUUGUUVVV,DXwWW) NXNN9NNOOO  Jt BBCCSH HuCCDDcI I CCC ?A??@@@5SVV<<==sBBBBBBB Cs%AB#B## D-A C=7D=Dctj}td|tj||\}}|rt d|ddfSt d|ddfS)z=Endpoint API per modificare i dati anagrafici di un prodotto.z8==> Ricevuta richiesta PUT per modificare anagrafica di TrrSFr)r r=r0rmodifica_anagraficar )rdati_modificatirUrVs r6api_update_product_anagraficar[sxlO OX O OPPP(>??DD5Y??@@#EErBz8/api/product//merge-into/mt_idrctj||\}}}|rtd||ddfStd|ddfS)zSRiconcilia un prodotto MT con uno Gemma: sposta gli eventi e cancella il record MT.T)rrn_eventirSFrr)rriconcilia_prodottir )r\rrUrVr^s r6api_merge_productsr`s_%3$Fuh$W$W!Hi[4I8TTUUWZZZ u;; < 'orphaned') Response (200): { "success": true, "message": "Prodotto 'Titolo' (FILM-123) eliminato con successo", "stats": { "eventi": 5, "omdb": 1, "sceneggiature": 2, "carrello_items": 0 } } Response (400): { "success": false, "message": "Impossibile eliminare prodotti Gemma (read-only)" } Response (404): { "success": false, "message": "Prodotto FILM-123 non trovato" } z5==> Ricevuta richiesta DELETE per eliminare prodotto T)rrstatsrSz non trovator impossibilegemmarrFr)r0relimina_prodottor r)rrUrVrb status_codes r6api_delete_productrgsL L( L LMMM!/!@!J!JHi         IOO-- - -KK ioo// / /7ioo>O>O3O3OKKK       rBz#/api/product//logsctj}td|d|tj||\}}|rt d|ddfSt d|ddfS)z Endpoint API per aggiungere un nuovo evento (log) a un prodotto. Riceve i dati del nuovo evento nel corpo della richiesta come JSON. z4==> Ricevuta richiesta POST per aggiungere evento a rMTrFr)r r=r0raggiungi_aggiornamentor )rdati_nuovo_eventorUrVs r6api_add_log_to_productrls  ` ` `M^ ` `aaa )?J[\\HiF4I>>??DD5Y??@@#EErBz/api/logs/log_idctj}td|d|tj||\}}|rt d|ddfSt d|ddfS)u Endpoint API per modificare un evento esistente. 'PUT' è il metodo standard per l'aggiornamento di una risorsa esistente. z.==> Ricevuta richiesta PUT per modificare log rMTrrSFrr r=r0rmodifica_aggiornamentor )rmrZrUrVs r6api_update_logrqs lO V6 V V_ V VWWW(?XXHiF4I>>??DD5Y??@@#EErBctd|tj|\}}|rtd|ddfSd|vrtd|ddfStd|ddfS) ur Endpoint API per eliminare un evento esistente. 'DELETE' è il metodo standard per la cancellazione. z0==> Ricevuta richiesta DELETE per eliminare log TrrSzNessun evento trovatoFrrr0relimina_aggiornamentor )rmrUrVs r6api_delete_logrus  EV E EFFF(>vFFHiF4I>>??DD #i / /uCCDDcI I5Y??@@#EErBz/api/distributorsctjdd}tjdddk}t j||}t |S)a] Endpoint API per recuperare distributori. Se viene passato il parametro 'nome', restituisce un singolo distributore (lookup esatto). Altrimenti restituisce la lista completa filtrata. Query Parameters: - nome (string, opzionale): Nome distributore per lookup esatto - includi_gemma (bool, opzionale): Include distributori da Gemma Response per nome singolo: { "id_distributore": 5, "nome": "Blue Fox" } Response per lista: [ {"id_distributore": 1, "nome": "Fox", ...}, {"id_distributore": 2, "nome": "Eagle", ...} ] rrrrr)r rr.rrget_distributori_listar )rrrs r6api_get_distributorsrxse0 <  FB ' 'DL$$_g>>DDFF&PM"8}MML <  rBz/api/lineup-contesticFtj}t|S)z]Restituisce i contesti che hanno almeno un evento LINE-UP (+ flag per quelli senza contesto).)rget_contesti_con_lineupr )r6s r6api_lineup_contestir{1s  1 3 3D 4==rBz/api/lineup-by-contestoctjdd}|st gSt j|}t |S)zRestituisce tutti gli eventi LINE-UP per un dato contesto (mercato). Passare contesto=__nessuno__ per estrarre LINE-UP senza contesto.rr)r rr.r/r rget_lineup_by_contesto)rr6s r6api_lineup_by_contestor~8sW| B//5577H r{{  0 : :D 4==rBz%/api/distributor/distributor_idctd|tjdddk}t j|}|tddidfSt j|d }t j |d }t||zd }t j ||}t|||d S) zCEndpoint API per recuperare i dettagli completi di un distributore.z)==> Richiesta dettaglio distributore ID: include_prodottirrNrzDistributore non trovatorrcT|dpdSNtitolo_ufficialerr.rr_s r6z-api_get_distributor_details..Ss$quu/006B==??rBkeyrprodottieventi) r0r rr.rrget_dettaglio_distributorer !get_prodotti_by_distributore_nomeget_prodotti_distributore_gemmasortedget_eventi_distributore)rrr prodotti_mtprodotti_gemmarrs r6api_get_distributor_detailsrCs Fn F FGGG|''(:GDDJJLLPVV!<^LLL!;<==sBB B Richiesta dettaglio distributore per nome: z , fonte: rdNr+Distributore non trovato nel database GemmarcT|dpdSrrrs r6rz-api_get_distributor_by_name..m$quu%788>BEEGGrBr num_prodottirrcT|dpdSrrrs r6rz-api_get_distributor_by_name..rrBid_distributore)rrrrz&Distributore non trovato in MediaTrackcT|dpdSrrrs r6rz-api_get_distributor_by_name..rrBz1==> Chiamata get_eventi_by_distributore_nome per z ==> Trovati z eventirz+==> ERRORE in api_get_distributor_by_name: zErrore interno del server: r)r rr.r0r get_dettaglio_distributore_gemmar rrrrget_eventi_by_distributore_nomeget_distributore_by_nomer1rrrm) nome_distributorerrrrrr dist_recordrSr error_traces r6api_get_distributor_by_namer]sYO#L,,VR88   ,77 c@Qcc\accddd G  )JK\]]L#)VWXXZ]]]+KL]^^N(JK\]]K,GGH,/x==L (#CDUVVF ,$   j (JK\]]K+KL]^^Nn,GGH )ABSTTK*# #H IT#^;??3D#E#E#EZ^ L$CDUVVF ,$  )JK\]]K Y)QRSSUXXX(ABSTTK+KL]^^Nn,GGH *% #H IT#^;??3D#E#E#EZ^ L YFWYY Z Z Z#CDUVVF 5V555 6 6 6 ,$   OOO**,,  DCFFDDEEE k!Gs1vv!G!GHII3NNNNNNN Os:BJA5Jrctd|tj|}|tddidfStj|}t||gdS)zMEndpoint API per recuperare i dettagli di un distributore dal database Gemma.z,==> Richiesta dettaglio distributore Gemma: Nrrrr)r0rrr r)rrrs r6!api_get_distributor_gemma_detailsrs L9J L LMMM!BCTUUL!NOPPRUUU=>OPPH $  rBctj}td|tj||\}}|rt d|ddfSt d|ddfS)z6Endpoint API per modificare i dati di un distributore.z7==> Ricevuta richiesta PUT per modificare distributore TrrSFr)r r=r0rmodifica_distributorer )rrZrUrVs r6api_update_distributorrsxlO TN T TUUU(>~__HiF4I>>??DD5Y??@@#EErBz+/api/distributor//eventctj}td|tj||\}}|rt d|ddfSt d|ddfS)z=Endpoint API per aggiungere un evento a livello distributore.zB==> Ricevuta richiesta POST per aggiungere evento al distributore TrriFr)r r=r0raggiungi_evento_distributorer )r dati_eventorUrVs r6api_add_distributor_eventrsy,K _~ _ _```(EnVabbHiF4I>>??DD5Y??@@#EErBz/api/distributor-by-name/eventctjdd}tj}t d| t j|}t j||\}}|rtd||ddfStd|dd fS#t$r+}tdt|dd fcYd }~Sd }~wwxYw) z}Aggiunge un evento a un distributore identificato per nome. Crea automaticamente il record in Distributori se non esiste.rrz0==> Aggiunta evento per distributore (by name): T)rrrriFrrN) r rr.r=r0rget_or_create_distributorerr r1rm)rrrrUrVrSs r6!api_add_distributor_event_by_namers ((44,K P=N P PQQQ C(CDUVV,I/[fgg)  Jt Veffggill luCCDDcI I CCC5SVV<<==sBBBBBBBCs%ABB C ! CC C z/api/event/event_idctj}td|tj||\}}|rt d|ddfSt d|ddfS)z@Endpoint API per modificare un evento (prodotto o distributore).z1==> Ricevuta richiesta PUT per modificare evento TrrSFrro)rrrUrVs r6api_update_eventrsx,K Hh H HIII(?+VVHiF4I>>??DD5Y??@@#EErBctd|tj|\}}|rtd|ddfStd|ddfS)z?Endpoint API per eliminare un evento (prodotto o distributore).z3==> Ricevuta richiesta DELETE per eliminare evento TrrSFrrs)rrUrVs r6api_delete_eventrso J J JKKK(>xHHHiF4I>>??DD5Y??@@#EErBz/api/product/insertcZ tj td stddddfSd vrd vr d} d}d}nމ d d d  d  d  d  d d d d d d d d dd} dd d d d d d d d d!}d"}t fd#|D }|dstdd$ddfS|d }|r,t j|stdd%|d&ddfSt j|||'\}}}|rtd(||d)d*fStd|ddfS#t$r@}td+|tdd,t|dd-fcYd.}~Sd.}~wwxYw)/u= Crea un nuovo prodotto con anagrafica ed evento iniziale. Supporta due formati di request body: Formato 1 (strutturato): { "anagrafica": { "titolo_ufficiale": "XENO", "tipo": "Film", "distributore_nome": "BLUE FOX", "codice_imdb": "tt27629220", "budget_produzione": "7mln" }, "evento": { "id_tipo_evento_fk": 1, "data_evento": "2025-02-17", "dettaglio_contesto": "EFM 2025", "costo_richiesto_ita": 75000.0, "note": "...", "buyer_contatto": "Andrea" } } Formato 2 (flat - retrocompatibilità): { "titolo_ufficiale": "XENO", "tipo": "Film", "distributore_nome": "BLUE FOX", ... } Response Success (201): { "success": true, "message": "Prodotto creato con successo.", "id_media": "FILM-1759744791528" } Response Error (400/500): { "success": false, "message": "Errore: [descrizione]" } z0==> POST /api/products: Creazione nuovo prodottoFzRequest body mancanterrreventortipoFilmdistributore_nomerbudget_produzioneformato piattaformaanno_produzioneduratapaesigeneriregistiattoritrama)rrrrrrrrrrrrrrid_tipo_evento_fkr data_eventodettaglio_contestocosto_richiesto_itacosto_richiesto_spanotebuyer_contatto percorso_file)rrrrrrrr)rrrrrrc3BK|]}|VdSr)r.).0rdatis r6 z%api_create_product..rs-!E!E!$((1++!E!E!E!E!E!ErBz%Campo 'titolo_ufficiale' obbligatoriozIl distributore 'zD' non esiste in Gemma. Seleziona un distributore valido dalla lista.) skip_eventoT)rrid_mediarizErrore in api_create_product: Errore server: rN) r r=r0r r.anyris_distributore_in_gemmainserisci_prodotto_completor1rm) dati_anagraficarr evento_fieldsrrUrVrrSrs @r6api_create_productrs`DW| ABBB Xu9PQQRRTWW W 4  H$4$4"<0Ox.KKK&*XX.@%A%A%)XXff%=%=%)XX.A%B%B%)XXm%<%<%)XX.A%B%B%)XXi%8%8%)XXm%<%<%)XX.?%@%@%)XXh%7%7%)XXg%6%6%)XXh%7%7%)XXi%8%8%)XXh%7%7%)XXg%6%6O"&*XX.A1%E%E#xx 66&*hh/C&D&D'+xx0E'F'F'+xx0E'F'F(("&((+;"<"#-66)5?4'2< '     9-..33 ??? 333444!78993>>>>>>>?sC C D$D=DDz /api/buyersc tj}td|idfS#t$r/}t d|tddidfcYd}~Sd}~wwxYw)z Recupera lista buyer unici da LogAggiornamenti. Response (200): { "buyers": ["Andrea", "Clemente", "Mercedes"] } Response Error (500): { "error": "Errore nel recupero buyer" } rrSzErrore in api_get_buyers: rzErrore nel recupero buyerrN)rrr r1r0)rrSs r6api_get_buyersrsD022&)**C// DDD .1..///!<=>>CCCCCCCDs%( A!$AA!A!z!/api/maintenance/products-no-imdbc tj}t|dfS#t$r/}t d|tddidfcYd}~Sd}~wwxYw)zG Endpoint per ottenere la lista di prodotti senza codice IMDb. rSz$Errore in api_get_products_no_imdb: rz Errore nel recupero dei prodottirN)rget_prodotti_senza_imdbr r1r0)rrSs r6api_get_products_no_imdbrs K!9;;x  #%% KKK 8Q88999!CDEEsJJJJJJJKrz/api/omdb/searchctjd}tjd}|stddidfS t j||}|tgdfSt|dfS#t $r?}td |tdd t|id fcYd}~Sd}~wwxYw) zV Endpoint per cercare titoli su OMDB. Restituisce una lista di risultati. titletyperzParametro 'title' mancanter)rNrSzErrore in api_omdb_search: rr) r rr.r rcerca_imdb_per_titolor1r0rm)rrrrSs r6api_omdb_searchrs \  g & &F <  F # #D E!=>??DDC"8dKKK  2;;# #y!!3&& CCC /A//000!;3q66!;!;<==sBBBBBBBCs$(B>B C4C CCz/api/omdb/lookupctjdd}|st ddidfS t j|}|r|ddkr(t d|d d d d fSt d |dd fS#t$r*}t dt|idfcYd}~Sd}~wwxYw)z1Fetch dati OMDB per IMDb ID senza salvare nel DB.rRrrzParametro 'imdb_id' mancanterResponseFalseFErrorz Non trovato)foundrrST)rrrN) r rr.r/r rscarica_dati_omdbr1rm)rRrrSs r6api_omdb_lookuprsly"--3355G G!?@AA3FF//88 ]txx ++w66UTXXg}5U5UVVWWY\\ \t4455s:: ///Q())3......./s% AB4 B44 C(>C#C(#C(z/api/maintenance/pre-scan-imdbcT tdtj}t|dd}td|t dd|ddfS#t $r@}td |t d d t |d d fcYd}~Sd}~wwxYw)z] Avvia la scansione preliminare per contrassegnare i prodotti non trovabili su IMDb. z7==> Avvio scansione preliminare IMDb (fast, solo DB)...r)checkedflaggedz&==> Scansione preliminare completata: Tz!Scansione preliminare completata.)rrreportrSzErrore in api_pre_scan_imdb: FrrrN)r0rrrr r1rm)rrrSs r6api_pre_scan_imdbrs W GHHH!9;; ]]q99 ?v??@@@:        WWW 1a1122255Os1vv5O5OPPQQSVVVVVVVVWsAA B''5B"B'"B'z$/api/maintenance/mark-imdb-not-foundcN tjpi}|d}|stddddfSt j|\}}|rdnd}t||d|fS#t $r.}tddt|dd fcYd }~Sd }~wwxYw) zCMarca un prodotto come non trovabile su OMDB (imposta status = -1).rFCampo 'id_media' mancanterrrSrrrN)r r=r.r rmarca_imdb_non_trovabiler1rmrrrUrVstatusrSs r6api_mark_imdb_not_foundr! W|!r88J'' \u9TUUVVX[[ [,EhOO) )c8 BBCCVKK WWW55Os1vv5O5OPPQQSVVVVVVVVW"8A,0A,, B$6#BB$B$z /api/maintenance/mark-imdb-foundcN tjpi}|d}|stddddfSt j|\}}|rdnd}t||d|fS#t $r.}tddt|dd fcYd }~Sd }~wwxYw) z>Marca un prodotto come trovabile su OMDB (imposta status = 1).rFrrrrSrrrN)r r=r.r rmarca_imdb_match_trovator1rmrs r6api_mark_imdb_foundr0rrz/api/screenplays/scanc tjpi}|d}|stddddfSt d|t j|}|drt d|d d t j}|}| d d | D}| d }|dgD]}|d|v|d<|dr|dz }||d<t d|t|dfSt|dfS#t$rU}t d|d dl } | tdt|ddfcYd}~Sd}~wwxYw)a Scansiona una directory per trovare tutti i file PDF sceneggiature. Request body: { "root_directory": "C:\Path\To\Screenplays" } Response: { "success": true, "pdf_count": 42, "pdfs": [ { "filename": "Inception.pdf", "full_path": "C:\..\Inception.pdf", "relative_path": "Fornitor\Inception.pdf", "parent_folder": "FernitoreA", "size_mb": 2.5 }, ... ] } root_directoryFzDirectory radice mancanterrz,[SCREENPLAY] Richiesta scansione directory: rz[SCREENPLAY] Trovati pdf_countz PDFz SELECT DISTINCT percorso_file FROM LogAggiornamenti WHERE percorso_file IS NOT NULL AND percorso_file != '' ch|] }|d S)rr)rrs r6 z'api_scan_screenplays..wsAAA3AAAArBrpdfs full_path associatedrassociated_countu$[SCREENPLAY] Di cui già associati: rSz[ERROR] api_scan_screenplays: Nr)r r=r.r r0rscan_pdf_directoryrrrfetchallrr1rrrm) rrresultconnrassociated_pathsr pdfrSrs r6api_scan_screenplaysrBsK61|!r"233  4  M^MMNNN 2>BB )  ( C&*=CCC D D D"'))D++--C KK    BA#,,..AAA  JJLLL ! zz&"-- * *$' $48H$HL!|$*$)$)9F% & K9IKK L L L6??C' '6??C' '  2q22333VV            s*8EDE E F=(A F82F=8F=z/api/screenplays/associatecL tjpi}|d}|d}|d}|r|r|stddddfSdd l}|j|stdd |dd fStd |d |d|d|dr|dd }tj }| }| d|f| sl|dd}tjd|dd} | jr,|tdd|ddd fS| jd} | ddt'| dz| dt'| dpd| d| d f||jdkr,|tdd|ddd fStd!|d"|dd#lm} tj }| }| d$| } || stdd%ddfS| d} dd l}| d&}tj }| }| d'|| |f| }|r|\}} |r<|d(r||n|r|gng}n#t8$r |r|gng}YnwxYw||vr||| d)|||f||td*|d+|td,d-||d.d/fS|| ||d0|d|dd d d d d d dd dd1 }tj||\}}|r*td2|td,d3||d.d/fStd4|td|dd5fS#t8$rU}td6|dd l }|!tdt'|dd5fcYd }~Sd }~wwxYw)7a Associa una sceneggiatura PDF a un prodotto MediaTrack/Gemma. Crea un evento di tipo SCENEGGIATURA (id_tipo_evento_fk=3). Request body: { "pdf_path": "C:\Path\To\screenplay.pdf", "product_id": "MT_00123", "product_title": "Inception" } Response: { "success": true, "event_id": 456, "message": "Sceneggiatura associata con successo" } pdf_path product_id product_titleFzAParametri mancanti: pdf_path, product_id, product_title richiestirrrNzFile PDF non trovato: rz)[SCREENPLAY] Associazione sceneggiatura: z -> z ()rrz7SELECT id_media FROM AnagraficaMedia WHERE id_media = ?r(''r&z7A_ID, A_TITOLO, A_TIPOLOGIA, A_DISTRIBUTORE, A_COD_IMDBr*zProdotto Gemma 'z ' non trovatoz{INSERT OR IGNORE INTO AnagraficaMedia (id_media, titolo_ufficiale, tipo, distributore_nome, codice_imdb) VALUES (?,?,?,?,?)A_IDA_TITOLO A_TIPOLOGIAFILMA_DISTRIBUTOREr)z([SCREENPLAY] Auto-import prodotto Gemma z in AnagraficaMedia)rz SELECT id_tipo_evento FROM TipiEvento WHERE UPPER(nome_evento) LIKE '%SCENEGGIATURA%' OR UPPER(nome_evento) LIKE '%SCREENPLAY%' LIMIT 1 zYTipo evento 'Sceneggiatura' non trovato nel database. Crealo prima in Editor Tipi Evento.z%Y-%m-%dz SELECT id_aggiornamento, percorso_file FROM LogAggiornamenti WHERE id_media_fk = ? AND id_tipo_evento_fk = ? AND date(data_evento) = ? ORDER BY id_aggiornamento DESC LIMIT 1 [zHUPDATE LogAggiornamenti SET percorso_file = ? WHERE id_aggiornamento = ?z.[SCREENPLAY] File aggiunto a evento esistente z per Tz-Sceneggiatura aggiunta a evento esistente di )rrrrSz Associato a: ) rrrrrrrrr data_reminder preferito venduto_aprivatoz,[SCREENPLAY] Evento creato con successo per zSceneggiatura associata a z&[SCREENPLAY] Errore creazione evento: rz"[ERROR] api_associate_screenplay: )"r r=r.r r|pathr+r0r rrrrrrr-r!rr"rmuppercommitrowcountrnowrr/r,r1appenddumpsrjrr)rrrrr|a_idr ra_id_escr0rrrid_tipo_sceneggiaturar4rFexisting existing_idexisting_filesfilesrrrrSrs r6api_associate_screenplayr0sB*X|!r88J''XXl++ 11  z   \   w~~h''  <(<<  f(ff ffYcfffggg   & & abb>D!'))D++--C KKQT^S` a a a<<>> b<<T22$.0X000U9vJJLLL"u?nR_?n?n?n#o#oppruuuHQK Rs1V9~~-j))AEE-00:F;;AACC.//l++     <1$$JJLLL"u?nR_?n?n?n#o#oppruuu````aaa JJLLL &%%%%%#%%kkmm     llnn   t  !$A '' 33#%%kkmm  / 7  9 9 9 <<>>  *2 'K C7Ec.J^J^J`J`JkJkloJpJpc N333LZxbyGxHxH`b C C C,:B(( Cu$$ X&&& KKbU++[9 ; ; ; KKMMM JJLLL a;aaU_aa b b bZ=ZZ(   "7 %BMBBZBBB"&"#'#'!%!  "*@[YY   MMM N N NG GG(   D7DD E E E     6166777VV            stA&U)4UC.U C5UB&U*BU9AO?>U?PUPBU/A.U%U V#A VV#V#z/api/screenplays/statsc$ tj}|}|d|d}|d|d}|t d||ddfS#t$rU}td|ddl }| t d t|d d fcYd}~Sd}~wwxYw) z Restituisce statistiche sulle sceneggiature associate. Response: { "success": true, "total_count": 42, "unique_products": 38 } zo SELECT COUNT(*) FROM LogAggiornamenti WHERE id_tipo_evento_fk = 3 rz SELECT COUNT(DISTINCT id_media_fk) FROM LogAggiornamenti WHERE id_tipo_evento_fk = 3 T)r total_countunique_productsrSz[ERROR] api_screenplay_stats: NFrr) rrrrrrr r1r0rrrm)r rr2r3rSrs r6api_screenplay_statsr4Ase$#%%     oo''*      !//++A. &.         2q22333VV            sB-B00 D:A D D Dz&/api/screenplays/search-by-distributorc b tjdd}t tjdd}t |dkrt dddd fStj}| }g}| d d |d |f| D]5}| |d |d |d|ddd6| |dd}tjd|dd}|js|d|}|D]o\}}| dt)|dz|d|d|dddp|dt d|d |t |d!d"fS#t,$rU} t/d#| d d l} | t dt)| dd$fcYd } ~ Sd } ~ wwxYw)%a> Cerca tutti i prodotti (MediaTrack e Gemma) per distributore. Query Parameters: - distributor (string, required, min 2 caratteri) - limit (int, opzionale, default 50) Response: { "success": true, "results": [ { "id": "MT_123" o "GEM_456", "titolo": "Inception", "source": "mediatrack" o "gemma", "distributore": "Warner Bros", "anno": "2010", "codice_prodotto": "..." } ], "count": 42 } distributorrlimit2rFz$Nome distributore minimo 2 caratterirraN SELECT id_media, titolo_ufficiale, distributore_nome, tipo FROM AnagraficaMedia WHERE LOWER(distributore_nome) LIKE LOWER(?) AND id_media NOT LIKE 'G_%' ORDER BY titolo_ufficiale LIMIT ? %rrrCr)rrrrsourcer(rz#LOWER(A_DISTRIBUTORE) LIKE LOWER('%z%')z+A_ID, A_TITOLO, A_DISTRIBUTORE, A_TIPOLOGIAr*rrrrrrdc:|dpdS)Nrr)r)r*s r6rz+api_search_by_distributor..sAhK$52#<#<#>#>rBrTN)rrcountrSz#[ERROR] api_search_by_distributor: r)r rr.r/intrr rrrrr r&rrr-r! sort_valuesheaditerrowsrmsortr1r0rr) distributor_queryr7r rrrdq_escr0rrSrs r6api_search_by_distributorrDus54H#L,,]B??EEGGGL$$Wb1122  ! !A % % ?  #%%   '#&&& . 0 0 0??$$  C NN!fa& #AA&      #**355&CCCCA   y //*--22599C,,..  3S[!1!11!ggj11$'GG,<$=$=GGM22%     >> ???vv\\         7A77888VV            s&BIGI J.A J)#J.)J.z/api/products/search-smartc< tjdd}tjdd}tjddt}|rt |d krt d d id fSt j||| }t |dfS#t$rW}td|ddl }| t d dt|idfcYd}~Sd}~wwxYw)a Ricerca intelligente real-time per search-as-you-type. Cerca in MediaTrack e Gemma con substring matching. AGGIORNATO: Post-migrazione V11 - usa distributore_nome (fonte unica: Gemma) Query Parameters: - titolo (string, required, min 2 caratteri) - distributore_nome (string, opzionale) - Nome distributore per filtrare - distributore_id (int, DEPRECATO) - Ignorato per backward compatibility - limit (int, opzionale, default 10) Esempio: /api/products/search-smart?titolo=xen&distributore_nome=Blue+Fox Response (200): { "mediatrack": [ { "id_media": "FILM-123", "titolo_ufficiale": "XENO", "tipo": "Film", "anno": "2024", "distributore_nome": "Blue Fox", "codice_imdb": "tt12345" } ], "gemma": [ { "id": "G_ABC123", "titolo": "XENO", "distributore": "Blue Fox", "tipo": "Film", "codice_imdb": "tt12345" } ] } Response Error (400): { "error": "Titolo minimo 2 caratteri" } rrrN)defaultr7 )rFrrrzTitolo minimo 2 caratterir)rrr7rSz%Errore in api_search_products_smart: rrr) r rr.r=rr rricerca_prodotto_smartr1r0rrrm)rrr7rrSrs r6api_search_products_smartrIsMXC!!(B//#L,,-@$,OO   "3 ?? HVqG%@ABBCG G"9/   y!!3&& CCC 9a99:::!;3q66!;!;<==sBBBBBBB Cs%BB:'B:: DA DDDz /api/poster/cddlm}|tjjddz dz |dz }|rt t|dStj |}|rt|Sd S) z Serve la locandina locale (local_db/posters/{codice_imdb}.jpg). Se non ancora scaricata, redirect all'URL OMDB dal DB. rrWrCrcpostersz.jpgz image/jpeg)mimetype)rr) rwrXapp root_pathrer+rrmrget_poster_url_by_imdbr)rrX poster_pathurls r6 api_posterrR(s $s}%%-a0:= I{L`L`L``KB[))LAAAA  / < >>!CDEEsJJJJJJJ Ks!$ A5> z""" III 6166777!ABCCSHHHHHHH IsAA B#>A   >;;<<< y>>A   ;Yq\;; < < <"^^        AAA 616677753q66::;;S@@@@@@@ Arwz/api/favorites/countc tjd}tj|}t d|ddfS#t $r=}t d|t dt|ddfcYd }~Sd }~wwxYw) z?Recupera solo il conteggio dei preferiti per l'utente corrente.rsTrr<rSz$[ERROR] Errore conteggio preferiti: FrrN)rr.rcount_favorite_eventsr r1r0rmrsr<rSs r6api_get_favorites_countrs A+i((4W==       AAA 8Q8899953q66::;;S@@@@@@@A;> B2B:BBz/api/reminders/countc tjd}tj|}t d|ddfS#t $r=}t d|t dt|ddfcYd }~Sd }~wwxYw) z>Recupera solo il conteggio dei reminder per l'utente corrente.rsTr}rSz#[ERROR] Errore conteggio reminder: FrrN)rr.rcount_reminder_eventsr r1r0rmrs r6api_get_reminders_countrs A+i((4W==       AAA 7A7788853q66::;;S@@@@@@@Arz/api/export/excelc  ddlm}tjpi}|di}|dd}|d}t dt d|t d |d t |d t d ||r)tjd }tj ||tj ||}|stddddfS| |||}| |dz}t dt|d|ddl} ddlddl} | } j| |t+d5} | |dddn #1swxYwY| fddtdd||dS#t4$rU} t d| ddl}|tdt;| ddfcYd} ~ Sd} ~ wwxYw)a Genera file Excel con prodotti filtrati. Request Body: { "filtri": { "distributori": ["Warner Bros", "Universal"], // opzionale "tipologia": "Film", // opzionale "data_da": "2025-01-01" // opzionale }, "includi_gemma": true, // default: false "preset_id": 5 // opzionale (per update last_used) } Response (200): - File Excel scaricabile - Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet - Content-Disposition: attachment; filename="MediaTrack_Export_20251209_143522.xlsx" r)excel_generatorfiltrirF preset_idz[EXPORT API] Received request:z filtri: z includi_gemma: z (type: rz preset_id: rsz0Nessun prodotto trovato con i filtri selezionatirrz.xlsxz[EXPORT] Generato Excel con z prodotti: rNwbc$jSr) startfile)r|tmp_pathsr6rz"api_export_excel...s  X(>(>rBT)r9r:z File aperto: )rrfilenamez[ERROR] Errore export Excel: rr)rrr r=r.r0rrrupdate_preset_last_usedget_export_datar generate_export_excelget_export_filenamertempfiler|r< gettempdirr!joinopenwritereadr>r?r1rrrm)rrrrrrsr excel_filerrr<tmp_dirrrSrr|rs @@r6api_export_excelrs9,1A%%%%%%|!r(B''%88 HH[))  /000 #6##$$$ O-OOm9L9LOOOPPP )i))***  Gk),,G  29g F F F"1&-HH  M  %::8V][[ #66v>>H QS]]QQxQQRRR '&&&&&&&&&&&%%''7<<22 (D ! ! 'Q GGJOO%% & & & ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' > > > > >tLLRRTTT44NH4N4N\deefff AAA 1a1122253q66::;;S@@@@@@@ AsKDH#BH#!(G H#GH#GAH## J-A I=7J=Jz/api/export/presetsc@ tjd}tj|}t d|ddfS#t $rU}t d|ddl}|t dt|d d fcYd}~Sd}~wwxYw) a7 Recupera tutti i preset export dell'utente corrente. Response (200): { "success": true, "presets": [ { "id_preset": 1, "nome_preset": "Export Settimanale Warner", "filtri_json": "{"distributori": ["Warner Bros"], "tipologia": "Film"}", "includi_gemma": false, "is_default": true, "data_creazione": "2025-12-09 10:30:00", "data_ultimo_uso": "2025-12-09 14:00:00" }, ... ] } rsT)rpresetsrSz [ERROR] Errore recupero preset: rNFrr) rr.rget_user_export_presetsr r1r0rrrm)rsrrSrs r6api_get_export_presetsr9s. A+i(( 8AA       AAA 44455553q66::;;S@@@@@@@ As;> BA BBBc ddl}tjd}tjpi}|dd}|di}|dd}|d d}|st dd d d fS||}tj|||||\}} } |rt d | | ddfSt d| d d fS#t$rU} td| ddl } | t dt| ddfcYd} ~ Sd} ~ wwxYw)a Crea nuovo preset export. Request Body: { "nome_preset": "Export Settimanale Warner", "filtri": { "distributori": ["Warner Bros"], "tipologia": "Film", "data_da": "2025-01-01" }, "includi_gemma": false, "is_default": false } Response (200): { "success": true, "message": "Preset salvato con successo", "preset_id": 5 } rNrs nome_presetrrrF is_defaultzNome preset obbligatoriorrT)rrrrSz![ERROR] Errore creazione preset: rr)r=rr.r r/r r'rcreate_export_presetr1r0rrrm) r=rsrrrrr filtri_jsonrrrrSrs r6api_create_export_presetr`s2*A +i((|!rhh}b117799 (B''%88 XXlE22   5  jj(( '5&I [+}j' ' #)  "&    "  AAA 5!5566653q66::;;S@@@@@@@ As,B%D(AD0D E#A EE#E#z#/api/export/presets/cz ddl}tjd}tjpi}|d}|d}|d}|d}|||nd}t j||||||\} } | rtd| d d fStd | d d fS#t$rU} td | ddl } | td t| ddfcYd} ~ Sd} ~ wwxYw)a  Aggiorna preset esistente (con verifica ownership). Request Body: { "nome_preset": "Export Settimanale Warner (Aggiornato)", // opzionale "filtri": {...}, // opzionale "includi_gemma": true, // opzionale "is_default": true // opzionale } Response (200): { "success": true, "message": "Preset aggiornato con successo" } rNrsrrrrTrrSFrz%[ERROR] Errore aggiornamento preset: rr) r=rr.r r'rupdate_export_presetr r1r0rrrm) rr=rsrrrrrrrrrSrs r6api_update_export_presetrs("A +i((|!rhh}-- (##11 XXl++ -3,>djj(((D *> w [-    "   "  AAA 9a99:::53q66::;;S@@@@@@@ As%CCC D:%A D5/D:5D:ct tjd}tj||\}}|rt d|ddfSt d|ddfS#t $rU}t d|dd l}|t dt|d d fcYd }~Sd }~wwxYw) z Elimina preset (con verifica ownership). Response (200): { "success": true, "message": "Preset eliminato con successo" } rsTrrSFrz$[ERROR] Errore eliminazione preset: rNrr) rr.rdelete_export_presetr r1r0rrrm)rrsrrrSrs r6api_delete_export_presetrsA+i(()>y'RR  "   "  AAA 8Q8899953q66::;;S@@@@@@@ As%AAA B7"A B2,B72B7z/c"td|S)z#Serve i file statici come CSS e JS.r)r )rs r6 serve_staticr s }h 7 77rB__main__Fi)rport)r)osyshasattrstdoutrr\rr r r r r rrrrr functoolsrr|r~logurur__name__rM secret_keyconfigrAr}r.rUrxrrrouterrrrrrrrrrrrrrrrmrr$r2r>rLrPrWr[r`rgrlr=rqrurxr{r~rrrrrrrrrrrrrrrrrrr0r4rDrIrRrUrYr^rbrfrhrjrlrorqrvr{rrrrrrrrrunrrBr6rsH  73:}%%?JGI>>>||||||||||||||||||||||  e !C(, $%(- $% 8:>>"##s** OOO, %Q%Q%QNXXX ug../. x00=6(++&&,+10&  5'22RR32R2 0010?UG,,//-,/ ?VH-- A A.- A ,ug>> A A?> A ,xjAAAABAA w//FF0/F x00'A'A10'AT 2UGDD]]ED]4 2XJGG \ \HG \" vh// + +0/ + QQQ,?///, +,, Uc U U U-, UL S     JJJJJ, 344!/c!/!/!/54!/H 2336/S6/6/6/436/x "VH55CC65C 7&JJC3CCCKJC2 +eW== FC F F F>= F EPVxXXBcBSBBBYXB +hZ@@;;;;A@;| 06(CCFSFFFDCF( #eW55 F3 F F F65 F #hZ88F3FFF98F* !! !< !""#"  $%%&% 233432 %&&[O[O'&[Oz >??@?  2UGDD F3 F F FED F 86(KK Fc F F FLK F +fX>>CC?>C$ &88 Fs F F F98 F & ;; Fs F F F<; F?VH-- 6(33rWrW43.-rWl !"" +,,0?0?-,#"0?f=DDD4 .// K K0/ K CCC(  / / / +fX>>WW?>W$ 1F8DD W WED W -x@@ W WA@ W" "VH55JJ65JZ '&::kk;:k\ #eW55//65/d 3eWEE``FE`N '((ACAC)(ACR -.. C   /. ( !"" K K#" K $%% I I&% I> 3)))>  %&&66'&6 !""22#"2 // / // / // / eW--AA.-A8 eW--AA.-A8 !E733 A A43 A  !E733 A A43 A( 11EAEA21EAP 5'22"A"A32"AJ 6(33AAAA43AAH 05'BB4A4ACB4An 08*EEAAFEAF 888  zGG%dG#####rB