diff options
Diffstat (limited to 'tutorius/bundler.py')
-rw-r--r-- | tutorius/bundler.py | 121 |
1 files changed, 49 insertions, 72 deletions
diff --git a/tutorius/bundler.py b/tutorius/bundler.py index 734c679..c9558b1 100644 --- a/tutorius/bundler.py +++ b/tutorius/bundler.py @@ -24,6 +24,7 @@ import logging import os import uuid import xml.dom.minidom +from xml.dom import NotFoundErr from sugar.tutorius import addon from sugar.tutorius.core import Tutorial, State, FiniteStateMachine @@ -37,8 +38,10 @@ def _get_store_root(): return os.path.join(os.getenv("HOME"), ".sugar",profile_name,"tutorius","data") # this is where activity bundled tutorials should be, under the activity bundle -def _get_bundle_root(): - return os.path.join(os.getenv("SUGAR_BUNDLE_PATH"),"data","tutorius","data") +def _get_bundle_root(base_path=None): + base_path = base_path or os.getenv("SUGAR_BUNDLE_PATH") + if base_path: + return os.path.join(os.getenv("SUGAR_BUNDLE_PATH"),"data","tutorius","data") INI_ACTIVITY_SECTION = "RELATED_ACTIVITIES" INI_METADATA_SECTION = "GENERAL_METADATA" @@ -48,46 +51,9 @@ INI_XML_FSM_PROPERTY = "FSM_FILENAME" INI_FILENAME = "meta.ini" TUTORIAL_FILENAME = "tutorial.xml" NODE_COMPONENT = "Component" -NODE_SUBCOMPONENT = "SubComponent" -NODE_SUBCOMPONENTLIST = "SubComponentList" - -class Vault(object): - """ - The Vault is the primary interface for the storage and installation of tutorials - on the machine. It needs to accomplish the following tasks : - - query() : Lists the - - installTutorial() : - - deleteTutorial() : - - readTutorial() : - - saveTutorial() : - """ - def query(keyword="", category="", start_index=0, num_results=10): - """ - Returns a list of tutorial meta-data corresponding to the keywords - and category mentionned. - - @param keyword The keyword to look for in the tutorial title and description. - @param category The category in which to look for tutorials - @param start_index The first result to be shown (e.g. ) - @param num_results The number of results to show - @return The list of tutorial metadata that corresponds to the query parameters. - """ - raise NotImplementedError("The query function on the Vault is not implemented") - - def installTutorial(path ,force_install=False): - """ - Inserts the tutorial inside the Vault. Once installed, it will show up - """ - raise NotImplementedError("Installation in the Vault not supported yet") - - def deleteTutorial(tutorial_id): - raise NotImplementedError("") - - def readTutorial(tutorial_id): - raise NotImplementedError("") - - def saveTutorial(tutorial, metadata, resource_list): - raise NotImplementedError("") +NODE_SUBCOMPONENT = "property" +NODE_SUBCOMPONENTLIST = "listproperty" +NEXT_STATE_ATTR = "next_state" class TutorialStore(object): @@ -99,7 +65,7 @@ class TutorialStore(object): @returns a map of tutorial {names : GUID}. """ # check both under the activity data and user installed folders - paths = [_get_store_root(), _get_bundle_root()] + paths = [p for p in [_get_store_root(), _get_bundle_root()] if p ] tutoGuidName = {} @@ -127,7 +93,7 @@ class TutorialStore(object): return tutoGuidName - def load_tutorial(self, Guid): + def load_tutorial(self, Guid, bundle_path=None): """ Rebuilds a tutorial object from it's serialized state. Common storing paths will be scanned. @@ -135,15 +101,15 @@ class TutorialStore(object): @param Guid the generic identifier of the tutorial @returns a Tutorial object containing an FSM """ - bundle = TutorialBundler(Guid) - bundle_path = bundle.get_tutorial_path() + bundler = TutorialBundler(Guid, bundle_path=bundle_path) + bundler_path = bundler.get_tutorial_path() config = SafeConfigParser() - config.read(os.path.join(bundle_path, INI_FILENAME)) + config.read(os.path.join(bundler_path, INI_FILENAME)) serializer = XMLSerializer() name = config.get(INI_METADATA_SECTION, INI_NAME_PROPERTY) - fsm = serializer.load_fsm(Guid) + fsm = serializer.load_fsm(Guid, bundler.Path) tuto = Tutorial(name, fsm) return tuto @@ -163,13 +129,13 @@ class Serializer(object): exception occur. If no GUID is provided, FSM is written in a new file in the store root. """ - NotImplementedError + return NotImplementedError() def load_fsm(self): """ Load fsm from disk. """ - NotImplementedError + return NotImplementedError() class XMLSerializer(Serializer): """ @@ -197,9 +163,9 @@ class XMLSerializer(Serializer): e.g. <Component Class="OnceWrapper"> - <SubComponent property="addon"> + <property name="addon"> <Component Class="BubbleMessage" message="'Hi!'" position="[12,32]"/> - </SubComponent> + </property> </Component> When reloading this node, we should look up the property name for the parent @@ -215,7 +181,7 @@ class XMLSerializer(Serializer): that represents another component. """ subCompNode = doc.createElement(NODE_SUBCOMPONENT) - subCompNode.setAttribute("property", parent_attr_name) + subCompNode.setAttribute("name", parent_attr_name) subNode = self._create_component_node(comp, doc) @@ -231,10 +197,10 @@ class XMLSerializer(Serializer): e.g. <Component Class="ChainAction"> - <SubComponentList property="actions"> + <listproperty name="actions"> <Component Class="BubbleMessage" message="'Hi!'" position="[15,35]"/> <Component Class="DialogMessage" message="'Multi-action!'" position="[45,10]"/> - </SubComponentList> + </listproperty> </Component> When reloading this node, we should look up the property name for the parent @@ -247,7 +213,7 @@ class XMLSerializer(Serializer): @returns A NODE_SUBCOMPONENTLIST node with the property attribute """ subCompListNode = doc.createElement(NODE_SUBCOMPONENTLIST) - subCompListNode.setAttribute("property", parent_attr_name) + subCompListNode.setAttribute("name", parent_attr_name) for comp in comp_list: compNode = self._create_component_node(comp, doc) @@ -308,8 +274,9 @@ class XMLSerializer(Serializer): Create and return a xml Node from a event filters. """ eventFiltersList = doc.createElement("EventFiltersList") - for event_f in event_filters: - eventFilterNode = self._create_component_node(event_f, doc) + for event, state in event_filters: + eventFilterNode = self._create_component_node(event, doc) + eventFilterNode.setAttribute(NEXT_STATE_ATTR, str(state)) eventFiltersList.appendChild(eventFilterNode) return eventFiltersList @@ -423,24 +390,29 @@ class XMLSerializer(Serializer): @param filters_elem An XML Element representing a list of event filters """ - reformed_event_filters_list = [] + transition_list = [] event_filter_element_list = self._get_direct_descendants_by_tag_name(filters_elem, NODE_COMPONENT) new_event_filter = None for event_filter in event_filter_element_list: + next_state = event_filter.getAttribute(NEXT_STATE_ATTR) + try: + event_filter.removeAttribute(NEXT_STATE_ATTR) + except NotFoundErr: + next_state = None new_event_filter = self._load_xml_component(event_filter) if new_event_filter is not None: - reformed_event_filters_list.append(new_event_filter) + transition_list.append((new_event_filter, next_state)) - return reformed_event_filters_list + return transition_list def _load_xml_subcomponents(self, node, properties): """ Loads all the subcomponent node below the given node and inserts them with the right property name inside the properties dictionnary. - @param node The parent node that contains one or many SubComponent nodes. + @param node The parent node that contains one or many property nodes. @param properties A dictionnary where the subcomponent property names and the instantiated components will be stored @returns Nothing. The properties dict will contain the property->comp mapping. @@ -448,7 +420,7 @@ class XMLSerializer(Serializer): subCompList = self._get_direct_descendants_by_tag_name(node, NODE_SUBCOMPONENT) for subComp in subCompList: - property_name = subComp.getAttribute("property") + property_name = subComp.getAttribute("name") internal_comp_node = self._get_direct_descendants_by_tag_name(subComp, NODE_COMPONENT)[0] internal_comp = self._load_xml_component(internal_comp_node) properties[str(property_name)] = internal_comp @@ -464,7 +436,7 @@ class XMLSerializer(Serializer): """ listOf_subCompListNode = self._get_direct_descendants_by_tag_name(node, NODE_SUBCOMPONENTLIST) for subCompListNode in listOf_subCompListNode: - property_name = subCompListNode.getAttribute("property") + property_name = subCompListNode.getAttribute("name") subCompList = [] for subCompNode in self._get_direct_descendants_by_tag_name(subCompListNode, NODE_COMPONENT): subComp = self._load_xml_component(subCompNode) @@ -568,18 +540,18 @@ class XMLSerializer(Serializer): # Load the event filters events = self._load_xml_event_filters(fsm_elem.getElementsByTagName("EventFiltersList")[0]) - for event in events: - fsm.add_event_filter(event) + for event, next_state in events: + fsm.add_event_filter(event, next_state) return fsm - def load_fsm(self, guid): + def load_fsm(self, guid, path=None): """ Load fsm from xml file whose .ini file guid match argument guid. """ # Fetch the directory (if any) - tutorial_dir = self._find_tutorial_dir_with_guid(guid) + tutorial_dir = path or self._find_tutorial_dir_with_guid(guid) # Open the XML file tutorial_file = os.path.join(tutorial_dir, TUTORIAL_FILENAME) @@ -597,7 +569,7 @@ class TutorialBundler(object): editor. """ - def __init__(self,generated_guid = None): + def __init__(self,generated_guid = None, bundle_path=None): """ 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, @@ -606,6 +578,7 @@ class TutorialBundler(object): self.Guid = generated_guid or str(uuid.uuid1()) + #FIXME: Look for the bundle in the activity first (more specific) #Look for the file in the path if a uid is supplied if generated_guid: #General store @@ -614,9 +587,13 @@ class TutorialBundler(object): self.Path = os.path.dirname(store_path) else: #Bundle store - 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) + base_bundle_path = _get_bundle_root(bundle_path) + if base_bundle_path: + bundle_path = os.path.join(base_bundle_path, generated_guid, INI_FILENAME) + if os.path.isfile(bundle_path): + self.Path = os.path.dirname(bundle_path) + else: + raise IOError(2,"Unable to locate metadata file for guid '%s'" % generated_guid) else: raise IOError(2,"Unable to locate metadata file for guid '%s'" % generated_guid) |