formatting fixes, fix a bug when resetting an existing table

This commit is contained in:
evilchili 2024-02-17 17:05:33 -08:00
parent 6984bfc5e8
commit c3a58696c2
4 changed files with 102 additions and 106 deletions

View File

@ -1,12 +1,13 @@
from collections import defaultdict from collections import defaultdict
from rolltable.types import RollTable
from rolltable import tables
import typer
from enum import Enum from enum import Enum
from rich import print
from pathlib import Path from pathlib import Path
from typing import List from typing import List
import typer
from rich import print
from rolltable import tables
from rolltable.types import RollTable
app = typer.Typer() app = typer.Typer()
app_state = defaultdict( app_state = defaultdict(
@ -16,89 +17,71 @@ app_state = defaultdict(
class OUTPUT_FORMATS(Enum): class OUTPUT_FORMATS(Enum):
text = 'text' text = "text"
yaml = 'yaml' yaml = "yaml"
markdown = 'markdown' markdown = "markdown"
@app.callback() @app.callback()
def main( def main(
frequency: str = typer.Option( frequency: str = typer.Option("default", help="use the specified frequency from the source file"),
'default', die: int = typer.Option(20, help="The size of the die for which to create a table"),
help='use the specified frequency from the source file'
),
die: int = typer.Option(
20,
help='The size of the die for which to create a table'
),
hide_rolls: bool = typer.Option( hide_rolls: bool = typer.Option(
False, False,
help='If True, do not show the Roll column.', help="If True, do not show the Roll column.",
),
collapsed: bool = typer.Option(
True,
help='If True, collapse multiple die values with the same option.'
),
width: int = typer.Option(
120,
help='Width of the table.'
), ),
collapsed: bool = typer.Option(True, help="If True, collapse multiple die values with the same option."),
width: int = typer.Option(120, help="Width of the table."),
output: OUTPUT_FORMATS = typer.Option( output: OUTPUT_FORMATS = typer.Option(
'text', "text",
help='The output format to use.', help="The output format to use.",
) ),
): ):
app_state['options'] = { app_state["options"] = {
'frequency': frequency, "frequency": frequency,
'die': die, "die": die,
'hide_rolls': hide_rolls, "hide_rolls": hide_rolls,
} }
app_state['collapsed'] = collapsed app_state["collapsed"] = collapsed
app_state['width'] = width app_state["width"] = width
app_state['output'] = output app_state["output"] = output
@app.command("custom") @app.command("custom")
def custom( def custom(
sources: List[Path] = typer.Argument( sources: List[Path] = typer.Argument(..., help="Path to one or more yaml-formatted source file."),
...,
help="Path to one or more yaml-formatted source file."),
): ):
""" """
Create roll tables from custom sources. Create roll tables from custom sources.
""" """
rt = RollTable([Path(s).read_text() for s in sources], **app_state['options']) rt = RollTable([Path(s).read_text() for s in sources], **app_state["options"])
print_table(rt) print_table(rt)
def print_table(table): def print_table(table):
if app_state['output'] == OUTPUT_FORMATS.yaml: if app_state["output"] == OUTPUT_FORMATS.yaml:
print(table.as_yaml()) print(table.as_yaml())
elif app_state['output'] == OUTPUT_FORMATS.markdown: elif app_state["output"] == OUTPUT_FORMATS.markdown:
print(table.as_markdown()) print(table.as_markdown())
else: else:
print(table.as_table( print(table.as_table(width=app_state["width"], expanded=not app_state["collapsed"]))
width=app_state['width'],
expanded=not app_state['collapsed']
))
def make_callback(roll_table_instance): def make_callback(roll_table_instance):
def inner(): def inner():
roll_table_instance.frequency = app_state['options']['frequency'] roll_table_instance.frequency = app_state["options"]["frequency"]
roll_table_instance.die = app_state['options']['die'] roll_table_instance.die = app_state["options"]["die"]
print_table(roll_table_instance) print_table(roll_table_instance)
return inner return inner
# step through all the predfined tables and create a cli for each # step through all the predfined tables and create a cli for each
for name, table in tables.index.items(): for name, table in tables.index.items():
help_text = name.replace('_', ' ').title() help_text = name.replace("_", " ").title()
app.command(name=name, help=f"Create a roll table of {help_text}")( app.command(name=name, help=f"Create a roll table of {help_text}")(make_callback(table))
make_callback(table)
)
if __name__ == '__main__': if __name__ == "__main__":
app() app()

View File

@ -1,21 +1,19 @@
from pathlib import Path from pathlib import Path
from rolltable.types import RollTable
from typing import Any from typing import Any
from rolltable.types import RollTable
def from_sources(names: list[str] = []) -> list: def from_sources(names: list[str] = []) -> list:
return RollTable([ return RollTable([(Path(__file__).parent / "sources" / name).read_text() for name in names])
(Path(__file__).parent / "sources" / name).read_text()
for name in names
])
index = dict( index = dict(
psychadelic_effects=from_sources(['psychadelic_effects.yaml']), psychadelic_effects=from_sources(["psychadelic_effects.yaml"]),
trinkets=from_sources(['trinkets.yaml']), trinkets=from_sources(["trinkets.yaml"]),
wild_magic=from_sources(['wild_magic.yaml']), wild_magic=from_sources(["wild_magic.yaml"]),
spells=from_sources(['spells.yaml']), spells=from_sources(["spells.yaml"]),
encounters=from_sources(['encounters.yaml']), encounters=from_sources(["encounters.yaml"]),
) )

View File

@ -1,10 +1,10 @@
import yaml
from csv2md.table import Table
from collections.abc import Iterable from collections.abc import Iterable
from typing import Optional, List, Union from typing import List, Optional, Union
from random_sets.datasources import DataSource
import rich.table import rich.table
import yaml
from csv2md.table import Table
from random_sets.datasources import DataSource
class RollTable: class RollTable:
@ -29,10 +29,15 @@ class RollTable:
d2-d4 Bar d2-d4 Bar
""" """
def __init__(self, sources: Union[List[str], List[DataSource]], frequency: str = 'default', def __init__(
die: Optional[int] = 20, hide_rolls: bool = False) -> None: self,
sources: Union[List[str], List[DataSource]],
frequency: str = "default",
die: Optional[int] = 20,
hide_rolls: bool = False,
) -> None:
self._sources = sources self._sources = sources
self._frequency = frequency self.frequency = frequency
self.die = die self.die = die
self.hide_rolls = hide_rolls self.hide_rolls = hide_rolls
self.data = None self.data = None
@ -70,21 +75,19 @@ class RollTable:
] ]
""" """
if not self._generated_values: if not self._generated_values:
self._generated_values = list(zip(*[ self._generated_values = list(zip(*[t.random_values(self.die) for t in self._data]))
t.random_values(self.die) for t in self._data
]))
return self._generated_values return self._generated_values
@property @property
def rows(self) -> List: def rows(self) -> List:
def formatted(lastrow, offset, row, i): def formatted(lastrow, offset, row, i):
thisrow = [f'd{i}' if offset + 1 == i else f'd{offset+1}-d{i}'] thisrow = [f"d{i}" if offset + 1 == i else f"d{offset+1}-d{i}"]
thisrow += self._flatten(lastrow) thisrow += self._flatten(lastrow)
return self._column_filter(thisrow) return self._column_filter(thisrow)
lastrow = None lastrow = None
offset = 0 offset = 0
self._rows = [self._column_filter(['Roll'] + self.headers)] self._rows = [self._column_filter(["Roll"] + self.headers)]
for face in range(self.die): for face in range(self.die):
row = self._values[face] row = self._values[face]
@ -96,19 +99,20 @@ class RollTable:
self._rows.append(formatted(lastrow, offset, row, face)) self._rows.append(formatted(lastrow, offset, row, face))
lastrow = row lastrow = row
offset = face offset = face
self._rows.append(formatted(lastrow, offset, row, face+1)) self._rows.append(formatted(lastrow, offset, row, face + 1))
return self._rows return self._rows
@property @property
def expanded_rows(self) -> List: def expanded_rows(self) -> List:
self._rows = [self._column_filter(['Roll'] + self.headers)] self._rows = [self._column_filter(["Roll"] + self.headers)]
for face in range(self.die): for face in range(self.die):
row = self._values[face] row = self._values[face]
self._rows.append(self._column_filter([f'd{face+1}'] + row)) self._rows.append(self._column_filter([f"d{face+1}"] + row))
return self._rows return self._rows
def reset(self) -> None: def reset(self) -> None:
self._generated_values = None self._generated_values = None
self._config()
def as_markdown(self) -> str: def as_markdown(self) -> str:
return Table(self.rows).markdown() return Table(self.rows).markdown()
@ -118,9 +122,9 @@ class RollTable:
for row in self.rows[1:]: for row in self.rows[1:]:
struct[row[0]] = {} struct[row[0]] = {}
# pad rows with empty cols as necessary # pad rows with empty cols as necessary
cols = row[1:] + [''] * (len(self.headers) - len(row[1:])) cols = row[1:] + [""] * (len(self.headers) - len(row[1:]))
for idx, col in enumerate(cols): for idx, col in enumerate(cols):
struct[row[0]][self.headers[idx] if idx < len(self.headers) else '_'] = col struct[row[0]][self.headers[idx] if idx < len(self.headers) else "_"] = col
return yaml.dump(struct, sort_keys=False) return yaml.dump(struct, sort_keys=False)
def as_table(self, width: int = 120, expanded: bool = False) -> str: def as_table(self, width: int = 120, expanded: bool = False) -> str:
@ -138,7 +142,7 @@ class RollTable:
self._header_excludes = [] self._header_excludes = []
for i in range(len(self._headers)): for i in range(len(self._headers)):
if self.headers[i] is None: if self.headers[i] is None:
self._header_excludes.append(i+1) self._header_excludes.append(i + 1)
def _config(self): def _config(self):
""" """
@ -151,7 +155,7 @@ class RollTable:
if type(src) is str: if type(src) is str:
src = [src] src = [src]
for one_source in src: for one_source in src:
ds = DataSource(one_source, frequency=self._frequency) ds = DataSource(one_source, frequency=self.frequency)
ds.load_source() ds.load_source()
self._data.append(ds) self._data.append(ds)
@ -162,9 +166,9 @@ class RollTable:
self.set_headers(*headers) self.set_headers(*headers)
def _column_filter(self, row): def _column_filter(self, row):
cols = [col or '' for (pos, col) in enumerate(row) if pos not in self._header_excludes] cols = [col or "" for (pos, col) in enumerate(row) if pos not in self._header_excludes]
# pad the row with empty columns if there are more headers than columns # pad the row with empty columns if there are more headers than columns
cols = cols + [''] * (1 + len(self.headers) - len(row)) cols = cols + [""] * (1 + len(self.headers) - len(row))
# strip the leading column if we're hiding the dice rolls # strip the leading column if we're hiding the dice rolls
return cols[1:] if self.hide_rolls else cols return cols[1:] if self.hide_rolls else cols
@ -177,5 +181,5 @@ class RollTable:
def __repr__(self) -> str: def __repr__(self) -> str:
rows = list(self.rows) rows = list(self.rows)
str_format = '\t'.join(['{:10s}'] * len(rows[0])) str_format = "\t".join(["{:10s}"] * len(rows[0]))
return "\n".join([str_format.format(*[r or '' for r in row]) for row in rows]) return "\n".join([str_format.format(*[r or "" for r in row]) for row in rows])

View File

@ -3,26 +3,37 @@ import pytest
from rolltable import tables from rolltable import tables
@pytest.mark.parametrize('table, expected', [ @pytest.mark.parametrize(
(tables.wild_magic, ['A third eye', 'Advantage on perception checks']), "table, expected",
(tables.trinkets, ['ivory mimic']), [
(tables.psychadelic_effects, ['Cosmic', 'mind expands', 'it will become so']), (tables.wild_magic, ["A third eye", "Advantage on perception checks"]),
(tables.encounters, ['None', 'Easy', 'Difficult', 'Dangerous', 'Deadly']) (tables.trinkets, ["ivory mimic"]),
]) (tables.psychadelic_effects, ["Cosmic", "mind expands", "it will become so"]),
(tables.encounters, ["None", "Easy", "Difficult", "Dangerous", "Deadly"]),
],
)
def test_flat(table, expected): def test_flat(table, expected):
table.die = 1000 table.die = 1000
assert 'd1000' in str(table) assert "d1000" in str(table)
for txt in expected: for txt in expected:
assert txt in str(table) assert txt in str(table)
def test_encounter_frequencies(): def test_encounter_frequencies():
table = tables.encounters table = tables.encounters
table.die = 1000
table.frequency = "none"
table.reset()
assert "Deadly" not in str(table)
table.frequency = "deadly"
table.reset()
assert "Deadly" in str(table)
def test_markdown(): def test_markdown():
tables.trinkets.die = 1 tables.trinkets.die = 1
md = tables.trinkets.as_markdown() md = tables.trinkets.as_markdown()
assert '| Roll | Trinket ' in md assert "| Roll | Trinket " in md
assert '| ---- |' in md assert "| ---- |" in md
assert 'd1' in md assert "d1" in md