Short Form Lizard


a screenshot of the philly cube website.

Philly MtG Cube Website

On some random day in mid June 2024, in a discord server for the Philadelphia cube draft scene, friend of the blog ParkerL posts...

"...why does googling “Philly Mtg cube” give zero helpful results? Maybe we need a website or something"

Today, phillymtgcube.com is the number one search result for “Philly Mtg cube” on every major search engine. Here's a little peak under the curtains.

Prior to the website proper, the community used a Google Sheet to manage signing up to bring a cube to our weekly meetup (and still does, more on that later). At the time, I hadn't really established myself within the community. I had been in the scene for about a year, but really I was just a person who drafted every once and a while. And while I would have loved to streamline the entire signup and rsvp process (I would not have loved to do this), I wanted to make this website with as little impact to the day to day functioning of the community as possible. So the decision on my mind was, could I make a website that was more or less a dolled up version of the spreadsheet, and could I get away with leaving the spreadsheet in tact?

Pros

  • Users don't need to learn a new system to list their cubes
  • No need for a database solution

Cons

  • File reading needs to account for user error / XSS
  • Relies on 3rd-party support (Google)

So I'm not terribly offput by these requirements so we run with it. I create a tiny little Flask server to manage the cube data. I would rather not make Google upset with the frequency of requests coming from it, so I set up a cache with a ttl of 15 minutes. If a user arrives after, we'll redownload the spreadsheet and see if there's any updates. With a little bit of poking around, I found out that you can very easily download a Google Sheet into an Excel file by adding /export?gid=[sheet id]&format=xlsx to the end of the url. We locked most of the essential cells behind admin permissions - no changing column headers or number of rows before the events listings since it could break the parsing script.

Lucky for me, I already know how to do some excel file processing using openpyxl from an old college project, so I already have an idea of how to get started with the API. There's a non-zero chance we add more rows to this sheet (a fact I'm not terribly happy with but seems to serve our needs as they arise) so the script first determines where in the sheet all of the important columns are. It then goes down, row by row, checks if the date hasn't already passed, then checks if there is a cube signed up for that week, and if all the necessary data is in place, it sanitizes the data and adds that event listing to the cache.

We also keep a record of many of our local designer's cubes on a separate tab on that sheet. I use a very similar process as above, but I also like to include a photo if the cube is listed on Cube Cobra and has a cover image. I make an http request of the page, do some scraping, and get the card uuid for Scryfall. After a request to their API, I cache the URL to the card's art crop, and the name of the artist. This process can be a bit much, both in the number of requests to these sites, but also in the amount of time it takes to get a response from a user perspective (APIs have rate limits, you see), so card images get a TTL of 24 hours, and card images are requested as soon as the server starts up. Once this is done, it's time to pack up the cache into a json response and send it back to the requester.

The website itself is run on a Node server and is built in React. I chose React mostly because I hadn't worked with it in a while and wanted an active project in React on my portfolio, but I'm not married to the framework (in fact most of my corporate experience is in Angular, and you may have been able to deduce that this website doesn't use any framework). On pageload we hit the flask server for a request of the events and cubes, and populate the results in a pretty way using MaterialUI.

All of this runs in my little free Oracle Cloud instance and gets routed properly using nginx. This lovely setup means that the only real upkeep cost is for the domain name (and I guess if we're counting it, the cost of doing hotfixes when things break and making sure the ssl certificates don't lapse).