1+ from typing import Optional , List , Dict , Any
2+ from dataclasses import dataclass , field
3+ from datetime import datetime , timezone
4+ import hashlib
5+ import json
6+
7+
8+ @dataclass
9+ class AuditEvent :
10+ event_id : str
11+ timestamp : datetime
12+ event_type : str
13+ roll_number : str
14+ session_id : Optional [str ]
15+ payload : Dict [str , Any ]
16+ metadata : Optional [Dict [str , Any ]] = None
17+
18+ def to_dict (self ) -> Dict [str , Any ]:
19+ return {
20+ "event_id" : self .event_id ,
21+ "timestamp" : self .timestamp .isoformat (),
22+ "event_type" : self .event_type ,
23+ "roll_number" : self .roll_number ,
24+ "session_id" : self .session_id ,
25+ "payload" : self .payload ,
26+ "metadata" : self .metadata ,
27+ }
28+
29+
30+ class AuditLogService :
31+ def __init__ (self ):
32+ self ._logs : List [AuditEvent ] = []
33+
34+ def _generate_event_id (self , roll_number : str , event_type : str , timestamp : datetime ) -> str :
35+ raw = f"{ timestamp .isoformat ()} :{ roll_number } :{ event_type } "
36+ return hashlib .sha256 (raw .encode ()).hexdigest ()[:16 ]
37+
38+ def log_verification_attempt (
39+ self ,
40+ roll_number : str ,
41+ session_id : Optional [str ],
42+ capture_info : Dict [str , Any ],
43+ result : str ,
44+ confidence : float ,
45+ ) -> AuditEvent :
46+ event = AuditEvent (
47+ event_id = self ._generate_event_id (roll_number , "verification_attempt" , datetime .now (timezone .utc )),
48+ timestamp = datetime .now (timezone .utc ),
49+ event_type = "verification_attempt" ,
50+ roll_number = roll_number ,
51+ session_id = session_id ,
52+ payload = {
53+ "result" : result ,
54+ "confidence" : confidence ,
55+ "capture" : capture_info ,
56+ },
57+ )
58+ self ._logs .append (event )
59+ return event
60+
61+ def log_match (
62+ self ,
63+ roll_number : str ,
64+ session_id : Optional [str ],
65+ similarity : float ,
66+ confidence : float ,
67+ ) -> AuditEvent :
68+ event = AuditEvent (
69+ event_id = self ._generate_event_id (roll_number , "face_match" , datetime .now (timezone .utc )),
70+ timestamp = datetime .now (timezone .utc ),
71+ event_type = "face_match" ,
72+ roll_number = roll_number ,
73+ session_id = session_id ,
74+ payload = {
75+ "similarity" : similarity ,
76+ "confidence" : confidence ,
77+ },
78+ )
79+ self ._logs .append (event )
80+ return event
81+
82+ def log_mismatch (
83+ self ,
84+ roll_number : str ,
85+ session_id : Optional [str ],
86+ similarity : float ,
87+ confidence : float ,
88+ threshold : float ,
89+ ) -> AuditEvent :
90+ event = AuditEvent (
91+ event_id = self ._generate_event_id (roll_number , "face_mismatch" , datetime .now (timezone .utc )),
92+ timestamp = datetime .now (timezone .utc ),
93+ event_type = "face_mismatch" ,
94+ roll_number = roll_number ,
95+ session_id = session_id ,
96+ payload = {
97+ "similarity" : similarity ,
98+ "confidence" : confidence ,
99+ "threshold" : threshold ,
100+ },
101+ )
102+ self ._logs .append (event )
103+ return event
104+
105+ def log_suspicious_event (
106+ self ,
107+ roll_number : str ,
108+ session_id : Optional [str ],
109+ reason : str ,
110+ conflict_id : Optional [str ] = None ,
111+ matched_rolls : Optional [List [str ]] = None ,
112+ ) -> AuditEvent :
113+ event = AuditEvent (
114+ event_id = self ._generate_event_id (roll_number , "suspicious_identity" , datetime .now (timezone .utc )),
115+ timestamp = datetime .now (timezone .utc ),
116+ event_type = "suspicious_identity" ,
117+ roll_number = roll_number ,
118+ session_id = session_id ,
119+ payload = {
120+ "reason" : reason ,
121+ "conflict_id" : conflict_id ,
122+ "matched_rolls" : matched_rolls or [],
123+ },
124+ )
125+ self ._logs .append (event )
126+ return event
127+
128+ def log_admin_override (
129+ self ,
130+ roll_number : str ,
131+ session_id : Optional [str ],
132+ conflict_id : str ,
133+ reviewer_id : str ,
134+ approved : bool ,
135+ reason : str ,
136+ ) -> AuditEvent :
137+ event = AuditEvent (
138+ event_id = self ._generate_event_id (roll_number , "admin_override" , datetime .now (timezone .utc )),
139+ timestamp = datetime .now (timezone .utc ),
140+ event_type = "admin_override" ,
141+ roll_number = roll_number ,
142+ session_id = session_id ,
143+ payload = {
144+ "conflict_id" : conflict_id ,
145+ "reviewer_id" : reviewer_id ,
146+ "approved" : approved ,
147+ "reason" : reason ,
148+ },
149+ )
150+ self ._logs .append (event )
151+ return event
152+
153+ def log_examination_access (
154+ self ,
155+ roll_number : str ,
156+ session_id : str ,
157+ granted : bool ,
158+ reason : Optional [str ] = None ,
159+ ) -> AuditEvent :
160+ event = AuditEvent (
161+ event_id = self ._generate_event_id (roll_number , "examination_access" , datetime .now (timezone .utc )),
162+ timestamp = datetime .now (timezone .utc ),
163+ event_type = "examination_access" ,
164+ roll_number = roll_number ,
165+ session_id = session_id ,
166+ payload = {
167+ "granted" : granted ,
168+ "reason" : reason ,
169+ },
170+ )
171+ self ._logs .append (event )
172+ return event
173+
174+ def get_timeline (self , roll_number : Optional [str ] = None ) -> List [Dict [str , Any ]]:
175+ events = self ._logs
176+ if roll_number :
177+ events = [e for e in events if e .roll_number == roll_number ]
178+ return [e .to_dict () for e in sorted (events , key = lambda x : x .timestamp )]
179+
180+ def get_events_by_type (self , event_type : str ) -> List [Dict [str , Any ]]:
181+ events = [e for e in self ._logs if e .event_type == event_type ]
182+ return [e .to_dict () for e in sorted (events , key = lambda x : x .timestamp )]
183+
184+ def get_session_timeline (self , session_id : str ) -> List [Dict [str , Any ]]:
185+ events = [e for e in self ._logs if e .session_id == session_id ]
186+ return [e .to_dict () for e in sorted (events , key = lambda x : x .timestamp )]
187+
188+ def search (self , query : str ) -> List [Dict [str , Any ]]:
189+ query_lower = query .lower ()
190+ results = []
191+ for event in self ._logs :
192+ payload_str = json .dumps (event .payload ).lower ()
193+ if query_lower in payload_str or query_lower in event .roll_number .lower ():
194+ results .append (event .to_dict ())
195+ return results
196+
197+ def clear (self ):
198+ self ._logs .clear ()
199+
200+ def count (self ) -> int :
201+ return len (self ._logs )
0 commit comments