Currently I’ve been playing KeyForge and Gwent. One of my first posts was about Magic: the Gathering and my 3D printed dice and token boxes also made an appearance on this blog. So it is no secret I like card games, and my collection of decks both on- and off-line is rapidly growing. There are plenty of tools to keep track of your decks for a single game, however I want to create something myself that supports all games I play.
This was also a good opportunity to learn Pelican, a static website generator for Python. I’ve been using Jekyll for some time, though developing extra features (like the ones I presented in the previous post) requires the programming language Ruby. As I’m not well versed in Ruby, it would be far more efficient to use my preferred programming language Python. Pelican seems to fit very well and this was a perfect project to check this out myself.
KeyForge is a game designed by Dr. Richard Garfield, famous for creating Magic: the Gathering over 25 years ago. This game however is one-of-a-kind, as each deck you buy is a unique combination of cards designed to be played only in that specific configuration. Unlike other collectible card games, there is no trading individual cards. Furthermore, each deck comes with a QR-code, which will register that deck on https://www.keyforgegame.com/ when it is scanned using the KeyForge App (available in the App and Play store).
This is prefect for our application, we can get all information (name, decklist, …) of a scanned deck directly from the KeyForge website. The only thing we need is the decks unique identifier, everything else we’ll fetch automatically. Furthermore, using the same identifier additional statistics on the deck (like the estimated quality of the cards, synergies between them, …) can be obtained from [Deck of KeyForge].
Static website generators, like Jekyll and Pelican take your content in a simple format (usually Markdown or reStructuredText) and templates that define how the final pages should look. These are combined into static html pages which can be hosted pretty much everywhere as there is no need for a database, php, … This elegant design with a clear separation between content and visualization has several advantages. The content is in a simple, text based format which can easily be stored, read, edited and reused. Sites are lightning fast as everything is pre-computed.
First go to https://github.com/4dcu-be/DeckLock and create your own fork of the repository. Next, use the commands below to clone your own repository, create a virtual environment and install all required packages.
git clone <url to your fork of DeckLock> ./DeckLock
python -m venv venv
pip install -r requirements.txt
On windows the line to activate the virtual environment (source venv/bin/activate) will not work, use the line below instead.
Setting up DeckLock
Configuring the Makefile
As we are using a virtual environment with pelican installed there, the patch to the executable in
the environment needs to be set in the Makefile. Open the file and change the path on the line below to match your
system. The pelican executable should be in
pelicanconf.py and publishconf.py
pelicanconf.py should be ready to go, though feel free to have a look to see if any of the
settings and paths need to be changed.
publishconf.py however you will need to specify the final url of your site.
SITEURL = "https://4dcu.be/DeckLock"
Decks of KeyForge API key
.env file, which will not be committed, is used to keep your api key secret.
KeyForge Deck IDs
Next, you’ll have to specify where the KeyForge data can be found (folder), in
pelicanconf.py. Note that this path is
relative to the content folder
KEYFORGE_PATH = "./data"
Now, add a
keyforge.json file to
./content/data, structured as followed with the identifiers of the decks to include.
A file with the KeyForge Decks I own is included as an example, the structure should be a shown below.
"deck_id" : "a4268ae8-a9f6-48c7-9739-b28a3553b108"
"deck_id" : "bfbf6786-218c-4320-a7b1-7ed4d6eddc69"
Building the platform
You can use make to build the website (if make is available on your system), use
make html to create a local instance
to test in the
_site directory. Use
make release to create the version for publication in the
alternatively you can use pelican directly, the content is in the folder
./content and the output folder should be set
./_site for a local test build. Write the output to the
./docs folder with the publication settings so this can be
hosted easily on GitHub.
pelican ./content -o ./_site
pelican ./content -o ./docs -s publishconf.py
Hosting locally for testing
You can use Pelican’s built in webserver using the command
Or you can build the site using
make html, navigate to the
_site folder and start a webserver
by running the command
python -m http.server.
In both cases you can see your site by pointing your browser to http://localhost:8000.
Hosting on GitHub
DeckLock includes a
make release command which will write the final version of the website to the
./docs folder. Make
sure to commit and push all files in your repository. On GitHub you can specify that this folder is used for the project pages, enable this in the settings and you’ll have
free hosting to show off the decks you have in your card game collection.
How it works
All code is available on https://github.com/4dcu-be/DeckLock. In a nutshell,
Pelican can trigger functions at different stages of the build. By creating a plugin a function
get_keyforge_external_data was added that will trigger when Pelican initializes. This function will read the settings,
find where keyforge.json is stored and the API key and connect to the different websites, pulling in all required
Pelican uses three components to create pages, Readers (which reads files and converts them to a page e.g. blog posts, articles, …), Generators that take data, processes it, generates a url and sends it with the correct template to a Writer that will combine data and template into a single html file. Generators are use to generate overview pages per category, … The default Writer is usually sufficient, so adding Readers and Generators is where the magic happens to create your own specific site’s structure.
get_keyforge_external_data will write everything required to a single json file, so we can forgo a Reader
and use a Generator, that loads the data, builds a url for each deck and ships the relevant data to our templates.
Here is a stub on how to create a function and register it to trigger on initialization and how to create a generator and register it with Pelican so it will produce our html files.
from pelican import signals, generators
""" 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.
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
# Here we can get all settings from generator.settings ...
# Access APIs
# Write all output to disk
"""Register our function to get all data and generator to create the pages"""
While Pelican did require a bit more setting up than Jekyll making it harder to get started, the fact that I could use Python to extend it was a significant advantage. Implementing complex features, like pulling in data from external APIs, wasn’t all that hard with packages like requests and the built-in json module available. So Pelican is a great framework for developing static pages that require more demanding processing of the input data or connections with external data. For a basic blog you are probably faster up and running with Jekyll.
Liked this post ? You can buy me a coffee