Xo-get
Jump to navigation
Jump to search
XO-Get is a very simple package-installer for the olpc xo-laptop.
- License: GPL
- The repository syncs with http://www.olpcaustria.org/mediawiki/index.php/Xo-get/Repository
- Installing with ActivityBundle.install() or plain unzipping
- Removing via ActivityBundle.uninstall()
- Infos stored in a local sqlite3 db (in ~/.xo_get/activities.db)
- Supported commands:
./xo-get.py update ./xo-get.py list [categories/category_name] ./xo-get.py search tag/activity_name ./xo-get.py install activity_name ./xo-get.py remove activity_name
#! /usr/bin/env python # script started by crazy-chris, olpc-austria # project page: http://www.olpcaustria.org/mediawiki/index.php/Xo-get # license: gpl import os import sys import urllib2 import sqlite3 class Activity: name = u"" desc = u"" xo_url = u"" category = u"" tags = u"" class Database: db_path = "" db_filename = "activities.db" cur = "" con = "" def __init__(self, db_path): self.db_path = db_path # Change Directory try: os.mkdir(self.db_path) except: pass os.chdir(self.db_path) # Init DB create_db = True if os.path.isfile(self.db_filename): create_db = False print "- [%s/%s]:" % (self.db_path, self.db_filename), self.con = sqlite3.connect(self.db_filename) self.cur = self.con.cursor() self.cur.execute("-- types unicode") if create_db: # Setup New Database self.cur.execute("CREATE TABLE 'activities' ('id' INTEGER PRIMARY KEY AUTOINCREMENT, 'name' VARCHAR( 100 ) NOT NULL, 'desc' VARCHAR( 355 ) NOT NULL, 'xo_url' VARCHAR( 255 ) NOT NULL, 'category' VARCHAR( 255 ) NOT NULL, 'tags' VARCHAR( 255 ) NOT NULL);") self.con.commit() print "created" else: print "ok" # q = "SELECT * FROM activities WHERE 1" # print self.query(q) def query(self, q): dataList = [] self.cur.execute(q) data = self.cur.fetchall() if data: dataList = [list(row) for row in data] return dataList def commit(self, q): self.cur.execute(q) self.con.commit() class XOGet: version = "0.4" update_url = "http://www.linuxuser.at/xo-get.txt" # domain = "http://wiki.laptop.org" # path = "/go/Xo-get/Repository" domain = "http://www.olpcaustria.org" path = "/mediawiki/index.php?title=Xo-get/Repository&printable=yes" localpath = "%s/%s" % (os.path.expanduser( '~' ), ".xo_get") def __init__(self): print print " xo-get %s" % self.version print " %s" % ("~" * (len(self.version)+7)) if len(sys.argv) == 1: print " http://www.olpcaustria.org/mediawiki/index.php/Xo-get\n\n use: - update\n - list ['categories' / category_name] \n - search activity_name / tag\n - install activity_name\n - remove activity_name" print sys.exit(0) # Loading DB self.db = Database(self.localpath) print if sys.argv[1] == "update": self.update() if sys.argv[1] == "list": self.list() if sys.argv[1] == "search": self.search() if sys.argv[1] == "install": self.install() if sys.argv[1] == "remove": self.remove() print def force_input(self, question, possibilities): i = "" while i not in possibilities: print "%s [%s]" % (question, "/".join(possibilities)), i = raw_input() return i def remove(self): # To remove, we need sugar, and the .xo file if len(sys.argv) > 2: # Load Sugar try: sugar_loaded = True from sugar.bundle.activitybundle import ActivityBundle except: sugar_loaded = False try: dbus_loaded = True from dbus.mainloop.glib import DBusGMainLoop DBusGMainLoop(set_as_default=True) except: dbus_loaded = False if sugar_loaded and dbus_loaded: # Try to get correct app_name, then xo file app_name = sys.argv[2] print "- Starting to remove '%s'" % app_name # Get .xo Filename q = "SELECT xo_url FROM activities WHERE name = '%s'" % app_name res = self.db.query(q) if len(res) == 0: print " - No exact matching name found." q = "SELECT name FROM activities WHERE name LIKE '%s'" % app_name res = self.db.query(q) if res[0][0] != "": app_name = res[0][0] q = "SELECT xo_url FROM activities WHERE name = '%s'" % app_name res = self.db.query(q) else: return False # Security Ask print i = self.force_input("- Remove activity '%s'?" % app_name, ["y", "n"]) if i == "n": return False # Extract Filename from URL => Write function for that (used 2x) fn = "%s/%s" % (self.localpath, res[0][0][res[0][0].rindex('/')+1:]) if fn.count(";f=") > 0: fn = fn[fn.rindex(";f=")+3:] if os.path.isfile(fn): print "- Source file found (%s)" % fn else: print "- No source file found (%s)" % fn return False bundle = ActivityBundle(fn) if bundle.is_installed(): print "- Removing..." bundle.uninstall() print "- Finished!" return True else: print "- Activity is not yet installed" return False else: # DBUS or Sugar Failure if sugar_loaded == False: print "- Couldn't load sugar environment; removing not possible." print " http://wiki.laptop.org/go/Sugar" if dbus_loaded == False: print "- Couldn't load dbus.mainloop.glib" else: print "- Plase supply an activity_name (eg: '%s remove imagequiz')" % sys.argv[0] return False def list(self): # Lists available Activities q = "SELECT name, desc, category, tags FROM activities WHERE 1" print "- Listing", if len(sys.argv) > 2: if sys.argv[2] == "categories": print "Categories:" q = "SELECT DISTINCT category FROM activities WHERE 1 ORDER BY category ASC" res = self.db.query(q) for r in res: print " - [%s]" % r[0] return True else: print "Category '%s'" % sys.argv[2] q = "%s AND category LIKE '%s'" % (q, sys.argv[2]) else: print "All Activities" q = "%s%s" % (q, " ORDER BY category ASC, name ASC") res = self.db.query(q) spaces = 18 spaces2 = 14 for r in res: print " -", " " * (spaces2 - len(r[2])), print "[ %s ] " % r[2], r[0], "." * (spaces - len(r[0])), r[1] def search(self): # Search for tags and name if len(sys.argv) > 2: s = sys.argv[2].replace(" ", "_") print "- Results for '%s':" % s q = "SELECT name, desc, category FROM activities WHERE tags LIKE '%s%s%s' OR name LIKE '%s%s%s' ORDER BY category ASC, name ASC" % ('%', s, '%', '%', s, '%') res = self.db.query(q) spaces = 18 spaces2 = 8 for r in res: print " - [%s]" % r[2], " " * (spaces2 - len(r[2])), r[0], "." * (spaces - len(r[0])), r[1] else: print "- Please supply query-string (eg: '%s search quiz')" % sys.argv[0] def install(self, s = None): # s = Activity_Name, If not supplied to function, take from sys.argv if s == None: if len(sys.argv) > 2: s = sys.argv[2].replace(" ", "_") else: print "- Plase supply an activity_name (eg: '%s install imagequiz')" % sys.argv[0] return False # Start Installation print "- Installing Activity '%s'" % s q = "SELECT xo_url FROM activities WHERE name='%s'" % s res = self.db.query(q) # No xo_url means no found activity if len(res) == 0: print " - No matching name found." # Search DB with LIKE q = "SELECT name FROM activities WHERE name LIKE '%s'" % s res = self.db.query(q) if len(res) == 0: print " - Nothing found. Please try 'xo-get search'" return False else: print " - Found quite similar name: '%s'" % res[0][0] # Only Upper/Lowercase Problem if s.lower() == res[0][0].lower(): print self.install(res[0][0]) return True # Else Ask if to take it i = self.force_input(" - Use this name?", ["y", "n"]) print if i == "y": self.install(res[0][0]) return True else: return False # Installation process ready to download i = self.force_input(" - Do you want to proceed?", ["y", "n"]) if i == "n": return False print # start Download url = res[0][0] fn = "%s/%s" % (self.localpath, res[0][0][res[0][0].rindex('/')+1:]) if fn.count(";f=") > 0: fn = fn[fn.index(";f=")+3:] print "- Download => %s" % (fn) # print "- %s => %s" % (url, fn) # 1. Check if file exists download = True if os.path.isfile(fn): # File Exists: Ask if dl or use i = self.force_input(" - File already exists. Download again?", ["y", "n"]) if i == "n": download = False print # 2. If wanted, download .xo if download: try: xo = urllib2.urlopen(url).read() except: print "- Could not contact server." return False f = open(fn, "w") f.write(xo) f.close() # Start Installation - Try The Sugar Way try: sugar_loaded = True from sugar.bundle.activitybundle import ActivityBundle except: sugar_loaded = False try: dbus_loaded = True from dbus.mainloop.glib import DBusGMainLoop DBusGMainLoop(set_as_default=True) except: dbus_loaded = False print "- Starting Installation" if sugar_loaded and dbus_loaded: # install the sugar way # like os.system("sugar-install-bundle %s" % fn), but in here: bundle = ActivityBundle(fn) try: bundle.install() except: print " - Already installed :-)" else: if sugar_loaded == False: print " - Couldn't load sugar.bundle.activitybundle" if dbus_loaded == False: print " - Couldn't load dbus.mainloop.glib" i = self.force_input(" - Extract the Activity Bundle to %s/?" % self.localpath, ["y", "n"]) if i == "y": c = "unzip %s -d %s" % (fn, self.localpath) print c os.system(c) print print "- Finished" def update_xoget(self, to_version): # Set local script filename (/.../xo-get.py) fn = sys.argv[0][sys.argv[0].rindex('/'):] fn = "%s%s" % (sys.path[0], fn) print "- Updating", fn # Download new Script content = urllib2.urlopen(self.update_url).read() f = open(fn, "w") f.write(content) f.close() # os.system("python %s init" % sys.argv[0]) # print "rm %s/%s" % (self.localpath, self.db.db_filename) print "- Update to version %s finished" % to_version def update(self): # Download print "- Contacting server: [ %s%s ]" % (self.domain, self.path) try: content = urllib2.urlopen("%s%s" % (self.domain, self.path)).read() except: "- Server Down :(" return False # Extract Version => version try: version = content[content.index('cur_ver=')+8:] version = version[:version.index('<')] except: version = None print "- Local version: %s (current: %s)" % (self.version, version) # Check Version if self.version != version and version != None: # Update Possible i = self.force_input(" - Update xo-get to version: %s?" % version, ["y", "n"]) if i == "y": # Update Now print self.update_xoget(version) return True # Extract Repository => content print content = content[content.index('<table border="0"'):]
content = content[:content.index('')]
# Clear DB print "- Clearing database:", self.db.commit("DELETE FROM activities WHERE 1") print "ok" # Add Activities print "- Adding activities:",
content_arr = content.split("") act_count = 0 for act_html in content_arr: # Extract Each Activity if act_html.count("") > 0:
# Add Activity
act_html = act_html.replace("", "") act_html = act_html.replace("", "") # Put all Info in class a = Activity() a = Activity() act_html_arr = act_html.split("")
a.name = act_html_arr[1].strip().replace(" ", "_") if a.name.count('">') > 0: a.name = a.name[a.name.index('">')+2:] a.name = a.name[:a.name.index('<')] # Existing Activity? if a.name != "": a.xo_url = act_html_arr[2].strip().replace("%s%s" % ("&a", "mp"), "&") a.xo_url = a.xo_url[a.xo_url.index("http"):] if a.xo_url.count('"') > 0: a.xo_url = a.xo_url[:a.xo_url.index('"')] a.desc = act_html_arr[3].strip() a.category = act_html_arr[4].strip() a.tags = act_html_arr[5].strip() # Submit to DB q = "INSERT INTO activities (name, xo_url, desc, category, tags) VALUES ('%s', '%s', '%s', '%s', '%s')" % (a.name, a.xo_url, a.desc, a.category, a.tags) act_count += 1 self.db.commit(q) print act_count print "- Database is up-to-date" if __name__ == "__main__": xoget = XOGet()