
    )9i&                        d dl mZ d dlmZ d dlmZmZ d dlmZ d dlZd dl	m
Z
 d dlZd dlmZ d dlZd dlmZ d dlZd d	lmZ d d
lmZmZmZmZ d dlmZ d dlZd dlmZ d dlmZ d dlm Z m!Z!m"Z"m#Z#m$Z$m%Z%m&Z& d dl'm(Z(m)Z)m*Z*m+Z+m,Z,m-Z-m.Z.m/Z/ d dl0m1Z1m2Z2m3Z3 d dl4m5Z5 d dl6m7Z7 d dl8m9Z9m:Z: d dl;m<Z<  e       Z=dZ>h dZ?h dZ@h dZAh dZBh dZCh dZDh dZEde$deFfdZGd e9d!eFd"eFd#eFd$eFd%eHeFeIf   ddfd&ZJ ed'(       G d) d*             ZKd+eFdeFfd,ZLd+eFdeMeF   fd-ZNd.eFd/eFdeOfd0ZPd1eFd2eFdeQfd3ZRed e9fd4       ZSd5ej                  d6eFdeUeF   fd7ZVd e9deMeK   fd8ZWd9eFdeOfd:ZXd2eFdeFdz  fd;ZYd9eFdeFdz  fd<ZZd1eFdz  deFdz  fd=Z[d>eKdeUeF   fd?Z\d@eMeK   d9eFdeMeK   fdAZ]d@eMeK   d1eFdz  deMeK   fdBZ^d+eQdz  deFfdCZ_d>eKdeFfdDZ`dEe%d@eMeK   d1eFdz  de$fdFZadEe%d@eMeK   d1eFdz  de$fdGZbd e9dEe%de$dz  fdHZcd5e!dz  deMeF   fdIZdd5e!dz  deMeF   fdJZedKeFdz  deMeF   fdLZfd5e!dMeFddfdNZgdOeFdz  deFfdPZhdddQdReFdOeFdz  dSeFdz  deFfdTZidUeFdeHeFef   fdVZjdEeHeFef   dekeMeF   eMeMeF      f   fdWZlddXdEe%d e9dz  de$fdYZmdZej                  d[eFd\eFd]eFddf
d^ZodZej                  d[eFd_eFdeFfd`ZpdEe"d5e!de#fdaZqdEe"d5e!de#fdbZre=j                  dce&d       ee      fd e9de&fde       Zte=j                  dfe d       edgh       edh       ee      fdOeFdz  dKeFdz  d e9de fdi       Zue=j                  dj      	 	 	 	 dvdkeFdz  dleFdz  dmeFdz  dneFdz  def
do       Zve=j                  dp       ee      fd e9deHeFeOf   fdq       Zxe=j                  dre$d       ee      fdEe%d e9de$fds       Zye=j                  dte#d       ee      fdEe"d e9de#fdu       Zzy)w    )contextmanager)	dataclass)datetimetimezone)SequenceMatcherN)Path)quote)Any)	urlencode)	APIRouterDependsHTTPExceptionQuery)RedirectResponse)require_session)get_settings)$GoogleWorkspaceAuthorizationResponseGoogleWorkspaceConnectionGoogleWorkspaceCreateRequestGoogleWorkspaceCreateResponseGoogleWorkspaceDocumentPreviewGoogleWorkspacePreviewRequestGoogleWorkspaceStatusResponse)GOOGLE_DOCS_ENDPOINTGOOGLE_DRIVE_ENDPOINTGOOGLE_SHEETS_ENDPOINTbuild_google_authorization_urlbuild_google_code_challengeexchange_google_code_for_tokensfetch_google_account_emailis_google_workspace_configured)!build_google_workspace_connection"ensure_google_workspace_configured&get_active_google_workspace_connection)get_google_workspace_store)request_llm_chat_completion)SessionIdentityget_tenant_store)get_assistant_profilez.https://www.googleapis.com/auth/gmail.readonly>4      piùaeiadaialdadihoilinlounchecondaldeideldocfaipiutraunaallaallodocsdalladellafammifoglilistasalvasheettuttatuttetuttituttofogliogeneragoogleprezziprezzoscrivipreparaprodottiprodotto	documenti	documento	economici	economico
economiche>   cenacibofoodcucinapranzomangiare
alimentariingredienti>   barvinivinobirrabirredrinkbevandabevandeliquorispiritscocktail
analcolici
distillati>   r_   dolcr`   panerisoverdcarnepastapescesalse	formaggio>   rf   softrh   wineri   rk   bevandliquorspiritanalcolrp   >   bunclubrt   ru   baconburromanzorx   pizzapollosalsatoastburgerfarinagambermaialepiadinasalmonepomodoro	bruschett	crocchettr{   
mozzarella>$   acq 	coca colaginliqrumcocasodarh   acquaamarori   fantasuccovodkaaperolbitterbrandycognacmezcalspritetisanatonicawhiskyamaricocachacacamparicrodinogiffardliquoretequilawhiskeychinottoprosecco	aranciata	gingerino
distillatopreviewreturnc                    | j                   xs dj                         }|r|S | j                  dk(  rS| j                  xs dj                         }dj	                  |j                               }|r|d d S d| j                   S | j                  r&d| j                   dt        | j                         dS d| j                   S )	N r=    i  zAnteprima Google Doc pronta: zAnteprima Google Sheet pronta: z con z righe.)	summarystripkindcontentjoinsplittitlerowslen)r   r   r   compacts       A/var/www/html/apps/backend-hub/app/api/routes/google_workspace.py_document_preview_reply_textr      s    $"++-G||u??(b//1((7==?+4C= .w}}o>>||0uSEVDWW^__,W]]O<<    sessionuser_promptassistant_replyroutemodeltracec           
          t               }|j                  | d      }|j                  |j                  d|dd|dg       |j	                  |j                  | d|||||       y )N	documents)surfaceuserroler   	assistant)	thread_idr   r   r   r   user_messager   r   )r(   ensure_assistant_threadappend_assistant_messagesidcreate_assistant_run)r   r   r   r   r   r   storethreads           r   _record_documents_runr      s|     E**7K*HF	##		4 _=	
 
)) '  	r   T)frozenc                   f    e Zd ZU eed<   eed<   eed<   eed<   edz  ed<   edz  ed<   edz  ed<   y)	CatalogProductr   product_namelot_codesupplier_nameNproduct_codefinal_price_vatcategory)__name__
__module____qualname__int__annotations__strfloat r   r   r   r     s6    GM*T\!Djr   r   valuec                     t        j                  d| xs d      j                  dd      j                  d      }|j	                         j                  dd      }t        j                  dd|      }dj                  |j                               S )	NNFKDr   asciiignoreu   ’'z
[^a-z0-9]+r   )
unicodedata	normalizeencodedecodelowerreplaceresubr   r   )r   
normalizedlowereds      r   _normalize_search_textr     ss    &&vu{;BB7HU\\]deJ ((4Gff]C1G88GMMO$$r   c                     t        |       j                  d      D cg c]  }|s|	 }}|D cg c]  }|t        vs| }}|xs |S c c}w c c}w )Nr   )r   r   _DOCUMENT_QUERY_STOPWORDS)r   tokentokensfiltereds       r   _tokenize_search_queryr  &  sV    !7!>!D!DS!ISUeSFS#)T%U:S-STHTv TTs   A	A	AAleftrightc                     | |k(  ryt        |       dk\  rt        |      dk\  r| d d |d d k(  ryt        t        |       t        |            dk\  xr t        d | |      j                         dk\  S )NT      g=
ףp=?)r   minr   ratio)r  r  s     r   _tokens_matchr  ,  sn    u}
4yA~#e*/d3Bi5":.Es4y#e*%*atT5/Q/W/W/Y]a/aar   query	candidatec                 J   t        |       }t        |      }|r|syt        |       }|j                         t        fd|D              }d|v r||v rdnd}t        fd|D              }|r|dk(  r|dk(  r|dk(  ry|dz  |z   |z   t	        d ||      j                         z   S )N        c              3   N   K   | ]  t        fd D              sd  yw)c              3   6   K   | ]  }t        |        y wN)r  .0candidate_tokenr  s     r   	<genexpr>z1_score_catalog_match.<locals>.<genexpr>.<genexpr><  s       5D_n]5/5Z  5Ds      N)anyr  r  candidate_tokenss    @r   r  z'_score_catalog_match.<locals>.<genexpr><  s2       E  5D  sC  5D  2D!  Es   %%r   g       @c              3   l   K   | ]*  t              d k\  st        fdD              s'd , yw)   c              3   @   K   | ]  }|j                          y wr  
startswithr  s     r   r  z1_score_catalog_match.<locals>.<genexpr>.<genexpr>>  s+       P[  wFP_PjPjkpPq  P[s   g      ?N)r   r  r  s    @r   r  z'_score_catalog_match.<locals>.<genexpr>>  s?       \uE
aC  P[  JZ  P[  M[s  \s   444r   )r   r  r   sumr   r  )	r  r  normalized_querynormalized_candidatequery_tokensoverlapphrase_bonusprefix_bonusr  s	           @r   _score_catalog_matchr+  4  s    -e41)<#7)%0L+113  E  E  EG!116FJ^6^3dgL  \  \  \L1):|q?PS=<',6O_au9v9|9|9~~~r   c              #      K   t        | j                        }t        j                  |      }t        j                  |_        |j                  d       	 | |j                          y # |j                          w xY ww)NzPRAGMA foreign_keys = ON;)r   database_pathsqlite3connectRowrow_factoryexecuteclose)r   r-  
connections      r   _connect_orders_databaser5  D  sb     ../M/J$[[J23
s   AA>A) A>)A;;A>r4  
table_namec                     | j                  d| d      j                         }|D ch c]  }t        |d          c}S c c}w )NzPRAGMA table_info()name)r2  fetchallr   )r4  r6  r   rows       r   _orders_table_columnsr<  P  sB     2:,a@AJJLD(,-CF---s   A c                    t        |       5 }t        |d      }d|v rdnd}d|v rdnd}d|v rdnd}|j                  d| d	| d	| d
      j                         }d d d        D cg c]  }t	        t        |d         t        |d   xs d      j                         t        |d   xs d      j                         t        |d   xs d      j                         |d   t        |d         j                         nd |d   t        |d         nd |d   t        |d         j                         nd        c}S # 1 sw Y   xY wc c}w )Nordini_productsr   zNULL AS product_coder   zNULL AS final_price_vatr   zNULL AS categoryz
            SELECT
                id,
                product_name,
                lot_code,
                supplier_name,
                z,
                z
            FROM ordini_products
            WHERE active = 1
            ORDER BY supplier_name ASC, product_name ASC, lot_code ASC
            r   r   r   r   r   )r   r   r   r   r   r   r   )	r5  r<  r2  r:  r   r   r   r   r   )r   r4  product_columnsselect_product_codeselect_final_price_vatselect_categoryr   r;  s           r   _load_catalog_productsrC  U  s   	!'	* j/
<MN0>/0QnWm6G?6Z!2`y(2o(E*K]!! %% &'( ) ! "
 (* 	>   	3t9~S06B7==?Z.B/557c/28b9??A=@=P=\S01779bf=@AR=S=_E#&7"89ei58_5PSZ)//1VZ	
 + *s   AD=&CE	=Epromptc                 ~    t        |       dvrdvryt        fddD              ryt        fddD              S )NprodottcatalogFc              3   &   K   | ]  }|v  
 y wr  r   r  fragmentr   s     r   r  z/_is_catalog_document_request.<locals>.<genexpr>}  s       	J   )
z ordine z ordini z ordine#zstorico ordinizstorico acquistizrighe ordinezbatch ordinizacquisti realizdettagli dell ordinezdettaglio dell ordinec              3   &   K   | ]  }|v  
 y wr  r   rI  s     r   r  z/_is_catalog_document_request.<locals>.<genexpr>  s       	JrK  )	compriamoacquistiamocatalogorT   costosor\   	fornitore	fornitori	categoriaordinataordinatoordinatiordinareztra i prodottiznostri fornitorizdei nostri fornitori)r   r  )rD  r   s    @r   _is_catalog_document_requestrX  y  sW    '/J
"y
'B
 
   
  r   c                     t        |       }t        j                  dd|      }|j                         D cg c]  }|s|t        vs| }}dj                  |d d       j                         xs d S c c}w )Nu   \b(?:piu|più|meno|caro|cari|cara|care|economico|economici|economica|economiche|costoso|costosi|costosa|costose|ordinato|ordinata|ordinati|ordinate|prezzo|prezzi)\br      )r   r   r   r   r  r   r   )r  cleanedr  r  s       r   _clean_query_candidater\    st    $Y/Gff 	pG
 ")eEeKd>deeFe88F2AJ%%'/4/ fs   A2A2A2c                 ^   d}|D ]J  }t        j                  || t         j                        }|s+t        |j	                  d            }|sH|c S  t        |       }|sy |j                         }|D cg c]	  }|dvs| }}dj                  |d d       j                         xs d S c c}w )N)zH\bprodott[oi]\s+(.+?)\s+che\s+(?:compriamo|acquistiamo|teniamo|usiamo)\bz%\bprodott[oi]\s+(.+?)\s+ordinat[oi]\bu/   \bcategoria\s+([A-Za-zÀ-ÿ0-9' ./&_-]{2,80})\br  >   rO  rM  rN  r   r
  )r   search
IGNORECASEr\  groupr   r   r   )	rD  patternspatternmatchr  generic_candidater  r  
meaningfuls	            r   _extract_catalog_queryrf    s    H
  		'62==9*5;;q>:	 /v6$$&F%+eEu<d/d%eJe88JrN#))+3t3 fs   4	B*>B*c                     | sy t        |       }t        t        |             }|t        v s	|t        z  ry|t        v s	|t        z  ryy )Nr`   rk   )r   setr  _FOOD_QUERY_ALIASES_DRINK_QUERY_ALIASES)r  r   r'  s      r   _extract_catalog_bucketrk    sM    '.J-e45L((L;N,N))\<P-Pr   productc                    t        | j                  xs d      t        | j                        t        | j                        t        | j                        t               }rRt        fdt        D              r|j                  d       t        fdt        D              r|j                  d       t        fdt        D              r|j                  d       t        fdt        D              r|j                  d       |s%t        fdd	D              r|j                  d       |sd
k(  r|j                  d       |s-t        fddD              rdvrdvr|j                  d       |S )Nr   c              3   &   K   | ]  }|v  
 y wr  r   r  hintr   s     r   r  z)_product_bucket_labels.<locals>.<genexpr>  s     ADtxArK  r`   c              3   &   K   | ]  }|v  
 y wr  r   ro  s     r   r  z)_product_bucket_labels.<locals>.<genexpr>  s     BDtxBrK  rk   c              3   &   K   | ]  }|v  
 y wr  r   r  rp  r9  s     r   r  z)_product_bucket_labels.<locals>.<genexpr>  s     
6D44<
6rK  c              3   &   K   | ]  }|v  
 y wr  r   rs  s     r   r  z)_product_bucket_labels.<locals>.<genexpr>  s     
5D44<
5rK  c              3   &   K   | ]  }|v  
 y wr  r   )r  tagsuppliers     r   r  z)_product_bucket_labels.<locals>.<genexpr>  s     Fc#/FrK  )chefmarrbtc              3   &   K   | ]  }|v  
 y wr  r   )r  rJ  r   s     r   r  z)_product_bucket_labels.<locals>.<genexpr>  s     Q8(h.QrK  )kggpzmlcl)r   r   r   r   r   rh  r  _FOOD_CATEGORY_HINTSadd_DRINK_CATEGORY_HINTS_DRINK_NAME_HINTS_FOOD_NAME_HINTS)rl  labelsr   r   r9  rw  s     @@@@r   _product_bucket_labelsr    s-   %g&6&6&<"=H!'"6"67D%g&;&;<H%g&6&67HuFA,@AAJJvB,ABBJJw

6$5
66

7

5$4
55

6cF5EFF

6h$&

7cQ?PQQVZbjVjos  |D  pD

6Mr   productsc                    t        |      d}d}dt        dt        t        t        t
        t
        f   fd}t        fd|D              rt        | |      S t        fd|D              rt        | d	       S t        | d
       S )N)zpiu economico al piu costosozpiu economica al piu costosazmeno caro al piu carozmeno costoso al piu costoso	crescente)zpiu costoso al piu economicozpiu cara alla meno caradecrescenterl  r   c                     | j                   dnd}t        | j                   xs d      }||| j                  j                         | j                  j                         fS Nr  r   r  r   r   r   r   r   )rl  missing_priceprice_values      r   	price_keyz)_sort_catalog_products.<locals>.price_key  sV    $44<!G33:s;{G,A,A,G,G,I7K_K_KeKeKghhr   c              3   &   K   | ]  }|v  
 y wr  r   rI  s     r   r  z)_sort_catalog_products.<locals>.<genexpr>  s     
Fh8z!
FrK  keyc              3   &   K   | ]  }|v  
 y wr  r   rI  s     r   r  z)_sort_catalog_products.<locals>.<genexpr>  s     
Gh8z!
GrK  c                     | j                   dndt        | j                   xs d       | j                  j                         | j                  j                         fS r  r  rl  s    r   <lambda>z(_sort_catalog_products.<locals>.<lambda>	  sS    ,,4!//6378%%++-$$**,	! r   c                     | j                   j                         | j                  j                         | j                  j                         fS r  r   r   r   r   r  s    r   r  z(_sort_catalog_products.<locals>.<lambda>  sK    1F1F1L1L1NPWPdPdPjPjPlnun~n~  oE  oE  oG  1H r   )r   r   tupler   r   r   r  sorted)r  rD  ascending_fragmentsdescending_fragmentsr  r   s        @r   _sort_catalog_productsr    s    '/Ji> ieCS4H.I i
 
F2E
FFhI..

G2F
GG
 	
 (  !H  I  Ir   c                 b   |s| S t        |      }|rd| D cg c]  }|t        |      v s| }}dj                  d t        |      D              j	                         }|s|S |rt        ||      }|r|S |S g }| D ]  }dj                  t        d |j                  |j                  |j                  |j                  xs d|j                  xs dg            }t        ||      }	|	dk  ro|j                  |	|f        |j                  d        |D 
cg c]  \  }
}|	 c}}
S c c}w c c}}
w )Nr   c              3   >   K   | ]  }|t         t        z  vr|  y wr  )ri  rj  )r  r  s     r   r  z+_filter_catalog_products.<locals>.<genexpr>  s'      !
03GGH !
s   r   r   c                     | d    | d   j                   j                         | d   j                  j                         | d   j                  j                         fS )Nr   r  r  )items    r   r  z*_filter_catalog_products.<locals>.<lambda>:  sY    47(DG,A,A,G,G,I4PQ7K_K_KeKeKgimnoipiyiyii  jB  "C r   r  )rk  r  r   r  r   _filter_catalog_productsfilterr   r   r   r   r   r+  appendsort)r  r  bucketrl  bucket_matchesreduced_querynested_matchesranked
searchablescore_s              r   r  r    sX   $U+F19ggVG]^eGf=f'gg !
/6!
 
 %'	 	
 !!5nmTN%%!!13F (XX(($$))((.B$$*	

 %UJ7A:ug&'!($ KK  CK  D&,-
7G--E hD .s   D&D&D+c                 z    | y| dj                  dd      j                  dd      j                  dd      }d| S )NzPrezzo non disponibilez,.2f,X.u   €)r   )r   rendereds     r   _format_eurr  >  sH    }'&&sC088cBJJ3PSTH
r   c                 >    | j                   xs | j                  xs dS )N-)r   r   r  s    r   _product_format_labelr  E  s    :w33:s:r   payloadc          	         |s0d}|r| d| d}t        d| j                  d| j                  |      S t        |D ch c]7  }|j                  j                         s|j                  j                         9 c}      }| j                  dt        |       g}|r|j                  d|        |r%d	j                  |      }|j                  d
|        |j                  d       t        |d      D ]  \  }}d|j                  xs d g}	|j                  r|	j                  d|j                          |	j                  dt        |              |	j                  t        |j                               |j                  | d|j                   ddj                  |	      z           t        d| j                  dt        |       d| j                  dj                  |      j                               S c c}w )Nz[Non ho trovato prodotti reali coerenti con questa richiesta nel catalogo ordini del locale.z Filtro usato: r  r=   <Nessun prodotto reale trovato nel catalogo per questa bozza.r   r   r   destination_folder_idr   zProdotti reali trovati: zFiltro: z, zFornitori di riferimento: r   r  )startz
fornitore r  z
categoria zformato z. z | Anteprima basata su $ prodotti reali del catalogo locale.
)r   r   r  r  r   r   r   r  r   	enumerater   r  r  r   r   )
r  r  r  detailrl  supplier_nameslinesvisible_suppliersindexdetailss
             r   _build_catalog_doc_previewr  I  s    nxugQ7F---R")"?"?
 	
 (twV]VkVkVqVqVsW2288:tuN]]6s8}oFGExw'( IIn512C1DEF	LL#HA6 Rw 5 5 <=>?NNZ(8(8'9:;#8#A!CDE{7#:#:;<wb!5!5 6c:UZZ=PPQR *mm&s8}o5YZ%;;		% &&( # us   G<G<c          
      8   |D cg c]G  }|j                   |j                  xs d|j                  t        |      t	        |j
                        gI }}|rdt        |       dnd}|r| d| d}t        d| j                  || j                  g d|	      S c c}w )
Nr   r  r  r  z	 Filtro: r  rK   )Prodotto	Categoria	FornitoreFormatozPrezzo ivator   r   r   r  columnsr   )
r   r   r   r  r  r   r   r   r   r  )r  r  r  rl  r   r   s         r   _build_catalog_sheet_previewr  u  s      	    "!!!'*//0	
	D 	  s8}o-QRK 
 IYugQ/)mm%;;Q #	s   ABc                    t        |j                        sy t        |       }t        |j                        }t	        ||      }t        ||j                        }|j                  dk(  rt        |||      S t        |||      S )Nr=   )r  r  )	rX  rD  rC  rf  r  r  r   r  r  )r   r  r  r  filtered_productssorted_productss         r   _build_grounded_catalog_previewr    su     (7%g.H"7>>2E05A,->OO||u)'OSXYY'/QVWWr   c                     | | j                   j                         sg S | j                   j                         D cg c]#  }|j                         s|j                         % c}S c c}w r  )scoper   r   )r4  r  s     r   _granted_scopesr    sO    !1!1!7!7!9	%/%5%5%;%;%=NTDJJLNNNs   A&A&c                     t               j                  }t        t        |             }|D cg c]	  }||vs| c}S c c}w r  )r   google_workspace_scopes_listrh  r  )r4  requiredgrantedr  s       r   _missing_required_scopesr    s9    ~::H/*-.G'@e5+?E@@@s   	>>scope_profilec                     t        t               j                        }| xs dj                         j	                         }|dk(  rt
        |vr|j                  t
               |S )Nr   zfiscal-documents)listr   r  r   r   GOOGLE_GMAIL_READONLY_SCOPEr  )r  base_scopesr   s      r   !_authorization_scopes_for_profiler    sV    |~BBCK%2,,.446J'',G{,Z67r   r   c                    t        t        |             }dg}|dk(  r|j                  d       n|j                  d       |D cg c]	  }||vs| }}|rt        dd      y c c}w )Nz%https://www.googleapis.com/auth/driver=   z)https://www.googleapis.com/auth/documentsz,https://www.googleapis.com/auth/spreadsheets  zL'account Google e' collegato ma non ha ancora autorizzato tutti i permessi necessari. Usa 'Ricollega Google' e accetta anche l'accesso a Docs, Sheets e Drive.status_coder  )rh  r  r  r   )r4  r   r  required_scopesr  missings         r   $_ensure_google_workspace_permissionsr    s~    
 /*-.G>?Ou}JKMN"1JU'5IuJGJ[
 	
  Ks   	A$A$	return_toc                 R    | sy| j                  d      r| j                  d      ry| S )N
/documents/z//r"  r  s    r   _safe_return_tor    s,    $	(<(<T(Br   r  r  statusr  c                    t               }d| i}|r|d d |d<   t        |      }|j                  j                  d       t	        |       d| S )NrR      r  r  ?)r   r   portal_public_urlrstripr  )r  r  r  settingsparamsr  s         r   _portal_redirectr    s`    ~HF!$3<xfE((//45oi6P5QQRSXRYZZr   textc                    | j                         }	 t        j                  |      }t        |t              r|S 	 |j                  d      }|j                  d      }|dk(  s
|dk(  s||k  rt        dd      	 t        j                  |||dz          }t        |t              st        dd	      |S # t        j
                  $ r Y w xY w# t        j
                  $ r}t        dd|       |d }~ww xY w)
N{}r    z5L'LLM non ha restituito un JSON valido per la previewr  r  zPreview LLM non parseabile: z*La preview LLM deve essere un oggetto JSON)	r   jsonloads
isinstancedictJSONDecodeErrorfindrfindr   )r  rawr  start_index	end_indexexcs         r   _extract_json_payloadr    s    
**,C**S/gt$N %
 ((3-K		#IbIOyK/G4kllc**Sy1}=> gt$4`aaN    c6RSVRW4XY_bbcs)   &B3 9C 3C	C	C5C00C5c                    t               }| j                  d      }| j                  d      }t        |t              st	        dd      |D cg c]5  }t        |      j                         st        |      j                         7 }}|st	        dd      g }t        |t              r|d |j                   D ]  }t        |t              s|d t        |       D cg c]  }t        |      j                          }	}t        |	      t        |      k  r)|	j                  d       t        |	      t        |      k  r)|j                  |	        ||fS c c}w c c}w )Nr  r   r  z1La preview del foglio non contiene colonne valider  z1La preview del foglio richiede almeno una colonnar   )
r   getr  r  r   r   r   'google_workspace_sheet_preview_max_rowsr   r  )
r  r  raw_columnsraw_rowsr  r  r   r;  cellr   s
             r   _normalize_sheet_rowsr    s/   ~H++i(K{{6"Hk4(4ghh-8NTCIOO<Ms4y NGN4ghhD(D!NhNNO 	$Cc4(8;Nc'l8KL#d)//+LJLj/CL0!!"% j/CL0KK
#	$ D= O Ms   E-E Er   c          	      r  K   t        d      }|t        ||       }||S | j                  dk(  rt        d|j                   dddd| j
                   d| j                   d	dgd
       d {   \  }}t        |      }t        |j                  d      xs |j                  d      xs d      j                         }|st        dd      t        d| j
                  t        |j                  d      xs d      | j                  |      S t        d|j                   dddd| j
                   d| j                   ddgd
       d {   \  }}t        |      }t        |      \  }}	t        d| j
                  t        |j                  d      xs d      | j                  ||	      S 7 C7 dw)Nr   r=   systemz Prepari bozze operative per un locale hospitality. Restituisci solo JSON valido con le chiavi "summary" e "document_content". document_content deve essere testo semplice in italiano, pronto per Google Docs.r   r   zTitolo documento: z
Istruzioni del gestore:
z.

Crea una bozza concreta, sintetica ma utile.g?)temperaturedocument_contentr   r   r  z.La preview Google Doc non contiene testo utiler  r   zBozza pronta per la conferma.r  z Generi bozze tabellari per un locale hospitality. Restituisci solo JSON valido con le chiavi "summary", "columns" e "rows". columns deve essere un array di stringhe. rows deve essere un array di righe, ogni riga un array di stringhe.zTitolo foglio: z

Crea una tabella pronta da salvare su Google Sheets con colonne chiare. Se il brief richiede dati fattuali ma non li hai, dichiaralo nel summary invece di inventarli.rK   zTabella pronta per la conferma.r  )r)   r  r   r&   base_promptr   rD  r  r   r  r   r   r   r  r  )
r  r   documents_profilegrounded_preview	llm_replyr  parsedr   r  r   s
             r   _generate_previewr    s    
 .k::7GL'##||u8 %,889 :k k #,W]]O <44;NN3C DGG& )
 
	1, 'y1fjj!34S

98MSQSTZZ\C8hii---

9-P1PQ")"?"?
 	
 5 !(445 6D D %gmm_ 5007/? @}}	
& ) LIq, #9-F)&1MGT)mmFJJy)N-NO%;; u
Ds&   A-F7/F20C F7F5A"F75F7clientaccess_tokenfile_id	folder_idc                p  K   dd| i}| j                  t         d| |ddi       d {   }|j                          |j                         j                  dg       }|dd}|rd	j	                  |      |d
<   | j                  t         d| ||       d {   }|j                          y 7 7 w)NAuthorizationBearer /files/fieldsparentsheadersr  zid, parents)
addParentsr&  r  removeParents)r  r   raise_for_statusr   r   patch)	r  r  r   r!  r)  metadata_responser'  update_paramsupdate_responses	            r   _move_file_to_folderr1  d  s      ', 89G$jj 
!	2)$ )  
 &&($$&**9b9GM ),'):o&"LL 
!	2 )  O
 $$&%s"   *B6B2A/B6B4B64B6folder_referencec          	      ^  K   |j                         }|st        dd      d| dd}d|vr| j                  t         dt	        |d	
       dd| iddi       d {   }|j
                  dk(  r?|j                         }|j                  d      dk7  rt        dd      t        |d         S |j
                  dvr|j                          |j                  dd      j                  dd      }| j                  t         ddd| id| ddddd       d {   }|j                          |j                         j                  dg       }	t        |	t              r=|	r;t        |	d    t              r|	d    nd }|r|j                  d      rt        |d         S | j                  t         d|dd!i|dd"#       d {   }
	 |
j                          t        |
j                         d         S 7 7 7 6# t        j                  $ r}t        d$d%| d&|       |d }~ww xY ww)'N  z#La cartella Drive indicata e' vuotar  r$  application/jsonr#  zContent-Typer  r%  r   )safer#  r&  zid, name, mimeTyper(     mimeTypez"application/vnd.google-apps.folderzHIl valore inserito punta a un file Drive esistente ma non a una cartellar   >       r4  \z\\r   z\'z/fileszPmimeType = 'application/vnd.google-apps.folder' and trashed = false and name = 'r  createdTimez&files(id, name, mimeType, createdTime))qpageSizeorderByr&  filesr   zid, name)r9  r9  r)  r  r   r  z&Creazione cartella Drive fallita per 'z': )r   r   r  r   r	   r  r   r   r,  r   r  r  r  posthttpx	HTTPError)r  r  r2  normalized_referencer)  lookup_responser  escaped_referencesearch_responserA  create_responser
  s               r   _resolve_or_create_drive_folderrK    s     ,1134YZZ #<.1*G
 && &

$%WU3Gb-Q,RS$~&>?23 !+ !
 

 &&#-%**,G{{:&*NN# #e  wt}%%&&o=,,.,44T6BJJ3PUV"JJ 
!( GL>":;33D2EQH $>
 '  O $$&  "&&w3E%5(q48%(dw{{4(wt}%%"KK 
!(*%(<
	 (  O((* ##%d+,,o
$( ?? ;<P;QQTUXTYZ
 	s\   AH- G7!B>H-G: B%H-G<H-G> H-:H-<H->H*H%%H**H-c          
        K   t        |d       t               }d|j                   dd}	 t        j                  |j
                        4 d {   }|j                  t         d|d| j                  i	       d {   }|j                          |j                         }t        |d
         }|j                  t         d| d|ddddi| j                  digi	       d {   }|j                          | j                  rKt        ||j                  | j                         d {   }	t        ||j                  ||	       d {    d d d       d {    t%        d| j                  d| d| j                  |j&                        S 7 F7 7 7 p7 Q7 C# 1 d {  7  sw Y   SxY w# t        j                   $ r}
t#        dd|
       |
d }
~
ww xY ww)Nr=   r   r$  r5  r6  timeoutr  r   r)  r   
documentIdz/documents/:batchUpdaterequests
insertTextr  r  )locationr  r  r2  r  r   r!  r  zCreazione Google Doc fallita: r  z#https://docs.google.com/document/d//editr   r   r   web_urlr  account_email)r  r   r  rD  AsyncClient(google_workspace_request_timeout_secondsrC  r   r   r,  r   r   r   r  rK  r1  rE  r   r   r[  )r  r4  r  r)  r  rJ  documentdocument_idr0  r!  r
  s              r   _create_google_docr`    s    
 )%@~H":#:#:";<*G
(e$$X-^-^_ %	 %	ci$*KK'(
3w}}- %0 % O
 ,,.&++-Hh|45K$*KK'(K=M(-4aL(/+!	 %0 % O ,,.,,"A!+!8!8%,%B%B# 	
 +!+!8!8''	  A%	 %	R )mm5k]%H%;; .. S%	"
A%	 %	 %	 %	L ?? e6TUXTY4Z[addes   *G&$F: FF: +F% FA'F%(F)AF%.F/ F%F!F%F: F# F: $3G&F: F%F%F%!F%#F: %F7+F.,F73F: :G#GG##G&c          
        K   t        |d       t               }d|j                   dd}	 t        j                  |j
                        4 d {   }|j                  t         d|dd	| j                  ii
       d {   }|j                          |j                         }t        |d         }|j                  di g      d   j                  di       j                  dd      }|j                  t         d| d|ddidd| j                  g| j                  d       d {   }	|	j                          |j                  t         d| d|dd|ddidddigi
       d {   }
|
j                          | j                   rKt#        ||j                  | j                          d {   }t%        ||j                  ||        d {    d d d       d {    t+        d| j                  d$| d%| j                   |j,                  &      S 7 7 7 7 7 r7 S7 E# 1 d {  7  sw Y   UxY w# t        j&                  $ r}t)        d!d"| #      |d }~ww xY ww)'NrK   rM  r$  r5  r6  rN  z/spreadsheets
propertiesr   rP  spreadsheetIdsheetsr   sheetIdz/spreadsheets/z
/values/A1valueInputOptionUSER_ENTEREDA1ROWS)rangemajorDimensionvaluesrB  rR  rS  updateSheetPropertiesfrozenRowCountr  )re  gridPropertieszgridProperties.frozenRowCount)rb  r&  rV  rW  r  z Creazione Google Sheet fallita: r  z'https://docs.google.com/spreadsheets/d/rX  rY  )r  r   r  rD  r\  r]  rC  r   r   r,  r   r   r  putr  r   r  rK  r1  rE  r   r   r[  )r  r4  r  r)  r  rJ  spreadsheetspreadsheet_idsheet_idvalues_responsefreeze_responser!  r
  s                r   _create_google_sheetrv    s    
 )'B~H":#:#:";<*G
8g$$X-^-^_ 5	 5	ci$*KK)*-8"Wgmm$<= %0 % O
 ,,.)..0K _!=>N"x"6q9==lBOSST]_`aH$*JJ)*.8H
S*N;!&,&>>	 %/ 	% 	O ,,.$*KK)*.8HU3/77G6K/" +J6
! %0 % O" ,,.,,"A!+!8!8%,%B%B# 	
 +!+!8!8*'	  a5	 5	r )mm9.9IO%;; .. s5		(
a5	 5	 5	 5	l ?? g6VWZV[4\]cffgs   *I-$I HI -H,HB'H,*H"+AH,-H$.AH,3H&4 H,H(H,I $H*%I )3I-I H,"H,$H,&H,(H,*I ,H>2H53H>:I I*I%%I**I-z/status)response_modelc                    t               }t               j                  | j                  d      }t	               }t        |      }|r|rt        |      ng }|xr	 |d uxr | }|rd}n|r|rd}n|rd}nd}t        ||d u||r|j                  nd |r|j                  nd |r|j                  nd |r|j                  ng |||
      S )NTadopt_legacy_if_neededzOAccount Google collegato. Genera una preview e conferma la creazione nel cloud.z|Account collegato ma autorizzazione incompleta. Usa 'Ricollega Google' e accetta tutti i permessi per creare file nel cloud.zSCollega l'account Google del titolare per abilitare la creazione di file nel cloud.zTConfigura le credenziali OAuth Google nel backend-hub prima di collegare un account.)

configured	connectedreadyr[  
expires_atredirect_uriscopesgranted_scopesmissing_scopes	next_step)r   r%   get_connection	tenant_idr!   r  r  r   r[  r~  google_workspace_redirect_urir  )r   r  r4  r{  r  r  r}  r  s           r   google_workspace_statusr  [  s    ~H+-<<W=N=Ngk<lJ/1J$Z0N=GJ-j9\^NH:T1H.6HEe		
) 	 
i	j	(D(2<j..$,6:((D?IX;;t8Bx44%% r   z/oauth/startr  )defaultc                     t                t               }|j                  |j                  t	        |             }t        |j                  t        |j                        t        |            }t        |      S )N)statecode_challenger  )authorization_url)r#   r%   create_pending_authorizationr  r  r   r  r   code_verifierr  r   )r  r  r   r   pendingr  s         r   google_workspace_oauth_startr  ~  sf     '(&(E001B1BOT]D^_G6mm273H3HI0?
 0BSTTr   z/oauth/callbackr  codeerrorerror_descriptionc                   K   t               }|rt        t        d|xs |      d      S | r|st        t        dd      d      S |j                  |       }|t        t        dd      d      S 	 t	        ||j
                         d {   }|j                  d      }|st        d	d
      t        t        |             d {   }|j                  st        dd      |j                  |j                  d      }	t        |j                  d      xs d      }
|
s|	rt        |	j                  xs d      }
|
st        d	d      t        ||
||	r|	j                  nd       }|	r|j                  s|	j                  |_        |j!                  |j                  |       t        t        d|j*                        d      S 7 ;7 # t        t"        j$                  f$ rT}t'        |t              r|j(                  n
t        |      }t        t        d|j*                  |      d      cY d }~S d }~ww xY ww)Nr  )r  i3  )urlr  zCallback Google incompletozStato OAuth Google non valido)r  r  r  r  z(Google non ha restituito un access tokenr  r  z/Autorizzazione Google priva di tenant associatoTry  refresh_tokenr   z)Google non ha restituito un refresh token)r  r[  connected_atr  r|  r  )r%   r   r  consume_pending_authorizationr   r  r  r   r    r   r  r  r  r"   r  r  set_connectionrD  rE  r  r  r  )r  r  r  r  r   r  token_payloadr  r[  existing_connectionr  r4  r
  r  s                 r   google_workspace_oauth_callbackr    s&     '(E 1B1KeL
 	

  1MN
 	

 11%8G 1PQ
 	


=4W^WlWlmm$((8C8bcc8\9JKK  C8ijj#2273D3D]a2bM--o>D"E!4 3 A A GRHMC8cdd6''=P,99VZ	

 z'7'7288JW..
; [G4E4EF ; n L$ 5??+ 
)#}=3s8 G4E4EfU
 	

s\   A0H>3G G;G 	G
CG &"H>G G H;'A	H60H;1H>6H;;H>z/disconnectc                 P    t               j                  | j                         ddiS )NdisconnectedT)r%   clear_connectionr  r  s    r   google_workspace_disconnectr    s$     11'2C2CDD!!r   z/previewc                    K   ddl m}  |||        d {   }t        || j                  t	        |j
                        |j                  |j                  |j                         |j
                  S 7 \w)Nr   )+prepare_google_workspace_preview_with_tracer   r   r   r   r   r   )	*app.services.operational_assistant_servicer  r   rD  r   r   r   r   r   )r  r   r  outcomes       r   google_workspace_previewr    s`     
 g?QQGNN4W__Emmmmmm ?? Rs   A4A2AA4z/createc                 @  K   t                t        |       d {   }| j                  dk(  rt        | |       d {   }nt	        | |       d {   }t        |d| j                   d| j                   dd|j                   d|j                   d|j                   ddd	| j                  | j                  t        | j                  xs g       t        | j                  xs g       | j                  |j                  |j                  d
       |S 7 7 7 ƭw)Nr=   )r4  zCrea z 'r   zCreato z	'. Link: zdocuments-direct-creater   )r   r   r   	row_countcolumn_countr  rZ  r   r  )r#   r$   r   r`  rv  r   r   rZ  r   r   r  r  r   )r  r   r4  createds       r   google_workspace_creater    s     
 '(=gFFJ||u*7zJJ,WLLGLL>GMM?!<!',,r'--	'//IZ[''"LL]]W\\/R0 526%,%B%B	
" N1 G KLs3   DD#DD DDCDDD)NNNN){
contextlibr   dataclassesr   r   r   difflibr   r   pathlibr   r   urllib.parser	   r.  typingr
   r   r   fastapir   r   r   r   fastapi.responsesr   rD  app.api.depsr   app.core.configr   app.models.google_workspacer   r   r   r   r   r   r   $app.services.google_workspace_clientr   r   r   r   r   r   r    r!   %app.services.google_workspace_sessionr"   r#   r$   #app.services.google_workspace_storer%   app.services.llm_clientr&   app.services.tenant_storer'   r(   shared.assistant_profilesr)   routerr  r  ri  rj  r  r  r  r  r   r   r  objectr   r   r   r  r  boolr  r   r+  r5  
Connectionrh  r<  rC  rX  r\  rf  rk  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r\  r1  rK  r`  rv  r  r  r  r  rC  r  r  r  r   r   r   <module>r     sf   % ! ' #   	     " < < .  ( (  	 	 	 
 K ? G ; 
N 6 p	      4% P=*H =S =  	
   V 
< $  %# %# %# $s) b bC bD b     o  .g&8&8 .c .cRUh .
!O !^8L !H( ( (V0c 0cDj 043 43: 4.	3: 	#* 	N s3x < IT.%9  I3  I4P^K_  IF(.tN'; (.C$J (.SWXfSg (.Vut|  ;> ;c ;)*) >") :	)
 $)X* >" :	
 $BXX*X $d*X"O 9D @ OT#Y OA)BT)I AdSVi AS4Z DI 
)
 
 
	
.sTz c  >BX\ [S [d
 [3QU: [ad [ S#X 04S> eDItDQTI<V6W 8 '+M*M t#M $	M`'' ' 	'
 ' 
':G-G- G- 	G-
 	G-T=)= *= #	=@M)M *M #	M` I&CD7>7O _ Tq  ED N+OP!,7 %d 3&7UTzU:U U *	U QU  $(	<:<
*< :< Tz	<
 < <~ ];B?;S " "X\]`bf]fXg " "
 Z(FG  '7* $ H$ Y'DE  '7) # Fr   