Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/tutorius/tutorial.py
blob: a9c0cab18fd3aa3d82eee8e001f8cae2825e2e57 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421

class Tutorial(object):
    """ This class replaces the previous Tutorial class and 
        allows manipulation of the abstract representation
        of a tutorial as a state machine
    """

    _INIT = "INIT"
    _END = "END"

    def __init__(self, name, state_dict=None):
        """
        The constructor for the Tutorial. By default, the tutorial contains
        only an initial state and an end state.
        The initial state doesn't contain any action or transition. 
        The end state doesn't contain any action either. 

        If state_dict is provided, a valid initial state and an end state
        must be provided.

        @param name The name of the tutorial
        @param state_dict optional, a valid dictionary of states
        """
        self.name = name
        
        # We will use an adjacency list representation through the
        # usage of state objects because our graph representation
        # is really sparse an mostly linear, for a brief
        # example of graph programming in python see:
        # http://www.python.org/doc/essays/graphs
        self._state_dict = state_dict or \
             {Tutorial._INIT:State(name=Tutorial._INIT),\
              Tutorial._END:State(name=Tutorial._END)}
         
        # Minimally check for the presence of an INIT and an END
        # state
        if not self._state_dict.has_key(Tutorial._INIT):
            raise Exception("No INIT state found in state_dict")

        if not self._state_dict.has_key(Tutorial._END):
            raise Exception("No END state found in state_dict")
        
        self.validate() 

        # Initialize variables for generating unique names
        # TODO: We should take the max number from the
        #       existing state names
        self._state_name_nb = 0
        

    def add_state(self, action_list=[], transition_list=[]):
        """
        Add a new state to the state machine.  The state is
        initialized with the action list and transition list
        and a new unique name is returned for this state.
        
        The action is added using add_action.

        The transitions is added using add_transition.
        
        @param action_list The list of valid actions for this state
        @param transition_list The list of valid transitions
        @return string unique name for this state
        """
        name = self._generate_unique_state_name()
        
        for action in action_list:
            self._validate_action(action)

        for transition in transition_list:
            self._validate_transition(transition)

        state = State(name, action_list, transition_list)

        if self._state_dict.has_key(name):
            raise Exception("Name: " + name + " already exists, could not\
                            add a new state.")

        self._state_dict[name] = state

        return name
        

    def add_action(self, state_name, action):
        """
        Add an action to a specific state. A unique name for this
        tutorial is generated to refer precisely to this action
        and is returned.

        The action is validated.

        @param state_name The name of the state to add an action to
        @param action The action to be added
        @return unique name for this action
        """
        return "State/Action"

    def add_transition(self, state_name, transition):
        """
        Add a transition to a specific state. A unique name for this
        tutorial is generated to refer precisely to this transition
        and is returned. Inserting a duplicate transition will raise
        an exception.

        The transition is validated.

        @param state_name The name of the state to add a transition to
        @param transition The transition to be added
        @return unique name for this action
        @raise TransitionAlreadyExists
        """
        return "State/Transition"

    def update_action(self, action_name, action):
        """
        Update the properties of a specific action with a copy of the 
        properties of the action passed in. 
        
        The action is validated.
 
        @param action_name The name of the action to update
        @param action An action with the properties to copy from
        @return action_name if the update was successful, False otherwise
        """
        return action_name

    def update_transition(self, transition_name, transition):
        """
        Update the properties of a specific transition with a copy of the 
        properties of the transition passed in. 
        
        The transition is validated.
 
        @param transition_name The name of the transition to update
        @param transition An transition with the properties to copy from
        @return transition_name if the update was successful, False otherwise
        """
        return transition_name

    def delete_action(self, action_name):
        """ 
        Delete the action identified by action_name.
          
        @param action_name The name of the action to be deleted
        @return the action that has been deleted
        """
        return None

    def delete_transition(self, transition_name):
        """
        Delete the transition identified by transition_name.
       
        @param transition_name The name of the transition to be deleted
        @return the transition that has been deleted
        """
        return None

    def delete_state(self, state_name):
        """
        Delete the state, delete all the actions and transitions
        in this state, update the transitions from the state that
        pointed to this one to the next state and remove all the
        unreachable states recursively.

        @param state_name The name of the state to remove
        """
        pass

    def get_actions(self, state_name):
		"""
		@param state_name The name of the state to list actions from  
		@return A list of actions for state_name
		"""
        pass

    def get_events(self, state_name):
		"""
		@param state_name The name of the state to list actions from  
		@return A list of events for state_name
		"""
        pass

    def get_following_states(self, state_name):
        """
        Returns a tuple of the names of the states that point to the given
        state. If there is no such state, the function raises a KeyError.
        
        @param state_name The name of the state to analyse
        @raise KeyError When there is no state by this name in the FSM
        """
        pass


    def get_previous_states(self, state_name):
        """
        Returns a tuple of the names of the state that can transition to
        the given state. If there is no such state, the function raises a
        KeyError.
        
        @param state_name The name of the state that the returned states might
        transition to.
        @raise KeyError When there is no state by this name in the FSM
        """
		pass

    def _validate_action(self, action):
        """
        Validate that an action conforms to what we expect,
        throws an exception otherwise.
        
        @param action The action to validate
        @except InvalidAction if the action fails to conform to what we expect
        """
        pass

    def _validate_action_name(self, action_name):
        """
        Check if action_name exists.
        
        @param action_name The name to check
        @except UnknownName if the name is not present in the Tutorial
        """
        pass

    def _validate_transition(self, transition):
        """
        Validate that a transition conforms to what we expect,
        throws an exception otherwise.
        
        @param transition The transition to validate
        @except InvalidTransition if the transition fails to conform to what we expect
        """
        pass

    def _validate_transition_name(self, transition_name):
        """
        Check if transition_name exists.
        
        @param transition_name The name to check
        @except UnknownName if the name is not present in the Tutorial
        """
        pass

    def validate(self):
        """
        Validate the state machine for a serie of properties:
        1. No unreachable states
        2. No dead end state (except END)
        3. No branching in the main path
        4. No loop in the main path
        5. ...
 
        Throw an exception for the first condition that is not met.
        """
        pass

    def _generate_unique_state_name(self):
        name = "State" + str(self._state_name_nb)
	self._state_name_nb += 1
        
        return name
        
    def __str__(self):
        """
        Return a string representation of the tutorial
        """
        return ""

class State(object):
    """
    This is a step in a tutorial. The state represents a collection of actions 
    to undertake when entering the state, and a series of transitions to lead
    to next states.

    This class is not meant to be used explicitly as no validation is done on
    inputs, the validation should be done by the containing class.
    """
    
    def __init__(self, name="", action_list=[], transition_list=[]):
        """
        Initializes the content of the state, like loading the actions
        that are required and building the correct tests.
        
        @param action_list The list of actions to execute when entering this
        state
        @param transition_list A list of tuples of the form 
        (event, next_state_name), that explains the outgoing links for
        this state
        """
        object.__init__(self)
        
        self.name = name
        
        self._actions = {} 
        for action in action_list:
            self._actions[self._generate_unique_action_name(action)] = action
        
        self._transitions = {}
        for transition in transition_list:
            self._transitions[self._generate_unique_transition_name(transition)] = transition
        
    # Action manipulations
    def add_action(self, new_action):
        """
        Adds an action to the state (only if it wasn't added before)
        
        @param new_action The new action to execute when in this state
        @return a unique name for this action
        """
        if new_action not in self._actions:
            self._actions.append(new_action)
            return True
        return False

    def delete_action(self, action_name):
        """
        Delete the action with the name action_name returned when the 
        action was added or when they are listed
 
        @param action_name The name of the action to delete
        @return True if the action existed of False if no action had this name
        """
        pass

    def update_action(self, action_name, action):
        """ 
        Update the action with the name action_name with the properties from
        action

        @param action_name The name of the action to update
        @param action The action whose properties are copied over
        @return True if action_name existed and the properties were valid, False otherwise
        """
        pass
        
    def get_action_dict(self):
        """
        @return A dictionary of actions that the state will execute
        """
        return self._actions
        
    def delete_actions(self):
        """
        Removes all the action associated with this state. A cleared state will
        not do anything when entered or exited.
        """
        self._actions = {}

    # Transition manipulations    
    def add_transition(self, transition):
        """
        Adds a transition from this state to another state.
        
        The same transition may not be added twice.
        
        @param transition The new transition.
        @return A unique name for the transition could be added, False otherwise
        """
        return False

    def update_transition(self, transition_name, transition):
        """
        Update the transition with the name transition_name with the properties from
        transition
        
        @param transition_name The name of the transition to update
        @param transition The transition whose properties are copied over
        @return True if transition_name existed and the properties were valid, False otherwise
        """
        pass

    def delete_transition(self, transition_name):
        """
        Delete the transition with the name transition_name
        
        @param transition_name The name of the transition to delete
        @return True if transition_name existed, False otherwise
        """
        pass
    
    def get_transition_dict(self):
        """
        @return The dictionary of transitions associated with this state.
        """
        return self._transitions 
    
    def delete_transitions(self):
        """
        Delete all the transitions associated with this state.
        """
        self._transitions = {}

    def _generate_unique_action_name(self, action):
        """
        Returns a unique name for the action in this state
        
        @param action The action to generate a name for
        @return A name garanteed to be unique within this state
        """ 
        return None

    def _generate_unique_transition_name(self, transition):
        """
        Returns a unique name for the transition in this state

        @param transition The transition to generate a name for
        @return A name garanteed to be unique within this state
        """
        return None

    

################## Error Handling and Exceptions ##############################

class TransitionAlreadyExists(Exception):
	"""
	Raised when a duplicate transition is added to a state
	"""
	pass