(20260218) Updating APIs
This commit is contained in:
@@ -0,0 +1,581 @@
|
||||
"""
|
||||
|
||||
AUTHOR:
|
||||
|
||||
Khushal P Soonderji
|
||||
|
||||
DATE:
|
||||
|
||||
CREATED: Wed, 26th Nov, 2025
|
||||
UPDATED: Wed, 26th Nov, 2025
|
||||
|
||||
OBJECTIVE:
|
||||
|
||||
To achieve so-and-so-objective...
|
||||
|
||||
REFERENCES:
|
||||
|
||||
N/A
|
||||
|
||||
DOWNLOADS:
|
||||
|
||||
N/A
|
||||
|
||||
"""
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** IMPORT ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# To make sibling directories accessible for imports:
|
||||
import sys
|
||||
sys.path.append(".")
|
||||
sys.path.append("..")
|
||||
|
||||
# For system-level activities:
|
||||
import os
|
||||
|
||||
# To work with date and time:
|
||||
import time
|
||||
import datetime
|
||||
|
||||
# To work with tabulate data:
|
||||
import pandas as pd
|
||||
|
||||
# To make API calls:
|
||||
import requests
|
||||
|
||||
# Cosec-related:
|
||||
from cosec_web.cosec_web import CosecWeb
|
||||
|
||||
# My utils:
|
||||
from utils_v2.system import files
|
||||
from utils_v2.string import json
|
||||
from utils_v2.string import regex
|
||||
from utils_v2.date_time import date_time
|
||||
|
||||
# To work with datatypes:
|
||||
from typing import List, Dict, Any, Literal
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** MACROS / ONE-TIME INIT ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# --- Nothing Yet
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** VARIABLES ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# --- Nothing Yet
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** CLASSES ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# --- Nothing Yet
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** FUNCTIONS ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
def remove_special_chars(s: str) -> str:
|
||||
|
||||
return regex.replace(
|
||||
text = s,
|
||||
pattern = r"[^\w\d\- _]",
|
||||
substitute_text = "_"
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def login(
|
||||
tcaoff_creds: dict
|
||||
) -> bool:
|
||||
|
||||
"""
|
||||
Log in to TCAOFF. The retrieved session token is then updated in the input dict itself.
|
||||
:param tcaoff_creds: The set of credentials to use to log in.
|
||||
:return: True if logged in, else False.
|
||||
"""
|
||||
|
||||
# Make the API call:
|
||||
response = requests.post(
|
||||
url = tcaoff_creds["urls"]["login"],
|
||||
json = {
|
||||
"username": tcaoff_creds["creds"]["username"],
|
||||
"password": tcaoff_creds["creds"]["password"],
|
||||
"mode": "cosec"
|
||||
}
|
||||
)
|
||||
|
||||
# If login succeeded:
|
||||
if response.status_code in [200]:
|
||||
response_json = response.json()
|
||||
tcaoff_creds["creds"]["sessionToken"] = response_json["sessionToken"]
|
||||
tcaoff_creds["creds"]["user"] = {
|
||||
"userId": 1234
|
||||
}
|
||||
print("TCAOFF: logged in")
|
||||
return True
|
||||
|
||||
# If login failed:
|
||||
else:
|
||||
print("TCAOFF: log-in failed")
|
||||
return False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def logout(
|
||||
tcaoff_creds: dict
|
||||
) -> bool:
|
||||
|
||||
"""
|
||||
Log out from TCAOFF. The session token is cleared from the in-mem creds.
|
||||
:param tcaoff_creds: The set of credentials to use to log out.
|
||||
:return: True if logged out, else False.
|
||||
"""
|
||||
|
||||
# Make the API call:
|
||||
response = requests.post(
|
||||
url = tcaoff_creds["urls"]["logout"],
|
||||
json = {"username": tcaoff_creds["creds"]["sessionToken"]}
|
||||
)
|
||||
|
||||
# Clear the session details from the in-mem creds:
|
||||
tcaoff_creds["creds"]["sessionToken"] = None
|
||||
tcaoff_creds["creds"]["user"] = None
|
||||
|
||||
# If logout succeeded:
|
||||
if response.status_code in [200]:
|
||||
print("TCAOFF: logged out")
|
||||
return True
|
||||
|
||||
# If login failed:
|
||||
else:
|
||||
print("TCAOFF: log-out failed")
|
||||
return False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def branch_list(
|
||||
tcaoff_creds: dict
|
||||
) -> List[Dict[str, Any]] | None:
|
||||
|
||||
"""
|
||||
List the existing branches.
|
||||
:param tcaoff_creds: The set of credentials to use with the API call.
|
||||
:return: True if logged out, else False.
|
||||
"""
|
||||
|
||||
# Make the API call:
|
||||
response = requests.post(
|
||||
url = tcaoff_creds["urls"]["branchList"],
|
||||
headers = {"X-Session-Token": tcaoff_creds["creds"]["sessionToken"]},
|
||||
json = {"idUser": tcaoff_creds["creds"]["user"]["userId"]}
|
||||
)
|
||||
|
||||
# If the call succeeded:
|
||||
if response.status_code in [200]:
|
||||
response_json = response.json()
|
||||
branches = response_json["data"]["rs0"]
|
||||
print("TCAOFF: branches listed")
|
||||
return branches
|
||||
|
||||
# If the call failed:
|
||||
else:
|
||||
print("TCAOFF: branch-list failed")
|
||||
return None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def branch_add(
|
||||
tcaoff_creds: dict,
|
||||
branch_name: str
|
||||
) -> bool:
|
||||
|
||||
"""
|
||||
Add a new branch.
|
||||
:param tcaoff_creds: The set of credentials to use with the API call.
|
||||
:param branch_name: The name of the branch to add.
|
||||
:return: True if logged out, else False.
|
||||
"""
|
||||
|
||||
# Make the API call:
|
||||
response = requests.post(
|
||||
url = tcaoff_creds["urls"]["branchAdd"],
|
||||
headers = {"X-Session-Token": tcaoff_creds["creds"]["sessionToken"]},
|
||||
json = {
|
||||
"idUser": tcaoff_creds["creds"]["user"]["userId"],
|
||||
"branchName": branch_name
|
||||
}
|
||||
)
|
||||
|
||||
# Debugging:
|
||||
if response.status_code not in [200]:
|
||||
response_json = response.json()
|
||||
print("TCAOFF Branch-Add:", json.to_string(response_json))
|
||||
|
||||
# If the call succeeded:
|
||||
if response.status_code in [200]:
|
||||
print(f"TCAOFF: branch '{branch_name}' added")
|
||||
return True
|
||||
|
||||
# If the call failed:
|
||||
else:
|
||||
print(f"TCAOFF: branch '{branch_name}' NOT added")
|
||||
return False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def department_list(
|
||||
tcaoff_creds: dict
|
||||
) -> List[Dict[str, Any]] | None:
|
||||
|
||||
"""
|
||||
List the existing departments.
|
||||
:param tcaoff_creds: The set of credentials to use with the API call.
|
||||
:return: True if logged out, else False.
|
||||
"""
|
||||
|
||||
# Make the API call:
|
||||
response = requests.post(
|
||||
url = tcaoff_creds["urls"]["deptList"],
|
||||
headers = {"X-Session-Token": tcaoff_creds["creds"]["sessionToken"]},
|
||||
json = {"idUser": tcaoff_creds["creds"]["user"]["userId"]}
|
||||
)
|
||||
|
||||
# If the call succeeded:
|
||||
if response.status_code in [200]:
|
||||
response_json = response.json()
|
||||
branches = response_json["data"]["rs0"]
|
||||
print("TCAOFF: depts. listed")
|
||||
return branches
|
||||
|
||||
# If the call failed:
|
||||
else:
|
||||
print("TCAOFF: dept-list failed")
|
||||
return None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def department_add(
|
||||
tcaoff_creds: dict,
|
||||
department_name: str
|
||||
) -> bool:
|
||||
|
||||
"""
|
||||
Add a new department.
|
||||
:param tcaoff_creds: The set of credentials to use with the API call.
|
||||
:param department_name: The name of the department to add.
|
||||
:return: True if logged out, else False.
|
||||
"""
|
||||
|
||||
# Make the API call:
|
||||
response = requests.post(
|
||||
url = tcaoff_creds["urls"]["deptAdd"],
|
||||
headers = {"X-Session-Token": tcaoff_creds["creds"]["sessionToken"]},
|
||||
json = {
|
||||
"idUser": tcaoff_creds["creds"]["user"]["userId"],
|
||||
"departmentName": department_name
|
||||
}
|
||||
)
|
||||
|
||||
# Debugging:
|
||||
if response.status_code not in [200]:
|
||||
response_json = response.json()
|
||||
print("TCAOFF Dept-Add:", json.to_string(response_json))
|
||||
|
||||
# If the call succeeded:
|
||||
if response.status_code in [200]:
|
||||
print(f"TCAOFF: dept. '{department_name}' added")
|
||||
return True
|
||||
|
||||
# If the call failed:
|
||||
else:
|
||||
print(f"TCAOFF: dept. '{department_name}' NOT added")
|
||||
return False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def team_list(
|
||||
tcaoff_creds: dict
|
||||
) -> List[Dict[str, Any]] | None:
|
||||
|
||||
"""
|
||||
List the existing team members.
|
||||
:param tcaoff_creds: The set of credentials to use with the API call.
|
||||
:return: True if logged out, else False.
|
||||
"""
|
||||
|
||||
# Make the API call:
|
||||
response = requests.post(
|
||||
url = tcaoff_creds["urls"]["teamList"],
|
||||
headers = {"X-Session-Token": tcaoff_creds["creds"]["sessionToken"]},
|
||||
json = {"idUser": tcaoff_creds["creds"]["user"]["userId"]}
|
||||
)
|
||||
|
||||
# If the call succeeded:
|
||||
if response.status_code in [200]:
|
||||
response_json = response.json()
|
||||
branches = response_json["data"]["rs0"]
|
||||
print("TCAOFF: team listed")
|
||||
return branches
|
||||
|
||||
# If the call failed:
|
||||
else:
|
||||
print("TCAOFF: team-list failed")
|
||||
return None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def team_add(
|
||||
tcaoff_creds: dict,
|
||||
branch_id: int,
|
||||
dept_id: int,
|
||||
reporting_to: int | None,
|
||||
team_name: str,
|
||||
email: str,
|
||||
phone_no: str,
|
||||
role: str,
|
||||
username: str,
|
||||
password: str,
|
||||
applicant_notes: dict | list | str = None
|
||||
) -> bool:
|
||||
|
||||
"""
|
||||
Add a new team member.
|
||||
:param tcaoff_creds: The set of credentials to use with the API call.
|
||||
:param dept_id: The id of the department that this team member is working in.
|
||||
:param branch_id: The id of the branch that this team member is working in.
|
||||
:param reporting_to: The id of the senior to whom this team member will report.
|
||||
:param team_name: The name of the team member. This is the full display name. Can be the same as others.
|
||||
:param email: The email id of the team member.
|
||||
:param phone_no: The phone no. of the team member.
|
||||
:param role: The role of the team member in the organization.
|
||||
:param username: The unique username of the team member. Cannot be the same as anyone else.
|
||||
:param password: The password for this team member's login.
|
||||
:param applicant_notes: Optional notes about the team member.
|
||||
:return: True if successful, else False.
|
||||
"""
|
||||
|
||||
# # Ensure that JSON notes are converted to string:
|
||||
# if isinstance(applicant_notes, (list, dict)):
|
||||
# applicant_notes = json.to_string(applicant_notes, no_space = True)
|
||||
|
||||
# Make the API call:
|
||||
response = requests.post(
|
||||
url = tcaoff_creds["urls"]["teamAdd"],
|
||||
headers = {"X-Session-Token": tcaoff_creds["creds"]["sessionToken"]},
|
||||
json = {
|
||||
"branchId": branch_id,
|
||||
"idDepartment": dept_id,
|
||||
"reportingTo": reporting_to,
|
||||
"name": team_name,
|
||||
"email": email,
|
||||
"phoneNo": phone_no,
|
||||
"role": role,
|
||||
"username": username,
|
||||
"password": password,
|
||||
"hierarchy": 1,
|
||||
"applicantNotes": applicant_notes
|
||||
}
|
||||
)
|
||||
|
||||
# Debugging:
|
||||
if response.status_code not in [200]:
|
||||
response_json = response.json()
|
||||
print("TCAOFF Team-Add:", json.to_string(response_json))
|
||||
|
||||
# If the call succeeded:
|
||||
if response.status_code in [200]:
|
||||
print(f"TCAOFF: team '{team_name} ({username})' added")
|
||||
return True
|
||||
|
||||
# If the call failed:
|
||||
else:
|
||||
print(f"TCAOFF: team '{team_name} ({username})' NOT added")
|
||||
return False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def team_update(
|
||||
tcaoff_creds: dict,
|
||||
user_id: int,
|
||||
branch_id: int,
|
||||
dept_id: int,
|
||||
reporting_to: int | None,
|
||||
team_name: str,
|
||||
email: str,
|
||||
phone_no: str,
|
||||
role: str,
|
||||
applicant_notes: dict | list | str = None
|
||||
) -> bool:
|
||||
|
||||
"""
|
||||
Add a new team member.
|
||||
:param tcaoff_creds: The set of credentials to use with the API call.
|
||||
:param dept_id: The id of the department that this team member is working in.
|
||||
:param branch_id: The id of the branch that this team member is working in.
|
||||
:param reporting_to: The id of the senior to whom this team member will report.
|
||||
:param team_name: The name of the team member. This is the full display name. Can be the same as others.
|
||||
:param email: The email id of the team member.
|
||||
:param phone_no: The phone no. of the team member.
|
||||
:param role: The role of the team member in the organization.
|
||||
:param applicant_notes: Optional notes about the team member.
|
||||
:return: True if successful, else False.
|
||||
"""
|
||||
|
||||
# # Ensure that JSON notes are converted to string:
|
||||
# if isinstance(applicant_notes, (list, dict)):
|
||||
# applicant_notes = json.to_string(applicant_notes, no_space = True)
|
||||
|
||||
# Make the API call:
|
||||
response = requests.post(
|
||||
url = tcaoff_creds["urls"]["teamUpdate"],
|
||||
headers = {"X-Session-Token": tcaoff_creds["creds"]["sessionToken"]},
|
||||
json = {
|
||||
"branchId": branch_id,
|
||||
"idDepartment": dept_id,
|
||||
"reportingTo": reporting_to,
|
||||
"name": team_name,
|
||||
"email": email,
|
||||
"phoneNo": phone_no,
|
||||
"role": role,
|
||||
"applicantNotes": applicant_notes
|
||||
}
|
||||
)
|
||||
|
||||
# Debugging:
|
||||
if response.status_code not in [200]:
|
||||
response_json = response.json()
|
||||
print("TCAOFF Team-Update:", json.to_string(response_json))
|
||||
|
||||
# If the call succeeded:
|
||||
if response.status_code in [200]:
|
||||
print(f"TCAOFF: team '{user_id}' updated")
|
||||
return True
|
||||
|
||||
# If the call failed:
|
||||
else:
|
||||
print(f"TCAOFF: team '{user_id}' NOT updated")
|
||||
return False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def attendance_mark(
|
||||
tcaoff_creds: dict,
|
||||
user_id: int,
|
||||
status: Literal["P", "H", "A", "OT"],
|
||||
over_time: int | float,
|
||||
attendance_date: datetime.datetime | None = None,
|
||||
json_notes: dict = None
|
||||
) -> bool:
|
||||
|
||||
"""
|
||||
Add a new department.
|
||||
:param tcaoff_creds: The set of credentials to use with the API call.
|
||||
:param user_id: The id of the user whose attendance is being marked.
|
||||
:param status: The status of the attendance.
|
||||
1. "P" for present,
|
||||
2. "H" for half-day,
|
||||
3. "A" for absent,
|
||||
4. "OT" for over-time.
|
||||
:param over_time: The amount of over-time work in hours.
|
||||
:param attendance_date: The date of the attendance. If not given, today's date will be used.
|
||||
:return: True if logged out, else False.
|
||||
"""
|
||||
|
||||
# Prepare the payload:
|
||||
json_payload = {
|
||||
"date": (
|
||||
date_time.parse_date_time(attendance_date) or
|
||||
date_time.get_current_date_time()
|
||||
).strftime("%Y-%m-%d"),
|
||||
"idUser": user_id,
|
||||
"status": status if over_time <= 0.0 else "OT",
|
||||
"ot": over_time,
|
||||
}
|
||||
|
||||
# Add JSON notes is needed:
|
||||
if json_notes:
|
||||
json_payload["jsonNotes"] = json_notes
|
||||
|
||||
# Make the API call:
|
||||
response = requests.post(
|
||||
url = tcaoff_creds["urls"]["attendanceMark"],
|
||||
headers = {"X-Session-Token": tcaoff_creds["creds"]["sessionToken"]},
|
||||
json = json_payload
|
||||
)
|
||||
|
||||
# Debugging:
|
||||
if response.status_code not in [200]:
|
||||
try:
|
||||
response_json = response.json()
|
||||
print("TCAOFF Attendance-Mark:", json.to_string(response_json))
|
||||
except Exception as e:
|
||||
print("TCAOFF Attendance-Mark:", e)
|
||||
print("TCAOFF Attendance-Mark:", response.content)
|
||||
print("Payload:", json.to_string(json_payload))
|
||||
|
||||
# If the call succeeded:
|
||||
if response.status_code in [200]:
|
||||
print(f"TCAOFF: attendance for '{user_id}' marked")
|
||||
return True
|
||||
|
||||
# If the call failed:
|
||||
else:
|
||||
print(f"TCAOFF: attendance for '{user_id}' NOT marked")
|
||||
return False
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** MAIN PROGRAM ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
pass
|
||||
Reference in New Issue
Block a user