At Canonical's Online Services we can do cool stuff on fridays. We do cool
stuff all week, actually, but on fridays we can do crazier cool stuff.
So, today, I ripped off a great service offered by http://calepin.co
and implemented a prototype blog-through-Ubuntu-One web application.
Of course, it's powered by Nikola,
The code is absolute nonsense, and it needs to be looked at by someone
who understands Django, OAuth, OpenID, and programming in general better
than I do, but hey, it does work (for a very loose definition of "work").
It's called Shoreham and no, you can't have it yet.
As a teaser, here's a video. With a pony.
In the near future I will do a better post about this explaining the code, etc.
Así que acá va un pequeño tutorial acerca de como usar algunas de esas APIs. Lo hice usando Python y PyQt por varios motivos:
Son excelentes herramientas para prototipos
Tienen excelente soporte para las cosas que necesito (DBus, HTTP, OAuth)
Es lo que sé y me gusta. Lo hice un domingo, no lo pienso hacer en PHP
y Gtk.
Dicho eso, no hay nada específico de python o de Qt en este código. Donde hago
un request HTTP usando QtNetwork, podés usar libsoup o lo que fuere.
Vayamos a los bifes entonces. Las piezas más importantes de Ubuntu One, desde el
punto de vista de infraestructura, son Ubuntu SSO Client, que se encarga de
login, registración, etc, y SyncDaemos que maneja la sincronización de archivos.
Para interactuar con ellas, en Linux, ofrecen interfaces DBus. Así que, por ejemplo,
este es un fragmento mostrando como obtener las credenciales de Ubuntu One (esto
normalmente sería parte del __init__ de un objeto):
# Get the session busbus=dbus.SessionBus():::# Get the credentials proxy and interfaceself.creds_proxy=bus.get_object("com.ubuntuone.Credentials","/credentials",follow_name_owner_changes=True)# Connect to signals so you get a call when something# credential-related happensself.creds_iface=dbus.Interface(self.creds_proxy,"com.ubuntuone.CredentialsManagement")self.creds_proxy.connect_to_signal('CredentialsFound',self.creds_found)self.creds_proxy.connect_to_signal('CredentialsNotFound',self.creds_not_found)self.creds_proxy.connect_to_signal('CredentialsError',self.creds_error)# Call for credentialsself._credentials=Noneself.get_credentials()
Tal vez notaste que get_credentials no devuelve las credenciales. Lo que hace es, le
dice a SyncDaemon que las obtenga, y entonces, si/cuando aparecen, se emite una de esas
señales, y uno de los métodos conectados se llama. Esto está bueno porque no tenemos
que preocuparnos de que se nos bloquee la aplicación mientras SyncDaemon está buscando
las credenciales.
¿Y qué hay en esos métodos? ¡No mucho!
defget_credentials(self):# Do we have them already? If not, get'emifnotself._credentials:self.creds_proxy.find_credentials()# Return what we've got, could be Nonereturnself._credentialsdefcreds_found(self,data):# Received credentials, save them.print"creds_found",dataself._credentials=data# Don't worry about get_quota yet ;-)ifnotself._quota_info:self.get_quota()defcreds_not_found(self,data):# No credentials, remove old ones.print"creds_not_found",dataself._credentials=Nonedefcreds_error(self,data):# No credentials, remove old ones.print"creds_error",dataself._credentials=None
Así que básicamente, self._credentials contiene unas credenciales, o None. Felicitaciones, ya
entramos a Ubuntu One.
¡Hagamos algo útil! ¿Que tal preguntar cuánto espacio libre hay en la cuenta? Para eso, no
podemos usar las APIs locales, si no conectarnos a los servers, que son los que saben
si estás excedido de quota o no.
El acceso se controla via OAuth, por lo que para acceder a esa API necesitamos firmar
nuestros pedidos. Aquí se ve como se hace. No es particularmente iluminador, yo no lo
escribí, solamente lo uso:
defsign_uri(self,uri,parameters=None):# Without credentials, return unsigned URLifnotself._credentials:returnuriifisinstance(uri,unicode):uri=bytes(iri2uri(uri))print"uri:",urimethod="GET"credentials=self._credentialsconsumer=oauth.OAuthConsumer(credentials["consumer_key"],credentials["consumer_secret"])token=oauth.OAuthToken(credentials["token"],credentials["token_secret"])ifnotparameters:_,_,_,_,query,_=urlparse(uri)parameters=dict(cgi.parse_qsl(query))request=oauth.OAuthRequest.from_consumer_and_token(http_url=uri,http_method=method,parameters=parameters,oauth_consumer=consumer,token=token)sig_method=oauth.OAuthSignatureMethod_HMAC_SHA1()request.sign_request(sig_method,consumer,token)print"SIGNED:",repr(request.to_url())returnrequest.to_url()
¿Y cómo pedimos el estado de quota? Accediendo al punto de entrada https://one.ubuntu.com/api/quota/
con la autorización adecuada, se obtiene un diccionario JSON con el espacio total y el usado.
Acá hay una muestra de como hacerlo:
# This is on __init__self.nam=QtNetwork.QNetworkAccessManager(self,finished=self.reply_finished):::defget_quota(self):"""Launch quota info request."""uri=self.sign_uri(QUOTA_API)url=QtCore.QUrl()url.setEncodedUrl(uri)self.nam.get(QtNetwork.QNetworkRequest(url))
De nuevo: get_quota no devuelve la quota. Sólo lanza un pedido HTTP a los servers de
Ubuntu One, que (eventualmente) responden con los datos. No querés que tu app se quede
ahí trabada mientras tanto, por eso QNetworkAccessManager va a llamar a self.reply_finished
cuando tenga la respuesta:
¿Qué más queremos? ¿Qué tal notificación cuando cambia el status de SyncDaemon? Por ejemplo,
cuando la sincronización está al día, o cuando te desconecta. De nuevo, esas son señales
DBus a las que uno se conecta en __init__:
self.status_proxy=bus.get_object('com.ubuntuone.SyncDaemon','/status')self.status_iface=dbus.Interface(self.status_proxy,dbus_interface='com.ubuntuone.SyncDaemon.Status')self.status_iface.connect_to_signal('StatusChanged',self.status_changed)# Get the status as of right nowself._last_status=self.process_status(self.status_proxy.current_status())
la función process_status is código aburrido para convertir la info de status de syncdaemon en
una cosa legible como "Sync is up-to-date", así que guardamos eso en self._last_status y
actualizamos el menú.
¿Qué menú? ¡Un menú contextual de un QSystemTrayIcon! Lo que leyeron son las piezas principales
que se necesitan para crear algo útil: una aplicación de SystemTray para Ubuntu One que se puede usar
en KDE, XFCE u Openbox. O, si estás en unity y tenés sni-qt instalado, un app indicator.