implement GetUser in TUI

pull/20/head
Max Erenberg 1 year ago
parent af73dd713d
commit d3c98e418a
  1. 33
      ceo/tui/CeoFrame.py
  2. 3
      ceo/tui/ConfirmView.py
  3. 1
      ceo/tui/ErrorView.py
  4. 12
      ceo/tui/Model.py
  5. 55
      ceo/tui/ResultView.py
  6. 1
      ceo/tui/TransactionView.py
  7. 11
      ceo/tui/members/GetUserResultView.py
  8. 29
      ceo/tui/members/GetUserView.py
  9. 2
      ceo/tui/members/RenewUserView.py
  10. 11
      ceo/tui/start.py

@ -13,6 +13,7 @@ class CeoFrame(Frame):
on_load=None,
title=None,
save_data=False, # whether to save widget state for resizing
has_dynamic_layouts=False, # whether layouts are created on load
):
super().__init__(
screen,
@ -28,6 +29,7 @@ class CeoFrame(Frame):
self._model = model
self._name = name
self._loaded = False
self._has_dynamic_layouts = has_dynamic_layouts
def _ceoframe_on_load(self):
# We usually don't want _on_load() to be called multiple times
@ -44,14 +46,39 @@ class CeoFrame(Frame):
if self._extra_on_load is not None:
self._extra_on_load()
def _on_unload(self):
def _ceoframe_on_unload(self):
"""
This should be called just after the screen gets resized,
but before the new scenes are constructed.
The idea is to save the user's data in the input fields
so that we can restore them in the new scenes.
"""
if not self._save_data:
return
# save the input fields' values so that they don't disappear when
# the window gets resized
self.save()
self._model.viewdata[self._name] = self.data
def _ceoframe_on_reset(self):
"""
This needs to be called whenever we return to the home screen
after some kind of operation was completed.
Currently this is called from Model.reset().
"""
# We want a fresh slate once we return to the home screen, so we
# want on_load() to be called for the scenes.
self._loaded = False
if self._has_dynamic_layouts:
# We don't want layouts to accumulate.
self.clear_layouts()
def clear_layouts(self):
# OK so this a *really* bad thing to do, since we're reaching
# into the private variables of a third-party library.
# Unfortunately asciimatics doesn't allow us to clear the layouts
# of an existing frame, and we need this to be able to re-use
# frames which create layouts dynamically.
self._layouts.clear()
def add_buttons(
self, back_btn=False, back_btn_text='Back',
next_scene=None, next_btn_text='Next', on_next=None,

@ -8,6 +8,7 @@ class ConfirmView(CeoFrame):
super().__init__(
screen, height, width, model, 'Confirm',
on_load=self._confirmview_on_load, title='Confirmation',
has_dynamic_layouts=True,
)
def _add_line(self, text: str = ''):
@ -48,7 +49,7 @@ class ConfirmView(CeoFrame):
def _next(self):
self.flash_message('Sending request...', force_update=True)
try:
self._model.deferred_req_resp = self._model.deferred_req()
self._model.resp = self._model.deferred_req()
finally:
self.clear_flash_message()
self.go_to_next_scene('Result')

@ -9,6 +9,7 @@ class ErrorView(CeoFrame):
super().__init__(
screen, height, width, model, 'Error',
on_load=self._errorview_on_load, title='Error',
has_dynamic_layouts=True,
)
def _errorview_on_load(self):

@ -6,6 +6,7 @@ class Model:
def __init__(self):
self.screen = None
self.views = []
self.title = None
self.scene_stack = []
self.error_message = None
@ -28,7 +29,9 @@ class Model:
'labels': {},
'status': 'not started',
},
'Result': {},
'GetUser': {
'uid': '',
},
}
self.viewdata = deepcopy(self._initial_viewdata)
# data which is shared between multiple views
@ -36,7 +39,7 @@ class Model:
self.confirm_lines = None
self.operations = None
self.deferred_req = None
self.deferred_req_resp = None
self.resp = None
def reset(self):
self.viewdata = deepcopy(self._initial_viewdata)
@ -44,7 +47,10 @@ class Model:
self.confirm_lines = None
self.operations = None
self.deferred_req = None
self.deferred_req_resp = None
self.resp = None
self.title = None
self.error_message = None
self.scene_stack.clear()
for view in self.views:
if hasattr(view, '_ceoframe_on_reset'):
view._ceoframe_on_reset()

@ -10,33 +10,46 @@ class ResultView(CeoFrame):
super().__init__(
screen, height, width, model, 'Result',
on_load=self._resultview_on_load, title='Result',
has_dynamic_layouts=True,
)
self._summary_layout = Layout([1, 10], fill_frame=True)
self.add_layout(self._summary_layout)
self._show_msg()
self.add_buttons(on_next_excl=self._next)
def _next(self):
self._model.reset()
raise NextScene('Welcome')
def _show_msg(self, text: str = '\n', center=False):
# TODO: deduplicate this from ConfirmView
def _add_text(self, text: str = '\n', center: bool = False):
if center:
layout = Layout([100])
align = '^'
col = 0
else:
layout = Layout([1, 10])
align = '<'
col = 1
self.add_layout(layout)
for line in text.splitlines():
align = '^' if center else '<'
self._summary_layout.add_widget(Label(line, align=align), 1)
layout.add_widget(Label(line, align=align), col)
def _add_pair(self, key: str, val: str):
layout = Layout([10, 1, 10])
self.add_layout(layout)
layout.add_widget(Label(key + ':', align='>'), 0)
layout.add_widget(Label(val, align='<'), 2)
# override this method in child classes if desired
def show_result(self, resp: requests.Response):
self._show_msg('The operation was successfully performed.', center=True)
self._add_text('The operation was successfully performed.', center=True)
def _resultview_on_load(self):
resp = self._model.deferred_req_resp
try:
if resp.status_code != 200:
self._show_msg('An error occurred:')
self._show_msg(resp.text.rstrip())
return
self._add_text()
resp = self._model.resp
if resp.status_code != 200:
self._add_text('An error occurred:')
self._add_text(resp.text.rstrip())
else:
self.show_result(resp)
finally:
self.fix()
# fill the rest of the space
self.add_layout(Layout([100], fill_frame=True))
self.add_buttons(on_next_excl=self._next)
self.fix()
def _next(self):
self._model.reset()
raise NextScene('Welcome')

@ -14,6 +14,7 @@ class TransactionView(CeoFrame):
super().__init__(
screen, height, width, model, 'Transaction',
on_load=self._txnview_on_load, title='Running Transaction',
has_dynamic_layouts=True,
)
self._model = model
# map operation names to label widgets

@ -0,0 +1,11 @@
import requests
from ...utils import user_dict_kv
from ..ResultView import ResultView
class GetUserResultView(ResultView):
def show_result(self, resp: requests.Response):
pairs = user_dict_kv(resp.json())
for key, val in pairs:
self._add_pair(key, val)

@ -0,0 +1,29 @@
from asciimatics.widgets import Layout, Text
from ...utils import http_get
from ..CeoFrame import CeoFrame
class GetUserView(CeoFrame):
def __init__(self, screen, width, height, model):
super().__init__(
screen, height, width, model, 'GetUser',
save_data=True,
)
layout = Layout([100], fill_frame=True)
self.add_layout(layout)
self._username = Text("Username:", "uid")
layout.add_widget(self._username)
self.add_flash_message_layout()
self.add_buttons(
back_btn=True,
next_scene='GetUserResult', on_next=self._next)
self.fix()
def _next(self):
uid = self._username.value
self.flash_message('Looking up user...', force_update=True)
try:
self._model.resp = http_get(f'/api/members/{uid}')
finally:
self.clear_flash_message()

@ -1,4 +1,4 @@
from asciimatics.widgets import Layout, Text, Label
from asciimatics.widgets import Layout, Text
from ...term_utils import get_terms_for_renewal
from ...utils import http_post, defer

@ -12,6 +12,8 @@ from .ResultView import ResultView
from .TransactionView import TransactionView
from .WelcomeView import WelcomeView
from .members.AddUserView import AddUserView
from .members.GetUserView import GetUserView
from .members.GetUserResultView import GetUserResultView
from .members.RenewUserView import RenewUserView
@ -29,11 +31,10 @@ views = []
def screen_wrapper(screen, last_scene, model):
global views
model.screen = screen
# unload the old views
for name, view in views:
if hasattr(view, '_on_unload'):
view._on_unload()
if hasattr(view, '_on_ceoframe_unload'):
view._on_ceoframe_unload()
width = min(screen.width, 90)
height = min(screen.height, 24)
views = [
@ -44,10 +45,14 @@ def screen_wrapper(screen, last_scene, model):
('Error', ErrorView(screen, width, height, model)),
('AddUser', AddUserView(screen, width, height, model)),
('RenewUser', RenewUserView(screen, width, height, model)),
('GetUser', GetUserView(screen, width, height, model)),
('GetUserResult', GetUserResultView(screen, width, height, model)),
]
scenes = [
Scene([view], -1, name=name) for name, view in views
]
model.screen = screen
model.views = [view for name, view in views]
screen.play(
scenes, stop_on_resize=True, start_scene=last_scene, allow_int=True,
unhandled_input=unhandled)

Loading…
Cancel
Save