
    `j                    D   d Z ddlmZ ddlmZ ddlmZ ddlmZ ddl	m
Z
mZmZmZmZ ddlmZ ddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZ ddlmZ ddlmZ ddZddZ ddZ!d dZ"d!dZ#d"dZ$d#dZ%	 	 	 	 	 	 	 	 d$dZ&	 	 	 	 	 	 d%dZ'	 	 	 	 	 	 d&dZ(d'dZ)d(dZ*d(dZ+y))z@Daily C59 aggregation service for Regione Liguria movement rows.    )annotations)defaultdict)date)Iterable)ExpressionWrapperFIntegerFieldQSum)Coalesce)Booking)normalize_guest_for_istat)XmlPayloadValidationError)IstatC59RowPayload)IstatLookupService)Property)	Structurec                J    | j                   |cxk  xr | j                  k  S c S N)check_in_datecheck_out_date)bookingtarget_dates     =/backend/istat/xml_export/services/c59_aggregation_service.pybooking_overlaps_dayr      s$      KH'2H2HHHHH    c                P    t        | |       t        |       z  t        |       z  S )N)check_in_date__ltecheck_out_date__gt)r   )r   )r
   )r   s    r   c59_relevant_booking_filterr       s+    	[[I
+
&	'
;
'	(r   c                    t         j                  j                  t        |      | d      j	                  ddd      j                  d      j                  dd      S )	NT)structure_idis_checked_inpropertyproperty_type	structureguestsr   id)r   objectsfilterr    select_relatedprefetch_relatedorder_byr"   r   s     r   fetch_c59_bookingsr/   !   sS    '4% 	 	

 

O[	A		(	#	/4	(	r   c                F    | xs dj                         j                         S )N )stripupper)values    r   _normalize_coder5   .   s    KR &&((r   c                   t        | xs d      j                         }|j                         r|j                  d      }|j                         rt	        |      dk7  rt        d| d|  d      |S )Nr1      zInvalid z code 'z'' in C59 aggregation (must be 3 digits))strr2   isdigitzfilllenr   )r4   contextcodes      r   _three_digit_coder>   2   si    u{!!#D||~zz!}<<>SY!^'wiwug-TU
 	
 Kr   c                   t        |       }|st        d      |dk(  r`t        |      }|st        d      t        j                  |      }|t        d| d      dt	        |j                  d      d| d	
      fS t        j                  |      }|t        d| d      dt	        |j                  d      d| d	
      fS )Nz'Country is required for C59 aggregationITz=Province is required for Italian residents in C59 aggregationz$Missing ISTAT province mapping for 'z' in C59 aggregationiprovince_codez
province '')r<   z#Missing ISTAT country mapping for 'e
istat_codez	country ')r5   r   r   get_provincer>   getget_country)countryprovincecountry_codeprovince_siglaprovince_mappingcountry_mappings         r   resolve_c59_residence_coderO   =   s	   "7+L'(QRRt(2+O  .::>J#+6~6FFZ[  %  1  02
 
 	

 )44\BO'1,?ST
 	
 !L)L>+  r   c                    t        |       }t        |dd       }t        |t              r|j	                  d      nd }t        t        |dd       |      S )N
extra_datarJ   rI   )r   getattr
isinstancedictrG   rO   )guestnormalized_guestrQ   rJ   s       r   _guest_residence_keyrW   ]   sR    07)<>J-7
D-Iz~~j)tH% )T2 r   c                   t        d       }|D ]  }|j                  |k(  }|j                  |k(  }t        ||      }|s|s|s4|j                  j                         D ]E  }	 t        |      }	|r||	   dxx   dz  cc<   |r||	   dxx   dz  cc<   |s6||	   dxx   dz  cc<   G  |j                         D cg c]1  \  \  }}}|d   s
|d   s|d   rt        |||d   |d   |d   d	
      3 }}}}|j                  d        |S # t        $ r.}
t        d|j                   d|j                   d|
       |
d }
~
ww xY wc c}}}w )Nc                     ddddS )Nr   )arrivipartenzepresenze r]   r   r   <lambda>z&build_daily_c59_rows.<locals>.<lambda>m   s    q&Qr   zGuest z
 (booking z): rZ      r[   r\   r   )nazione	residenzarZ   r[   r\   diurnic                2    | j                   | j                  fS r   )r`   ra   )rows    r   r^   z&build_daily_c59_rows.<locals>.<lambda>   s    s{{CMM:r   )key)r   r   r   r   r'   allrW   r   r(   itemsr   sort)r&   r   bookingsaggregationr   
is_arrivalis_departureis_presencerU   re   excr`   ra   countsrowss                  r   build_daily_c59_rowsrq   g   s    QRK**k9
--<*7K@lk^^'')E*51 C *a/*C ,1,C ,1, * @ -8,=,=,? -@( Wi&(vj1VJ5G 	(#J'J'	
 -@ 	  	II:I;K3 - /UXXJjCuEs   !D
;6E
	E)D<<Ec                r    t         j                  j                  |       }t        ||t	        | |            S )N)r(   )r&   r   ri   )r   r)   rG   rq   r/   )r"   r   r&   s      r   "build_daily_c59_rows_for_structurers      s:    
 !!%%%6I#L+> r   c                    t         j                  j                  | d||d      j                  d      j	                         j                         S )NTF)r"   r#   r   r   property_id__isnullproperty_id)r   r)   r*   valuesdistinctcountr.   s     r   calculate_c59_occupied_roomsrz      sI     	%** % 	 	
 
			r   c                    t         j                  j                  | t         j                  j                        j                  d      S )Nr&   availabilityr%   )r   r)   r*   Availability	AVAILABLEr+   )r&   s    r    _active_properties_for_structurer      s>    ""**44 #  n_%&r   c                    t        | dd       xs t        | dd       }|r|dkD  rt        |      S t        |       j                         S )Ntotal_roomstotal_unitsr   )rR   intr   ry   )r&   r   s     r   calculate_c59_available_roomsr      sM    )]D9 W=$>K {Q;+I6<<>>r   c           
     n   t         j                  j                  | t         j                  j                        j                  t        t        t        d      d      t        t        d      d      z   t                           j                  t        t        d      d            }t        |d	         S )
u  Calculate total available beds for a structure.

    Each PropertyType defines beds *per room/unit* (num_beds + num_sofa_beds).
    The total is the sum of (beds_per_room × number_of_active_rooms) across all
    property types that have at least one active room in this structure.

    Uses a single aggregated DB query to avoid N+1 issues:
      SUM(num_beds + num_sofa_beds)  grouped per active Property row.

    The ``structure.total_beds`` shortcut is intentionally NOT used here because
    that field is not present on the Structure model and the per-room calculation
    is the authoritative source of truth for C59 reporting.
    r|   property_type__num_bedsr   property_type__num_sofa_beds)output_field)beds_per_roomr   )totalr   )r   r)   r*   r~   r   annotater   r   r   r	   	aggregater   r   )r&   results     r   calculate_c59_available_bedsr      s     	!..88 	  	
 
+45q91;<a@A)^ 
 

 
#o"6:	;  vgr   N)r   r   r   r   returnbool)r   r   r   r
   )r"   r   r   r   )r4   
str | Noner   r8   )r4   r   r<   r8   r   r8   )rI   r   rJ   r   r   tuple[str, str])r   r   )r&   r   r   r   ri   zIterable[Booking]r   list[IstatC59RowPayload])r"   r   r   r   r   r   )r"   r   r   r   r   r   )r&   r   )r&   r   r   r   ),__doc__
__future__r   collectionsr   datetimer   typingr   django.db.modelsr   r   r	   r
   r   django.db.models.functionsr   bookings.modelsr   guests.guest_defaultsr   istat.xml_export.exceptionsr   #istat.xml_export.models.c59_payloadr   (istat.xml_export.services.lookup_servicer   properties.modelsr   structures.modelsr   r   r    r/   r5   r>   rO   rW   rq   rs   rz   r   r   r   r]   r   r   <module>r      s    F " #   G G / # ; A B G & 'I
)@,, ,  	,
 ,^

 
 	
  		&&? r   