(20251114) Ready to start API testing.
This commit is contained in:
+2
-1
@@ -5,4 +5,5 @@ __pycache__/
|
|||||||
|
|
||||||
*.pem
|
*.pem
|
||||||
*.pyc
|
*.pyc
|
||||||
*.pyd
|
*.pyd
|
||||||
|
/downloads/
|
||||||
|
|||||||
@@ -0,0 +1,244 @@
|
|||||||
|
"""
|
||||||
|
|
||||||
|
AUTHOR:
|
||||||
|
|
||||||
|
Khushal P Soonderji
|
||||||
|
|
||||||
|
DATE:
|
||||||
|
|
||||||
|
Fri, 14th Nov, 2025
|
||||||
|
|
||||||
|
OBJECTIVE:
|
||||||
|
|
||||||
|
To build APIs to serve In/Out Reports from Cosec's Web portal.
|
||||||
|
|
||||||
|
REFERENCES:
|
||||||
|
|
||||||
|
N/A
|
||||||
|
|
||||||
|
DOWNLOADS:
|
||||||
|
|
||||||
|
N/A
|
||||||
|
|
||||||
|
NOTES:
|
||||||
|
|
||||||
|
N/A
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** IMPORT ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
# To make sibling directories accessible for imports:
|
||||||
|
import sys
|
||||||
|
sys.path.append(".")
|
||||||
|
sys.path.append("..")
|
||||||
|
|
||||||
|
# For system-level activities:
|
||||||
|
import os
|
||||||
|
|
||||||
|
# For using Quart:
|
||||||
|
from quart import Blueprint, current_app, request
|
||||||
|
|
||||||
|
# My utils:
|
||||||
|
from utils_v2.string import json
|
||||||
|
from utils_v2.date_time import date_time
|
||||||
|
from utils_v2.system import files
|
||||||
|
from utils_v2.api.codes import StatusCodes, HttpCodes
|
||||||
|
from utils_v2.api.response import ResponseModel
|
||||||
|
from utils_v2.api.async_quart import (
|
||||||
|
set_api_version,
|
||||||
|
read_input,
|
||||||
|
get_session_info,
|
||||||
|
log_request_to_mongo,
|
||||||
|
log_chain_to_mongo,
|
||||||
|
should_not_be_under_maintenance,
|
||||||
|
only_whitelisted_ips,
|
||||||
|
limit_rate,
|
||||||
|
validate_input,
|
||||||
|
handle_cancelled_request
|
||||||
|
)
|
||||||
|
|
||||||
|
# Models:
|
||||||
|
from backend.models.api.common import SimpleCosecCredentialsHeaders
|
||||||
|
from backend.models.api.reports.in_out_report import CosecInOutReportRequestData
|
||||||
|
|
||||||
|
# Cosec-related:
|
||||||
|
from cosec_web.cosec_web import CosecWeb
|
||||||
|
|
||||||
|
# Common:
|
||||||
|
from backend.shared import constants
|
||||||
|
|
||||||
|
# To work with dat and time:
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
# For asynchronous activities:
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** MACROS / ONE-TIME INIT ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
# Related to Quart:
|
||||||
|
in_out_report_bp = Blueprint("in_out_report", __name__)
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** VARIABLES ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
# --- Nothing Yet
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** FUNCTIONS ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
@in_out_report_bp.record_once
|
||||||
|
def init(blueprint_setup_state):
|
||||||
|
|
||||||
|
# This gets called when the blueprints is registered.
|
||||||
|
# Consider this to be a one-time setup for the whole blueprints:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
@in_out_report_bp.route("/generate", methods = ["GET"])
|
||||||
|
@set_api_version(api_version = "1.0.0")
|
||||||
|
@read_input(sanitize_headers = False, sanitize_data = False)
|
||||||
|
# @get_session_info(key = "X-User-Id", session_coro = "get_session")
|
||||||
|
# @log_request_to_mongo(
|
||||||
|
# attr_name = "logs_mongo",
|
||||||
|
# project = constants.PROJECT_NAME,
|
||||||
|
# log_type = constants.MODULE_NAME,
|
||||||
|
# operation = "ytLnkAddApi",
|
||||||
|
# log_input = True,
|
||||||
|
# log_output = True,
|
||||||
|
# sensitive_keys = None
|
||||||
|
# )
|
||||||
|
# @log_chain_to_mongo(attr_name = "logs_mongo")
|
||||||
|
@should_not_be_under_maintenance(attr_name = "is_under_maintenance")
|
||||||
|
@validate_input(
|
||||||
|
header_validator = lambda x: SimpleCosecCredentialsHeaders(**x).model_dump(),
|
||||||
|
data_validator = lambda x: CosecInOutReportRequestData(**x)
|
||||||
|
)
|
||||||
|
@handle_cancelled_request()
|
||||||
|
async def in_out_report_generate(
|
||||||
|
inbound_headers: SimpleCosecCredentialsHeaders | dict = None,
|
||||||
|
inbound_data: CosecInOutReportRequestData | dict = None,
|
||||||
|
inbound_files: dict = None,
|
||||||
|
**kwargs
|
||||||
|
):
|
||||||
|
|
||||||
|
"""
|
||||||
|
To automate the web browser interaction with Cosec's web portal and return a JSON of the actual report content.
|
||||||
|
:param inbound_headers: auto-extracted by the decorators.
|
||||||
|
:param inbound_data: auto-extracted by the decorators.
|
||||||
|
:param inbound_files: auto-extracted by the decorators.
|
||||||
|
:param kwargs: Any number of extra inputs supplied by the decorators.
|
||||||
|
:return: A standard response structure.
|
||||||
|
"""
|
||||||
|
|
||||||
|
current_app.printer("Hi")
|
||||||
|
|
||||||
|
# Start by assuming failure:
|
||||||
|
success = False
|
||||||
|
report_data = None
|
||||||
|
|
||||||
|
# Figure out the paths:
|
||||||
|
base_dir = files.get_parent_directory(
|
||||||
|
files.get_file_directory(include_filename = False),
|
||||||
|
depth = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
# Put together the directory for the drivers, the downloads, etc.:
|
||||||
|
chrome_driver_dir = os.path.join(base_dir, r"drivers/chrome")
|
||||||
|
user_data_dir = os.path.join(base_dir, r"browser/user_data")
|
||||||
|
downloads_dir = os.path.join(base_dir, r"downloads")
|
||||||
|
|
||||||
|
# Show all the paths for debugging:
|
||||||
|
current_app.printer("PATHS:", chrome_driver_dir, user_data_dir, downloads_dir)
|
||||||
|
|
||||||
|
# Create an instance of the automation object:
|
||||||
|
cosec = CosecWeb(
|
||||||
|
cosec_url = inbound_headers.cosecUrl,
|
||||||
|
username = inbound_headers.cosecUsername,
|
||||||
|
password = inbound_headers.cosecPassword,
|
||||||
|
driver_dir = chrome_driver_dir,
|
||||||
|
user_data_dir = user_data_dir,
|
||||||
|
downloads_dir = downloads_dir,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Perform the login:
|
||||||
|
cosec.login(initial_sleep = 2.5)
|
||||||
|
|
||||||
|
# Get the in/out report:
|
||||||
|
report_path = cosec.get_in_out_summary(
|
||||||
|
initial_sleep = 1.0,
|
||||||
|
from_date = inbound_data.fromDate,
|
||||||
|
to_date = inbound_data.toDate,
|
||||||
|
group_ids = inbound_data.groupIds,
|
||||||
|
download_timeout = 60.0
|
||||||
|
)
|
||||||
|
|
||||||
|
# Log out to end the cycle:
|
||||||
|
cosec.logout()
|
||||||
|
|
||||||
|
# Close the browser window:
|
||||||
|
cosec.quit()
|
||||||
|
|
||||||
|
# If we didn't get any path, the download failed:
|
||||||
|
if report_path is None:
|
||||||
|
success = False
|
||||||
|
|
||||||
|
# Convert the In/Out Summary to a Pandas DF:
|
||||||
|
else:
|
||||||
|
success = True
|
||||||
|
report_df = cosec.read_in_out_summary_xls(report_path)
|
||||||
|
report_data = report_df.to_dict(orient = "records")
|
||||||
|
|
||||||
|
# ┳┓
|
||||||
|
# ┣┫┏┓┏┏┓┏┓┏┓┏┏┓
|
||||||
|
# ┛┗┗ ┛┣┛┗┛┛┗┛┗
|
||||||
|
# ┛
|
||||||
|
|
||||||
|
if success: return ResponseModel(
|
||||||
|
status_code = StatusCodes.OK,
|
||||||
|
http_code = HttpCodes.SUCCESS,
|
||||||
|
data = report_data,
|
||||||
|
message = f"Successfully fetched {len(report_data)} records."
|
||||||
|
)
|
||||||
|
else: return ResponseModel(
|
||||||
|
status_code = StatusCodes.FAILED,
|
||||||
|
http_code = HttpCodes.INTERNAL_SERVER_ERROR,
|
||||||
|
message = f"Failed to fetch records. Please try again."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** MAIN PROGRAM ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
pass
|
||||||
@@ -0,0 +1,413 @@
|
|||||||
|
"""
|
||||||
|
|
||||||
|
AUTHOR:
|
||||||
|
|
||||||
|
Khushal P Soonderji
|
||||||
|
|
||||||
|
DATE:
|
||||||
|
|
||||||
|
Fri, 14th Nov, 2025
|
||||||
|
|
||||||
|
OBJECTIVE:
|
||||||
|
|
||||||
|
This is the central location for the Quart module.
|
||||||
|
We define the app here, and import and attach all blueprints here.
|
||||||
|
|
||||||
|
REFERENCES:
|
||||||
|
|
||||||
|
N/A
|
||||||
|
|
||||||
|
DOWNLOADS:
|
||||||
|
|
||||||
|
N/A
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** IMPORT ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
# To make sibling directories accessible for imports:
|
||||||
|
import sys
|
||||||
|
sys.path.append("blueprints")
|
||||||
|
sys.path.append("")
|
||||||
|
|
||||||
|
# For system level activities:
|
||||||
|
import gc
|
||||||
|
import os
|
||||||
|
import psutil
|
||||||
|
|
||||||
|
# For using Quart:
|
||||||
|
from quart import Quart, current_app
|
||||||
|
from quart_cors import cors
|
||||||
|
|
||||||
|
# My utils:
|
||||||
|
from utils_v2.date_time import date_time
|
||||||
|
from utils_v2.api.async_quart import (
|
||||||
|
set_api_version,
|
||||||
|
log_request_to_mongo,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Shared elements:
|
||||||
|
from backend.shared import constants
|
||||||
|
|
||||||
|
# To make REST API calls:
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
# For debugging:
|
||||||
|
from icecream import IceCreamDebugger
|
||||||
|
|
||||||
|
# All the blueprints:
|
||||||
|
from backend.api.blueprints.reports.in_out_report import in_out_report_bp
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** MACROS / ONE-TIME INIT ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
# Quart related:
|
||||||
|
MODULE_BASE = constants.MODULE_NAME
|
||||||
|
APP_VERSION = constants.APP_VERSION
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** VARIABLES ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
# The Quart app:
|
||||||
|
app = Quart(__name__)
|
||||||
|
app = cors(app)
|
||||||
|
# ---
|
||||||
|
app.register_blueprint(in_out_report_bp, url_prefix = f"/{MODULE_BASE}/reports/in-out")
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** FUNCTIONS ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
@app.before_serving
|
||||||
|
@set_api_version(api_version = APP_VERSION)
|
||||||
|
# @log_request_to_mongo(
|
||||||
|
# attr_name = "logs_mongo",
|
||||||
|
# project = constants.PROJECT_NAME,
|
||||||
|
# log_type = constants.MODULE_NAME,
|
||||||
|
# operation = "apiStart",
|
||||||
|
# log_input = True,
|
||||||
|
# log_output = True
|
||||||
|
# )
|
||||||
|
async def app_startup(**kwargs):
|
||||||
|
|
||||||
|
"""
|
||||||
|
To initialize the variables that you would like to use in this module.
|
||||||
|
WARNING: ALL VARIABLES WILL BE INITIALIZED 'n' NUMBER OF TIMES, WHERE 'n' IS THE COUNT OF WORKERS DEPLOYED.
|
||||||
|
SO, IF YOU WANT TO CONNECT TO A DATABASE AND YOU ALLOW A POOL-SIZE OF 10 AND IF YOU DEPLOY 4 WORKERS, YOU WILL END
|
||||||
|
UP WITH 40 CONNECTIONS TO THE DATABASE.
|
||||||
|
:return: None.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Safe-halt mechanism for upgrades (for a single-worker run):
|
||||||
|
current_app.is_under_maintenance = False
|
||||||
|
|
||||||
|
# ┳┓ ┓ •
|
||||||
|
# ┃┃┏┓┣┓┓┏┏┓┏┓┓┏┓┏┓
|
||||||
|
# ┻┛┗ ┗┛┗┻┗┫┗┫┗┛┗┗┫
|
||||||
|
# ┛ ┛ ┛
|
||||||
|
|
||||||
|
# Debugging:
|
||||||
|
enable_debugging = True if os.environ["DEBUG"].strip().lower() == "true" else False
|
||||||
|
current_app.printer = IceCreamDebugger(prefix = f"{MODULE_BASE} (Q) | ", includeContext = True)
|
||||||
|
if not enable_debugging: current_app.printer.disable()
|
||||||
|
current_app.printer("initializing worker.")
|
||||||
|
|
||||||
|
# ┓┏┏┳┓┏┳┓┏┓ ┏┓┓•
|
||||||
|
# ┣┫ ┃ ┃ ┃┃ ┃ ┃┓┏┓┏┓╋
|
||||||
|
# ┛┗ ┻ ┻ ┣┛ ┗┛┗┗┗ ┛┗┗
|
||||||
|
|
||||||
|
# # Make an instance of an HTTP client to use to make API calls:
|
||||||
|
# current_app.http_client = httpx.AsyncClient(
|
||||||
|
# limits = httpx.Limits(
|
||||||
|
# max_connections = 100, # ............ Maximum number of connections allowed in the pool.
|
||||||
|
# max_keepalive_connections = 50, # ... Maximum number of connections that can be kept alive.
|
||||||
|
# ),
|
||||||
|
# timeout = httpx.Timeout(
|
||||||
|
# pool = 120.0, # .... Time to wait for a free connection from the pool.
|
||||||
|
# connect = 2.5, # ... Time to wait for establishing a connection to the server.
|
||||||
|
# write = 10.0, # .... Time to wait for sending data.
|
||||||
|
# read = 9.9 # ....... Time to wait for receiving data.
|
||||||
|
# )
|
||||||
|
# )
|
||||||
|
# current_app.printer("HTTP client ready.")
|
||||||
|
|
||||||
|
# ┏┓ ┓ ┓ ┳┓
|
||||||
|
# ┃ ┏┓┏┓┏┫ ┏┓┏┓┏┫ ┃┃┏┓╋┏┓
|
||||||
|
# ┗┛┛ ┗ ┗┻ ┗┻┛┗┗┻ ┻┛┗┻┗┗┻
|
||||||
|
|
||||||
|
# # Get the script credentials and data:
|
||||||
|
# script_id = os.environ["SCRIPT_ID"]
|
||||||
|
# response = await current_app.http_client.get(
|
||||||
|
# url = r"https://nexcom.ditscentre.in/internal/cred/get",
|
||||||
|
# headers = {"X-Script-Id": script_id}
|
||||||
|
# )
|
||||||
|
# script_cred = response.json().get("data")
|
||||||
|
# response = await current_app.http_client.get(
|
||||||
|
# url = r"https://nexcom.ditscentre.in/internal/data/get",
|
||||||
|
# headers = {"X-Script-Id": script_id}
|
||||||
|
# )
|
||||||
|
# current_app.script_data = response.json().get("data")
|
||||||
|
# current_app.printer("Cred and data ready.", type(script_cred), type(current_app.script_data))
|
||||||
|
|
||||||
|
# ┏┓
|
||||||
|
# ┗┓┓┏┏╋┏┓┏┳┓
|
||||||
|
# ┗┛┗┫┛┗┗ ┛┗┗
|
||||||
|
# ┛
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
# ┏┓ ┓
|
||||||
|
# ┃ ┏┓┏┣┓┏┓
|
||||||
|
# ┗┛┗┻┗┛┗┗
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
# ┳┳┓ ┳┓┳┓
|
||||||
|
# ┃┃┃┏┓┏┓┏┓┏┓┃┃┣┫
|
||||||
|
# ┛ ┗┗┛┛┗┗┫┗┛┻┛┻┛
|
||||||
|
# ┛
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
# ┏┓
|
||||||
|
# ┃ ┏┓┏┓┏┓┏┓┏╋┏┓┏┓┏
|
||||||
|
# ┗┛┗┛┛┗┛┗┗ ┗┗┗┛┛ ┛
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
# ┏┓ ┓┓
|
||||||
|
# ┃ ┏┓┏┓╋┏┓┏┓┃┃┏┓┏┓┏
|
||||||
|
# ┗┛┗┛┛┗┗┛ ┗┛┗┗┗ ┛ ┛
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
# ┳┳┓•
|
||||||
|
# ┃┃┃┓┏┏
|
||||||
|
# ┛ ┗┗┛┗
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
# ┏┓┓
|
||||||
|
# ┃ ┃┏┓┏┓┏┓┓┏┏┓
|
||||||
|
# ┗┛┗┗ ┗┻┛┗┗┻┣┛
|
||||||
|
# ┛
|
||||||
|
|
||||||
|
# Remove unwanted/sensitive variables from RAM:
|
||||||
|
# del script_cred
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
|
# Done here!
|
||||||
|
current_app.printer("Worker ready!")
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
@app.after_serving
|
||||||
|
@set_api_version(api_version = APP_VERSION)
|
||||||
|
# @log_request_to_mongo(
|
||||||
|
# attr_name = "logs_mongo",
|
||||||
|
# project = constants.PROJECT_NAME,
|
||||||
|
# log_type = constants.MODULE_NAME,
|
||||||
|
# operation = "apiStop",
|
||||||
|
# log_input = True,
|
||||||
|
# log_output = True
|
||||||
|
# )
|
||||||
|
async def app_shutdown(**kwargs):
|
||||||
|
|
||||||
|
"""
|
||||||
|
This is called when "app.shutdown()" is called.
|
||||||
|
:return: None.
|
||||||
|
"""
|
||||||
|
|
||||||
|
current_app.printer("Shutting down...")
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
@app.route(f"/", methods = ["GET", "POST"])
|
||||||
|
@app.route(f"/{MODULE_BASE}", methods = ["GET", "POST"])
|
||||||
|
async def root():
|
||||||
|
|
||||||
|
"""
|
||||||
|
To check if the service is running or not.
|
||||||
|
Use this to monitor the service from your "watchman" script.
|
||||||
|
:return: only "ok"
|
||||||
|
"""
|
||||||
|
|
||||||
|
return "ok"
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
@app.route(f"/metrics/memory", methods = ["GET", "POST"])
|
||||||
|
@app.route(f"/{MODULE_BASE}/metrics/memory", methods = ["GET", "POST"])
|
||||||
|
async def memory_metrics():
|
||||||
|
|
||||||
|
"""
|
||||||
|
TO measure the metrics of the app.
|
||||||
|
:return: A JSON of the metrics.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Figure pout the parent process.
|
||||||
|
# This is important for multi-worker environments:
|
||||||
|
parent_pid = os.getppid()
|
||||||
|
parent_process = psutil.Process(parent_pid)
|
||||||
|
parent_mem_info = parent_process.memory_info()
|
||||||
|
|
||||||
|
# Figure out all the children of the parent process:
|
||||||
|
child_pids = [child.pid for child in parent_process.children()]
|
||||||
|
child_processes = [psutil.Process(child_pid) for child_pid in child_pids]
|
||||||
|
child_stats = []
|
||||||
|
for pid, process in zip(child_pids, child_processes):
|
||||||
|
mem_info = process.memory_info()
|
||||||
|
child_stats.append({
|
||||||
|
"pid": pid,
|
||||||
|
"mem": round(mem_info.rss / (1024 ** 2), 2)
|
||||||
|
})
|
||||||
|
|
||||||
|
# Construct the response:
|
||||||
|
metrics_json = {
|
||||||
|
"project": constants.PROJECT_NAME,
|
||||||
|
"parent": {
|
||||||
|
"pid": parent_pid,
|
||||||
|
"mem": round(parent_mem_info.rss / (1024 ** 2), 2)
|
||||||
|
},
|
||||||
|
"children": child_stats,
|
||||||
|
"unit": {
|
||||||
|
"mem": "MB"
|
||||||
|
},
|
||||||
|
"ts": date_time.get_current_ist_date_time(as_string = True)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Done here:
|
||||||
|
return metrics_json
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
@app.route(f"/{MODULE_BASE}/debug/<action>", methods = ["POST", "GET"])
|
||||||
|
async def change_debug(action):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Enable or disable debugging for the entire microservice.
|
||||||
|
WARNING: NOT RECOMMENDED FOR MULTI-WORKER DEPLOYMENTS.
|
||||||
|
:param action: "enable" to allow debugging on the terminal, or "disable".
|
||||||
|
:return: "enabled"/"disabled" if successful, else "ok"
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Enable or disable debugging only if the password matches:
|
||||||
|
action = action.lower()
|
||||||
|
if action == "enable": current_app.printer.enable()
|
||||||
|
elif action == "disable": current_app.printer.disable()
|
||||||
|
return "ok"
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
@app.route(f"/{MODULE_BASE}/maintenance/<action>", methods = ["POST", "GET"])
|
||||||
|
async def change_maintenance(action):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Enable or disable debugging for the entire microservice.
|
||||||
|
WARNING: NOT RECOMMENDED FOR MULTI-WORKER DEPLOYMENTS.
|
||||||
|
:param action: "enable" to stop taking new requests on the API, or "disable".
|
||||||
|
:return: "enabled"/"disabled" if successful, else "ok"
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Enable or disable debugging only if the password matches:
|
||||||
|
action = action.lower()
|
||||||
|
if action == "enable":
|
||||||
|
current_app.is_under_maintenance = True
|
||||||
|
os.environ["IS_UNDER_MAINTENANCE"] = "True"
|
||||||
|
elif action == "disable":
|
||||||
|
current_app.is_under_maintenance = False
|
||||||
|
os.environ["IS_UNDER_MAINTENANCE"] = "False"
|
||||||
|
return "ok"
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** MAIN PROGRAM ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
# To get args from the terminal:
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
# To run the ASGI:
|
||||||
|
import uvicorn
|
||||||
|
from multiprocessing import freeze_support
|
||||||
|
|
||||||
|
# Get the config from the command-line:
|
||||||
|
parser = argparse.ArgumentParser(description = f"Microservice for '{MODULE_BASE}' API.")
|
||||||
|
parser.add_argument(
|
||||||
|
"--workers",
|
||||||
|
type = int,
|
||||||
|
help = "The no. of threads to spin up for this instance!",
|
||||||
|
default = 2
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--host",
|
||||||
|
type = str,
|
||||||
|
help = "The host for the app. e.g.: '0.0.0.0' or '127.0.0.1'.",
|
||||||
|
default = "127.0.0.1"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--port",
|
||||||
|
type = int,
|
||||||
|
help = "The port no. to bind the app to.",
|
||||||
|
default = 8080
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--script-id",
|
||||||
|
type = str,
|
||||||
|
help = "The id of this script (will affect the loaded config)."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--debug",
|
||||||
|
action = "store_true",
|
||||||
|
help = "Whether, or not, you want to see debugging messages in the terminal.",
|
||||||
|
default = False
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Note down the config;
|
||||||
|
os.environ["SCRIPT_ID"] = args.script_id
|
||||||
|
os.environ["DEBUG"] = str(args.debug)
|
||||||
|
|
||||||
|
# Run the gateway:
|
||||||
|
freeze_support()
|
||||||
|
uvicorn.run(
|
||||||
|
app = "main:app",
|
||||||
|
workers = args.workers,
|
||||||
|
host = args.host,
|
||||||
|
port = args.port
|
||||||
|
)
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
"""
|
||||||
|
|
||||||
|
AUTHOR:
|
||||||
|
|
||||||
|
Khushal P Soonderji
|
||||||
|
|
||||||
|
DATE:
|
||||||
|
|
||||||
|
Fri, 14th Nov, 2025
|
||||||
|
|
||||||
|
OBJECTIVE:
|
||||||
|
|
||||||
|
To provide data model(s) for common elements of every REST APi request (like the headers).
|
||||||
|
|
||||||
|
REFERENCES:
|
||||||
|
|
||||||
|
N/A
|
||||||
|
|
||||||
|
DOWNLOADS:
|
||||||
|
|
||||||
|
N/A
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** IMPORT ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
# To make sibling directories accessible for imports:
|
||||||
|
import sys
|
||||||
|
sys.path.append(".")
|
||||||
|
sys.path.append("..")
|
||||||
|
|
||||||
|
# For making data behaviour_models:
|
||||||
|
from pydantic import BaseModel, Field, field_validator, AwareDatetime
|
||||||
|
from typing import Optional, Literal
|
||||||
|
|
||||||
|
# My utils:
|
||||||
|
from utils_v2.string import regex
|
||||||
|
from utils_v2.date_time import date_time
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** MACROS / ONE-TIME INIT ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
# --- Nothing Yet
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** VARIABLES ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
# --- Nothing Yet
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** FUNCTIONS ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleCosecCredentialsHeaders(BaseModel):
|
||||||
|
|
||||||
|
cosecUrl: str = Field(
|
||||||
|
description = "The URL to Cosec's portal.",
|
||||||
|
frozen = True,
|
||||||
|
alias = "X-Cosec-URL"
|
||||||
|
)
|
||||||
|
|
||||||
|
cosecUsername: str = Field(
|
||||||
|
description = "The username on Cosec's portal.",
|
||||||
|
frozen = True,
|
||||||
|
alias = "X-Cosec-Username"
|
||||||
|
)
|
||||||
|
|
||||||
|
cosecPassword: str = Field(
|
||||||
|
description = "The password on Cosec's portal.",
|
||||||
|
frozen = True,
|
||||||
|
alias = "X-Cosec-Password"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ┏┓ ┏•
|
||||||
|
# ┃ ┏┓┏┓╋┓┏┓
|
||||||
|
# ┗┛┗┛┛┗┛┗┗┫
|
||||||
|
# ┛
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = "allow"
|
||||||
|
|
||||||
|
def model_dump(self, *args, **kwargs):
|
||||||
|
return super().model_dump(*args, by_alias = True, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** MAIN PROGRAM ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
pass
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
"""
|
||||||
|
|
||||||
|
AUTHOR:
|
||||||
|
|
||||||
|
Khushal P Soonderji
|
||||||
|
|
||||||
|
DATE:
|
||||||
|
|
||||||
|
Fri, 14th Nov, 2025.
|
||||||
|
|
||||||
|
OBJECTIVE:
|
||||||
|
|
||||||
|
To provide data model(s) for receiving API requests to get In?Out Reports.
|
||||||
|
|
||||||
|
REFERENCES:
|
||||||
|
|
||||||
|
N/A
|
||||||
|
|
||||||
|
DOWNLOADS:
|
||||||
|
|
||||||
|
N/A
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** IMPORT ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
# To make sibling directories accessible for imports:
|
||||||
|
import sys
|
||||||
|
sys.path.append("..")
|
||||||
|
sys.path.append("../..")
|
||||||
|
|
||||||
|
# For making data behaviour_models:
|
||||||
|
from pydantic import BaseModel, Field, field_validator, AwareDatetime
|
||||||
|
from typing import Optional, Literal, List
|
||||||
|
|
||||||
|
# My utils:
|
||||||
|
from utils_v2.string import regex
|
||||||
|
from utils_v2.date_time import date_time
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** MACROS / ONE-TIME INIT ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
# --- Nothing Yet
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** VARIABLES ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
# --- Nothing Yet
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** FUNCTIONS ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
class CosecInOutReportRequestData(BaseModel):
|
||||||
|
|
||||||
|
fromDate: AwareDatetime | None = Field(
|
||||||
|
description = "The start date from when the in-out report is desired.",
|
||||||
|
frozen = True,
|
||||||
|
default = None
|
||||||
|
)
|
||||||
|
|
||||||
|
toDate: AwareDatetime | None = Field(
|
||||||
|
description = "The end date till when the in-out report is desired.",
|
||||||
|
frozen = True,
|
||||||
|
default = None
|
||||||
|
)
|
||||||
|
|
||||||
|
groupIds: List[str] | str = Field(
|
||||||
|
description = "Each company/entity in Cosec's system is represented by a group id. This is a list of those ids.",
|
||||||
|
frozen = True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# ┏┓ ┏•
|
||||||
|
# ┃ ┏┓┏┓╋┓┏┓
|
||||||
|
# ┗┛┗┛┛┗┛┗┗┫
|
||||||
|
# ┛
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = "forbid"
|
||||||
|
|
||||||
|
# ┓┏ ┓• ┓ •
|
||||||
|
# ┃┃┏┓┃┓┏┫┏┓╋┓┏┓┏┓┏
|
||||||
|
# ┗┛┗┻┗┗┗┻┗┻┗┗┗┛┛┗┛
|
||||||
|
|
||||||
|
@field_validator("fromDate", "toDate", mode = "before")
|
||||||
|
def parse_date_time(cls, value):
|
||||||
|
if value is None: value = date_time.get_current_utc_date_time(as_string = False)
|
||||||
|
value = date_time.parse_date_time(
|
||||||
|
input_value = value,
|
||||||
|
date_formats = [
|
||||||
|
"%Y%m%d",
|
||||||
|
"%Y-%m-%d",
|
||||||
|
"%Y-%m-%d %H:%M:%S",
|
||||||
|
"%Y-%m-%d %H:%M:%S%z",
|
||||||
|
"%Y-%m-%dT%H:%M:%S",
|
||||||
|
"%Y-%m-%dT%H:%M:%S%z",
|
||||||
|
],
|
||||||
|
timezone = date_time.TIMEZONE_UTC
|
||||||
|
)
|
||||||
|
return value
|
||||||
|
|
||||||
|
@field_validator("groupIds", mode = "after")
|
||||||
|
def parse_group_ids(cls, value):
|
||||||
|
if isinstance(value, str): value = value.split(",")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** MAIN PROGRAM ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
pass
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
"""
|
||||||
|
|
||||||
|
AUTHOR:
|
||||||
|
|
||||||
|
Khushal P Soonderji
|
||||||
|
|
||||||
|
DATE:
|
||||||
|
|
||||||
|
Fri, 14th Nov, 2025
|
||||||
|
|
||||||
|
OBJECTIVE:
|
||||||
|
|
||||||
|
To keep variables and values that will be shared among various parts of the project.
|
||||||
|
|
||||||
|
REFERENCES:
|
||||||
|
|
||||||
|
N/A
|
||||||
|
|
||||||
|
DOWNLOADS:
|
||||||
|
|
||||||
|
N/A
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** IMPORT ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
# To make sibling directories accessible for imports:
|
||||||
|
# ---
|
||||||
|
import sys
|
||||||
|
sys.path.append(".")
|
||||||
|
sys.path.append("..")
|
||||||
|
|
||||||
|
# System-level activities:
|
||||||
|
import os
|
||||||
|
|
||||||
|
# For parsing URLs:
|
||||||
|
import urllib
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** MACROS / ONE-TIME INIT ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
APP_VERSION = "1.0.0"
|
||||||
|
PROJECT_NAME = "cosec"
|
||||||
|
MODULE_NAME = "cosec"
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
|||||||
|
// Copyright 2015 The Chromium Authors
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google LLC nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -0,0 +1,27 @@
|
|||||||
|
// Copyright 2015 The Chromium Authors
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google LLC nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Use this to run the microservice without any docker setup.
|
# Use this to run the microservice without any docker setup.
|
||||||
source .venv/bin/activate
|
source .venv/bin/activate
|
||||||
streamlit run main.py &
|
python3 "$(pwd)/backend/api/main.py" --host "127.0.0.1" --port 5000 --workers 1 --script-id "n/a" --debug
|
||||||
deactivate
|
deactivate
|
||||||
|
|
||||||
# All done:
|
# All done:
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ import io
|
|||||||
import torch
|
import torch
|
||||||
from transformers import pipeline
|
from transformers import pipeline
|
||||||
|
|
||||||
# To download images from URLs:
|
# To downloads images from URLs:
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
# To read images:
|
# To read images:
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ import io
|
|||||||
import torch
|
import torch
|
||||||
from transformers import pipeline
|
from transformers import pipeline
|
||||||
|
|
||||||
# To download images from URLs:
|
# To downloads images from URLs:
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
# To read images:
|
# To read images:
|
||||||
|
|||||||
@@ -1655,14 +1655,14 @@ class AsyncMongoStorage(AsyncMongo):
|
|||||||
):
|
):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Returns a GridOut object so that you can implement your own download logic using the built-in 'read' method.
|
Returns a GridOut object so that you can implement your own downloads logic using the built-in 'read' method.
|
||||||
Once the reading is done, use the 'close' method to release the resources used by the stream.
|
Once the reading is done, use the 'close' method to release the resources used by the stream.
|
||||||
:param file_id: (RECOMMENDED) the id of the save file.
|
:param file_id: (RECOMMENDED) the id of the save file.
|
||||||
:param file_name: The name of the saved file. NOT RECOMMENDED because you could have many files with the same
|
:param file_name: The name of the saved file. NOT RECOMMENDED because you could have many files with the same
|
||||||
name. The best way to tell files apart if from the id.
|
name. The best way to tell files apart if from the id.
|
||||||
:param session: The session if you need to do this in a transaction.
|
:param session: The session if you need to do this in a transaction.
|
||||||
:param raise_exception: Whether, or not, you want to raise an exception when something fails.
|
:param raise_exception: Whether, or not, you want to raise an exception when something fails.
|
||||||
:return: The download stream that implements the 'read' and 'close' methods, or None if something failed.
|
:return: The downloads stream that implements the 'read' and 'close' methods, or None if something failed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Ensure you are connected:
|
# Ensure you are connected:
|
||||||
@@ -1671,7 +1671,7 @@ class AsyncMongoStorage(AsyncMongo):
|
|||||||
# Assume failure:
|
# Assume failure:
|
||||||
stream = None
|
stream = None
|
||||||
|
|
||||||
# Try to open a download stream:
|
# Try to open a downloads stream:
|
||||||
try:
|
try:
|
||||||
|
|
||||||
# If a file id is supplied (preferred way):
|
# If a file id is supplied (preferred way):
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ class GmailMessage:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
Add a file as an attachment to the mail. This file, even if possible, will not be rendered on the screen in-line
|
Add a file as an attachment to the mail. This file, even if possible, will not be rendered on the screen in-line
|
||||||
with the body. It will be made available as a download.
|
with the body. It will be made available as a downloads.
|
||||||
:param attachment_file: The file that you would like to attach.
|
:param attachment_file: The file that you would like to attach.
|
||||||
:param file_name: The name of the file. This is the same name by which it will be downloaded. You need not
|
:param file_name: The name of the file. This is the same name by which it will be downloaded. You need not
|
||||||
specify this if the input file is specified as a path. Needed when you give the input file as a buffer.
|
specify this if the input file is specified as a path. Needed when you give the input file as a buffer.
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ import numpy as np
|
|||||||
# To run OCR:
|
# To run OCR:
|
||||||
import easyocr
|
import easyocr
|
||||||
|
|
||||||
# To download images from the web:
|
# To downloads images from the web:
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
# My utils:
|
# My utils:
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ import numpy as np
|
|||||||
# To run OCR:
|
# To run OCR:
|
||||||
import easyocr
|
import easyocr
|
||||||
|
|
||||||
# To download images from the web:
|
# To downloads images from the web:
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
# My utils:
|
# My utils:
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ class PDFMaker:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
Download a file from a URL.
|
Download a file from a URL.
|
||||||
:param url: The URL to download the file from.
|
:param url: The URL to downloads the file from.
|
||||||
:param follow_redirects: Whether, or not, to follow along with any redirects when downloading the file.
|
:param follow_redirects: Whether, or not, to follow along with any redirects when downloading the file.
|
||||||
:return: The downloaded file or None.
|
:return: The downloaded file or None.
|
||||||
"""
|
"""
|
||||||
@@ -458,7 +458,7 @@ class PDFMaker:
|
|||||||
"""
|
"""
|
||||||
Download a font from a URL and then register it for use.
|
Download a font from a URL and then register it for use.
|
||||||
:param name: The name you would later refer to the font by.
|
:param name: The name you would later refer to the font by.
|
||||||
:param url: The URL to download the font from.
|
:param url: The URL to downloads the font from.
|
||||||
:param follow_redirects: Whether, or not, to follow along with any redirects when downloading the file.
|
:param follow_redirects: Whether, or not, to follow along with any redirects when downloading the file.
|
||||||
:return: True if registered, else False.
|
:return: True if registered, else False.
|
||||||
"""
|
"""
|
||||||
@@ -719,7 +719,7 @@ class PDFMaker:
|
|||||||
"""
|
"""
|
||||||
Asynchronously downloads an image and returns it as a PIL object.
|
Asynchronously downloads an image and returns it as a PIL object.
|
||||||
Use this instead of just passing the URL to 'draw_image' for better efficiency.
|
Use this instead of just passing the URL to 'draw_image' for better efficiency.
|
||||||
:param url: [str] The URL to download the image from.
|
:param url: [str] The URL to downloads the image from.
|
||||||
:param follow_redirects: [bool] Whether, or not, to follow redirect URLs when downloading the file.
|
:param follow_redirects: [bool] Whether, or not, to follow redirect URLs when downloading the file.
|
||||||
:param as_pil: [bool] If True, a PIL object will be returned, else a PNG file will be returned in RAM (BytesIO).
|
:param as_pil: [bool] If True, a PIL object will be returned, else a PNG file will be returned in RAM (BytesIO).
|
||||||
:param format: [str] The type of output file that you want. Not applicable for PIL objects.
|
:param format: [str] The type of output file that you want. Not applicable for PIL objects.
|
||||||
@@ -1369,7 +1369,7 @@ class PDFMaker:
|
|||||||
width = end_x - start_x
|
width = end_x - start_x
|
||||||
height = end_y - start_y
|
height = end_y - start_y
|
||||||
|
|
||||||
# In case the image is passed as a URL, we try to download it and open it as a PIL object:
|
# In case the image is passed as a URL, we try to downloads it and open it as a PIL object:
|
||||||
if isinstance(image, str):
|
if isinstance(image, str):
|
||||||
if image.startswith("https://") or image.startswith("http://"):
|
if image.startswith("https://") or image.startswith("http://"):
|
||||||
image = Image.open(io.BytesIO(requests.get(image).content))
|
image = Image.open(io.BytesIO(requests.get(image).content))
|
||||||
|
|||||||
@@ -405,7 +405,7 @@ class AsyncTelegramBot:
|
|||||||
) -> TelegramApiResponse:
|
) -> TelegramApiResponse:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
To download a file from Telegram.
|
To downloads a file from Telegram.
|
||||||
NOTE: THIS DOES NOT WORK WHILE A WEBHOOK IS SET UP. YOU CAN EITHER USE THIS OR A WEBHOOK, NOT BOTH
|
NOTE: THIS DOES NOT WORK WHILE A WEBHOOK IS SET UP. YOU CAN EITHER USE THIS OR A WEBHOOK, NOT BOTH
|
||||||
SIMULTANEOUSLY.
|
SIMULTANEOUSLY.
|
||||||
:param file_id: The 'file_id' of this file. Not to be confused with 'file_unique_id'.
|
:param file_id: The 'file_id' of this file. Not to be confused with 'file_unique_id'.
|
||||||
|
|||||||
Reference in New Issue
Block a user