From f347ec202fe5b4404fa380694d7fe3d3d070ae7b Mon Sep 17 00:00:00 2001 From: Simon Poirier Date: Mon, 18 May 2009 20:27:41 +0000 Subject: fixed major missing parts in bundler integrated bundler to creator tutorial loading still is unusable (fiters don't seem to load) --- (limited to 'src/sugar/tutorius/bundler.py') diff --git a/src/sugar/tutorius/bundler.py b/src/sugar/tutorius/bundler.py index f9a3911..0eb6b64 100644 --- a/src/sugar/tutorius/bundler.py +++ b/src/sugar/tutorius/bundler.py @@ -25,14 +25,16 @@ import os import uuid import xml.dom.minidom -from sugar.tutorius import gtkutils, overlayer +from sugar.tutorius import gtkutils, overlayer, tutorial from sugar.tutorius.core import Tutorial, State, FiniteStateMachine from sugar.tutorius.filters import * from sugar.tutorius.actions import * from ConfigParser import SafeConfigParser +# this is where user installed/generated tutorials will go def _get_store_root(): - return os.path.join(os.getenv("SUGAR_PREFIX"),"share","tutorius","data") + return os.path.join(os.getenv("HOME"),".sugar",os.getenv("SUGAR_PROFILE"),"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") @@ -41,62 +43,76 @@ INI_METADATA_SECTION = "GENERAL_METADATA" INI_GUID_PROPERTY = "GUID" INI_NAME_PROPERTY = "NAME" INI_XML_FSM_PROPERTY = "FSM_FILENAME" +INI_FILENAME = "meta.ini" +TUTORIAL_FILENAME = "tutorial.xml" -class TutorialStore: +class TutorialStore(object): - def list_avaible_tutorials(self, activity_name, activity_vers): + def list_available_tutorials(self, activity_name, activity_vers): """ - Recuperate the list of all tutorials present on disk for a + Generate the list of all tutorials present on disk for a given activity. + + @returns a map of tutorial {names : GUID}. """ - - store_root = _get_store_root() - bundle_root = _get_bundle_root() - - logging.debug("*********** Path of store_root : " + store_root) - - # Create /data/tutorius if no exists - if not os.path.exists(store_root): - os.mkdir(store_root) - logging.debug("************* Creating %s folder" % store_root) - + # check both under the activity data and user installed folders + paths = [_get_store_root(), _get_bundle_root()] + tutoGuidName = {} - - # iterate in each GUID subfolder - for dir in os.listdir(store_root): - # iterate for each ".ini" file in the activity store_root folder - for file_name in os.listdir(store_root + "/" + dir): - - if file_name.endswith(".ini"): - logging.debug("************** .ini file found : " + file_name) - # Filter for just .ini files who metadata ACTIVITY_NAME - # match 'activity_name' given in argument. - config = SafeConfigParser() - config.read(file_name) - # Get all activity tuples (Activity_Name: Activity_Version) - file_activity_tuples = config.items(INI_ACTIVITY_SECTION) - - for i in range(0, len(file_activity_tuples) - 1): - - if file_activity_tuples[i][0] == activity_name and \ - int(file_activity_tuples[i][1]) == activity_vers: - # Add this tutorial guid and name in the dictionary - file_activity_guid = config.get(INI_METADATA_SECTION, - INI_GUID_PROPERTY) - file_activity_name = config.get(INI_METADATA_SECTION, - INI_NAME_PROPERTY) - tutoGuidName[file_activity_name] = file_activity_guid + + for repository in paths: + # (our) convention dictates that tutorial folders are named + # with their GUID (for unicity) but this is not enforced. + try: + for tuto in os.listdir(repository): + parser = SafeConfigParser() + parser.read(os.path.join(repository, tuto, INI_FILENAME)) + guid = parser.get(INI_METADATA_SECTION, INI_GUID_PROPERTY) + name = parser.get(INI_METADATA_SECTION, INI_NAME_PROPERTY) + activities = parser.options(INI_ACTIVITY_SECTION) + # enforce matching activity name AND version, as UI changes + # break tutorials. We may lower this requirement when the + # UAM gets less dependent on the widget order. + # Also note property names are always stored lowercase. + if activity_name.lower() in activities: + version = parser.get(INI_ACTIVITY_SECTION, activity_name) + if activity_vers == version: + tutoGuidName[guid] = name + except OSError: + # the repository may not exist. Continue scanning + pass return tutoGuidName - -class Serializer: + def load_tutorial(self, Guid): + """ + Rebuilds a tutorial object from it's serialized state. + Common storing paths will be scanned. + + @param Guid the generic identifier of the tutorial + @returns a Tutorial object containing an FSM + """ + bundle = TutorialBundler(Guid) + bundle_path = bundle.get_tutorial_path() + config = SafeConfigParser() + config.read(os.path.join(bundle_path, INI_FILENAME)) + + serializer = XMLSerializer() + + name = config.get(INI_METADATA_SECTION, INI_NAME_PROPERTY) + fsm = serializer.load_fsm(Guid) + + tuto = Tutorial(name, fsm) + return tuto + + +class Serializer(object): """ Interface that provide serializing and deserializing of the FSM used in the tutorials to/from disk. Must be inherited. """ - def save_fsm(self,fsm, guid = None): + def save_fsm(self,fsm): """ Save fsm to disk. If a GUID parameter is provided, the existing GUID is located in the .ini files in the store root and bundle root and @@ -106,7 +122,7 @@ class Serializer: """ NotImplementedError - def load_fsm(self, guid): + def load_fsm(self): """ Load fsm from disk. """ @@ -143,9 +159,7 @@ class XMLSerializer(Serializer): # Write down just the name of the Action class as the Class # property -- - # 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__)) + actionNode.setAttribute("Class",type(action).__name__) if type(action) is DialogMessage: actionNode.setAttribute("Message", action.message.value) @@ -211,9 +225,7 @@ class XMLSerializer(Serializer): # Write down just the name of the Action class as the Class # property -- - # 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__)) + eventFilterNode.setAttribute("Class", type(event_f).__name__) # Write the name of the next state eventFilterNode.setAttribute("NextState", event_f.next_state) @@ -365,38 +377,38 @@ class XMLSerializer(Serializer): description """ # TO ADD: an elif for each type of action - if action.getAttribute("Class") == str(DialogMessage): + if action.getAttribute("Class") == 'DialogMessage': message = action.getAttribute("Message") positionX = int(action.getAttribute("PositionX")) positionY = int(action.getAttribute("PositionY")) position = [positionX, positionY] return DialogMessage(message,position) - elif action.getAttribute("Class") == str(BubbleMessage): + elif action.getAttribute("Class") == '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_posX = int(action.getAttribute("Tail_posX")) + tail_posY = int(action.getAttribute("Tail_posY")) tail_pos = [tail_posX, tail_posY] return BubbleMessage(message,position,None,tail_pos) - elif action.getAttribute("Class") == str(WidgetIdentifyAction): + elif action.getAttribute("Class") == 'WidgetIdentifyAction': return WidgetIdentifyAction() - elif action.getAttribute("Class") == str(ChainAction): + elif action.getAttribute("Class") == 'ChainAction': # Load the subactions subActionsList = self._load_xml_actions(action.getElementsByTagName("Actions")[0]) return ChainAction(subActionsList) - elif action.getAttribute("Class") == str(DisableWidgetAction): + elif action.getAttribute("Class") == 'DisableWidgetAction': # Get the target targetName = action.getAttribute("Target") return DisableWidgetAction(targetName) - elif action.getAttribute("Class") == str(TypeTextAction): + elif action.getAttribute("Class") == 'TypeTextAction': # Get the widget and the text to type widget = action.getAttribute("Widget") text = action.getAttribute("Text") return TypeTextAction(widget, text) - elif action.getAttribute("Class") == str(ClickAction): + elif action.getAttribute("Class") == 'ClickAction': # Load the widget to click widget = action.getAttribute("Widget") @@ -484,7 +496,7 @@ class XMLSerializer(Serializer): tutorial_dir = self._find_tutorial_dir_with_guid(guid) # Open the XML file - tutorial_file = os.path.join(tutorial_dir, "fsm.xml") + tutorial_file = os.path.join(tutorial_dir, TUTORIAL_FILENAME) xml_dom = xml.dom.minidom.parse(tutorial_file) @@ -493,7 +505,7 @@ class XMLSerializer(Serializer): return self._load_xml_fsm(fsm_elem) -class TutorialBundler: +class TutorialBundler(object): """ This class provide the various data handling methods useable by the tutorial editor. @@ -506,7 +518,7 @@ class TutorialBundler: a new GUID will be generated, """ - self.Guid = generated_guid or uuid.uuid1() + self.Guid = generated_guid or str(uuid.uuid1()) #Look for the file in the path if a uid is supplied if generated_guid: @@ -524,48 +536,28 @@ 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) - os.mkdir(store_path) + store_path = os.path.join(_get_store_root(), self.Guid) + os.makedirs(store_path) self.Path = store_path - - def __SetGuid(self, value): - self.__guid = value - - def __GetGuid(self): - return self.__guid - - def __DelGuid(self): - del self.__guid - - def __SetPath(self, value): - self.__path = value - - def __GetPath(self): - return self.__path - - def __DelPath(self): - del self.__path - - Guid = property(fget=__SetGuid, - fset=__GetGuid, - fdel=__DelGuid, - doc="The guid associated with the Tutoria_Bundler") - - Path = property(fget=__SetPath, - fset=__GetPath, - fdel=__DelPath, - doc="The path associated with the Tutoria_Bundler") - - - def write_metadata_file(self, data): - """ - Write metadata to a property file. If a GUID is provided, TutorialBundler - will try to find and overwrite the existing property file who contain the - given GUID, and will raise an exception if it cannot find it. - """ - NotImplementedError - + def write_metadata_file(self, tutorial): + """ + Write metadata to the property file. + @param tutorial Tutorial for which to write metadata + """ + #Create the Config Object and populate it + cfg = SafeConfigParser() + cfg.add_section(INI_METADATA_SECTION) + cfg.set(INI_METADATA_SECTION, INI_GUID_PROPERTY, self.Guid) + cfg.set(INI_METADATA_SECTION, INI_NAME_PROPERTY, tutorial.name) + cfg.set(INI_METADATA_SECTION, INI_XML_FSM_PROPERTY, TUTORIAL_FILENAME) + cfg.add_section(INI_ACTIVITY_SECTION) + cfg.set(INI_ACTIVITY_SECTION, os.environ['SUGAR_BUNDLE_NAME'], + os.environ['SUGAR_BUNDLE_VERSION']) + + #Write the ini file + cfg.write( file( os.path.join(self.Path, INI_FILENAME), 'w' ) ) + def get_tutorial_path(self): """ Return the path of the .ini file associated with the guiven guid set in @@ -587,16 +579,16 @@ class TutorialBundler: # iterate for each .ini file in the store_root folder - for file_name in os.listdir(store_root + "/" + dir): + for file_name in os.listdir(os.path.join(store_root, dir)): if file_name.endswith(".ini"): logging.debug("******************* Found .ini file : " \ + file_name) - config.read(file_name) + config.read(os.path.join(store_root, dir, file_name)) if config.get(INI_METADATA_SECTION, INI_GUID_PROPERTY) == self.Guid: xml_filename = config.get(INI_METADATA_SECTION, INI_XML_FSM_PROPERTY) - path = os.path.join(store_root, self.Guid) + path = os.path.join(store_root, dir) return path logging.debug("************ Path of bundle_root folder of activity : " \ @@ -607,12 +599,12 @@ class TutorialBundler: for dir in os.listdir(bundle_root): # iterate for each .ini file in the bundle_root folder - for file_name in os.listdir(bundle_root + "/" + dir): + for file_name in os.listdir(os.path.join(bundle_root, dir)): if file_name.endswith(".ini"): logging.debug("******************* Found .ini file : " \ + file_name) - config.read(file_name) - if config.get(INI_METADATA_SECTION, INI_GUID_PROPERTY) == guid: + config.read(os.path.join(bundle_root, dir, file_name)) + if config.get(INI_METADATA_SECTION, INI_GUID_PROPERTY) == self.Guid: path = os.path.join(bundle_root, self.Guid) return path @@ -620,25 +612,24 @@ class TutorialBundler: logging.debug("**************** Error : GUID not found") raise KeyError - def write_fsm(self, fsm, guid=None): + def write_fsm(self, fsm): """ Save fsm to disk. If a GUID parameter is provided, the existing GUID is located in the .ini files in the store root and bundle root and the corresponding FSM is/are created or overwritten. If the GUID is not found, an exception occur. - """ - + """ + 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) - serializer.save_fsm(fsm, xml_filename, store_root) - - + + serializer = XMLSerializer() + path = os.path.join(self.Path, "meta.ini") + config.read(path) + xml_filename = config.get(INI_METADATA_SECTION, INI_XML_FSM_PROPERTY) + serializer.save_fsm(fsm, xml_filename, self.Path) + + def add_resources(self, typename, file): """ Add ressources to metadata. -- cgit v0.9.1