Jans Blog

Blog

15.09.2020

App API Rewrite in Rust 1

Motivation

My two Android apps Metal Only and MensaUPB require some kind of online service to get their data from and - since I cannot force users to update apps easily - I wrote my own API as a wrapper to the upstream APIs provided by https://www.metal-only.de/ and https://studierendenwerk-pb.de/. That way, whenever one of these pages changes their API, I can just adapt the wrapper and keep my own API backwards-compatible so the apps themselves don’t require any updates.

The current version of the API was developed with Play! Scala. However, due to my interest in the Rust programming language I considered a rewrite. An additional advantage of switching to Rust would be the potential of switching to running the app on-demand instead of keeping it always running: a restart of the current Play! Scala API takes around 3~5 seconds.

Plan

The rewrite will be split in multiple phases due to limitted time.

The Experimental Phase will just consist of developing multiple proof of concepts to see how viable using Rust for the rewrite is. This entails querying upstream APIs and providing a REST API which can be used by my apps.

In the Cleanup Phase unused endpoints of the current API will be removed which is useful even without a rewrite. The fastest way to do this cleanup was quite simple:

The Rewrite Phase is most likely over-engineered and consists of two parts: an integration test and the actual apps. The integration test is used to ensure that the rewrite works as the currently existing app which is currently tested in production. The basic idea here is to replace the upstream APIs with simulated data (“mocks”). The first step here was to make the upstream API configurable.

Since there was never a requirement to replace the upstream API’s URL, there was no option to do so. The option was added as an environment variable and - if the environment variable is not set - defaults to the hard-coded URL (pseudo-code):

url = get_environment_variable("UPSTREAM_URL")
if is_not_set(url) {
  url = DEFAULT_UPSTREAM_URL
}

Additionally, I wanted to use this rewrite to further my skills with Docker and docker-compose: the rewritten API, the current API, the integration test, and the mocked upstream APIs should all be started with a single command, i.e. docker-compose up app-api-rewrite.

Current State

The rewrite started in March 2023 and was paused for a few months. While experimenting and cleaning up were done quite fast, the rewrite itself is still in progress. One of the major factors is the deliberate over-engineering.

01.07.2020

Oral Examination Notes for Type Systems for Correctness and Security

After sharing the minutes of my examination in Type Systems for Correctness and Security multiple times via email, I decided to provide a direct download on this blog.

Click here to download my Oral Examination Notes for Type Systems for Correctness and Security.

Click here to download the corresponding .tex source file

01.05.2020

Switching to English

Previous posts on this blog are primarily in German. Future posts will be written in English.

01.05.2020

Switching to English

Previous posts on this blog are primarily in German and will not be translated. Future posts will be written in English.

12.06.2017

Trouble mit dem Play Store


Metal Only wurde aus Play Store entfernt

Eines meiner Projekte - die App für den Metal Only e. V. - wurde vor kurzem aus dem Play Store entfernt. In diesem Post möchte ich kurz beschreiben, welche Schritte notwendig waren, die App wieder in den Store zu bekommen.

Post ganz lesen
12.06.2017

Trip Home


On the Ferry: Turku -> Stockholm
On the Ferry: Turku -> Stockholm

Um den Abschluss des Auslandssemesters gebührend zu feiern, ging es für mich nicht direkt nach Hause. Mit einem mehr oder weniger kleinen Umweg ging es für mich nach Stockholm, København und München.

Post ganz lesen
01.06.2017

Kruunuvuoren nuotiopaikka


Abandoned House

Wieder ein lange aufgeschobener Eintrag. Am 25. März war ich mit ein paar Leuten in einem “Abandoned Place”: Kruunuvuoren nuotiopakkia. Leider hats auch einen traurigen Grund, warum ich das jetzt poste: irgendwer hat die Häuschen abgefackelt :(

Post ganz lesen
22.04.2017

Letzter Tag in Lappland


Sternbild Orion

Am letzten Tag war morgens Langlauf angesagt. Abends gingen wir in die Sauna und du wirst nicht glauben, was dann passiert ist! (Clickbait, yeah \o/)

Post ganz lesen
21.04.2017

Lappland - Ankunft in Ivalo


Sonnenuntergang in der Nähe des Naverniemi Holiday Centers

Der zweite Tag bestand aus der Fahrt nach Ivalo, der Ankunft im Naverniemi Holiday Center und zwei Wanderungen auf dem Ivalo-River.

Post ganz lesen
20.04.2017

Lappland - Anreise & Rovaniemi


Gruppenfoto vor Riesen-Schneemann im Santa Claus Village

Letzten Monat war eine Reise nach Lappland angesagt. Bisher kam ich aber noch nicht dazu, irgendwas darüber zu schreiben, da ich anderweitig beschäftigt war. In den nächsten Tagen kommt daher nach und nach alles, was es dort so interessantes gab ;)

Post ganz lesen
15.02.2017

Wanderung in Nuuksio


Eiswand

Am 02.02. war eine Wanderung von Hydepakki in Nuuksio angesagt. Hydepakki ist eine Studierendenorganisation, die Wnaderungen und andere Events – beispielsweise Sauna am Strand mit Baden im Eismeer ;) – organisiert.

Post ganz lesen
06.02.2017

Metal-Only App

Diese App wurde ursprünglich von den Codingspezis entwickelt und später von mir übernommen. In dieser Kategorie gibt es gelegentlich Neuigkeiten über die Entwicklung, geplante Features und weiteres.

02.02.2017

Tagesausflug nach Tallinn


Blick über die Stadt

Eckeröline hat zur Zeit ein sehr gutes Winterangebot: extrem günstige Tagesausflüge nach Tallinn. Diese Gelegenheit haben einige andere Studenten und ich vor ca. zwei Wochen genutzt, um Tallinn zu besuchen.

Post ganz lesen
23.01.2017

Lux Helsinki

"Flowers of Life" at Unioninkatu 38

Durch Zufall bin ich gerade noch rechtzeitig für Lux Helsinki hier angekommen. Diese Gelegenheit hab ich genutzt, um einmal durch die Stadt zu streifen und ein paar Fotos zu machen.

Post ganz lesen
23.12.2016

Umzug zu Github Pages

Heute habe ich meinen Blog von Wordpress zu Jekyll umgezogen, damit ich leichter Beiträge verfassen kann. Der größte Vorteil durch die Umstellung ist die Möglichtkeit, offline zu schreiben und später via git push die Veröffentlichung anzustoßen. Gehostet wird über Github Pages. Das verwendete Theme nennt sich hagura und wurde von mir leicht angepasst.

23.05.2016

Internet defekt – Techniker ist informiert

Nach einem Umzug mit Unitymedia dauerte es länger als einen Monat, bis wieder Internet zur Verfügung stand. Grund war eine Störung, über deren Existenz Unitymedia widersprüchliche Angaben machte. Die volle Geschichte:

Post ganz lesen
29.02.2016

MensaUPB 2.18.7

Download: MensaUPB-2.18.7

06.12.2015

Metal Only 0.4.16

Nachdem rbrtslmn keine Zeit mehr hatte, die Entwicklung fortzuführen, habe ich die offzielle Android-App von Metal Only e.V. übernommen. Die neueste Änderung ist der Fix eines Absturzes, der über Google Play gemeldet wurde.

metal-only-release-0.4.16

22.11.2015

MensaUPB 2.18.6

Seit 2.15.4 gab es einige Bug-Fixes und ein paar Funktionserweiterungen. Hier eine kurze Aufführung:

Download: MensaUPB-2.18.6

23.04.2015

MensaUPB 2.15.4

Primär Verbesserungen im Design: MensaUPPB-2.15.4

29.03.2015

MensaUPB 2.15.0

Verbesserungen in der Oberfläche und folgende Fehler wurden behoben:

MensaUPB-2.15.0

06.03.2015

Mockup Node Detailscreen

Some mockups for a node detail screen… Looking for a designer nonetheless 😉

Mock: online freifunk node

 

Mockup: Offline freifunk node

10.02.2015

MensaUPB 2.11.2

Kleine Fixes ggü. 2.11.1 (Feature regression)

MensaUpb-2.11.2

08.02.2015

MensaUPB 2.11.1

Neu:

Download: MensaUPB-2.11.1

18.01.2015

MensaUPB 2.9.0

Neu: Campus Mensae in einer Liste!

MensaUPB-2.9.0

Verworfen aufgrund von größerem Bug: Pull to refresh; Download: MensaUPB-2.8.5

30.12.2014

Cleaning some data with Clojure 3

The first post in this series was about the original problem: there is a data-set of strings with potential duplicates and many typos. The second was about computing the similarity of two strings. In this post we will focus on the question

Can we group multiple strings of a set by similarity?

Matching the names: The first step is to match the names. I hope, that the following code is documented good enough.

(defn- singleMap 
  "Produces a function that takes one argument str2 and computes
  [str1 str2 (f str1 str2)]."
  [f str1]
  (fn [str2]
    [str1 str2 (f str1 str2)]))

(defn- split? 
  "Produces a split predicate based on a."
  [str] 
  (fn [x] 
    (pos? (compare str x))))

(defn- getAfterStr 
  "Returns the elements after str of the supplied sequence"
  [str strings]
  (get (split-with (split? str) strings) 1))

(defn- singleMapWAll
  "Produces a producer function that maps the matching function
  with a to all elements after a."
  [f strings] 
  (fn 
    [str1] 
    (map (singleMap f str1) getAfterStr)))

(defn match
  "Uses the given similarity function f and matches all
  strings with each other string once"
  [f strings]
  ; Applys the function produced by singleMapWAll to strings
  ; and concatenates the result
  (mapcat (singleMapWAll f strings) strings)) 

The following table provides some more information. a, b, c, d, and e are the strings to be matched. “f str1” denotes that a function “f str1” is mapped onto the corresponding string in the last row (which will be used as str2 in the matching).

(f d)
(f c f c)
(f b f b f b)
(f a f a f a f a)
a b c d e

Whereas map would produce a mapping as above, mapcat applys the mapping and concatenates the results as displayed below.

(f a f a f a f a f b f b f b f c f c f d)
a b c d e c d e d e e

After all matching fs are applied, we get our result tuples in one list:

([a b (f a b)] [a c (f a c)] [d e (f d e)])

Filtering the results: This filtering step is just an intermediate step. We will use the results find good “grouping” values. Remember that a result vector has the form [str1 str2 (similarity str1 str2).

(defn- minSimFilterBuilder 
  [min-sim]
  (fn [x]
    (>= (get x 2) min-sim)))

(defn- unequalName? 
  [x]
  (not (= (get x 0) (get x 1))))

(defn cleanMatches 
  [matches min-sim]
   (filter (minSimFilterBuilder min-sim) 
    (filter unequalName? matches)))

minSimFilterBuilder is used to construct filter functions based on a minimum similarity value. The function unequalName? simply checks, if the names of the result tuple are different. The result of cleanMatches will then contain only matches with distinct strings that are “similar enough” for us.

Constructing clusters: sadg

27.12.2014

Cleaning some data with Clojure 2

Cleaning some data with Clojure 1” provides some context on this project. This post here focusses on the question

“Can we tell, if two strings are similar?”.

Levenshtein Distance: My first idea was to use the Levenshtein Distance. This metric counts the minimum number of operations needed to transform one string into the other when allowing insertion, deletion or substition of characters. Its definition is as follows:

Definition of Levenshtein Distance<figcaption class="wp-caption-text">Definition of Levenshtein Distance</figcaption></figure>

indicator_functiondenotes the indicator function, which is equal to 0 when the i-th character of a is equal to the j-th character of b. Otherwise it’s 1.

Transforming this into clojure-code is fairly simple, given the definitions. First, the indicator-function:

(defn indicator
  "0, if the i-th character of a and the j-th character of b
are the same. 1 otherwise."
  [a b i j]
  (if (= (get a (dec i)) (get b (dec j)))
   0
   1))

Next, a helper function which will be called recursively:

(defn lev
  "Helper function to compute the levenshtein distance"
  [a b i j]
  (if (zero? (min i j))
   (max i j)
   (min (inc (lev a b (dec i) j))
        (inc (lev a b i (dec j)))
        (+ (lev a b (dec i) (dec j)) (indicator a b i j)))))

And finally the “public api”:

(defn levenshtein
  "Computes the Levenshtein distance between two strings a and b"
  [a b]
  (lev a b (count a) (count b)))

Unfortunately, this solution is not very performant [0]. Also it blows the stack somewhere above 50 characters (tested for 140 characters). Nonetheless, this is enough for my use-case 😉

=> (def alphabet "alphabetdefghijklmnopqrstuvwxyz")
#'/alphabet
=> (time (levenshtein (subs alphabet 0 10) (subs alphabet 0 10)))
"Elapsed time: 3855.856306 msecs"
0
=> (time (levenshtein (subs alphabet 0 11) (subs alphabet 0 11)))
"Elapsed time: 21493.424412 msecs"
0

We have this performance issue because most values are computed multiple times; some are computed extremely often. The solution in this case is a well-placed memoize to reuse the computed values: (def lev (memoize lev)). All in all we get a huge performance boost [1]:

=> (time (levenshtein (subs alphabet 0 11) (subs alphabet 0 11)))
"Elapsed time: 1.230304 msecs"
0
=> (time (levenshtein (subs alphabet 0 15) (subs alphabet 0 15)))
"Elapsed time: 1.280788 msecs"
0
=> (time (levenshtein alphabet alphabet))
"Elapsed time: 3.099704 msecs"
0
=> (time (levenshtein rnd_50_1 rnd_50_2))
"Elapsed time: 11.464978 msecs"
38
=> (time (levenshtein rnd_140_1 rnd_140_2))
StackOverflowError clojure.lang.AFn.applyToHelper (AFn.java:148)

Note, that “rnd_X_Y” are distinct pseudo-random strings that were “generated” by hitting the keyboard X times.

Extending this metric: This metric does not take into account that \”o is equivalent to ö. We can fix this by replacing these special values with more common ones before computing the distances:

(defn cleanString
  "A hacky cleaning function. Replaces unusual characters by more common ones."
  [s]
  (switch "\"A" "Ä"
   (switch "\"O" "Ö"
    (switch "\"U" "Ü"
     (switch "\"a" "ä"
      (switch "\"o" "ö"
       (switch "\"u" "ü"
        (switch "\\ss{}" "ß" s))))))))))))

And for a vector etc. of strings:

(defn clean 
  "Cleans a vector of strings"
  [strings]
  (map cleanString strings))

From distance to similarity: Our next step is to convert distances between two strings into a “similarity value”. The Levenshtein distance of two strings is (<= 0 (levenshtein a b) (max (count a) (count b))). Therefore we can convert a Levenshtein distance to a similarity metric as follows:

(defn levensthein-similarity
  "Computes a similarity measure based on Levenshtein distance. 0 ~ a and b completely different, 1 ~ a and b equal"
  [a b]
  (- 1 (/ (levensthein a b)
          (max (count a) (count b)))))

TL;DR: Use an appropriate String Metric and convert it to a similarity metric. I tried out Levenshtein Distance which does not takes into account that \”o and ö are equivalent.

[0] “We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.” (Knuth, Donald. Structured Programming with go to Statements, ACM Journal Computing Surveys, Vol 6, No. 4, Dec. 1974. p.268.); from clean-code-developer.de

[1] These numbers mean next to nothing: we don’t know yet if these improvements hold true for random strings. This will probably be on a later post.


Edit: Better code listings.

 

 

25.12.2014

Cleaning some data with Clojure 1

Just playing around with some distance measures for strings and some test data. Post ganz lesen
14.12.2014

Krombels Dash 0.1.1

Kleinere fixes, und Anpassungen an das Krombel Design.

Deinstallation vor update notwendig: krombel-0.1.1

ffpb-krombel

14.12.2014

Krombels Dashboard auf Android

Ein kleiner Prototyp, der Krombels Dashboard auf Android Geräte bringt: krombel-0.1.0.

Features:

Die App ist nicht über den Play Store verfügbar; dies wird später der Fall sein. Quellcode gibt es auf github.

ffpb-krombel-device

12.12.2014

MensaUPB 2.7.5

MensaUPB-v2.7.5

Neu:

29.11.2014

MensaUPB 2.7.0

MensaUPB-2.7.0 als Beta veröffentlicht. Neu sind hier vor allem die Badges (laktosefrei, glutenfrei, vegan, …).

Hier noch die Unterschiede zur Play Store Version:

17.11.2014

MensaUpb 2.4.0

MensaUpb-2.4.0 … mit einem ersten Blick auf das Material Design. Bisher nur die support library version angepasst.

14.11.2014

MensaUPB 2.3.0

MensaUPB-2.3.0

08.11.2014

MensaUPB 2.1.9

MensaUPB-2.1.9

Behoben: Manche Gerichte wurden in der Übersichtsliste fehlerhaft mit “Preis/100g” ausgezeichnet.

01.11.2014

MensaUPB 2.1.7

Kleinere Fixes: MensaUPB-2.1.7

16.10.2014

MensaUPB 2.1.5

Bei einigen Benutzern kam es zu einem Absturz nach dem Upgrade. Dieser wurde nun behoben: MensaUPB-2.1.5

14.10.2014

MensaUPB 2.1.3

Ein neues Release. Seit einiger Zeit im PlayStore, jetzt auch hier: MensaUPB 2.1.3

26.09.2014

MensaUPB Beta

Ab heute gibt es ein Beta-Release von MensaUPB mit der neuen API des Studentenwerks. Um diese zu testen, müsst ihr folgendes tun:

  1. Tretet der Google+ Community oder der Beta-Mailingliste bei.
  2. Anschließend besucht ihr diese Seite und werdet Tester.
  3. Sollte die App noch nicht installiert sein, könnt ihr diese aus dem Play Store herunterladen.

Falls die App bereits installiert ist, entfällt der letzte Schritt: es gibt ein automatisches Update auf die Beta-Version.

Ihr könnt jederzeit unter dieser Seite entscheiden, ob ihr Tester sein wollt oder nicht. Die App “wechselt” dann automatisch.

31.05.2014

OpenVPN der UPB unter Linux

Auf den Seiten des IMT findet sich eine Anleitung zum Einrichten des VPN-Zugriffs. Dort wird auch eine .conf-Datei bereitgestellt, welche vom Benutzer importiert werden soll. Da diese bei mir einige Probleme verursachte (VPN Zugriff nicht möglich, Freeze im Einstellungsbildschirm), habe ich folgende Ergänzungen zur IMT-Anleitung:

Hier eine von mir minimal korrigierte openvpn-upb. Die Pfade (s.o.) müssen noch korrigiert werden.

Edit: broken Link durch Redesign der Uni-Homepage gefixt.

24.05.2014

MensaUPB 1.8.0

Auf expliziten Wunsch wurden in dieser Version die Preise hinzugefügt. Diese sind noch etwas buggy (Preise pro 100g werden ohne “pro 100g” dargestellt), jedoch sind die wichtigsten Gerichte aka Vorschlagsmenüs korrekt angezeigt.

MensaUPB-1.8.0

25.04.2014

MensaUPB 1.6.0

Neue Funktionen:

Apk: MensaUPB-1.6.0

18.04.2014

Fachschaftsdroid 0.0.9

Ein kleiner Bugfix, der einen reproduzierbaren Absturz behebt: fsdroid-v0.0.9

17.04.2014

MensaUPB veröffentlicht

Vor einigen Tagen war es endlich so weit: die Open Source App MensaUPB konnte wieder im Play Store veröffentlicht werden. Post ganz lesen
13.02.2014

Fachschaftsdroide Beta im Play Store

Da die Versionsverwaltung über den Play Store deutlich einfacher als über diesen Blog ist, gibt es den Fachschaftsdroiden nun im Play Store als Beta. Testen könnt ihr wie folgt, die Reihenfolge der Schritte ist zu beachten:

  1. Fachschaftsdroide: zum Beta testen https://plus.google.com/communities/112299389493406857610 oder https://groups.google.com/d/forum/fachschaftsdroide-beta beitreten
  2. Danach als Tester im Play Store anmelden https://play.google.com/apps/testing/de.upb.fsmi
  3. Probleme, Wünsche per Email an ljan@mail.upb.de
20.11.2013

Fachschaftsdroide 0.0.2

Vor der Installation müssen unbekannte Quellen aktiviert werden, eine Anleitung gibts auf https://github.com/ironjan/MensaUPB.

Download: fsdroid-0.0.2

Fehlermeldungen/Feature-Wünsche bitte per Mail an mich.

Features:

Bekannte Bugs/Ziele für die Beta:

14.11.2013

Der Fachschaftsdroide

Da der Fachschaftsdroide sich nun im Alpha-Stadium befindet, musste eine Seite zum Download her – das wäre dann mein “Blog under construction.

Vor der Installation müssen unbekannte Quellen aktiviert werden, eine Anleitung gibts auf https://github.com/ironjan/MensaUPB [0]

Download: fsdroid-alpha-0.0

Features:

Bekannte Bugs:

[0] Schamlose Eigenwerbung – der Downloadlink von MensaUPB is nicht mehr aktuell. Neues von MensaUPB gibts dann auch hier.