implement GetUser in TUI
This commit is contained in:
parent
af73dd713d
commit
d3c98e418a
|
@ -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()
|
||||
|
||||
# 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():
|
||||
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._add_text('The operation was successfully performed.', center=True)
|
||||
|
||||
def _resultview_on_load(self):
|
||||
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)
|
||||
# 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')
|
||||
|
||||
def _show_msg(self, text: str = '\n', center=False):
|
||||
for line in text.splitlines():
|
||||
align = '^' if center else '<'
|
||||
self._summary_layout.add_widget(Label(line, align=align), 1)
|
||||
|
||||
# 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)
|
||||
|
||||
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.show_result(resp)
|
||||
finally:
|
||||
self.fix()
|
||||
|
|
|
@ -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…
Reference in New Issue