
    	5j4                       d Z 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
mZ ddlmZ ddlmZ dd	lmZ dd
lmZ ddhZ e	d      Z e	d      Ze G d d             Z	 d	 	 	 	 	 ddZddZ	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZy)u	  
Bad spending pattern flags — Story 10.5.

FR-3.4: Surface automatically when:
  1. A category exceeds its budget by >20% for 2 consecutive months
  2. A subscription has no matching transaction in 60+ days
  3. Total dining/entertainment > 15% of monthly income
    )annotations)	dataclass)date	timedelta)Decimal)func)db)Transaction)Budget)CategoryDiningEntertainmentz1.20z0.15c                  L    e Zd ZU ded<   ded<   dZded<   dZded<   dZded	<   y)
SpendingFlagstr	flag_typemessageNz
str | Nonecategory_nameDecimal | Noneamount	threshold)__name__
__module____qualname____annotations__r   r   r        ?/var/www/html/financials/app/services/insights/pattern_flags.pyr   r      s*    NL $M:$!FN! $I~$r   r   Nc                    t        j                         }g }|j                  t        |             |r|j                  t	        ||             | r | dkD  r|j                  t        | |             |S )z
    Compute all three flag types for the current month context.

    monthly_income: from Settings (passed as plain value)
    subscription_merchants: list of merchant names detected as subscriptions
    r   )r   todayextend_check_consecutive_over_budget_check_inactive_subscriptions!_check_dining_entertainment_ratio)monthly_incomesubscription_merchantsr    flagss       r   get_spending_flagsr(   %   sg     JJLE "E 
LL/67 23I5QR .1,6~uMNLr   c                n   g }dD ]  }|dk(  r| j                   | j                  }}n<| j                  dk(  r| j                   dz
  d}}n| j                   | j                  dz
  }}| d|dd}t        j                  j	                  ||      j                         }|st        j                  j                  t        j                  t        j                  t        j                        j                  d	            j                  t        j                  j!                  |      t        j"                  d
k(        j%                  t        j                        j                         }|D ci c]/  }|j                  t'        t)        |j*                  xs d            1 }	}|D ]  }
|
j                  dk  r|	j-                  |
j                  t'        d            }||
j                  t.        z  kD  sPt0        j                  j-                  |
j                        }|r|j2                  nd}|dk(  s| j                    d| j                  dd}t        j                  j                  t        j                  t        j                              j                  t        j                  j!                  |      t        j                  |
j                  k(  t        j"                  d
k(        j5                         }t'        t)        |xs d            }t        j                  j	                  |
j                  | j                  | j                         j7                         }|s||j                  t.        z  kD  s|j9                  t;        d| d|||
j                  t.        z                 |S c c}w )N)r      r   r*      -02d-%)monthyeartotalF0Unknown)category_idr/   r0   over_budgetz: has exceeded its budget by >20% for 2 consecutive months.r   r   r   r   r   )r0   r/   r   query	filter_byallr	   sessionr
   r4   r   sumr   labelfilterr   like	is_creditgroup_byr   r   r1   get_BUDGET_OVERAGE_THRESHOLDr   namescalarfirstappendr   )r    r'   month_offsetr0   r/   month_prefixbudgets
spent_rowsr	spent_mapbudgetspentcatcat_name
cur_prefixcur_spent_row	cur_spent
cur_budgets                     r   r"   r"   @   s   E 91**ekk%D{{a#jj1nbe#jj%++/eqs2.,,((u4(@DDF JJ''++,227; VK$$)),79N9NRW9WXXk--.SU 	 ISS1Q]]GC1,=$>>S	S  	F}}!MM&"4"4gclCEv}}'@@@nn((););<'*388	1$$)JJ<qS0A!DJ

((+2D2D)EF',,11*='33v7I7II'11U:
   " !(M,>Q(? @I!'!7!7$*$6$6#kk

 "8 " eg  "i*2C2CF_2_&_\&3'/j0j$k*2#(&,mm6O&O& 5 	39t LG Ts   )4N2c           	        |t        d      z
  j                         }g }| D ]  }t        j                  j	                  t        j
                  |k(  t        j                  dk(        j                  t        j                  j                               j                         }||j                  |k  s|j                  t        dd| dd d               |S )N<   )daysFinactive_subscriptionzSubscription 'z' has no charge in 60+ days.)r   r   r   r   )r   	isoformatr
   r7   r=   merchant_normalizedr?   order_byr   descrE   rF   r   )r&   r    cutoffr'   merchantlast_txns         r   r#   r#      s     iR((335FE* V//8;%%. Xk&&++-.UW 	 x}}v5LL1(
2NO"	 " Lr   c                   |j                    d|j                  dd}t        }t        j                  j                  t        j                  j                  |      t        j                  dk(        j                         }|sg S |D cg c]  }|j                   }}t        j                  j	                  t        j                  t        j                               j                  t        j"                  j%                  |      t        j&                  j                  |      t        j(                  dk(        j+                         }t-        t/        |xs d            }| t0        z  }	||	kD  rt3        dd|d	d
|	d	dd||	      gS g S c c}w )Nr,   r-   r.   TFr   dining_entertainment_ratioz$Dining + Entertainment this month ($z.2fz") exceeds 15% of monthly income ($z).zDining + Entertainmentr6   )r0   r/   _DINING_ENTERTAINMENTr   r7   r=   rC   in_	is_activer9   idr	   r:   r   r;   r
   r   r   r>   r4   r?   rD   r   r   _INCOME_DINING_THRESHOLDr   )
r%   r    rH   	cat_namescatsccat_idstotal_spentr1   r   s
             r   r$   r$      sb    jj\5;;s"326L%I>>  !2!29!=x?Q?QUY?YZ^^`D	!"qtt"G" 	

+"4"456	!!,/##''0!!U*


 
  C(q)*E!99Iy26uSk B++4S/= 3	
 	 		 I3 #s   F	)N)r%   r   r&   zlist[str] | Nonereturnlist[SpendingFlag])r    r   rl   rm   )r&   z	list[str]r    r   rl   rm   )r%   r   r    r   rl   rm   )__doc__
__future__r   dataclassesr   datetimer   r   decimalr   
sqlalchemyr   app.extensionsr	   app.models.transactionr
   app.models.budgetr   app.models.categoryr   rb   rB   rf   r   r(   r"   r#   r$   r   r   r   <module>rx      s    # ! $    . $ ( "?3 #FO "6?  % % % 04", 6<~% 4""" "r   