Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/sharedslides.py
diff options
context:
space:
mode:
Diffstat (limited to 'sharedslides.py')
-rwxr-xr-xsharedslides.py176
1 files changed, 176 insertions, 0 deletions
diff --git a/sharedslides.py b/sharedslides.py
new file mode 100755
index 0000000..d49c4cd
--- /dev/null
+++ b/sharedslides.py
@@ -0,0 +1,176 @@
+# -*- mode:python; tab-width:4; indent-tabs-mode:nil; -*-
+
+# sharedslides.py
+#
+# Class that performs all work relating to the sharing of slide decks and ink.
+# Kris Plunkett <kp86@cs.washington.edu>
+#
+# 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 logging
+
+import sys
+import os
+import time
+import random
+import gobject
+
+import telepathy
+import telepathy.client
+
+import dbus
+from dbus.service import method, signal
+from dbus.gobject_service import ExportedGObject
+
+from sugar.presence import presenceservice
+from sugar import network
+from sugar.presence.tubeconn import TubeConnection
+
+SERVICE = "edu.washington.cs.ClassroomPresenterXO"
+IFACE = SERVICE
+PATH = "/edu/washington/cs/ClassroomPresenterXO"
+
+
+# Define a simple HTTP server for sharing data.
+class ReadHTTPRequestHandler(network.ChunkedGlibHTTPRequestHandler):
+ def translate_path(self, path):
+ return self.server._filepath
+
+class ReadHTTPServer(network.GlibTCPServer):
+ def __init__(self, server_address, filepath):
+ self._filepath = filepath
+ network.GlibTCPServer.__init__(self, server_address, ReadHTTPRequestHandler)
+
+
+class SharedSlides(gobject.GObject):
+ """ Handles all sharing of slides and ink """
+
+ __gsignals__ = {
+ 'deck-download-complete' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
+ }
+
+ def __init__(self, init, cpxo_path, shared_activity, read_file_cb):
+ gobject.GObject.__init__(self)
+
+ self.__is_initiating = init
+ self.__cpxo_path = cpxo_path
+ self.__shared_activity = shared_activity
+ self.read_file_cb = read_file_cb
+ self.__logger = logging.getLogger('SharedSlides')
+
+ self.__tubes_chan = self.__shared_activity.telepathy_tubes_chan
+ self.__iface = self.__tubes_chan[telepathy.CHANNEL_TYPE_TUBES]
+
+ if (self.__is_initiating):
+ self.__logger.debug('Hello from SharedSlides (sharer).')
+ self.__have_deck = True
+ self.share_deck()
+ else:
+ # find a stream tube to download the slide deck from
+ self.__logger.debug('Hello from SharedSlides (joiner).')
+ self.__iface.connect_to_signal('NewTube', self.new_tube_cb)
+ self.__have_deck = False
+ self.get_stream_tube()
+
+ def get_stream_tube(self):
+ """ Attempts to download the slide deck from an available stream tube """
+ self.__iface.ListTubes(
+ reply_handler=self.list_tubes_reply_cb,
+ error_handler=self.list_tubes_error_cb)
+
+ def handle_download_fail(self):
+ """ If an attempt to download the deck fails, this method takes care of it """
+ self.__logger.error('Download failed! Sleeping five seconds and trying again.')
+ time.sleep(5)
+ self.get_stream_tube()
+
+ def list_tubes_reply_cb(self, tubes):
+ for tube_info in tubes:
+ self.new_tube_cb(*tube_info)
+
+ def list_tubes_error_cb(self, e):
+ self.__logger.error('ListTubes() failed: %s', e)
+ self.handle_download_fail
+
+ def new_tube_cb(self, id, initiator, type, service, params, state):
+ self.__logger.debug('New tube: ID=%d initiator=%d type=%d service=%s params=%r state=%d',
+ id, initiator, type, service, params, state)
+
+ if (not self.__have_deck and
+ type == telepathy.TUBE_TYPE_STREAM and
+ service == SERVICE and
+ state == telepathy.TUBE_STATE_LOCAL_PENDING):
+ addr = self.__iface.AcceptStreamTube(id,
+ telepathy.SOCKET_ADDRESS_TYPE_IPV4,
+ telepathy.SOCKET_ACCESS_CONTROL_LOCALHOST, 0,
+ utf8_strings=True)
+ self.__logger.debug("Got a stream tube!")
+
+ # sanity checks
+ assert isinstance(addr, dbus.Struct)
+ assert len(addr) == 2
+ assert isinstance(addr[0], str)
+ assert isinstance(addr[1], (int, long))
+ assert addr[1] > 0 and addr[1] < 65536
+ ip_addr = addr[0]
+ port = int(addr[1])
+
+ self.__logger.debug("The stream tube is good!")
+ self.download_file(ip_addr, port, id)
+
+ def download_file(self, ip_addr, port, tube_id):
+ """ Performs the actual download of the slide deck """
+ self.__logger.debug("Downloading from ip %s and port %d.", ip_addr, port)
+
+ getter = network.GlibURLDownloader("http://%s:%d/document" % (ip_addr, port))
+ getter.connect("finished", self.download_result_cb, tube_id)
+ getter.connect("progress", self.download_progress_cb, tube_id)
+ getter.connect("error", self.download_error_cb, tube_id)
+ self.__logger.debug("Starting download to %s...", self.__cpxo_path)
+ getter.start(self.__cpxo_path)
+
+ def download_result_cb(self, getter, tempfile, suggested_name, tube_id):
+ """ Called when the file download was successful """
+ self.__logger.debug("Got file %s (%s) from tube %u",
+ tempfile, suggested_name, tube_id)
+ self.emit('deck-download-complete')
+ self.read_file_cb(self.__cpxo_path)
+
+ def download_progress_cb(self, getter, bytes_downloaded, tube_id):
+ tmp = True
+ #self.__logger.debug("Bytes downloaded from tube %u: %u", tube_id, bytes_downloaded)
+
+ def download_error_cb(self, getter, err, tube_id):
+ self.__logger.error('Download failed on tube %u: %s', tube_id, err)
+ self.handle_download_fail()
+
+ def share_deck(self):
+ """ As the instructor XO, or as a student that has completed the deck download
+ share the deck with others in the activity """
+
+ # get a somewhat random port number
+ self.__port = random.randint(1024, 65535)
+ self.__ip_addr = "127.0.0.1"
+
+ self._fileserver = ReadHTTPServer(("", self.__port), self.__cpxo_path)
+ self.__logger.debug('Started an HTTP server on port %d', self.__port)
+
+ self.__iface.OfferStreamTube(SERVICE, {},
+ telepathy.SOCKET_ADDRESS_TYPE_IPV4,
+ (self.__ip_addr, dbus.UInt16(self.__port)),
+ telepathy.SOCKET_ACCESS_CONTROL_LOCALHOST, 0)
+ self.__logger.debug('Made a stream tube.')
+
+gobject.type_register(SharedSlides)