interview.py 6.49 KB
Newer Older
Theresa Pollinger's avatar
Theresa Pollinger committed
1
2
#!/usr/bin/env python3

3
# http://cmd2.readthedocs.io
Theresa Pollinger's avatar
Theresa Pollinger committed
4
import cmd2 as cmd
5
# http://mattoc.com/python-yes-no-prompt-cli.html
Theresa Pollinger's avatar
Theresa Pollinger committed
6
from distutils.util import strtobool
7
# https://github.com/phfaist/pylatexenc for directly converting Latex commands to unicode
Theresa Pollinger's avatar
Theresa Pollinger committed
8
9
10
from pylatexenc.latex2text import LatexNodes2Text
import pyparsing as pp

11
from pde_state_machine import *
12
13
14
15

# This "main class" is two things: a REPL loop, by subclassing the cmd2 Cmd class
# and a state machine as given by the pytransitions package
class Interview(cmd.Cmd):
Theresa Pollinger's avatar
Theresa Pollinger committed
16
    def __init__(self, *args, **kwargs):
17
18
19

        self.state_machine = PDE_States(self.poutput, self.update_prompt, self.please_prompt)

Theresa Pollinger's avatar
Theresa Pollinger committed
20
21
22
        # initialize legal characters for cmd
        self.legalChars = u'!#$%.:;?@_-<>' + pp.printables + pp.alphas8bit + pp.punc8bit
        # TODO why does "<" not show?
23
24
        # allow all useful unicode characters to be used, and some more
        for i in range(0x20, 0x2E7F):
Theresa Pollinger's avatar
Theresa Pollinger committed
25
26
27
28
29
30
            self.legalChars += chr(i)

        # call cmd constructor
        super(Interview, self).__init__(*args, **kwargs)

        # Initialize cmd member variables
31
        self.myname = 'TheInterview'
Theresa Pollinger's avatar
Theresa Pollinger committed
32
        self.username = 'user'
33
34
35
36
        self.intro = "Hello, " + self.username + "! I am " + self.myname + \
                     ", your partial differential equations and simulations expert. " \
                     "Let's set up a simulation together.\n" \
                     "Please enter anything to start the interview."
37
        # self.greeting()
Theresa Pollinger's avatar
Theresa Pollinger committed
38
39
        self.update_prompt()

40

41
    #### functions for user interaction
Theresa Pollinger's avatar
Theresa Pollinger committed
42
43

    def obviously_stupid_input(self):
44
        self.poutput("Trying to be funny, huh?")
Theresa Pollinger's avatar
Theresa Pollinger committed
45

46
    ############# input processing if not explain or undo
Theresa Pollinger's avatar
Theresa Pollinger committed
47
48
49
    def default(self, line):
        raw = line.parsed['raw']
        arg = LatexNodes2Text().latex_to_text(raw)
50
        # pythonic switch-case, cf. https://bytebaker.com/2008/11/03/switch-case-statement-in-python/
51

52
53
54
        if not self.keyword_handling(arg):
            if not self.prompt_input_handling(arg):
                self.state_input_handling(arg)
55
56
57
58

    def state_input_handling(self, arg):
        """The standard input handling, depending on which state we are in"""
        # pythonic switch-case, cf. https://bytebaker.com/2008/11/03/switch-case-statement-in-python/
Theresa Pollinger's avatar
Theresa Pollinger committed
59
        try:
60
            self.state_machine.stateDependentInputHandling[self.state_machine.state](arg)
61
        except Exception as error:
62
            #self.state_machine.exaout.create_output(self.state_machine.simdata)
Theresa Pollinger's avatar
Theresa Pollinger committed
63
64
            raise

65
    def please_prompt(self, query, if_yes, if_no=None, pass_other=False):
Theresa Pollinger's avatar
Theresa Pollinger committed
66
        self.poutput(str(query) + " [y/n]? ")
67
68
69
        self.state_machine.prompted = True
        self.state_machine.if_yes = if_yes
        self.state_machine.if_no = if_no
70
        self.state_machine.pass_other = pass_other
71
72
73
74

    def prompt_input_handling(self, arg):
        """ If we asked for a yes-no answer, execute what was specified in please_prompt.
        return true if the input was handled here, and false if not."""
75
        if self.state_machine.prompted:
76
77
78
79
80
81
            if arg == "":
                ret = True
            else:
                try:
                    ret = strtobool(str(arg).strip().lower())
                except ValueError:
82
83
                    if self.state_machine.pass_other:
                        return False
84
                    # or use as input to callback an input processing fcn..?
85
                    self.poutput("Please answer with y/n")
86
                    return True
87
            self.state_machine.prompted = False
88
            if ret:
89
90
                if self.state_machine.if_yes is not None:
                    self.state_machine.if_yes()
91
92
            elif self.state_machine.if_no is not None:
                self.state_machine.if_no()
93
94
95
            return True
        return False

96
97
98
99
100
101
102
103
104
105
106
    def keyword_handling(self, arg):
        """ If keywords for special meta-functions are given,
        executes the corresponding functions and returns true if it did."""
        if arg.startswith("explain"):
            self.state_machine.explain(arg)
            return True
        if arg.startswith("recap"):
            self.state_machine.recap(arg)
            return True
        return False

Theresa Pollinger's avatar
Theresa Pollinger committed
107
    def help_explain(self):
108
109
110
        self.poutput('\n'.join(['explain [expression]',
                                'explain the expression given or the theory currently used',
                                ]))
Theresa Pollinger's avatar
Theresa Pollinger committed
111
112
113
114
115
116
117

    # called when user types 'undo'
    def do_undo(self, expression):
        "Go back to the last question"
        self.trigger('last_state')

    def help_undo(self):
118
119
120
        self.poutput('\n'.join(['undo',
                                'Go back to the last question',
                                ]))
Theresa Pollinger's avatar
Theresa Pollinger committed
121
122

    def update_prompt(self):
123
        self.prompt = "(" + self.state_machine.state + ")"
Theresa Pollinger's avatar
Theresa Pollinger committed
124

125
    # tab completion for empty lines
Theresa Pollinger's avatar
Theresa Pollinger committed
126
127
128
129
130
131
    def completenames(self, text, line, begidx, endidx):
        """Override of cmd2 method which completes command names both for command completion and help."""
        command = text
        if self.case_insensitive:
            command = text.lower()
        if not command:
132
            # define the "default" input for the different states we can be in
Theresa Pollinger's avatar
Theresa Pollinger committed
133
134
135
            self.stateDependentDefaultInput = {
                'dimensions': '1',
                'domain': ['Ω = [ 0 ; 1 ]'],
136
                'unknowns': ['u : Ω → ℝ'],
137
                'parameters': ['f :  ℝ → ℝ = [x: ℝ] x '],  # ['f : Ω → ℝ = [x:Ω] x ⋅ x'],
138
139
140
                'pdes': ['∆u = f(x_1)'],
                'bcs': ['u (0) = 0'],  # ,'u (1) = x_1**2'],
                'sim': ['FD'],
Theresa Pollinger's avatar
Theresa Pollinger committed
141
            }
142
            return self.stateDependentDefaultInput[self.state_machine.state]
Theresa Pollinger's avatar
Theresa Pollinger committed
143
144
145
146
147
148
149
150
151
        else:
            # Call super class method.  Need to do it this way for Python 2 and 3 compatibility
            cmd_completion = cmd.Cmd.completenames(self, command)

            # If we are completing the initial command name and get exactly 1 result and are at end of line, add a space
            if begidx == 0 and len(cmd_completion) == 1 and endidx == len(line):
                cmd_completion[0] += ' '
            return cmd_completion

152
153
154
155
    def greeting(self):  # TODO make work in proper order
        self.poutput(
            "Hello, " + self.username + "! I am " + self.myname + ", your partial differential equations and simulations expert. " \
                                                                  "Let's set up a simulation together.\n")
Theresa Pollinger's avatar
Theresa Pollinger committed
156
157
        self.trigger("greeting_over")

158

Theresa Pollinger's avatar
Theresa Pollinger committed
159
160
if __name__ == '__main__':
    Interview().cmdloop()