Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/Experior.Activity/sugarbot.py
blob: 717ffedb465127e2e345ae9174dbf58540aea0ff (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
205
206
207
#!/usr/bin/env python
# encoding: utf-8
"""
sugarbot.py

This file is part of sugarbot.

sugarbot 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 3 of the License, or
(at your option) any later version.

sugarbot 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 sugarbot.  If not, see <http://www.gnu.org/licenses/>.
"""
import gobject
import gtk
import logging
import os
import sblog
import socket
import sys

from xmlrpclib          import ServerProxy

from sugar.activity     import activity
#from sugar.activity    import registry
from jarabe.model.bundleregistry import get_registry

from sbrpcserver        import sbRpcServer, proxyString

# For reading GDK.Event's
from sbgui import sbGUI

class sugarbot(activity.Activity):
    def __dynamicImport(self, fullname, path=None):
        """
        Handles emulation of the target Activity.

        Dynamically modifies the module-search path, and imports an object from
        a module.  Some fancy work is done to get that to work properly.
        @param fullname: The module to import, example calculate.Calculate.
        @param path: If necessary, the path to the imported module.
        """
        module      = None
        module_name = ""
        class_name  = ""

        if fullname is None:
            return self.__parentClass

        try:
            # <hack>=========================================================
            # This could probably be cleaned up or refactored into separate
            # functions.  However, the code is short enough that it is not
            # likely worth it.
            if path != None:
                sys.path.append(path)

            splitted_module     = fullname.rsplit('.', 1)

            if len(splitted_module) >= 2:
                module_name         = splitted_module[0]
                class_name          = splitted_module[1]

            if module_name is not None and class_name is not None and \
                len(module_name) > 0 and len(class_name) > 0:
                module              = __import__(module_name)

                for comp in module_name.split('.')[1:]:
                    module = getattr(module, comp)
            # </hack>========================================================
        except ImportError:
            raise ImportError, \
                "Error in sugarbot._dynamicImport(%s,%s)" % (fullname,path)

        finally:
            if hasattr(module, class_name):
                self.__parentClass = getattr(module, class_name)
            else:   # Failsafe
                self.__parentClass = activity.Activity

        return self.__parentClass

    def __cloneActivity(self,sugarHandle):
        """
        Starts the cloned activity, where self.__parentClass is the activity
        class.  This is performed by [1] inheriting from the parentClass,
        and [2] calling parentClass.__init__.
        """
        if self.__parentClass is None:
            raise AttributeError, "self.__parentClass not defined properly."

        else:
            sugarbot.__bases__ = (self.__parentClass,)
            self.__parentClass.__init__(self,sugarHandle)

    def __getActivityList(self):
        """
        Gets a list of activities from the registry.  Stores this list in
        self.__activityList.
        """
        # Prevent looking up all of the activities multiple times.
        if not hasattr(self,"__activityList"):
            #self.__activityList = registry.get_registry().get_activities()
            self.__activityList = get_registry()._bundles
            return self.__activityList

        # If we didn't get any activities, something went wrong.
        if len(self.__activityList) < 1:
            raise "Activity list is empty.  Cannot get activity info!"

        return None

    def __selectActivity(self,name):
        """
        Selects an activity from the activityList by name.  This allows
        simpler access to the list of activities.
        (For example, one can specify only 'Calculate' instead of
        'org.laptop.Calculate' or 'calculate.Calculate').
        @param name: The name of the activity (e.g. 'Calculate')
        """
        # Get the list of activities if it does not already exist
        activityList = self.__getActivityList()

        # Initialization of some variables...
        self.__path             = None
        self.__importClass      = None
        self.__className        = None

        # Iterate through the activity list
        for activity in activityList:
            if activity.get_name() == name:
                self.__path         = activity.get_path()
                self.__importClass  = activity.get_command().split()[-1]
                self.__className    = self.__importClass.split(".")[-1]
                self.log.debug("Importing class %s" % name)
                return

        # If we ever get here, that means we didn't find anything...
        if (self.__path is None) and (self.__importClass is None) and \
            (self.__className is None):
            raise NameError, "Could not find '%s' in activity list." % name

    def __initializeScript(self):
        """
        Initialize the script on the XML-RPC server.
        Returns the name of the Activity that should be instantiated
        """
        rpc = self.__xmlRPC

        try:
            self.sessID = os.environ['SUGARBOT_ID']
        except KeyError:
            self.sessID = 0

        # Start the script
        if not rpc.startScript(self.sessID):
            rpc.fail("Could not start the script!", self.sessID)
            exit()

        # Get our activity name
        activityName    = rpc.getActivityName(self.sessID)
        if activityName is None:
            rpc.fail('Bad activity name provided', self.sessID)
            exit()

        self.log.info("Activity is %s" % activityName)
        return activityName


    def __init__(self, handle):
        """
        Performs setup, and runs the specified activity.
        """
        self.log = logging.getLogger('sugarbot')

        # Set up threading...
        gtk.gdk.threads_init()

        # Handle is set to 'None' for testing purposes via Nose.  Obviously,
        # if Sugar sets the handle to None, there are other problems...
        if handle is None:
            return

        try:
            # Create the RPC connection object
            self.__xmlRPC = ServerProxy(proxyString())

            # Set up the sbGUI object for automation
            self.__sbgui    = sbGUI(self, self.__xmlRPC)

            # Get our activity name
            activityName    = self.__initializeScript()

            # Actually clone the activity
            self.__selectActivity(activityName)
            self.__dynamicImport(self.__importClass,self.__path)
            self.__cloneActivity(handle)
        except socket.error:
            sys.log.error("====== COULD NOT CONNECT TO XML-RPC SERVER ======")
            sys.exit(-1)