From 0bcdde0ffb81055cae7bc6bd0d275fe45192c988 Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Sat, 17 Jun 2023 19:53:05 -0700 Subject: [PATCH] Add ticket and note panes to new TUI --- scripts/wk/clone/ddrescue.py | 47 +++++++++++++++++++----------------- scripts/wk/hw/diags.py | 28 ++++++++++++++++----- scripts/wk/osticket.py | 25 +++++++++++-------- scripts/wk/ui/tmux.py | 4 +-- scripts/wk/ui/tui.py | 39 +++++++++++++++++++++++++++++- 5 files changed, 102 insertions(+), 41 deletions(-) diff --git a/scripts/wk/clone/ddrescue.py b/scripts/wk/clone/ddrescue.py index a87fc0ed..6a1bc79e 100644 --- a/scripts/wk/clone/ddrescue.py +++ b/scripts/wk/clone/ddrescue.py @@ -1104,8 +1104,10 @@ class State(): # Write to progress file 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.""" + if not note_lines: + note_lines = [] source_exists = True source_str = '' dest_exists = True @@ -1173,27 +1175,27 @@ class State(): dest_str, ) + # Bail if ticket not selected + if not self.ost: + return + # Ticket Details - # TODO: Fixme - if self.ost and self.ost.ticket_id: - text = ansi.color_string( - [ - self.ost.ticket_name, - ' ' if self.ost.note else '\n', - f'Ticket #{self.ost.ticket_id}', - f'\n{self.ost.note.splitlines()[0]}' if self.ost.note else '', - ], - ['CYAN', None, None, 'YELLOW'], - sep='', + self.ui.reset_subtitle_pane() + if self.ost.ticket_id and not self.ui.layout['Subtitle']['Panes']: + self.ui.add_subtitle_pane( + ansi.color_string( + [f'#{self.ost.ticket_id}', str(self.ost.ticket_name)], + [None, 'CYAN'], + ), + str(self.ost.ticket_subject), ) - if self.panes.get('Ticket', None): - tmux.respawn_pane(self.panes['Ticket'], text=text) - else: - self.panes['Ticket'] = tmux.split_window( - behind=True, - lines=2, - text=text, - vertical=True, + + # Tech note + note_lines = self.ost.note.replace('...', '').splitlines() + if note_lines: + self.ui.add_subtitle_pane( + ansi.color_string('Tech Note', 'YELLOW'), + ' | '.join(note_lines), ) @@ -1910,6 +1912,7 @@ def main() -> None: main_menu.actions[menus.MENU_ACTIONS[2]]['Separator'] = True else: main_menu.actions['Add tech note']['Separator'] = True + state.update_top_panes() try: state.init_recovery(args) except (FileNotFoundError, std.GenericAbort): @@ -1937,10 +1940,10 @@ def main() -> None: # Tech note if 'tech note' in selection[0]: - state.ost.add_note( + note_lines = state.ost.add_note( 'Please enter any additional information about this recovery', ) - state.update_top_panes() + state.update_top_panes(note_lines=note_lines) # Start over if 'Fresh start' in selection[0]: diff --git a/scripts/wk/hw/diags.py b/scripts/wk/hw/diags.py index 4a2ddc96..33808938 100644 --- a/scripts/wk/hw/diags.py +++ b/scripts/wk/hw/diags.py @@ -144,6 +144,7 @@ class State(): # Reset objects self.disks.clear() self.test_groups.clear() + self.ui.remove_all_subtitle_panes() # osTicket self.ost.init() @@ -859,7 +860,12 @@ def print_countdown(proc, seconds) -> None: 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.""" aborted = False 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 if state.ost.ticket_id: - state.top_text += cli.color_string( - [f' #{state.ost.ticket_id}', state.ost.ticket_name], - [None, 'CYAN'], - ) + state.ui.add_subtitle_pane( + cli.color_string( + [f'#{state.ost.ticket_id}', str(state.ost.ticket_name)], + [None, 'CYAN'], + ), + str(state.ost.ticket_subject), + ) # Add note if (state.ost.ticket_id 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 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) # Done + state.ui.remove_all_subtitle_panes() state.save_debug_reports() atexit.unregister(state.save_debug_reports) if quick_mode: diff --git a/scripts/wk/osticket.py b/scripts/wk/osticket.py index a3f43a8f..d01cc312 100644 --- a/scripts/wk/osticket.py +++ b/scripts/wk/osticket.py @@ -38,11 +38,12 @@ class osTicket(): def __init__(self): self.db_connection = None self.db_cursor = None - self.disabled = False - self.errors = False - self.note = None - self.ticket_id = None - self.ticket_name = None + self.disabled: bool = False + self.errors: bool = False + self.note: str = '' + self.ticket_id: int | None = None + self.ticket_name: str | None = None + self.ticket_subject: str | None = None # Ensure connection is closed atexit atexit.register(self._disconnect) @@ -162,7 +163,7 @@ class osTicket(): LOG.error('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.""" lines = [] if not prompt: @@ -175,18 +176,21 @@ class osTicket(): # Get note while True: - text = cli.input_text('> ') + text = cli.input_text('> ', allow_empty=True) if not text: break lines.append(text.strip()) # Save note if lines: - self.note = lines.pop(0) - for line in lines: + self.note = lines[0] + for line in lines[1:]: self.note += f'\n...{line}' else: - self.note = None + self.note = '' + + # Done + return lines def init(self): """Revert to defaults.""" @@ -314,6 +318,7 @@ class osTicket(): if cli.ask('Is this correct?'): self.ticket_id = _id self.ticket_name = _name + self.ticket_subject = _subject # Done self._disconnect() diff --git a/scripts/wk/ui/tmux.py b/scripts/wk/ui/tmux.py index 726f841f..d49dfb34 100644 --- a/scripts/wk/ui/tmux.py +++ b/scripts/wk/ui/tmux.py @@ -67,7 +67,7 @@ def fix_layout( # Calculate constraints avail_horizontal, avail_vertical = get_window_size() avail_vertical -= layout['Current'].get('height', 0) - for group in ('Title', 'Info'): + for group in ('Title', 'Subtitle', 'Info'): if not layout[group]['Panes']: continue avail_vertical -= layout[group].get('height', 0) + 1 @@ -95,7 +95,7 @@ def fix_layout( ) for group, data in layout.items(): 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 avail_horizontal -= (num_panes - 1) pane_width, remainder = divmod(avail_horizontal, num_panes) diff --git a/scripts/wk/ui/tui.py b/scripts/wk/ui/tui.py index 23ab6470..257a7ef3 100644 --- a/scripts/wk/ui/tui.py +++ b/scripts/wk/ui/tui.py @@ -19,6 +19,7 @@ TMUX_SIDE_WIDTH = 21 TMUX_TITLE_HEIGHT = 2 TMUX_LAYOUT = { # NOTE: This needs to be in order from top to bottom 'Title': {'Panes': [], 'height': TMUX_TITLE_HEIGHT}, + 'Subtitle': {'Panes': [], 'height': TMUX_TITLE_HEIGHT}, 'Info': {'Panes': []}, 'Current': {'Panes': [environ.get('TMUX_PANE', None)]}, 'Workers': {'Panes': []}, @@ -115,6 +116,29 @@ class TUI(): # Add pane 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( self, lines: int | None = None, @@ -216,6 +240,12 @@ class TUI(): self.layout['Info']['Panes'].clear() 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: """Remove all worker panes and update layout.""" self.layout['Workers'].pop('height', None) @@ -237,6 +267,13 @@ class TUI(): self.layout['Title']['Panes'] = panes[:1] 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: """Set current pane height and update layout.""" self.layout['Current']['height'] = height @@ -318,7 +355,7 @@ def fix_layout(layout, forced: bool = False) -> None: pass # Update "group" panes widths - for group in ('Title', 'Info'): + for group in ('Title', 'Subtitle', 'Info'): num_panes = len(layout[group]['Panes']) if num_panes <= 1: continue