Moved mod_str function from forms.py back to stats.py, and black formatting.

This commit is contained in:
Mark Wolfman
2021-07-07 10:29:46 -05:00
parent 8a054b335f
commit a31b9eb42c
10 changed files with 99 additions and 59 deletions
+38 -27
View File
@@ -11,10 +11,7 @@ from dungeonsheets.forms import dice_re, jinja_environment
def create_epub(
chapters: Mapping,
title: str,
basename: str,
use_dnd_decorations: bool = False
chapters: Mapping, title: str, basename: str, use_dnd_decorations: bool = False
):
"""Prepare an EPUB file from the list of chapters.
@@ -34,14 +31,18 @@ def create_epub(
"""
# Create a new epub book
book = epub.EpubBook()
book.set_identifier('id123456')
book.set_identifier("id123456")
book.set_title(title)
book.set_language('en')
book.set_language("en")
# Add the css files
css_template = jinja_env.get_template("dungeonsheets_epub.css")
style = css_template.render(use_dnd_decorations=use_dnd_decorations)
css = epub.EpubItem(uid="style_default", file_name="style/gm_sheet.css",
media_type="text/css", content=style)
css = epub.EpubItem(
uid="style_default",
file_name="style/gm_sheet.css",
media_type="text/css",
content=style,
)
book.add_item(css)
toc = ["nav"]
# Create the separate chapters
@@ -49,15 +50,22 @@ def create_epub(
for chap_title, content in chapters.items():
chap_fname = chap_title.replace(" - ", "-").replace(" ", "_").lower()
chap_fname = "{}.html".format(chap_fname)
chapter = epub.EpubHtml(title=chap_title,
file_name=chap_fname, lang="en",
media_type="application/xhtml+xml")
chapter = epub.EpubHtml(
title=chap_title,
file_name=chap_fname,
lang="en",
media_type="application/xhtml+xml",
)
chapter.set_content(content)
chapter.add_item(css)
book.add_item(chapter)
html_chapters.append(chapter)
# Add entries for the table of contents
toc.append(toc_from_headings(html=content, filename=chap_fname, chapter_title=chap_title))
toc.append(
toc_from_headings(
html=content, filename=chap_fname, chapter_title=chap_title
)
)
# Add the table of contents
book.toc = toc
book.spine = ("nav", *html_chapters)
@@ -74,7 +82,7 @@ class HeadingParser(HTMLParser):
_curr_level = None
_curr_id = None
_curr_title = None
def __init__(self, *args, **kwargs):
self.headings = []
super().__init__(*args, **kwargs)
@@ -85,22 +93,22 @@ class HeadingParser(HTMLParser):
return int(match.group(1))
else:
return None
def handle_starttag(self, tag, attrs):
this_level = self.heading_level(tag)
if this_level is not None:
# Found a heading, so process the properties
self._curr_level = this_level
attrs = {k: v for k, v in attrs}
self._curr_id = attrs.get('id')
self._curr_id = attrs.get("id")
def handle_endtag(self, tag):
this_level = self.heading_level(tag)
if this_level is not None and this_level == self._curr_level:
heading = {
"level": this_level,
"id": self._curr_id,
"title": self._curr_title
"title": self._curr_title,
}
self.headings.append(heading)
@@ -110,7 +118,9 @@ class HeadingParser(HTMLParser):
self._curr_title = data
def toc_from_headings(html: str, filename: str = "", chapter_title: str = "Sheet") -> list:
def toc_from_headings(
html: str, filename: str = "", chapter_title: str = "Sheet"
) -> list:
"""Accept a chapter of HTML, and extract a table of contents segment.
Parameters
@@ -120,12 +130,12 @@ def toc_from_headings(html: str, filename: str = "", chapter_title: str = "Sheet
filename
The name of this file to be used for hrefs. E.g.
"index.html#heading_1".
Returns
-------
toc
A sequence of table-of-contents links.
"""
# [(<ebooklib.epub.Section at 0x7fdf903595d0>,
# [(<ebooklib.epub.Section at 0x7fdf90359310>,
@@ -149,18 +159,19 @@ def toc_from_headings(html: str, filename: str = "", chapter_title: str = "Sheet
href = f"{filename}#{heading['id']}"
parent_section = sections_stack[-1]
is_last = idx == (len(headings) - 1)
is_leaf = is_last or heading['level'] >= headings[idx+1]['level']
is_leaf = is_last or heading["level"] >= headings[idx + 1]["level"]
# Add a leaf or branch depending on the heading structure
if is_leaf:
parent_section[1].append(epub.Link(href=href, title=heading['title'], uid=href))
parent_section[1].append(
epub.Link(href=href, title=heading["title"], uid=href)
)
else:
new_section = (epub.Section(href=href, title=heading['title']),
[])
new_section = (epub.Section(href=href, title=heading["title"]), [])
parent_section[1].append(new_section)
sections_stack.append(new_section)
# Walk back up the stack
if not is_last:
for idx in range(max(0, heading['level'] - headings[idx + 1]['level'])):
for idx in range(max(0, heading["level"] - headings[idx + 1]["level"])):
sections_stack.pop()
return toc
@@ -258,5 +269,5 @@ def to_heading_id(inpt: str) -> str:
# Prepare the jinja environment
jinja_env = jinja_environment()
jinja_env.filters['rst_to_html'] = rst_to_html
jinja_env.filters['to_heading_id'] = to_heading_id
jinja_env.filters["rst_to_html"] = rst_to_html
jinja_env.filters["to_heading_id"] = to_heading_id
+1
View File
@@ -25,5 +25,6 @@ class JSONFormatError(RuntimeError):
class UnknownFileType(RuntimeError):
"""The input file does not match one of the known formats."""
class UnknownOutputFormat(RuntimeError):
"""The output format requested is not one of the known outputs."""
+1 -1
View File
@@ -160,7 +160,7 @@ def create_character_pdf_template(character, basename, flatten=False):
# Additional attacks beyond 3
attack = [
f"{w.name}: Atk {w.attack_modifier:+d}, Dam {w.damage}/{w.damage_type}"
for w in character.weapons[len(weapon_fields):]
for w in character.weapons[len(weapon_fields) :]
]
# Other attack information
if character.armor:
+3 -7
View File
@@ -3,15 +3,13 @@ import re
from jinja2 import Environment, PackageLoader
from dungeonsheets.stats import mod_str
# A dice string, with optional backticks: ``1d6 + 3``
dice_re = re.compile(r"`*(\d+d\d+(?:\s*\+\s*\d+)?)`*")
def mod_str(modifier):
"""Converts a modifier to a string, eg 2 -> '+2'."""
return "{:+d}".format(modifier)
def jinja_environment():
"""Prepare a new environment for Jinja templates.
@@ -34,5 +32,3 @@ def jinja_environment():
)
jinja_env.filters["mod_str"] = mod_str
return jinja_env
+6 -1
View File
@@ -43,7 +43,12 @@ def _remove_temp_files(basename_):
filename.unlink()
def create_latex_pdf(tex: str, basename: str, keep_temp_files: bool=False, use_dnd_decorations: bool=False):
def create_latex_pdf(
tex: str,
basename: str,
keep_temp_files: bool = False,
use_dnd_decorations: bool = False,
):
# Create tex document
tex_file = f"{basename}.tex"
with open(tex_file, mode="w", encoding="utf-8") as f:
+29 -11
View File
@@ -11,7 +11,15 @@ from multiprocessing import Pool, cpu_count
from itertools import product
from typing import Union, Sequence, Optional
from dungeonsheets import character as _char, exceptions, readers, latex, epub, monsters, forms
from dungeonsheets import (
character as _char,
exceptions,
readers,
latex,
epub,
monsters,
forms,
)
from dungeonsheets.forms import mod_str
from dungeonsheets.content_registry import find_content
from dungeonsheets.fill_pdf_template import (
@@ -127,13 +135,14 @@ def create_druid_shapes_tex(
def create_random_tables_content(
conjure_animals: bool,
suffix: str,
use_dnd_decorations: bool = False,
conjure_animals: bool,
suffix: str,
use_dnd_decorations: bool = False,
) -> str:
template = jinja_env.get_template(f"random_tables_template.{suffix}")
return template.render(conjure_animals=conjure_animals,
use_dnd_decorations=use_dnd_decorations)
return template.render(
conjure_animals=conjure_animals, use_dnd_decorations=use_dnd_decorations
)
def make_sheet(
@@ -236,7 +245,10 @@ def make_gm_sheet(
summary = gm_props.pop("summary", "")
content.append(
create_party_summary_content(
party, summary_rst=summary, suffix=content_suffix, use_dnd_decorations=fancy_decorations
party,
summary_rst=summary,
suffix=content_suffix,
use_dnd_decorations=fancy_decorations,
)
)
# Add the monsters
@@ -257,10 +269,14 @@ def make_gm_sheet(
monsters_.append(new_monster)
if len(monsters_) > 0:
content.append(
create_monsters_content(monsters_, suffix=content_suffix, use_dnd_decorations=fancy_decorations)
create_monsters_content(
monsters_, suffix=content_suffix, use_dnd_decorations=fancy_decorations
)
)
# Add the random tables
random_tables = [s.replace(" ", "_").lower() for s in gm_props.pop("random_tables", [])]
random_tables = [
s.replace(" ", "_").lower() for s in gm_props.pop("random_tables", [])
]
content.append(
create_random_tables_content(
conjure_animals=("conjure_animals" in random_tables),
@@ -303,7 +319,8 @@ def make_gm_sheet(
)
else:
raise exceptions.UnknownOutputFormat(
f"Unknown output format requested: {output_format}. Valid options are: 'pdf', 'epub'"
f"Unknown output format requested: {output_format}. Valid options are:"
" 'pdf', 'epub'"
)
@@ -512,7 +529,8 @@ def main(args=None):
),
)
parser.add_argument(
"--output-format", "-o",
"--output-format",
"-o",
help="Specify the output format for the sheets.",
choices=["pdf", "epub"],
default="pdf",
+7 -1
View File
@@ -4,7 +4,13 @@ game mechanics."""
from dungeonsheets.spells import Spell
from dungeonsheets.features import Feature
from dungeonsheets.infusions import Infusion
from dungeonsheets.weapons import Weapon, MeleeWeapon, RangedWeapon, SimpleWeapon, MartialWeapon
from dungeonsheets.weapons import (
Weapon,
MeleeWeapon,
RangedWeapon,
SimpleWeapon,
MartialWeapon,
)
from dungeonsheets.armor import Armor, Shield
from dungeonsheets.magic_items import MagicItem
from dungeonsheets.monsters import Monster
+8 -8
View File
@@ -9,7 +9,7 @@ from dungeonsheets.stats import Ability
class Baboon(Monster):
""" Pack Tactics.
"""Pack Tactics.
The baboon has advantage on an attack roll against a creature if
at least one of the baboon's allies is within 5 ft. of the
creature and the ally isn't incapacitated.
@@ -109,11 +109,11 @@ class Balor(Monster):
Variant: Summon Demon.
The demon chooses what to summon and attempts a magical
summoning.
A balor has a 50 percent chance of summoning 1d8 vrocks, 1d6
hezrous, 1d4 glabrezus, 1d3 nalfeshnees, 1d2 mariliths, or one
goristro.
A summoned demon appears in an unoccupied space within 60 feet
of its summoner, acts as an ally of its summoner, and can't
summon other demons. It remains for 1 minute, until it or its
@@ -148,7 +148,7 @@ class Bandit(Monster):
Light Crossbow.
Ranged Weapon Attack: +3 to hit, range 80 ft./320 ft., one
target. Hit: 5 (1d8 + 1) piercing damage.
"""
name = "Bandit"
@@ -262,20 +262,20 @@ class Basilisk(Monster):
success, the effect ends. On a failure, the creature is
petrified until freed by the greater restoration spell or other
magic.
A creature that isn't surprised can avert its eyes to avoid the
saving throw at the start of its turn. If it does so, it can't
see the basilisk until the start of its next turn, when it can
avert its eyes again. If it looks at the basilisk in the
meantime, it must immediately make the save.
If the basilisk sees its reflection within 30 ft. of it in
bright light, it mistakes itself for a rival and targets itself
with its gaze.
Bite.
Melee Weapon Attack: +5 to hit, reach 5 ft., one target. Hit: 10
(2d6 + 3) piercing damage plus 7 (2d6) poison damage.
"""
name = "Basilisk"
@@ -410,7 +410,7 @@ class Behir(Monster):
other effects outside the behir, and it takes 21 (6d6) acid
damage at the start of each of the behir's turns. A behir can
have only one creature swallowed at a time.
If the behir takes 30 damage or more on a single turn from the
swallowed creature, the behir must succeed on a DC 14
Constitution saving throw at the end of that turn or regurgitate
+1 -3
View File
@@ -528,9 +528,7 @@ class FoundryCharacterReader(JSONCharacterReader):
tool_profs.extend([s.strip() for s in custom_tool_profs.split(";")])
char_props["_proficiencies_text"] = tool_profs
# Combat stats
char_props["hp_max"] = self.as_int(
json_data["data"]["attributes"]["hp"]["max"]
)
char_props["hp_max"] = self.as_int(json_data["data"]["attributes"]["hp"]["max"])
# Equipment
currency = json_data["data"]["currency"]
char_props["cp"] = currency["cp"]
+5
View File
@@ -30,6 +30,11 @@ from dungeonsheets.features import (
log = logging.getLogger(__name__)
def mod_str(modifier):
"""Converts a modifier to a string, eg 2 -> '+2'."""
return "{:+d}".format(modifier)
AbilityScore = namedtuple("AbilityScore", ("value", "modifier", "saving_throw"))