Xo-get: Difference between revisions
Crazy-chris (talk | contribs) m (+f.close()) |
|||
(53 intermediate revisions by 19 users not shown) | |||
Line 1: | Line 1: | ||
<table border="0"> |
|||
XO-Get is a very simple package-installer for the olpc xo-laptop. |
|||
<tr> |
|||
* License: GPL |
|||
<td valign="top">__TOC__</td> |
|||
* The repository syncs with http://www.olpcaustria.org/mediawiki/index.php/Xo-get/Repository |
|||
<td valign="top" style="padding:10px 26px;">[[Image:activity-xoget.png]]</td> |
|||
* Installing with ActivityBundle.install or unzip |
|||
<td valign="top" style="width:300px; text-align:center;"> |
|||
* Infos stored in a local sqlite3 db (in ~/.xo_get/activities.db) |
|||
'''<font color="green">XO-Get</font>''' is a very simple package-installer / manager, used for installing, testing and removing activities for / on the xo-laptop and emulators. |
|||
* Supported commands: |
|||
'''<font color="green">News</font>''': We are currently developing a [[Xo-get/Xo-get-gtk|Graphical User Interface]] with [[PyGTK]]</td> |
|||
</tr> |
|||
</table> |
|||
== About xo-get == |
|||
* [http://www.olpc-europe.org/browse/xo-get.py xo-get.py] (GPL) |
|||
* [http://dev.laptop.org/git?p=projects/xo-get;a=tree Source Tree] |
|||
* [http://xo-get.olpc.at/repository/ Activity Repositories] |
|||
* [[Xo-get/Xo-get-gtk|Graphical User Interface]] |
|||
== Supported commands == |
|||
Run this within Sugar in Terminal activity as user |
|||
(not as root nor su nor sudo) |
|||
./xo-get.py update |
./xo-get.py update |
||
./xo-get.py list [categories/category_name] |
./xo-get.py list ['categories' / category_name] |
||
./xo-get.py search |
./xo-get.py search keyword |
||
./xo-get.py install activity_name |
./xo-get.py install activity_name / activity_file.xo |
||
./xo-get.py remove activity_name / activity_file.xo / activity_directory |
|||
./xo-get.py start activity_name |
|||
./xo-get.py status ['log'] |
|||
'''Explanation:''' |
|||
: <table border="0"><tr><td style="width:70px;">''update''</td><td>Updates the Database</td></tr><tr><td>''list''</td><td>Lists installable Activities</td></tr><tr><td>''search''</td><td>Searches for tags, names, descriptions</td></tr><tr><td>''install''</td><td>Installs the given Activity</td></tr><tr><td>''remove''</td><td>Removes an Activity</td></tr><tr><td>''start''</td><td>Starts Activity</td></tr><tr><td>''status''</td><td>List all installed Activities</td></tr></table> |
|||
== Usage == |
|||
=== Installation of xo-get === |
|||
wget http://xo-get.olpc.at/xo-get.py |
|||
chmod u+x xo-get.py |
|||
=== Update the xo-get repository database=== |
|||
./xo-get.py update |
|||
=== Installation of xo-get GUI === |
|||
./xo-get.py install xo-get |
|||
: Note: Joyride 1702, although it installed ok. When i click on the frame icon to run it. Flashes for awhile, then goes away. I ran it in terminal by '''xo-get.py start xo-get''' and appears to run ok. (Filing some bugs and suggestions too. :) --[[User:Ixo|ixo]] 02:11, 17 February 2008 (EST) |
|||
sugar-install-bundle/media/<usb stick name> |
|||
=== Usage Examples === |
|||
'''List installed Activities''' |
|||
./xo-get.py status |
|||
'''Installing & Removing''' |
|||
./xo-get.py install simcity |
|||
./xo-get.py remove simcity |
|||
'''Local .xo files''' |
|||
./xo-get install simcity.xo |
|||
./xo-get remove simcity.xo |
|||
=== Name Assistant === |
|||
* Search, list, install and remove activities with typing only the beginning of the activity's/category's name as parameter |
|||
List the category "Fun Games": |
|||
./xo-get.py list fun |
|||
Install CC Licensing Activity: |
|||
./xo-get.py install cc |
|||
Removing simcity |
|||
./xo-get.py remove simc |
|||
=== GCompris === |
|||
List GCompris activities: |
|||
./xo-get.py list gcompris |
|||
Install GCompris activities: |
|||
./xo-get install gcompris-... |
|||
Install all GCompris activities (> 100): |
|||
./xo-get install gcompris |
|||
== Repositories == |
|||
Newer versions of xo-get (>1.0) use a [http://xo-get.olpc.at/repository/ central repository] ([http://xo-get.olpc.at/repository/xoget.xml xml]), which is synced daily with the sub-'repositories'. Advantages of this structure are |
|||
* 'xo-get update' will not break on changes in the repositories |
|||
* supplying with filesizes and system-names (which sometimes differ from the wiki-names) |
|||
* much less traffic on updates |
|||
* backup repositories |
|||
Repository Listing: |
|||
# [[Activities]] |
|||
# http://gcompris.net/incoming/xo/ |
|||
It's possible to add / change repositories on request ([[Xo-get#Contributors|mail]]). Also, any ideas for further developments are welcome! |
|||
== Bugs / Issues == |
|||
Please file a ticket for any bug you find to the 'xo-get' component here: http://dev.laptop.org/newticket |
|||
== Feature Request / Wishlist == |
|||
* [[Xo-get/Wishlist|Add an item]] |
|||
{{:Xo-get/Wishlist}} |
|||
== Changelog == |
|||
* 1.2.3: Switched to one [http://xo-get.olpc.at/repository/ central repository], lots of minor fixes |
|||
== Contributors == |
|||
'''Author''' |
|||
* [[User:Crazy-chris|Chris Hager]] (chris at linuxuser dot at) |
|||
'''Testering''' |
|||
* [[User:Jaume|Jaume Nualart]] |
|||
* [[User:ixo|ixo]] - testering as much as I can. Submitted bug reports. |
|||
If you have ideas *and* some spare time: contact us! This project is open for anyone :-) |
|||
[[Category:Activities]] |
|||
[[Category:Developers]] |
|||
[[Category:Resources]] |
|||
[[Category:Package management]] |
|||
[[Category:Activity installation]] |
|||
<gallery> |
|||
'''[http://www.linuxuser.at/xo-get.txt xo-get.py]''' |
|||
Image:Example.jpg|Caption1 |
|||
#! /usr/bin/env python |
|||
Image:Example.jpg|Caption2 |
|||
</gallery> |
|||
# 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) |
|||
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 |
|||
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('</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() |
Latest revision as of 18:11, 20 November 2011
XO-Get is a very simple package-installer / manager, used for installing, testing and removing activities for / on the xo-laptop and emulators.
|
About xo-get
Supported commands
Run this within Sugar in Terminal activity as user
(not as root nor su nor sudo)
./xo-get.py update ./xo-get.py list ['categories' / category_name] ./xo-get.py search keyword ./xo-get.py install activity_name / activity_file.xo ./xo-get.py remove activity_name / activity_file.xo / activity_directory ./xo-get.py start activity_name ./xo-get.py status ['log']
Explanation:
update Updates the Database list Lists installable Activities search Searches for tags, names, descriptions install Installs the given Activity remove Removes an Activity start Starts Activity status List all installed Activities
Usage
Installation of xo-get
wget http://xo-get.olpc.at/xo-get.py chmod u+x xo-get.py
Update the xo-get repository database
./xo-get.py update
Installation of xo-get GUI
./xo-get.py install xo-get
- Note: Joyride 1702, although it installed ok. When i click on the frame icon to run it. Flashes for awhile, then goes away. I ran it in terminal by xo-get.py start xo-get and appears to run ok. (Filing some bugs and suggestions too. :) --ixo 02:11, 17 February 2008 (EST)
sugar-install-bundle/media/<usb stick name>
Usage Examples
List installed Activities
./xo-get.py status
Installing & Removing
./xo-get.py install simcity ./xo-get.py remove simcity
Local .xo files
./xo-get install simcity.xo ./xo-get remove simcity.xo
Name Assistant
- Search, list, install and remove activities with typing only the beginning of the activity's/category's name as parameter
List the category "Fun Games":
./xo-get.py list fun
Install CC Licensing Activity:
./xo-get.py install cc
Removing simcity
./xo-get.py remove simc
GCompris
List GCompris activities:
./xo-get.py list gcompris
Install GCompris activities:
./xo-get install gcompris-...
Install all GCompris activities (> 100):
./xo-get install gcompris
Repositories
Newer versions of xo-get (>1.0) use a central repository (xml), which is synced daily with the sub-'repositories'. Advantages of this structure are
- 'xo-get update' will not break on changes in the repositories
- supplying with filesizes and system-names (which sometimes differ from the wiki-names)
- much less traffic on updates
- backup repositories
Repository Listing:
It's possible to add / change repositories on request (mail). Also, any ideas for further developments are welcome!
Bugs / Issues
Please file a ticket for any bug you find to the 'xo-get' component here: http://dev.laptop.org/newticket
Feature Request / Wishlist
Improvements
- Position Quit-button the standard way / use the Toolbar
- Better Installation and Removal Feedback
- Auto-Refresh, Refresh with graphical representation
- Bigger Scrollbars / Autoscroll on KeyDown
- Less scrolling: Wrap Description, Changable width of columns
- Better "Information": Icon, Version Number
- Highlight Activities without checking
- Hide Apply Button after Installation (which does nothing)
- Change the "Close" button label to "Show List". "Close" currently implies closing the application given the current context of the application and the full screen dialog box.
- checkbox for showing version #.
- Legend.. what the heck does the (*) mean ???
- Group activities by, and alpha sort list (Allow collapsible groups)
- Base pre-installed, non-removable
- Installed Activities
- Groups of packaged activities, such as GCompris
- Arrow keys
- Left/Right - scroll left/right
- Up/Down - scroll up/down
- Remove all custom packages, [Button] (except of course xo-get :)
New Features
- Self-Update screen for the GUI
- Preferences
- where to save files
- clean old .xo's
- GUI: Install .xo from disk / usb
- Button for 'check for Activity updates' (compare installed versions with versions available)
- Button for 'update all'
- Add entries for installed activities in the Journal for a common deletion/removal mechanism.
Changelog
- 1.2.3: Switched to one central repository, lots of minor fixes
Contributors
Author
- Chris Hager (chris at linuxuser dot at)
Testering
- Jaume Nualart
- ixo - testering as much as I can. Submitted bug reports.
If you have ideas *and* some spare time: contact us! This project is open for anyone :-)