Xo-get: Difference between revisions

From OLPC
Jump to navigation Jump to search
m (script updates)
m (script update - unzip, bundle.install, ... (15 activities))
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 for the olpc xo-laptop.
* License: GPL
* The repository syncs with this page: http://www.olpcaustria.org/mediawiki/index.php/Xo-get/Repository
* The repository syncs with http://www.olpcaustria.org/mediawiki/index.php/Xo-get/Repository
* Infos stored in a local sqlite3 db (in ~/.xo_get/main.db)
* Installing with ActivityBundle.install or unzip
* Infos stored in a local sqlite3 db (in ~/.xo_get/activities.db)
* Supported commands:
* Supported commands:
./xo-get.py update
./xo-get.py update
Line 8: Line 10:
./xo-get.py install activity_name
./xo-get.py install activity_name


'''xo-get.py'''
'''[http://www.linuxuser.at/xo-get.txt xo-get.py]'''
#! /usr/bin/env python
#! /usr/bin/env python
# script started by crazy-chris @ olpc-austria
# script started by crazy-chris, olpc-austria
# project page: http://www.olpcaustria.org/mediawiki/index.php/Xo-get
# license: gpl
import os
import os
Line 16: Line 21:
import urllib2
import urllib2
import sqlite3
import sqlite3
class Activity:
class Activity:
name = u""
name = u""
Line 42: Line 47:
if os.path.isfile(self.db_filename): create_db = False
if os.path.isfile(self.db_filename): create_db = False
print "- Loading db [%s/%s]:" % (self.db_path, self.db_filename),
print "- [%s/%s]:" % (self.db_path, self.db_filename),
self.con = sqlite3.connect(self.db_filename)
self.con = sqlite3.connect(self.db_filename)
self.cur = self.con.cursor()
self.cur = self.con.cursor()
Line 70: Line 75:
class XOGet:
class XOGet:
version = "0.3"
update_url = "http://www.linuxuser.at/xo-get.txt"
# domain = "http://wiki.laptop.org"
# domain = "http://wiki.laptop.org"
# path = "/go/Xo-get/Repository"
# path = "/go/Xo-get/Repository"
domain = "http://www.olpcaustria.org"
domain = "http://www.olpcaustria.org"
path = "/mediawiki/index.php/Xo-get/Repository"
path = "/mediawiki/index.php?title=Xo-get/Repository&printable=yes"
localpath = "%s/%s" % (os.path.expanduser( '~' ), ".xo_get")
localpath = "%s/%s" % (os.path.expanduser( '~' ), ".xo_get")
def __init__(self):
def __init__(self):
print
print
print " xo-get"
print " xo-get %s" % self.version
print " ======"
print " %s" % ("~" * (len(self.version)+7))
if len(sys.argv) == 1:
if len(sys.argv) == 1:
print " use: - update\n - list {categories / [category_name]} \n - search [activity_name] / [tag]\n - install [activity_name]"
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"
print
print
sys.exit(0)
sys.exit(0)
Line 90: Line 98:
# Loading DB
# Loading DB
self.db = Database(self.localpath)
self.db = Database(self.localpath)
print
if sys.argv[1] == "update": self.update()
if sys.argv[1] == "update": self.update()
Line 95: Line 104:
if sys.argv[1] == "search": self.search()
if sys.argv[1] == "search": self.search()
if sys.argv[1] == "install": self.install()
if sys.argv[1] == "install": self.install()
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 list(self):
def list(self):
# Lists available Activities
print
q = "SELECT name, desc, category, tags FROM activities WHERE 1"
q = "SELECT name, desc, category, tags FROM activities WHERE 1"
print "- Listing",
print "- Listing",
Line 107: Line 125:
for r in res:
for r in res:
print " - [%s]" % r[0]
print " - [%s]" % r[0]
print
return True
return True
else:
else:
Line 118: Line 135:
res = self.db.query(q)
res = self.db.query(q)
spaces = 18
spaces = 18
spaces2 = 8
spaces2 = 14
for r in res:
for r in res:
print " - [%s]" % r[2], " " * (spaces2 - len(r[2])), r[0], "." * (spaces - len(r[0])), r[1]
print " -", " " * (spaces2 - len(r[2])),
print "[ %s ] " % r[2], r[0], "." * (spaces - len(r[0])), r[1]
print
def search(self):
def search(self):
# Search for tags and name
print
if len(sys.argv) > 2:
if len(sys.argv) > 2:
s = sys.argv[2].replace(" ", "_")
s = sys.argv[2].replace(" ", "_")
print "- Results for '%s':" % s
print "- Results for '%s':" % s
q = "SELECT name, desc, category FROM activities WHERE tags LIKE '%s%s%s' OR name LIKE '%s%s%s'" % ('%', 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)
res = self.db.query(q)
Line 138: Line 155:
else:
else:
print "- Please supply query-string (eg: '%s search quiz')" % sys.argv[0]
print "- Please supply query-string (eg: '%s search quiz')" % sys.argv[0]
print
def install(self, s = None):
def install(self, s = None):
# s = Activity_Name, If not supplied to function, take from sys.argv
print
if s == None:
if s == None:
if len(sys.argv) > 2:
if len(sys.argv) > 2:
Line 148: Line 163:
else:
else:
print "- Plase supply an activity_name (eg: '%s install xo_imagequiz')" % sys.argv[0]
print "- Plase supply an activity_name (eg: '%s install xo_imagequiz')" % sys.argv[0]
print
return False
return False
# Start Installation
print "- Installing Activity '%s'" % s
print "- Installing Activity '%s'" % s
q = "SELECT xo_url FROM activities WHERE name='%s'" % s
q = "SELECT xo_url FROM activities WHERE name='%s'" % s
Line 158: Line 173:
if len(res) == 0:
if len(res) == 0:
print " - No matching name found."
print " - No matching name found."
# Search DB with LIKE
q = "SELECT name FROM activities WHERE name LIKE '%s'" % s
q = "SELECT name FROM activities WHERE name LIKE '%s'" % s
res = self.db.query(q)
res = self.db.query(q)
if len(res) == 0:
if len(res) == 0:
print " - Nothing found. Please try 'xo-get search'"
print " - Nothing found. Please try 'xo-get search'"
print
return False
return False
else:
else:
Line 169: Line 185:
# Only Upper/Lowercase Problem
# Only Upper/Lowercase Problem
if s.lower() == res[0][0].lower():
if s.lower() == res[0][0].lower():
print
self.install(res[0][0])
self.install(res[0][0])
return True
return True
# Else Ask
# Else Ask if to take it
i = self.force_input(" - Use this name?", ["y", "n"])
i = ""
print
while i != 'y' and i != 'n':
print " - use this? [y/n]",
i = raw_input()
if i == "y":
if i == "y":
self.install(res[0][0])
self.install(res[0][0])
Line 184: Line 198:
return False
return False
# Installation process ready to download
i = ""
i = self.force_input(" - Do you want to proceed?", ["y", "n"])
while i != 'y' and i != 'n':
if i == "n":
print "- Do you want to proceed? [y/n]",
i = raw_input()
return False
if i == "n": return False
print
# Okay, we have the xo_url
# start Download
url = res[0][0]
url = res[0][0]
fn = "%s/%s" % (self.localpath, res[0][0][res[0][0].rindex('/')+1:])
fn = "%s/%s" % (self.localpath, res[0][0][res[0][0].rindex('/')+1:])
# print "- %s => %s" % (url, fn)
# 1. Check if file exists
download = True
# print "- %s => %s" % (url, fn)
print "- Download => %s" % (fn)
print "- Download => %s" % (fn)
if os.path.isfile(fn):
xo = urllib2.urlopen(url).read()
# File Exists: Ask if dl or use
f = open(fn, "w")
i = self.force_input(" - File already exists. Download again?", ["y", "n"])
f.write(xo)
if i == "n":
download = False
print
# 2. If wanted, download .xo
if download:
xo = urllib2.urlopen(url).read()
f = open(fn, "w")
f.write(xo)
# 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"
print "- Starting Installation"
if sugar_loaded and dbus_loaded:
os.system("sugar-install-bundle %s" % fn)
# install the sugar way
print "- Activity installed!"
bundle = ActivityBundle(fn)
print
bundle.install()
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":
os.system("unzip %s -d %s" % (fn, self.localpath))
# os.system("sugar-install-bundle %s" % fn)
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)
# 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):
def update(self):
print
# Download
print "- update from %s%s" % (self.domain, self.path)
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
content = urllib2.urlopen("%s%s" % (self.domain, self.path)).read()
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 border="0"'):]
content = content[:content.index('</table>')]
content = content[:content.index('</table>')]
# Clear DB
print "- clearing database:",
print "- Clearing database:",
self.db.commit("DELETE FROM activities WHERE 1")
self.db.commit("DELETE FROM activities WHERE 1")
print "ok"
print "ok"
# Add Activities
print "- adding activities:",
print "- Adding activities:",
content_arr = content.split("<tr>")
content_arr = content.split("<tr>")
act_count = 0
act_count = 0
for act_html in content_arr:
for act_html in content_arr:
# Extract Activities
# Extract Each Activity
if act_html.count("<td>") > 0:
if act_html.count("<td>") > 0:
# Add Activity
# Add Activity
Line 228: Line 322:
act_html = act_html.replace("</tr>", "")
act_html = act_html.replace("</tr>", "")
# Put all Info in class a = Activity()
a = Activity()
a = Activity()
act_html_arr = act_html.split("<td>")
act_html_arr = act_html.split("<td>")
# Name
a.name = act_html_arr[1].strip().replace(" ", "_")
a.name = act_html_arr[1].strip().replace(" ", "_")
if a.name.count('">') > 0:
if a.name.count('">') > 0:
Line 237: Line 331:
a.name = a.name[:a.name.index('<')]
a.name = a.name[:a.name.index('<')]
# Existing Activity?
if a.name != "":
if a.name != "":
a.xo_url = act_html_arr[2].strip().replace("%s%s" % ("&a", "mp"), "&")
# XO Url
a.xo_url = act_html_arr[2].strip()
a.xo_url = a.xo_url[a.xo_url.index("http:"):]
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('"')]
if a.xo_url.count('"') > 0: a.xo_url = a.xo_url[:a.xo_url.index('"')]
# DESC
a.desc = act_html_arr[3].strip()
a.desc = act_html_arr[3].strip()
# DESC
a.category = act_html_arr[4].strip()
a.category = act_html_arr[4].strip()
# DESC
a.tags = act_html_arr[5].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)
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
act_count += 1
# print q
self.db.commit(q)
self.db.commit(q)
# print
print act_count
print act_count
print "- xo-get is now up-to-date"
print "- Database is up-to-date"
print
if __name__ == "__main__":
if __name__ == "__main__":

Revision as of 18:23, 6 December 2007

XO-Get is a very simple package-installer for the olpc xo-laptop.

./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

#! /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.3"
	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"
			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()
		
		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 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 xo_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:])

		# 1. Check if file exists
		download = True
#			print "- %s => %s" % (url, fn)

		print "- Download => %s" % (fn)
		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:
			xo = urllib2.urlopen(url).read()
			f = open(fn, "w")
			f.write(xo)

		# 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
			bundle = ActivityBundle(fn)
			bundle.install()
			
		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":
				os.system("unzip %s -d %s" % (fn, self.localpath))
#		os.system("sugar-install-bundle %s" % fn)
		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)
		
#		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()