Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use external json data #114

Merged
merged 17 commits into from May 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/unreleased/Added-20230526-141403.yaml
@@ -0,0 +1,5 @@
kind: Added
body: 'add (dev) dependency: types-requests'
time: 2023-05-26T14:14:03.096815+01:00
custom:
GithubIssue: ""
5 changes: 5 additions & 0 deletions .changes/unreleased/Changed-20230523-143152.yaml
@@ -0,0 +1,5 @@
kind: Changed
body: Update gameinfo/*.json from assets
time: 2023-05-23T14:31:52.94028+01:00
custom:
GithubIssue: ""
5 changes: 5 additions & 0 deletions .changes/unreleased/Changed-20230526-002444.yaml
@@ -0,0 +1,5 @@
kind: Changed
body: run poetry update
time: 2023-05-26T00:24:44.813911+01:00
custom:
GithubIssue: ""
5 changes: 5 additions & 0 deletions .changes/unreleased/Changed-20230526-142952.yaml
@@ -0,0 +1,5 @@
kind: Changed
body: fix simple night sheet for 9 jinx insanity
time: 2023-05-26T14:29:52.483899+01:00
custom:
GithubIssue: ""
5 changes: 5 additions & 0 deletions .changes/unreleased/Fixed-20230526-085928.yaml
@@ -0,0 +1,5 @@
kind: Fixed
body: fix Docker build after gameinfo/ -> data/ changes
time: 2023-05-26T08:59:28.463431+01:00
custom:
GithubIssue: ""
5 changes: 5 additions & 0 deletions .changes/unreleased/Removed-20230526-141439.yaml
@@ -0,0 +1,5 @@
kind: Removed
body: remove duplicates css style
time: 2023-05-26T14:14:39.373335+01:00
custom:
GithubIssue: ""
4 changes: 2 additions & 2 deletions Dockerfile
Expand Up @@ -9,7 +9,7 @@
# - https://github.com/Kozea/WeasyPrint/issues/1384#issuecomment-902620644

ARG FUNCTION_DIR="/function"
FROM python:3.10-bullseye as build-image
FROM python:3.10-bullseye AS build-image
RUN apt-get update && \
apt-get install -y \
--no-install-recommends \
Expand Down Expand Up @@ -60,7 +60,7 @@ ENTRYPOINT [ "/usr/local/bin/python", "-m", "awslambdaric" ]
CMD [ "botcpdf.lambda.render" ]

#COPY app.py ./
COPY gameinfo ./gameinfo/
COPY data ./data/
COPY templates ./templates/
COPY icons ./icons/

Expand Down
10 changes: 0 additions & 10 deletions Makefile
Expand Up @@ -78,16 +78,6 @@ ifeq ($(shell uname),Darwin)
@open -a Preview "pdfs/just-baked.pdf"
endif

# this is just a quick helper for my own use - Chisel
refresh-json:
@curl -so gameinfo/roles-bra1n.json https://raw.githubusercontent.com/bra1n/townsquare/develop/src/roles.json
@curl -so gameinfo/roles-bra1n-fabled.json https://raw.githubusercontent.com/bra1n/townsquare/develop/src/fabled.json
@curl -so gameinfo/nightsheet.json https://script.bloodontheclocktower.com/data/nightsheet.json
@curl -so gameinfo/jinx.json https://script.bloodontheclocktower.com/data/jinx.json
@changie new -k "Changed" -b "Update gameinfo/*.json from assets"
@git add gameinfo/*.json .changes/unreleased/Changed-*.yaml
@git commit --no-verify -m "Update *.json from assets"

next-version:
@poetry version patch
@git commit --no-verify -m "bump pyproject version to $$(poetry version --short)" pyproject.toml
Expand Down
20 changes: 8 additions & 12 deletions botcpdf/jinx.py
Expand Up @@ -4,7 +4,7 @@
# as we're experimenting with this we'll keep it simple and just
# use {slug-jinxer}-{slug-jinxee} as the key

from botcpdf.util import cleanup_role_id, load_jinxdata
from botcpdf.util import cleanup_role_id, get_role_data


class Jinx:
Expand All @@ -31,32 +31,28 @@ def __repr__(self) -> str:
class Jinxes:
"""A representation of jinxes in a game"""

hatred: dict[str, dict] = {}

def __init__(self):
def __init__(self) -> None:
"""Initialize jinx data"""
json = load_jinxdata()

for jinx_block in json:
self.hatred: dict[str, dict] = {}

full_data = get_role_data()
jinx_data = full_data["jinxes"]

for jinx_block in jinx_data:
for jinxed_char in jinx_block["jinx"]:
jinxer_id = cleanup_role_id(jinx_block["id"])
jinxed_id = cleanup_role_id(jinxed_char["id"])
reason = jinxed_char["reason"]

jinx_info = Jinx(jinxer_id, jinxed_id, reason)

# the paired storage is for easy lookup of jinxes
# self.jinx_pair[f"{jinxer_id}-{jinxed_id}"] = jinx_info

# if we don't yet have a hatred entry for the jinxer, create it
if jinxer_id not in self.hatred:
self.hatred[jinxer_id] = {}
# add the jinxer to the bibble
self.hatred[jinxer_id][jinxed_id] = jinx_info

def __repr__(self) -> str:
return "⨷"

def player_jinxed(self, jinxer_id: str, jinxed_id: str) -> bool:
"""Check if a player has been jinxed by another player"""
return jinxer_id in self.hatred and jinxed_id in self.hatred[jinxer_id]
Expand Down
139 changes: 25 additions & 114 deletions botcpdf/role.py
Expand Up @@ -2,14 +2,6 @@

from typing import Optional
from botcpdf.jinx import Jinx
from botcpdf.util import (
cleanup_role_id,
load_extra_roles,
load_fabled_data,
load_nightdata,
load_nightmeta,
load_role_data,
)


class Role:
Expand All @@ -21,9 +13,9 @@ class Role:
name: str
edition: Optional[str] = None
team: str
first_night: int = 0
first_night_position: int = 0
first_night_reminder: str
other_night: int = 0
other_night_position: int = 0
other_night_reminder: str
reminders: list[str]
setup: bool
Expand All @@ -36,14 +28,16 @@ def __init__(self, role_data: dict, stylize: bool = True):

# we expect these to always exist, so we don't need .get()
self.id_slug = role_data["id"]
self.name = role_data["name"]
self.team = role_data["team"]
self.first_night_position = role_data.get("firstNight", None)
self.first_night_reminder = role_data["firstNightReminder"]
self.name = role_data["name"]
self.other_night_position = role_data.get("otherNight", None)
self.other_night_reminder = role_data.get("otherNightReminder", "")
self.reminders = role_data.get("reminders", [])
self.setup = role_data.get("setup", False)
self.team = role_data["team"]

# we need to knoiw if we're stylizing or not before we can store the
# we need to know if we're stylizing or not before we can store the
# ability
self.stylized = stylize
self.ability = self.stylize(role_data["ability"])
Expand Down Expand Up @@ -94,8 +88,25 @@ def __repr__(self):
)

def __str__(self):
# build up night order info, if we have it
night_order = ""
if self.first_night_position is not None and self.first_night_position > 0:
night_order += f"first_night_position={self.first_night_position}"
if self.other_night_position is not None and self.other_night_position > 0:
# if we already have a first night position, we'll add a comma
if night_order:
night_order += ", "
night_order += f"other_night_position={self.other_night_position}"
# if we have a night order, prefix it with a comma and a space
if night_order:
night_order = f", {night_order}"

# jinxes, if we don't have them (empty list) strigify as 'None'
if not self.jinxes:
jinxes = "None"

# pylint: disable=line-too-long
return f"name: {self.name}, id: {self.id_slug}, team: {self.team}, edition: {self.edition}, jinxes: {self.jinxes}"
return f"Role(name='{self.name}', id_slug='{self.id_slug}', team='{self.team}', edition='{self.edition}', jinxes={jinxes}{night_order})"

def get_edition_name(self) -> str:
"""Get the name of the edition."""
Expand All @@ -105,103 +116,3 @@ def get_edition_name(self) -> str:
"bmr": "Bad Moon Rising",
}
return lookup.get(self.edition, "Unknown Edition")


class RoleData:
# pylint: disable=too-few-public-methods
"""Holds information for all the roles in the game"""

roles: dict[str, Role] = {}

def __init__(self):
"""Initialize role data."""

# 'regular' role info from roles-bra1n.json
self.add_character_roles()

# extra characters not in the main json (yet)
self.add_extra_roles()

# we'll add fabled roles to the same dict
self.add_fabled_roles()

# Demon and Minion info, Dawn
self.add_meta_roles()

# work out values for first_night and other_night
self.derive_night_values()

def add_extra_roles(self) -> None:
"""Add extra roles to the role data."""
role_data = load_extra_roles()
for role in role_data:
# if it already exists, we'll warn and preserve the existing data
if role["id"] in self.roles:
print(
f"Warning: role with id '{role['id']}' already exists; "
"preserving existing data"
)
continue

self.roles[role["id"]] = Role(role)

def derive_night_values(self):
"""Derive values for first_night and other_night"""
night_data = load_nightdata()

# loop through firstNight list in night_data; we need the index as well
for index, role_id in enumerate(night_data["firstNight"]):
# get the role object for the given id
role = self.get_role(cleanup_role_id(role_id))
# set the first_night attribute
role.first_night = index + 1

# loop through otherNight list in night_data; we need the index as well
for index, role_id in enumerate(night_data["otherNight"]):
# get the role object for the given id
role = self.get_role(cleanup_role_id(role_id))
# set the other_night attribute
role.other_night = index + 1

def get_role(self, id_slug: str) -> Role:
"""Get a role by ID."""

# if there's no role with the given id, raise an error
if id_slug not in self.roles:
# print the sorted list of role ids for debugging
raise ValueError(
f"Role with ID '{id_slug}' not found; "
f"""known role ids: {", ".join(sorted(self.roles.keys()))}"""
)

return self.roles[id_slug]

def add_character_roles(self) -> None:
"""Add character roles to the role data."""
role_data = load_role_data()
for role in role_data:
self.roles[role["id"]] = Role(role)

def add_fabled_roles(self) -> None:
"""Add character roles to the role data."""
role_data = load_fabled_data()
for role in role_data:
self.roles[role["id"]] = Role(role)

def add_meta_roles(self) -> None:
"""Add meta roles to the role data.

i.e. Minion/Demon info, Dawn, Dusk"""

nightmeta = load_nightmeta()

for role in nightmeta:
self.roles[role["id"]] = Role(role)

def get_first_night_meta_roles(self) -> list[Role]:
"""Get a list of meta roles."""
return [self.roles["_minion"], self.roles["_demon"], self.roles["_dawn"]]

def get_other_night_meta_roles(self) -> list[Role]:
"""Get a list of meta roles."""
return [self.roles["_dawn"], self.roles["_dusk"]]
49 changes: 49 additions & 0 deletions botcpdf/roledata.py
@@ -0,0 +1,49 @@
""" Holds information for all the roles in the game """


from botcpdf.role import Role
from botcpdf.util import get_role_data


class RoleData:
# pylint: disable=too-few-public-methods
"""Holds information for all the roles in the game"""

roles: dict[str, Role] = {}
json_filename: str = "data/imported/roles-combined.json"

def __init__(self):
"""Initialize role data."""

full_data = get_role_data()

# we have { "character_by_id": { id: {...} } }
# in our data; loop through the roles and create Role objects
# for each one
for role_id, role_data in full_data["character_by_id"].items():
# create the role
role = Role(role_data)

# store it in the dict
self.roles[role_id] = role

def get_role(self, id_slug: str) -> Role:
"""Get a role by ID."""

# if there's no role with the given id, raise an error
if id_slug not in self.roles:
# print the sorted list of role ids for debugging
raise ValueError(
f"Role with ID '{id_slug}' not found; "
f"""known role ids: {", ".join(sorted(self.roles.keys()))}"""
)

return self.roles[id_slug]

def get_first_night_meta_roles(self) -> list[Role]:
"""Get a list of meta roles."""
return [self.roles["MINION"], self.roles["DEMON"], self.roles["DAWN"]]

def get_other_night_meta_roles(self) -> list[Role]:
"""Get a list of meta roles."""
return [self.roles["DAWN"], self.roles["DUSK"]]