diff --git a/dungeonsheets/character.py b/dungeonsheets/character.py index 81b04ac..0a06a3a 100644 --- a/dungeonsheets/character.py +++ b/dungeonsheets/character.py @@ -4,7 +4,7 @@ import re import warnings import math from types import ModuleType -from typing import Sequence, Union +from typing import Sequence, Union, MutableMapping import jinja2 @@ -22,7 +22,7 @@ from dungeonsheets import ( ) from dungeonsheets.stats import findattr from dungeonsheets.weapons import Weapon -from dungeonsheets.readers import read_character_file +from dungeonsheets.readers import read_sheet_file from dungeonsheets.entity import Entity @@ -884,9 +884,25 @@ class Character(Entity): return () @classmethod - def load(Cls, character_file): - # Create a character from the character definition - char_props = read_character_file(character_file) + def load(Cls, char_props: MutableMapping): + """Factory Creates a character from the character definition. + + 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", []) # backwards compatability if (len(classes) == 0) and ("character_class" in char_props): diff --git a/dungeonsheets/make_sheets.py b/dungeonsheets/make_sheets.py index 82b7446..f9d2d63 100755 --- a/dungeonsheets/make_sheets.py +++ b/dungeonsheets/make_sheets.py @@ -12,8 +12,7 @@ from itertools import product from jinja2 import Environment, PackageLoader -from dungeonsheets import character as _char -from dungeonsheets import exceptions, readers, latex +from dungeonsheets import character as _char, exceptions, readers, latex from dungeonsheets.stats import mod_str from dungeonsheets.fill_pdf_template import ( create_character_pdf_template, @@ -132,7 +131,8 @@ def make_sheet( """ 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 char_base = os.path.splitext(character_file)[0] + "_char" diff --git a/dungeonsheets/readers.py b/dungeonsheets/readers.py index febd654..11cd59f 100644 --- a/dungeonsheets/readers.py +++ b/dungeonsheets/readers.py @@ -12,7 +12,7 @@ from dungeonsheets import exceptions 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. The definition file should be an importable python file or a JSON diff --git a/examples/gm.py b/examples/gm.py new file mode 100644 index 0000000..172ff85 --- /dev/null +++ b/examples/gm.py @@ -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" diff --git a/tests/test_character.py b/tests/test_character.py index 1dc4440..64db68d 100755 --- a/tests/test_character.py +++ b/tests/test_character.py @@ -235,6 +235,11 @@ class TestCharacter(TestCase): char.race = None 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): def test_learned_spells(self): diff --git a/tests/test_make_sheets.py b/tests/test_make_sheets.py index f8f6a16..c294357 100644 --- a/tests/test_make_sheets.py +++ b/tests/test_make_sheets.py @@ -1,9 +1,8 @@ import unittest import os -from pathlib import Path 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/")) @@ -25,5 +24,59 @@ class PdfOutputTestCase(unittest.TestCase): # self.assertFalse(os.path.exists(pdf_name), f'{pdf_name} already exists.') char = character.Character(name="Clara") 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.") + + +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) diff --git a/tests/test_readers.py b/tests/test_readers.py index aac430f..9f3c8ba 100644 --- a/tests/test_readers.py +++ b/tests/test_readers.py @@ -3,27 +3,40 @@ from pathlib import Path import unittest 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() CHAR_PYTHON_FILE = EG_DIR / "rogue1.py" +GM_PYTHON_FILE = EG_DIR / "gm.py" ROLL20_JSON_FILE = EG_DIR / "barbarian3.json" FOUNDRY_JSON_FILE = EG_DIR / "bard3_foundry.json" SPELLCASTER_JSON_FILE = EG_DIR / "artificer2.json" 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 - result = read_character_file(charfile) + result = read_sheet_file(charfile) 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): def test_load_json_file(self): charfile = ROLL20_JSON_FILE with warnings.catch_warnings(record=True): - result = read_character_file(charfile) + result = read_sheet_file(charfile) expected_data = dict( name="Ulthar Jenkins", classes=["Barbarian"], @@ -88,7 +101,7 @@ class Roll20ReaderTests(unittest.TestCase): def test_load_json_spells(self): charfile = SPELLCASTER_JSON_FILE with warnings.catch_warnings(record=True): - result = read_character_file(charfile) + result = read_sheet_file(charfile) expected_data = dict( spells_prepared=[ "cure wounds", @@ -127,7 +140,7 @@ class FoundryReaderTests(unittest.TestCase): def test_load_json_file(self): charfile = FOUNDRY_JSON_FILE with warnings.catch_warnings(record=True): - result = read_character_file(charfile) + result = read_sheet_file(charfile) expected_data = dict( name="Sam Lloyd", classes=["Bard"], @@ -207,7 +220,7 @@ class FoundryReaderTests(unittest.TestCase): def test_load_json_spells(self): charfile = SPELLCASTER_JSON_FILE with warnings.catch_warnings(record=True): - result = read_character_file(charfile) + result = read_sheet_file(charfile) expected_data = dict( spells_prepared=[ "cure wounds",