Plumbing stable for GUI to call upon via inserted ENUM DICT
This commit is contained in:
parent
69f4e7114f
commit
7ba8e1e6d3
4 changed files with 85 additions and 40 deletions
|
|
@ -3,7 +3,7 @@ from core.infrastructure.error_popup import PopupProtocol
|
||||||
from core.infrastructure.service_registry import ServiceLocator
|
from core.infrastructure.service_registry import ServiceLocator
|
||||||
|
|
||||||
from core.models.Result import Result, ResultError
|
from core.models.Result import Result, ResultError
|
||||||
from core.infrastructure.action_registry import ENUM_MAP
|
# from core.infrastructure.action_registry import ENUM_MAP
|
||||||
from core.errors.logger import logger
|
from core.errors.logger import logger
|
||||||
|
|
||||||
# from infrastructure.observer_service import notify
|
# from infrastructure.observer_service import notify
|
||||||
|
|
@ -23,11 +23,12 @@ class ThreadPlumbing:
|
||||||
Manage Execution of functions on threads, and orchestrate their retries,
|
Manage Execution of functions on threads, and orchestrate their retries,
|
||||||
then pass the data through to popup prompts for the user to decide how to solve any error, by enum type.
|
then pass the data through to popup prompts for the user to decide how to solve any error, by enum type.
|
||||||
"""
|
"""
|
||||||
def __init__(self, action: Callable, ui_dispatcher: MainThreadDispatcher, error_implementation: Type[PopupProtocol], auto_navigate_dict: dict, ui_caller: Optional[Callable] = None, *args, **kwargs):
|
def __init__(self, action: Callable, ui_dispatcher: MainThreadDispatcher, error_implementation: Type[PopupProtocol], ENUM_MAP: dict, auto_navigate_dict: dict, ui_caller: Optional[Callable] = None, *args, **kwargs):
|
||||||
self.action = action
|
self.action = action
|
||||||
self.ui_dispatcher = ui_dispatcher
|
self.ui_dispatcher = ui_dispatcher
|
||||||
self.ui_caller = ui_caller
|
self.ui_caller = ui_caller
|
||||||
self.error_implementation = error_implementation
|
self.error_implementation = error_implementation
|
||||||
|
self.ENUM_MAP = ENUM_MAP
|
||||||
self.auto_navigate_dict = auto_navigate_dict
|
self.auto_navigate_dict = auto_navigate_dict
|
||||||
self.navigator = ServiceLocator.get("navigator")
|
self.navigator = ServiceLocator.get("navigator")
|
||||||
self.args = args
|
self.args = args
|
||||||
|
|
@ -55,7 +56,7 @@ class ThreadPlumbing:
|
||||||
if not isinstance(result, Result):
|
if not isinstance(result, Result):
|
||||||
result = convert_to_result(result)
|
result = convert_to_result(result)
|
||||||
if not result.valid:
|
if not result.valid:
|
||||||
result.function_choice = resolve_prescription(result, *self.args, **self.kwargs)
|
result.function_choice = resolve_prescription(result, self.ENUM_MAP, *self.args, **self.kwargs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_msg = f"There was an error: {str(e)}"
|
error_msg = f"There was an error: {str(e)}"
|
||||||
# notify(error_msg)
|
# notify(error_msg)
|
||||||
|
|
@ -121,7 +122,7 @@ def normalize_seq(s):
|
||||||
"""Ensures input is a tuple regardless of if it was a list or tuple."""
|
"""Ensures input is a tuple regardless of if it was a list or tuple."""
|
||||||
return tuple(s) if s is not None else ()
|
return tuple(s) if s is not None else ()
|
||||||
|
|
||||||
def resolve_prescription(result: Result, *args, **kwargs):
|
def resolve_prescription(result: Result, ENUM_MAP: dict, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Find the correct solution to the type of enum problem,
|
Find the correct solution to the type of enum problem,
|
||||||
And then add the args from either the original caller's args,
|
And then add the args from either the original caller's args,
|
||||||
|
|
@ -133,6 +134,7 @@ def resolve_prescription(result: Result, *args, **kwargs):
|
||||||
return result.function_choice
|
return result.function_choice
|
||||||
|
|
||||||
# Priority 2: Look up the Enum map
|
# Priority 2: Look up the Enum map
|
||||||
|
# logger.info(f"self.ENUM_MAP is {self.ENUM_MAP}")
|
||||||
if result.error_type not in ENUM_MAP:
|
if result.error_type not in ENUM_MAP:
|
||||||
return None # No solution
|
return None # No solution
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -134,11 +134,13 @@ def api_call_and_extraction(
|
||||||
# EVALUATE SERVER-SIDE ISSUES (assuming it replied)
|
# EVALUATE SERVER-SIDE ISSUES (assuming it replied)
|
||||||
#-----------------------------
|
#-----------------------------
|
||||||
# Layer 2: Validity of Server's Reply:
|
# Layer 2: Validity of Server's Reply:
|
||||||
raw_reply = api_response.data
|
pre_validated_reply = api_response.data
|
||||||
logger.info("Validating the Validity of the Server's Reply..")
|
logger.info("Validating the Validity of the Server's Reply..")
|
||||||
worked, server_reply = validate_model(ServerReply, raw_reply)
|
worked, server_reply = validate_model(ServerReply, pre_validated_reply)
|
||||||
|
|
||||||
error_msg = f"Invalid Structure for ServerReply! The raw data is {raw_reply}"
|
error_msg = f"Validation of the Server's Reply had issues! Pre-validate: {pre_validated_reply}. Post validate: {server_reply}"
|
||||||
|
|
||||||
|
logger.info("Checking if validation worked..")
|
||||||
|
|
||||||
if not worked:
|
if not worked:
|
||||||
logger.error(error_msg)
|
logger.error(error_msg)
|
||||||
|
|
@ -148,21 +150,29 @@ def api_call_and_extraction(
|
||||||
logger.error(error_msg)
|
logger.error(error_msg)
|
||||||
return invalid_server_format()
|
return invalid_server_format()
|
||||||
|
|
||||||
|
logger.info("Validation successfull. Checking if the server's reply says the operation worked..")
|
||||||
if not server_reply.valid:
|
if not server_reply.valid:
|
||||||
error_message = f"Server reply was correctly structured, but the actual invoice failed to be created. The raw data is {raw_reply}"
|
error_message = f"Server reply was correctly structured, but the actual invoice failed to be created. The raw data is {raw_reply}"
|
||||||
logger.error(error_message)
|
logger.error(error_message)
|
||||||
# translate by enum error code:
|
# translate by enum error code:
|
||||||
return translate_server_failure(server_reply)
|
return translate_server_failure(server_reply)
|
||||||
|
|
||||||
|
logger.info(f"Checking for the desired data type of {desired_type}")
|
||||||
if not hasattr(server_reply, desired_type):
|
if not hasattr(server_reply, desired_type):
|
||||||
error_msg = "Developer Error: You're asking for a data type that isn't in the object!"
|
error_msg = "Developer Error: You're asking for a data type that isn't in the object!"
|
||||||
logger.error(error_msg)
|
logger.error(error_msg)
|
||||||
return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg)
|
return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg)
|
||||||
|
|
||||||
|
print(f"right before check, server_reply is {server_reply} and desired_type is {desired_type}")
|
||||||
data = getattr(server_reply, desired_type)
|
data = getattr(server_reply, desired_type)
|
||||||
if data:
|
logger.info(f"Successfully returning the data of {data}")
|
||||||
return Result(valid=True, data=data)
|
return Result(valid=True, data=data)
|
||||||
else:
|
|
||||||
return Result(valid=False, error_type=ResultError.MISSING_DATA, message="Server Replied it was valid, but failed to provide the data.")
|
|
||||||
|
|
||||||
|
# else:
|
||||||
|
# error_msg = f"Server Replied it was valid, but failed to provide the data for the desired type of {desired_type}."
|
||||||
|
# logger.error(error_msg)
|
||||||
|
# return Result(valid=False, error_type=ResultError.MISSING_DATA, message=error_msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -115,17 +115,19 @@ class CheckPaidTimer:
|
||||||
# Reset instance:
|
# Reset instance:
|
||||||
CheckPaidTimer._instance = None
|
CheckPaidTimer._instance = None
|
||||||
|
|
||||||
# SAVE INDIVIDUAL:
|
|
||||||
if self.payment_type == PaymentType.CRYPTO:
|
|
||||||
marked = mark_as_paid(profile_id=self.profile_id)
|
marked = mark_as_paid(profile_id=self.profile_id)
|
||||||
|
|
||||||
# SAVE TICKETS PAID:
|
# # SAVE INDIVIDUAL:
|
||||||
elif self.payment_type == PaymentType.TICKET:
|
# if self.payment_type == PaymentType.CRYPTO:
|
||||||
marked = mark_global_tickets_paid()
|
# marked = mark_as_paid(profile_id=self.profile_id)
|
||||||
|
|
||||||
else:
|
# # SAVE TICKETS PAID:
|
||||||
logger.error("Invalid payment type picked.")
|
# elif self.payment_type == PaymentType.TICKET:
|
||||||
marked = Result(valid=False, error_type=ResultError.INVALID_INPUT, message="Unsupported protocol.")
|
# marked = mark_global_tickets_paid()
|
||||||
|
|
||||||
|
# else:
|
||||||
|
# logger.error("Invalid payment type picked.")
|
||||||
|
# marked = Result(valid=False, error_type=ResultError.INVALID_INPUT, message="Unsupported protocol.", billing_code=self.billing_code)
|
||||||
|
|
||||||
# Either way evaluate:
|
# Either way evaluate:
|
||||||
if not marked.valid:
|
if not marked.valid:
|
||||||
|
|
@ -141,27 +143,27 @@ class CheckPaidTimer:
|
||||||
print("Not yet paid, but continue to check..")
|
print("Not yet paid, but continue to check..")
|
||||||
self.total_attempts = self.total_attempts + 1
|
self.total_attempts = self.total_attempts + 1
|
||||||
message = f"Status: Not yet Paid ({self.total_attempts} Checks)"
|
message = f"Status: Not yet Paid ({self.total_attempts} Checks)"
|
||||||
self.view_callback(paid=False, stopped=False, message=message)
|
self.view_callback(paid=False, stopped=False, message=message, billing_code=self.billing_code)
|
||||||
|
|
||||||
# invalid inputs, stop immediately,
|
# invalid inputs, stop immediately,
|
||||||
elif result.error_type == ResultError.INVALID_INPUT:
|
elif result.error_type == ResultError.INVALID_INPUT:
|
||||||
print("immediately stop, the billing code or other input is invalid")
|
print("immediately stop, the billing code or other input is invalid")
|
||||||
self.stop_polling()
|
self.stop_polling()
|
||||||
CheckPaidTimer._instance = None # Reset instance
|
CheckPaidTimer._instance = None # Reset instance
|
||||||
self.view_callback(paid=False, stopped=True, message=result.message)
|
self.view_callback(paid=False, stopped=True, message=result.message, billing_code=self.billing_code)
|
||||||
|
|
||||||
# connection error (timed out). Not related to payment status:
|
# connection error (timed out). Not related to payment status:
|
||||||
elif self.failed_connections > MAX_FAILED_CONNECTIONS:
|
elif self.failed_connections > MAX_FAILED_CONNECTIONS:
|
||||||
self.stop_polling()
|
self.stop_polling()
|
||||||
CheckPaidTimer._instance = None # Reset instance
|
CheckPaidTimer._instance = None # Reset instance
|
||||||
message = f"Your network connection timed out after {self.failed_connections} failed attempts"
|
message = f"Your network connection timed out after {self.failed_connections} failed attempts"
|
||||||
self.view_callback(paid=False, stopped=True, message=message)
|
self.view_callback(paid=False, stopped=True, message=message, billing_code=self.billing_code)
|
||||||
|
|
||||||
# evaluate timeouts:
|
# evaluate timeouts:
|
||||||
elif result.error_type == ResultError.CONNECTION:
|
elif result.error_type == ResultError.CONNECTION:
|
||||||
self.failed_connections = self.failed_connections + 1
|
self.failed_connections = self.failed_connections + 1
|
||||||
message = f"The number of failed connections is {self.failed_connections}. This will time you out after {MAX_FAILED_CONNECTIONS} in total."
|
message = f"The number of failed connections is {self.failed_connections}. This will time you out after {MAX_FAILED_CONNECTIONS} in total."
|
||||||
self.view_callback(paid=False, stopped=True, message=message)
|
self.view_callback(paid=False, stopped=True, message=message, billing_code=self.billing_code)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print("Other errors/unknown errors")
|
print("Other errors/unknown errors")
|
||||||
|
|
@ -169,5 +171,5 @@ class CheckPaidTimer:
|
||||||
print(f"result.message is {result.error_type}")
|
print(f"result.message is {result.error_type}")
|
||||||
self.stop_polling()
|
self.stop_polling()
|
||||||
CheckPaidTimer._instance = None
|
CheckPaidTimer._instance = None
|
||||||
self.view_callback(paid=False, stopped=True, message=result.message)
|
self.view_callback(paid=False, stopped=True, message=result.message, billing_code=self.billing_code)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,22 @@ def the_same_payment_cycle(payment_cycle: OverallPaymentCycle, profile_id: Optio
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def is_payment_active_for(profile_id: int) -> tuple:
|
||||||
|
"""
|
||||||
|
Evaluates if a payment is active, and is it for a given profile.
|
||||||
|
|
||||||
|
Returns a Tuple. First is it active. Second is it active for THIS PROFILE
|
||||||
|
"""
|
||||||
|
payment_cycle = OverallPaymentCycle()
|
||||||
|
if not payment_cycle.active:
|
||||||
|
return False, False
|
||||||
|
|
||||||
|
if profile_id == payment_cycle.profile_id:
|
||||||
|
return True, True
|
||||||
|
else:
|
||||||
|
return False, False
|
||||||
|
|
||||||
|
|
||||||
def start_new_payment_cycle(category: PaymentCategory, profile_id: Optional[int] = None) -> Result:
|
def start_new_payment_cycle(category: PaymentCategory, profile_id: Optional[int] = None) -> Result:
|
||||||
|
|
||||||
if not profile_id and category == PaymentCategory.INDIVIDUAL:
|
if not profile_id and category == PaymentCategory.INDIVIDUAL:
|
||||||
|
|
@ -89,7 +105,7 @@ def start_new_payment_cycle(category: PaymentCategory, profile_id: Optional[int]
|
||||||
logger.info("It's the same as before, returning the code")
|
logger.info("It's the same as before, returning the code")
|
||||||
return Result(valid=True, data=payment_cycle.billing_code)
|
return Result(valid=True, data=payment_cycle.billing_code)
|
||||||
else:
|
else:
|
||||||
error_msg = "There's already a billing process active, do you want to stop that?"
|
error_msg = f"There's already a billing process active, do you want to stop that?"
|
||||||
logger.error(error_msg)
|
logger.error(error_msg)
|
||||||
return Result(valid=False, error_type=ResultError.ACTIVE_BILLING, message=error_msg)
|
return Result(valid=False, error_type=ResultError.ACTIVE_BILLING, message=error_msg)
|
||||||
|
|
||||||
|
|
@ -100,7 +116,7 @@ def start_new_payment_cycle(category: PaymentCategory, profile_id: Optional[int]
|
||||||
if category == PaymentCategory.INDIVIDUAL:
|
if category == PaymentCategory.INDIVIDUAL:
|
||||||
payment_cycle.profile_id = profile_id
|
payment_cycle.profile_id = profile_id
|
||||||
payment_cycle.active_data = PaymentData(instance_id=profile_id)
|
payment_cycle.active_data = PaymentData(instance_id=profile_id)
|
||||||
subscription = get_profile_subscription(profile_id)
|
subscription = get_profile_subscription(profile_id=profile_id, save_pending=True) # save to disk that it's pending
|
||||||
if subscription:
|
if subscription:
|
||||||
billing_code = subscription.billing_code
|
billing_code = subscription.billing_code
|
||||||
else:
|
else:
|
||||||
|
|
@ -343,7 +359,7 @@ def check_paid(billing_code: str, connection_observer: ConnectionObserver) -> Re
|
||||||
if not result.valid:
|
if not result.valid:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
payment_status = result.payment_status
|
payment_status = result.data
|
||||||
|
|
||||||
# paid
|
# paid
|
||||||
if payment_status:
|
if payment_status:
|
||||||
|
|
@ -368,7 +384,7 @@ def paying_for_tickets() -> bool:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def get_current_billing_data() -> Result:
|
def get_current_billing_data(category: Optional[PaymentCategory] = None) -> Result:
|
||||||
"""
|
"""
|
||||||
Goal:
|
Goal:
|
||||||
Get whatever current billing data is relevant by the overall cycle, for either billing category
|
Get whatever current billing data is relevant by the overall cycle, for either billing category
|
||||||
|
|
@ -380,30 +396,39 @@ def get_current_billing_data() -> Result:
|
||||||
# Is there currently a process?
|
# Is there currently a process?
|
||||||
payment_cycle = OverallPaymentCycle()
|
payment_cycle = OverallPaymentCycle()
|
||||||
|
|
||||||
if not payment_cycle:
|
if not payment_cycle and not category:
|
||||||
error_msg = "There is no current billing cycle."
|
error_msg = "There is no current billing cycle AND no category provided."
|
||||||
return Result(valid=False, error_type=ResultError.INVALID, message=error_msg)
|
return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg)
|
||||||
|
|
||||||
|
# if not payment_cycle and category:
|
||||||
|
|
||||||
|
|
||||||
billing_data = payment_cycle.active_data
|
billing_data = payment_cycle.active_data
|
||||||
|
|
||||||
if not billing_data:
|
if not billing_data:
|
||||||
error_msg = "There is no current billing data."
|
error_msg = "There is no current billing data."
|
||||||
return Result(valid=False, error_type=ResultError.INVALID, message=error_msg)
|
return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg)
|
||||||
|
|
||||||
return Result(valid=True, data=billing_data)
|
return Result(valid=True, data=billing_data)
|
||||||
|
|
||||||
|
|
||||||
def wipe_billing_cycle():
|
def wipe_billing_cycle() -> Result:
|
||||||
"""Wipes the entire overall cycle"""
|
"""Wipes the entire overall cycle"""
|
||||||
logger.info("Wiping the overall cycle, so this cycle no longer blocks other new ones")
|
logger.info("Wiping the overall cycle, so this cycle no longer blocks other new ones")
|
||||||
payment_cycle = OverallPaymentCycle()
|
payment_cycle = OverallPaymentCycle()
|
||||||
if payment_cycle:
|
if payment_cycle:
|
||||||
payment_cycle.wipe()
|
payment_cycle.wipe()
|
||||||
|
return Result(valid=True)
|
||||||
|
|
||||||
|
|
||||||
|
def get_profile_subscription(profile_id: int, save_pending: Optional[bool] = False) -> Subscription:
|
||||||
|
"""
|
||||||
|
Main Purpose:
|
||||||
|
Lookup the full profile object & get it's subscription. (metaphor of a GET request)
|
||||||
|
|
||||||
def get_profile_subscription(profile_id: int) -> Subscription:
|
2nd Feature:
|
||||||
"""Lookup the full profile object & get it's subscription"""
|
Optional flag to SAVE that the billing is pending. (metaphor of a POST request)
|
||||||
|
"""
|
||||||
print(f"starting lookup of profile id {profile_id}")
|
print(f"starting lookup of profile id {profile_id}")
|
||||||
profile = Profile.find_by_id(profile_id)
|
profile = Profile.find_by_id(profile_id)
|
||||||
if not profile:
|
if not profile:
|
||||||
|
|
@ -411,6 +436,12 @@ def get_profile_subscription(profile_id: int) -> Subscription:
|
||||||
logger.error(error_msg)
|
logger.error(error_msg)
|
||||||
return Result(valid=False, message=error_msg, error_type=ResultError.INVALID_INPUT)
|
return Result(valid=False, message=error_msg, error_type=ResultError.INVALID_INPUT)
|
||||||
|
|
||||||
|
|
||||||
|
# Optional save pending flag:
|
||||||
|
if save_pending:
|
||||||
|
profile.billing_pending = True
|
||||||
|
profile.save()
|
||||||
|
|
||||||
print(f"returning profile {profile}")
|
print(f"returning profile {profile}")
|
||||||
return profile.subscription
|
return profile.subscription
|
||||||
|
|
||||||
|
|
@ -424,13 +455,13 @@ def mark_as_paid() -> Result:
|
||||||
|
|
||||||
if not payment_cycle:
|
if not payment_cycle:
|
||||||
error_msg = "There is no current billing cycle."
|
error_msg = "There is no current billing cycle."
|
||||||
return Result(valid=False, error_type=ResultError.INVALID, message=error_msg)
|
return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg)
|
||||||
|
|
||||||
billing_data = payment_cycle.instance
|
billing_data = payment_cycle.instance
|
||||||
|
|
||||||
if not billing_data:
|
if not billing_data:
|
||||||
error_msg = "There is no current billing data."
|
error_msg = "There is no current billing data."
|
||||||
return Result(valid=False, error_type=ResultError.INVALID, message=error_msg)
|
return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg)
|
||||||
|
|
||||||
category = payment_cycle.category
|
category = payment_cycle.category
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue