From 749f6f51e10c536847a209fb1706583e2a6ad883 Mon Sep 17 00:00:00 2001 From: mike Date: Thu, 12 Nov 2009 13:54:11 +0000 Subject: Merge branch 'engineADT' of ../mainline Conflicts: tests/vaulttests.py tutorius/vault.py --- diff --git a/addons/bubblemessage.py b/addons/bubblemessage.py index 6572a6a..1ed1fe0 100644 --- a/addons/bubblemessage.py +++ b/addons/bubblemessage.py @@ -48,7 +48,7 @@ class BubbleMessage(Action): self._bubble = None self._speaker = None - def do(self): + def do(self, **kwargs): """ Show the dialog """ diff --git a/addons/changecolor.py b/addons/changecolor.py new file mode 100644 index 0000000..460da32 --- /dev/null +++ b/addons/changecolor.py @@ -0,0 +1,127 @@ +# Copyright (C) 2009, Tutorius.org +# +# 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 + +import time + +import gobject + +import gtk, gtk.gdk + +from sugar.tutorius.actions import Action +from sugar.tutorius.properties import TUAMProperty +from sugar.tutorius.gtkutils import find_widget + +from sugar import profile + +# for easy profile access +xo_line_color = profile.get_color().get_stroke_color() +xo_fill_color = profile.get_color().get_fill_color() + +class ChangeColor(Action): + """ + ChangeColorEvent + """ + # widget address property + widaddr = TUAMProperty("0") + + # set timeout + timeout = 500 + + def __init__(self, widaddr=None): + """Constructor - Change a widget color + @param widaddr: the widget for which you want to change the color (UAM) + """ + Action.__init__(self) + + if widaddr: self.widaddr = widaddr + + self.init_style = None + self._new_color = None + + self.wid = None + + self._handler_id = None + + def do(self, **kwargs): + """ + do. + Change the color of the widaddr widget with the chosen color + """ + + if not "activity" in kwargs: + raise TypeError("activity argument is Mandatory") + + # get widget instance + self.wid = find_widget(kwargs["activity"], self.widaddr, ignore_errors=False) + + if not self.wid: + raise NameError("widget not found") + + # we have to get the initial color in the sugar rc theme + self.init_style = self.wid.rc_get_style() + + # define new color + self._new_color = gtk.gdk.color_parse(xo_fill_color) + + # set change color timeout (flash) + self._handler_id = gobject.timeout_add(ChangeColor.timeout, self._timeout_cb) + + def undo(self): + """ + Remove timer and go back to the original color + """ + + if self._handler_id: + try: + #remove the timer + gobject.source_remove(self._handler_id) + except: + pass + + # modify bg color (go back to original color) + self.wid.modify_bg(gtk.STATE_NORMAL, self.init_style.bg[gtk.STATE_NORMAL]) + self.wid.modify_bg(gtk.STATE_PRELIGHT, self.init_style.bg[gtk.STATE_PRELIGHT]) + self.wid.modify_bg(gtk.STATE_ACTIVE, self.init_style.bg[gtk.STATE_ACTIVE]) + self.wid.modify_bg(gtk.STATE_INSENSITIVE, self.init_style.bg[gtk.STATE_INSENSITIVE]) + + def _timeout_cb(self): + """ + _timeout_cb triggers the eventfilter callback. + """ + + if self.wid.rc_get_style().bg[gtk.STATE_NORMAL] == self._new_color: + # modify bg color (go back to original color) + self.wid.modify_bg(gtk.STATE_NORMAL, self.init_style.bg[gtk.STATE_NORMAL]) + self.wid.modify_bg(gtk.STATE_PRELIGHT, self.init_style.bg[gtk.STATE_PRELIGHT]) + self.wid.modify_bg(gtk.STATE_ACTIVE, self.init_style.bg[gtk.STATE_ACTIVE]) + self.wid.modify_bg(gtk.STATE_INSENSITIVE, self.init_style.bg[gtk.STATE_INSENSITIVE]) + else: + # modify bg color (to new color) + self.wid.modify_bg(gtk.STATE_NORMAL, self._new_color) + self.wid.modify_bg(gtk.STATE_PRELIGHT, self._new_color) + self.wid.modify_bg(gtk.STATE_ACTIVE, self._new_color) + self.wid.modify_bg(gtk.STATE_INSENSITIVE, self._new_color) + + return True + +__action__ = { + "name" : "ChangeColor", + "display_name" : "Change widget color", + "icon" : "message-bubble", + "class" : ChangeColor, + "mandatory_props" : ["widaddr"] +} + diff --git a/addons/clickaction.py b/addons/clickaction.py index 88c5519..071af28 100644 --- a/addons/clickaction.py +++ b/addons/clickaction.py @@ -29,7 +29,7 @@ class ClickAction(Action): Action.__init__(self) self.widget = widget - def do(self): + def do(self, **kwargs): """ click the widget """ diff --git a/addons/dialogmessage.py b/addons/dialogmessage.py index 9250693..fad6d2c 100644 --- a/addons/dialogmessage.py +++ b/addons/dialogmessage.py @@ -36,7 +36,7 @@ class DialogMessage(Action): self.message = message if position: self.position = position - def do(self): + def do(self, **kwargs): """ Show the dialog """ diff --git a/addons/disablewidget.py b/addons/disablewidget.py index fd88303..b3d9ae6 100644 --- a/addons/disablewidget.py +++ b/addons/disablewidget.py @@ -30,7 +30,7 @@ class DisableWidgetAction(Action): self.target = target self._widget = None - def do(self): + def do(self, **kwargs): """Action do""" os = ObjectStore() if os.activity: diff --git a/addons/messagebuttonnext.py b/addons/messagebuttonnext.py new file mode 100644 index 0000000..74ce1bb --- /dev/null +++ b/addons/messagebuttonnext.py @@ -0,0 +1,171 @@ +# Copyright (C) 2009, Tutorius.org +# +# 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 + +import gtk, gtk.gdk + +from sugar.tutorius.filters import EventFilter +from sugar.tutorius.properties import TStringProperty, TArrayProperty +from sugar.tutorius import overlayer + +from sugar import profile + +xo_line_color_profile = profile.get_color().get_stroke_color() +xo_fill_color_profile = profile.get_color().get_fill_color() + +class MessageButtonNext(EventFilter): + """ + MessageButtonNext + """ + # set message + message = TStringProperty("Message") + + # create the position as an array of fixed-size 2 + position = TArrayProperty((0,0), 2, 2) + + # set padding + padding = 40 + + def __init__(self, message=None, position=None, center_pos=False): + """Constructor. + + @param message message to display + @param position message position + """ + super(MessageButtonNext,self).__init__() + + if position: + self.position = position + else: + # TODO: to be removed when creator supports editing properties on events + self.position = (300, 200) + + if message: + self.message = message + + self.overlay = None + self.msgnext = None + + def install_handlers(self, callback, **kwargs): + """install_handlers creates the message button next and shows it""" + super(MessageButtonNext,self).install_handlers(callback, **kwargs) + + if not "activity" in kwargs: + raise TypeError("activity argument is Mandatory") + + # get activity instance + self.activity = kwargs["activity"] + + # get or inject overlayer + self.overlay = self.activity._overlayer + + if not self.overlay: + self.overlay = self.activity._overlayer + + btntext = "NEXT" + + self.msgnext = MsgNext(text=self.message,btntext=btntext) + self.msgnext._btnnext.connect("clicked", self.btnnext_clicked) + + # add space around minimum need size + wid_width, wid_height = self.msgnext.size_request() + self.msgnext.set_size_request(wid_width+MessageButtonNext.padding, wid_height+MessageButtonNext.padding) + + # set position + x, y = self.position + + self.msgnext.show() + self.overlay.put(self.msgnext, x, y) + self.overlay.queue_draw() + + def remove_handlers(self): + """remove handler removes the message button next""" + super(MessageButtonNext,self).remove_handlers() + + if self.msgnext: + self.msgnext.destroy() + self.msgnext = None + + def btnnext_clicked(self, widget): + self.do_callback() + +__event__ = { + "name" : "MessageButtonNext", + "display_name" : "Message button next", + "icon" : "message-bubble", + "class" : MessageButtonNext, + "mandatory_props" : ["message"] +} + +class MsgNext(gtk.EventBox): + """ + Create an EventBox + """ + def __init__(self, text, btntext): + """ + Creates an Event Box + """ + gtk.EventBox.__init__(self) + + self.message = text + self.btnmessage = btntext + + self.set_visible_window(True) + + # create a vbox + self.box = gtk.VBox() + + # create a label (set message to display) + self._label = gtk.Label() + self._text = "%s" % self.message + self._label.set_markup(self._text) + self._label.set_line_wrap(True) + + self._colortext = gtk.gdk.color_parse("white") + self._label.modify_fg(gtk.STATE_NORMAL, self._colortext) + self._label.modify_fg(gtk.STATE_PRELIGHT, self._colortext) + self._label.modify_fg(gtk.STATE_ACTIVE, self._colortext) + self._label.modify_fg(gtk.STATE_INSENSITIVE, self._colortext) + + self._label.show() + + # create a hbox (holding button) + self._hbox = gtk.HBox() + + # create a button inside hbox + self._btnnext = gtk.Button(self.btnmessage) + + self._colorbtn = gtk.gdk.color_parse(xo_fill_color_profile) + + self._btnnext.modify_bg(gtk.STATE_NORMAL, self._colorbtn) + self._btnnext.modify_bg(gtk.STATE_PRELIGHT, self._colorbtn) + self._btnnext.modify_bg(gtk.STATE_ACTIVE, self._colorbtn) + + self._btnnext.show() + + self._hbox.pack_end(self._btnnext, expand=False) + + self._hbox.show() + + self.box.pack_start(self._label, expand=True) + self.box.pack_start(self._hbox, expand=True) + + self.box.show() + + self.add(self.box) + + self._colormsgnext = gtk.gdk.color_parse(xo_fill_color_profile) + self.modify_bg(gtk.STATE_NORMAL, self._colormsgnext) + diff --git a/addons/oncewrapper.py b/addons/oncewrapper.py index 5db3b60..c404ae4 100644 --- a/addons/oncewrapper.py +++ b/addons/oncewrapper.py @@ -32,7 +32,7 @@ class OnceWrapper(Action): self._need_undo = False self.action = action - def do(self): + def do(self, **kwargs): """ Do the action only on the first time """ diff --git a/addons/readfile.py b/addons/readfile.py index 0d276b9..3cd41b6 100644 --- a/addons/readfile.py +++ b/addons/readfile.py @@ -34,7 +34,7 @@ class ReadFile(Action): if filename: self.filename=filename - def do(self): + def do(self, **kwargs): """ Perform the action, call read_file on the activity """ diff --git a/addons/widgetidentifyaction.py b/addons/widgetidentifyaction.py index 3df244b..c44964b 100644 --- a/addons/widgetidentifyaction.py +++ b/addons/widgetidentifyaction.py @@ -24,7 +24,7 @@ class WidgetIdentifyAction(Action): self.activity = None self._dialog = None - def do(self): + def do(self, **kwargs): os = ObjectStore() if os.activity: self.activity = os.activity diff --git a/tests/storetests.py b/tests/storetests.py index 1752fe6..0c36973 100644 --- a/tests/storetests.py +++ b/tests/storetests.py @@ -15,6 +15,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import unittest +import uuid from tests.utils import skip, catch_unimplemented import random @@ -79,7 +80,6 @@ class StoreProxyLoginTest(unittest.TestCase): if session_id is not None: self.store.close_session() - @catch_unimplemented def test_get_session_id(self): @@ -99,13 +99,14 @@ class StoreProxyLoginTest(unittest.TestCase): 'name': 'newtut', 'summary': 'This is a tutorial', 'filename': 'test.xml', + 'guid': str(uuid.uuid1()), 'homepage': 'http://google.com', 'version': '1', 'cat1': '17', 'cat2': '18', 'cat3': '' } - assert self.store.publish('This should be a real tutorial...', tutorial_info) + assert self.store.publish('This should be a real tutorial...', tutorial_info) != -1 @catch_unimplemented def test_unpublish(self): diff --git a/tests/vaulttests.py b/tests/vaulttests.py index ca61225..0af2d65 100644 --- a/tests/vaulttests.py +++ b/tests/vaulttests.py @@ -260,10 +260,10 @@ class VaultInterfaceTest(unittest.TestCase): # (so we can specifiy that we want only the metadata for this particular tutorial - def test_add_delete_get_path_ressource(self): + def test_add_delete_get_path_resource(self): """ - This test verify that the vault interface function add_ressource succesfully add ressource in the vault - and return the new ressource id. It also test the deletion of the ressource. + This test verify that the vault interface function add_resource succesfully add resource in the vault + and return the new resource id. It also test the deletion of the resource. """ # Path of an image file in the test folder image_path = os.path.join(os.getcwd(), 'tests', 'ressources', 'icon.svg') @@ -279,22 +279,23 @@ class VaultInterfaceTest(unittest.TestCase): ressource_id = Vault.add_resource(self.save_test_guid, image_path) # Check that the image file is now in the vault - assert os.path.isfile(os.path.join(tuto_path, 'ressources', ressource_id)), 'image file not found in vault' + assert os.path.isfile(os.path.join(tuto_path, 'resources', resource_id)), 'image file not found in vault' - # Check if get_ressource_path Vault interface function is working - vault_path = Vault.get_resource_path(self.save_test_guid, ressource_id) + # Check if get_resource_path Vault interface function is working + vault_path = Vault.get_resource_path(self.save_test_guid, resource_id) assert os.path.isfile(vault_path), 'path returned is not a file' basename, extension = os.path.splitext(vault_path) assert extension == '.svg', 'wrong file path have been returned' - - # Delete the ressource - Vault.delete_resource(self.save_test_guid, ressource_id) + # Delete the resource + Vault.delete_resource(self.save_test_guid, resource_id) - # Check that the ressource is not in the vault anymore - assert os.path.isfile(os.path.join(tuto_path, 'ressources', ressource_id)) == False, 'image file found in vault when it should have been deleted.' - + # Check that the resource is not in the vault anymore + assert os.path.isfile(os.path.join(tuto_path, 'resources', resource_id)) == False, 'image file found in vault when it should have been deleted.' + + + def tearDown(self): folder = os.path.join(os.getenv("HOME"),".sugar", 'default', 'tutorius', 'data'); for file in os.listdir(folder): @@ -445,7 +446,7 @@ class TutorialBundlerTests(unittest.TestCase): This module contains all the tests for the storage mecanisms for tutorials This mean testing saving and loading tutorial, .ini file management and - adding ressources to tutorial + adding resources to tutorial """ def setUp(self): diff --git a/tutorius/TProbe.py b/tutorius/TProbe.py index 08c9651..465b24f 100644 --- a/tutorius/TProbe.py +++ b/tutorius/TProbe.py @@ -130,8 +130,8 @@ class TProbe(dbus.service.Object): if action._props: action._props.update(loaded_action._props) - action.do() - + action.do(activity=self._activity) + return address @dbus.service.method("org.tutorius.ProbeInterface", diff --git a/tutorius/apilib/__init__.pyc b/tutorius/apilib/__init__.pyc deleted file mode 100644 index bd4346b..0000000 --- a/tutorius/apilib/__init__.pyc +++ /dev/null Binary files differ diff --git a/tutorius/apilib/httplib2/__init__.pyc b/tutorius/apilib/httplib2/__init__.pyc deleted file mode 100644 index e5f8ebe..0000000 --- a/tutorius/apilib/httplib2/__init__.pyc +++ /dev/null Binary files differ diff --git a/tutorius/apilib/httplib2/iri2uri.pyc b/tutorius/apilib/httplib2/iri2uri.pyc deleted file mode 100644 index 879e719..0000000 --- a/tutorius/apilib/httplib2/iri2uri.pyc +++ /dev/null Binary files differ diff --git a/tutorius/apilib/mimeTypes.pyc b/tutorius/apilib/mimeTypes.pyc deleted file mode 100644 index 35ef2b2..0000000 --- a/tutorius/apilib/mimeTypes.pyc +++ /dev/null Binary files differ diff --git a/tutorius/apilib/restful_lib.pyc b/tutorius/apilib/restful_lib.pyc deleted file mode 100644 index 5b06765..0000000 --- a/tutorius/apilib/restful_lib.pyc +++ /dev/null Binary files differ diff --git a/tutorius/engine.py b/tutorius/engine.py index e77a018..b0a49a8 100644 --- a/tutorius/engine.py +++ b/tutorius/engine.py @@ -4,17 +4,100 @@ from jarabe.model import shell from sugar.bundle.activitybundle import ActivityBundle from .vault import Vault +from .TProbe import ProbeManager +from .dbustools import save_args + + +class TutorialRunner(object): + """ + Driver for the execution of one tutorial + """ + def __init__(self, tutorial, probeManager): + """Constructor + @param tutorial Tutorial to execute + @param probeManager probeManager to use + """ + self._tutorial = tutorial + self._pM = probeManager + + #State + self._state = None + self._sEvents = set() #Subscribed Events + + #Cached objects + self._actions = {} + + #Temp FIX until event/actions have an activity id + self._activity_id = None + + #Temp FIX until event, actions have an activity id + def setCurrentActivity(self): + self._pM.currentActivity = self._activity_id + + def start(self): + self.setCurrentActivity() #Temp Hack until activity in events/actions + self.setState(self._tutorial.INIT) + + def stop(self): + self.setCurrentActivity() #Temp Hack until activity in events/actions + self.setState(self._tutorial.END) + self._teardownState() + self._state = None + + def _handleEvent(self, next_state, event): + #FIXME sanity check + self.setState(next_state) + + def _teardownState(self): + if self._state is None: + #No state, no teardown + return + + #Clear the current actions + for action in self._actions.values(): + self._pM.uninstall(action) + self._actions = {} + + #Clear the EventFilters + for event in self._sEvents: + self._pM.unsubscribe(event) + self._sEvents.clear() + + def _setupState(self): + if self._state is None: + raise RuntimeError("Attempting to setupState without a state") + + self._actions = self._tutorial.get_action_dict(self._state) + transitions = self._tutorial.get_transition_dict(self._state) + for (event, next_state) in transitions.values(): + self._sEvents.add(self._pM.subscribe(event, save_args(self._handleEvent, next_state))) + for action in self._actions.values(): + self._pM.install(action) + + def setState(self, state_name): + self.setCurrentActivity() #Temp Hack until activity in events/actions + if state_name == self._state: + #Nothing to do + return + + self._teardownState() + self._state = state_name + self._setupState() + class Engine: """ Driver for the execution of tutorials """ - def __init__(self): - # FIXME Probe management should be in the probe manager + def __init__(self, probeManager=None): + """Constructor + @param probeManager (optional) ProbeManager instance to use + """ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) #FIXME shell.get_model() will only be useful in the shell process self._shell = shell.get_model() + self._probeManager = probeManager or ProbeManager() self._tutorial = None def launch(self, tutorialID): @@ -22,25 +105,33 @@ class Engine: @param tutorialID unique tutorial identifier used to retrieve it from the disk """ if self._tutorial: - self._tutorial.detach() - self._tutorial = None + self.stop() + + self._tutorial = TutorialRunner(Vault.loadTutorial(tutorialID), self._probeManager) #Get the active activity from the shell activity = self._shell.get_active_activity() - self._tutorial = Vault.loadTutorial(tutorialID) - #TProbes automatically use the bundle id, available from the ActivityBundle bundle = ActivityBundle(activity.get_bundle_path()) - self._tutorial.attach(bundle.get_bundle_id()) - def stop(self): + self._tutorial._activity_id = bundle.get_bundle_id() #HACK until we have activity id's in action/events + + self._tutorial.start() + + def stop(self, tutorialID=None): """ Stop the current tutorial """ - self._tutorial.detach() + if tutorialID is None: + logging.warning( + "stop() without a tutorialID will become deprecated") + self._tutorial.stop() self._tutorial = None - def pause(self): + def pause(self, tutorialID=None): """ Interrupt the current tutorial and save its state in the journal """ + if tutorialID is None: + logging.warning( \ + "pause() without a tutorialID will become deprecated") raise NotImplementedError("Unable to store tutorial state") diff --git a/tutorius/gtkutils.py b/tutorius/gtkutils.py index 1a9cb0f..c96a73f 100644 --- a/tutorius/gtkutils.py +++ b/tutorius/gtkutils.py @@ -33,7 +33,7 @@ def raddr_lookup(widget): return ".".join(name) -def find_widget(base, target_fqdn): +def find_widget(base, target_fqdn, ignore_errors=True): """Find a widget by digging into a parent widget's children tree @param base the parent widget @param target_fqdn fqdn-style target object name @@ -57,7 +57,9 @@ def find_widget(base, target_fqdn): try: obj = get_children(obj)[int(path.pop(0))] except: - break + if ignore_errors: + break + return None return obj diff --git a/tutorius/service.py b/tutorius/service.py index 8694cb5..11a94a5 100644 --- a/tutorius/service.py +++ b/tutorius/service.py @@ -38,7 +38,7 @@ class Service(dbus.service.Object): @param tutorialID unique tutorial identifier used to retrieve it from the disk """ if self._engine == None: - self._engine = Engine() + self._engine = Engine(self._probeMgr) self._engine.launch(tutorialID) @dbus.service.method(_DBUS_SERVICE_IFACE, diff --git a/tutorius/store.py b/tutorius/store.py index 81925ed..cf20dd0 100644 --- a/tutorius/store.py +++ b/tutorius/store.py @@ -323,27 +323,31 @@ class StoreProxy(object): headers = { 'X-API-Auth' : self.api_auth_key } response = self.conn.request_post(request_url, None, None, None, headers) - if self.helper.iserror(response): - return False + return -1 - return True + return tutorial_store_id # Otherwise, we want to publish a new tutorial if tutorial_info == None: - return False + return -1 request_url = "/%s/publish/" % (self.tutorius_api) headers = { 'X-API-Auth' : self.api_auth_key } response = self.conn.request_post(request_url, tutorial_info, tutorial, tutorial_info['filename'], headers) - + if self.helper.iserror(response): - return False + return -1 + + xml_response = minidom.parseString(response['body']) + + id_node = xml_response.getElementsByTagName("id")[0] + + id = id_node.getAttribute('value') - return True - + return id def unpublish(self, tutorial_store_id): """ diff --git a/tutorius/vault.py b/tutorius/vault.py index 9576de9..4d817ec 100644 --- a/tutorius/vault.py +++ b/tutorius/vault.py @@ -58,6 +58,7 @@ INI_XML_FSM_PROPERTY = "fsm_filename" INI_VERSION_PROPERTY = 'version' INI_FILENAME = "meta.ini" TUTORIAL_FILENAME = "tutorial.xml" +RESOURCES_FOLDER = 'resources' ###################################################################### # XML Tag names and attributes @@ -74,7 +75,6 @@ NODE_SUBCOMPONENTLIST = "listproperty" NAME_ATTR = "__name__" NEXT_STATE_ATTR = "__next_state__" START_STATE_ATTR = "__start_state__" -RESSOURCES_FOLDER = 'ressources' class Vault(object): @@ -86,7 +86,7 @@ class Vault(object): given activity. @param activity_name the name of the activity associated with this tutorial. None means ALL activities - @param activity_vers the version number of the activity to find tutorail for. 0 means find for ANY version. If activity_name is None, version number is not used + @param activity_vers the version number of the activity to find tutorial for. 0 means find for ANY version. If activity_name is None, version number is not used @returns a map of tutorial {names : GUID}. """ # check both under the activity data and user installed folders @@ -250,6 +250,7 @@ class Vault(object): # Return tutorial list return tutorial_list + @staticmethod def loadTutorial(Guid): @@ -341,69 +342,70 @@ class Vault(object): else: return False - + @staticmethod def add_resource(tutorial_guid, file_path): """ - Returns a unique name for this resource composed from the original name of the file - and a suffix to make it unique ( ex: name_1.jpg ) and add it to the resources for the tutorial. + Add given resource file in the vault and returns a unique name for this resource + composed from the original name of the file and a suffix to make it unique + ( ex: name_1.jpg ). @param tutorial_guid The guid of the tutorial - @param file_path the file path of the ressource to add - @returns the ressource_id of the ressource + @param file_path the file path of the resource to add + @returns the resource_id of the resource """ - + assert os.path.isfile(file_path) # Get the tutorial path bundler = TutorialBundler(tutorial_guid) tutorial_path = bundler.get_tutorial_path(tutorial_guid) # Get the file name - fname_splitted = file_path.rsplit('/') - file_name = fname_splitted[fname_splitted.__len__() - 1] + file_name = os.path.basename(file_path) + #fname_splitted = file_path.rsplit('/') + #file_name = fname_splitted[fname_splitted.__len__() - 1] base_name, extension = os.path.splitext(file_name) # Append unique name to file name file_name_appended = base_name + '_' + str(uuid.uuid1()) + extension - # Check if the ressource file already exists - new_file_path = os.path.join(tutorial_path, RESSOURCES_FOLDER, file_name_appended) + # Check if the resource file already exists + new_file_path = os.path.join(tutorial_path, RESOURCES_FOLDER, file_name_appended) if os.path.isfile(new_file_path) == False: - # Copy the ressource file in the vault - if os.path.isdir(os.path.join(tutorial_path, RESSOURCES_FOLDER)) == False: - os.makedirs(os.path.join(tutorial_path, RESSOURCES_FOLDER)) - assert os.path.isfile(file_path) + # Copy the resource file in the vault + if os.path.isdir(os.path.join(tutorial_path, RESOURCES_FOLDER)) == False: + os.makedirs(os.path.join(tutorial_path, RESOURCES_FOLDER)) shutil.copyfile(file_path, new_file_path) return file_name_appended @staticmethod - def delete_resource(tutorial_guid, ressource_id): + def delete_resource(tutorial_guid, resource_id): """ Delete the resource from the resources of the tutorial. @param tutorial_guid the guid of the tutorial - @param ressource_id the ressource id of the ressource to delete + @param resource_id the resource id of the resource to delete """ # Get the tutorial path bundler = TutorialBundler(tutorial_guid) tutorial_path = bundler.get_tutorial_path(tutorial_guid) - # Check if the ressource file exists - file_path = os.path.join(tutorial_path, RESSOURCES_FOLDER, ressource_id) + # Check if the resource file exists + file_path = os.path.join(tutorial_path, RESOURCES_FOLDER, resource_id) if os.path.isfile(file_path): - # Delete the ressource + # Delete the resource os.remove(file_path) else: - print('File not found, no delete took place') + logging.info('File not found, no delete took place') @staticmethod - def get_resource_path(tutorial_guid, ressource_id): + def get_resource_path(tutorial_guid, resource_id): """ Returns the absolute file path to the resourceID @param tutorial_guid the guid of the tutorial - @param ressource_id the ressource id of the ressource to find the path for - @returns the absolute path of the ressource file + @param resource_id the resource id of the resource to find the path for + @returns the absolute path of the resource file """ # Get the tutorial path bundler = TutorialBundler(tutorial_guid) tutorial_path = bundler.get_tutorial_path(tutorial_guid) - # Check if the ressource file exists - file_path = os.path.join(tutorial_path, RESSOURCES_FOLDER, ressource_id) + # Check if the resource file exists + file_path = os.path.join(tutorial_path, RESOURCES_FOLDER, resource_id) if os.path.isfile(file_path): return file_path else: -- cgit v0.9.1