Add ticket and note panes to new TUI
This commit is contained in:
parent
9b51bcbdc3
commit
0bcdde0ffb
5 changed files with 102 additions and 41 deletions
|
|
@ -1104,8 +1104,10 @@ class State():
|
||||||
# Write to progress file
|
# Write to progress file
|
||||||
self.progress_out.write_text('\n'.join(report), encoding='utf-8', errors='ignore')
|
self.progress_out.write_text('\n'.join(report), encoding='utf-8', errors='ignore')
|
||||||
|
|
||||||
def update_top_panes(self) -> None:
|
def update_top_panes(self, note_lines: list | None = None) -> None:
|
||||||
"""(Re)create top source/destination panes."""
|
"""(Re)create top source/destination panes."""
|
||||||
|
if not note_lines:
|
||||||
|
note_lines = []
|
||||||
source_exists = True
|
source_exists = True
|
||||||
source_str = ''
|
source_str = ''
|
||||||
dest_exists = True
|
dest_exists = True
|
||||||
|
|
@ -1173,27 +1175,27 @@ class State():
|
||||||
dest_str,
|
dest_str,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Bail if ticket not selected
|
||||||
|
if not self.ost:
|
||||||
|
return
|
||||||
|
|
||||||
# Ticket Details
|
# Ticket Details
|
||||||
# TODO: Fixme
|
self.ui.reset_subtitle_pane()
|
||||||
if self.ost and self.ost.ticket_id:
|
if self.ost.ticket_id and not self.ui.layout['Subtitle']['Panes']:
|
||||||
text = ansi.color_string(
|
self.ui.add_subtitle_pane(
|
||||||
[
|
ansi.color_string(
|
||||||
self.ost.ticket_name,
|
[f'#{self.ost.ticket_id}', str(self.ost.ticket_name)],
|
||||||
' ' if self.ost.note else '\n',
|
[None, 'CYAN'],
|
||||||
f'Ticket #{self.ost.ticket_id}',
|
),
|
||||||
f'\n{self.ost.note.splitlines()[0]}' if self.ost.note else '',
|
str(self.ost.ticket_subject),
|
||||||
],
|
|
||||||
['CYAN', None, None, 'YELLOW'],
|
|
||||||
sep='',
|
|
||||||
)
|
)
|
||||||
if self.panes.get('Ticket', None):
|
|
||||||
tmux.respawn_pane(self.panes['Ticket'], text=text)
|
# Tech note
|
||||||
else:
|
note_lines = self.ost.note.replace('...', '').splitlines()
|
||||||
self.panes['Ticket'] = tmux.split_window(
|
if note_lines:
|
||||||
behind=True,
|
self.ui.add_subtitle_pane(
|
||||||
lines=2,
|
ansi.color_string('Tech Note', 'YELLOW'),
|
||||||
text=text,
|
' | '.join(note_lines),
|
||||||
vertical=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1910,6 +1912,7 @@ def main() -> None:
|
||||||
main_menu.actions[menus.MENU_ACTIONS[2]]['Separator'] = True
|
main_menu.actions[menus.MENU_ACTIONS[2]]['Separator'] = True
|
||||||
else:
|
else:
|
||||||
main_menu.actions['Add tech note']['Separator'] = True
|
main_menu.actions['Add tech note']['Separator'] = True
|
||||||
|
state.update_top_panes()
|
||||||
try:
|
try:
|
||||||
state.init_recovery(args)
|
state.init_recovery(args)
|
||||||
except (FileNotFoundError, std.GenericAbort):
|
except (FileNotFoundError, std.GenericAbort):
|
||||||
|
|
@ -1937,10 +1940,10 @@ def main() -> None:
|
||||||
|
|
||||||
# Tech note
|
# Tech note
|
||||||
if 'tech note' in selection[0]:
|
if 'tech note' in selection[0]:
|
||||||
state.ost.add_note(
|
note_lines = state.ost.add_note(
|
||||||
'Please enter any additional information about this recovery',
|
'Please enter any additional information about this recovery',
|
||||||
)
|
)
|
||||||
state.update_top_panes()
|
state.update_top_panes(note_lines=note_lines)
|
||||||
|
|
||||||
# Start over
|
# Start over
|
||||||
if 'Fresh start' in selection[0]:
|
if 'Fresh start' in selection[0]:
|
||||||
|
|
|
||||||
|
|
@ -144,6 +144,7 @@ class State():
|
||||||
# Reset objects
|
# Reset objects
|
||||||
self.disks.clear()
|
self.disks.clear()
|
||||||
self.test_groups.clear()
|
self.test_groups.clear()
|
||||||
|
self.ui.remove_all_subtitle_panes()
|
||||||
|
|
||||||
# osTicket
|
# osTicket
|
||||||
self.ost.init()
|
self.ost.init()
|
||||||
|
|
@ -859,7 +860,12 @@ def print_countdown(proc, seconds) -> None:
|
||||||
print('')
|
print('')
|
||||||
|
|
||||||
|
|
||||||
def run_diags(state, menu, quick_mode=False, test_mode=False) -> None:
|
def run_diags(
|
||||||
|
state: State,
|
||||||
|
menu: cli.Menu,
|
||||||
|
quick_mode: bool = False,
|
||||||
|
test_mode: bool = False,
|
||||||
|
) -> None:
|
||||||
"""Run selected diagnostics."""
|
"""Run selected diagnostics."""
|
||||||
aborted = False
|
aborted = False
|
||||||
atexit.register(state.save_debug_reports)
|
atexit.register(state.save_debug_reports)
|
||||||
|
|
@ -878,15 +884,24 @@ def run_diags(state, menu, quick_mode=False, test_mode=False) -> None:
|
||||||
|
|
||||||
# Update top_text
|
# Update top_text
|
||||||
if state.ost.ticket_id:
|
if state.ost.ticket_id:
|
||||||
state.top_text += cli.color_string(
|
state.ui.add_subtitle_pane(
|
||||||
[f' #{state.ost.ticket_id}', state.ost.ticket_name],
|
cli.color_string(
|
||||||
|
[f'#{state.ost.ticket_id}', str(state.ost.ticket_name)],
|
||||||
[None, 'CYAN'],
|
[None, 'CYAN'],
|
||||||
|
),
|
||||||
|
str(state.ost.ticket_subject),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add note
|
# Add note
|
||||||
if (state.ost.ticket_id
|
if (state.ost.ticket_id
|
||||||
and menu.toggles['osTicket Tech Note']['Selected']):
|
and menu.toggles['osTicket Tech Note']['Selected']):
|
||||||
state.ost.add_note()
|
note_lines = state.ost.add_note()
|
||||||
|
if note_lines:
|
||||||
|
state.ui.add_subtitle_pane(
|
||||||
|
cli.color_string('Tech Note', 'YELLOW'),
|
||||||
|
' | '.join(note_lines),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Run tests
|
# Run tests
|
||||||
for group in state.test_groups:
|
for group in state.test_groups:
|
||||||
|
|
@ -927,6 +942,7 @@ def run_diags(state, menu, quick_mode=False, test_mode=False) -> None:
|
||||||
hw_osticket.update_checkboxes(state, NUM_DISK_TESTS)
|
hw_osticket.update_checkboxes(state, NUM_DISK_TESTS)
|
||||||
|
|
||||||
# Done
|
# Done
|
||||||
|
state.ui.remove_all_subtitle_panes()
|
||||||
state.save_debug_reports()
|
state.save_debug_reports()
|
||||||
atexit.unregister(state.save_debug_reports)
|
atexit.unregister(state.save_debug_reports)
|
||||||
if quick_mode:
|
if quick_mode:
|
||||||
|
|
|
||||||
|
|
@ -38,11 +38,12 @@ class osTicket():
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.db_connection = None
|
self.db_connection = None
|
||||||
self.db_cursor = None
|
self.db_cursor = None
|
||||||
self.disabled = False
|
self.disabled: bool = False
|
||||||
self.errors = False
|
self.errors: bool = False
|
||||||
self.note = None
|
self.note: str = ''
|
||||||
self.ticket_id = None
|
self.ticket_id: int | None = None
|
||||||
self.ticket_name = None
|
self.ticket_name: str | None = None
|
||||||
|
self.ticket_subject: str | None = None
|
||||||
|
|
||||||
# Ensure connection is closed atexit
|
# Ensure connection is closed atexit
|
||||||
atexit.register(self._disconnect)
|
atexit.register(self._disconnect)
|
||||||
|
|
@ -162,7 +163,7 @@ class osTicket():
|
||||||
LOG.error('Ticket ID not set')
|
LOG.error('Ticket ID not set')
|
||||||
raise RuntimeError('Ticket ID not set')
|
raise RuntimeError('Ticket ID not set')
|
||||||
|
|
||||||
def add_note(self, prompt=None):
|
def add_note(self, prompt: str = 'Add note') -> list[str]:
|
||||||
"""Add note to be included in osTicket replies."""
|
"""Add note to be included in osTicket replies."""
|
||||||
lines = []
|
lines = []
|
||||||
if not prompt:
|
if not prompt:
|
||||||
|
|
@ -175,18 +176,21 @@ class osTicket():
|
||||||
|
|
||||||
# Get note
|
# Get note
|
||||||
while True:
|
while True:
|
||||||
text = cli.input_text('> ')
|
text = cli.input_text('> ', allow_empty=True)
|
||||||
if not text:
|
if not text:
|
||||||
break
|
break
|
||||||
lines.append(text.strip())
|
lines.append(text.strip())
|
||||||
|
|
||||||
# Save note
|
# Save note
|
||||||
if lines:
|
if lines:
|
||||||
self.note = lines.pop(0)
|
self.note = lines[0]
|
||||||
for line in lines:
|
for line in lines[1:]:
|
||||||
self.note += f'\n...{line}'
|
self.note += f'\n...{line}'
|
||||||
else:
|
else:
|
||||||
self.note = None
|
self.note = ''
|
||||||
|
|
||||||
|
# Done
|
||||||
|
return lines
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
"""Revert to defaults."""
|
"""Revert to defaults."""
|
||||||
|
|
@ -314,6 +318,7 @@ class osTicket():
|
||||||
if cli.ask('Is this correct?'):
|
if cli.ask('Is this correct?'):
|
||||||
self.ticket_id = _id
|
self.ticket_id = _id
|
||||||
self.ticket_name = _name
|
self.ticket_name = _name
|
||||||
|
self.ticket_subject = _subject
|
||||||
|
|
||||||
# Done
|
# Done
|
||||||
self._disconnect()
|
self._disconnect()
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ def fix_layout(
|
||||||
# Calculate constraints
|
# Calculate constraints
|
||||||
avail_horizontal, avail_vertical = get_window_size()
|
avail_horizontal, avail_vertical = get_window_size()
|
||||||
avail_vertical -= layout['Current'].get('height', 0)
|
avail_vertical -= layout['Current'].get('height', 0)
|
||||||
for group in ('Title', 'Info'):
|
for group in ('Title', 'Subtitle', 'Info'):
|
||||||
if not layout[group]['Panes']:
|
if not layout[group]['Panes']:
|
||||||
continue
|
continue
|
||||||
avail_vertical -= layout[group].get('height', 0) + 1
|
avail_vertical -= layout[group].get('height', 0) + 1
|
||||||
|
|
@ -95,7 +95,7 @@ def fix_layout(
|
||||||
)
|
)
|
||||||
for group, data in layout.items():
|
for group, data in layout.items():
|
||||||
num_panes = len(data['Panes'])
|
num_panes = len(data['Panes'])
|
||||||
if num_panes < 2 or group not in ('Title', 'Info'):
|
if num_panes < 2 or group not in ('Title', 'Subtitle', 'Info'):
|
||||||
continue
|
continue
|
||||||
avail_horizontal -= (num_panes - 1)
|
avail_horizontal -= (num_panes - 1)
|
||||||
pane_width, remainder = divmod(avail_horizontal, num_panes)
|
pane_width, remainder = divmod(avail_horizontal, num_panes)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ TMUX_SIDE_WIDTH = 21
|
||||||
TMUX_TITLE_HEIGHT = 2
|
TMUX_TITLE_HEIGHT = 2
|
||||||
TMUX_LAYOUT = { # NOTE: This needs to be in order from top to bottom
|
TMUX_LAYOUT = { # NOTE: This needs to be in order from top to bottom
|
||||||
'Title': {'Panes': [], 'height': TMUX_TITLE_HEIGHT},
|
'Title': {'Panes': [], 'height': TMUX_TITLE_HEIGHT},
|
||||||
|
'Subtitle': {'Panes': [], 'height': TMUX_TITLE_HEIGHT},
|
||||||
'Info': {'Panes': []},
|
'Info': {'Panes': []},
|
||||||
'Current': {'Panes': [environ.get('TMUX_PANE', None)]},
|
'Current': {'Panes': [environ.get('TMUX_PANE', None)]},
|
||||||
'Workers': {'Panes': []},
|
'Workers': {'Panes': []},
|
||||||
|
|
@ -115,6 +116,29 @@ class TUI():
|
||||||
# Add pane
|
# Add pane
|
||||||
self.layout['Title']['Panes'].append(tmux.split_window(**tmux_args))
|
self.layout['Title']['Panes'].append(tmux.split_window(**tmux_args))
|
||||||
|
|
||||||
|
def add_subtitle_pane(self, line1: str, line2: str) -> None:
|
||||||
|
"""Add pane to subtitle row."""
|
||||||
|
lines = [line1, line2]
|
||||||
|
tmux_args = {
|
||||||
|
'behind': True,
|
||||||
|
'lines': TMUX_TITLE_HEIGHT,
|
||||||
|
'target_id': None,
|
||||||
|
'text': '\n'.join(lines),
|
||||||
|
'vertical': True,
|
||||||
|
}
|
||||||
|
if self.layout['Subtitle']['Panes']:
|
||||||
|
tmux_args.update({
|
||||||
|
'behind': False,
|
||||||
|
'percent': 50,
|
||||||
|
'target_id': self.layout['Subtitle']['Panes'][-1],
|
||||||
|
'text': '\n'.join(lines),
|
||||||
|
'vertical': False,
|
||||||
|
})
|
||||||
|
tmux_args.pop('lines')
|
||||||
|
|
||||||
|
# Add pane
|
||||||
|
self.layout['Subtitle']['Panes'].append(tmux.split_window(**tmux_args))
|
||||||
|
|
||||||
def add_worker_pane(
|
def add_worker_pane(
|
||||||
self,
|
self,
|
||||||
lines: int | None = None,
|
lines: int | None = None,
|
||||||
|
|
@ -216,6 +240,12 @@ class TUI():
|
||||||
self.layout['Info']['Panes'].clear()
|
self.layout['Info']['Panes'].clear()
|
||||||
tmux.kill_pane(*panes)
|
tmux.kill_pane(*panes)
|
||||||
|
|
||||||
|
def remove_all_subtitle_panes(self) -> None:
|
||||||
|
"""Remove all subtitle panes and update layout."""
|
||||||
|
panes = self.layout['Subtitle']['Panes'].copy()
|
||||||
|
self.layout['Subtitle']['Panes'].clear()
|
||||||
|
tmux.kill_pane(*panes)
|
||||||
|
|
||||||
def remove_all_worker_panes(self) -> None:
|
def remove_all_worker_panes(self) -> None:
|
||||||
"""Remove all worker panes and update layout."""
|
"""Remove all worker panes and update layout."""
|
||||||
self.layout['Workers'].pop('height', None)
|
self.layout['Workers'].pop('height', None)
|
||||||
|
|
@ -237,6 +267,13 @@ class TUI():
|
||||||
self.layout['Title']['Panes'] = panes[:1]
|
self.layout['Title']['Panes'] = panes[:1]
|
||||||
self.set_title(line1, line2, colors)
|
self.set_title(line1, line2, colors)
|
||||||
|
|
||||||
|
def reset_subtitle_pane(self) -> None:
|
||||||
|
"""Remove all extra subtitle panes and update layout."""
|
||||||
|
panes = self.layout['Subtitle']['Panes'].copy()
|
||||||
|
if len(panes) > 1:
|
||||||
|
tmux.kill_pane(*panes[1:])
|
||||||
|
self.layout['Subtitle']['Panes'] = panes[:1]
|
||||||
|
|
||||||
def set_current_pane_height(self, height: int) -> None:
|
def set_current_pane_height(self, height: int) -> None:
|
||||||
"""Set current pane height and update layout."""
|
"""Set current pane height and update layout."""
|
||||||
self.layout['Current']['height'] = height
|
self.layout['Current']['height'] = height
|
||||||
|
|
@ -318,7 +355,7 @@ def fix_layout(layout, forced: bool = False) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Update "group" panes widths
|
# Update "group" panes widths
|
||||||
for group in ('Title', 'Info'):
|
for group in ('Title', 'Subtitle', 'Info'):
|
||||||
num_panes = len(layout[group]['Panes'])
|
num_panes = len(layout[group]['Panes'])
|
||||||
if num_panes <= 1:
|
if num_panes <= 1:
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue