Prepared package for adding GM sheets.

- Improved test coverage in ``make_sheets.py``.
- Refactor file loading to make it more flexible.
- Added a stub ``gm.py`` example.
This commit is contained in:
Mark Wolfman
2021-06-01 22:51:10 -05:00
parent bb3a40ed0e
commit 3b8dbc0566
7 changed files with 116 additions and 19 deletions
+21 -5
View File
@@ -4,7 +4,7 @@ import re
import warnings import warnings
import math import math
from types import ModuleType from types import ModuleType
from typing import Sequence, Union from typing import Sequence, Union, MutableMapping
import jinja2 import jinja2
@@ -22,7 +22,7 @@ from dungeonsheets import (
) )
from dungeonsheets.stats import findattr from dungeonsheets.stats import findattr
from dungeonsheets.weapons import Weapon from dungeonsheets.weapons import Weapon
from dungeonsheets.readers import read_character_file from dungeonsheets.readers import read_sheet_file
from dungeonsheets.entity import Entity from dungeonsheets.entity import Entity
@@ -884,9 +884,25 @@ class Character(Entity):
return () return ()
@classmethod @classmethod
def load(Cls, character_file): def load(Cls, char_props: MutableMapping):
# Create a character from the character definition """Factory Creates a character from the character definition.
char_props = read_character_file(character_file)
Parameters
==========
char_props
Keys and values holding all the attributes of the
character. E.g. ``char_props['strength'] = 16``
Returns
=======
char
The initialized ``Character`` object with associated
parameters.
"""
# Parse the sheet type
char_props.pop('sheet_type', "")
# Load classes
classes = char_props.get("classes", []) classes = char_props.get("classes", [])
# backwards compatability # backwards compatability
if (len(classes) == 0) and ("character_class" in char_props): if (len(classes) == 0) and ("character_class" in char_props):
+3 -3
View File
@@ -12,8 +12,7 @@ from itertools import product
from jinja2 import Environment, PackageLoader from jinja2 import Environment, PackageLoader
from dungeonsheets import character as _char from dungeonsheets import character as _char, exceptions, readers, latex
from dungeonsheets import exceptions, readers, latex
from dungeonsheets.stats import mod_str from dungeonsheets.stats import mod_str
from dungeonsheets.fill_pdf_template import ( from dungeonsheets.fill_pdf_template import (
create_character_pdf_template, create_character_pdf_template,
@@ -132,7 +131,8 @@ def make_sheet(
""" """
if character is None: if character is None:
character = _char.Character.load(character_file) char_props = readers.read_sheet_file(character_file)
character = _char.Character.load(char_props)
# Set the fields in the FDF # Set the fields in the FDF
char_base = os.path.splitext(character_file)[0] + "_char" char_base = os.path.splitext(character_file)[0] + "_char"
+1 -1
View File
@@ -12,7 +12,7 @@ from dungeonsheets import exceptions
log = logging.getLogger(__file__) log = logging.getLogger(__file__)
def read_character_file(filename: str): def read_sheet_file(filename: str):
"""Create a character object from the given definition file. """Create a character object from the given definition file.
The definition file should be an importable python file or a JSON The definition file should be an importable python file or a JSON
+10
View File
@@ -0,0 +1,10 @@
"""This file describes game-manager notes.
It's used for creating notes for the GM to keep track of various
monsters, etc.
"""
dungeonsheets_version = "0.14.0"
sheet_type = "gm"
+5
View File
@@ -235,6 +235,11 @@ class TestCharacter(TestCase):
char.race = None char.race = None
self.assertEqual(char.speed, "30") self.assertEqual(char.speed, "30")
def test_load_char(self):
char = Character.load({"name": "Dave", "sheet_type": "character"})
self.assertFalse(hasattr(char, "sheet_type"),
"'sheet_type' not stripped from char props")
class DruidTestCase(TestCase): class DruidTestCase(TestCase):
def test_learned_spells(self): def test_learned_spells(self):
+56 -3
View File
@@ -1,9 +1,8 @@
import unittest import unittest
import os import os
from pathlib import Path
from dungeonsheets import make_sheets, character from dungeonsheets import make_sheets, character
from dungeonsheets.fill_pdf_template import create_character_pdf_template, create_spells_pdf_template from dungeonsheets.classes import monk
EG_DIR = os.path.abspath(os.path.join(os.path.split(__file__)[0], "../examples/")) EG_DIR = os.path.abspath(os.path.join(os.path.split(__file__)[0], "../examples/"))
@@ -25,5 +24,59 @@ class PdfOutputTestCase(unittest.TestCase):
# self.assertFalse(os.path.exists(pdf_name), f'{pdf_name} already exists.') # self.assertFalse(os.path.exists(pdf_name), f'{pdf_name} already exists.')
char = character.Character(name="Clara") char = character.Character(name="Clara")
char.saving_throw_proficiencies = ["strength"] char.saving_throw_proficiencies = ["strength"]
make_sheets.create_character_pdf_template(character=char, basename=self.basename) make_sheets.create_character_pdf_template(
character=char, basename=self.basename
)
self.assertTrue(os.path.exists(pdf_name), f"{pdf_name} not created.") self.assertTrue(os.path.exists(pdf_name), f"{pdf_name} not created.")
class TexCreatorTestCase(unittest.TestCase):
"""Test various helper functions for creating TeX from a character."""
def new_character(self):
char = character.Character(
classes=["Monk", "Druid", "Artificer"],
levels=[1, 1, 1],
subclasses=["way of the open hand", None, None],
magic_items=["cloak of protection"],
spells=["invisibility"],
wild_shapes=["crocodile"],
infusions=["boots of the winding path"]
)
return char
def test_create_subclasses_tex(self):
char = self.new_character()
tex = make_sheets.create_subclasses_tex(character=char)
self.assertIn(r"\section*{Subclasses}", tex)
self.assertIn(r"\subsection*{Way of the Open Hand}", tex)
def test_create_features_tex(self):
char = self.new_character()
tex = make_sheets.create_features_tex(character=char)
self.assertIn(r"\section*{Features}", tex)
self.assertIn(r"\subsection*{Martial Arts}", tex)
def test_create_magic_items_tex(self):
char = self.new_character()
tex = make_sheets.create_magic_items_tex(character=char)
self.assertIn(r"\section*{Magic Items}", tex)
self.assertIn(r"\subsection*{Cloak of Protection}", tex)
def test_create_spellbook_tex(self):
char = self.new_character()
tex = make_sheets.create_spellbook_tex(character=char)
self.assertIn(r"\section*{Spells}", tex)
self.assertIn(r"\section*{Invisibility}", tex)
def test_create_infusions_tex(self):
char = self.new_character()
tex = make_sheets.create_infusions_tex(character=char)
self.assertIn(r"\section*{Infusions}", tex)
self.assertIn(r"\subsection*{Boots of the Winding Path}", tex)
def test_create_druid_shapes_tex(self):
char = self.new_character()
tex = make_sheets.create_druid_shapes_tex(character=char)
self.assertIn(r"\section*{Known Beasts}", tex)
self.assertIn(r"\section*{Crocodile}", tex)
+20 -7
View File
@@ -3,27 +3,40 @@ from pathlib import Path
import unittest import unittest
import types import types
from dungeonsheets.readers import read_character_file from dungeonsheets import exceptions
from dungeonsheets.readers import read_sheet_file
EG_DIR = (Path(__file__).parent.parent / "examples").resolve() EG_DIR = (Path(__file__).parent.parent / "examples").resolve()
CHAR_PYTHON_FILE = EG_DIR / "rogue1.py" CHAR_PYTHON_FILE = EG_DIR / "rogue1.py"
GM_PYTHON_FILE = EG_DIR / "gm.py"
ROLL20_JSON_FILE = EG_DIR / "barbarian3.json" ROLL20_JSON_FILE = EG_DIR / "barbarian3.json"
FOUNDRY_JSON_FILE = EG_DIR / "bard3_foundry.json" FOUNDRY_JSON_FILE = EG_DIR / "bard3_foundry.json"
SPELLCASTER_JSON_FILE = EG_DIR / "artificer2.json" SPELLCASTER_JSON_FILE = EG_DIR / "artificer2.json"
class PythonReaderTests(unittest.TestCase): class PythonReaderTests(unittest.TestCase):
def test_load_python_file(self): def test_load_python_gm_sheet(self):
gmfile = GM_PYTHON_FILE
result = read_sheet_file(gmfile)
self.assertEqual(result["sheet_type"], "gm")
def test_load_python_character(self):
charfile = CHAR_PYTHON_FILE charfile = CHAR_PYTHON_FILE
result = read_character_file(charfile) result = read_sheet_file(charfile)
self.assertEqual(result["strength"], 10) self.assertEqual(result["strength"], 10)
def test_load_bad_file(self):
"""This file is not a valid character, so should fail."""
this_file = __file__
with self.assertRaises(exceptions.CharacterFileFormatError):
read_sheet_file(this_file)
class Roll20ReaderTests(unittest.TestCase): class Roll20ReaderTests(unittest.TestCase):
def test_load_json_file(self): def test_load_json_file(self):
charfile = ROLL20_JSON_FILE charfile = ROLL20_JSON_FILE
with warnings.catch_warnings(record=True): with warnings.catch_warnings(record=True):
result = read_character_file(charfile) result = read_sheet_file(charfile)
expected_data = dict( expected_data = dict(
name="Ulthar Jenkins", name="Ulthar Jenkins",
classes=["Barbarian"], classes=["Barbarian"],
@@ -88,7 +101,7 @@ class Roll20ReaderTests(unittest.TestCase):
def test_load_json_spells(self): def test_load_json_spells(self):
charfile = SPELLCASTER_JSON_FILE charfile = SPELLCASTER_JSON_FILE
with warnings.catch_warnings(record=True): with warnings.catch_warnings(record=True):
result = read_character_file(charfile) result = read_sheet_file(charfile)
expected_data = dict( expected_data = dict(
spells_prepared=[ spells_prepared=[
"cure wounds", "cure wounds",
@@ -127,7 +140,7 @@ class FoundryReaderTests(unittest.TestCase):
def test_load_json_file(self): def test_load_json_file(self):
charfile = FOUNDRY_JSON_FILE charfile = FOUNDRY_JSON_FILE
with warnings.catch_warnings(record=True): with warnings.catch_warnings(record=True):
result = read_character_file(charfile) result = read_sheet_file(charfile)
expected_data = dict( expected_data = dict(
name="Sam Lloyd", name="Sam Lloyd",
classes=["Bard"], classes=["Bard"],
@@ -207,7 +220,7 @@ class FoundryReaderTests(unittest.TestCase):
def test_load_json_spells(self): def test_load_json_spells(self):
charfile = SPELLCASTER_JSON_FILE charfile = SPELLCASTER_JSON_FILE
with warnings.catch_warnings(record=True): with warnings.catch_warnings(record=True):
result = read_character_file(charfile) result = read_sheet_file(charfile)
expected_data = dict( expected_data = dict(
spells_prepared=[ spells_prepared=[
"cure wounds", "cure wounds",