from argparse import _SubParsersAction
from functools import partial
from typing import List, Optional
from enum import IntEnum
from lxml.objectify import Element, IntElement, StringElement, BoolElement
from q2_sdk.ardent.refresh_cache import RefreshCache
from q2_sdk.core.cli.cli_tools import SelectMenu, MenuOption
from q2_sdk.core.cli import textui, validators
from q2_sdk.core.configuration import settings
from q2_sdk.core.dynamic_imports import (
api_ExecuteStoredProcedure as ExecuteStoredProcedure,
)
from q2_sdk.entrypoints import invalidate_hq_cache
from q2_sdk.hq.db.navigation_style import NavigationStyle
from q2_sdk.hq.db.ui_text import UiText
from q2_sdk.hq.exceptions import MissingNavNodeError
from q2_sdk.hq.models.hq_credentials import HqCredentials
from q2_sdk.tools import utils
from .db_object import DbObject, DEFAULT
from .representation_row_base import RepresentationRowBase
from ..models.hq_params.stored_procedure import Param
D_TYPES = ExecuteStoredProcedure.DataType
[docs]
class NavigationType(IntEnum):
StandardMenu = 1
MobileThumbBar = 2
NavLandingPage = 3
[docs]
def prompt_for_route(navigation_style):
if navigation_style.ShortName.text == "DirectLink":
route = _prompt_for_url()
else:
route = textui.query(
"What would you like to put in the Route column?",
validators=[validators.RegexValidator(r".*")],
)
return route
[docs]
def prompt_for_display_name(default=None):
display_name = textui.query("\nNav item text?", default=default)
return display_name
def _prompt_for_url():
url = textui.query("\nURL to associate with this Nav Item?")
if not url.startswith("http"):
url = "http://{}".format(url)
return url
[docs]
def prompt_for_order(nav_nodes, parent_node):
if parent_node is not None:
choice_sub_nodes = [
x
for x in nav_nodes
if x.find("ParentNavigationNodeID") == parent_node.NavigationNodeID
]
textui.puts(textui.colored.cyan("\n--{}--".format(parent_node.TextValue)))
else:
choice_sub_nodes = [
x for x in nav_nodes if not hasattr(x, "ParentNavigationNodeID")
]
textui.puts(
textui.colored.cyan(
"-\n-{}--".format("--Online Top Level Navigation Menu--")
)
)
counter = 1
for node in choice_sub_nodes:
textui.puts(
textui.colored.blue("{}) ".format(counter))
+ textui.colored.yellow("-> here")
)
textui.puts(textui.colored.cyan("\t{}".format(node.TextValue)))
counter += 1
textui.puts(
textui.colored.blue("{}) ".format(counter)) + textui.colored.yellow("-> here")
)
choice = (
int(
textui.query(
"\nWhere should this nav node appear in the submenu? ",
validators=[
validators.OptionValidator([str(x) for x in range(1, counter + 1)])
],
)
)
- 1
)
if choice == 0:
lower_bound = 0
else:
lower_bound = choice_sub_nodes[choice - 1].Order
order = lower_bound + 1
return order
[docs]
def prompt_for_central_order(nav_nodes, parent_node):
if parent_node is not None:
choice_sub_nodes = [
x
for x in nav_nodes
if x.find("ParentNavigationID") == parent_node.NavigationID
]
textui.puts(textui.colored.cyan("\n--{}--".format(parent_node.TextValue)))
else:
choice_sub_nodes = [
x for x in nav_nodes if not hasattr(x, "ParentNavigationID")
]
textui.puts(
textui.colored.cyan(
"-\n-{}--".format("--Online Top Level Navigation Menu--")
)
)
counter = 1
for node in choice_sub_nodes:
textui.puts(
textui.colored.blue("{}) ".format(counter))
+ textui.colored.yellow("-> here")
)
textui.puts(textui.colored.cyan("\t{}".format(node.TextValue)))
counter += 1
textui.puts(
textui.colored.blue("{}) ".format(counter)) + textui.colored.yellow("-> here")
)
choice = (
int(
textui.query(
"\nWhere should this nav node appear in the submenu? ",
validators=[
validators.OptionValidator([str(x) for x in range(1, counter + 1)])
],
)
)
- 1
)
if choice == 0:
lower_bound = 0
else:
lower_bound = choice_sub_nodes[choice - 1].Order
if choice == len(choice_sub_nodes):
upper_bound = lower_bound + 200
else:
upper_bound = choice_sub_nodes[choice].Order
order = round((upper_bound + lower_bound) / 2)
return order
[docs]
def prompt_for_parent_node_id(top_level_nodes):
choice = SelectMenu(
[MenuOption(x.TextValue, x.NavigationNodeID.pyval) for x in top_level_nodes],
"\nWhich menu should this nav node live under? (Leave blank to insert at top level nav)",
subheaders=["--Online Top Level Navigation Menu--"],
allow_blank=True,
).prompt()
return choice
[docs]
def prompt_for_central_parent_node_id(top_level_nodes):
choice = SelectMenu(
[MenuOption(x.TextValue, x.NavigationID.pyval) for x in top_level_nodes],
"\nWhich menu should this nav node live under?",
subheaders=["--Central Top Level Navigation Menu--"],
allow_blank=False,
).prompt()
return choice
[docs]
def prompt_for_css_class(default=None):
css_classes = NavNode.AVAILABLE_ICONS
choice = SelectMenu(
[MenuOption("{}".format(x), x) for x in css_classes],
"Which Icon should be displayed? (Leave blank for default)",
subheaders=["--Current list of icons--"],
default=default,
allow_blank=True,
).prompt()
return choice
[docs]
def prompt_for_nav_node(nav_nodes):
nav_node = SelectMenu(
[MenuOption(x.TextValue.text, x) for x in nav_nodes], "Which Nav Node?"
).prompt()
return nav_node
[docs]
def prompt_for_device_bitflag(default="7"):
device_bitflag = textui.query(
"\nDevice Bitflag?",
default=default,
validators=[
validators.OptionValidator(
[str(x) for x in range(0, 8)],
message="Device Bitflag must be between 0 and 7",
)
],
)
return device_bitflag
[docs]
def prompt_for_enable_nav(default="1") -> str:
enable_nav = textui.query(
"\nEnabled?",
default=default,
validators=[
validators.BooleanValidator(message="Please enter a valid boolean value.")
],
)
return enable_nav
[docs]
def prompt_for_route_params():
route_params = textui.query(
"\nRoute Params? (Formatted as a json string)",
validators=[validators.RegexValidator(r".*")],
)
return route_params
[docs]
class NavNodeRow(RepresentationRowBase):
NavigationNodeID: IntElement = "NavigationNodeID"
TextValue: StringElement = "TextValue"
ParentNavigationNodeID: IntElement = "ParentNavigationNodeID"
Order: IntElement = "Order"
CssClass: StringElement = "CssClass"
ShortName: StringElement = "ShortName"
Route: StringElement = "Route"
FormID: IntElement = "FormID"
VendorID: IntElement = "VendorID"
DeviceBitflag: IntElement = "DeviceBitflag"
Enabled: BoolElement = "Enabled"
DynNavWindowFeaturesId: IntElement = "DynNavWindowFeaturesId"
RouteParameters: StringElement = "RouteParameters"
NavigationID: IntElement = "NavigationID"
NavigationTypeID: IntElement = "NavigationTypeID"
[docs]
class CentralNavNodeRow(RepresentationRowBase):
NavigationID: IntElement = "NavigationID"
TextValue: StringElement = "TextValue"
ParentNavigationID: IntElement = "ParentNavigationID"
Order: IntElement = "Order"
ShortName: StringElement = "ShortName"
FormID: IntElement = "FormID"
CssClass: StringElement = "CssClass"
[docs]
class NavNode(DbObject):
GET_BY_NAME_KEY = "ShortName"
NAME = "NavigationNode"
REPRESENTATION_ROW_CLASS = NavNodeRow
AVAILABLE_ICONS = [
"landing-page",
"message-center",
"transactions",
"payments",
"commercial",
"branches",
"services",
"news",
"reports",
"settings",
"help",
"logoff",
]
def __init__(
self,
logger,
hq_credentials: Optional[HqCredentials] = None,
ret_table_obj: bool | None = None,
):
super().__init__(logger, hq_credentials, ret_table_obj)
self._nav_menu = None
self._central_nav_menu = None
[docs]
def add_arguments(self, parser: _SubParsersAction):
subparser = parser.add_parser("get_nav_nodes")
subparser.set_defaults(
parser="get_nav_nodes", func=partial(self.get, serialize_for_cli=True)
)
subparser.add_argument(
"-p",
"--parent_nav_node_id",
help="Q2_NavigationNode.ParentNavigationNodeID",
)
subparser = parser.add_parser("add_nav_node")
subparser.set_defaults(
parser="add_nav_node", func=partial(self.create_with_prompts)
)
subparser.add_argument("--display_name", help="Text visible to end users")
subparser.add_argument(
"--parent_nav_node_id", help="Q2_NavigationNode.ParentNavigationNodeID"
)
subparser.add_argument("--css_class", help="Q2_NavigationNode.CssClass")
subparser = parser.add_parser("remove_nav_node")
subparser.set_defaults(
parser="remove_nav_node", func=partial(self.delete_with_prompts)
)
subparser.add_argument(
"-n", "--nav_node_id", help="Q2_NavigationNode.NavigationNodeID"
)
subparser = parser.add_parser("update_nav_node")
subparser.set_defaults(
parser="update_nav_node", func=partial(self.update_with_prompts)
)
subparser.add_argument(
"-n", "--nav_node_id", help="Q2_NavigationNode.NavigationNodeID"
)
subparser = parser.add_parser("update_central_nav_node")
subparser.set_defaults(
parser="update_central_nav_node",
func=partial(self.update_central_with_prompts),
)
subparser.add_argument("-n", "--nav_node_id", help="Q2_Navigation.NavigationID")
[docs]
async def get(
self, parent_nav_node_id: Optional[int] = None, serialize_for_cli=False
) -> List[NavNodeRow]:
"""Returns a list of NavNode Elements"""
response = await self.call_hq("sdk_GetNavNodes")
nav_menu = NavMenu(response)
self._nav_menu = nav_menu
self._nav_menu.clear_saved_properties()
response = nav_menu.sorted_nodes
if parent_nav_node_id:
response = [
x
for x in response
if x.findtext("ParentNavigationNodeID") == parent_nav_node_id
or x.findtext("NavigationNodeID") == parent_nav_node_id
]
if serialize_for_cli:
response = self.serialize_for_cli(
response,
fields_to_display=[
"NavigationNodeID",
"ParentNavigationNodeID",
"Order",
"TextValue",
"CssClass",
"Route",
"FormID",
"VendorID",
"PropertyID",
"DeviceBitflag",
"NavigationID",
],
fields_to_truncate=["Route"],
)
return response
[docs]
async def get_central(
self, parent_nav_node_id: Optional[int] = None, serialize_for_cli=False
) -> list[CentralNavNodeRow]:
"""Returns a list of NavNode Elements"""
response = await self.call_hq(
"sdk_GetCentralNavNodes", representation_class_override=CentralNavNodeRow
)
central_nav_menu = CentralNavMenu(response)
self._central_nav_menu = central_nav_menu
self._central_nav_menu.clear_saved_properties()
response = central_nav_menu.nodes
if parent_nav_node_id:
response = [
x
for x in response
if x.findtext("ParentNavigationID") == parent_nav_node_id
or x.findtext("NavigationID") == parent_nav_node_id
]
if serialize_for_cli:
response = self.serialize_for_cli(
response,
fields_to_display=[
"NavigationID",
"ParentNavigationID",
"Order",
"TextValue",
"ShortName",
"FormID",
],
)
return response
[docs]
async def create(
self,
navigation_style_id: int,
route: Optional[str],
order: int,
short_name: str,
ui_text_element_id: int,
parent_nav_node_id: Optional[int] = None,
css_class: Optional[str] = None,
form_id: Optional[int] = None,
property_id: Optional[int] = None,
route_parameters: Optional[str] = None,
vendor_id: Optional[int] = None,
sso_iframe_option: Optional[bool] = False,
device_bit_flag: Optional[int] = 7,
enabled: Optional[bool] = True,
navigation_id: Optional[int] = 1,
navigation_type_id: Optional[int] = NavigationType.StandardMenu.value,
available_for_landing: Optional[bool] = False,
):
if parent_nav_node_id == "":
parent_nav_node_id = None
nav_menu = await self.get_nav_menu()
await self.__clear_position(
nav_menu=nav_menu,
navigation_id=navigation_id,
parent_nav_node_id=parent_nav_node_id,
order=order,
)
if parent_nav_node_id == "":
parent_nav_node_id = None
if vendor_id and route is None:
route = "sso"
params = []
parameters = [
Param(navigation_style_id, D_TYPES.Int, "navigation_style_id"),
Param(route, D_TYPES.VarChar, "route"),
Param(order, D_TYPES.Int, "order"),
Param(short_name, D_TYPES.VarChar, "short_name"),
Param(ui_text_element_id, D_TYPES.Int, "ui_text_element_id"),
Param(parent_nav_node_id, D_TYPES.Int, "parent_navigation_node"),
Param(css_class, D_TYPES.VarChar, "css_class"),
Param(form_id, D_TYPES.Int, "form_id"),
Param(vendor_id, D_TYPES.Int, "vendor_id"),
Param(property_id, D_TYPES.Int, "property_id"),
Param(route_parameters, D_TYPES.VarChar, "route_parameters"),
Param(device_bit_flag, D_TYPES.Int, "device_bit_flag"),
Param(navigation_id, D_TYPES.Int, "navigation_id"),
Param(enabled, D_TYPES.Bit, "enabled"),
Param(navigation_type_id, D_TYPES.Int, "navigation_type_id"),
Param(available_for_landing, D_TYPES.Bit, "available_for_landing"),
]
if sso_iframe_option:
parameters.append(Param("1", D_TYPES.Int, "sso_iframe_option"))
for param in parameters:
param.add_to_param_list(params)
result = await self.call_hq(
"sdk_AddNavNode", ExecuteStoredProcedure.SqlParameters(params)
)
# get new nav_menu, can't get nav_node_id without querying so might as well get it all and make it easier
await self.get()
return result
[docs]
async def create_central(
self,
order: int,
short_name: str,
text_element_id: int,
parent_nav_node_id: int,
form_id: int,
):
parameters = [
ExecuteStoredProcedure.SqlParam(
ExecuteStoredProcedure.DataType.Int, "order", order
),
ExecuteStoredProcedure.SqlParam(
ExecuteStoredProcedure.DataType.VarChar, "short_name", short_name
),
ExecuteStoredProcedure.SqlParam(
ExecuteStoredProcedure.DataType.Int, "text_element_id", text_element_id
),
ExecuteStoredProcedure.SqlParam(
ExecuteStoredProcedure.DataType.Int,
"parent_navigation_node",
parent_nav_node_id,
),
ExecuteStoredProcedure.SqlParam(
ExecuteStoredProcedure.DataType.Int, "form_id", form_id
),
]
result = await self.call_hq(
"sdk_AddCentralNav", ExecuteStoredProcedure.SqlParameters(parameters)
)
await self.get_central()
return result
[docs]
async def delete(self, short_name, form_id: int = None):
if form_id:
parameters = [
(
ExecuteStoredProcedure.SqlParam(
ExecuteStoredProcedure.DataType.Int, "form_id", form_id
)
)
]
else:
parameters = [
ExecuteStoredProcedure.SqlParam(
ExecuteStoredProcedure.DataType.VarChar, "short_name", short_name
)
]
result = await self.call_hq(
"sdk_RemoveNavNode", ExecuteStoredProcedure.SqlParameters(parameters)
)
if self._nav_menu:
self._nav_menu.remove_node(short_name)
return result
[docs]
async def delete_central(self, short_name):
parameters = [
ExecuteStoredProcedure.SqlParam(
ExecuteStoredProcedure.DataType.VarChar, "short_name", short_name
)
]
result = await self.call_hq(
"sdk_RemoveCentralNavNode", ExecuteStoredProcedure.SqlParameters(parameters)
)
if self._central_nav_menu:
self._central_nav_menu.remove_node(short_name)
return result
[docs]
async def update(
self,
navigation_node_id: int,
ui_text: str,
route: str,
order: int,
css_class: Optional[str] = DEFAULT,
parent_navigation_node: Optional[int] = DEFAULT,
device_bit_flag: Optional[int] = DEFAULT,
route_parameters: Optional[str] = DEFAULT,
form_id: Optional[int] = DEFAULT,
vendor_id: Optional[int] = DEFAULT,
enabled: Optional[bool] = DEFAULT,
dyn_nav_window_features_id: Optional[int] = DEFAULT,
property_id: Optional[int] = DEFAULT,
navigation_id: Optional[int] = DEFAULT,
navigation_type_id: Optional[int] = DEFAULT,
available_for_landing: Optional[bool] = DEFAULT,
**kwargs,
):
if kwargs.get("sso_iframe_option"):
dyn_nav_window_features_id = 1
nav_menu = await self.get_nav_menu()
nav_node = nav_menu.get_node_by_id(navigation_node_id)
if nav_node is None:
raise MissingNavNodeError
if css_class == DEFAULT:
try:
css_class = nav_node.CssClass.pyval
except AttributeError:
css_class = None
if parent_navigation_node == DEFAULT:
try:
parent_navigation_node = nav_node.ParentNavigationNodeID.pyval
except AttributeError:
parent_navigation_node = None
if device_bit_flag == DEFAULT:
try:
device_bit_flag = nav_node.DeviceBitflag.pyval
except AttributeError:
device_bit_flag = 7
if route_parameters == DEFAULT:
try:
route_parameters = nav_node.RouteParameters.pyval
except AttributeError:
route_parameters = None
if enabled == DEFAULT:
try:
enabled = nav_node.Enabled.pyval
except AttributeError:
enabled = True
if dyn_nav_window_features_id == DEFAULT:
try:
dyn_nav_window_features_id = nav_node.DynNavWindowFeaturesID.pyval
except AttributeError:
dyn_nav_window_features_id = None
if navigation_id == DEFAULT:
try:
navigation_id = nav_node.NavigationID.pyval
except AttributeError:
navigation_id = None
if form_id == DEFAULT:
try:
form_id = nav_node.FormID.pyval
except AttributeError:
form_id = None
if vendor_id == DEFAULT:
try:
vendor_id = nav_node.VendorID.pyval
except AttributeError:
vendor_id = None
if property_id == DEFAULT:
try:
property_id = nav_node.PropertyID.pyval
except AttributeError:
property_id = None
if navigation_type_id == DEFAULT:
try:
navigation_type_id = nav_node.NavigationTypeID.pyval
except AttributeError:
navigation_type_id = None
if available_for_landing == DEFAULT:
try:
available_for_landing = nav_node.AvailableForLanding.pyval
except AttributeError:
available_for_landing = False
await self.__clear_position(
nav_menu=nav_menu,
navigation_id=navigation_id,
parent_nav_node_id=parent_navigation_node,
order=order,
not_navigation_node_id=navigation_node_id,
)
parameters = []
parameters_list = [
Param(navigation_node_id, D_TYPES.Int, "navigation_node_id"),
Param(ui_text, D_TYPES.VarChar, "ui_text"),
Param(route, D_TYPES.VarChar, "route"),
Param(parent_navigation_node, D_TYPES.Int, "parent_navigation_node"),
Param(order, D_TYPES.Int, "order"),
Param(css_class, D_TYPES.VarChar, "css_class"),
Param(device_bit_flag, D_TYPES.Int, "device_bit_flag"),
Param(route_parameters, D_TYPES.VarChar, "route_parameters"),
Param(form_id, D_TYPES.Int, "form_id"),
Param(vendor_id, D_TYPES.Int, "vendor_id"),
Param(enabled, D_TYPES.Bit, "enabled"),
Param(navigation_id, D_TYPES.Int, "navigation_id"),
Param(property_id, D_TYPES.Int, "property_id"),
Param(
dyn_nav_window_features_id, D_TYPES.Int, "dyn_nav_window_features_id"
),
Param(navigation_type_id, D_TYPES.Int, "navigation_type_id"),
Param(available_for_landing, D_TYPES.Bit, "available_for_landing"),
]
for param in parameters_list:
param.add_to_param_list(parameters)
result = await self.call_hq(
"sdk_UpdateNavNode", ExecuteStoredProcedure.SqlParameters(parameters)
)
self._nav_menu.update_node(
navigation_node_id, ui_text, route, order, css_class, parent_navigation_node
)
await invalidate_hq_cache.invalidate(self.logger, self.hq_credentials)
return result
[docs]
async def update_central(
self,
navigation_node_id: int,
ui_text: str,
order: int,
css_class: Optional[str] = None,
parent_navigation_node: Optional[int] = None,
):
nav_menu = await self.get_central_nav_menu()
if nav_menu.get_node_by_id(navigation_node_id) is None:
raise MissingNavNodeError
for node in nav_menu.sorted_nodes:
if node.NavigationID.pyval == navigation_node_id:
continue
node_parent = node.findtext("ParentNavigationID")
if node_parent == parent_navigation_node and node.Order == order:
await self.update_central(
node.NavigationID.pyval,
node.findtext("TextValue"),
node.Order.pyval + 1,
node.findtext("CssClass"),
node.findtext("ParentNavigationID"),
)
result = await self.call_hq(
"sdk_UpdateCentralNavNode",
ExecuteStoredProcedure.SqlParameters([
ExecuteStoredProcedure.SqlParam(
ExecuteStoredProcedure.DataType.Int,
"navigation_node_id",
navigation_node_id,
),
ExecuteStoredProcedure.SqlParam(
ExecuteStoredProcedure.DataType.VarChar, "ui_text", ui_text
),
ExecuteStoredProcedure.SqlParam(
ExecuteStoredProcedure.DataType.Int,
"parent_navigation_node",
parent_navigation_node,
),
ExecuteStoredProcedure.SqlParam(
ExecuteStoredProcedure.DataType.Int, "order", order
),
ExecuteStoredProcedure.SqlParam(
ExecuteStoredProcedure.DataType.VarChar, "css_class", css_class
),
]),
)
self._central_nav_menu.update_node(
navigation_node_id, ui_text, order, css_class, parent_navigation_node
)
return result
async def _prompt_for_navigation_style(self): # pragma: no cover
db_obj = NavigationStyle(self.logger)
nav_styles = await db_obj.get()
supported = ["DirectLink", "Route"]
choice = SelectMenu(
[
MenuOption(x.ShortName.text, x)
for x in nav_styles
if x.ShortName.text in supported
],
"Which Type of Nav Node?",
).prompt()
return choice
async def _get_ui_text_element_id(self, display_name): # pragma: no cover
ui_text_db = UiText(self.logger)
ui_text_obj = await ui_text_db.get(display_name)
if ui_text_obj == []:
await ui_text_db.create_per_language(
display_name, display_name, display_name
)
ui_text_obj = await ui_text_db.get(display_name)
ui_text_element_id = ui_text_obj[0].UiTextElementID.pyval
return ui_text_element_id
[docs]
async def create_with_prompts(
self,
display_name: Optional[str] = None,
parent_nav_node_id: Optional[int] = None,
css_class: Optional[str] = None,
):
navigation_style = await self._prompt_for_navigation_style()
navigation_style_id = navigation_style.NavigationStyleID.pyval
nav_menu = await self.get_nav_menu()
if not parent_nav_node_id: # pragma: no cover
parent_nav_node_id = prompt_for_parent_node_id(nav_menu.top_level_nodes)
parent_node = None
if parent_nav_node_id:
parent_node = nav_menu.get_node_by_id(parent_nav_node_id)
order = prompt_for_order(nav_menu.sorted_nodes, parent_node)
route = prompt_for_route(navigation_style)
if parent_node is None and css_class is None: # pragma: no cover
css_class = prompt_for_css_class()
if not display_name: # pragma: no cover
display_name = prompt_for_display_name()
ui_text_element_id = await self._get_ui_text_element_id(display_name)
device_bitflag = prompt_for_device_bitflag()
route_params = prompt_for_route_params()
nav_enabled = prompt_for_enable_nav()
await self.create(
navigation_style_id,
route,
order,
utils.snakeify(display_name),
ui_text_element_id,
parent_nav_node_id,
css_class,
route_parameters=route_params,
device_bit_flag=device_bitflag,
enabled=nav_enabled,
)
await self._refresh_caches()
async def _refresh_caches(self):
await invalidate_hq_cache.invalidate(
logger=self.logger,
hq_credentials=self.hq_credentials,
refresh_type=["NavigationNodes", "UserPropertyDataElements"],
)
if settings.ARDENT_URL:
await RefreshCache(self.logger).refresh_cache()
[docs]
async def delete_with_prompts(self, nav_node_id=None):
nav_menu = await self.get_nav_menu()
if nav_node_id is None:
nav_node = prompt_for_nav_node(nav_menu.sorted_nodes)
else:
nav_node = nav_menu.get_node_by_id(nav_node_id)
short_name = nav_node.ShortName.pyval
await self.delete(short_name)
[docs]
async def update_with_prompts(self, nav_node_id=None):
nav_menu = await self.get_nav_menu()
if nav_node_id:
node_to_update = nav_menu.get_node_by_id(nav_node_id)
else:
node_to_update = prompt_for_nav_node(nav_menu.nodes)
display_name = prompt_for_display_name(node_to_update.TextValue.text)
parent_node = None
require_update = textui.query_yn("Update location?")
order = node_to_update.Order.pyval
if require_update:
parent_nav_node_id = prompt_for_parent_node_id(nav_menu.top_level_nodes)
parent_node = nav_menu.get_node_by_id(parent_nav_node_id)
order = prompt_for_order(nav_menu.sorted_nodes, parent_node)
css_class = None
if parent_node is None:
current_css_class = None
if node_to_update.find("CssClass") is not None:
current_css_class = node_to_update.CssClass.text
css_class = prompt_for_css_class(current_css_class)
parent_nav_id = None
if parent_node is not None:
parent_nav_id = parent_node.findtext("NavigationNodeID")
device_bitflag = prompt_for_device_bitflag()
route_parameters = prompt_for_route_params()
enable_nav = prompt_for_enable_nav()
await self.update(
node_to_update.NavigationNodeID.pyval,
display_name,
node_to_update.findtext("Route"),
order,
css_class,
parent_nav_id,
device_bit_flag=device_bitflag,
route_parameters=route_parameters,
enabled=enable_nav,
)
await self._refresh_caches()
[docs]
async def update_central_with_prompts(self, nav_node_id=None):
nav_menu = await self.get_central_nav_menu()
if nav_node_id:
node_to_update = nav_menu.get_node_by_id(nav_node_id)
else:
node_to_update = prompt_for_nav_node(nav_menu.nodes)
display_name = prompt_for_display_name(node_to_update.TextValue.text)
parent_nav_id = node_to_update.ParentNavigationID.pyval
parent_node = nav_menu.get_node_by_id(parent_nav_id)
require_update = textui.query_yn("Update location?")
order = node_to_update.Order.pyval
if require_update:
parent_nav_node_id = prompt_for_central_parent_node_id(
nav_menu.top_level_nodes
)
parent_node = nav_menu.get_node_by_id(parent_nav_node_id)
order = prompt_for_central_order(nav_menu.sorted_nodes, parent_node)
css_class = None
if node_to_update.find("CssClass") is not None:
css_class = node_to_update.CssClass.text
parent_nav_id = parent_node.NavigationID.pyval
await self.update_central(
node_to_update.NavigationID.pyval,
display_name,
order,
css_class,
parent_nav_id,
)
async def __clear_position(
self,
nav_menu: NavMenu,
navigation_id: int,
parent_nav_node_id: Optional[int],
order: int,
not_navigation_node_id: int = None,
):
def safe_pyval(element):
return element.pyval if element else None
for node in nav_menu.sorted_nodes:
node_id = node.NavigationNodeID.pyval
node_navigation_id = node.NavigationID.pyval
node_parent_nav_node_id = safe_pyval(node.find("ParentNavigationNodeID"))
node_order = node.Order.pyval
node_ui_text = safe_pyval(node.find("TextValue"))
node_route = safe_pyval(node.find("Route"))
if not all([
not_navigation_node_id != node_id,
navigation_id == node_navigation_id,
parent_nav_node_id == node_parent_nav_node_id,
order == node_order,
]):
continue
await self.update(
navigation_node_id=node_id,
ui_text=node_ui_text,
route=node_route,
order=node_order + 1,
)