diff --git a/.lycheeignore b/.lycheeignore index 6562b48..9dc627f 100644 --- a/.lycheeignore +++ b/.lycheeignore @@ -1,2 +1,3 @@ /themes https://old.reddit.com/r/rust +https://zubanls.com/pricing/ \ No newline at end of file diff --git a/config.toml b/config.toml index 99c8fe1..877bdf1 100644 --- a/config.toml +++ b/config.toml @@ -4,7 +4,7 @@ default_language = "en" title = "Rob's Blog | Python • Rust • Ramblings?" # Whether to automatically compile all Sass files in the sass directory -compile_sass = false +compile_sass = true # Whether to build a search index to be used later on by a JavaScript library build_search_index = false @@ -29,6 +29,7 @@ after_dark_menu = [ { name = "Home", url = "$BASE_URL" }, { name = "About", url = "$BASE_URL/about/" }, { name = "Now", url = "$BASE_URL/now/" }, + { name = "Boardgames", url = "$BASE_URL/boardgames" }, { name = "TILs", url = "$BASE_URL/tils/" }, { name = "Tags", url = "$BASE_URL/tags" }, { name = "Source", url = "https://github.com/sinon/sinon.github.io" }, diff --git a/content/future-python-type-checkers.md b/content/future-python-type-checkers.md index b39e862..7a670f5 100644 --- a/content/future-python-type-checkers.md +++ b/content/future-python-type-checkers.md @@ -79,7 +79,7 @@ Before examining these new Rust-based tools, it's worth understanding the curren - Created by the author of the popular Python LSP tool `jedi` - Aims for high-degree of compatibility with `mypy` to make adoption in large existing codebases seamless. -- Not FOSS[^2], will require a license for codebases above 1.5 MB (~50,000 lines of code)[^3]. +- ~~Not FOSS[^2], will require a license for codebases above 1.5 MB (~50,000 lines of code)[^3]~~**Update (September 2025):** Now open source under the AGPL license, though commercial licensing is available for business who prefer to avoid AGPL compliance. - Currently maintained by a single author seems a potential risk to long-term sustainability as Python typing does not stand still. **Philosophy:** Zuban aims to provide the smoothest possible migration path from existing type checkers, particularly `mypy`, making it attractive for organizations with substantial existing typed codebases. @@ -108,7 +108,7 @@ __Generated 29/08/2025__ > That being said even though `ty` is lagging on this metric at the moment it is still the type checker that I am most excited to use long-term because of the quality of the tooling Astral has built so far. | Type Checker | Total Test Case Passes | Total Test Case Partial | Total False Positives | Total False Negatives | -| :---------------------------------------------: | :--------------------: | :---------------------: | :-------------------: | :-------------------: | +| :---------------------------------------------: | :--------------------: | :---------------------: | :-------------------: | :-------------------: | | zuban 0.0.20 | 97 | 42 | 152 | 89 | | ty 0.0.1-alpha.19 (e9cb838b3 2025-08-19) | 20 | 119 | 371 | 603 | | Local:ty ruff/0.12.11+27 (0bf5d2a20 2025-08-29) | 20 | 119 | 370 | 590 | @@ -165,7 +165,7 @@ For teams evaluating these type checkers, the conformance scores provide valuabl [^1]: Which can be demonstrated in the [open issues](https://github.com/astral-sh/ruff/issues?q=is%3Aissue%20state%3Aopen%20label%3Atype-inference ) on ruff tagged with `type-inference` which are bugs or new features that can only be resolved with `ruff` having access to deeper type inference data that `ty` can supply. [^2]: David has indicated a plan to make [source available in the future](https://github.com/python/typing/pull/2067#issuecomment-3177937964) when adding Zuban to the Python typing conformance suite. -[^3]: Full pricing information at: +[^3]: ~~Full pricing information at: https://zubanls.com/pricing/~~ **Update (September 2025):** Pricing is now available on request for the non-AGPL license. [^4]: This is just for this blog post, no plans to seek merging this. diff --git a/content/pages/boardgames.md b/content/pages/boardgames.md new file mode 100644 index 0000000..8bcbae0 --- /dev/null +++ b/content/pages/boardgames.md @@ -0,0 +1,7 @@ ++++ +title = "Boardgames" +date = 2025-08-27 +template = "boardgames.html" +sort_by = "none" +path = "boardgames" ++++ diff --git a/sass/boardgames.sass b/sass/boardgames.sass new file mode 100644 index 0000000..be1201e --- /dev/null +++ b/sass/boardgames.sass @@ -0,0 +1,77 @@ +.boardgames-container + max-width: 1200px + margin: 0 auto + padding: 20px + +.games-grid + display: grid + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)) + gap: 20px + margin-top: 20px + +.game-card + border: 1px solid #404040 + border-radius: 8px + padding: 16px + background: var(--bg-primary) + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3) + transition: transform 0.2s, box-shadow 0.2s + + &:hover + transform: translateY(-2px) + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4) + border-color: var(--note-color) + +.game-header + display: flex + gap: 12px + margin-bottom: 12px + +.game-thumbnail + width: 85px + height: 110px + object-fit: cover + border-radius: 4px + border: 1px solid #404040 + +.game-info + flex: 1 + +.game-title + font-weight: bold + margin: 0 0 4px 0 + color: #ffffff + +.game-year + color: #cccccc + font-size: 0.9em + margin: 0 + +.game-stats + display: flex + gap: 16px + margin-bottom: 8px + font-size: 0.9em + color: #aaaaaa + +.game-rating + font-weight: bold + color: var(--note-color) + + &.rating-high + color: #27ae60 + + &.rating-medium + color: #f39c12 + + &.rating-low + color: #e74c3c + + &.rating-unrated + color: #999999 + +.game-comment + color: #cccccc + font-size: 0.9em + margin-top: 8px + font-style: italic diff --git a/scripts/boardgames.py b/scripts/boardgames.py new file mode 100755 index 0000000..ddab4fe --- /dev/null +++ b/scripts/boardgames.py @@ -0,0 +1,122 @@ +#!/usr/bin/env -S uv run +# /// script +# requires-python = ">=3.13" +# dependencies = [ +# "requests", +# ] +# /// + +import requests +import xml.etree.ElementTree as ET +import json +import time +import logging +from typing import List, Dict, Any + +logging.basicConfig( + level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" +) +logger = logging.getLogger(__name__) + + +def fetch_boardgame_collection(username: str) -> str: + url = f"https://boardgamegeek.com/xmlapi2/collection?username={username}&own=1&stats=1&excludesubtype=boardgameexpansion" + + max_retries = 5 + retry_delay = 2 + + for attempt in range(max_retries): + response = requests.get(url) + + if response.status_code == 202: + logger.info( + f"Collection still processing, retrying in {retry_delay} seconds... (attempt {attempt + 1}/{max_retries})" + ) + time.sleep(retry_delay) + retry_delay *= 2 + continue + + response.raise_for_status() + return response.text + + raise Exception(f"Failed to fetch collection after {max_retries} attempts") + + +def parse_collection_xml(xml_content: str) -> List[Dict[str, Any]]: + root = ET.fromstring(xml_content) + games = [] + + for item in root.findall("item"): + game = { + "objectid": item.get("objectid"), + "name": None, + "yearpublished": None, + "my_rating": None, + "stats": {}, + "comment": None, + } + + name_elem = item.find("name") + if name_elem is not None: + game["name"] = name_elem.text + + year_elem = item.find("yearpublished") + if year_elem is not None: + game["yearpublished"] = year_elem.text + + thumbnail_elem = item.find("image") + if thumbnail_elem is not None: + game["image"] = thumbnail_elem.text + comment_elem = item.find("comment") + if comment_elem is not None: + game["comment"] = comment_elem.text + + stats_elem = item.find("stats") + if stats_elem is not None: + rating_elem = stats_elem.find("rating") + if rating_elem is not None: + game["stats"] = { + "minplayers": stats_elem.get("minplayers"), + "maxplayers": stats_elem.get("maxplayers"), + "playingtime": stats_elem.get("playingtime"), + } + if rating_elem.get("value") != "N/A": + game["my_rating"] = float(rating_elem.get("value")) + games.append(game) + games = sorted(games, key=lambda x: x["my_rating"] or 0, reverse=True) + return games + + +def save_to_json(data: List[Dict[str, Any]], filename: str) -> None: + with open(filename, "w", encoding="utf-8") as f: + json.dump(data, f, indent=2, ensure_ascii=False) + + +def main(): + username = "sinon88" + output_file = "../static/boardgames_collection.json" + + try: + logger.info(f"Fetching board game collection for user: {username}") + xml_content = fetch_boardgame_collection(username) + + logger.info("Parsing XML content...") + games_list = parse_collection_xml(xml_content) + + logger.info(f"Found {len(games_list)} games in collection") + + logger.info(f"Saving to {output_file}...") + save_to_json(games_list, output_file) + + logger.info(f"Successfully saved board game collection to {output_file}") + + except requests.exceptions.RequestException as e: + logger.error(f"Error fetching data: {e}") + except ET.ParseError as e: + logger.error(f"Error parsing XML: {e}") + except Exception as e: + logger.error(f"Unexpected error: {e}") + + +if __name__ == "__main__": + main() diff --git a/static/boardgames_collection.json b/static/boardgames_collection.json new file mode 100644 index 0000000..ff571c7 --- /dev/null +++ b/static/boardgames_collection.json @@ -0,0 +1,795 @@ +[ + { + "objectid": "373106", + "name": "Sky Team", + "yearpublished": "2023", + "my_rating": 9.0, + "stats": { + "minplayers": "2", + "maxplayers": "2", + "playingtime": "20" + }, + "comment": "Excellent, the perfect light easy to setup game that is tense and engaging throughout. The modules add interesting new worries to each game, meaning it still hasn't gotten stale after 10+ plays.", + "image": "https://cf.geekdo-images.com/uXMeQzNenHb3zK7Hoa6b2w__original/img/mWOQnkpyYBorh_Y1-0Y2o-ew17k=/0x0/filters:format(jpeg)/pic7398904.jpg" + }, + { + "objectid": "28143", + "name": "Race for the Galaxy", + "yearpublished": "2007", + "my_rating": 8.55, + "stats": { + "minplayers": "2", + "maxplayers": "4", + "playingtime": "60" + }, + "comment": "Excellent tableau builder, quick once players internalise the symbolic language. Never overstays it's welcome and still come across new combinations with each play.", + "image": "https://cf.geekdo-images.com/-DOqixs8uwKUvvWPKI4f9w__original/img/Vh-DCkTPa8OU45LaJdUywwhiYqE=/0x0/filters:format(jpeg)/pic5261714.jpg" + }, + { + "objectid": "124361", + "name": "Concordia", + "yearpublished": "2013", + "my_rating": 8.5, + "stats": { + "minplayers": "2", + "maxplayers": "5", + "playingtime": "100" + }, + "comment": "Simple to teach with a nice strategic depth.", + "image": "https://cf.geekdo-images.com/CzwSm8i7tkLz6cBnrILZBg__original/img/BhJ3sB3uk-eSdR1iW4EP3cu0Wi0=/0x0/filters:format(jpeg)/pic3453267.jpg" + }, + { + "objectid": "161936", + "name": "Pandemic Legacy: Season 1", + "yearpublished": "2015", + "my_rating": 8.5, + "stats": { + "minplayers": "2", + "maxplayers": "4", + "playingtime": "60" + }, + "comment": "4p, never finished. The theme and novelty are probably what keeps this rating so high.", + "image": "https://cf.geekdo-images.com/-Qer2BBPG7qGGDu6KcVDIw__original/img/PlzAH7swN1nsFxOXbfUvE3TkE5w=/0x0/filters:format(png)/pic2452831.png" + }, + { + "objectid": "162886", + "name": "Spirit Island", + "yearpublished": "2017", + "my_rating": 8.5, + "stats": { + "minplayers": "1", + "maxplayers": "4", + "playingtime": "120" + }, + "comment": "Great theme, great variety in spirits/difficulty/scenarios.", + "image": "https://cf.geekdo-images.com/kjCm4ZvPjIZxS-mYgSPy1g__original/img/9uLd9C3XAvInLCLhAoXqKVk56zs=/0x0/filters:format(jpeg)/pic7013651.jpg" + }, + { + "objectid": "503", + "name": "Through the Desert", + "yearpublished": "1998", + "my_rating": 8.2, + "stats": { + "minplayers": "2", + "maxplayers": "5", + "playingtime": "45" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/Rq_hJIAO7jDgIpAW943hJA__original/img/9lIQGNwBYmAlsgXOI6Iw2Q2CIkA=/0x0/filters:format(jpeg)/pic7697747.jpg" + }, + { + "objectid": "386937", + "name": "Lacuna", + "yearpublished": "2023", + "my_rating": 8.1, + "stats": { + "minplayers": "2", + "maxplayers": "2", + "playingtime": "15" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/kYZoWRZUfVD_g4TefiBu-Q__original/img/0S_hY9a3JRwwJPLNONiLjDLCTcI=/0x0/filters:format(png)/pic7488897.png" + }, + { + "objectid": "93", + "name": "El Grande", + "yearpublished": "1995", + "my_rating": 8.0, + "stats": { + "minplayers": "2", + "maxplayers": "5", + "playingtime": "120" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/RRKDHaYtFPHhczkUDcHOmg__original/img/E_QazS4f8ffj6oBcUl3C_VROCEw=/0x0/filters:format(jpeg)/pic7906240.jpg" + }, + { + "objectid": "204493", + "name": "Sakura Arms", + "yearpublished": "2016", + "my_rating": 8.0, + "stats": { + "minplayers": "2", + "maxplayers": "2", + "playingtime": "30" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/IMslo9J8RnWXQospAjO6Dw__original/img/DPPq9spt37SJlliXpyqKXFVwu_4=/0x0/filters:format(jpeg)/pic3545811.jpg" + }, + { + "objectid": "424975", + "name": "Wilmot's Warehouse", + "yearpublished": "2024", + "my_rating": 8.0, + "stats": { + "minplayers": "2", + "maxplayers": "6", + "playingtime": "30" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/me8N7vrfzBRD83d0VcRM-g__original/img/4LHTwSEhk4OPd5ZHExPvfHXNOJs=/0x0/filters:format(png)/pic8327408.png" + }, + { + "objectid": "368061", + "name": "Zoo Vadis", + "yearpublished": "2023", + "my_rating": 8.0, + "stats": { + "minplayers": "3", + "maxplayers": "7", + "playingtime": "40" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/Kl3NjtNKpuJNPjdBQtdsow__original/img/3ABkMQwQ-85hqaBO7gHP5ekBAQ0=/0x0/filters:format(jpeg)/pic6988937.jpg" + }, + { + "objectid": "200680", + "name": "Agricola (Revised Edition)", + "yearpublished": "2016", + "my_rating": 7.9, + "stats": { + "minplayers": "1", + "maxplayers": "4", + "playingtime": "120" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/YCGWJMFwOI5efji2RJ2mSw__original/img/jC_He46LcIcKWU-kSwkYdr9Z45E=/0x0/filters:format(jpeg)/pic8093340.jpg" + }, + { + "objectid": "73439", + "name": "Troyes", + "yearpublished": "2010", + "my_rating": 7.9, + "stats": { + "minplayers": "1", + "maxplayers": "4", + "playingtime": "90" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/fBIe-aEPtJKC-ceNlspK6A__original/img/otcIVdG4ebZmK1SjUtzma18JtJ4=/0x0/filters:format(jpeg)/pic750583.jpg" + }, + { + "objectid": "84876", + "name": "The Castles of Burgundy", + "yearpublished": "2011", + "my_rating": 7.8, + "stats": { + "minplayers": "2", + "maxplayers": "4", + "playingtime": "90" + }, + "comment": "Go to game with Wife for a non-confrontational resource gathering game. Not too headsdown, still a lot of focus on what moves the opponent is making. \n\nOnly played 2 player", + "image": "https://cf.geekdo-images.com/sH2YTQ10dHj1ibfS-KKtGA__original/img/L_gsMsuhbAe0kyq1QLAmyeKOeSs=/0x0/filters:format(jpeg)/pic8745814.jpg" + }, + { + "objectid": "124742", + "name": "Android: Netrunner", + "yearpublished": "2012", + "my_rating": 7.5, + "stats": { + "minplayers": "2", + "maxplayers": "2", + "playingtime": "45" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/2ewHIIG_TRq8bYlqk0jIMw__original/img/cassW39WF2QrPImJF59efADAmM0=/0x0/filters:format(jpeg)/pic3738560.jpg" + }, + { + "objectid": "178900", + "name": "Codenames", + "yearpublished": "2015", + "my_rating": 7.5, + "stats": { + "minplayers": "2", + "maxplayers": "8", + "playingtime": "15" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/nC6ifPCDnAItwoKSKXVrnw__original/img/Id-jjIer_61ZbvI2_RVRCeBZFY4=/0x0/filters:format(jpeg)/pic8907965.jpg" + }, + { + "objectid": "207016", + "name": "Flick 'em Up!: Dead of Winter", + "yearpublished": "2017", + "my_rating": 7.5, + "stats": { + "minplayers": "1", + "maxplayers": "10", + "playingtime": "45" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/P7W3dCZliN22VxH7JPUG3A__original/img/FJ0Y-DDM5A_rmkXvNXEm-brHaHk=/0x0/filters:format(jpeg)/pic3365608.jpg" + }, + { + "objectid": "339924", + "name": "Hot Lead", + "yearpublished": "2022", + "my_rating": 7.5, + "stats": { + "minplayers": "2", + "maxplayers": "5", + "playingtime": "15" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/yM17m8v87Gj_kIpc3hBQXw__original/img/o0a_QN1SLe3kXV4K1rrPx2jGzQ8=/0x0/filters:format(png)/pic6231247.png" + }, + { + "objectid": "118", + "name": "Modern Art", + "yearpublished": "2021", + "my_rating": 7.5, + "stats": { + "minplayers": "3", + "maxplayers": "5", + "playingtime": "45" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/DHO7YSykDvUQqLMRJL37BQ__original/img/AmSoL3kBrXSlTGG-cJkMzxAVOfE=/0x0/filters:format(jpeg)/pic7161137.jpg" + }, + { + "objectid": "141572", + "name": "Paperback", + "yearpublished": "2014", + "my_rating": 7.5, + "stats": { + "minplayers": "2", + "maxplayers": "5", + "playingtime": "45" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/iZl2U-2BjftbWfLw5y25Vg__original/img/2kvlCGamVRWxUarnVS0nuV8LudM=/0x0/filters:format(jpeg)/pic7069377.jpg" + }, + { + "objectid": "237182", + "name": "Root", + "yearpublished": "2018", + "my_rating": 7.5, + "stats": { + "minplayers": "2", + "maxplayers": "4", + "playingtime": "90" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/JUAUWaVUzeBgzirhZNmHHw__original/img/E0s2LvtFA1L5YKk-_44D4u2VD2s=/0x0/filters:format(jpeg)/pic4254509.jpg" + }, + { + "objectid": "372", + "name": "Schotten Totten", + "yearpublished": "1999", + "my_rating": 7.3, + "stats": { + "minplayers": "2", + "maxplayers": "2", + "playingtime": "20" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/69mwXe7c6HNYmT6S35Y4zg__original/img/C5YBiD7Qhpkf5FO1GteEjo9KRVE=/0x0/filters:format(jpeg)/pic2932872.jpg" + }, + { + "objectid": "12333", + "name": "Twilight Struggle", + "yearpublished": "2005", + "my_rating": 7.3, + "stats": { + "minplayers": "2", + "maxplayers": "2", + "playingtime": "180" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/pNCiUUphnoeWOYfsWq0kng__original/img/Iae47UtAd_RXVd5tJ3YzbDHOv4E=/0x0/filters:format(jpeg)/pic3530661.jpg" + }, + { + "objectid": "36218", + "name": "Dominion", + "yearpublished": "2008", + "my_rating": 7.1, + "stats": { + "minplayers": "2", + "maxplayers": "4", + "playingtime": "30" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/j6iQpZ4XkemZP07HNCODBA__original/img/96COuakNiLRrjDLc1sM4Zxsw4WE=/0x0/filters:format(jpeg)/pic394356.jpg" + }, + { + "objectid": "163412", + "name": "Patchwork", + "yearpublished": "2014", + "my_rating": 7.1, + "stats": { + "minplayers": "2", + "maxplayers": "2", + "playingtime": "30" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/RDOwMRBnIb3Ehl6GyXj9xg__original/img/iVl8KAZ8JMmDXsnBqQecFRkOhK8=/0x0/filters:format(jpeg)/pic8669620.jpg" + }, + { + "objectid": "70919", + "name": "Takenoko", + "yearpublished": "2011", + "my_rating": 7.1, + "stats": { + "minplayers": "2", + "maxplayers": "4", + "playingtime": "45" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/uvz-5V6A2R6dp2oWIXmj_g__original/img/HxMG1z7bIVGCGPI6u6ciVO0Oubk=/0x0/filters:format(jpeg)/pic1912529.jpg" + }, + { + "objectid": "227224", + "name": "The Red Cathedral", + "yearpublished": "2020", + "my_rating": 7.05, + "stats": { + "minplayers": "1", + "maxplayers": "4", + "playingtime": "80" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/-PuiCnbxlBzMSjnUegp9AQ__original/img/hH1AwTR4fdtYY_cO8IYp1-I8dhE=/0x0/filters:format(jpeg)/pic5556025.jpg" + }, + { + "objectid": "247367", + "name": "Air, Land, & Sea", + "yearpublished": "2019", + "my_rating": 7.0, + "stats": { + "minplayers": "2", + "maxplayers": "2", + "playingtime": "20" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/3wpYeNwO7k_yjpIN7IQwaw__original/img/_qQC98hufgmPn_fwMrP4I0sYKO8=/0x0/filters:format(jpeg)/pic4387681.jpg" + }, + { + "objectid": "219475", + "name": "Axio", + "yearpublished": "2017", + "my_rating": 7.0, + "stats": { + "minplayers": "1", + "maxplayers": "4", + "playingtime": "45" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/wROIUYusbdD2Lov5cnMCNw__original/img/KZ4ikPksvug5e2zrTM0tdFtAT3U=/0x0/filters:format(jpeg)/pic3742268.jpg" + }, + { + "objectid": "147151", + "name": "Concept", + "yearpublished": "2013", + "my_rating": 7.0, + "stats": { + "minplayers": "4", + "maxplayers": "12", + "playingtime": "40" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/hHFs0_KRoW_FJ0cMIVgZcw__original/img/vbR7Wrdlq4fPcwUE1Ck9CowlJHY=/0x0/filters:format(jpeg)/pic4991925.jpg" + }, + { + "objectid": "98778", + "name": "Hanabi", + "yearpublished": "2010", + "my_rating": 7.0, + "stats": { + "minplayers": "2", + "maxplayers": "5", + "playingtime": "25" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/JDVksMwfcqoem1k_xtZrOA__original/img/5vNHZiTEhK4aRDuGXv5KImp9cmQ=/0x0/filters:format(jpeg)/pic2007286.jpg" + }, + { + "objectid": "161882", + "name": "Irish Gauge", + "yearpublished": "2014", + "my_rating": 7.0, + "stats": { + "minplayers": "3", + "maxplayers": "5", + "playingtime": "60" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/BZW9WTKwGMfb3R4EELel3w__original/img/K3rGKFzRT3m1MAgyAUQaDXdnFMM=/0x0/filters:format(jpeg)/pic7628587.jpg" + }, + { + "objectid": "54043", + "name": "Jaipur", + "yearpublished": "2009", + "my_rating": 7.0, + "stats": { + "minplayers": "2", + "maxplayers": "2", + "playingtime": "30" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/_LTujSe_o16nvjDC-J0seA__original/img/zUnAdsgt4dCxvtoRDc_9DHbic0M=/0x0/filters:format(jpeg)/pic5100947.jpg" + }, + { + "objectid": "253399", + "name": "Journal 29: Interactive Book Game", + "yearpublished": "2017", + "my_rating": 7.0, + "stats": { + "minplayers": "1", + "maxplayers": "10", + "playingtime": "120" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/Y1dibOE_gxTh9wEfThT55w__original/img/uwJazrctvqI7ZA5zCCQsedI_fyo=/0x0/filters:format(jpeg)/pic4675327.jpg" + }, + { + "objectid": "181304", + "name": "Mysterium", + "yearpublished": "2015", + "my_rating": 7.0, + "stats": { + "minplayers": "2", + "maxplayers": "7", + "playingtime": "42" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/1nQ3ZKudtDeAP7IiKE-kNg__original/img/6igmXolQesapNU9yEnexip6m9B8=/0x0/filters:format(jpeg)/pic8625343.jpg" + }, + { + "objectid": "147949", + "name": "One Night Ultimate Werewolf", + "yearpublished": "2014", + "my_rating": 7.0, + "stats": { + "minplayers": "3", + "maxplayers": "10", + "playingtime": "10" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/rqLju4uaZq-G9z4g91aPPQ__original/img/eOxUlbCuix0lOjuyBYpjtIXVihc=/0x0/filters:format(png)/pic8783294.png" + }, + { + "objectid": "30549", + "name": "Pandemic", + "yearpublished": "2008", + "my_rating": 7.0, + "stats": { + "minplayers": "2", + "maxplayers": "4", + "playingtime": "45" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/S3ybV1LAp-8SnHIXLLjVqA__original/img/IsrvRLpUV1TEyZsO5rC-btXaPz0=/0x0/filters:format(jpeg)/pic1534148.jpg" + }, + { + "objectid": "25669", + "name": "Qwirkle", + "yearpublished": "2006", + "my_rating": 7.0, + "stats": { + "minplayers": "2", + "maxplayers": "4", + "playingtime": "45" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/k7zHj8j_a6uUAtXUt5Fvuw__original/img/FNi6yap-UmVKX8X6rj0A_YW2q8Y=/0x0/filters:format(jpeg)/pic309353.jpg" + }, + { + "objectid": "92415", + "name": "Skull", + "yearpublished": "2011", + "my_rating": 7.0, + "stats": { + "minplayers": "3", + "maxplayers": "6", + "playingtime": "45" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/OPrd2iXm43dir7BwKAMOuQ__original/img/ygPnbmg8FCpA7jO5gXw38uyNQ10=/0x0/filters:format(jpeg)/pic6097488.jpg" + }, + { + "objectid": "14996", + "name": "Ticket to Ride: Europe", + "yearpublished": "2005", + "my_rating": 7.0, + "stats": { + "minplayers": "2", + "maxplayers": "5", + "playingtime": "60" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/0K1AOciqlMVUWFPLTJSiww__original/img/O37sCRSJLq4S8EpCxFDNVsNBuxE=/0x0/filters:format(jpeg)/pic66668.jpg" + }, + { + "objectid": "822", + "name": "Carcassonne", + "yearpublished": "2000", + "my_rating": 6.99, + "stats": { + "minplayers": "2", + "maxplayers": "5", + "playingtime": "45" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/okM0dq_bEXnbyQTOvHfwRA__original/img/aVZEXAI-cUtuunNfPhjeHlS4fwQ=/0x0/filters:format(png)/pic6544250.png" + }, + { + "objectid": "131357", + "name": "Coup", + "yearpublished": "2012", + "my_rating": 6.9, + "stats": { + "minplayers": "2", + "maxplayers": "6", + "playingtime": "15" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/MWhSY_GOe2-bmlQ2rntSVg__original/img/ayAAWBK1rAEumARNmROsOtvqW-4=/0x0/filters:format(jpeg)/pic2016054.jpg" + }, + { + "objectid": "148203", + "name": "Dutch Blitz", + "yearpublished": "1960", + "my_rating": 6.9, + "stats": { + "minplayers": "2", + "maxplayers": "4", + "playingtime": "15" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/ipuSaHTjxDpXAee-3vttiA__original/img/oyPs5ZLZb6e2g_8nA0uZAOKE4EM=/0x0/filters:format(jpeg)/pic336418.jpg" + }, + { + "objectid": "118048", + "name": "Targi", + "yearpublished": "2012", + "my_rating": 6.7, + "stats": { + "minplayers": "2", + "maxplayers": "2", + "playingtime": "60" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/wHg4fOf48cs1kw1PDOk1tQ__original/img/St4ABQhpauYiq4Mt6-f38JtLOjM=/0x0/filters:format(jpeg)/pic3958793.jpg" + }, + { + "objectid": "41114", + "name": "The Resistance", + "yearpublished": "2009", + "my_rating": 6.5, + "stats": { + "minplayers": "5", + "maxplayers": "10", + "playingtime": "30" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/cAPTkL2BR3snLO71dkH8rw__original/img/_UahoIUTqh39xRqzCc2jwgdYbSA=/0x0/filters:format(jpeg)/pic2576459.jpg" + }, + { + "objectid": "133473", + "name": "Sushi Go!", + "yearpublished": "2013", + "my_rating": 6.5, + "stats": { + "minplayers": "2", + "maxplayers": "5", + "playingtime": "15" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/Fn3PSPZVxa3YurlorITQ1Q__original/img/5vRsn2TT9eJmq27i9HtLWKpB_xs=/0x0/filters:format(jpeg)/pic1900075.jpg" + }, + { + "objectid": "16992", + "name": "Tsuro", + "yearpublished": "2005", + "my_rating": 6.5, + "stats": { + "minplayers": "2", + "maxplayers": "8", + "playingtime": "20" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/2V3d4ryhxkS3RoPtDrvpUw__original/img/TP5U-O2eujPBK9IVmmifdfkWwdQ=/0x0/filters:format(jpeg)/pic875761.jpg" + }, + { + "objectid": "157969", + "name": "Sheriff of Nottingham", + "yearpublished": "2014", + "my_rating": 6.3, + "stats": { + "minplayers": "3", + "maxplayers": "5", + "playingtime": "60" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/BBgLFKUzr6tcKtlIM2JSFw__original/img/fll2MuTBPVKb7fyBJ8D-4_c1XHM=/0x0/filters:format(jpeg)/pic2075830.jpg" + }, + { + "objectid": "110327", + "name": "Lords of Waterdeep", + "yearpublished": "2012", + "my_rating": 6.1, + "stats": { + "minplayers": "2", + "maxplayers": "5", + "playingtime": "120" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/-hk8f8iGk_DyWyMrfiPBkg__original/img/GWNOhno3y7g0XE00VacKaRxoczk=/0x0/filters:format(jpeg)/pic1116080.jpg" + }, + { + "objectid": "129622", + "name": "Love Letter", + "yearpublished": "2012", + "my_rating": 6.0, + "stats": { + "minplayers": "2", + "maxplayers": "4", + "playingtime": "20" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/T1ltXwapFUtghS9A7_tf4g__original/img/xIAzJY7rl-mtPStRZSqnTVsAr8Y=/0x0/filters:format(jpeg)/pic1401448.jpg" + }, + { + "objectid": "150312", + "name": "Welcome to the Dungeon", + "yearpublished": "2013", + "my_rating": 6.0, + "stats": { + "minplayers": "2", + "maxplayers": "4", + "playingtime": "30" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/6eQXDEMIWldaut9w7jXihg__original/img/E_d5wpS2jEaH7mdO6humOGULycA=/0x0/filters:format(jpeg)/pic6771265.jpg" + }, + { + "objectid": "6249", + "name": "Alhambra", + "yearpublished": "2003", + "my_rating": 5.9, + "stats": { + "minplayers": "2", + "maxplayers": "6", + "playingtime": "60" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/OiqKsYDh7pqeRYKG__kMSw__original/img/Oe2ZTGE2hUnyZQIEoIjM_zwYfcY=/0x0/filters:format(jpeg)/pic4893652.jpg" + }, + { + "objectid": "295949", + "name": "Adventure Games: The Grand Hotel Abaddon", + "yearpublished": "2020", + "my_rating": 5.1, + "stats": { + "minplayers": "1", + "maxplayers": "4", + "playingtime": "270" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/dKRPsLgTrkFVAwUqU4BBqQ__original/img/pAum8eEQuvv0gpvP7VMADmaH1dM=/0x0/filters:format(jpeg)/pic6210950.jpg" + }, + { + "objectid": "342207", + "name": "echoes: The Dancer", + "yearpublished": "2021", + "my_rating": 5.0, + "stats": { + "minplayers": "1", + "maxplayers": "6", + "playingtime": "60" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/znvaTlswIC23fYH5oHMrGg__original/img/mR9QatN077Q3u1DnRWobE6m0l3E=/0x0/filters:format(jpeg)/pic6306043.jpg" + }, + { + "objectid": "62871", + "name": "Zombie Dice", + "yearpublished": "2010", + "my_rating": 5.0, + "stats": { + "minplayers": "2", + "maxplayers": "99", + "playingtime": "20" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/iPy584JMAJYrupqRdQp8gA__original/img/zPbWjPYgLscrjd96Uitf3GTPb34=/0x0/filters:format(jpeg)/pic4991962.jpg" + }, + { + "objectid": "1269", + "name": "Skip-Bo", + "yearpublished": "1967", + "my_rating": 4.5, + "stats": { + "minplayers": "2", + "maxplayers": "6", + "playingtime": "20" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/aaUJua2zRXA6lqbVbNcBGg__original/img/dWxG7nwxGBijXQ1849TFwtym06I=/0x0/filters:format(jpeg)/pic8204244.jpg" + }, + { + "objectid": "2593", + "name": "Pass the Pigs", + "yearpublished": "1977", + "my_rating": 4.0, + "stats": { + "minplayers": "2", + "maxplayers": "10", + "playingtime": "30" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/blW_a3O5Md5bE1jFygJ6SA__original/img/rbBCCKoEY8QvfaXeDSySuVVTUXk=/0x0/filters:format(jpeg)/pic697422.jpg" + }, + { + "objectid": "292032", + "name": "Funkoverse Strategy Game: Harry Potter 100", + "yearpublished": "2019", + "my_rating": null, + "stats": { + "minplayers": "2", + "maxplayers": "4", + "playingtime": "60" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/9JmZzK_PuB_1OT0YCpvlbg__original/img/KJPsSH6epyHV18r4DKcT5wMbMoY=/0x0/filters:format(png)/pic5008393.png" + }, + { + "objectid": "174430", + "name": "Gloomhaven", + "yearpublished": "2017", + "my_rating": null, + "stats": { + "minplayers": "1", + "maxplayers": "4", + "playingtime": "120" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/sZYp_3BTDGjh2unaZfZmuA__original/img/7d-lj5Gd1e8PFnD97LYFah2c45M=/0x0/filters:format(jpeg)/pic2437871.jpg" + }, + { + "objectid": "54138", + "name": "Imperial 2030", + "yearpublished": "2009", + "my_rating": null, + "stats": { + "minplayers": "2", + "maxplayers": "6", + "playingtime": "180" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/YSopSx1x5iea7qI21ogYBw__original/img/BTpxWEL57yDfISpu-WZdfzX6Di0=/0x0/filters:format(jpeg)/pic586346.jpg" + }, + { + "objectid": "338353", + "name": "Soda Smugglers", + "yearpublished": "2022", + "my_rating": null, + "stats": { + "minplayers": "3", + "maxplayers": "8", + "playingtime": "20" + }, + "comment": null, + "image": "https://cf.geekdo-images.com/zRdItFkzzgkuSn-Ix8PwWA__original/img/OyshAnPREylCv0ZfeYd2a7HiLS8=/0x0/filters:format(png)/pic6231245.png" + } +] \ No newline at end of file diff --git a/templates/boardgames.html b/templates/boardgames.html new file mode 100644 index 0000000..a238fc9 --- /dev/null +++ b/templates/boardgames.html @@ -0,0 +1,51 @@ +{% extends "after-dark/templates/page.html" %} +{% block css %} +{{ super() }} + +{% endblock css %} + +{% block content %} +{% block header %} +{{ super() }} +{% endblock header %} +
+

Board Games Collection

+
+ {% set data = load_data(path="static/boardgames_collection.json") %} + {% for game in data %} + {% set rating_class = "" %} + {% if game.my_rating %} + {% if game.my_rating >= 8 %} + {% set rating_class = "rating-high" %} + {% elif game.my_rating >= 6 %} + {% set rating_class = "rating-medium" %} + {% else %} + {% set rating_class = "rating-low" %} + {% endif %} + {% else %} + {% set rating_class = "rating-unrated" %} + {% endif %} +
+
+ {{ game.name }} +
+

{{ game.name }}

+

{{ game.yearpublished }}

+
+
+
+ 👥 {{ game.stats.minplayers }}-{{ game.stats.maxplayers }} players + ⏱️ {{ game.stats.playingtime }} min +
+
+ Rating: {% if game.my_rating %}{{ game.my_rating }}{% else %}N/A{% endif %} +
+ {% if game.comment %} +
{{ game.comment }}
+ {% endif %} +
+ {% endfor %} +
+
+ +{% endblock %} \ No newline at end of file