Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/sugar/tutorius/bundler.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/sugar/tutorius/bundler.py')
-rw-r--r--src/sugar/tutorius/bundler.py247
1 files changed, 179 insertions, 68 deletions
diff --git a/src/sugar/tutorius/bundler.py b/src/sugar/tutorius/bundler.py
index a28d6ef..34b3a12 100644
--- a/src/sugar/tutorius/bundler.py
+++ b/src/sugar/tutorius/bundler.py
@@ -24,17 +24,17 @@ import logging
import os
import uuid
import xml.dom.minidom
-import xml.dom.ext
from sugar.tutorius import gtkutils, overlayer
from sugar.tutorius.core import Tutorial, State, FiniteStateMachine
-import sugar.tutorius.actions
+from sugar.tutorius.actions import *
import sugar.tutorius.filters
+#from sugar.tutorius.filters import EventFilter, TimerEvent, GtkWidgetEventFilter, GtkWidgetTypeFilter
from ConfigParser import SafeConfigParser
-def __get_store_root():
+def _get_store_root():
return os.path.join(os.getenv("SUGAR_PREFIX"),"share","tutorius","data")
-def __get_bundle_root():
+def _get_bundle_root():
return os.path.join(os.getenv("SUGAR_BUNDLE_PATH"),"data","tutorius","data")
INI_ACTIVITY_SECTION = "RELATED_ACTIVITIES"
@@ -51,8 +51,8 @@ class TutorialStore:
given activity.
"""
- store_root = __get_store_root()
- bundle_root = __get_bundle_root()
+ store_root = _get_store_root()
+ bundle_root = _get_bundle_root()
logging.debug("*********** Path of store_root : " + store_root)
@@ -119,70 +119,90 @@ class XMLSerializer(Serializer):
used in the tutorials to/from a .xml file. Inherit from Serializer
"""
- def create_state_dict_node(self, state_dict, doc):
+ def _create_state_dict_node(self, state_dict, doc):
"""
Create and return a xml Node from a State dictionnary.
"""
statesList = doc.createElement("States")
for state_name, state in state_dict.items():
- stateNode = statesList.appendChild("State")
- stateNode.setAttribute("State:Name", state_name)
- stateNode = stateNode.appendChild(create_action_list_node(state.action_list, doc))
- stateNode = stateNode.appendChild(create_event_filters_node(state.event_filters, doc))
+ stateNode = doc.createElement("State")
+ statesList.appendChild(stateNode)
+ stateNode.setAttribute("Name", state_name)
+ actionsList = stateNode.appendChild(self._create_action_list_node(state.get_action_list(), doc))
+ eventfiltersList = stateNode.appendChild(self._create_event_filters_node(state.get_event_filter_list(), doc))
return statesList
- def create_action_list_node(self, action_list, doc):
+ def _create_action_list_node(self, action_list, doc):
"""
Create and return a xml Node from a Action list.
"""
actionsList = doc.createElement("Actions")
for action in action_list:
- actionNode = actionsList.appendChild("Action")
+ actionNode = doc.createElement("Action")
+ actionsList.appendChild(actionNode)
+ # Using .__class__ since type() doesn't have the same behavior
+ # with class derivating from object and class that don't
+ actionNode.setAttribute("Class", str(action.__class__))
+
if type(action) is DialogMessage:
- actionNode.setAttribute("Action:Class", type(action))
- actionNode.setAttribute("Action:Message", action.message)
- actionNode.setAttribute("Action:Position", action.position)
+ actionNode.setAttribute("Message", action.message.value)
+ actionNode.setAttribute("PositionX", action.position.value[0])
+ actionNode.setAttribute("PositionY", str(action.position.value[1]))
elif type(action) is BubbleMessage:
- actionNode.setAttribute("Action:Class", str(type(action)))
- actionNode.setAttribute("Action:Message", action.message)
- actionNode.setAttribute("Action:Position", action.position)
- actionNode.setAttribute("Action:Tail_pos", action.tail_pos)
+ actionNode.setAttribute("Message", action.message.value)
+ actionNode.setAttribute("PositionX", str(action.position.value[0]))
+ actionNode.setAttribute("PositionY", str(action.position.value[1]))
+ actionNode.setAttribute("Tail_posX", str(action.tail_pos.value[0]))
+ actionNode.setAttribute("Tail_posY", str(action.tail_pos.value[1]))
# TODO : elif for each type of action
elif type(action) is WidgetIdentifyAction:
- actionNode.setAttribute("Action:Class", str(type(action)))
- # TODO
+ # Nothing else to save
+ pass
elif type(action) is ChainAction:
- # TODO
- actionNode.setAttribute("Action:Class", str(type(action)))
+ # Recusively write the contained actions - in the correct order
+ subActionsNode = self._create_actions_node(action._actions, doc)
+ actionNode.appendChild(subActionsNode)
elif type(action) is DisableWidgetAction:
- # TODO
- actionNode.setAttribute("Action:Class", str(type(action)))
+ # Remember the target
+ actionNode.setAttribute("Target", action._target)
elif type(action) is TypeTextAction:
- # TODO
- actionNode.setAttribute("Action:Class", str(type(action)))
+ # Save the text and the widget
+ actionNode.setAttribute("Widget", action._widget)
+ actionNode.setAttribute("Text", action._text)
elif type(action) is ClickAction:
- # TODO
- actionNode.setAttribute("Action:Class", str(type(action)))
+ # Save the widget to click
+ actionNode.setAttribute("Widget", action._widget)
return actionsList
- def create_event_filters_node(self, event_filters, doc):
+ def _create_event_filters_node(self, event_filters, doc):
"""
Create and return a xml Node from a event filters.
"""
eventFiltersList = doc.createElement("EventFiltersList")
for event_f in event_filters:
eventFilterNode = eventFiltersList.appendChild("EventFilter")
- # TODO : elif for each type of event filters
- if type(event_f) is TimerEvent:
- # TODO
- eventFilterNode.setAttribute("EventFilter:Class", str(type(event_f)))
- elif type(event_f) is GtkWidgetEventFilter:
- # TODO
- eventFilterNode.setAttribute("EventFilter:Class", str(type(event_f)))
- elif type(event_f) is GtkWidgetTypeFilter:
- # TODO
- eventFilterNode.setAttribute("EventFilter:Class", str(type(event_f)))
+
+ # Using .__class__ since type() doesn't have the same behavior
+ # with class derivating from object and class that don't
+ eventFilterNode.setAttribute("Class", str(event_f.__class__))
+
+ # Write the name of the next state
+ eventFilterNode.setAttribute("NextState", event_f.next_state)
+
+ if type(event_f) is sugar.tutorius.filters.TimerEvent:
+ eventFilterNode.setAttribute("Timeout_s", str(event_f._timeout))
+
+ elif type(event_f) is sugar.tutorius.filters.GtkWidgetEventFilter:
+ eventFilterNode.setAttribute("EventName", event_f._event_name)
+ eventFilterNode.setAttribute("ObjectId", event_f._object_id)
+
+ elif type(event_f) is sugar.tutorius.filters.GtkWidgetTypeFilter:
+ eventFilterNode.setAttribute("ObjectId", event_f._object_id)
+ if event_f._strokes is not None:
+ eventFilterNode.setAttribute("Strokes", event_f._strokes)
+ if event_f._text is not None:
+ eventFilterNode.setAttribute("Text", event_f._text)
return eventFiltersList
@@ -194,15 +214,18 @@ class XMLSerializer(Serializer):
doc = xml.dom.minidom.Document()
fsm_element = doc.createElement("FSM")
doc.appendChild(fsm_element)
- fsm_element.setAttribute("fsm:Name", fsm.name)
- fsm_element.setAttribute("fsm:StartStateName", fsm.start_state_name)
- fsm_element = fsm_element.appendChild(create_state_dict_node(fsm.state_dict, doc))
- fsm_element = fsm_element.appendChild(create_action_list_node(fsm.action_list, doc))
+ fsm_element.setAttribute("Name", fsm.name)
+ fsm_element.setAttribute("StartStateName", fsm.start_state_name)
+ statesDict = fsm_element.appendChild(self._create_state_dict_node(fsm._states, doc))
+
+ fsm_actions_node = self._create_action_list_node(fsm.actions, doc)
+ fsm_actions_node.tagName = "FSMActions"
+ actionsList = fsm_element.appendChild(fsm_actions_node)
- file_object = open(path + "/" + xml_filename, "w")
- xml.dom.ext.PrettyPrint(doc, file_object)
+ file_object = open(os.path.join(path, xml_filename), "w")
+ file_object.write(doc.toprettyxml())
file_object.close()
-
+
def _find_tutorial_dir_with_guid(self, guid):
"""
@@ -218,12 +241,12 @@ class XMLSerializer(Serializer):
@param guid The GUID of the tutorial that is to be loaded.
"""
# Attempt to find the tutorial's directory in the global directory
- global_dir = os.path.join(__get_store_root(), guid)
+ global_dir = os.path.join(_get_store_root(), guid)
# Then in the activty's bundle path
- activity_dir = os.path.join(__get_bundle_root(), guid)
+ activity_dir = os.path.join(_get_bundle_root(), guid)
# If they both exist
- if os.path.isdir(global_filename) and os.path.isdir(activity_dir):
+ if os.path.isdir(global_dir) and os.path.isdir(activity_dir):
# Inspect both metadata files
global_meta = os.path.join(global_dir, "meta.ini")
activity_meta = os.path.join(activity_dir, "meta.ini")
@@ -254,7 +277,7 @@ class XMLSerializer(Serializer):
return activity_dir
# Error : none of these directories contain the tutorial
- raise IOError(2, "Neither the global nor the bundle directory contained the tutorial with GUID"%guid)
+ raise IOError(2, "Neither the global nor the bundle directory contained the tutorial with GUID %s"%guid)
def _load_xml_properties(self, properties_elem):
"""
@@ -271,7 +294,40 @@ class XMLSerializer(Serializer):
@param filters_elem An XML Element representing a list of event filters
"""
- return []
+ reformed_event_filters_list = []
+ # item(0) because there is always only one <EventFilterList> tag in the xml file
+ # so states_elem should always contain only one element
+ event_filter_element_list = filters_elem.item(0).getElementsByTagName("EventFilter")
+ new_event_filter = None
+
+ for event_filter in event_filter_element_list:
+ # Load the name of the next state for this filter
+ next_state = event_filter.getAttribute("NextState")
+
+ if event_filter.getAttribute("Class") == str(sugar.tutorius.filters.TimerEvent):
+ timeout = int(event_filter.getAttribute("Timeout_s"))
+ new_event_filter = TimerEvent(next_state, timeout)
+
+ elif event_filter.getAttribute("Class") == str(sugar.tutorius.filters.GtkWidgetEventFilter):
+ # Get the event name and the object's ID
+ event_name = event_filter.getAttribute("EventName")
+ object_id = event_filter.getAttribute("ObjectId")
+
+ new_event_filter = GtkWidgetEventFilter(next_state, object_id, event_name)
+ elif event_filter.getAttribute("Class") == str(sugar.tutorius.filters.GtkWidgetTypeFilter):
+ # Get the widget to write in and the text
+ object_id = event_filter.getAttribute("ObjectId")
+ if event_filter.hasAttribute("Text"):
+ text = event_filter.getAttribute("Text")
+ new_event_filter = GtkWidgetTypeFilter(next_state, object_id, text=text)
+ elif event_filter.hasAttribute("Strokes"):
+ strokes = event_filter.getAttribute("Strokes")
+ new_event_filter = GtkWidgetTypeFilter(next_state, object_id, strokes=strokes)
+
+ if new_event_filter is not None:
+ reformed_event_filter.append(new_event_filter)
+
+ return reformed_event_filters_list
def _load_xml_actions(self, actions_elem):
"""
@@ -279,7 +335,51 @@ class XMLSerializer(Serializer):
@param actions_elem An XML Element representing a list of Actions
"""
- return []
+ reformed_actions_list = []
+ # item(0) because there is always only one <Actions> tag in the xml file
+ # so states_elem should always contain only one element
+ actions_element_list = actions_elem.item(0).getElementsByTagName("Action")
+
+ for action in actions_element_list:
+ # TODO : elif for each type of action
+ if action.getAttribute("Class") == str(DialogMessage):
+ message = action.getAttribute("Message")
+ positionX = int(action.getAttribute("PositionX"))
+ positionY = int(action.getAttribute("PositionY"))
+ position = [positionX, positionY]
+ reformed_actions_list.append(DialogMessage(message,position))
+ elif action.getAttribute("Class") == str(BubbleMessage):
+ message = action.getAttribute("Message")
+ positionX = int(action.getAttribute("PositionX"))
+ positionY = int(action.getAttribute("PositionY"))
+ position = [positionX, positionY]
+ tail_posX = action.getAttribute("Tail_posX")
+ tail_posY = action.getAttribute("Tail_posY")
+ tail_pos = [tail_posX, tail_posY]
+ reformed_actions_list.append(BubbleMessage(message,position,None,tail_pos))
+ elif action.getAttribute("Class") == str(WidgetIdentifyAction):
+ reformed_actions_list.append(WidgetIdentifyAction())
+ elif action.getAttribute("Class") == str(ChainAction):
+ # Load the subactions
+ subActionsList = _load_xml_actions(action.getElementsByTagName("Actions"))
+ reformed_actions_list.append(ChainAction(subActionsList))
+ elif action.getAttribute("Class") == str(DisableWidgetAction):
+ # Get the target
+ targetName = action.getAttribute("Target")
+ reformed_actions_list.append(DisableWidgetAction(targetName))
+ elif action.getAttribute("Class") == str(TypeTextAction):
+ # Get the widget and the text to type
+ widget = action.getAttribute("Widget")
+ text = action.getAttribute("Text")
+
+ reformed_actions_list.append(TypeTextAction(widget, text))
+ elif action.getAttribute("Class") == str(ClickAction):
+ # Load the widget to click
+ widget = action.getAttribute("Widget")
+
+ reformed_actions_list.append(ClickAction(widget))
+
+ return reformed_actions_list
def _load_xml_states(self, states_elem):
"""
@@ -288,7 +388,18 @@ class XMLSerializer(Serializer):
@param states_elem An XML Element that represents a list of States
"""
- return []
+ reformed_state_list = []
+ # item(0) because there is always only one <States> tag in the xml file
+ # so states_elem should always contain only one element
+ states_element_list = states_elem.item(0).getElementsByTagName("State")
+
+ for state in states_element_list:
+ stateName = state.getAttribute("Name")
+ actions_list = self._load_xml_actions(state.getElementsByTagName("Actions"))
+ event_filters_list = self._load_xml_event_filters(state.getElementsByTagName("EventFiltersList"))
+ reformed_state_list.append(State(stateName, actions_list, event_filters_list))
+
+ return reformed_state_list
def _load_xml_fsm(self, fsm_elem):
"""
@@ -298,28 +409,28 @@ class XMLSerializer(Serializer):
@param fsm_elem The XML element that describes a FSM
"""
# Load the FSM's name and start state's name
- fsm_name = fsm_elem.getAttribute("fsm:Name")
+ fsm_name = fsm_elem.getAttribute("Name")
fsm_start_state_name = None
try:
- fsm_start_state_name = fsm_elem.getAttribute("fsm:StartStateName")
+ fsm_start_state_name = fsm_elem.getAttribute("StartStateName")
except:
pass
fsm = FiniteStateMachine(fsm_name, start_state_name=fsm_start_state_name)
# Load the states
- states = self._load_xml_states(fsm_elem.getElementsByName("States"))
+ states = self._load_xml_states(fsm_elem.getElementsByTagName("States"))
for state in states:
fsm.add_state(state)
# Load the actions on this FSM
- actions = self._load_xml_actions(fsm_elem.getElementsByName("Actions"))
+ actions = self._load_xml_actions(fsm_elem.getElementsByTagName("FSMActions"))
for action in actions:
fsm.add_action(action)
# Load the event filters
- events = self._load_xml_event_filters(fsm_elem.getElementsByName("EventFiltersList"))
+ events = self._load_xml_event_filters(fsm_elem.getElementsByTagName("EventFiltersList"))
for event in events:
fsm.add_event_filter(event)
@@ -338,7 +449,7 @@ class XMLSerializer(Serializer):
xml_dom = xml.dom.minidom.parse(tutorial_file)
- fsm_elem = xml_dom.getElementsByTagName("fsm")[0]
+ fsm_elem = xml_dom.getElementsByTagName("FSM")[0]
return self._load_xml_fsm(fsm_elem)
@@ -351,7 +462,6 @@ class TutorialBundler:
def __init__(self,generated_guid = None):
"""
- TODO.
Tutorial_bundler constructor. If a GUID is given in the parameter, the
Tutorial_bundler object will be associated with it. If no GUID is given,
a new GUID will be generated,
@@ -362,12 +472,12 @@ class TutorialBundler:
#Look for the file in the path if a uid is supplied
if generated_guid:
#General store
- store_path = os.path.join(__get_store_root(), generated_guid, INI_FILENAME)
+ store_path = os.path.join(_get_store_root(), generated_guid, INI_FILENAME)
if os.path.isfile(store_path):
self.Path = os.path.dirname(store_path)
else:
#Bundle store
- bundle_path = os.path.join(__get_bundle_root(), generated_guid, INI_FILENAME)
+ bundle_path = os.path.join(_get_bundle_root(), generated_guid, INI_FILENAME)
if os.path.isfile(bundle_path):
self.Path = os.path.dirname(bundle_path)
else:
@@ -375,7 +485,7 @@ class TutorialBundler:
else:
#Create the folder, any failure will go through to the caller for now
- store_path = os.path.join(__get_store_root(), generated_guid)
+ store_path = os.path.join(_get_store_root(), generated_guid)
os.mkdir(store_path)
self.Path = store_path
@@ -424,8 +534,8 @@ class TutorialBundler:
more than one path, the store_root is given priority.
"""
- store_root = __get_store_root()
- bundle_root = __get_bundle_root()
+ store_root = _get_store_root()
+ bundle_root = _get_bundle_root()
config = SafeConfigParser()
path = None
@@ -483,10 +593,11 @@ class TutorialBundler:
config = SafeConfigParser()
if guid is not None:
+ serializer = XMLSerializer()
path = get_tutorial_path() + "/meta.ini"
config.read(path)
xml_filename = config.get(INI_METADATA_SECTION, INI_XML_FSM_PROPERTY)
- save_fsm(fsm, xml_filename, store_root)
+ serializer.save_fsm(fsm, xml_filename, store_root)
def add_resources(self, typename, file):