RMISDelta.py

import requests
import time
import json
import xml.etree.ElementTree as ET
import subprocess
import threading
from DocumentAPI import GetDocument
from RMISLibrary import Delta, Carrier, RMISCredentials, Document
from TruckStopDBHelper import *

baseURL = "https://api.rmissecure.com"  # Base URL for all RMIS API calls

def Start():
    rmisCreds = RMISCredentials(1234, "MyPass123!")  # Hardcoded credentials for the example, should be sourced from user in production
    while True:  # Run indefinitely        
        while (int(DeltaSummary(rmisCreds)) > 0): # Loop until the Summary endpoint returns 0
            deltasToClear = []  # Empty list to store deltas to clear
            for delta in DeltaFetch(rmisCreds):  # Get a batch of deltas for processing                
                #Get the carrier details from expanded, then store them in the database, finally add the delta to the list of deltas to clear                
                carrier = CallExpanded(rmisCreds, delta)
                dataBaseObject = GetDBConnection("RMISCarriers")
                InsertIntoTable(table="Carriers", dataBaseObject=dataBaseObject, object=carrier, whereFields=['ClientID', 'CarrierRMISID'], whereValues=[carrier.clientID, carrier.CarrierRMISID])
                dataBaseObject.sqlConn.close()
                deltasToClear.append(Delta(int(delta), str(carrier.lastUpdated)))  # Append delta to the list to be cleared
            if len(deltasToClear) > 0:  # If there are deltas to clear, call the clear function with the list
                DeltaClear(rmisCreds, deltasToClear)
        time.sleep(300)  # Wait 5 minutes before running again

def DeltaSummary(rmisCreds:RMISCredentials):
    url = baseURL + "/_c/std/api/DeltaAPI.aspx"  # URL for Delta API
    payload = {
        "clientID": rmisCreds.clientID,  
        "clientPassword": rmisCreds.clientPassword,
        "APIMode": "Summary"
    }
    headers = {
        "accept": "application/json",
        "content-type": "application/json"
    }

    response = requests.post(url, json=payload, headers=headers)  # Send a POST request to the RMIS Delta API
    data = response.json()  # Parse JSON response

    # Check if the call was successful
    if (data.get("RMISDeltaAPI", {}).get('Header', {}).get('Result') == "ERROR"):
        print(data.get("RMISDeltaAPI", {}).get('Header', {}).get('Errors'))  # Print errors
        return 0
    else:
        try:
            # Extract the value of TotalInsdIDs from the response
            total_insdids = data.get('RMISDeltaAPI', {}).get('SUMMARY', {}).get('TotalInsdIDs')
            if (str(total_insdids).isdigit()):  # Check if the total is a digit
                print("\n\nNumber of deltas in queue (Summary): " + total_insdids)
                return total_insdids  # Return the total number of deltas
            else:
                print("\n\nNumber of deltas in queue (Summary): 0")
                return 0  # Return 0 if not a digit

        except json.JSONDecodeError:
            print("Error decoding JSON response.")  # Handle JSON decoding errors
            return None

def DeltaFetch(rmisCreds:RMISCredentials):
    url = baseURL + "/_c/std/api/DeltaAPI.aspx"  # URL for Delta API
    payload = {
        "clientID": rmisCreds.clientID, 
        "clientPassword": rmisCreds.clientPassword,
        "APIMode": "Fetch",
        "MaxRecs": 50  # Number of records to fetch in each call
    }
    headers = {
        "accept": "application/json", 
        "content-type": "application/json"
    }
    
    response = requests.post(url, json=payload, headers=headers) # Send a POST request to fetch deltas
    data = response.json()  # Parse JSON response

    try:
        # Extract the list of deltas (InsdID)
        deltaList = data.get('RMISDeltaAPI', {}).get('FETCH', {}).get('InsdID')
        return deltaList  # Return the list of deltas
    except:
        print("No Deltas returned in fetch call")  # Print message if no deltas
        return []  # Return an empty list

def CallExpanded(rmisCreds:RMISCredentials, delta:Delta):
    documentList = []  # Empty list to store documents

    url = baseURL + "/_c/std/api/ExpandedCarrierAPI.aspx"
    url_params = {
        "clientID": rmisCreds.clientID,
        "pwd": rmisCreds.clientPassword,
        "querytype": "insdid",
        "queryid": delta,  # Carrier (Insured) ID to query
        "version": 13,  # API version
    }
    headers = {
        "accept": "application/json",  
        "content-type": "application/json"
    }
    response = requests.get(url, params=url_params, headers=headers)  # Send a GET request to fetch detailed carrier data

    try:
        # Expanded carrier API returns in XML, so process the data as such
        root = ET.fromstring(response.text)  # Parse XML response
        timeStamp = root.find('.//TimeStamp').text  # Find the timestamp from the response
        
        # Check for errors in the response
        try:
            errors = root.find('.//Errors')
            for error in errors:
                if ("Carrier does not belong to this client" in error.text):
                    # This error indicates the carrier has been detached; remove from the queue
                    print(error.text)
                    return timeStamp  # Return the timestamp
                else:
                    pass  # Placeholder for handling other errors
        except:
            pass # Placeholder for handling exceptions

        coverages = root.find('.//Coverages')  # Find list of coverages in the XML
        if coverages is not None:        
            for coverage in coverages:
                rmisImageId = coverage.find('.//RMISImageID').text  # Extract RMISImageID
                if (rmisImageId):
                    documentList.append(Document("CERTIFICATE", rmisImageId, delta))  # Append document to list (Type, image ID, carrier ID)

        carrierNode = root.find('.//Carrier')
        if carrierNode is not None:
            mcNumber = carrierNode.find('.//MCNumber').text
            dotNumber = carrierNode.find('.//DOTNumber').text
            carrierName = carrierNode.find('.//CompanyName').text
        
        carrier = Carrier()
        carrier.carrierName = carrierName
        carrier.carrierMC = mcNumber
        carrier.carrierDOT = dotNumber
        carrier.CarrierRMISID = delta
        carrier.clientID = rmisCreds.clientID
        carrier.lastUpdated = timeStamp

        # Start the document API script in a separate thread
        StartDocumentThread(rmisCreds, documentList)
        return carrier  # Return the timestamp
    except json.JSONDecodeError:
        print("Error decoding JSON response.")  # Handle JSON decoding errors
        return None

def StartDocumentThread(rmisCreds:RMISCredentials, documentList:list[Document]):
    # Start a new thread for running the document API script
    thread = threading.Thread(target=RunDocumentAPIScript, args=(rmisCreds, documentList))
    thread.start()

def RunDocumentAPIScript(rmisCreds:RMISCredentials, documentList:list[Document]):
    for document in documentList:
        try:
            # Run the Document API script as a subprocess
            #subprocess.run(['python', 'DocumentAPI.py', str(rmisCreds.clientID), rmisCreds.clientPassword, document.documentType, document.documentID, document.carrierID])
            GetDocument(rmisCreds=rmisCreds, document=document)
        except subprocess.CalledProcessError as e:
            print(f"Error getting document {document.documentID}: {e}")  # Print error if subprocess fails

def DeltaClear(rmisCreds:RMISCredentials, deltaList:list[Delta]):
    url = baseURL + "/_c/std/api/DeltaAPI.aspx"  # URL for Delta API
    clear_insureds = [
        {
            "insdID": int(delta.insdID),  # Insured ID
            "timeStamp": str(delta.timeStamp)  # Timestamp
        }
        for delta in deltaList  # Create a list of dictionaries for clearing
    ]
    payload = {
        "clientID": rmisCreds.clientID,  # Client ID
        "clientPassword": rmisCreds.clientPassword,  # Client password
        "APIMode": "Clear",  # Request mode
        "ClearInsureds": clear_insureds  # List of insureds to clear
    }
    headers = {
        "accept": "application/json",  # Expected response format
        "content-type": "application/json"  # Content type of the request
    }
    # Send a POST request to clear processed deltas
    response = requests.post(url, json=payload, headers=headers)
    data = response.json()  # Parse JSON response
    
    print(data)  # Print the response for verification
    print("\n\n")  # Print new lines for clarity

Start()  # Start the process