def __init__(self, debug=False, storage=None, *args, **kwargs): tk.Tk.__init__(self, *args, **kwargs) self.storageDirectory = storage self.title('Questionnaire Language (QL)') self.buildWidgets() self.parser = Parser(debug=debug) # To keep output in order self.widgets = OrderedDict() self.answers = OrderedDict() self.questions = [] self.branches = []
def setUp(self): self.duplicateQuestions = DuplicateQuestions() self.undefinedQuestions = UndefinedQuestions() self.nonBoolean = NonBooleanTypes() self.nonOperand = NonOperandTypes() self.nonExpressions = NonExpressions() self.parser = Parser() self.checker = TypeChecker() self.checker.register(self.duplicateQuestions) self.checker.register(self.undefinedQuestions) self.checker.register(self.nonBoolean) self.checker.register(self.nonOperand) self.checker.register(self.nonExpressions)
def __init__(self, debug=False, *args, **kwargs): tk.Tk.__init__(self, *args, **kwargs) self.title('Questionnaire Language (QL)') self.parser = Parser(debug=debug) self.buildWidgets() self.currentWidget = None self.answers = {}
class EvaluationTests(unittest.TestCase): def setUp(self): self.parser = Parser() def testTrueExpressions(self): with open(lib.formFilePath("trueExpressions.txt"), "r") as file: parsed = self.parser.parse(file.read()) gui = GUI() gui._buildForm(parsed) # Fire 'change' event gui.onChange() for node in gui.branches: if not node.evaluate(gui.answers): print node print node.evaluate(gui.answers) print gui.answers print "-----" self.assertEqual(True, bool(node.evaluate(gui.answers))) def testFalseExpressions(self): with open(lib.formFilePath("falseExpressions.txt"), "r") as file: parsed = self.parser.parse(file.read()) gui = GUI() gui._buildForm(parsed) # Fire 'change' event gui.onChange() for node in gui.branches: if node.evaluate(gui.answers): print node print node.evaluate(gui.answers) print gui.answers print "-----" self.assertEqual(False, bool(node.evaluate(gui.answers)))
class ParseTests(unittest.TestCase): def setUp(self): self.parser = Parser() def testEmptyFile(self): formText = " \t \r\n \n \n " parsed = self.parser.parse(formText) self.assertEquals(parsed, []) def testEmptyForm(self): formText = "form taxOfficeExample {}" parsed = self.parser.parse(formText) self.assertEquals(parsed.__class__.__name__, "Form") self.assertEquals(len(parsed.block), 0) def testNonClosingForm(self): formText = "form taxOfficeExample {" self.assertRaises(ParseError, self.parser.parse, formText) # Parser does not accept multiple forms in single file def testMultipleForms(self): formText = "form taxOfficeExample {} form taxExampleB {}" self.assertRaises(ParseError, self.parser.parse, formText) def testSimpleForm(self): with open(lib.formFilePath("simple.txt"), "r") as file: parsed = self.parser.parse(file.read()) self.assertEquals(parsed.__class__.__name__, "Form") self.assertEquals(len(parsed.block), 4) file.close() def testSkipComments(self): with open(lib.formFilePath("comments.txt"), "r") as file: parsed = self.parser.parse(file.read()) self.assertEquals(parsed.__class__.__name__, "Form") self.assertEquals(len(parsed.block), 1) file.close() def testTypes(self): with open(lib.formFilePath("types.txt"), "r") as file: parsed = self.parser.parse(file.read()) self.assertEquals(parsed.__class__.__name__, "Form") self.assertEquals(len(parsed.block), 5) file.close() def testUndefinedType(self): with open(lib.formFilePath("undefinedType.txt"), "r") as file: self.assertRaises(ParseError, self.parser.parse, file.read()) file.close() def testIfBlocks(self): with open(lib.formFilePath("simpleIf.txt"), "r") as file: parsed = self.parser.parse(file.read()) self.assertEquals(parsed.__class__.__name__, "Form") self.assertEquals(len(parsed.block), 2) file.close() def testIfElse(self): with open(lib.formFilePath("ifElse.txt"), "r") as file: parsed = self.parser.parse(file.read()) self.assertEquals(parsed.__class__.__name__, "Form") self.assertEquals(len(parsed.block), 1) file.close() def testNestedIfBlocks(self): with open(lib.formFilePath("nestedIf.txt"), "r") as file: parsed = self.parser.parse(file.read()) self.assertEquals(parsed.__class__.__name__, "Form") self.assertEquals(len(parsed.block), 1) self.assertEquals(parsed.block[0].__class__.__name__, "Branch") self.assertEquals(len(parsed.block[0].ifChildren), 2) file.close() def testExpressions(self): with open(lib.formFilePath("expressions.txt"), "r") as file: parsed = self.parser.parse(file.read()) self.assertEquals(parsed.__class__.__name__, "Form") self.assertEquals(len(parsed.block), 13) file.close()
def setUp(self): self.parser = Parser()
class TypecheckingTests(unittest.TestCase): def setUp(self): self.duplicateQuestions = DuplicateQuestions() self.undefinedQuestions = UndefinedQuestions() self.nonBoolean = NonBooleanTypes() self.nonOperand = NonOperandTypes() self.nonExpressions = NonExpressions() self.parser = Parser() self.checker = TypeChecker() self.checker.register(self.duplicateQuestions) self.checker.register(self.undefinedQuestions) self.checker.register(self.nonBoolean) self.checker.register(self.nonOperand) self.checker.register(self.nonExpressions) def testDuplicateQuestions(self): with open(lib.formFilePath("duplicateQuestions.txt"), "r") as file: parsed = self.parser.parse(file.read()) self.checker.checkAST(parsed) self.assertEqual(len(self.duplicateQuestions.errors), 1) self.assertEqual(len(self.undefinedQuestions.errors), 0) def testDuplicateQuestionsNested(self): with open(lib.formFilePath("duplicateQuestionsNested.txt"), "r") as file: parsed = self.parser.parse(file.read()) self.checker.checkAST(parsed) self.assertEqual(len(self.duplicateQuestions.errors), 1) def testUndefinedQuestions(self): with open(lib.formFilePath("undefinedQuestions.txt"), "r") as file: parsed = self.parser.parse(file.read()) self.checker.checkAST(parsed) self.assertEqual(len(self.duplicateQuestions.errors), 0) self.assertEqual(len(self.undefinedQuestions.errors), 1) def testUndefinedQuestionsNested(self): with open(lib.formFilePath("undefinedQuestionsNested.txt"), "r") as file: parsed = self.parser.parse(file.read()) self.checker.checkAST(parsed) self.assertEqual(len(self.duplicateQuestions.errors), 0) self.assertEqual(len(self.undefinedQuestions.errors), 1) def testIncompatibleTypes(self): with open(lib.formFilePath("expressionTypes.txt"), "r") as file: parsed = self.parser.parse(file.read()) self.checker.checkAST(parsed) self.assertEqual(len(self.duplicateQuestions.errors), 0) self.assertEqual(len(self.undefinedQuestions.errors), 0) self.assertEqual(len(self.nonExpressions.errors), 1) def testIncompatibleNestedTypes(self): with open(lib.formFilePath("expressionTypesNested.txt"), "r") as file: parsed = self.parser.parse(file.read()) self.checker.checkAST(parsed) self.assertEqual(len(self.duplicateQuestions.errors), 0) self.assertEqual(len(self.undefinedQuestions.errors), 0) self.assertEqual(len(self.nonExpressions.errors), 4)
class GUI(tk.Tk): def __init__(self, debug=False, storage=None, *args, **kwargs): tk.Tk.__init__(self, *args, **kwargs) self.storageDirectory = storage self.title('Questionnaire Language (QL)') self.buildWidgets() self.parser = Parser(debug=debug) # To keep output in order self.widgets = OrderedDict() self.answers = OrderedDict() self.questions = [] self.branches = [] def run(self): self.mainloop() def buildWidgets(self): self.frame = tk.Frame() self.frame.pack( side="top", fill="both", expand=True, pady=10, padx=10 ) self.buttonBarRight = tk.Frame(padx=10, pady=10) self.buttonBarRight.pack(side="right", anchor="e") self.buttonBarLeft = tk.Frame(pady=10) self.buttonBarLeft.pack(side="left", anchor="w") self.fileSelect = myTk.FileDialog( master=self.frame, onFileSelected=self._parseContents ) self.saveButton = tk.Button( self.buttonBarRight, text="Save form", command=self._save, bg='#CBE86B', cursor="hand2" ) self.resetButton = tk.Button( self.buttonBarLeft, text="Reset", command=self._reset ) self.cancelButton = tk.Button( self.buttonBarLeft, text="Cancel", command=self._cancel ) def _parseContents(self, contents=""): self._parsed = self.parser.parse(contents) if self.parser.hasErrors: errors = self.parser.getErrorMessages() tkMessageBox.showerror( "Parsing file", "\n\n".join(errors) ) return None self._typeChecking(self._parsed) def _typeChecking(self, parsed): checker = QLTypeChecker(parsed) if checker.hasErrors: errors = checker.getErrorMessages() tkMessageBox.showerror( "Typechecking", "\n\n".join(errors) ) return None self._buildForm(parsed) def _buildForm(self, parsed): self.widgets = OrderedDict() self.answers = OrderedDict() self.questions = parsed.findAll("Question") self.branches = parsed.findAll("Branch") self.fileSelect.hide() self.cancelButton.pack(side="left", padx=5) self.resetButton.pack(side="right") self.saveButton.pack(side="right") rowCount = 0 for node in self.questions: try: nodeWidget = getattr(Widgets, node.widgetName())(self.frame, node, self.onChange, rowCount) self.widgets[node] = nodeWidget rowCount += nodeWidget.rowCount except Exception as err: print err # Display top-level questions for node in parsed.findChildren("Question"): self.widgets[node].show() self.onChange() def onChange(self, event=None): for node, widget in self.widgets.iteritems(): widget.setValid() if widget.visible: self.answers[node] = widget.value() # Compute & display readOnly values for widget in self.widgets.values(): if widget.isReadOnly(): widget.setValue(self.answers) # Evaluate branches and display children-widgets accordingly for node in self.branches: if node.evaluate(self.answers): toShow = node.ifChildren toHide = node.elseChildren else: toShow = node.elseChildren toHide = node.ifChildren for child in toShow: if child in self.widgets: self.widgets[child].show() for child in toHide: if child in self.widgets: self.widgets[child].hide() def _save(self): userId = uuid.uuid4() userFile = "%s/%s.txt" % (self.storageDirectory, userId) with open(userFile,'w') as f: for question, answer in self.answers.iteritems(): f.write(question.labelText()) f.write("\n") f.write("Answer: %s" % answer) f.write("\n-------------------\n") f.close() self._cancel() def _cancel(self): # Remove current widgets for node, widget in self.widgets.iteritems(): widget.tearDown() self.saveButton.pack_forget() self.resetButton.pack_forget() self.cancelButton.pack_forget() self.fileSelect.show() def _reset(self): # Remove current form widgets for node, widget in self.widgets.iteritems(): widget.tearDown() # Rebuild form self._buildForm(self._parsed)
class ParseTests(unittest.TestCase): def setUp(self): self.parser = Parser() def testEmptyFile(self): formText = " \t \r\n \n \n " parsed = self.parser.parse(formText) self.assertEquals(parsed, None) def testEmptyForm(self): formText = "form taxOfficeExample {}" parsed = self.parser.parse(formText) self.assertEquals(parsed.__class__.__name__, "Form") self.assertEquals(len(parsed.block), 0) def testNonClosingForm(self): formText = "form taxOfficeExample {" parsed = self.parser.parse(formText) self.assertEquals(True, self.parser.hasErrors) # Parser does not accept multiple forms in single file def testMultipleForms(self): formText = "form taxOfficeExample {} form taxExampleB {}" parsed = self.parser.parse(formText) self.assertEquals(True, self.parser.hasErrors) def testSimpleForm(self): with open(lib.formFilePath("simple.txt"), "r") as file: parsed = self.parser.parse(file.read()) self.assertEquals(parsed.__class__.__name__, "Form") self.assertEquals(len(parsed.block), 4) file.close() def testSkipComments(self): with open(lib.formFilePath("comments.txt"), "r") as file: parsed = self.parser.parse(file.read()) self.assertEquals(parsed.__class__.__name__, "Form") self.assertEquals(len(parsed.block), 1) file.close() def testTypes(self): with open(lib.formFilePath("types.txt"), "r") as file: parsed = self.parser.parse(file.read()) self.assertEquals(parsed.__class__.__name__, "Form") self.assertEquals(len(parsed.block), 5) file.close() def testUndefinedType(self): with open(lib.formFilePath("undefinedType.txt"), "r") as file: parsed = self.parser.parse(file.read()) self.assertEquals(True, self.parser.hasErrors) file.close() def testIfBlocks(self): with open(lib.formFilePath("simpleIf.txt"), "r") as file: parsed = self.parser.parse(file.read()) self.assertEquals(parsed.__class__.__name__, "Form") self.assertEquals(len(parsed.block), 2) file.close() def testIfElse(self): with open(lib.formFilePath("ifElse.txt"), "r") as file: parsed = self.parser.parse(file.read()) self.assertEquals(parsed.__class__.__name__, "Form") self.assertEquals(len(parsed.block), 1) file.close() def testNestedIfBlocks(self): with open(lib.formFilePath("nestedIf.txt"), "r") as file: parsed = self.parser.parse(file.read()) self.assertEquals(parsed.__class__.__name__, "Form") self.assertEquals(len(parsed.block), 1) self.assertEquals(parsed.block[0].__class__.__name__, "Branch") self.assertEquals(len(parsed.block[0].ifChildren), 2) file.close() def testExpressions(self): with open(lib.formFilePath("expressions.txt"), "r") as file: parsed = self.parser.parse(file.read()) self.assertEquals(parsed.__class__.__name__, "Form") self.assertEquals(len(parsed.block), 13) file.close()
class GUI(tk.Tk): def __init__(self, debug=False, storage=None, *args, **kwargs): tk.Tk.__init__(self, *args, **kwargs) self.storageDirectory = storage self.title('Questionnaire Language (QL)') self.buildWidgets() self.parser = Parser(debug=debug) # To keep output in order self.widgets = OrderedDict() self.answers = OrderedDict() self.questions = [] self.branches = [] def run(self): self.mainloop() def buildWidgets(self): self.frame = tk.Frame() self.frame.pack(side="top", fill="both", expand=True, pady=10, padx=10) self.buttonBarRight = tk.Frame(padx=10, pady=10) self.buttonBarRight.pack(side="right", anchor="e") self.buttonBarLeft = tk.Frame(pady=10) self.buttonBarLeft.pack(side="left", anchor="w") self.fileSelect = myTk.FileDialog(master=self.frame, onFileSelected=self._parseContents) self.saveButton = tk.Button(self.buttonBarRight, text="Save form", command=self._save, bg='#CBE86B', cursor="hand2") self.resetButton = tk.Button(self.buttonBarLeft, text="Reset", command=self._reset) self.cancelButton = tk.Button(self.buttonBarLeft, text="Cancel", command=self._cancel) def _parseContents(self, contents=""): self._parsed = self.parser.parse(contents) if self.parser.hasErrors: errors = self.parser.getErrorMessages() tkMessageBox.showerror("Parsing file", "\n\n".join(errors)) return None self._typeChecking(self._parsed) def _typeChecking(self, parsed): checker = QLTypeChecker(parsed) if checker.hasErrors: errors = checker.getErrorMessages() tkMessageBox.showerror("Typechecking", "\n\n".join(errors)) return None self._buildForm(parsed) def _buildForm(self, parsed): self.widgets = OrderedDict() self.answers = OrderedDict() self.questions = parsed.findAll("Question") self.branches = parsed.findAll("Branch") self.fileSelect.hide() self.cancelButton.pack(side="left", padx=5) self.resetButton.pack(side="right") self.saveButton.pack(side="right") rowCount = 0 for node in self.questions: try: nodeWidget = getattr(Widgets, node.widgetName())(self.frame, node, self.onChange, rowCount) self.widgets[node] = nodeWidget rowCount += nodeWidget.rowCount except Exception as err: print err # Display top-level questions for node in parsed.findChildren("Question"): self.widgets[node].show() self.onChange() def onChange(self, event=None): for node, widget in self.widgets.iteritems(): widget.setValid() if widget.visible: self.answers[node] = widget.value() # Compute & display readOnly values for widget in self.widgets.values(): if widget.isReadOnly(): widget.setValue(self.answers) # Evaluate branches and display children-widgets accordingly for node in self.branches: if node.evaluate(self.answers): toShow = node.ifChildren toHide = node.elseChildren else: toShow = node.elseChildren toHide = node.ifChildren for child in toShow: if child in self.widgets: self.widgets[child].show() for child in toHide: if child in self.widgets: self.widgets[child].hide() def _save(self): userId = uuid.uuid4() userFile = "%s/%s.txt" % (self.storageDirectory, userId) with open(userFile, 'w') as f: for question, answer in self.answers.iteritems(): f.write(question.labelText()) f.write("\n") f.write("Answer: %s" % answer) f.write("\n-------------------\n") f.close() self._cancel() def _cancel(self): # Remove current widgets for node, widget in self.widgets.iteritems(): widget.tearDown() self.saveButton.pack_forget() self.resetButton.pack_forget() self.cancelButton.pack_forget() self.fileSelect.show() def _reset(self): # Remove current form widgets for node, widget in self.widgets.iteritems(): widget.tearDown() # Rebuild form self._buildForm(self._parsed)
class GUI(tk.Tk): def __init__(self, debug=False, *args, **kwargs): tk.Tk.__init__(self, *args, **kwargs) self.title('Questionnaire Language (QL)') self.parser = Parser(debug=debug) self.buildWidgets() self.currentWidget = None self.answers = {} def run(self): self.mainloop() def buildWidgets(self): self.frame = tk.Frame() self.frame.pack(side="top", fill="both", expand=True) self.frame.grid_columnconfigure(0, weight=1) self.fileSelect = tk.Button(text="Select file", command=self.loadFile) self.fileSelect.pack() def loadFile(self): try: with askopenfile(mode='r') as file: self._parseContents(file.read()) file.close() except AttributeError: # User cancels file selection pass except Exception as p: print p def _parseContents(self, contents="", callback=None): try: parsed = self.parser.parse(contents) except Exception as parseError: return tkMessageBox.showerror( "Parsing file", "Cannot parse file `%s`\n\n%s" % (self.fileName, parseError) ) self._typeCheck(parsed) def _typeCheck(self, parsed): checker = QLTypeChecker(parsed) if checker.hasErrors: errors = checker.getErrorMessages() tkMessageBox.showerror( "Typechecking", "\n\n".join(errors) ) else: self._buildForm(parsed) def _buildForm(self, parsed): # Hide file select self.fileSelect.pack_forget() # Display buttons for progress self.prevButton = tk.Button(text="Back", command=self._prev) self.prevButton.pack() self.nextButton = tk.Button(text="Next question", command=self._next) self.nextButton.pack() # Start initial question self.builder = FormBuilder(self.frame, parsed) self._next() def _prev(self): widget = self.builder.prevQuestion() self._displayQuestion(widget) def _next(self): if self.currentWidget: val = self.currentWidget.value() self.answers.update(val) self.builder.availableAnswers(self.answers) try: widget = self.builder.nextQuestion() self._displayQuestion(widget) except ValueError as err: tkMessageBox.showerror( "Input error", "Your given answer is not a number" ) def _displayQuestion(self, questionWidget=None): # Remove "previous" visible question if self.currentWidget: self.currentWidget.tearDown() if questionWidget: self.currentWidget = questionWidget else: self._saveAnswers() self._reset() def _saveAnswers(self): if self.answers: ID = uuid.uuid1() # TODO Store/persist answers in database # i.e. start state of file-select def _reset(self): self.fileSelect.pack() self.prevButton.pack_forget() self.nextButton.pack_forget()