X-Git-Url: https://code.grnet.gr/git/snf-image-creator/blobdiff_plain/49c07ce389257085642c76aa3318b80076e18bd6..8bae613f329a2e5c6765914e51a7e1a1dc90f983:/image_creator/dialog_wizard.py diff --git a/image_creator/dialog_wizard.py b/image_creator/dialog_wizard.py index f761d99..e5a39f8 100644 --- a/image_creator/dialog_wizard.py +++ b/image_creator/dialog_wizard.py @@ -39,6 +39,7 @@ snf-image-creator. import time import StringIO +import json from image_creator.kamaki_wrapper import Kamaki, ClientError from image_creator.util import MD5, FatalError @@ -47,6 +48,7 @@ from image_creator.dialog_util import extract_image, update_background_title, \ add_cloud, edit_cloud PAGE_WIDTH = 70 +PAGE_HEIGHT = 10 class WizardExit(Exception): @@ -54,8 +56,8 @@ class WizardExit(Exception): pass -class WizardInvalidData(Exception): - """Exception triggered when the user provided data are invalid""" +class WizardReloadPage(Exception): + """Exception that reloads the last WizardPage""" pass @@ -81,20 +83,22 @@ class Wizard: idx = 0 while True: try: - idx += self.pages[idx].run(self.session, idx, len(self.pages)) + total = len(self.pages) + title = "(%d/%d) %s" % (idx + 1, total, self.pages[idx].title) + idx += self.pages[idx].run(self.session, title) except WizardExit: return False - except WizardInvalidData: + except WizardReloadPage: continue if idx >= len(self.pages): - msg = "All necessary information has been gathered:\n\n" + text = "All necessary information has been gathered:\n\n" for page in self.pages: - msg += " * %s\n" % page.info - msg += "\nContinue with the image creation process?" + text += " * %s\n" % page.info + text += "\nContinue with the image creation process?" ret = self.d.yesno( - msg, width=PAGE_WIDTH, height=8 + len(self.pages), + text, width=PAGE_WIDTH, height=8 + len(self.pages), ok_label="Yes", cancel="Back", extra_button=1, extra_label="Quit", title="Confirmation") @@ -114,14 +118,26 @@ class WizardPage(object): NEXT = 1 PREV = -1 - def __init__(self, **kargs): + def __init__(self, name, display_name, text, **kargs): + self.name = name + self.display_name = display_name + self.text = text + + self.title = kargs['title'] if 'title' in kargs else "" + self.default = kargs['default'] if 'default' in kargs else "" + self.extra = kargs['extra'] if 'extra' in kargs else None + self.extra_label = \ + kargs['extra_label'] if 'extra_label' in kargs else 'Extra' + + self.info = "%s: " % self.display_name + validate = kargs['validate'] if 'validate' in kargs else lambda x: x setattr(self, "validate", validate) display = kargs['display'] if 'display' in kargs else lambda x: x setattr(self, "display", display) - def run(self, session, index, total): + def run(self, session, title): """Display this wizard page This function is used by the wizard program when accessing a page. @@ -129,67 +145,134 @@ class WizardPage(object): raise NotImplementedError -class WizardRadioListPage(WizardPage): - """Represent a Radio List in a wizard""" - def __init__(self, name, printable, message, choices, **kargs): - super(WizardRadioListPage, self).__init__(**kargs) - self.name = name - self.printable = printable - self.message = message +class WizardPageWthChoices(WizardPage): + """Represents a Wizard Page that allows the user to select something from + a list of choices. + + The available choices are created by a function passed to the class through + the choices variable. If the choices function returns an empty list, a + fallback funtion is executed if available. + """ + def __init__(self, name, display_name, text, choices, **kargs): + super(WizardPageWthChoices, self).__init__(name, display_name, text, + **kargs) self.choices = choices - self.title = kargs['title'] if 'title' in kargs else '' - self.default = kargs['default'] if 'default' in kargs else "" + self.fallback = kargs['fallback'] if 'fallback' in kargs else None + + +class WizardRadioListPage(WizardPageWthChoices): + """Represent a Radio List in a wizard""" - def run(self, session, index, total): + def run(self, session, title): d = session['dialog'] w = session['wizard'] choices = [] - for i in range(len(self.choices)): - default = 1 if self.choices[i][0] == self.default else 0 - choices.append((self.choices[i][0], self.choices[i][1], default)) + for choice in self.choices(): + default = 1 if choice[0] == self.default else 0 + choices.append((choice[0], choice[1], default)) (code, answer) = d.radiolist( - self.message, height=10, width=PAGE_WIDTH, ok_label="Next", - cancel="Back", choices=choices, - title="(%d/%d) %s" % (index + 1, total, self.title)) + self.text, width=PAGE_WIDTH, ok_label="Next", cancel="Back", + choices=choices, height=PAGE_HEIGHT, title=title) if code in (d.DIALOG_CANCEL, d.DIALOG_ESC): return self.PREV w[self.name] = self.validate(answer) self.default = answer - self.info = "%s: %s" % (self.printable, self.display(w[self.name])) + self.info = "%s: %s" % (self.display_name, self.display(w[self.name])) return self.NEXT class WizardInputPage(WizardPage): """Represents an input field in a wizard""" - def __init__(self, name, printable, message, **kargs): - super(WizardInputPage, self).__init__(**kargs) - self.name = name - self.printable = printable - self.message = message - self.info = "%s: " % self.printable - self.title = kargs['title'] if 'title' in kargs else '' - self.init = kargs['init'] if 'init' in kargs else '' - def run(self, session, index, total): + def run(self, session, title): d = session['dialog'] w = session['wizard'] (code, answer) = d.inputbox( - self.message, init=self.init, width=PAGE_WIDTH, ok_label="Next", - cancel="Back", title="(%d/%d) %s" % (index + 1, total, self.title)) + self.text, init=self.default, width=PAGE_WIDTH, ok_label="Next", + cancel="Back", height=PAGE_HEIGHT, title=title) if code in (d.DIALOG_CANCEL, d.DIALOG_ESC): return self.PREV value = answer.strip() - self.init = value + self.default = value w[self.name] = self.validate(value) - self.info = "%s: %s" % (self.printable, self.display(w[self.name])) + self.info = "%s: %s" % (self.display_name, self.display(w[self.name])) + + return self.NEXT + + +class WizardFormPage(WizardPage): + """Represents a Form in a wizard""" + + def __init__(self, name, display_name, text, fields, **kargs): + super(WizardFormPage, self).__init__(name, display_name, text, **kargs) + self.fields = fields + + def run(self, session, title): + d = session['dialog'] + w = session['wizard'] + + field_lenght = len(self.fields()) + form_height = field_lenght if field_lenght < PAGE_HEIGHT - 4 \ + else PAGET_HEIGHT - 4 + + (code, output) = d.form( + self.text, width=PAGE_WIDTH, height=PAGE_HEIGHT, + form_height=form_height, ok_label="Next", cancel="Back", + fields=self.fields(), title=title) + + if code in (d.DIALOG_CANCEL, d.DIALOG_ESC): + return self.PREV + + w[self.name] = self.validate(output) + self.default = output + self.info = "%s: %s" % (self.display_name, self.display(w[self.name])) + + return self.NEXT + + +class WizardMenuPage(WizardPageWthChoices): + """Represents a menu dialog with available choices in a wizard""" + + def run(self, session, title): + d = session['dialog'] + w = session['wizard'] + + extra_button = 1 if self.extra else 0 + + choices = self.choices() + + if len(choices) == 0: + assert self.fallback, "Zero choices and no fallback" + if self.fallback(): + raise WizardReloadPage + else: + return self.PREV + + default_item = self.default if self.default else choices[0][0] + + (code, choice) = d.menu( + self.text, width=PAGE_WIDTH, ok_label="Next", cancel="Back", + title=title, choices=choices, height=PAGE_HEIGHT, + default_item=default_item, extra_label=self.extra_label, + extra_button=extra_button) + + if code in (d.DIALOG_CANCEL, d.DIALOG_ESC): + return self.PREV + elif code == d.DIALOG_EXTRA: + self.extra() + raise WizardReloadPage + + self.default = choice + w[self.name] = self.validate(choice) + self.info = "%s: %s" % (self.display_name, self.display(w[self.name])) return self.NEXT @@ -197,74 +280,120 @@ class WizardInputPage(WizardPage): def start_wizard(session): """Run the image creation wizard""" - d = session['dialog'] - clouds = Kamaki.get_clouds() - if not len(clouds): - if not add_cloud(session): - return False - else: - while 1: - choices = [] - for (name, cloud) in clouds.items(): - descr = cloud['description'] if 'description' in cloud else '' - choices.append((name, descr)) - - (code, choice) = d.menu( - "In this menu you can select existing cloud account to use " - " or add new ones. Press