Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/tutorius/translator.py
blob: d0504beda7abbebafb4f2b0e783427f42c76c04e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# 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 os
import logging
import copy as copy_module

logger = logging.getLogger("ResourceTranslator")

from .properties import *
from .vault import Vault 

class ResourceTranslator(object):
    """
    Handles the conversion of resource properties into file
    properties before action execution. This class works as a decorator
    to the ProbeManager class, as it is meant to be a transparent layer
    before sending the action to execution.

    An architectural note : every different type of translation should have its
    own method (translate_resource, etc...), and this function must be called
    from the translate method, under the type test. The translate_* method
    must take in the input property and give the output property that should
    replace it. 
    """

    def __init__(self, probe_manager, tutorial_id):
        """
        Creates a new ResourceTranslator for the given tutorial. This
        translator is tasked with replacing resource properties of the
        incoming action into actually usable file properties pointing
        to the correct resource file. This is done by querying the vault
        for all the resources and creating a new file property from the
        returned path.
        
        @param probe_manager The probe manager to decorate
        @param tutorial_id The ID of the current tutorial
        """
        self._probe_manager = probe_manager
        self._tutorial_id = tutorial_id
        
        self._translation_mapping = {}

    def translate_resource(self, res_value):
        """
        Replace the TResourceProperty in the container by their
        runtime-defined file equivalent. Since the resources are stored
        in a relative manner in the vault and that only the Vault knows
        which is the current folder for the current tutorial, it needs
        to transform the resource identifier into the absolute path for
        the process to be able to use it properly.

        @param res_prop The resource property's value to be translated
        @return The TFileProperty corresponding to this resource, containing
                an absolute path to the resource
        """
        # We need to replace the resource by a file representation
        filepath = Vault.get_resource_path(self._tutorial_id, res_value)
        logger.debug("ResourceTranslator :: Matching resource %s to file %s" % (res_value, filepath))

        # Create the new file representation
        file_prop = TFileProperty(filepath)

        return file_prop

    def translate(self, prop_container):
        """
        Applies the required translation to be able to send the container to an
        executing endpoint (like a Probe). For each type of property that
        requires it, there is translation function that will take care of
        mapping the property to its executable form.

        This function does not return anything, but its post-condition is
        that all the properties of the input container have been replaced
        by their corresponding executable versions.

        An example of translation is taking a resource (a relative path to
        a file under the tutorial folder) and transforming it into a file
        (a full absolute path) to be able to load it when the activity receives
        the action.

        @param prop_container The property container in which we want to
                        replace all the resources for file properties and
                        to recursively do so for addon and addonlist
                        properties.
        """
        for propname in prop_container.get_properties():
            prop_value = getattr(prop_container, propname)
            prop_type = getattr(type(prop_container), propname).type

            # If the property is a resource, then we need to query the
            # vault to create its correspondent
            if prop_type == "resource":
                # Apply the translation
                file_prop = self.translate_resource(prop_value)
                # Set the property with the new value
                prop_container.replace_property(propname, file_prop)

            # If the property is an addon, then its value IS a
            # container too - we need to translate it
            elif prop_type == "addon":
                # Translate the sub properties
                self.translate(prop_value)

            # If the property is an addon list, then we need to translate all
            # the elements of the list
            elif prop_type == "addonlist":
                # Now, for each sub-container in the list, we will apply the
                # translation processing. This is done by popping the head of
                # the list, translating it and inserting it back at the end.
                for index in range(0, len(prop_value)):
                    # Pop the head of the list
                    container = prop_value[0]
                    del prop_value[0]
                    # Translate the sub-container
                    self.translate(container)

                    # Put the value back in the list
                    prop_value.append(container)
                # Change the list contained in the addonlist property, since
                # we got a copy of the list when requesting it
                prop_container.replace_property(propname, prop_value)
                
    ### ProbeManager interface for decorator ###

    ## Unchanged functions ##
    def setCurrentActivity(self, activity_id):
        self._probe_manager.currentActivity = activity_id 

    def getCurrentActivity(self):
        return self._probe_manager.currentActivity

    currentActivity = property(fget=getCurrentActivity, fset=setCurrentActivity)
    def attach(self, activity_id):
        self._probe_manager.attach(activity_id)

    def detach(self, activity_id):
        self._probe_manager.detach(activity_id)

    def subscribe(self, event, callback):
        return self._probe_manager.subscribe(event, callback)

    def unsubscribe(self, address):
        return self._probe_manager.unsubscribe(address)

    def register_probe(self, process_name, unique_id):
        self._probe_manager.register_probe(process_name, unique_id)

    def unregister_probe(self, unique_id):
        self._probe_manager.unregister_probe(unique_id)
    
    def get_registered_probes_list(self, process_name=None):
        return self._probe_manager.get_registered_probes_list(process_name)

    ## Decorated functions ##
    def install(self, action, callback, block=False):
        # Make a new copy of the action that we want to install,
        # because translate() changes the action and we
        # don't want to modify the caller's action representation
        new_action = copy_module.deepcopy(action)
        # Execute the replacement
        self.translate(new_action)

        # Send the new action to the probe manager
        action_address = self._probe_manager.install(new_action, callback, block)

        # Remember the address
        self._translation_mapping[action_address] = new_action

        return action_address

    def update(self, action_address, newaction, block=False):
        # TODO : Repair this as it currently doesn't work.
        # Actions are being copied, then translated in install(), so they
        # won't be addressable via the same object that is in the Tutorial
        # Runner.
        translated_new_action = copy_module.deepcopy(newaction)
        self.translate(translated_new_action)

        self._translation_mapping[action_address] = translated_new_action

        return self._probe_manager.update(action_address, translated_new_action, block)

    def uninstall(self, action_address, block=False):
        return_value = self._probe_manager.uninstall(action_address, block)

        if self._translation_mapping.has_key(action_address):
            del self._translation_mapping[action_address]

        return return_value