
    >j                       d Z ddlmZ ddlZddlZddlmZ ddlmZmZ ddl	m
Z
mZ ddlmZ ddlmZmZmZ dd	lmZ d
Zg dZh dZh dZh dZh dZh dZe G d d             Z	 d	 	 	 	 	 ddZddZddZddZddZ ddZ!y)a  
Generic CSV/TSV transaction parser.

Supports tab-separated and comma-separated files with or without headers.
Auto-detects separator and column layout.

Tested against: Home Depot TSV export (05/26/2026, $839.20, THE HOME DEPOT LONDONDERRY  NH, purchase)
    )annotationsN)	dataclass)datetimedate)DecimalInvalidOperation)IO)StagedTransaction
ParseErrorcompute_dedup_hash)	normalizez1.0.0)z%m/%d/%Yz%m/%d/%yz%Y-%m-%dz%m-%d-%Yz%d/%m/%Y>   	post date
trans dateposted datetransaction dater   >   debitamountchargecredit>   memonamepayeemerchantdescription>   transaction typetypecategory>   r   refundreturnpayment
adjustmentc                  ,    e Zd ZU ded<   ded<   ded<   y)CsvParseResultzlist[StagedTransaction]transactionszlist[ParseError]errorsint	row_countN)__name__
__module____qualname____annotations__     3/var/www/html/financials/app/services/csv_parser.pyr#   r#   (   s    ))Nr-   r#   c                   t        | t              r&t        | d      5 }|j                         }ddd       n| j                         }	 j	                  d      }t        |      }t        j                  t        j                  |      |      }t        |      }|st        g g d      S |d   D cg c]   }|j                         j                         " }	}t        d |	D              }
|
r|	}|dd }nd}|}t!        ||r|d   ng       }g }g }t#        ||
rd	nd
      D ]:  \  }}t        d |D              s	 t%        ||||      }||j'                  |       < t        ||t1        |            S # 1 sw Y   5xY w# t
        $ r j	                  d      }Y Cw xY wc c}w # t(        $ rH}|j+                  |      dd }|j'                  t-        ||t        |      t.                     Y d}~d}~ww xY w)z
    Parse a CSV or TSV transaction file.

    Accepts either a file-like object or a file path string.
    Auto-detects separator (tab or comma) and column layout.
    rbNz	utf-8-sigzlatin-1)	delimiterr   c              3  H   K   | ]  }|t         t        z  t        z  v   y wN)
_DATE_COLS_AMOUNT_COLS
_DESC_COLS).0cols     r.   	<genexpr>zparse_csv.<locals>.<genexpr>Q   s     XsSJ5
BBXs    "      )startc              3  <   K   | ]  }|j                           y wr3   )strip)r7   cells     r.   r9   zparse_csv.<locals>.<genexpr>a   s     0D4::<0s      )r$   r%   r'   )
isinstancestropenreaddecodeUnicodeDecodeError_detect_separatorcsvreaderioStringIOlistr#   r>   lowerany_detect_columns	enumerate
_parse_rowappend	Exceptionjoinr   _PARSER_VERSIONlen)file_objissuerfrawcontentseprI   rowsc	first_row
has_headerheaders	data_rowscol_mapr$   r%   row_numrowresultexcraw_texts                        r.   	parse_csvri   /   s    (C (D! 	Q&&(C	 	 mmo(**[)
 G
$CZZG,<F<Db"a(( -1G4q"4I4XiXXJH		 gyy|bIG,.L!F!)
1J 
T0C00	TWfg>F!##F+
T |FcR[n]]c	 	  (**Y'( 50  	Txx}Tc*HMM*WhC/RSS	Ts;   E2E? /%F 8!F%2E<?FF%	G6.>G11G6c                    | j                  d      d   }|j                  d      }|j                  d      }||kD  rdS dS )N
r   	,)splitcount)r[   
first_linetabscommass       r.   rG   rG   o   sF    t$Q'JD!Dc"F&=4)c)r-   c                    | r~i }t        |       D ]l  \  }}|j                         j                         }|t        v r
d|vr||d<   6|t        v r
d|vr||d<   H|t
        v r
d|vr||d<   Z|t        v scd|vsh||d<   n |S i }t        |      D ]}  \  }}|j                         }d|vrt        |      r||d<   +d|vrt        |      r||d<   @d|vr!|j                         t        h dz  v r||d<   ed|vsjt        |      dkD  sy||d<    |S )z=Return {role: col_index} for date, amount, description, type.r   r   r   r   >   r   r   purchase   )rP   r>   rM   r4   r5   r6   
_TYPE_COLS_looks_like_date_looks_like_amount_CREDIT_TYPESrV   )ra   
sample_rowrc   ihh_lowervals          r.   rO   rO   v   sA   g& 		$DAqggioo'G*$w)>"#L(XW-D$%!J&=+G)*&J&6+@"#		$  GJ' 	'3iik %5c%:GFOW$);C)@ !GH7"syy{mFe6e'eGFO')c#hl%&GM"	' Nr-   c                    t         D ](  }	 t        j                  | j                         |        y y# t        $ r Y 7w xY w)NTF)_DATE_FORMATSr   strptimer>   
ValueError)r~   fmts     r.   rw   rw      sH     	ciik3/   		s   $3	??c                    | j                         j                  d      j                  dd      }	 t        |       y# t        $ r Y yw xY w)N$rm    TF)r>   lstripreplacer   r   )r~   cleaneds     r.   rx   rx      sG    iik  %--c26G s   = 	A	A	c                    d fd} |d      } |d      } |d      } |d      j                         }|r|sy d }	t        D ](  }
	 t        j                  ||
      j	                         }	 n |	t        d|      |j                  d      j                  dd	      }	 t        |      }|sd| }|t        d      k  xs |t        v }t        |      }t        |      }t        |||	      }t        |	||||||ddj                  d  D              d d 	      S # t
        $ r Y w xY w# t        $ r t        d
|      w xY w)Nc                n    j                  |       }|!|t              k  r|   j                         S y)Nr   )getrV   r>   )keyidxrc   re   s     r.   _getz_parse_row.<locals>._get   s4    kk#?sSX~s8>>##r-   r   r   r   r   zCannot parse date: r   rm   r   zCannot parse amount: zRow 0g      ?rl   c              3  2   K   | ]  }t        |        y wr3   )rB   )r7   r^   s     r.   r9   z_parse_row.<locals>.<genexpr>   s     /a3q6/s   r@   )	r   merchant_rawmerchant_normalizedr   	is_creditrX   
dedup_hashconfidence_scorerh   )r   rB   r   rB   )rM   r   r   r   r   r   r   r   r   r   ry   absr   r   r
   rT   )re   rc   rX   rd   r   date_str
amount_strdesctxn_typetxn_dater   amount_cleanr   r   
abs_amountnormr   s   ``               r.   rQ   rQ      s    fHhJm$Df##%H: H 	((37<<>H .xl;<< $$S)11#r:LA& gY%B])BIVJT?D#D*h?J /3//5
 
)  		  A0?@@As   	$D"D+ 	D('D(+E)
csv_import)rW   zIO[bytes] | strrX   rB   r   r#   )r[   rB   r   rB   )ra   zlist[str] | Nonerz   rL   r   dict)r~   rB   r   bool)
re   rL   rc   r   rX   rB   rd   r&   r   zStagedTransaction | None)"__doc__
__future__r   rH   rJ   dataclassesr   r   r   decimalr   r   typingr	   app.services.pdf_parsers.baser
   r   r    app.services.merchant_normalizerr   rU   r   r4   r5   r6   rv   ry   r#   ri   rG   rO   rw   rx   rQ   r,   r-   r.   <module>r      s    # 
 	 ! # -  [ [ 6 W
7D
8
G    =^=^=^ =^@*B3r-   