(20260210) Made a faster way to loop through the attendance records in one go to compute the work done.

This commit is contained in:
2026-02-10 18:27:27 +05:30
parent 3b9621f80e
commit 94293cf2fc
2 changed files with 64 additions and 128 deletions
File diff suppressed because one or more lines are too long
+63 -127
View File
@@ -335,8 +335,9 @@ def compute_work_done(
"first_in": first_in, "first_in": first_in,
"last_out": last_out, "last_out": last_out,
"work_seconds": work_seconds, "work_seconds": work_seconds,
"work_ot_seconds": work_ot_seconds,
"work_hours": work_hours, "work_hours": work_hours,
"work_ot_seconds": work_ot_seconds,
"work_ot_hours": work_ot_seconds / (60.0 * 60.0),
"work_status": work_status, "work_status": work_status,
}) })
@@ -353,137 +354,72 @@ async def sync_attendance_to_tcaoff(
verbose: bool = False verbose: bool = False
) -> Dict[str, int]: ) -> Dict[str, int]:
# # Get the list of existing team members from TCAOFF: # Convert the In/Out data to a DataFrame:
# # NOTE: `pseudonym` is the unique username of the user.
# tcaoff_teams = await tcaoff_client.team_list()
# # print("TCAOFF TEAMS:", json.to_string(tcaoff_teams))
#
# # Get a mapping from Cosec id to TCAOFF record:
# cosec_id_to_tcaoff_team = {}
# for t in tcaoff_teams:
# app_notes = json.from_string(t["json_notes"]).get("applicantNotes", {})
# if isinstance(app_notes, str): app_notes = json.from_string(app_notes)
# if not app_notes: continue
# cosec_notes = app_notes.get("cosec", {})
# if not cosec_notes: continue
# print("Cosec Notes:", cosec_notes)
# cosec_id_to_tcaoff_team[cosec_notes.get("User ID") or cosec_notes.get("UserID")] = t
# # print("COSEC to TCAOFF TEAMS:", json.to_string(cosec_id_to_tcaoff_team))
# Convert the data to a DataFrame:
in_out_df = pd.DataFrame(cosec_in_out_summary["report"]) in_out_df = pd.DataFrame(cosec_in_out_summary["report"])
# in_out_df["Punch Time"] = pd.to_datetime(in_out_df["Punch Time"], unit = "s", utc = True).dt.tz_convert("Asia/Kolkata")
print(in_out_df)
in_out_df.info()
# Compute the work done: # Compute the work done:
work_reports = compute_work_done(in_out_df) work_reports = compute_work_done(in_out_df)
print("WORK REPORTS:", work_reports)
print("WORK REPORTS:", json.to_string(work_reports))
# # Convert the dt column to actual dt objects and apply the timezone on them, # Get the list of existing team members from TCAOFF:
# # then enlist the unique dates: # NOTE: `pseudonym` is the unique username of the user.
# in_out_df["Punch Time"] = pd.to_datetime(in_out_df["Punch Time"], unit = "s", utc = True).dt.tz_convert("Asia/Kolkata") tcaoff_teams = await tcaoff_client.team_list()
# unique_dates = sorted(in_out_df['Punch Time'].dt.date.unique())
# print(f"UNIQUE DATES ({len(unique_dates)}):", unique_dates) # Get a mapping from Cosec id to TCAOFF record:
# cosec_id_to_tcaoff_team = {}
# # Get the unique user ids: for t in tcaoff_teams:
# unique_cosec_user_ids = in_out_df["User ID"].unique().tolist() app_notes = json.from_string(t["json_notes"]).get("applicantNotes", {})
# print(f"UNIQUE USER IDS ({len(unique_cosec_user_ids)}):", unique_cosec_user_ids) if isinstance(app_notes, str): app_notes = json.from_string(app_notes)
# if not app_notes: continue
# # We will create all async. tasks for firing attendance marking: cosec_notes = app_notes.get("cosec", {})
# tasks = [] if not cosec_notes: continue
# # print("Cosec Notes:", cosec_notes)
# # For every user: cosec_id_to_tcaoff_team[cosec_notes.get("User ID") or cosec_notes.get("UserID")] = t
# for user_count, cosec_user_id in enumerate(unique_cosec_user_ids):
# # Create the tasks to fire:
# # Debugging: tasks = []
# now_time = date_time.get_current_date_time(as_string = True) for wr in work_reports:
# printer("Cosec User:", cosec_user_id, user_count, now_time)
# # Extract basic details:
# # Find the equivalent TCAOFF team member record: cosec_user_id = wr["user_id"]
# tcaoff_team = cosec_id_to_tcaoff_team.get(cosec_user_id)
# if tcaoff_team is None: # Match it to the TCAOFF team-member:
# print(f"TCAOFF SYNC ERR: Cosesc User Id '{cosec_user_id}' not found in TCAOFF") tcaoff_team = cosec_id_to_tcaoff_team.get(cosec_user_id)
# continue if tcaoff_team is None:
# print(f"TCAOFF SYNC ERR: Cosesc User Id '{cosec_user_id}' not found in TCAOFF")
# # For every date: continue
# for work_dt in unique_dates:
# # Create the task for this attendance:
# # Fetch only the successful events: tasks.append(
# user_allowed_events = in_out_df[ tcaoff_client.attendance_mark(
# (in_out_df["User ID"] == cosec_user_id) & user_id = tcaoff_team["user_id"],
# (in_out_df["Event Status"] == "Allowed") & status = wr["work_status"],
# (in_out_df["Punch Time"].dt.date == work_dt) over_time = wr["work_ot_hours"],
# ] attendance_date = wr["work_date"],
# json_notes = {
# # Debugging: "totHours": wr["work_hours"],
# weekday = work_dt.weekday() + 1 # ... 1 = Monday, 7 = Sunday "firstIn": wr["first_in"],
# events = len(user_allowed_events) "lastOut": wr["last_out"],
# if verbose: printer(cosec_user_id, work_dt, weekday, events) }
# )
# # Check if the summary is empty: )
# if user_allowed_events.empty:
# if verbose: printer("Nothing to sync.", cosec_user_id, work_dt, weekday, events) print("TASKS COUNT:", len(tasks))
# continue
# def chunks(lst, size = 10):
# # We note down the first "In" time of the user for i in range(0, len(lst), size):
# # and the last "Out" time of the user: yield lst[i:i + size]
# first_in = None
# last_out = None results = []
# for idx, row in user_allowed_events.iterrows(): count = 1
# # print(f"{row['I/O Type']: >4} at row: {idx: <5} | ts: {row['Punch Time']: <15} | dt: {date_time.parse_date_time(row['Punch Time'])}") for chunk in chunks(tasks[-35:]):
# if row["I/O Type"] == "In" and first_in is None: first_in = row["Punch Time"].timestamp() print("Task Chunk:", count)
# if row["I/O Type"] == "Out" and first_in is not None: last_out = row["Punch Time"].timestamp() _res = await asyncio.gather(*chunk)
# results += _res
# # Figure out the worked time: count += 1
# if first_in is None and last_out is None:
# time_worked = {
# "work_seconds": 0.0, print(f"RESULTS ({len(results)}):", results)
# "work_date": work_dt print("SUCCESS:", sum(results))
# }
# elif first_in is None or last_out is None:
# time_worked = {
# "work_seconds": 60 * 60 * 10.0, # ... 10 hours represented in seconds.
# "work_date": work_dt
# }
# else:
# time_worked = {
# "work_seconds": last_out - first_in,
# "work_date": work_dt
# }
#
# # Figure out the number of hours worked:
# hours_worked = time_worked["work_seconds"] / (60.0 * 60.0)
# if hours_worked > 10.0: status = "OT"
# elif 7.5 < hours_worked <= 10.0: status = "P"
# elif 2.5 < hours_worked <= 5.0: status = "H"
# else: status = "A"
#
# # Create this TCAOFF task:
# task = tcaoff_client.attendance_mark(
# user_id = tcaoff_team["user_id"],
# status = status,
# over_time = max(0.0, hours_worked - 10.0),
# attendance_date = time_worked["work_date"].strftime("%Y-%m-%d"),
# json_notes = {
# "totHours": hours_worked,
# "firstIn": first_in,
# "lastOut": last_out,
# }
# )
# tasks.append(task)
#
# # break
#
# print("TASK COUNT:", len(tasks))
#
# # Now we fire all the tasks:
# now_time = date_time.get_current_date_time(as_string = True)
# printer("Marking Attendance", len(tasks), now_time)
# results = await asyncio.gather(*tasks)
# now_time = date_time.get_current_date_time(as_string = True)
# printer("Marked Attendance", len(tasks), now_time, results)
# ***************************************************************************************************************** # *****************************************************************************************************************