mirror of
https://github.com/Threnklyn/dungeon-sheets.git
synced 2026-05-18 20:23:27 +02:00
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:
@@ -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):
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -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):
|
||||||
|
|||||||
@@ -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
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user