import logging from fasthtml.fastapp import fast_app from starlette.routing import Mount, Route from myfasthtml.core.constants import Routes, ROUTE_ROOT utils_app, utils_rt = fast_app() logger = logging.getLogger("Commands") def mount_if_not_exists(app, path: str, sub_app): """ Mounts a sub-application only if no Mount object already exists at the specified path in the main application's router. """ is_mounted = False for route in app.router.routes: if isinstance(route, Mount): if route.path == path: is_mounted = True break if not is_mounted: app.mount(path, app=sub_app) def merge_classes(*args): all_elements = [] for element in args: if element is None or element == '': continue if isinstance(element, (tuple, list, set)): all_elements.extend(element) elif isinstance(element, dict): if "cls" in element: all_elements.append(element.pop("cls")) elif "class" in element: all_elements.append(element.pop("class")) elif isinstance(element, str): all_elements.append(element) else: raise ValueError(f"Cannot merge {element} of type {type(element)}") if all_elements: # Remove duplicates while preserving order unique_elements = list(dict.fromkeys(all_elements)) return " ".join(unique_elements) else: return None def debug_routes(app): for route in app.router.routes: if isinstance(route, Mount): for sub_route in route.app.router.routes: print(f"path={route.path}{sub_route.path}, method={sub_route.methods}, endpoint={sub_route.endpoint}") elif isinstance(route, Route): print(f"path={route.path}, methods={route.methods}, endpoint={route.endpoint}") def mount_utils(app): """ Mounts the commands_app to the given application instance if the route does not already exist. :param app: The application instance to which the commands_app will be mounted. :type app: Any :return: Returns the result of the mount operation performed by mount_if_not_exists. :rtype: Any """ return mount_if_not_exists(app, ROUTE_ROOT, utils_app) def get_default_ft_attr(ft): """ for every type of HTML element (ft) gives the default attribute to use for binding :param ft: :return: """ if ft.tag == "input": if ft.attrs.get("type") == "checkbox": return "checked" elif ft.attrs.get("type") == "radio": return "checked" elif ft.attrs.get("type") == "file": return "files" else: return "value" else: return None # indicate that the content of the FT should be updated def get_default_attr(data): all_attrs = data.__dict__.keys() return next(iter(all_attrs)) @utils_rt(Routes.Commands) def post(session: str, c_id: str): """ Default routes for all commands. :param session: :param c_id: :return: """ logger.debug(f"Entering {Routes.Commands} with {session=}, {c_id=}") from myfasthtml.core.commands import CommandsManager command = CommandsManager.get_command(c_id) if command: return command.execute() raise ValueError(f"Command with ID '{c_id}' not found.") @utils_rt(Routes.Bindings) def post(session: str, b_id: str, values: dict): """ Default routes for all bindings. :param session: :param b_id: :param values: :return: """ logger.debug(f"Entering {Routes.Bindings} with {session=}, {b_id=}, {values=}") from myfasthtml.core.bindings import BindingsManager binding = BindingsManager.get_binding(b_id) if binding: return binding.update(values) raise ValueError(f"Binding with ID '{b_id}' not found.")