Xo-get: Difference between revisions

From OLPC
Jump to navigation Jump to search
(0.4.1: install and remove any local .xo file)
(remove source from wiki)
Line 1: Line 1:
XO-Get is a very simple package-installer for the olpc xo-laptop.
XO-Get is a very simple package-installer / manager, used for installing, testing and removing activities for / on the xo-laptop.
# [http://www.linuxuser.at/xo-get.txt Source]
# [http://www.linuxuser.at/xo-get.txt Source] (GPL)
# [http://www.olpcaustria.org/mediawiki/index.php/Xo-get/Screenshots Screenshots]
# License: GPL
# Repository syncs with http://www.olpcaustria.org/mediawiki/index.php/Xo-get/Repository
# [http://www.olpcaustria.org/mediawiki/index.php/Xo-get/Repository Public Repository]: 25 Activities (add your .xo now!)
# [http://www.olpcaustria.org/mediawiki/index.php/Xo-get Project Page] @ Olpc-Austria
# Installing & Removing of local .xo files, or download over web
# Installing with ActivityBundle.install() or plain unzipping
# Installing & removing via local .xo files or downloaded bundles
# Removing Activities via ActivityBundle.uninstall()
# ActivityBundle.install(), Unzipping, ActivityBundle.uninstall()
# Infos stored in a local sqlite3 db (in ~/.xo_get/activities.db)
# Infos stored in a local sqlite3 db (in ~/.xo_get/activities.db)
# Successfully tested on the xo (build_648). [http://www.olpcaustria.org/mediawiki/index.php/Xo-get/Screenshots Screenshots]
# Successfully tested on the xo (build_648)
# Supported commands:
# Supported commands:
./xo-get.py update
./xo-get.py update
Line 16: Line 16:


'''[http://www.linuxuser.at/xo-get.txt xo-get.py]'''
'''[http://www.linuxuser.at/xo-get.txt xo-get.py]'''

#! /usr/bin/env python
# script started by crazy-chris, olpc-austria
# project page: http://www.olpcaustria.org/mediawiki/index.php/Xo-get
# license: gpl
# version: 0.4.1
VERSION_NEWS = " 1. Install and remove any .xo file (like 'test.xo'): \n\
./xo-get.py install test.xo \n\
./xo-get.py remove test.xo"
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 = ""
version = ""
def __init__(self, db_path, version):
self.db_path = db_path
self.version = version
# 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
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 "- [%s/%s]: created\n" % (self.db_path, self.db_filename),
# print "created"
else:
# print "ok"
pass
# Check for version & display version news on update
self.cur.execute("CREATE TABLE IF NOT EXISTS 'versions' ('id' INTEGER PRIMARY KEY AUTOINCREMENT, 'version' VARCHAR( 10 ) NOT NULL, 'notes' VARCHAR( 355 ) NOT NULL);")
self.con.commit()
q = "SELECT count(*) FROM versions WHERE version='%s'" % version
res = self.query(q)
if res[0][0] == 0:
# New Version Update
q = "INSERT INTO versions (version, notes) VALUES ('%s', '%s')" % (version, VERSION_NEWS.replace("'", '"'))
self.commit(q)
print
print " News to version %s:\n -----------------%s\n%s" % (version, "-"*len(version), VERSION_NEWS)
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.1"
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))
# Loading DB
self.db = Database(self.localpath, self.version)
print
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 / activity_file.xo \n - remove activity_name / activity_file.xo"
print
sys.exit(0)
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
# NEW: Install any .xo-file (outside repo)
if os.path.isfile(app_name):
print " - (Local file)"
fn = app_name
else:
# Normal install via activity_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 len(res) > 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
# 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:]
# Security Question
print
i = self.force_input("- Remove activity '%s'?" % app_name, ["y", "n"])
if i == "n": return False
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 dbus_loaded == False: print "- Couldn't load dbus.mainloop.glib"
if sugar_loaded == False:
print "- Couldn't load sugar environment; removing not possible."
print " http://wiki.laptop.org/go/Sugar"
else:
# len(sys.argv) != 2
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
# local .xo filename
fn = None
# Check Parameters
if s == None:
if len(sys.argv) > 2:
s = sys.argv[2]
if os.path.isfile(s):
# NEW: Supplied .xo app-name will search for .xo file, and if found installs from that
fn = s
print "- Installing Activity '%s'" % s
i = self.force_input(" - Do you want to proceed?", ["y", "n"])
if i == "n": return False
print
else:
# Else: Format Name (" " > "_")
s = s.replace(" ", "_")
print "- Installing Activity '%s'" % s
else:
print "- Plase supply an activity_name (eg: '%s install imagequiz')" % sys.argv[0]
return False
# If there is no direct .xo-name supplied, search DB for .xo url
if fn == None:
q = "SELECT xo_url FROM activities WHERE name='%s'" % s
res = self.db.query(q)
# If no xo_url, then Search for similar Name!
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":
print
self.install(res[0][0])
return True
else:
return False
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:]
# Installation process ready to download
i = self.force_input(" - Do you want to proceed?", ["y", "n"])
if i == "n": return False
print
# start Download
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()
else: # (if fn != None)
# If supplied .xo-filename, it's already checked and ready to go
pass
# Start Installation - Try The Sugar Way
# app_name and fn, all ok, proceed to installation
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 " - I think it's 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 "\n- %s" % c
os.system(c)
print
print "- Finished"
def update_xoget(self, to_version):
# Set local script filename (/.../xo-get.py)
fn = sys.argv[0]
if fn.count('/') > 0: fn = fn[fn.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)
print "- Contacting server: [ %s ]" % (self.domain)
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('</table>')]
# Clear DB
print "- Clearing database:",
self.db.commit("DELETE FROM activities WHERE 1")
print "ok"
# Add Activities
print "- Adding activities:",
content_arr = content.split("<tr>")
act_count = 0
for act_html in content_arr:
# Extract Each Activity
if act_html.count("<td>") > 0:
# Add Activity
act_html = act_html.replace("</td>", "")
act_html = act_html.replace("</tr>", "")
# Put all Info in class a = Activity()
a = Activity()
act_html_arr = act_html.split("<td>")
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()

Revision as of 16:24, 7 December 2007

XO-Get is a very simple package-installer / manager, used for installing, testing and removing activities for / on the xo-laptop.

  1. Source (GPL)
  2. Screenshots
  3. Public Repository: 25 Activities (add your .xo now!)
  4. Project Page @ Olpc-Austria
  5. Installing & removing via local .xo files or downloaded bundles
  6. ActivityBundle.install(), Unzipping, ActivityBundle.uninstall()
  7. Infos stored in a local sqlite3 db (in ~/.xo_get/activities.db)
  8. Successfully tested on the xo (build_648)
  9. Supported commands:
./xo-get.py update
./xo-get.py list     ['categories' / category_name]
./xo-get.py search   activity_name / tag
./xo-get.py install  activity_name / activity_file.xo
./xo-get.py remove   activity_name / activity_file.xo

xo-get.py