Momenteel speel ik KeyForge en Gwent. Een van mijn eerste artikels ging over Magic: the Gathering en ook mijn 3D-geprinte dobbelsteen- en fichebakjes verschenen al op deze blog. Het is dus geen geheim dat ik van kaartspellen hou, en mijn collectie decks groeit zowel online als offline snel. Er bestaan genoeg tools om je decks voor één spel bij te houden, maar ik wil zelf iets maken dat alle spellen ondersteunt die ik speel.

Dit was ook een goede gelegenheid om Pelican te leren kennen, een statische-websitegenerator voor Python. Ik gebruik Jekyll al een tijdje, maar om extra functies te ontwikkelen (zoals die uit het vorige artikel) heb je de programmeertaal Ruby nodig. Omdat ik Ruby niet zo goed beheers, zou het veel efficiënter zijn om mijn favoriete programmeertaal Python te gebruiken. Pelican leek uitstekend te passen en dit was het perfecte project om dat zelf uit te zoeken.

Je kunt het resultaat van deze blog hier bekijken. De code en instructies om DeckLock met je eigen decks te bouwen vind je op GitHub: https://github.com/4dcu-be/DeckLock

Overzicht van mijn KeyForge-decks, gegenereerd met DeckLock

Inleiding

KeyForge

KeyForge is een spel ontworpen door dr. Richard Garfield, bekend als de bedenker van Magic: the Gathering, meer dan 25 jaar geleden. Dit spel is echter uniek: elk deck dat je koopt, is een unieke combinatie van kaarten die uitsluitend in die specifieke samenstelling bedoeld is om te spelen. Anders dan bij andere verzamelkaartspellen worden individuele kaarten niet geruild. Bovendien heeft elk deck een QR-code. Wanneer je die met de KeyForge-app scant (beschikbaar in de App Store en Play Store), wordt het deck geregistreerd op https://www.keyforgegame.com/.

Dit is perfect voor onze toepassing: alle informatie (naam, decklijst, …) van een gescand deck kunnen we rechtstreeks van de KeyForge-website halen. Het enige wat we nodig hebben is de unieke identificatiecode van het deck; al de rest halen we automatisch op. Met diezelfde identificatiecode kunnen bovendien extra statistieken over het deck (zoals de geschatte kwaliteit van de kaarten, de synergie ertussen, …) via Decks of KeyForge worden verkregen.

Pelican

Statische-websitegeneratoren zoals Jekyll en Pelican combineren je inhoud in een eenvoudig formaat (meestal Markdown of reStructuredText) met sjablonen die bepalen hoe de uiteindelijke pagina’s eruitzien. Zo ontstaan statische HTML- pagina’s die vrijwel overal kunnen worden gehost, omdat er geen databank, PHP, … nodig is. Dit elegante ontwerp met een duidelijke scheiding tussen inhoud en weergave heeft verschillende voordelen. De inhoud staat in een eenvoudig, tekstgebaseerd formaat dat gemakkelijk kan worden opgeslagen, gelezen, bewerkt en hergebruikt. Sites zijn razendsnel omdat alles vooraf wordt berekend.

Aan de slag

Ga eerst naar https://github.com/4dcu-be/DeckLock en maak je eigen fork van de repository. Gebruik vervolgens de onderstaande commando’s om je eigen repository te klonen, een virtuele omgeving te maken en alle vereiste pakketten te installeren.

git clone <url to your fork of DeckLock> ./DeckLock
cd DeckLock
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt

Op Windows werkt de regel om de virtuele omgeving te activeren (source venv/bin/activate) niet. Gebruik in plaats daarvan de onderstaande regel.

venv\Scripts\activate.bat

DeckLock instellen

Het Makefile configureren

Omdat we een virtuele omgeving gebruiken waarin Pelican is geïnstalleerd, moet het pad naar het uitvoerbare bestand in die omgeving in het Makefile worden ingesteld. Open het bestand en pas het pad in de onderstaande regel aan je systeem aan. Het uitvoerbare Pelican-bestand hoort in venv/bin of venv/Scripts te staan.

PELICAN?=d:\Git\DeckLock\venv\Scripts\pelican

pelicanconf.py en publishconf.py

pelicanconf.py zou gebruiksklaar moeten zijn, maar kijk gerust na of bepaalde instellingen en paden moeten worden aangepast.

In publishconf.py moet je wel de uiteindelijke URL van je site instellen.

SITEURL = "https://4dcu.be/DeckLock"

API-sleutel voor Decks of KeyForge

Als je deckstatistieken van Decks of KeyForge wilt opnemen, moet je een account aanmaken en een API-sleutel aanvragen via https://decksofkeyforge.com/. Maak een bestand .env en voeg de onderstaande regel toe.

Een .env-bestand dat niet wordt gecommit, houdt je API-sleutel geheim.

DOK_API_KEY=your_api_key

Identificatiecodes van KeyForge-decks

Vervolgens moet je in pelicanconf.py instellen in welke map de KeyForge-gegevens te vinden zijn. Merk op dat dit pad relatief is ten opzichte van de inhoudsmap.

KEYFORGE_PATH = "./data"

Voeg nu een bestand keyforge.json toe aan ./content/data, met de onderstaande structuur en de identificatiecodes van de decks die je wilt opnemen. Als voorbeeld is een bestand met mijn eigen KeyForge-decks meegeleverd. De structuur hoort er als volgt uit te zien.

[
  {
    "deck_id" : "a4268ae8-a9f6-48c7-9739-b28a3553b108"
  }, {
    "deck_id" : "bfbf6786-218c-4320-a7b1-7ed4d6eddc69"
  }
]

Het platform bouwen

Je kunt make gebruiken om de website te bouwen (als make op je systeem beschikbaar is). Met make html maak je een lokale versie om in de map _site te testen. Gebruik make release om de publicatieversie in de map ./docs te maken.

make html

make release

Je kunt Pelican ook rechtstreeks gebruiken. De inhoud staat in de map ./content en voor een lokale testbuild stel je ./_site als uitvoermap in. Schrijf de uitvoer met de publicatie-instellingen naar de map ./docs, zodat die gemakkelijk op GitHub kan worden gehost.

pelican ./content -o ./_site

pelican ./content -o ./docs -s publishconf.py

Lokaal hosten om te testen

Je kunt de ingebouwde webserver van Pelican starten met het commando make serve.

Je kunt de site ook bouwen met make html, naar de map _site gaan en een webserver starten met het commando python -m http.server.

In beide gevallen kun je de site bekijken door in je browser naar http://localhost:8000 te gaan.

Hosten op GitHub

DeckLock bevat een commando make release dat de definitieve versie van de website naar de map ./docs schrijft. Zorg ervoor dat je alle bestanden in je repository commit en pusht. Op GitHub kun je instellen dat deze map voor de projectpagina’s wordt gebruikt. Schakel dat in via de instellingen en je hebt gratis hosting om de decks uit je kaartspelcollectie te tonen.

Hoe het werkt

Alle code staat op https://github.com/4dcu-be/DeckLock. Kort samengevat kan Pelican functies op verschillende momenten tijdens de build activeren. Via een plugin werd de functie get_keyforge_external_data toegevoegd, die wordt uitgevoerd wanneer Pelican initialiseert. Deze functie leest de instellingen, zoekt waar keyforge.json en de API-sleutel zijn opgeslagen en maakt verbinding met de verschillende websites om alle vereiste informatie op te halen.

Pelican gebruikt drie componenten om pagina’s te maken: Readers (die bestanden lezen en omzetten in een pagina, zoals blogposts, artikels, …), Generators die gegevens verwerken, een URL genereren en alles met het juiste sjabloon naar een Writer sturen, die de gegevens en het sjabloon tot één HTML-bestand combineert. Generators worden gebruikt om overzichtspagina’s per categorie, … te maken. De standaard Writer volstaat meestal, dus door Readers en Generators toe te voegen gebeurt de magie waarmee je de specifieke structuur van je eigen site bouwt.

De functie get_keyforge_external_data schrijft alles wat nodig is naar één JSON-bestand. Daardoor kunnen we een Reader achterwege laten en een Generator gebruiken die de gegevens laadt, voor elk deck een URL bouwt en de relevante gegevens naar onze sjablonen stuurt.

Hieronder staat een skelet dat toont hoe je een functie maakt en registreert om bij de initialisatie te worden uitgevoerd, en hoe je een generator maakt en bij Pelican registreert zodat die onze HTML-bestanden produceert.

from pelican import signals, generators

class KeyForgeGenerator(generators.Generator):
    """ Generator Class to produce pages based on keyforge.cache.json """

    template_overview = "keyforge_overview.html"
    template_deck = "keyforge_deck.html"
    
    def __init__(self, context, settings, path, theme, output_path, **kwargs):
        # Initialization function
        
    def generate_output(self, writer):
        # This function is required, this will be started by Pelican
        # Here we'll call two other functions, one to create an overview page
        # and one to create a page with deck details.
        self.generate_keyforge_overview_page(writer, self.keyforge_data)

        for k, v in self.keyforge_data.items():
            self.generate_keyforge_deck_page(writer, k, v)
        
    def generate_keyforge_deck_page(self, writer, deck_id, data):
        # Function to generate deck pages
        
    def generate_keyforge_overview_page(self, writer, data):
        # Function to generate overview page        

def get_keyforge_external_data(generator):
    # Here we can get all settings from generator.settings ...
    
    # Access APIs
    
    # Write all output to disk

def get_generators(generators):
    return KeyForgeGenerator

def register():
    """Register our function to get all data and generator to create the pages"""
    signals.initialized.connect(get_keyforge_external_data)

Conclusie

Hoewel Pelican wat meer configuratie vergde dan Jekyll en daardoor moeilijker was om mee te beginnen, was het een groot voordeel dat ik het met Python kon uitbreiden. Complexe functies implementeren, zoals gegevens uit externe API’s ophalen, was met pakketten als requests en de ingebouwde json-module helemaal niet zo moeilijk. Pelican is dus een uitstekend framework voor statische pagina’s die een zwaardere verwerking van invoergegevens of verbindingen met externe gegevens vereisen. Voor een eenvoudige blog ben je waarschijnlijk sneller aan de slag met Jekyll.

Er bestaan ook allerlei frameworks in andere talen die ik niet heb bekeken. Vooral Gatsby, dat onder de motorkap het JavaScript-ecosysteem gebruikt, en Hugo, dat door Go wordt aangedreven. Gatsby is bijzonder interessant omdat het met React verweven is, wat handig kan zijn om een krachtige, interactieve front-end te bouwen…

Ondersteuning voor Magic: the Gathering werd in het volgende artikel geïmplementeerd. Gwent werd in deel 3 toegevoegd.