Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/olpcfr
diff options
context:
space:
mode:
Diffstat (limited to 'olpcfr')
-rw-r--r--olpcfr/__init__.py3
-rw-r--r--olpcfr/_server.py26
-rw-r--r--olpcfr/config.py111
-rw-r--r--olpcfr/flask/__init__.py8
-rw-r--r--olpcfr/flask/_app.py60
-rw-r--r--olpcfr/semanticxo/__init__.py1
-rw-r--r--olpcfr/semanticxo/_runner.py45
-rw-r--r--olpcfr/tools/__init__.py2
-rw-r--r--olpcfr/tools/_logger.py10
-rw-r--r--olpcfr/tools/_obj.py9
-rw-r--r--olpcfr/tools/image.py110
-rw-r--r--olpcfr/tools/registry.py48
-rw-r--r--olpcfr/tools/sound.py111
-rw-r--r--olpcfr/tools/storage.py150
14 files changed, 694 insertions, 0 deletions
diff --git a/olpcfr/__init__.py b/olpcfr/__init__.py
new file mode 100644
index 0000000..0274fab
--- /dev/null
+++ b/olpcfr/__init__.py
@@ -0,0 +1,3 @@
+# olpcfr import
+from olpcfr._server import Server
+from olpcfr.flask import BUNDLE, ROOT
diff --git a/olpcfr/_server.py b/olpcfr/_server.py
new file mode 100644
index 0000000..8b79403
--- /dev/null
+++ b/olpcfr/_server.py
@@ -0,0 +1,26 @@
+# python import
+import atexit, multiprocessing
+
+# olpcfr import
+from olpcfr import config
+
+
+class Server(object):
+
+ def __init__(self):
+ from olpcfr.flask import app, run_app
+ if app:
+ # start the server
+ self._server = multiprocessing.Process(target=run_app)
+ self._server.start()
+ # .. required to close properly
+ atexit.register(self.close)
+ else:
+ # .. nothing to start
+ self._server = None
+
+ def close(self):
+ # stop web thread if started
+ if self._server:
+ self._server.terminate()
+ self._server.join()
diff --git a/olpcfr/config.py b/olpcfr/config.py
new file mode 100644
index 0000000..9180e54
--- /dev/null
+++ b/olpcfr/config.py
@@ -0,0 +1,111 @@
+
+# python import
+import logging
+from ConfigParser import SafeConfigParser
+
+# get application logger
+logger = logging.getLogger('atoideweb')
+
+
+class Config(object):
+
+ class __Singleton:
+ """Our singleton object.
+ """
+
+ def __init__(self, config=None):
+ """Create the new singleton with the application config.
+
+ :param config: SafeConfigParser object for all the application
+ :see: `ConfigParser.SafeConfigParser`
+ """
+ # ensure config
+ if config is None:
+ # ...
+ self.__config = SafeConfigParser()
+ # ...
+ self.__config.read('config.ini')
+ # ...
+ else:
+ self.__config = config
+
+ def set(self, path, value, type_=str):
+ # set value
+ self.set_value(*path.split(">"), value=value, type_=type_)
+
+ def get(self, path, type_=str):
+ """A little jQuery like shortcut for the `get_value` method.
+
+ :param path: something like "section>option"
+ :param type_: type of the expected value. Default: str
+ :return: expected value in the expected type or None
+ :rtype: `object`
+ """
+ # get value
+ _value = self.get_value(*path.split(">"), type_=type_)
+ # check and return
+ return None if _value is None or _value == "" else _value
+
+ def set_value(self, section, option, value=None, type_=str):
+ # check has config
+ if self.__config is None:
+ return
+ # ensure section
+ if self.__config.has_section(section):
+ pass
+ else:
+ self.__config.add_section(section)
+ # do set
+ self.__config.set(section, option, value)
+
+ def get_value(self, section, option, type_=str):
+ """Simple config value getter to avoid exception risk when getting
+ a config value. If the section and option exist, returns the
+ corresponding value, None otherwise.
+
+ The `type_` parameter specify the expected option type and
+ return.
+
+ :param section: section name of the expected value
+ :param option: option name name of the expected value
+ :param type_: type of the expected value. Default: str
+ :return: expected value in the expected type or None
+ :rtype: `object`
+ """
+ # check has config
+ if self.__config is None:
+ return None
+ # check value exist
+ elif self.__config.has_section(section) \
+ and self.__config.has_option(section, option):
+ # check expected value type
+ if type_ is int:
+ return self.__config.getint(section, option)
+ elif type_ is bool:
+ return self.__config.getboolean(section, option)
+ elif type_ is str:
+ return self.__config.get(section, option)
+ elif type_ is list:
+ _v = self.__config.get(section, option)
+ return _v.split(' ')
+ # unexpected type ??
+ else:
+ return None
+ # value does not exist
+ else:
+ # do nothing
+ return None
+
+ # singleton instance
+ instance = None
+
+ def __new__(c, config=None, force=False):
+ """Singleton new init.
+ """
+ # if doesn't already initialized
+ if not Config.instance \
+ or force is True:
+ # create a new instance
+ Config.instance = Config.__Singleton(config=config)
+ # return the manager object
+ return Config.instance
diff --git a/olpcfr/flask/__init__.py b/olpcfr/flask/__init__.py
new file mode 100644
index 0000000..1c3de70
--- /dev/null
+++ b/olpcfr/flask/__init__.py
@@ -0,0 +1,8 @@
+# flask import
+try:
+ from flask import request, jsonify
+except Exception, e:
+ pass
+
+# olpcfr import
+from olpcfr.flask._app import app, render, run_app, APP_NAME, BUNDLE, ROOT
diff --git a/olpcfr/flask/_app.py b/olpcfr/flask/_app.py
new file mode 100644
index 0000000..371fe59
--- /dev/null
+++ b/olpcfr/flask/_app.py
@@ -0,0 +1,60 @@
+# python import
+import os
+
+# common gettext import
+from gettext import gettext
+
+# olpcfr import
+from olpcfr import config
+
+# get APP_NAME or default name
+APP_NAME = config.Config().get('activity>name')
+APP_NAME = 'my_activity' if APP_NAME is None else APP_NAME
+
+# get app config values
+_debug = config.Config().get('server>debug')
+_key = config.Config().get('server>secret_key')
+_port = config.Config().get('server>port', type_=int)
+
+# sugar or debug root path factory
+try:
+ from sugar.activity import activity
+ BUNDLE = activity.get_bundle_path()
+ ROOT = activity.get_activity_root()
+except Exception, e:
+ BUNDLE = os.path.abspath(os.path.join(
+ os.path.dirname(__file__), '..', '..', '..'))
+ ROOT = BUNDLE
+
+
+try:
+ # flask import
+ import flask
+ # init app
+ app = flask.Flask(__name__)
+ app.debug = True if _debug is None else _debug
+ app.secret_key = 'NO_KEY_OOPS' if _key is None else _key
+ # override jinja template path
+ app.jinja_loader.searchpath = [os.path.join(BUNDLE, 'templates')]
+ # init static folder path
+ from werkzeug import SharedDataMiddleware
+ app.wsgi_app = SharedDataMiddleware(app.wsgi_app,
+ {'/static': os.path.join(BUNDLE, 'static')})
+except Exception, e:
+ app = None
+
+
+def run_app():
+ """run method to trigger at from python class.
+ """
+ if app:
+ app.run(port=_port)
+ else:
+ pass
+
+
+def render(template, **context):
+ """Crappy hack for gettext issue in templates!
+ """
+ context['_'] = gettext
+ return flask.render_template(template, **context)
diff --git a/olpcfr/semanticxo/__init__.py b/olpcfr/semanticxo/__init__.py
new file mode 100644
index 0000000..eea4501
--- /dev/null
+++ b/olpcfr/semanticxo/__init__.py
@@ -0,0 +1 @@
+from olpcfr.semanticxo._runner import Runner
diff --git a/olpcfr/semanticxo/_runner.py b/olpcfr/semanticxo/_runner.py
new file mode 100644
index 0000000..cdbe650
--- /dev/null
+++ b/olpcfr/semanticxo/_runner.py
@@ -0,0 +1,45 @@
+# python import
+import os, shlex, subprocess, tarfile
+
+# olpcfr tools import
+from olpcfr.tools import logger, storage
+
+
+class Runner(object):
+
+ class __Singleton:
+
+ def __init__(self):
+ self.proc = None
+
+ def run(self):
+ # prepare paths
+ _store_path = storage.get_path(path='store')
+ # change dir for unzipping
+ os.chdir(_store_path)
+ if os.path.exists('redstore'):
+ pass
+ else:
+ _trip_zip = storage.get_path(path='data/triplestore.tar.bz2')
+ # extract files in tmp dir
+ _tar = tarfile.open(_trip_zip)
+ _tar.extractall()
+ _tar.close()
+ # get args
+ args = shlex.split('sh %s/wrapper.sh' % _store_path)
+ self.proc = subprocess.Popen(args)
+ # stay in the dir
+ os.chdir(storage.BUNDLE)
+
+ def stop(self):
+ pass
+
+ # singleton instance
+ instance = None
+
+ def __new__(c):
+ if Runner.instance is None:
+ Runner.instance = Runner.__Singleton()
+ else:
+ pass
+ return Runner.instance
diff --git a/olpcfr/tools/__init__.py b/olpcfr/tools/__init__.py
new file mode 100644
index 0000000..071fee0
--- /dev/null
+++ b/olpcfr/tools/__init__.py
@@ -0,0 +1,2 @@
+from olpcfr.tools._obj import obj
+from olpcfr.tools._logger import logger
diff --git a/olpcfr/tools/_logger.py b/olpcfr/tools/_logger.py
new file mode 100644
index 0000000..f8bb9bd
--- /dev/null
+++ b/olpcfr/tools/_logger.py
@@ -0,0 +1,10 @@
+
+# flask logger
+from olpcfr.flask import app, APP_NAME
+if app:
+ logger = app.logger
+# std logger
+else:
+ import logging
+ logger = logging.getLogger(APP_NAME)
+ logger.setLevel(logging.DEBUG)
diff --git a/olpcfr/tools/_obj.py b/olpcfr/tools/_obj.py
new file mode 100644
index 0000000..85c461a
--- /dev/null
+++ b/olpcfr/tools/_obj.py
@@ -0,0 +1,9 @@
+
+class obj(object):
+
+ def __init__(self, d):
+ for a, b in d.items():
+ if isinstance(b, (list, tuple)):
+ setattr(self, a, [obj(x) if isinstance(x, dict) else x for x in b])
+ else:
+ setattr(self, a, obj(b) if isinstance(b, dict) else b)
diff --git a/olpcfr/tools/image.py b/olpcfr/tools/image.py
new file mode 100644
index 0000000..cffc156
--- /dev/null
+++ b/olpcfr/tools/image.py
@@ -0,0 +1,110 @@
+
+# python import
+import logging, os, struct, StringIO
+
+# atoideweb import
+from atoideweb.tools import registry
+
+
+def compute_width_height(width, height, max_width, max_height, use_max=False):
+ # compute ratio
+ _ratio_scr = max_width / float(max_height)
+ _ratio_img = width / float(height)
+ # ..
+ if width > max_width\
+ or height > max_height:
+ if _ratio_img > _ratio_scr:
+ width = max_width
+ height = int(max_width / _ratio_img)
+ elif _ratio_img < _ratio_scr:
+ width = int(max_height * _ratio_img)
+ height = max_height
+ else:
+ width = max_width
+ height = max_height
+ # ..
+ return width, height
+ # ..
+ elif use_max is True:
+ return max_width, max_height
+ # ..
+ else:
+ return width, height
+
+
+def get_image_info(path):
+ """Tricky method found on Internet that returns the image info from a given
+ raw image data.
+ """
+ # little check
+ _info = registry.InfoRegistry().get_info(path)
+ # already exist
+ if _info is not None:
+ return _info
+ elif os.path.exists(path):
+ pass
+ else:
+ return None, 0, 0
+ # read file
+ _f = open(path)
+ _data = _f.read()
+ _f.close()
+ #
+ size = len(_data)
+ height = 0
+ width = 0
+ content_type = None
+
+ # handle GIFs
+ if (size >= 10) and _data[:6] in ('GIF87a', 'GIF89a'):
+ # Check to see if content_type is correct
+ content_type = 'image/gif'
+ w, h = struct.unpack("<HH", _data[6:10])
+ width = int(w)
+ height = int(h)
+
+ # See PNG 2. Edition spec (http://www.w3.org/TR/PNG/)
+ # Bytes 0-7 are below, 4-byte chunk length, then 'IHDR'
+ # and finally the 4-byte width, height
+ elif ((size >= 24) and _data.startswith('\211PNG\r\n\032\n')
+ and (_data[12:16] == 'IHDR')):
+ content_type = 'image/png'
+ w, h = struct.unpack(">LL", _data[16:24])
+ width = int(w)
+ height = int(h)
+
+ # Maybe this is for an older PNG version.
+ elif (size >= 16) and _data.startswith('\211PNG\r\n\032\n'):
+ # Check to see if we have the right content type
+ content_type = 'image/png'
+ w, h = struct.unpack(">LL", _data[8:16])
+ width = int(w)
+ height = int(h)
+
+ # handle JPEGs
+ elif (size >= 2) and _data.startswith('\377\330'):
+ content_type = 'image/jpeg'
+ jpeg = StringIO.StringIO(_data)
+ jpeg.read(2)
+ b = jpeg.read(1)
+ try:
+ while (b and ord(b) != 0xDA):
+ while (ord(b) != 0xFF): b = jpeg.read(1)
+ while (ord(b) == 0xFF): b = jpeg.read(1)
+ if (ord(b) >= 0xC0 and ord(b) <= 0xC3):
+ jpeg.read(3)
+ h, w = struct.unpack(">HH", jpeg.read(4))
+ break
+ else:
+ jpeg.read(int(struct.unpack(">H", jpeg.read(2))[0])-2)
+ b = jpeg.read(1)
+ width = int(w)
+ height = int(h)
+ except struct.error:
+ pass
+ except ValueError:
+ pass
+ # udpate registry
+ registry.InfoRegistry().set_info(path, (content_type, width, height))
+ # ..
+ return content_type, width, height
diff --git a/olpcfr/tools/registry.py b/olpcfr/tools/registry.py
new file mode 100644
index 0000000..7e575c3
--- /dev/null
+++ b/olpcfr/tools/registry.py
@@ -0,0 +1,48 @@
+
+# python import
+import logging
+
+
+class Registry(object):
+
+ class __Singleton:
+ """Our singleton object.
+ """
+
+ def __init__(self):
+ """Create the new singleton for a simple use.
+ """
+ # ensure config
+ self.__dict = dict()
+
+ def get_info(self, path):
+ # ..
+ if path in self.__dict:
+ return self.__dict[path]
+ else:
+ return None
+
+ def set_info(self, path, info):
+ # clear previous
+ if path in self.__dict:
+ del self.__dict[path]
+ else:
+ pass
+ # ...
+ self.__dict[path] = info
+
+ # singleton instance
+ instance = None
+
+ def __new__(c, force=False):
+ """Singleton new init.
+ """
+ # if doesn't already initialized
+ if not Registry.instance \
+ or force is True:
+ # create a new instance
+ Registry.instance = Registry.__Singleton()
+ else:
+ pass
+ # return the manager object
+ return Registry.instance
diff --git a/olpcfr/tools/sound.py b/olpcfr/tools/sound.py
new file mode 100644
index 0000000..7ac6a57
--- /dev/null
+++ b/olpcfr/tools/sound.py
@@ -0,0 +1,111 @@
+
+# python import
+import gst
+# ..
+from datetime import timedelta
+
+
+class Player(object):
+
+ def __init__(self, loop=False):
+ # playing flag
+ self.playing = False
+ self.loop = loop
+ # player object
+ self.player = None
+ self._init_player()
+ # file to play
+ self._soundfile = None
+
+ def _reload_cb(self, bus, message):
+ if self.loop is True:
+ self.player.set_state(gst.STATE_READY)
+ self.player.set_state(gst.STATE_PLAYING)
+ else:
+ pass
+
+ def _error_cb(self, bus, message):
+ self.player.set_state(gst.STATE_NULL)
+
+ def _init_player(self):
+ # make player
+ self.player = gst.element_factory_make("playbin", "player")
+ # video fake
+ _fakesink = gst.element_factory_make('fakesink', "my-fakesink")
+ self.player.set_property("video-sink", _fakesink)
+ # bus ..
+ bus = self.player.get_bus()
+ bus.add_signal_watch()
+ bus.connect('message::eos', self._reload_cb)
+ bus.connect('message::error', self._error_cb)
+
+ def serialize(self):
+ # little check
+ if self._soundfile is None:
+ return None
+ else:
+ return file(self._soundfile, 'r').read()
+
+ def load(self, soundfile):
+ # file to play
+ self._soundfile = soundfile
+ # little check
+ if self._soundfile is None:
+ pass
+ else:
+ # load sound file
+ self.player.set_state(gst.STATE_NULL)
+ self.player.set_property('uri', 'file://' + self._soundfile)
+
+ def get_position(self):
+ # little check
+ if self._soundfile is None:
+ return None
+ else:
+ # ...
+ _position = self.player.query_duration(gst.FORMAT_TIME)[0]
+ # ...
+ return timedelta(seconds=(_position / gst.SECOND))
+
+ def get_duration(self):
+ # little check
+ if self._soundfile is None:
+ return None
+ else:
+ # _duration = self.player.query_duration(gst.FORMAT_TIME)[0]
+ # ..
+ _parser = gst.parse_launch("filesrc name=source ! decodebin2 ! fakesink")
+ # ..
+ _source = _parser.get_by_name("source")
+ _source.set_property("location", self._soundfile)
+ # ..
+ _parser.set_state(gst.STATE_PLAYING)
+ _parser.get_state()
+ # ..
+ _format = gst.Format(gst.FORMAT_TIME)
+ _duration = _parser.query_duration(_format)[0]
+ _parser.set_state(gst.STATE_NULL)
+ # ..
+ return timedelta(seconds=(_duration / gst.SECOND))
+
+ def seek(self, time):
+ # little check
+ if self._soundfile is None:
+ return
+ else:
+ # format time
+ _seek = time * 1000000000
+ # do seek
+ self.player.seek_simple(gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, _seek)
+
+ def play(self):
+ self.playing = True
+ self.player.set_state(gst.STATE_PLAYING)
+
+ def pause(self):
+ self.playing = False
+ self.player.set_state(gst.STATE_PAUSED)
+
+ def stop(self):
+ self.playing = False
+ self.player.set_state(gst.STATE_NULL)
diff --git a/olpcfr/tools/storage.py b/olpcfr/tools/storage.py
new file mode 100644
index 0000000..3e6ebbf
--- /dev/null
+++ b/olpcfr/tools/storage.py
@@ -0,0 +1,150 @@
+# python import
+import os
+
+# olpcfr import
+from olpcfr import BUNDLE, ROOT
+
+
+ACTIVITY_NAMES = {
+ 'paint': 'org.laptop.Oficina',
+ 'record': 'org.laptop.RecordActivity',
+ }
+
+
+def check_dir(path):
+ if os.path.exists(path):
+ pass
+ else:
+ os.mkdir(path)
+
+
+def check_file(path):
+ if os.path.exists(path):
+ pass
+ else:
+ _f = open(path, 'wb')
+ _f.write('')
+ _f.close()
+
+
+def get_path(path=None, bundle=True):
+ # ..
+ path = 'static' if path is None else path
+ # ..
+ if bundle is True:
+ return os.path.join(BUNDLE, path)
+ else:
+ return os.path.join(ROOT, path)
+
+
+def list_dir(path=None, bundle=True):
+ # ..
+ path = get_path(path=path, bundle=bundle)
+ # ..
+ return os.listdir(path)
+
+
+def get_sound_path(filename, path=None, bundle=True, ext=None):
+ # ..
+ path = get_path(path=path, bundle=bundle)
+ filename = filename if ext is None else '%s.%s' % (filename, ext)
+ # ..
+ return os.path.join(path, filename)
+
+
+def get_sound_path(filename, path=None, bundle=True, ext='ogg'):
+ # ..
+ return get_sound_path(filename, path=path, bundle=bundle, ext=ext)
+
+
+def get_image_path(filename, path=None, bundle=True, ext='png'):
+ # ..
+ return get_sound_path(filename, path=path, bundle=bundle, ext=ext)
+
+
+def __do_query(query):
+ from sugar.datastore import datastore
+ # find in ds
+ _results, _count = datastore.find(query, sorting='timestamp')
+ for _r in _results:
+ # get meta
+ _m = _r.get_metadata()
+ if 'activity' in query:
+ yield _r
+ elif _m['activity'] == '':
+ yield _r
+ else:
+ continue
+
+
+def get_journal_objects(activity_name=None, mime_type=None):
+ # init
+ _query = dict()
+ # prepare query name
+ if activity_name is None\
+ and mime_type is None:
+ return []
+ elif mime_type is None:
+ return __do_query({'activity': ACTIVITY_NAMES[activity_name]})
+ else:
+ return __do_query({'mime_type': mime_type})
+
+
+def list_info_from_journal(activity_name=None, mime_type=None):
+ # get objects first
+ _objs = get_journal_objects(activity_name=activity_name, mime_type=mime_type)
+ # make unique titles
+ _titles = {}
+ # return infos
+ for _o in _objs:
+ # get meta
+ _m = _o.get_metadata()
+ # get title
+ _t = _m['title']
+ # ensure description
+ _d = _m['description'] if 'description' in _m else ''
+ _p = _m['preview'] if 'preview' in _m else None
+ # little check
+ if _t in _titles:
+ # udpate reg
+ _titles[_t] += 1
+ # update value to show
+ _t = '%s (%s)' % (_t, _titles[_t])
+ # init title reg
+ else:
+ _titles[_t] = 1
+ # ensure info
+ yield {
+ 'activity_id' : _m['activity_id'],
+ 'description' : _d,
+ 'timestamp' : _m['timestamp'],
+ 'preview' : _p,
+ 'title' : _t,
+ }
+
+
+def list_files_from_journal(activity_name=None, mime_type=None):
+ # get objects first
+ _objs = get_journal_objects(activity_name=activity_name,
+ mime_type=mime_type)
+ # return paths
+ for _o in _objs:
+ # TODO open the files
+ yield _o.get_file_path()
+
+
+def get_path_from_journal(timestamp, mime_type):
+ from sugar.datastore import datastore
+ # ..
+ _query = {
+ 'timestamp': int(timestamp),
+ 'mime_type': mime_type
+ }
+ # find in ds
+ _results, _count = datastore.find(_query)
+ # ..
+ if _count == 1:
+ # get path
+ return _results[0].get_file_path()
+ else:
+ return None