In meiner Freelancer-Kolumne schreibe ich diesmal über’s Programmieren.

Als Tech-Journalist schreibt man in der Regel entweder über Hard- oder über Software, selten über beides. Ich fand schon immer das letztere Thema das interessantere. Denn was ist auch die coolste Hardware ohne Code, der sie am laufen hält? Ziemlich nutzlos. Außerdem ist Software viel mehr in Bewegung als Hardware und es gibt öfter Neues zu berichten, oft auch unabhängig von irgendwelchen Hersteller-Informationen – vor allem wenn man sich auch mit Open-Source-Entwicklung befasst. Aus all diesen Gründen schreibe ich seit jeher hauptsächlich über Software; seien es nun Sicherheitslücken, Betriebssysteme, Spiele oder Anwenderprogramme.

Bevor ich Journalist wurde, habe ich eigentlich regelmäßig selbst programmiert. Nicht in C oder .NET, aber in Java, Python und JavaScript. In meinem ersten Studentenjob war ich sogar als Programmierer angestellt – damals habe ich zusammen mit mehreren anderen studentischen Hilfskräften eine Web-Applikation zur Qualitätssicherung und Nachverfolgung von Waren in der Landwirtschaft entwickelt. Nachdem ich anfing, Vollzeit zu arbeiten, habe ich mich zwar immer mal wieder der Webentwicklung hingegeben, aber das beschränkte sich in der Regel aufs Design (sprich: HTML und CSS).

Ich fand es eigentlich immer sehr schade, dass ich als Journalist nebenbei nie wirklich Zeit hatte, zu programmieren. Ich denke nämlich, dass man in meinem Job nur dann richtig gut ist, wenn man weiß, wo von man redet. Und als jemand, der den ganzen Tag über Software schreibt, sollte man möglichst viele Programmierkentnisse haben, finde ich. Leider hat man in meinem Beruf ständig irgendwas zu recherchieren oder zu schreiben und die Freizeit ist eh schon so dünn gesäht, dass man da meistens auch keine Lust hat, Sachen zu machen, die so nah am Job dran sind, dass man sie eigentlich in der Arbeitszeit erledigen müsste. Und so habe ich es nicht mal bei Heise geschafft, nebenbei auch nur ein einziges Software-Projekt umzusetzen …auch wenn ich daraus locker einen oder mehrere Artikel hätte machen können.

Nun bin ich allerdings seit Anfang des Jahres mein eigener Chef und kann mir selbst aussuchen, worauf ich meine Arbeitszeit verwende. Deswegen habe ich mir vorgenommen, wieder zu programmieren. Diese Woche habe ich damit angefangen, mein erstes Projekt anzugehen. Ich habe mich bei der Umsetzung für Python und Django entschieden, da ich mit diesen beiden Techniken die meiste Erfahrung habe und mir das Arbeiten damit am meisten Spaß macht.

Die Idee, was ich programmieren will, kam mir passenderweise vor ein paar Wochen auch gleich. Seitdem ich wieder angefangen habe, Magic the Gathering zu spielen, erfasse ich meine Kartensammlung von Hand in Textdateien. Das funktioniert ganz gut, ist aber mühseliger, als es sein muss. Also habe ich mich dafür entschieden, eine Django-Web-App zu bauen, mit der ich meine Sammlung erfassen und anzeigen kann – ich habe nämlich in der Richtung keine Software gefunden, die mir gefällt. Nachdem ich ein paar Stunden damit verbracht habe, ein mögliches Datenbank-Modell zum Speichern der Kartendaten zu entwerfen, habe ich dann Ende der Woche damit angefangen, Code zu schreiben. Dabei bin ich relativ schnell wieder in die Arbeit mit Django hineingekommen, so dass ich mittlerweile ein funktionierendes Admin-Interface zum Erfassen der Karten habe. Heute morgen habe ich dann auch noch ein Icon für die App entworfen, die ich Mage Tower nenne.

Als nächstes muss ich nun den Hauptteil des Programms entwerfen und den Code schreiben, der meine Kartensammlung anzeigen soll. Ich freue mich schon drauf, weiter an dem Ding zu basteln. Es hat richtig Spaß gemacht, mich wieder ins Programmieren mit Python und Django reinzufuchsen. Nach so langer Zeit hätte ich mir das viel mühseliger vorgestellt.


Nachtrag: Wen es übrigens interessiert, wie viele Informationen man zu einer einzigen Magic-Karte speichern muss, wenn man sie komplett erfassen will, der kann sich den folgenden Code für mein entsprechendes Datenbank-Modell anschauen. Und dazu kommen dann noch Klassen, die Daten zu den einzelnen Magic-Editionen und zur Anzahl der Karten (normale Ausgaben und Foil-Versionen) in der Sammlung enthalten.

    class Card(models.Model):
      LAND = 'L'
      COMMON = 'C'
      UNCOMMON = 'U'
      RARE = 'R'
      MYTHIC = 'M'
      RARITY_CHOICES = (
        (LAND, 'Land'),
        (COMMON, 'Common'),
        (UNCOMMON, 'Uncommon'),
        (RARE, 'Rare'),
        (MYTHIC, 'Mythic Rare'),
      )
      name = models.CharField(max_length=200)
      slug = models.SlugField(max_length=220, unique=True, primary_key=True)
      cost_w = models.IntegerField(blank=True, null=True)
      cost_u = models.IntegerField(blank=True, null=True)
      cost_b = models.IntegerField(blank=True, null=True)
      cost_r = models.IntegerField(blank=True, null=True)
      cost_g = models.IntegerField(blank=True, null=True)
      cost_c = models.IntegerField(blank=True, null=True)
      cost_wu = models.IntegerField(blank=True, null=True)
      cost_wb = models.IntegerField(blank=True, null=True)
      cost_cw = models.IntegerField(blank=True, null=True)
      cost_ub = models.IntegerField(blank=True, null=True)
      cost_ur = models.IntegerField(blank=True, null=True)
      cost_cu = models.IntegerField(blank=True, null=True)
      cost_br = models.IntegerField(blank=True, null=True)
      cost_bg = models.IntegerField(blank=True, null=True)
      cost_cb = models.IntegerField(blank=True, null=True)
      cost_rg = models.IntegerField(blank=True, null=True)
      cost_rw = models.IntegerField(blank=True, null=True)
      cost_cr = models.IntegerField(blank=True, null=True)
      cost_gb = models.IntegerField(blank=True, null=True)
      cost_gw = models.IntegerField(blank=True, null=True)
      cost_cg = models.IntegerField(blank=True, null=True)
      art = models.FileField(upload_to='img/card/', blank=True)
      supertype = models.CharField(max_length=50, blank=True)
      type = models.CharField(max_length=50, blank=True)
      subtype = models.CharField(max_length=50, blank=True)
      expansion = models.ForeignKey(Expansion,
      on_delete=models.CASCADE)
      power = models.IntegerField(blank=True, null=True)
      toughness = models.IntegerField(blank=True, null=True)
      loyalty = models.IntegerField(blank=True, null=True)
      rules = models.TextField(blank=True)
      flavour = models.TextField(blank=True)
      number = models.IntegerField(blank=True, null=True)
      max_number = models.IntegerField(blank=True, null=True)
      rarity = models.CharField(
          max_length=1,
          choices=RARITY_CHOICES,
          default=COMMON,
      )
      artist = models.CharField(max_length=50, blank=True)
      errata = models.TextField(blank=True)
      url = models.URLField(max_length=220, unique=True)
      added = models.DateField(auto_now_add=True)
      modified = models.DateField(auto_now=True)