From eb03421c5dd132fb79ac12a436efefbfb0287544 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Savard Date: Mon, 30 Mar 2009 17:38:36 +0000 Subject: Core (bug 341760) : TutoSerializer, 2nd version (fonction save_tutorial added) + Tests : added serializertests.py --- diff --git a/src/sugar/activity/activity.py b/src/sugar/activity/activity.py index 21e38f6..3e2d3d4 100644 --- a/src/sugar/activity/activity.py +++ b/src/sugar/activity/activity.py @@ -77,6 +77,7 @@ from sugar.datastore import datastore from sugar.session import XSMPClient from sugar import wm from sugar.tutorius.services import ObjectStore +from sugar.tutorius.tutoserialize import TutoSerializer _ = lambda msg: gettext.dgettext('sugar-toolkit', msg) @@ -126,11 +127,21 @@ class ActivityToolbar(gtk.Toolbar): if hasattr(self._activity,"get_tutorials") and hasattr(self._activity.get_tutorials,"__call__"): self.tutorials = ToolComboBox(label_text=_('Tutorials:')) self.tutorials.combo.connect('changed', self.__tutorial_changed_cb) - tutorials = self._activity.get_tutorials() + # Get tutorial list by file + logging.debug("************************************ before creating serialize") + serialize = TutoSerializer() + logging.debug("************************************ before calling load_tuto_list()") + + #tutorials = self._activity.get_tutorials() + if getattr(self._activity,"_tutorials",None) is None: + tutorials = serialize.load_tuto_list() + self._current_tutorial = None if tutorials: for key, tutorial in tutorials.items(): - self.tutorials.combo.append_item(key, tutorial.name) + # self.tutorials.combo.append_item(key, _(tutorial.name)) + self.tutorials.combo.append_item(key, _(tutorial)) + self.insert(self.tutorials, -1) self.tutorials.show() @@ -196,16 +207,34 @@ class ActivityToolbar(gtk.Toolbar): """ Callback for tutorial combobox item change """ - model = combo.get_model() - it = combo.get_active_iter() - (key, ) = model.get(it, 0) - tutorial = self._activity.get_tutorials().get(key,None) - if not tutorial is None: + logging.debug("************ function __tutorial_changed_cb called") + serialize = TutoSerializer() + + if self._current_tutorial: + self._current_tutorial.detach() + + model = self.tutorials.combo.get_model() + it = self.tutorials.combo.get_active_iter() + (key,) = model.get(it, 0) + + #Load and build chosen tutorial from Pickle file + logging.debug("****************** before tuto build") +## tutorials = self._activity.get_tutorials() + tuto = serialize.build_tutorial(key) + self._activity._tutorials = tuto + logging.debug("****************** after tuto build") +## tutorial = self._activity.get_tutorials().get(key,None) + tutorial = tuto.get(key, None) + + if not getattr(self._activity,"_tutorials",None) is None: if not self._current_tutorial is None: self._current_tutorial.detach() + self._current_tutorial = tutorial + logging.debug(" *************** try to attach tuto") self._current_tutorial.attach(self._activity) + def __keep_clicked_cb(self, button): self._activity.copy() diff --git a/src/sugar/tutorius/Makefile.am b/src/sugar/tutorius/Makefile.am index 02f832b..a74b796 100644 --- a/src/sugar/tutorius/Makefile.am +++ b/src/sugar/tutorius/Makefile.am @@ -1,5 +1,3 @@ -SUBDIRS = uam - sugardir = $(pythondir)/sugar/tutorius sugar_PYTHON = \ __init__.py \ @@ -12,5 +10,4 @@ sugar_PYTHON = \ overlayer.py \ editor.py \ linear_creator.py \ - constraints.py \ - properties.py + bundler.py diff --git a/src/sugar/tutorius/services.py b/src/sugar/tutorius/services.py index 467eca0..9ed2e50 100644 --- a/src/sugar/tutorius/services.py +++ b/src/sugar/tutorius/services.py @@ -66,3 +66,4 @@ class ObjectStore(object): tutorial = property(fset=set_tutorial,fget=get_tutorial,doc="tutorial") __doc__ = __ObjectStore.__doc__ + diff --git a/src/sugar/tutorius/tests/serializertests.py b/src/sugar/tutorius/tests/serializertests.py new file mode 100644 index 0000000..7a31602 --- /dev/null +++ b/src/sugar/tutorius/tests/serializertests.py @@ -0,0 +1,121 @@ +# Copyright (C) 2009, Tutorius.org +# Copyright (C) 2009, Jean-Christophe Savard +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +""" +Serialization Tests + +This module contains all the tests that pertain to the usage of the Tutorius +Serializer object. This means testing saving a tutorial dictionary to a .tml +file, loading the list of tutorials for this activity and building chosen +tutorial. +""" + +import unittest + +import logging +import linecache +import os +import cPickle as pickle + +from sugar.tutorius import gtkutils, overlayer +from sugar.tutorius.core import Tutorial, State, FiniteStateMachine +from sugar.tutorius.actions import DialogMessage, OnceWrapper, BubbleMessage +from sugar.tutorius.filters import GtkWidgetEventFilter, TimerEvent +from sugar.tutorius.tutoserialize import TutoSerializer + + +# Helper classes to help testing + + + + +class SerializerTest(unittest.TestCase): + """ + This class has to test the Serializer methods as well as the expected + functionality. + """ + + def test_pickle_integrity(self): + """ + Validates content is uncorrupted trough a pickle file save/load. + """ + + # Sample valid FSM dict + sampleDict = { + "INIT":State("INIT", + action_list=[ + OnceWrapper(BubbleMessage(message="Welcome to the text editor tutorial!\n\n Click on the canvas and type a letter.", pos=[100,100], tailpos=[-10,-20])), + ], + event_filter_list=[ + GtkWidgetEventFilter("TEXT","0.0.0.1.0.0.0","key-press-event"), + TimerEvent("LOST",15), + ], + ), + "LOST":State("LOST", + action_list=[BubbleMessage("Click in the canvas and type on your keyboard", [400, 400]),], + event_filter_list=[ + GtkWidgetEventFilter("TEXT","0.0.0.1.0.0.0","key-press-event"), + TimerEvent("INIT",5), + ], + ), + "TEXT":State("TEXT", + action_list=[OnceWrapper(BubbleMessage(" You can type more letters if you want!\n\n" + + "To proceed to the next step, select your text.\n\n Click and drag over the text!", [200,150])),], + event_filter_list=[ + GtkWidgetEventFilter("SELECTED","0.0.0.1.0.0","text-selected"), + ], + ), + } + + testpath = "/tmp/testdata/" + + # Create testdata/ folder if no exists + if not os.path.exists(testpath): + os.mkdir(testpath) + + serialize = TutoSerializer() + + # Make the class believe the test is in a activity path + os.environ["SUGAR_ACTIVITY_ROOT"] = testpath + + fsm = FiniteStateMachine("Test", state_dict=sampleDict) + + serialize.save_tutorial("Test", "Test", fsm, "serializeTest") + + fileDict = serialize.load_tuto_list() + + for filekey, tutorial in fileDict.items(): + if filekey == "Test": + reformedTuto = serialize.build_tutorial(filekey) + + reformedfsm = reformedTuto.get("Test").state_machine + + #Tests + assert reformedfsm._states.get("INIT").name == fsm._states.get("INIT").name, \ + 'FSM underlying dictionary differ from original to pickled/reformed one' + assert reformedfsm._states.get("LOST").name == fsm._states.get("LOST").name, \ + 'FSM underlying dictionary differ from original to pickled/reformed one' + assert reformedfsm._states.get("TEXT").name == fsm._states.get("TEXT").name, \ + 'FSM underlying dictionary differ from original to pickled/reformed one' + + + os.remove(testpath + "serializeTest.tml") + os.rmdir(testpath) + os.rmdir("/tmp") + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/src/sugar/tutorius/tutoserialize.py b/src/sugar/tutorius/tutoserialize.py new file mode 100644 index 0000000..ebd17c6 --- /dev/null +++ b/src/sugar/tutorius/tutoserialize.py @@ -0,0 +1,143 @@ +# Copyright (C) 2009, Tutorius.org +# Copyright (C) 2009, Jean-Christophe Savard +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" +-Save and load FSM Dictionnary to file in $SUGAR_ACTIVITY_ROOT/data/ +""" + +import logging +import linecache +import os +import cPickle as pickle + +from sugar.tutorius import gtkutils, overlayer +from sugar.tutorius.core import Tutorial, State, FiniteStateMachine +from sugar.tutorius.actions import DialogMessage, OnceWrapper, BubbleMessage +from sugar.tutorius.filters import GtkWidgetEventFilter, TimerEvent + +class TutoSerializer: + """ + Serializer is a class that provide serializing and file reading and + writing to disk services for the FSM Dictionary used in the tutorials. + """ + def __init__(self): + """ + TutoSerializer constructor (empty) + """ + pass + + + def load_tuto_list(self): + """ + Load the tutorial key/name list for this activity from .tml files. + Return the key/name dictionary + """ + # TODO : Temp path; decide how .tml files will be distrribued in file + # architecture + + path = os.getenv("SUGAR_ACTIVITY_ROOT") + "/data/" + logging.debug("*********** Path of /data/ folder of activity : " + path) + + # Create /data/ folder if no exists + if not os.path.exists(path): + os.mkdir(path) + logging.debug("************* Creating data folder") + + tutoKeyName = {} + + # iterate for each .tml file in the activity /data folder + for file_name in os.listdir(path): + + if file_name.endswith(".tml"): + logging.debug("************** .tml file found : " + file_name) + + # TODO : Filter for just current activity files + # (design convention and/or filter xxxxx.yyyyyy key ? ) + + # Get the key line (always 1st line of .tml file) + key_line = linecache.getline(path + file_name, 1) + key_line = key_line.split("\n")[0] + # Get the name line (always 2nd line of .tml file) + name_line = linecache.getline(path + file_name, 2) + name_line = name_line.split("\n")[0] + # Create dictionary + tutoKeyName[key_line.split("--KEY::")[1]] = \ + name_line.split("--NAME::")[1] + + +## tutoKeyName = {} +## tutoKeyName["Writus.CopyPasteStyle"] = "Copy-paste and style" + + return tutoKeyName + + def build_tutorial(self,key): + """ + Load the chosen dictionnary from Pickle file on disk, then build and + return the corresponding tutorial. + TODO : Decode pickle to prevent broswer corruption + """ + path = os.getenv("SUGAR_ACTIVITY_ROOT") + "/data/" + logging.debug("************ Path of /data/ folder of activity : " \ + + path) + logging.debug("************ User key = " + key) + + # iterate for each .tml file in the activity /data folder + for file_name in os.listdir(path): + + if file_name.endswith(".tml"): + + logging.debug("************ found .tml file : " + file_name) + key_line = linecache.getline(path + file_name, 1) + key_line = key_line.split("\n")[0] + fileKey = key_line.split("--KEY::")[1] + logging.debug("************ fileKey = " + fileKey) + if key == fileKey: + logging.debug("************ Key : " + key + \ + " = fileKey : " + fileKey) + tml = file(path + file_name, "r") + str = tml.read() + pick = str.split("--PICKLE::")[1] + + fsm = pickle.loads(pick) + tuto = {key:Tutorial(key,fsm)} + + return tuto + + + def save_tutorial(self, key, name, fsm, filename): + """ + Save the dictionnary given in parameter to a filename.tml file with a + key and a name. + + TODO : TEST : In developpement, untested. + TODO : Encode pickle to prevent broswer corruption + """ + + path = os.getenv("SUGAR_ACTIVITY_ROOT") + "/data/" + # Create /data/ folder if no exists + if not os.path.exists(path): + os.mkdir(path) + logging.debug("************* Creating data folder") + + # Save the dictionnary to .tml file + tutoSave = file(path + filename + ".tml", 'w') + str = "--KEY::" + key + "\n--NAME::" + name + "\n--PICKLE::" + \ + pickle.dumps(fsm,0) + tutoSave.write(str) + tutoSave.close() + \ No newline at end of file -- cgit v0.9.1