interview.py 6.6 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
# https://github.com/pytransitions/transitions
Theresa Pollinger's avatar
Theresa Pollinger committed
6
7
from transitions import Machine, State
from collections import OrderedDict
8
9
# strings:
# http://mattoc.com/python-yes-no-prompt-cli.html
Theresa Pollinger's avatar
Theresa Pollinger committed
10
11
from distutils.util import strtobool
from pathlib import Path
12
# https://github.com/phfaist/pylatexenc for directly converting Latex commands to unicode
Theresa Pollinger's avatar
Theresa Pollinger committed
13
14
15
16
from pylatexenc.latex2text import LatexNodes2Text
import pyparsing as pp
import re

17
from pde_state_machine import *
18
from string_handling import *
Theresa Pollinger's avatar
Theresa Pollinger committed
19

20
21
22
23

# 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
24
    def __init__(self, *args, **kwargs):
25
26
27

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

Theresa Pollinger's avatar
Theresa Pollinger committed
28
29
30
        # initialize legal characters for cmd
        self.legalChars = u'!#$%.:;?@_-<>' + pp.printables + pp.alphas8bit + pp.punc8bit
        # TODO why does "<" not show?
31
32
        # allow all useful unicode characters to be used, and some more
        for i in range(0x20, 0x2E7F):
Theresa Pollinger's avatar
Theresa Pollinger committed
33
34
35
36
37
38
            self.legalChars += chr(i)

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

        # Initialize cmd member variables
39
        self.myname = 'TheInterview'
Theresa Pollinger's avatar
Theresa Pollinger committed
40
41
        self.username = 'user'
        self.intro = "Hello, " + self.username + "! I am " + self.myname + ", your partial differential equations and simulations expert. " \
42
                                                                           "Let's set up a simulation together.\n" \
43
                                                                           "How many dimensions does your model have?"
44
        # self.greeting()
Theresa Pollinger's avatar
Theresa Pollinger committed
45
46
        self.update_prompt()

47
        self.state_machine.prompted = False
Theresa Pollinger's avatar
Theresa Pollinger committed
48

49

50
    #### functions for user interaction
Theresa Pollinger's avatar
Theresa Pollinger committed
51
52

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

55
    ############# input processing if not explain or undo
Theresa Pollinger's avatar
Theresa Pollinger committed
56
57
58
    def default(self, line):
        raw = line.parsed['raw']
        arg = LatexNodes2Text().latex_to_text(raw)
59
        # pythonic switch-case, cf. https://bytebaker.com/2008/11/03/switch-case-statement-in-python/
60
61
62
63
64
65
66

        if not self.prompt_input_handling(arg):
            self.state_input_handling(arg)

    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
67
        try:
68
            self.state_machine.stateDependentInputHandling[self.state_machine.state](arg)
69
        except Exception as error:
70
            #self.state_machine.exaout.create_output(self.state_machine.simdata)
Theresa Pollinger's avatar
Theresa Pollinger committed
71
72
            raise

73
    def please_prompt(self, query, if_yes, if_no=None):
Theresa Pollinger's avatar
Theresa Pollinger committed
74
        self.poutput(str(query) + " [y/n]? ")
75
76
77
        self.state_machine.prompted = True
        self.state_machine.if_yes = if_yes
        self.state_machine.if_no = if_no
78
79
80
81

    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."""
82
        if self.state_machine.prompted:
83
84
85
86
87
88
89
90
91
92
            if arg == "":
                self.poutput("Yes")
                ret = True
            else:
                try:
                    ret = strtobool(str(arg).strip().lower())
                except ValueError:
                    # or use as input to callback an input processing fcn..?
                    self.poutput("Please answer with Y/n")
                    return True
93
            self.state_machine.prompted = False
94
            if ret:
95
96
97
                self.state_machine.if_yes()
            elif self.state_machine.if_no is not None:
                self.state_machine.if_no()
98
99
100
101
102
            else:
                return False
            return True
        return False

Theresa Pollinger's avatar
Theresa Pollinger committed
103
104
105
106
    # called when user types 'explain [expression]'
    def do_explain(self, expression):
        "Explain an expression or the theoretical background to what we are currently looking for"
        if expression:
107
            explanation = "hello, " + expression  # TODO query flexiformal content through mmt
Theresa Pollinger's avatar
Theresa Pollinger committed
108
109
110
111
112
        else:
            explanation = 'hello'
        self.poutput(explanation)

    def help_explain(self):
113
114
115
        self.poutput('\n'.join(['explain [expression]',
                                'explain the expression given or the theory currently used',
                                ]))
Theresa Pollinger's avatar
Theresa Pollinger committed
116
117
118
119
120
121
122

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

    def help_undo(self):
123
124
125
        self.poutput('\n'.join(['undo',
                                'Go back to the last question',
                                ]))
Theresa Pollinger's avatar
Theresa Pollinger committed
126
127

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

130
    # tab completion for empty lines
Theresa Pollinger's avatar
Theresa Pollinger committed
131
132
133
134
135
136
    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:
137
            # define the "default" input for the different states we can be in
Theresa Pollinger's avatar
Theresa Pollinger committed
138
139
140
            self.stateDependentDefaultInput = {
                'dimensions': '1',
                'domain': ['Ω = [ 0 ; 1 ]'],
141
                'unknowns': ['u : Ω → ℝ'],
142
                'parameters': ['f :  ℝ → ℝ = [x: ℝ] x '],  # ['f : Ω → ℝ = [x:Ω] x ⋅ x'],
143
144
145
                'pdes': ['∆u = f(x_1)'],
                'bcs': ['u (0) = 0'],  # ,'u (1) = x_1**2'],
                'sim': ['FD'],
Theresa Pollinger's avatar
Theresa Pollinger committed
146
            }
147
            return self.stateDependentDefaultInput[self.state_machine.state]
Theresa Pollinger's avatar
Theresa Pollinger committed
148
149
150
151
152
153
154
155
156
        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

157
158
159
160
    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
161
162
        self.trigger("greeting_over")

163

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