Ë
    ôjî?  ã                  ó¦  — d Z ddlmZ ddlZddlmZmZ ddlmZ ddl	m
Z
 ddlmZmZmZ ddlmZ dd	lmZmZ dd
lmZ ddl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l&m'Z'm(Z(m)Z)m*Z*  ejV                  ejX                  d¬«        ejZ                  d«      Z.dd d„Z/d!d„Z0dddddœZ1d"d„Z2d#d„Z3d$d„Z4d%d„Z5d&d„Z6d'd„Z7d!d„Z8 e/«       Z9y)(uþ  
fraudreport.py
â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
Standalone Fraud Report FastAPI application.

This module is intentionally decoupled from the main People Hub app:
it has its own FastAPI instance, its own static files, and its own
data path. Mount it under a different port (default 8001) to run
side-by-side with the main app.

Run:
    uvicorn fraud_report.fraudreport:app --port 8001 --reload

Endpoints:
    GET  /                            â†’ serves the dashboard HTML
    GET  /api/fraud/access-keys       â†’ all available access keys
    GET  /api/fraud/report/{key}      â†’ full fraud report for one key
    GET  /api/fraud/stages            â†’ canonical stage track (static)
    GET  /api/fraud/health            â†’ liveness probe

Design notes:
    â€¢ All business logic lives in the `services` section so the routes
      stay thin and the same logic can be reused by future endpoints
      (export-to-PDF, websocket live feed, analytics aggregationsâ€¦).
    â€¢ Exception translation happens in one place (the
      `@app.exception_handler` block) so swapping the data backend
      doesn't drag handlers along.
    â€¢ The frontend is served from this same app for convenience â€”
      production can equally well put a CDN in front of /static/.
é    )ÚannotationsN)ÚCounterÚdefaultdict)ÚPath)ÚOptional)ÚFastAPIÚHTTPExceptionr   )ÚCORSMiddleware)ÚFileResponseÚJSONResponse)ÚStaticFilesé   )ÚAccessKeyNotFoundErrorÚFraudDataCorruptErrorÚFraudDataEmptyErrorÚFraudDataNotFoundErrorÚFraudRepositoryÚget_repository)ÚAccessKeyEntryÚAccessKeyListÚ
FraudEventÚFraudEventOutÚFraudReportÚReportSummaryÚ	StageInfoÚStageSummary)Úcanonical_stage_trackÚdescribe_roundÚround_from_pageÚround_label_for_pageu6   %(asctime)s  %(levelname)-7s  %(name)s â€” %(message)s)ÚlevelÚformatÚfraud_reportc                óh  — t        ddd¬«      }|j                  t        dgddgdg¬«       t        t        «      j                  «       j                  d	z  }|j                  d
t        t        |«      ¬«      d	¬«       | xs
 t        «       }||j                  _        t        |«       t        |«       |S )Nu   People Hub â€” Fraud Reportz1.0.0z”Premium dashboard surface for fraud_log.json. Pluggable storage layer means the same API will work against SQL / Mongo without any frontend changes.)ÚtitleÚversionÚdescriptionÚ*FÚGET)Úallow_originsÚallow_credentialsÚallow_methodsÚallow_headersÚstaticz/static)Ú	directory)Úname)r   Úadd_middlewarer
   r   Ú__file__ÚresolveÚparentÚmountr   Ústrr   ÚstateÚrepoÚ_register_exception_handlersÚ_register_routes)Ú
repositoryÚapplicationÚ
static_dirr8   s       ús/Users/priyanka/Documents/AI_Agent_Assessment/pepole_hub_candidate/people_hub_candidate/fraud_report/fraudreport.pyÚ
create_appr?   Q   s¸   € ÜØ+Øð8ô	€Kð ×ÑÜØ�eØØ�gØ�eð ô ô ”h“×'Ñ'Ó)×0Ñ0°8Ñ;€JØ×ÑØÜœc *›oÔ.Øð ô ð 'Ò:¬.Ó*:€DØ!€K×ÑÔä  Ô-Ü�[Ô!ØÐó    c                óä   — | j                  t        «      dd„«       }| j                  t        «      dd„«       }| j                  t        «      dd„«       }| j                  t        «      dd„«       }y )	Nc              “  ój   K  — t         j                  d|«       t        ddt        |«      ddœ¬«      S ­w)Nzfraud data missing: %si÷  Úfraud_data_missingz/Set FRAUD_LOG_PATH or seed data/fraud_log.json.)ÚerrorÚmessageÚhint©Ústatus_codeÚcontent)ÚlogÚwarningr   r6   ©Ú_Úexcs     r>   Ú_missingz._register_exception_handlers.<locals>._missing}   s7   è ø€ ä�‰Ð,¨cÔ2ÜØà-Ü˜s›8ØIñô
ð 	
ùs   ‚13c              “  óh   K  — t         j                  d|«       t        ddt        |«      dœ¬«      S ­w)Nzfraud data corrupt: %siô  Úfraud_data_corrupt©rD   rE   rG   )rJ   rD   r   r6   rL   s     r>   Ú_corruptz._register_exception_handlers.<locals>._corrupt‰   s1   è ø€ ä�	‰	Ð*¨CÔ0ÜØØ2¼sÀ3»xÑHô
ð 	
ùs   ‚02c              “  ól   K  — t         j                  d|«       t        ddt        |«      dg dœ¬«      S ­w)Nzfraud data empty: %séÈ   Úfraud_data_emptyr   )rD   rE   ÚcountÚitemsrG   )rJ   Úinfor   r6   rL   s     r>   Ú_emptyz,_register_exception_handlers.<locals>._empty‘   s:   è ø€ ä�‰Ð'¨Ô-ÜØà+Ü˜s›8ØØñ	ô
ð 	
ùs   ‚24c              “  ó<   K  — t        ddt        |«      dœ¬«      S ­w)Né”  Úaccess_key_not_foundrR   rG   )r   r6   rL   s     r>   Ú_no_keyz-_register_exception_handlers.<locals>._no_keyž   s"   è ø€ äØØ4ÄÀSÃÑJô
ð 	
ùs   ‚)rN   r   )rN   r   )rN   r   )rN   r   )Úexception_handlerr   r   r   r   )ÚapprO   rS   rZ   r^   s        r>   r9   r9   {   s€   € à×ÑÔ1Ó2ò	
ó 3ð	
ð 	×ÑÔ0Ó1ò
ó 2ð
ð 	×ÑÔ.Ó/ò

ó 0ð

ð 	×ÑÔ1Ó2ò
ó 3ñ
r@   é   é   é   )ÚlowÚmediumÚhighÚcriticalc                ó¬  — | j                   xs i }|j                  d«      xs dj                  «       j                  «       }|t        v r|S |j                  d«      xs dj                  «       j                  «       }|dk(  ry| j                  xs dj                  «       }|dk(  ry|dk(  ry| j                  xs dj                  «       }|d	v ry
|dv ryy)u0  Derive severity from metadata, action, and event type.

    Order of authority:
      1. Explicit metadata.severity (the new client monitor sets it).
      2. risk_score on disqualification rows (CRITICAL).
      3. Action â€” disqualify â†’ critical, warn â†’ medium.
      4. Event-type heuristic.
    ÚseverityÚ Ú
risk_scoreÚCRITICALrg   Ú
disqualifyÚwarnre   >   ÚCUTÚCOPYÚPASTEÚ
TAB_SWITCHÚSTORAGE_TAMPERrf   >   ÚIDLEÚ
INACTIVITYÚWINDOW_BLURrd   )ÚmetadataÚgetÚlowerÚstripÚ_SEVERITY_RANKÚupperÚactionÚ
event_type)ÚevÚmetaÚsÚrscorer}   Úets         r>   Ú_infer_severityr„   ®   sÒ   € ð �;‰;Ò˜"€DØ	�‰�*Ó	Ò	# ×*Ñ*Ó,×2Ñ2Ó4€AØŒNÑØˆà�h‰h�|Ó$Ò*¨×1Ñ1Ó3×9Ñ9Ó;€FØ�ÒØà�i‰iŠo˜2×$Ñ$Ó&€FØ�ÒØØ�ÒØà
�-‰-Ò
˜2×	$Ñ	$Ó	&€BØ	ÐEÑEØØ	Ð2Ñ2ØØr@   c                ó$   — | syt        | d„ ¬«      S )Nrd   c                ó.   — t         j                  | d«      S )Nr   )r{   rx   )Úvs    r>   Ú<lambda>z_max_severity.<locals>.<lambda>Ñ   s   € ¤^×%7Ñ%7¸¸1Ô%=r@   ©Úkey)Úmax)Úvaluess    r>   Ú_max_severityr�   Î   s   € ÙØÜˆvÑ=Ô>Ð>r@   c                ó   — | j                   xs i }|j                  d«      }t        | j                  xs d«      }|dk  rt	        |«      }|�|}| j                  «       }||d<   t        di |¤t        | «      t        di t        |«      ¤Ž|j                  d«      xs d|j                  d«      |j                  d«      �t        |j                  d«      «      nd |j                  d«      ||j                  d	«      d
œ¤ŽS )NÚpager   ÚroundÚsourcezclient-monitorÚ
session_idÚscenario_idÚscreenshot_urlÚua_hint)ri   ÚstageÚdetection_sourcer’   r“   r”   r�   Ú
user_agent© )rw   rx   Úintr�   r   Ú
model_dumpr   r„   r   r   r6   )r   r€   r�   Ú	raw_roundÚ	from_pageÚpayloads         r>   Ú_enrichrŸ   Ô   só   € Ø�;‰;Ò˜"€DØ�8‰8�FÓ€Dô �B—H‘H’M Ó"€IØ�A‚~Ü# DÓ)ˆ	ØÐ Ø!ˆIà�m‰m‹o€Gð !€GˆGÑäñ 
Ø
ð
ä  Ó$ÜÑ4œ.¨Ó3Ñ4ØŸ(™( 8Ó,Ò@Ð0@Ø—8‘8˜LÓ)Ø48·H±H¸]Ó4KÐ4W”C˜Ÿ™ Ó/Ô0Ð]aØ—x‘xÐ 0Ó1ØØ—8‘8˜IÓ&ó
ð 
r@   c                óT  — t        d„ |D «       «      }t        d„ |D «       «      }t        d„ |D «       «      }t        |D �ch c]  }|j                  sŒ|j                  ’Œ c}«      }t        |D �cg c]5  }|j                  s|j                  sŒ|j                  xs |j                  ‘Œ7 c}«      }|r|d   nd }|r|d   nd }	t        d„ |D «       «      }
|
s|j                  dd«      dkD  rd}nL|j                  d	d«      d
k\  rd}n4|j                  d	d«      dkD  s|j                  dd«      d
k\  rd}n|rd}nd}|
rdn|dv rdnd}|
rdn|rdnd}t        d„ |D «       d «      }t        | |t        |«      t        |«      t        |«      t        |«      |||	||||
¬«      S c c}w c c}w )Nc              3  ó4   K  — | ]  }|j                   –— Œ y ­w©N)r~   ©Ú.0Úes     r>   Ú	<genexpr>z_summarise.<locals>.<genexpr>ù   s   è ø€ Ð'E¹f¸¨¯­¹fùó   ‚c              3  ó4   K  — | ]  }|j                   –— Œ y ­wr¢   )ri   r£   s     r>   r¦   z_summarise.<locals>.<genexpr>ú   s   è ø€ Ð'E¹f¸¨¯
­
¹fùr§   c              3  óF   K  — | ]  }t        |j                  «      –— Œ y ­wr¢   )r6   r�   r£   s     r>   r¦   z_summarise.<locals>.<genexpr>û   s   è ø€ Ð'E¹f¸¬¨A¯G©G¯¹fùs   ‚!r   éÿÿÿÿc              3  óê   K  — | ]k  }|j                   xs d j                  «       dk(  xsD |j                  xs d j                  «       dk(  xs! |j                  xs i j                  d«      du –— Œm y­w)rj   rm   ÚDISQUALIFIEDÚdisqualifiedTN)r}   ry   r~   r|   rw   rx   r£   s     r>   r¦   z_summarise.<locals>.<genexpr>  sw   è ø€ ð ñ ˆAð 
�‰Š�R×ÑÓ  LÑ0ò 	:Ø�L‰LÒ˜B×%Ñ%Ó'¨>Ñ9ò	:à�J‰JÒ˜"×!Ñ! .Ó1°TÐ9ó	:ñ ùs   ‚A1A3rg   rl   rf   rb   ÚHIGHre   ÚMEDIUMÚLOWÚCLEANr¬   >   r®   r¯   rl   ÚWARNÚPASSÚENDEDÚACTIVEc              3  óN   K  — | ]  }|j                   sŒ|j                   –— Œ y ­wr¢   )Úcandidate_idr£   s     r>   r¦   z_summarise.<locals>.<genexpr>"  s   è ø€ Ð:¡˜A¨1¯>«>ˆ��¡ùs   ‚%”%)Ú
access_keyr·   Útotal_eventsÚby_event_typeÚby_severityÚby_roundÚsessions_seenÚfirst_event_atÚlast_event_atrk   Úfraud_statusÚsession_statusÚis_disqualified)r   Úsortedr’   Ú
created_atÚ	timestampÚanyrx   Únextr   ÚlenÚdict)r¸   ÚeventsÚby_typeÚby_sevr¼   r¥   ÚsessionsÚ
timestampsÚfirstÚlastr­   ÚriskrÀ   rÁ   r·   s                  r>   Ú
_summariserÒ   ø   sÈ  € Ü 'Ñ'E¹fÓ'EÓ E€GÜ 'Ñ'E¹fÓ'EÓ E€FÜ 'Ñ'E¹fÓ'EÓ E€Hä Ù$ó"Ù$˜¨¯«ˆ�‹˜fñ"ó €Hô Ù.4ÓV©f¨¸¿ºÈÏËˆ�‰Ò	$˜Ÿ™Ñ	$¨fÑVó€Jñ )ˆJ�qŠM¨d€EÙ(ˆJ�rŠN¨d€Däñ ñ ó	ó €Lñ �v—z‘z *¨aÓ0°1Ò4Ø‰Ø	�‰�F˜AÓ	 !Ò	#Ø‰Ø	�‰�F˜AÓ	 Ò	" f§j¡j°¸1Ó&=ÀÒ&BØ‰Ù	Ø‰àˆñ '‰ØÐ=Ñ=‰VØð ñ
 (4‘^ÁF¹ÐPX€NäÙ:¡Ó:Øó€Lô
 ØØ!Ü˜“[Ü˜7“mÜ˜“LÜ�h“ØØØØØ!Ø%Ø$ôð ùòS"ùò
 	Ws   Á F ÁF Á0F%ÂF%c                óž  ‡	— t        t        «      }| D ]-  }|t        |j                  xs d«         j	                  |«       Œ/ t        «       D �cg c]  }|d   ‘Œ	 c}Š	t        ˆ	fd„|j                  «       D «       «      }‰	|z   }g }|D ]²  }|j                  |g «      }|j	                  t        t        di t        |«      ¤Žt        |«      |r#t        |D �cg c]  }|j                  ‘Œ c}«      ndt        |D �ch c]  }|j                  xs d’Œ c}«      t!        |«      t#        d„ |D «       «      ¬«      «       Œ´ |S c c}w c c}w c c}w )	u  Build the round-by-round stepper used by the UI.

    Returns the canonical track first (Pre-Session â†’ HR) so the stepper
    is always the same shape; any *extra* rounds present in the data
    (Round 7+) are appended in numerical order so unusual flows still
    surface.
    r   Úround_idc              3  ó,   •K  — | ]  }|‰vsŒ|–— Œ y ­wr¢   r™   )r¤   ÚridÚcanonical_idss     €r>   r¦   z_stage_track.<locals>.<genexpr>D  s   øè ø€ ÐR¡o˜s¸ÀMÒ9Q”s¡oùs   ƒ	�rd   Únonec              3  ó^   K  — | ]%  }|j                   xs d j                  «       dk(  –— Œ' y­w)rj   rm   N)r}   ry   r£   s     r>   r¦   z_stage_track.<locals>.<genexpr>P  s'   è ø€ ÐVÉvÈ!˜aŸh™hšn¨"×3Ñ3Ó5¸ÕEÉvùs   ‚+-)r–   Úevent_countÚseverity_maxÚactionsÚflaggedr­   r™   )r   Úlistrš   r�   Úappendr   rÃ   Úkeysrx   r   r   r   rÈ   r�   ri   r}   ÚboolrÆ   )
rÊ   r¼   r¥   r�   Ú	extra_idsÚordered_idsÚoutrÖ   Úbucketr×   s
            @r>   Ú_stage_trackræ   7  s0  ø€ ô 0;¼4Ó/@€HÛˆØ”�Q—W‘W’\ Ó"Ñ#×*Ñ*¨1Õ-ð ô -BÔ,CÓDÑ,C q�Q�z“]Ð,CÑD€MÜÓR h§m¡m¤oÓRÓR€IØ )Ñ+€Kà €CÛˆØ—‘˜c 2Ó&ˆØ�
‰
”<ÜÑ2œn¨SÓ1Ñ2Ü˜F›ÙHNœ¹FÓ'C¹F°q¨¯
«
¸FÑ'CÔDÐTYÜ¹&ÓA¹&°Q˜QŸX™XÒ/¨Ñ/¸&ÑAÓBÜ˜“LÜÑVÉvÓVÓVô
õ 	ð ð €Jùò Eùò (DùÚAs   ÁE ÃEÃ:E
c                óè   — | j                  |¬«      }|st        d|›�«      ‚|D �cg c]  }t        |«      ‘Œ }}|j                  d„ ¬«       t	        t        ||«      t        |«      |¬«      S c c}w )u7   Top-level service: raw events â†’ enriched FraudReport.©r¸   z%no fraud events found for access_key=c                ó>   — | j                   xs | j                  xs dS ©Nrj   )rÄ   rÅ   ©r¥   s    r>   rˆ   z_build_report.<locals>.<lambda>_  s   €  §¡Ò!B°·±Ò!BÀÐ!Br@   r‰   )ÚsummaryÚstage_trackrÊ   )Úfetch_eventsr   rŸ   Úsortr   rÒ   ræ   )r8   r¸   Úrawr   Úenricheds        r>   Ú_build_reportrò   U  s   € à
×
Ñ
 zÐ
Ó
2€CÙÜ$Ø3°J°>ÐBó
ð 	
ñ '*Ó*¡c ”˜• c€HÐ*à‡M�MÑC€MÔDäÜ˜: xÓ0Ü  Ó*Øôð ùò	 +s   §A/c                ó8  ‡ — ‰ j                  dd¬«      d„ «       }‰ j                  d«      ˆ fd„«       }‰ j                  d«      d„ «       }‰ j                  d	t        ¬
«      ˆ fd„«       }‰ j                  dt        ¬
«      t        ddddd¬«      f	 dˆ fd„«       }y )NÚ/F)Úinclude_in_schemac               “  óŒ   K  — t        t        «      j                  «       j                  dz  } t	        t        | dz  «      «      S ­w)Nr.   zfraudreport.html)r   r2   r3   r4   r   r6   )r=   s    r>   Úindexz_register_routes.<locals>.indexn  s9   è ø€ äœ(“^×+Ñ+Ó-×4Ñ4°xÑ?ˆ
ÜœC 
Ð-?Ñ ?Ó@ÓAÐAùs   ‚AAz/api/fraud/healthc               “  ó,   •K  — dd‰ j                   dœS ­w)NTzfraud-report)ÚokÚservicer&   )r&   )r`   s   €r>   Úhealthz _register_routes.<locals>.healtht  s   øè ø€ à ~À#Ç+Á+ÑNÐNùs   ƒz/api/fraud/stagesc               “  ó"   K  — dt        «       iS ­w)NrX   )r   r™   r@   r>   Ústagesz _register_routes.<locals>.stagesy  s   è ø€ àÔ.Ó0Ð1Ð1ùs   ‚z/api/fraud/access-keys)Úresponse_modelc            
   “  óÈ  •K  — ‰j                   j                  } | j                  «       }g }|D ]‚  }| j                  |¬«      }|sŒ|D �cg c]  }t	        |«      ‘Œ }}t        ||«      }|j                  t        ||j                  |j                  |j                  |j                  ¬«      «       Œ„ |j                  d„ d¬«       t        t        |«      |¬«      S c c}w ­w)Nrè   )r¸   r·   rÚ   r¿   rk   c                ó"   — | j                   xs dS rê   )r¿   rë   s    r>   rˆ   z7_register_routes.<locals>.access_keys.<locals>.<lambda>�  s   €  §¡Ò!6°BÐ!6r@   T)rŠ   Úreverse)rW   rX   )r7   r8   Úlist_access_keysrî   rŸ   rÒ   rß   r   r·   r¹   r¿   rk   rï   r   rÈ   )	r8   rà   rX   ÚkÚevsr¥   rñ   Úsummr`   s	           €r>   Úaccess_keysz%_register_routes.<locals>.access_keys~  sÏ   øè ø€ à #§	¡	§¡ˆØ×$Ñ$Ó&ˆØ&(ˆÛˆAØ×#Ñ#¨qÐ#Ó1ˆCÙØÙ,/Ó0©C qœ �
¨CˆHÐ0Ü˜a Ó*ˆDØ�L‰LœØØ!×.Ñ.Ø ×-Ñ-Ø"×0Ñ0ØŸ?™?ôõ ð ð 	�
‰
Ñ6Àˆ
ÔEÜ¤3 u£:°UÔ;Ð;ùò 1ùs   ƒAC"Á
CÁBC"z/api/fraud/report/{access_key}.r   é@   z^[A-Za-z0-9_\-]+$z8Candidate access key (alphanumeric / dash / underscore).)Ú
min_lengthÚ
max_lengthÚpatternr'   c              “  ó    •K  — ‰j                   j                  }	 t        || «      S # t        $ r}t	        dt        |«      ¬«      ‚d }~ww xY w­w)Nr\   )rH   Údetail)r7   r8   rò   r   r	   r6   )r¸   r8   r¥   r`   s      €r>   Úreportz _register_routes.<locals>.report”  sM   øè ø€ ð !$§	¡	§¡ˆð	@Ü   zÓ2Ð2øÜ%ò 	@ô  ¨C¼¸A»Ô?Ð?ûð	@üs$   ƒA›' ¦A§	A°AÁAÁA)r¸   r6   )rx   r   r   ÚFPath)r`   r÷   rû   rý   r  r  s   `     r>   r:   r:   k  sÌ   ø€ ð 	‡W�WˆS E€WÓ*ñBó +ðBð
 	‡W�WÐ Ó!óOó "ðOð 	‡W�WÐ Ó!ñ2ó "ð2ð 	‡W�WÐ%´m€WÓDó<ó Eð<ð* 	‡W�WÐ-¼k€WÓJäØØØØ(ØRô
ð@Øô@ó Kñ@r@   r¢   )r;   zOptional[FraudRepository]Úreturnr   )r`   r   r  ÚNone)r   r   r  r6   )rŒ   z	list[str]r  r6   )r   r   r  r   )r¸   r6   rÊ   úlist[FraudEventOut]r  r   )rÊ   r  r  zlist[StageSummary])r8   r   r¸   r6   r  r   ):Ú__doc__Ú
__future__r   ÚloggingÚcollectionsr   r   Úpathlibr   Útypingr   Úfastapir   r	   r  Úfastapi.middleware.corsr
   Úfastapi.responsesr   r   Úfastapi.staticfilesr   Údata_sourcer   r   r   r   r   r   Úmodelsr   r   r   r   r   r   r   r   Ú	stage_mapr   r   r   r    ÚbasicConfigÚINFOÚ	getLoggerrJ   r?   r9   r{   r„   r�   rŸ   rÒ   ræ   rò   r:   r`   r™   r@   r>   Ú<module>r"     sÊ   ðñõ< #ã ß ,Ý Ý ç 9Ñ 9Ý 2ß 8Ý +÷÷ ÷	÷ 	ó 	÷ó ð €× Ñ Ø
�,‰,ØCõð €g×Ñ˜Ó'€ô#óT(
ð`  a°ÀÑB€óó@?ó!óH<ó~ó<ó,9@ñz ƒl�r@   