# core from core.controllers.ConfigurationController import ConfigurationController from core.controllers.tickets.UseTicketController import use_ticket from core.services.prepare_tickets.ticket_tracker import get_a_random_ticket from core.models.BaseProfile import BaseProfile as Profile from core.controllers.ProfileController import ProfileController # from core.controllers.tickets.TicketPayController import initiate_payment from core.services.subscriptions.subscriptions_by_protocol import start_new_payment_cycle, save_subscription, get_ticket_invoice from core.services.payment_phase.ticket_cycle import wipe_ticket_cycle # from core.services.payment_phase.payment_state import OverallPaymentCycle from core.models.enum_models import PaymentCategory from core.services.prepare_tickets.ticket_tracker import get_all_unused_tickets from core.utils.type_checker import type_checker from core.models.Result import Result, ResultError from core.errors.logger import logger # gui from gui.v2.infrastructure.action_registry import register_action from gui.v2.infrastructure.take_plumbing_action import take_action from gui.v2.infrastructure.setup_observers import connection_observer, ticket_observer from gui.v2.actions.error_handlers import navigate_to_new_sub_page # generic: from typing import Callable @register_action(ResultError.NO_TICKET) def start_and_navigate_to_ticketing(): """Starts a new ticketing cycle. (assumes the user opted in).""" start_result = start_new_payment_cycle(category=PaymentCategory.TICKET) if start_result.valid: logger.info("creating a new payment cycle was valid, it worked.") return Result(valid=False, error_type=ResultError.NAVIGATE_TO, navigate_to="plan_picker") else: logger.info(f"Returning the error of start_result {start_result}") return start_result @type_checker def random_ticket_dispatch(profile_id: int, passed_ui_caller: Callable) -> Result: """ User explicitly wants a random ticket. The reason this is in GUI repo, is because use_a_ticket is. """ random_ticket_result = get_a_random_ticket() if not random_ticket_result.valid: return random_ticket_result which_ticket = random_ticket_result.data # this is a redundant sanity check: if not which_ticket: Result(valid=False, error_type=ResultError.NO_TICKET, message="No tickets left. Would you like to get some?") # calls GUI repo function: return use_a_ticket(profile_id=profile_id, which_ticket=which_ticket, ui_caller=passed_ui_caller) @type_checker def dispatch_ticketing(profile_id: int, passed_ui_caller: Callable) -> Result: """Evaluate if a random ticket will be used.""" use_random_setting = ConfigurationController.use_random() if use_random_setting: return random_ticket_dispatch(profile_id=profile_id, passed_ui_caller=passed_ui_caller) # User does NOT want an automated selection, so they can pick from their tickets: do_they_have_tickets = get_ticket_data() if do_they_have_tickets.valid: return Result(valid=False, error_type=ResultError.NAVIGATE_TO, navigate_to="ticket_or_billing_choice") else: # user can decide if they want to setup tickets or do an individual profile: return navigate_to_new_sub_page() def start_ticket(ui_caller: Callable) -> Result: logger.info("started ticketing billing state") return take_action( action=start_new_payment_cycle, category=PaymentCategory.TICKET, ui_caller=ui_caller ) def fetch_ticket_invoice(ui_caller: Callable) -> str | None: return take_action( action=get_ticket_invoice, ui_caller=ui_caller, connection_observer=connection_observer ) def get_ticket_data() -> Result: """This is running in the background ALREADY. Get & format a list of unused tickets from core""" ticket_results = get_all_unused_tickets() valid = ticket_results.get('valid') if not valid: message = ticket_results.get('message') if not message: message = "Error gathering ticket data" Result(valid=False, error_type=ResultError.MISSING_FILE, message=message) raw_tickets = ticket_results.get('data') print(f"raw_tickets is {raw_tickets}") if not raw_tickets: Result(valid=False, error_type=ResultError.NO_TICKET, message="No tickets available. Would you like to get some?") formatted_data = [(f"Ticket #{t}", t) for t in raw_tickets] print(f"formatted_data is {formatted_data}") return Result(valid=True, data=formatted_data) def use_a_ticket(profile_id: int, which_ticket: int, ui_caller: Callable) -> str | None: """Coordinate the use of a given ticket. Returns a string on error, or None on success""" try: profile = ProfileController.get(int(profile_id)) except Exception as e: return f"The selected profile of {str(profile_id)} is either malformed or missing. {str(e)}" which_location = profile.location.id protocol_choice = profile.connection.code PRE_REQS = [profile, which_ticket, which_location, protocol_choice, ui_caller] if None in PRE_REQS: return "You are missing one of the pre-reqs to use a ticket." ticket_user = TicketUser( actual_profile=profile, which_ticket=which_ticket, ui_caller=ui_caller ) ticket_user.begin_thread() return None # no error class TicketUser(): actual_profile: Profile which_ticket: int ui_caller: Callable def begin_thread(self): """Step 1, begin a background thread with use""" take_action( action=use_ticket, ui_caller=self.apply_code, which_location=self.which_location, protocol_choice=self.protocol_choice, ticket_observer=ticket_observer, connection_observer=connection_observer, ) def apply_code(self, result: Result): """Step 2. Check if it worked. If so, apply it""" if not result.valid: logger.error("Use failed, returning to UI layer..") self.ui_caller(result) subscription = result.data if not subscription: logger.error("Use of ticket gave a blank subscription, returning to UI layer..") self.ui_caller(result) # core saves it: logger.info("saving subscription..") saved = save_subscription(profile=self.actual_profile, subscription=subscription) if not saved: error_msg = "Could not save the subscription" logger.error(error_msg) new_result = Result(valid=False, error_type=ResultError.FILE_SYSTEM, message=error_msg) self.ui_caller(new_result) logger.info("Saved sub, returning to UI layer..") self.ui_caller(result) @register_action(ResultError.TICKET_EXPIRED) def wipe_ticket_billing() -> Result: """Wipe existing data and start over.""" wiped = wipe_ticket_cycle() if wiped.valid: return Result(valid=True, error_type=ResultError.NAVIGATE_TO, navigate_to="plan_picker") else: return Result(valid=False, error_type=ResultError.NAVIGATE_TO, navigate_to="plan_picker")