You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

229 lines
7.2 KiB

"""
Logging and utility services for Plutus payment processing application.
This module provides database logging functionality for tracking application activities,
particularly automated script executions and payment processing operations.
"""
from datetime import datetime, timezone
from typing import Optional, Union
from app import db
from models import Logs
# System user ID for automated processes
SYSTEM_USER_ID = 1
def log_activity(
user_id: int,
action: str,
entity_type: str,
entity_id: Optional[int] = None,
details: Optional[str] = None,
ip_address: Optional[str] = None
) -> Optional[int]:
"""
Log an activity to the database.
Args:
user_id (int): ID of the user performing the action (use SYSTEM_USER_ID for automated processes)
action (str): Type of action performed (e.g., 'BATCH_RUN', 'PAYMENT_PROCESSED', 'API_ACCESS')
entity_type (str): Type of entity involved (e.g., 'Batch', 'Payment', 'PaymentPlan')
entity_id (int, optional): ID of the specific entity involved
details (str, optional): Detailed description of the activity
ip_address (str, optional): IP address of the request (for web requests)
Returns:
int: ID of the created log entry, or None if failed
"""
try:
log_entry = Logs(
User_ID=user_id,
Action=action,
Entity_Type=entity_type,
Entity_ID=entity_id,
Log_Entry=details,
IP_Address=ip_address,
Added=datetime.now(timezone.utc)
)
db.session.add(log_entry)
db.session.commit()
return log_entry.id
except Exception as e:
db.session.rollback()
print(f"Failed to log activity: {e}")
return None
def log_script_start(script_name: str, mode: str, environment: str) -> Optional[int]:
"""
Log the start of a script execution.
Args:
script_name (str): Name of the script being executed
mode (str): Running mode (batch, payintent, payplan)
environment (str): Environment (live, sandbox)
Returns:
int: Log entry ID or None if failed
"""
details = f"{script_name} started in {mode} mode ({environment} environment)"
return log_activity(
user_id=SYSTEM_USER_ID,
action="SCRIPT_START",
entity_type="Script",
details=details
)
def log_script_completion(
script_name: str,
mode: str,
success_count: int = 0,
failed_count: int = 0,
total_amount: float = 0.0,
batch_ids: Optional[list] = None,
duration_seconds: Optional[float] = None,
errors: Optional[list] = None
) -> Optional[int]:
"""
Log the completion of a script execution with summary statistics.
Args:
script_name (str): Name of the script that completed
mode (str): Running mode that was executed
success_count (int): Number of successful operations
failed_count (int): Number of failed operations
total_amount (float): Total amount processed
batch_ids (list, optional): List of batch IDs created
duration_seconds (float, optional): Execution time in seconds
errors (list, optional): List of error messages encountered
Returns:
int: Log entry ID or None if failed
"""
total_operations = success_count + failed_count
success_rate = (success_count / total_operations * 100) if total_operations > 0 else 0
details_parts = [
f"{script_name} completed in {mode} mode",
f"Total operations: {total_operations}",
f"Successful: {success_count}",
f"Failed: {failed_count}",
f"Success rate: {success_rate:.1f}%"
]
if total_amount > 0:
details_parts.append(f"Total amount: ${total_amount:,.2f}")
if batch_ids:
details_parts.append(f"Batch IDs: {', '.join(map(str, batch_ids))}")
if duration_seconds:
details_parts.append(f"Duration: {duration_seconds:.1f}s")
if errors:
details_parts.append(f"Errors encountered: {len(errors)}")
if len(errors) <= 3:
details_parts.extend([f"- {error}" for error in errors])
else:
details_parts.extend([f"- {error}" for error in errors[:3]])
details_parts.append(f"... and {len(errors) - 3} more errors")
details = "\\n".join(details_parts)
action = "SCRIPT_SUCCESS" if failed_count == 0 else "SCRIPT_PARTIAL" if success_count > 0 else "SCRIPT_FAILED"
return log_activity(
user_id=SYSTEM_USER_ID,
action=action,
entity_type="Script",
details=details
)
def log_batch_created(batch_id: int, payment_method: str, customer_count: int) -> Optional[int]:
"""
Log the creation of a payment batch.
Args:
batch_id (int): ID of the created batch
payment_method (str): Payment method type (Direct Debit, Card, etc.)
customer_count (int): Number of customers in the batch
Returns:
int: Log entry ID or None if failed
"""
details = f"Payment batch created for {payment_method} with {customer_count} customers"
return log_activity(
user_id=SYSTEM_USER_ID,
action="BATCH_CREATED",
entity_type="PaymentBatch",
entity_id=batch_id,
details=details
)
def log_payment_plan_run(
active_plans: int,
due_plans: int,
processed_count: int,
failed_count: int,
total_amount: float
) -> Optional[int]:
"""
Log the results of a payment plan execution.
Args:
active_plans (int): Total number of active payment plans
due_plans (int): Number of plans due for payment today
processed_count (int): Number of payments successfully processed
failed_count (int): Number of failed payments
total_amount (float): Total amount processed
Returns:
int: Log entry ID or None if failed
"""
details = (
f"Payment plan execution: {active_plans} active plans, "
f"{due_plans} due today, {processed_count} successful, "
f"{failed_count} failed, ${total_amount:,.2f} total"
)
action = "PAYPLAN_SUCCESS" if failed_count == 0 else "PAYPLAN_PARTIAL" if processed_count > 0 else "PAYPLAN_FAILED"
return log_activity(
user_id=SYSTEM_USER_ID,
action=action,
entity_type="PaymentPlan",
details=details
)
def log_payment_intent_followup(
pending_count: int,
succeeded_count: int,
failed_count: int,
still_pending: int
) -> Optional[int]:
"""
Log the results of payment intent follow-up processing.
Args:
pending_count (int): Number of payment intents checked
succeeded_count (int): Number that succeeded
failed_count (int): Number that failed
still_pending (int): Number still pending
Returns:
int: Log entry ID or None if failed
"""
details = (
f"Payment intent follow-up: {pending_count} intents checked, "
f"{succeeded_count} succeeded, {failed_count} failed, "
f"{still_pending} still pending"
)
return log_activity(
user_id=SYSTEM_USER_ID,
action="PAYINTENT_FOLLOWUP",
entity_type="PaymentIntent",
details=details
)