From f03752836b0fdbcc805d504cd9b2437f89e6b475 Mon Sep 17 00:00:00 2001 From: erick Date: Sat, 24 Oct 2009 19:05:34 +0000 Subject: Initial commit of the tutorial ADT --- (limited to 'tutorius/tutorial.py') diff --git a/tutorius/tutorial.py b/tutorius/tutorial.py new file mode 100644 index 0000000..a9c0cab --- /dev/null +++ b/tutorius/tutorial.py @@ -0,0 +1,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 + + -- cgit v0.9.1