Ir al contenido principal

Ralsina.Me — El sitio web de Roberto Alsina

APIs de Ubuntu One en Ejemplos (parte 1)

Así que acá va un pe­que­ño tu­to­rial acer­ca de co­mo usar al­gu­nas de esas APIs. Lo hi­ce usan­do Py­thon y Py­Qt por va­rios mo­ti­vo­s:

  • Son ex­­ce­­len­­tes he­­rra­­mien­­tas pa­­ra pro­­­to­­­ti­­pos

  • Tie­­nen ex­­ce­­len­­te so­­­po­r­­te pa­­ra las co­­sas que ne­­ce­­si­­to (DBus, HTTP, OAu­­th)

  • Es lo que sé y me gus­­ta. Lo hi­­ce un do­­­mi­n­­go, no lo pien­­so ha­­cer en PHP y Gtk.

Di­cho eso, no hay na­da es­pe­cí­fi­co de py­thon o de Qt en es­te có­di­go. Don­de ha­go un re­quest HTTP usan­do QtNe­two­rk, po­dés usar lib­soup o lo que fue­re.

Va­ya­mos a los bi­fes en­ton­ce­s. Las pie­zas más im­por­tan­tes de Ubun­tu One, des­de el pun­to de vis­ta de in­fra­es­truc­tu­ra, son Ubun­tu SSO Clien­t, que se en­car­ga de lo­gi­n, re­gis­tra­ció­n, etc, y Syn­c­Dae­mos que ma­ne­ja la sin­cro­ni­za­ción de ar­chi­vo­s.

Pa­ra in­te­rac­tuar con ella­s, en Li­nu­x, ofre­cen in­ter­fa­ces DBus. Así que, por ejem­plo, es­te es un frag­men­to mos­tran­do co­mo ob­te­ner las cre­den­cia­les de Ubun­tu One (es­to nor­mal­men­te se­ría par­te del __i­ni­t__ de un ob­je­to­):

# Get the session bus
bus = dbus.SessionBus()

:
:
:

# Get the credentials proxy and interface
self.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 happens
self.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 credentials
self._credentials = None
self.get_credentials()

Tal vez no­tas­te que ge­t_­cre­den­tials no de­vuel­ve las cre­den­cia­le­s. Lo que ha­ce es, le di­ce a Syn­c­Dae­mon que las ob­ten­ga, y en­ton­ce­s, si/­cuan­do apa­re­cen, se emi­te una de esas se­ña­le­s, y uno de los mé­to­dos co­nec­ta­dos se lla­ma. Es­to es­tá bue­no por­que no te­ne­mos que preo­cu­par­nos de que se nos blo­quee la apli­ca­ción mien­tras Syn­c­Dae­mon es­tá bus­can­do las cre­den­cia­le­s.

¿Y qué hay en esos mé­to­do­s? ¡No mu­cho!

def get_credentials(self):
    # Do we have them already? If not, get'em
    if not self._credentials:
        self.creds_proxy.find_credentials()
    # Return what we've got, could be None
    return self._credentials

def creds_found(self, data):
    # Received credentials, save them.
    print "creds_found", data
    self._credentials = data
    # Don't worry about get_quota yet ;-)
    if not self._quota_info:
        self.get_quota()

def creds_not_found(self, data):
    # No credentials, remove old ones.
    print "creds_not_found", data
    self._credentials = None

def creds_error(self, data):
    # No credentials, remove old ones.
    print "creds_error", data
    self._credentials = None

Así que bá­si­ca­men­te, se­l­f._­cre­den­tials con­tie­ne unas cre­den­cia­le­s, o No­ne. Fe­li­ci­ta­cio­nes, ya en­tra­mos a Ubun­tu One.

¡Ha­ga­mos al­go úti­l! ¿Que tal pre­gun­tar cuán­to es­pa­cio li­bre hay en la cuen­ta? Pa­ra eso, no po­de­mos usar las APIs lo­ca­le­s, si no co­nec­tar­nos a los ser­ver­s, que son los que sa­ben si es­tás ex­ce­di­do de quo­ta o no.

El ac­ce­so se con­tro­la via OAu­th, por lo que pa­ra ac­ce­der a esa API ne­ce­si­ta­mos fir­mar nues­tros pe­di­do­s. Aquí se ve co­mo se ha­ce. No es par­ti­cu­lar­men­te ilu­mi­na­do­r, yo no lo es­cri­bí, so­la­men­te lo uso:

def sign_uri(self, uri, parameters=None):
    # Without credentials, return unsigned URL
    if not self._credentials:
        return uri
    if isinstance(uri, unicode):
        uri = bytes(iri2uri(uri))
    print "uri:", uri
    method = "GET"
    credentials = self._credentials
    consumer = oauth.OAuthConsumer(credentials["consumer_key"],
                                   credentials["consumer_secret"])
    token = oauth.OAuthToken(credentials["token"],
                             credentials["token_secret"])
    if not parameters:
        _, _, _, _, 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())
    return request.to_url()

¿Y có­mo pe­di­mos el es­ta­do de quo­ta? Ac­ce­dien­do al pun­to de en­tra­da http­s://o­ne.u­bun­tu.­co­m/a­pi/­quo­ta/ con la au­to­ri­za­ción ade­cua­da, se ob­tie­ne un dic­cio­na­rio JSON con el es­pa­cio to­tal y el usa­do. Acá hay una mues­tra de co­mo ha­cer­lo:

    # This is on __init__
    self.nam = QtNetwork.QNetworkAccessManager(self,
        finished=self.reply_finished)

:
:
:

def get_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 nue­vo: ge­t_­quo­ta no de­vuel­ve la quo­ta. Só­lo lan­za un pe­di­do HTTP a los ser­vers de Ubun­tu One, que (e­ven­tual­men­te) res­pon­den con los da­to­s. No que­rés que tu app se que­de ahí tra­ba­da mien­tras tan­to, por eso QNe­two­rkAc­ce­ss­Ma­na­ger va a lla­mar a se­l­f.­re­pl­y_­fi­nis­hed cuan­do ten­ga la res­pues­ta:

def reply_finished(self, reply):
    if unicode(reply.url().path()) == u'/api/quota/':
        # Handle quota responses
        self._quota_info = json.loads(unicode(reply.readAll()))
        print "Got quota: ", self._quota_info
        # Again, don't worry about update_menu yet ;-)
        self.update_menu()

¿Qué más que­re­mo­s? ¿Qué tal no­ti­fi­ca­ción cuan­do cam­bia el sta­tus de Syn­c­Dae­mo­n? Por ejem­plo, cuan­do la sin­cro­ni­za­ción es­tá al día, o cuan­do te des­co­nec­ta. De nue­vo, esas son se­ña­les DBus a las que uno se co­nec­ta en __i­ni­t__:

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 now
self._last_status = self.process_status(
    self.status_proxy.current_status())

Y es­ta es sta­tus_­chan­ge­d:

def status_changed(self, status):
    print "New status:", status
    self._last_status = self.process_status(status)
    self.update_menu()

la fun­ción pro­ce­ss_s­ta­tus is có­di­go abu­rri­do pa­ra con­ver­tir la in­fo de sta­tus de syn­c­dae­mon en una co­sa le­gi­ble co­mo "S­ync is up-­to­-­da­te", así que guar­da­mos eso en se­l­f._­las­t_s­ta­tus y ac­tua­li­za­mos el me­nú.

¿Qué me­nú? ¡Un me­nú con­tex­tual de un QS­ys­te­m­Tra­yI­co­n! Lo que le­ye­ron son las pie­zas prin­ci­pa­les que se ne­ce­si­tan pa­ra crear al­go úti­l: una apli­ca­ción de Sys­te­m­Tray pa­ra Ubun­tu One que se pue­de usar en KDE, XFCE u Open­bo­x. O, si es­tás en uni­ty y te­nés sni-­qt ins­ta­la­do, un app in­di­ca­to­r.

http://ubuntuone.com/7iXTbysoMM9PIUS9Ai4TNn

El in­di­ca­dor en ac­ció­n.

El có­di­go fuen­te del ejem­plo com­ple­to es­tá en mi pro­yec­to u1-­to­ys en laun­ch­pad y és­te es el có­di­go fuen­te com­ple­to (ex­cep­to los ico­nos, ba­jen­se el re­po, me­jo­r)

Vi­nien­do pron­to (es­pe­ro­), más apps de ejem­plo, y co­sas co­pa­das que se pue­den ha­cer con nues­tras APIs.

Dear Dr. Sheldon Cooper...

This post is a jo­ke.

Dr. Co­ope­r, I ho­pe this le­tter fi­ds you in good heal­th. I could­n't avoid overhea­ring you ta­lk wi­th Dr. Ho­fs­ta­dter on our buil­din­g's stair­ca­se the other da­y.

I am speaking about when you mu­s­ed about how li­fe would be if peo­ple evol­ved from rep­ti­le­s. I am so di­sappointed in you.

Firs­t, you men­tion that li­zar­ds are col­d-­bloode­d. Whi­ch is true. And that when it's cold they get slo­we­r. Whi­ch is al­so true. But then you sa­yd so­me­thing like "the wea­ther­li­zard would say 'i­t's slow ou­tsi­de' ins­tead of 'i­t's col­d'".

PO­PP­Y­CO­CK Dr. Co­ope­r! If the li­zard is slow be­cau­se it's col­d, it would per­cei­ve eve­r­y­thing out the­re as fast, not slo­w, just like slow cars see fas­ter cars as, you kno­w... fast?

Al­so, the men­tion about su­gges­ting the li­zard should wear a swea­ter is a slap on the fa­ce of ph­y­si­cs. Swea­ters are an in­su­la­to­r, not an ener­gy sour­ce. What makes the in­si­de of the swea­ter warm is the hu­man, Dr. Col­d-­blooded li­zar­ds would ha­ve no su­ch effect be­yond the tiny ther­mal iner­tia su­ch an im­per­fect wool in­su­la­tor would allo­w.

If you are in­te­res­ted on fur­ther ar­gu­ment in hu­man-­like rep­ti­le ci­vi­li­za­tion and fo­lk­lo­re I wi­ll be ha­ppy to in­dul­ge, but I must say I ex­pec­ted be­tter of you.

Mr­s. Var­ta­be­dian.

Gordo

-- Ri­chard Ba­ch­man (S­te­phen Kin­g)

No siem­pre fuí gor­do. Yo so­lía ser muy muy fla­co. Tan fla­co que mi vie­ja me obli­ga­ba a co­me­r. Me­día 1.75 y pe­sa­ba ti­po 65 ki­los cuan­do ter­mi­né el se­cun­da­rio. Tan fla­co que a los 25 to­da­vía usa­ba re­me­ras ta­lle M.

Cual­quie­ra que me co­noz­ca sa­be que ya no es así. Pa­sé los 100Kg ha­ce 10 año­s, y el lu­nes pa­sa­do pe­sa­ba 123.5K­g. Da­do que no mi­do 3.5 me­tros de al­to, es jus­to de­cir que du­pli­qué mi an­cho (mi diá­me­tro? Mi sec­ció­n!) en es­tos 25 año­s.

Eso no es bue­no. Es tan no bue­no, que mi doc­tor me ha ex­pli­ca­do ama­ble­men­te que si no ba­jo mu­cho de pe­so pron­to, me voy a ca­gar mu­rien­do. No la se­ma­na que vie­ne, no el año que vie­ne, pe­ro tam­po­co den­tro de 40 año­s.

Co­mo em­pe­cé a su­bir de pe­so cuan­do es­ta­ba en la fa­cul­tad y mi vie­jo se en­fer­mó, siem­pre cul­pé a la an­sie­da­d, a co­mer ba­su­ra, a una vi­da se­den­ta­ria, lo que ha­cen to­do­s.

Así que me hi­ce es­tu­dio­s. Re­sul­ta que era más o me­nos cier­to. Más por­que sí, ten­go que de­jar de co­mer ba­su­ra, y ha­cer más ejer­ci­cio. Pe­ro me­no­s, por­que ten­go (en­tre otras co­sas) hi­pe­rin­su­li­ne­mia. ¿S­aben cuá­les son los sín­to­mas mas co­mu­nes?

  • Hi­­pe­r­­ten­­sión (me­­di­­ca­­do ha­­ce 5 año­­s)

  • Au­­men­­to de VLDL (dia­g­­no­s­­ti­­ca­­do ha­­ce 2 año­­s)

  • Le­­ta­r­­go (mie­r­­da que sí)

  • Au­­men­­to de pe­­so (do­­­ble mie­r­­da que sí)

¿Y qué te ha­ce es­ta cla­se de au­men­to de pe­so? Un mon­tón de otras co­sas ma­la­s.

Des­de el mar­tes es­toy con una die­ta muy es­pe­cí­fi­ca, y to­man­do Me­tfor­mi­na pa­ra re­du­cir mi ni­vel de in­su­li­na. Me sien­to ac­ti­vo, y ba­jé 3 ki­los en 4 días (sí, ya sé que no es sos­te­ni­ble, y que no voy a ba­jar siem­pre así).

No se me hin­chan más los pie­s.

No ten­go ham­bre to­do el día.

Ca­mino 30 mi­nu­to­s, dos ve­ces al día.

Quie­ro pro­gra­ma­r.

Me sien­to bien. Otras ve­ce­s, cuan­do me cam­bia­ban me­di­ca­ción de la pre­sión me he sen­ti­do me­jor, cuan­do tu­ve ata­ques me he sen­ti­do peor, pe­ro bien? No me ha­bía sen­ti­do bien en mu­cho, mu­cho tiem­po.

Tal vez no sea la dro­ga ni la die­ta sino el efec­to pla­ce­bo. Ca­paz que es otra co­sa. Por otro la­do, creo que me es­tá yen­do de­ma­sia­do bien co­mo pa­ra ser tan bo­lu­do de mo­rir­me aho­ra. Vea­mos co­mo sa­le.

1003 is a funny number, just not funny haha


In 1003, Erik the Red died after ha­ving ru­led Green­lan­d. Whi­ch of cour­se, was a bad idea, co­lo­r-­com­bi­na­tio­n-wi­se.

http://piachepiu.com.ar/wp-content/uploads/2011/08/KKC4250.jpg

Arti­cle 1003 at pia­che­piu.­co­m.ar

Al­so a bad idea was de­cree 1003/89 whi­ch par­do­ned eve­ry "sub­ver­si­ve" or "te­rro­ris­t", in­clu­ding tho­se dead or di­sappea­re­d. This is after de­cree 1002, that par­dons tho­se who ki­lled or di­sappea­red the­m, of cour­se.

Mu­ch mil­der is de­cree 1003/98 whi­ch gi­ves an award to Ro­ber­to De­Vi­cen­zo. I wro­te a pro­gram ca­lled de­vi­cen­zo, BTW.

http://sp270.fotolog.com/photo/27/20/74/linea91_150/1267667784122_f.jpg

Car 1003 of the 56 bus.

Bug #1003 in laun­ch­pad is about laun­ch­pa­d, the ti­tle is "pro­jec­ts +sear­ch pa­ge 404" and the des­crip­tion is "Pa­ge not found Pa­ge not foun­d". It's fixe­d.

If you sear­ch for 1003 in Google Map­s, it gi­ves you one re­sult, in La­tvia.


View Larger Map

But Bing gi­ves you ano­ther pla­ce, in Lau­san­ne.

1003 da­ys ago, it was sep­tem­ber 15­th, 2002, the day of my 31st bir­th­da­y.

http://upload.wikimedia.org/wikipedia/commons/thumb/e/e0/Albatross_%28American_Motor_Boat%2C_1912%29.jpg/300px-Albatross_%28American_Motor_Boat%2C_1912%29.jpg

USS Al­ba­tro­ss, SP-1003

Pa­ge 1003 of Gra­y's ana­to­my is about the de­ve­lo­p­ment of the eye and con­tains a drawing of "the eye of an eigh­teen da­ys’ em­br­yo ra­bbi­t.".

http://www.visionembroidery.com/components/com_virtuemart/show_image_in_imgtag.php?filename=1003_Rabbit_Skin_49d05d18b2535.jpg&newxsize=240&newysize=320&fileout=

"1003 Ra­bbit Skins Snap Bi­b" (s­ca­r­y!)

Pa­ge 1003 of "Fa­mi­liar Quo­ta­tion­s, 10­th ed of 1919" sa­ys "We are dan­cing on a vol­cano­.", attri­buted to the Co­m­te de Sal­vand­y. Al­to del Za­po­te, in Cu­ba is 1003 me­ters ta­ll, but is not a vol­cano at all.

http://www.kopernik.org/images/archive/n1003.jpg

NGC 1003, a di­ffu­se spi­ral ga­la­xy in Per­seus.

Oh, and this blog has 1003 pos­ts.


Contents © 2000-2023 Roberto Alsina