(20260216) Attendance mark is now truly async.

This commit is contained in:
2026-02-16 14:32:43 +05:30
parent beca5e3127
commit 3079a8f6f0
5 changed files with 67 additions and 29 deletions
+6 -2
View File
@@ -461,7 +461,7 @@ class AsyncTheCAOffice:
# applicant_notes = json.to_string(applicant_notes, no_space = True) # applicant_notes = json.to_string(applicant_notes, no_space = True)
# Make the API call: # Make the API call:
response = requests.post( response = await self._http_client.post(
url = self.TEAM_ADD_URL, url = self.TEAM_ADD_URL,
headers = {"X-Session-Token": self.__session_token}, headers = {"X-Session-Token": self.__session_token},
json = { json = {
@@ -526,6 +526,8 @@ class AsyncTheCAOffice:
:return: True if the attendance was marked, else False. :return: True if the attendance was marked, else False.
""" """
start_time = time.time()
# Prepare the payload: # Prepare the payload:
json_payload = { json_payload = {
"date": ( "date": (
@@ -542,7 +544,7 @@ class AsyncTheCAOffice:
json_payload["jsonNotes"] = json_notes json_payload["jsonNotes"] = json_notes
# Make the API call: # Make the API call:
response = requests.post( response = await self._http_client.post(
url = self.ATTENDANCE_MARK_URL, url = self.ATTENDANCE_MARK_URL,
headers = {"X-Session-Token": self.__session_token}, headers = {"X-Session-Token": self.__session_token},
json = json_payload json = json_payload
@@ -564,6 +566,7 @@ class AsyncTheCAOffice:
"Attendance marked.", "Attendance marked.",
user_id, user_id,
) )
print("ONE TIME TAKEN:", time.time() - start_time)
return True return True
# If the call failed: # If the call failed:
@@ -575,6 +578,7 @@ class AsyncTheCAOffice:
user_id, user_id,
response_json response_json
) )
print("ONE TIME TAKEN:", time.time() - start_time)
return False return False
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+14 -2
View File
@@ -164,6 +164,7 @@ def get_muster_roll(
# Start by assuming failure: # Start by assuming failure:
success = False success = False
report_path = None
# If test mode: # If test mode:
if test_mode: if test_mode:
@@ -232,7 +233,7 @@ def get_muster_roll(
# Sort by hierarchy (BFS): # Sort by hierarchy (BFS):
pass pass
print(f"MUSTER ROLL ({len(report_data)}):", json.to_string(report_data)) # print(f"MUSTER ROLL ({len(report_data)}):", json.to_string(report_data))
# Save the data to a JSON file: # Save the data to a JSON file:
json.to_file( json.to_file(
@@ -428,6 +429,7 @@ def get_in_out_summary(
# Start by assuming failure: # Start by assuming failure:
success = False success = False
report_path = None
# If test mode: # If test mode:
if test_mode: if test_mode:
@@ -546,16 +548,19 @@ def compute_work_done(
punch_dt = date_time.parse_date_time(row["Punch Time"], timezone = date_time.TIMEZONE_IST) punch_dt = date_time.parse_date_time(row["Punch Time"], timezone = date_time.TIMEZONE_IST)
punch_date = punch_dt.strftime("%Y-%m-%d") punch_date = punch_dt.strftime("%Y-%m-%d")
punch_ts = punch_dt.timestamp() punch_ts = punch_dt.timestamp()
punch_loc = row[" Device/Source Detail"]
# Handle the first in time: # Handle the first in time:
if row["I/O Type"] == "In": if row["I/O Type"] == "In":
if work_reports[user_id][punch_date].get("first_in") is None: if work_reports[user_id][punch_date].get("first_in") is None:
work_reports[user_id][punch_date]["first_in"] = punch_ts work_reports[user_id][punch_date]["first_in"] = punch_ts
work_reports[user_id][punch_date]["first_in_loc"] = punch_loc
# Handle the last out time: # Handle the last out time:
if row["I/O Type"] == "Out": if row["I/O Type"] == "Out":
if work_reports[user_id][punch_date].get("first_in") is not None: if work_reports[user_id][punch_date].get("first_in") is not None:
work_reports[user_id][punch_date]["last_out"] = punch_ts work_reports[user_id][punch_date]["last_out"] = punch_ts
work_reports[user_id][punch_date]["last_out_loc"] = punch_loc
# Now use the first_in and last out times of each record to figure out the amount of work done: # Now use the first_in and last out times of each record to figure out the amount of work done:
for user_id, user_reports in work_reports.items(): for user_id, user_reports in work_reports.items():
@@ -569,7 +574,9 @@ def compute_work_done(
# Extract, clean and compute punch timing: # Extract, clean and compute punch timing:
first_in = punch_info.get("first_in") first_in = punch_info.get("first_in")
first_in_loc = punch_info.get("first_in_loc")
last_out = punch_info.get("last_out") last_out = punch_info.get("last_out")
last_out_loc = punch_info.get("last_out_loc")
# When the user has a valid in-time, but no known out time, # When the user has a valid in-time, but no known out time,
# we assume that he worked a full day: # we assume that he worked a full day:
@@ -606,7 +613,9 @@ def compute_work_done(
"user_id": user_id, "user_id": user_id,
"work_date": punch_date, "work_date": punch_date,
"first_in": first_in, "first_in": first_in,
"first_in_loc": first_in_loc,
"last_out": last_out, "last_out": last_out,
"last_out_loc": last_out_loc,
"work_seconds": work_seconds, "work_seconds": work_seconds,
"work_hours": work_hours, "work_hours": work_hours,
"work_ot_seconds": work_ot_seconds, "work_ot_seconds": work_ot_seconds,
@@ -695,7 +704,8 @@ async def sync_attendance_to_tcaoff(
yield lst[i:i + size] yield lst[i:i + size]
count = 0 count = 0
for chunk in chunks(tasks[:]): for chunk in chunks(tasks[:30]):
start_time = time.time()
count += 1 count += 1
printer("Task Chunk:", count) printer("Task Chunk:", count)
_res = await asyncio.gather(*chunk) _res = await asyncio.gather(*chunk)
@@ -704,6 +714,8 @@ async def sync_attendance_to_tcaoff(
results["success"] += success results["success"] += success
results["failure"] += failure results["failure"] += failure
results["total"] += len(_res) results["total"] += len(_res)
print("BATCH TIME TAKEN:", time.time() - start_time)
print("\n\n---\n\n")
# Done here: # Done here:
printer(results) printer(results)
+44 -22
View File
@@ -137,7 +137,7 @@ async def yesterday_cron(
# for the driver's resources to get freed: # for the driver's resources to get freed:
success = common.get_in_out_summary( success = common.get_in_out_summary(
cosec_creds = cosec_creds, cosec_creds = cosec_creds,
from_dt = date_time.get_current_ist_date_time() - datetime.timedelta(days = 7), from_dt = date_time.get_current_ist_date_time() - datetime.timedelta(days = 2),
to_dt = date_time.get_current_ist_date_time(), to_dt = date_time.get_current_ist_date_time(),
cache_file = common.PREV_DAY_IN_OUT_SUMMARY_CACHE_FILE, cache_file = common.PREV_DAY_IN_OUT_SUMMARY_CACHE_FILE,
test_mode = test_mode test_mode = test_mode
@@ -196,34 +196,56 @@ async def today_cron(
# Try the part that needs COSEC: # Try the part that needs COSEC:
try: try:
# Get the Muster Roll and then wait # # MUSTER-ROLL:
#
# # Get the Muster Roll and then wait
# # for the driver's resources to get freed:
# success = common.get_muster_roll(
# cosec_creds = cosec_creds,
# on_date = date_time.get_current_ist_date_time().replace(
# hour = 0,
# minute = 0,
# second = 0
# ),
# cache_file = common.PREV_DAY_IN_OUT_SUMMARY_CACHE_FILE,
# test_mode = test_mode
# )
#
# # Sync data between Cosec and TCAOFF:
# if success:
# print("MUSTER ROLL: Sync'ing with TCAOFF")
# cosec_muster_roll = json.from_file(common.MUSTER_ROLL_CACHE_FILE)
# await common.sync_branches_to_tcaoff(
# tcaoff_client = tcaoff_client,
# cosec_muster_roll = cosec_muster_roll,
# )
# await common.sync_departments_to_tcaoff(
# tcaoff_client = tcaoff_client,
# cosec_muster_roll = cosec_muster_roll,
# )
# await common.sync_teams_to_tcaoff(
# tcaoff_client = tcaoff_client,
# cosec_muster_roll = cosec_muster_roll,
# )
# IN-OUT SUMMARY:
# Get the previous day's In/Out Summary and then wait
# for the driver's resources to get freed: # for the driver's resources to get freed:
success = common.get_muster_roll( success = common.get_in_out_summary(
cosec_creds = cosec_creds, cosec_creds = cosec_creds,
on_date = date_time.get_current_ist_date_time().replace( from_dt = date_time.get_current_ist_date_time().replace(hour = 0, minute = 0, second = 0, microsecond = 0),
hour = 0, to_dt = date_time.get_current_ist_date_time(),
minute = 0, cache_file = common.IN_OUT_SUMMARY_CACHE_FILE,
second = 0
),
cache_file = common.PREV_DAY_IN_OUT_SUMMARY_CACHE_FILE,
test_mode = test_mode test_mode = test_mode
) )
# Sync data between Cosec and TCAOFF: # Sync data between Cosec and TCAOFF:
if success: if success:
print("MUSTER ROLL: Sync'ing with TCAOFF") printer("IN/OUT SUMMARY: Sync'ing with TCAOFF")
cosec_muster_roll = json.from_file(common.MUSTER_ROLL_CACHE_FILE) await common.sync_attendance_to_tcaoff(
await common.sync_branches_to_tcaoff(
tcaoff_client = tcaoff_client, tcaoff_client = tcaoff_client,
cosec_muster_roll = cosec_muster_roll, cosec_in_out_summary = json.from_file(common.IN_OUT_SUMMARY_CACHE_FILE),
)
await common.sync_departments_to_tcaoff(
tcaoff_client = tcaoff_client,
cosec_muster_roll = cosec_muster_roll,
)
await common.sync_teams_to_tcaoff(
tcaoff_client = tcaoff_client,
cosec_muster_roll = cosec_muster_roll,
) )
# If something goes wrong in the COSEC step: # If something goes wrong in the COSEC step:
@@ -262,7 +284,7 @@ async def set_scheduler(
# await yesterday_cron( # await yesterday_cron(
# cosec_creds = copy.deepcopy(cosec_creds), # cosec_creds = copy.deepcopy(cosec_creds),
# tcaoff_client = tcaoff_client, # tcaoff_client = tcaoff_client,
# test_mode = test_mode # # test_mode = test_mode
# ) # )
await today_cron( await today_cron(
cosec_creds = copy.deepcopy(cosec_creds), cosec_creds = copy.deepcopy(cosec_creds),