DeckLock: keep track of your card games
using Pelican to build an overview of your collection
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.
You can see the result of this blog here. The code and instructions to build DeckLock with your own decks can be found on GitHub: https://github.com/4dcu-be/DeckLock
Introduction
KeyForge
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].
Pelican
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.
Getting started
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
cd DeckLock
python -m venv venv
source venv/bin/activate
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.
venv\Scripts\activate.bat
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 venv/bin
or venv/Scripts
.
PELICAN?=d:\Git\DeckLock\venv\Scripts\pelican
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.
In publishconf.py
however you will need to specify the final url of your site.
SITEURL = "https://4dcu.be/DeckLock"
Decks of KeyForge API key
If you want to include deck statistics from Decks of KeyForge you’ll have to create an account and get an API key from
https://decksofkeyforge.com/. Create a file .env
in the and add the line below.
A .env
file, which will not be committed, is used to keep your api key secret.
DOK_API_KEY=your_api_key
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 ./docs
folder.
make html
make release
alternatively you can use pelican directly, the content is in the folder ./content
and the output folder should be set
to ./_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 make serve
.
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
information.
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.
The function 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
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)
Conclusion
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.
Note that there are various frameworks in other languages available which I did not consider. Most notably Gatsby which uses the javascript ecosystem under the hood and Hugo which is powered by GO-lang. The former is particularly interesting as it is entwined with react which could prove handy to create a powerful, interactive front-end….
Support for Magic: the Gathering was implemented in the next post. Gwent has been added in part 3.
Liked this post ? You can buy me a coffee