Drafts-Rezepte VII: Postleitzahlen mit Zippopotamus II

Der zweite Teil zu Postleitzahlen mit Zippopotamus geht in die entgegengesetzte Richtung. Statt nun also eine Postleitzahl zu ermitteln, geht es nun um die Städte oder Stadtteile hinter eine Postleitzahl.

Das Script ist wieder genauso einfach wie das erste. In Drafts muss eine neue Notiz nur die folgende Syntax einhalten:

[PLZ],[LÄNDERKÜRZEL]

Beispielsweise würde die folgende Eingabe der berühmten Postleitzahl eine Markdown-Liste in Drafts öffnen.

90210 , us

Auch hier kommt ein kleiner Haken ins Spiel, auch wenn er nicht ganz so lästig ist. Denn die API von Zippopotamus erlaubt es derzeit nicht, das Land nicht anzugeben. Unter Umständen müssen wir also mehr Infos angeben, als wir haben. Das Python-Script sieht derzeit so aus:

# -*- coding: utf-8 -*-
from console import alert
from json import loads
from sys import argv, exit
from urllib import urlopen, quote
import webbrowser

def error_dialog(title, message):
    '''
    a diaolog box for error messages.
    '''
    try:
        alert(title, message)
    except KeyboardInterrupt:
        pass
    webbrowser.open('drafts://')
    exit(message)

def handle_data(data):
    '''
    process json response from zippopotamus.
    will return a markdown list of items.
    '''
    city_json = loads(data)
    output = ''
    for item in city_json['places']:
        output += '- Ort: {place}, Region: {state}\n'.format(place=item['place name'], state=item['state'])
    return output

def get_by_postalcode(data):
    '''
    get all possible cities for a postal code in
    the given country.
    '''
    api_url_base = 'http://api.zippopotam.us/{country}/{postcode}'
    try:
        postcode_, country_= [item.strip() for item in data.split(',')]
    except Exception as err:
        error_dialog(str(err.__class__), err.message)

    try:
        response = urlopen(api_url_base.format(country=country_, postcode=postcode_))
    except IOError:
        error_dialog('Connection Error', 'Unable to perform request.')
    if response.getcode() == 200:
        postcode_data = handle_data(response.read())
        webbrowser.open('drafts://x-callback-url/create?text={0}'.format(quote(postcode_data)))
    else:
        error_dialog('Error', 'Status code: {0} - Message: {1}'.format(response.getcode(), response.read()))

if __name__ == '__main__':
    get_by_postalcode(argv[1])

Gist

Die dazugehörige Action für Drafts ist:

pythonista://city-by-postalcode?action=run&argv=[[draft]]

Import-Link

Drafts-Rezepte VI: Postleitzahlen mit Zippopotamus I

Den kleinen Gag kann ich mir nicht verkneifen. Ich mache zu Zippopotamus, einem guten, kleinen Dienst für Postleitzahlen in einer Vielzahl von Ländern, zwei Teile. Damit artet die Kleinteiligkeit aus wie der überbordende Song-Kosmos von Coheed & Cambria.

Das Script ist wieder sehr einfach. In Drafts muss eine neue Notiz nur die folgende Syntax einhalten:

[STADT], [REGIONSKÜRZEL], [LÄNDERKÜRZEL]

Beispielsweise würde die folgende Eingabe die berühmte Postleitzahlen für die Stadt als Markdown-Liste in Drafts öffenen.

beverly hills, ca, us

Leider kommt diese Sache nicht ohne einen kleinen Haken aus. Denn die API von Zippopotamus erlaubt es derzeit nicht, die Region und das Land nicht anzugeben. Unter Umständen müssen wir also mehr Infos angeben, als wir haben. Ich bin dabei, mir dafür eine Lösung zu überlegen. Das Python-Script sieht derzeit so aus:

# -*- coding: utf-8 -*-
from console import alert
from json import loads
from sys import argv, exit
from urllib import urlopen, quote
import webbrowser

def error_dialog(title, message):
    '''
    a diaolog box for error messages.
    '''
    try:
        alert(title, message)
    except KeyboardInterrupt:
        pass
    webbrowser.open('drafts://')
    exit(message)

def handle_data(data):
    '''
    process json response from zippopotamus.
    will return a markdown list of items.
    '''
    city_json = loads(data)
    output = ''
    for item in city_json['places']:
        output += '- City: {place}, Post code: {postcode}\n'.format(place=item['place name'], postcode=item['post code'])
    return output

def get_by_city(data):
    '''
    get all possible post codes for a city in
    the given country.
    '''
    api_url_base = 'http://api.zippopotam.us/{country}/{state}/{city}'
    try:
        city_, state_, country_= [item.strip() for item in data.split(',')]
    except Exception as err:
        error_dialog(str(err.__class__), err.message)

    try:
        response = urlopen(api_url_base.format(country=country_, state=state_, city=city_))
    except IOError:
        error_dialog('Connection Error', 'Unable to perform request.')
    if response.getcode() == 200:
        city_data = handle_data(response.read())
        webbrowser.open('drafts://x-callback-url/create?text={0}'.format(quote(city_data)))
    else:
        error_dialog('Error', 'Status code: {0} - Message: {1}'.format(response.getcode(), response.read()))

if __name__ == '__main__':
    get_by_city(argv[1])

Gist

Die dazugehörige Action für Drafts ist hier:

pythonista://postalcode-by-city?action=run&argv=[[draft]]

Import-Link

Drafts-Rezepte IIII: Hashes berechnen

Dann geht es fröhlich in der Reihe kleiner Skripte für Drafts und Pythonista weiter. Da ich hin und wieder mal Hashes als Prüfsummen benötige, hatte ich immer eigene Apps dafür auf dem iPhone. Da diese allerdings für ihren eigentlichen Zweck zu groß waren – sie waren allesamt auch noch hässlich -, habe ich sie nun durch folgendes Skript ersetzt. Das läuft bei mir ganz ordentlich.

Hierzu brauche ich wieder einen Action in Drafts, die in diesem Fall auf das Skript hasher in Pythonista zugreifen soll. Das sieht so aus:

pythonista://hasher?action=run&argv=[[draft]]

Import-Link

Etwas umfangreicher, daher auch erklärungsbedürftiger ist das Skript in Python. Dieses habe ich mal im Grunde angelegt, dass es wie ein Programm für die Shell genutzt werden kann.

# -*- coding: utf-8 -*-
import argparse
import clipboard
import hashlib
from sys import argv
import webbrowser

def hash_data(hashstring, hashfunction, url):
    '''Get the hash of given string.'''
    # Determine if a method for hashfunction
    # exists in hashlib. If no attribute/method is
    # found, default to sha1.   
    if hasattr(hashlib, hashfunction):
      hash_method = getattr(hashlib, hashfunction)
    else:
    	hash_method = hashlib.sha1

    # Put hash to clipboard, if hashstring exists.
    if hashstring:
    	clipboard.set(hash_method(hashstring).hexdigest())
    else:
        raise ValueError

    # Pythonista doesn't support x-callback.
    # So this is a pragmatic approach to calling
    # another app after hashing the string.
    webbrowser.open(url)

def parse_input(data):
    '''Parse input from Drafts command-line-like.'''
    parser = argparse.ArgumentParser(description='input a string to hash.')

    # Expects strings to hash.
    parser.add_argument('inputstring',
                        metavar='STRING',
                        nargs='*',
                        help='the string to hash')

    # Set the hash function.
    parser.add_argument('-hs', '--hs', '-hash', '--hash',
                        metavar='HASH-NAME',
                        default='sha1',
                        dest='hash',
                        help='the hash function of hashlib to use. defaults to sha1')

    # Intended to set a callback-like action.
    # Use to open a specific app via url scheme, if necessary. Otherwise will open Drafts.
    parser.add_argument('-u', '--u', '-url', '--url',
                        metavar='URL',
                        default='drafts://',
                        dest='url',
                        help='url scheme to call after hashing. use to call an app.')

    args = parser.parse_args(data)
    hash_data(' '.join(args.inputstring), args.hash, args.url)

if __name__ == '__main__':
    parse_input(argv[1].split(' '))

Gist

Ich muss also etwas erklären, was das Skript macht. Es kann drei Sorten von Input verarbeiten:

-hs (–hs, -hash, –hash) HASH_BEZEICHNUNG: Dieser Befehl ist optional und gibt die zu verwendenden Hashfunktion* an. Ist die Bezeichnung unbekannt oder falsch, wird der Standardwert ’sha1′ angenommen.

-u (–u, -url, –url) URL_SCHEMA: Dieser Befehl ist optional und gibt das URL-Schema einer aufzurufenden Anwendung an. Standardmäßig ist ‚drafts://‘ vorgegeben.

inputstring *STRING: Alle übrigen Übergabewerte werden zum Inputstring hinzugefügt, der gehasht wird.

Es lässt sich doch besser mit einigen Beispielen erklären. Nehmen wir mal folgenden Eingaben in Drafts an, wobei jede Zeile einer Eingabe entspricht:

Test

Test -s md5

Test -u tweetbot://

Test und noch mehr Test -s sha512

Die erste Eingabe würde den SHA1-Hash von ‚Test‘ berechnen, die zweite würde denselben String als MD5-Hash berechnen. Beide Eingaben führen dazu, dass nach der Berechnung in Pythonista wieder Drafts aufgerufen wird. Die dritte Eingabe berechnet den Standard, also SHA1, von ‚Test, kehrt aber nicht nach Drafts zurück, sondern öffnet Tweetbot (sofern es installiert wurde). Die letzte Eingabe nimmt ‚Test und noch mehr Test‘ und berechnet den SHA512-Hashwert für die Zeichenfolge.

* Laut der Dokumentation für Python 2.7, das von Pythonista genutzt wird, werden folgende Hashfunktionen unterstützt:

This module implements a common interface to many different secure hash and message digest algorithms. Included are the FIPS secure hash algorithms SHA1, SHA224, SHA256, SHA384, and SHA512 (defined in FIPS 180-2) as well as RSA’s MD5 algorithm (defined in Internet RFC 1321).

Drafts-Rezepte III: hAppy

Ein kleines Häppchen zwischendurch für den meines Erachtens grundsätzlich sympathischsten Client für ADN, den ich auf iOS finden konnte. hAppy* ist eine Grinsebacke. Es unterstützt auch einige Aktionen über das URL-Schema. Nicht alles lässt sich hier praktisch umsetzen. Sinnvoll sind aus meiner Sicht diese Actions, um sie aus Drafts zu starten.

Neuen Post erstellen

happy://create?text=[[draft]]

Link-Import

Post anzeigen
Eigentlich eine schöne Sache, allerdings müssen wir für die Posts ihre numerische ID angeben.

happy://post?postId=[[draft]]

Link-Import

Profil anzeigen
Auch hier ist die ID ein numerischer Wert, nicht aber der öffentlich bekannte Name.

happy://profile?userId=[[draft]]

Link-Import

Die folgenden Actions sind aus Drafts heraus etwas unpraktisch, weil sie eigentlich nur Ansichten in hAppy öffnen. Außerdem wird die Angelegenheit dadurch erschwert, dass Drafts keine leeren Drafts als Ausgangspunkt für Actions erlaubt. Das ist nachvollziehbar, aber führt dazu, dass mindestens ein Zeichen gegeben werden muss, damit eine der folgenden Actions zündet.

Stream öffnen

happy://stream

Link-Import

Mentions öffnen

happy://mentions

Link-Import

Direktnachrichten öffnen

happy://messages

Link-Import

Explore öffnen

happy://explore

Link-Import

Mit den nächsten kleinen Rezepten wird es noch spannender, weil ich mich daran machen werde, deutlich mehr Funktionalität einzubinden.

* Für den Theme-Editor per IAP lohnt sich hAppy schon. Das ist unter den ADN-Clients extrem innovativ.

Drafts-Rezepte II: Kurzlinks mit is.gd

Es ist ja durchaus sinnvoll, Links ab und an mal zu verkürzen, jedoch dafür extra eine App zu installieren erscheint mir etwas übertrieben. Das lässt sich zwar auch im Browser erledigen, artet aber in eine Klickorgie aus, auf die ich auch keine Lust habe. Mit Pythonista und Drafts ist das im Handumdrehen erledigt.

Dieses kleine Script habe ich als isgd.py im Dokumentenverzeichnis von Pythonista angelegt:

import clipboard
from console import alert
from sys import argv, exit
from urllib import urlopen
import webbrowser

def error_dialog(title, message):
	'''A diaolog box for error messages.'''
	try:
		alert(title, message)
	except KeyboardInterrupt:
		pass
	webbrowser.open('drafts://')
	exit(message)

def shorten_with_isgd(long_url):
	'''basic link-shortening via is.gd.'''
	api_url_base = 'http://is.gd/create.php?format=simple&url='
	try:
		response = urlopen(api_url_base + long_url)
	except IOError:
		error_dialog('Connection Error', 'Unable to perform request.')
	if response.getcode() == 200:
		short_link = response.read()
		clipboard.set(short_link)
	else:
		error_dialog('Error', 'Status code: {0} - Message: {1}'.format(response.getcode(), response.read()))
	webbrowser.open('drafts://')

if __name__ == '__main__':
	shorten_with_isgd(argv[1])

Gist

Es ist alles recht simpel gehalten, die Fehler werden nur rudimentär behandelt, aber es sollte ganz gut aufgehen. is.gd habe ich für den Anfang als Dienst gewählt, weil dort nicht großartig Registrierungen notwendig sind, um mit der API Links zu kürzen.

In Drafts muss dann nur die folgende Action angelegt werden, um das Script auszuführen.

pythonista://isgd?action=run&argv=[[draft]]

Import

Die Handhabung ist nun denkbar einfach: In Drafts in einer neuen Notiz die lange URL eingeben, danach die Action auslösen. Es wird sich Pythonista öffnen. Sofern keine Fehlermeldung aufpoppt, ist der Kurzlink in die Zwischenablage kopiert worden. Auf jeden Fall wird aber wieder Drafts geöffnet.

Drafts-Rezepte I: Yubnub

Mit Drafts ist die Automatisierung auf iOS sehr viel einfacher geworden. Ich habe mir vorgenommen, in der nächsten Zeit mal ein paar nützliche Rezepte zu posten, die den Alltag erleichtern können. Spätestens wenn Pythonista ins Spiel kommt, wird es etwas komplexer. Den Anfang will ich aber mit einem vergleichsweise einfachen Fall machen, der mir das Leben aber ungemein erleichtert.

Ich habe noch nie einen Hehl aus meiner Begeisterung für Yubnub* gemacht. Auf dem Desktop wandele ich alle Browser-Adresszeilen in eine Art Kommandozeile für’s Web ab. Da das Web zu durchsuchen unter iOS eine Qual ist, hätte ich gerne Yubnub auch dort. Mit Drafts ist das sehr einfach. Ich lege ein neues Draft an, beispielsweise mit einem Yubnub-typischen Suchkürzel und den Suchbegriffen wie etwa g iPhone Bumper. In diesem Fall würde Yubnub die Anfrage nach iPhone Bumper an Google weiterleiten. Dann löse ich zum Beispiel folgende URL-Action auf den Draft aus:

Safari

http://yubnub.org/parser/parse?command=[[draft]]

Import-Link

Aber nicht alle von uns wollen im schnöden Safari durchs Web. Auch da lässt sich was machen, solange ein installierter Browser sich mit einem URL-Schema bei iOS angemeldet hat.

Chrome

googlechrome://yubnub.org/parser/parse?command=[[draft]]

Import-Link

Dolphin

dolphin://http://yubnub.org/parser/parse?command=[[draft]]

Import-Link

Das sind nur Beispiele für eine Vielzahl von iOS-Browsern, die das Schema unterstützen und damit auch nun mit Yubnub mehr Komfort ins mobile Surfen bringen. Ein Liste mit weiteren Browsern findet sich bei HandleOpenURL. Das Schema muss in der Regel nur um die Browserbezeichnung ergänzt werden.

In den kommenden Tagen werden hier noch mehr Rezepte für URL-Actions aufschlagen. Das war bislang nur der Anfang. Es geht noch sehr viel mehr.

* Yubnub als zwischengeschalteter Dienst kann natürlich alle Suchanfragen protokollieren – zusätzlich zu den eigentlichen Zielseiten. Die Abwägung von Nutzen und Datenschutzaspekten kann ich niemandem abnehmen.

Drafts: Ich automatisiere mich in Grund und Boden

Seit gestern bastele ich mit Drafts für iOS herum, das nicht mit Draft für Android zu verwechseln ist. Drafts ist weit mehr als ein Texteditor, was mir bisher aber nicht aufgegangen ist. Ich hatte übersehen, dass Drafts zu einer Schaltzentrale für Texte und die automatisierte Verarbeitung wird. Drafts unterstützt nicht nur URI Schemes, eigentlich basiert die ganze App darauf, sich über das Schema einen Workflow zu erstellen, bei dem verschiedene Apps, sofern sie das Schema unterstützen, miteinander verzahnt werden.

Eine simple Anwendung von Drafts‘ Fähigkeiten ist es beispielsweise, einen Text zu erstellen, der per selbstgeschriebener URL-Schema-Aktion bei mehreren Microblogging-Diensten gleichzeitig gepostet wird. Wir bauen uns also einen einen Kettenblitz für Twitter und app.net, wobei wir im URL-Schema einen Callback in einen Callback bauen:

drafts://x-callback-url/create?text=[[draft]]&action={{Tweet: DoctorProk}}&afterSuccess=Delete&x-success={{drafts://x-callback-url/create?text=[[draft]]&afterSuccess=Delete&action=Post%20to%20App.net}}

Damit schlage ich zwei Fliegen mit einer Drafts-Klappe. Es ist alles noch einwenig holprig in der Kette, tut seinen Dienst aber schon mehr als ordentlich.

Je umfangreicher die Callback-Kette wird, desto unübersichtlicher wird es natürlich. Mit der Zeit entstehen so URL-Schlangen, die an die abstrakte Kunst erinnern, die von BWLern in Excel fabriziert werden, weil sie nicht auf VBA umschwenken wollen. Diese Ungetüme kann kein Mensch mehr warten. Damit hat Drafts also doch eine natürliche Grenze bei den Aktionen, sobald es sich nicht mehr realistisch überblicken und warten lässt. Nicht ganz.

Es gibt noch ein weiteres Workflow-Genie auf iOS. Pythonista ist eine Perle. Schöner, praktischer und schneller geht Python auf Mobilgeräten nicht. Und manche übersehen schnell Pythonista ausgefeilte URL-Schema-Schnittstelle. Jedes Skript in Pythonista ist von externen Apps ausgührbar, sogar mit Argumenten. So können komplexere Aufgaben leicht und locker an Pythonista ausgelagert werden. Ist das Skript fertig, ruft es einfach Drafts auf, übergibt die Bearbeitung und es kann wieder weitergehen.

Mit Drafts wir schon vieles einfacher, zusammen mit Pythonista wird aus Textverarbeitung auf iOS ganz schnell elaboriertes Anrühren von Zaubertränke mit komplizierten Ingredenzien.

ifttt – Wenn dies, dann das, dann jenes und vielleicht noch solches

ifttt (if this then that) hat auch schon jede Menge Aufmerksamkeit auf sich gezogen und ist aus meinem Alltag im Netz nicht mehr wegzudenken. Es ist ebenso wie Yahoo Pipes ein Dienst, der es ermöglicht, einzelne Informationen miteinander zu verbinden und damit lästige Arbeit zu automatisieren. Dabei bietet ifttt doch weniger Möglichkeiten als Pipes, was die Quellen für Informationen und die Dienste, mit denen man diese verknüpfen kann, angeht. Dafür ist die Einstiegshürde wesentlich niedrieger. Weiterlesen

Rock on mit Yahoo Pipes

Nachdem Yahoo Pipes nun allgemein vorgestellt wurde, will ich es ein wenig plastischer machen. Da ich die Musik auf Kultprok etwas vernachlässigt habe, bin ich in mich gegangen. Dabei fiel mir auf, dass ich eigentlich viele Neuveröffentlichungen nicht mehr im Blick habe. Dann, dachte ich mir, baue ich doch mal eine Pipe, in der ich einige Quellen für Neuveröffentlichungen bündele. Gesagt, getan. Ich präsentiere im Folgenden meine Pipe für neue Alben aus Rock, Pop und Metal. Nichts Weltbewegendes darf hiervon erwartet werden. Es dürfte aber doch einen genaueren Eindruck vermitteln, was mit Pipes möglich ist. Weiterlesen

Yahoo Pipes: Informationsautonomie

Es ist unfassbar, aber RSS scheint nicht mehr richtig abheben zu können. Warum, dafür habe ich eigentlich keine geeignete Erklärung. Der Nutzen ist doch eigentlich viel zu groß, wenn man nur bedenkt, wie einfach es ist, die Nachrichten unzähliger Seiten mittels eines Aggregators übersichtlich darstellen zu können. Da RSS schon wenig bekannt und durchgesetzt ist es auch kaum verwunderlich, dass eines der nützlichsten Tools zur weiteren Bearbeitung und Manipulation von RSS-Feeds in Deutschland anscheinend kaum bis gar nicht bekannt ist. Als Beweis für die Anklage bitte ich das hohe Gericht Beweisstück A zur Kenntnis zu nehmen. Das hohe Gericht versuche fürderhin bitte, einen deutschsprachigen Artikel zu finden. Die Anklage schließt ihre Beweisführung ab. RSS gibt es praktisch seit 1999, Yahoo Pipes immerhin schon seit 2007. Da sollte doch immerhin mehr Bekanntheit für RSS und Yahoo Pipes hervorgebracht haben1. Finde ich. Also hier mein Beitrag dazu. Weiterlesen