Skip to content

Commit 7ec6845

Browse files
committed
makes model and classlist printing more flexible via a display_fields property
1 parent c7fb185 commit 7ec6845

File tree

4 files changed

+120
-54
lines changed

4 files changed

+120
-54
lines changed

RATapi/classlist.py

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -56,31 +56,42 @@ def __init__(self, init_list: Union[Sequence[T], T] = None, name_field: str = "n
5656
super().__init__(init_list)
5757

5858
def __str__(self):
59+
# `display_fields` gives more control over the items displayed from the list if available
60+
if not self.data:
61+
return str([])
5962
try:
60-
[model.__dict__ for model in self.data]
63+
model_display_fields = [model.display_fields for model in self.data]
64+
# get all items included in at least one list
65+
# the list comprehension ensures they are in the order that they're in in the model
66+
required_fields = list(set().union(*model_display_fields))
67+
table_fields = ["index"] + [i for i in list(self.data[0].__dict__) if i in required_fields]
6168
except AttributeError:
62-
output = str(self.data)
69+
try:
70+
model_display_fields = [model.__dict__ for model in self.data]
71+
table_fields = ["index"] + list(self.data[0].__dict__)
72+
except AttributeError:
73+
return str(self.data)
74+
75+
if any(model_display_fields):
76+
table = prettytable.PrettyTable()
77+
table.field_names = [field.replace("_", " ") for field in table_fields]
78+
rows = []
79+
for index, model in enumerate(self.data):
80+
row = [index]
81+
for field in table_fields[1:]:
82+
value = getattr(model, field, "")
83+
if isinstance(value, np.ndarray):
84+
value = f"{'Data array: ['+' x '.join(str(i) for i in value.shape) if value.size > 0 else '['}]"
85+
elif field == "model":
86+
value = "\n".join(str(element) for element in value)
87+
else:
88+
value = str(value)
89+
row.append(value)
90+
rows.append(row)
91+
table.add_rows(rows)
92+
output = table.get_string()
6393
else:
64-
if any(model.__dict__ for model in self.data):
65-
table = prettytable.PrettyTable()
66-
table.field_names = ["index"] + [key.replace("_", " ") for key in self.data[0].__dict__]
67-
table.add_rows(
68-
[
69-
[index]
70-
+ list(
71-
f"{'Data array: ['+' x '.join(str(i) for i in v.shape) if v.size > 0 else '['}]"
72-
if isinstance(v, np.ndarray)
73-
else "\n".join(element for element in v)
74-
if k == "model"
75-
else str(v)
76-
for k, v in model.__dict__.items()
77-
)
78-
for index, model in enumerate(self.data)
79-
]
80-
)
81-
output = table.get_string()
82-
else:
83-
output = str(self.data)
94+
output = str(self.data)
8495
return output
8596

8697
def __getitem__(self, index: Union[int, slice, str, T]) -> T:

RATapi/models.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,15 @@ def __repr__(self):
4646

4747
def __str__(self):
4848
table = prettytable.PrettyTable()
49-
table.field_names = [key.replace("_", " ") for key in self.__dict__]
50-
table.add_row(list(self.__dict__.values()))
49+
table.field_names = [key.replace("_", " ") for key in self.display_fields]
50+
table.add_row(list(self.display_fields.values()))
5151
return table.get_string()
5252

53+
@property
54+
def display_fields(self) -> dict:
55+
"""A dictionary of which fields should be displayed by this model and their values."""
56+
return self.__dict__
57+
5358

5459
class Background(RATModel):
5560
"""Defines the Backgrounds in RAT."""
@@ -338,6 +343,8 @@ class Parameter(RATModel):
338343
mu: float = 0.0
339344
sigma: float = np.inf
340345

346+
show_priors: bool = False
347+
341348
@model_validator(mode="after")
342349
def check_min_max(self) -> "Parameter":
343350
"""The maximum value of a parameter must be greater than the minimum."""
@@ -352,6 +359,16 @@ def check_value_in_range(self) -> "Parameter":
352359
raise ValueError(f"value {self.value} is not within the defined range: {self.min} <= value <= {self.max}")
353360
return self
354361

362+
@property
363+
def display_fields(self) -> dict:
364+
visible_fields = ["name", "min", "value", "max", "fit"]
365+
if self.show_priors:
366+
visible_fields.append("prior_type")
367+
if self.prior_type == Priors.Gaussian:
368+
visible_fields.extend(["mu", "sigma"])
369+
370+
return {f: getattr(self, f) for f in visible_fields}
371+
355372

356373
class ProtectedParameter(Parameter):
357374
"""A Parameter with a fixed name."""

tests/test_classlist.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from collections.abc import Iterable, Sequence
88
from typing import Any, Union
99

10+
import prettytable
1011
import pytest
1112

1213
from RATapi.classlist import ClassList
@@ -44,6 +45,23 @@ def three_name_class_list():
4445
return ClassList([InputAttributes(name="Alice"), InputAttributes(name="Bob"), InputAttributes(name="Eve")])
4546

4647

48+
class DisplayFieldsClass:
49+
"""A classlist with four attributes and a display_fields property."""
50+
51+
def __init__(self, display_range):
52+
self.a = 1
53+
self.b = 2
54+
self.c = 3
55+
self.d = 4
56+
57+
self.display_range = display_range
58+
59+
@property
60+
def display_fields(self):
61+
fields = ["a", "b", "c", "d"][self.display_range[0] : self.display_range[1]]
62+
return {f: getattr(self, f) for f in fields}
63+
64+
4765
class TestInitialisation:
4866
@pytest.mark.parametrize(
4967
"input_object",
@@ -174,6 +192,26 @@ def test_str_empty_classlist() -> None:
174192
assert str(ClassList()) == str([])
175193

176194

195+
@pytest.mark.parametrize(
196+
"display_ranges, expected_header",
197+
(
198+
([(1, 3), (1, 3), (1, 3)], ["b", "c"]),
199+
([(1, 2), (0, 4), (2, 3)], ["a", "b", "c", "d"]),
200+
([(0, 2), (0, 1), (2, 3)], ["a", "b", "c"]),
201+
),
202+
)
203+
def test_str_display_fields(display_ranges, expected_header):
204+
"""If a class has the `display_fields` property, the ClassList should print with the minimal required attributes."""
205+
class_list = ClassList([DisplayFieldsClass(dr) for dr in display_ranges])
206+
expected_table = prettytable.PrettyTable()
207+
expected_table.field_names = ["index"] + expected_header
208+
expected_vals = {"a": 1, "b": 2, "c": 3, "d": 4}
209+
row = [expected_vals[v] for v in expected_header]
210+
expected_table.add_rows([[0] + row, [1] + row, [2] + row])
211+
212+
assert str(class_list) == expected_table.get_string()
213+
214+
177215
@pytest.mark.parametrize(
178216
["new_item", "expected_classlist"],
179217
[

tests/test_project.py

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -80,47 +80,47 @@ def default_project_str():
8080
"Geometry: ------------------------------------------------------------------------------------------\n\n"
8181
"air/substrate\n\n"
8282
"Parameters: ----------------------------------------------------------------------------------------\n\n"
83-
"+-------+---------------------+-----+-------+-----+------+------------+-----+-------+\n"
84-
"| index | name | min | value | max | fit | prior type | mu | sigma |\n"
85-
"+-------+---------------------+-----+-------+-----+------+------------+-----+-------+\n"
86-
"| 0 | Substrate Roughness | 1.0 | 3.0 | 5.0 | True | uniform | 0.0 | inf |\n"
87-
"+-------+---------------------+-----+-------+-----+------+------------+-----+-------+\n\n"
83+
"+-------+---------------------+-----+-------+-----+------+\n"
84+
"| index | name | min | value | max | fit |\n"
85+
"+-------+---------------------+-----+-------+-----+------+\n"
86+
"| 0 | Substrate Roughness | 1.0 | 3.0 | 5.0 | True |\n"
87+
"+-------+---------------------+-----+-------+-----+------+\n\n"
8888
"Bulk In: -------------------------------------------------------------------------------------------\n\n"
89-
"+-------+---------+-----+-------+-----+-------+------------+-----+-------+\n"
90-
"| index | name | min | value | max | fit | prior type | mu | sigma |\n"
91-
"+-------+---------+-----+-------+-----+-------+------------+-----+-------+\n"
92-
"| 0 | SLD Air | 0.0 | 0.0 | 0.0 | False | uniform | 0.0 | inf |\n"
93-
"+-------+---------+-----+-------+-----+-------+------------+-----+-------+\n\n"
89+
"+-------+---------+-----+-------+-----+-------+\n"
90+
"| index | name | min | value | max | fit |\n"
91+
"+-------+---------+-----+-------+-----+-------+\n"
92+
"| 0 | SLD Air | 0.0 | 0.0 | 0.0 | False |\n"
93+
"+-------+---------+-----+-------+-----+-------+\n\n"
9494
"Bulk Out: ------------------------------------------------------------------------------------------\n\n"
95-
"+-------+---------+---------+----------+----------+-------+------------+-----+-------+\n"
96-
"| index | name | min | value | max | fit | prior type | mu | sigma |\n"
97-
"+-------+---------+---------+----------+----------+-------+------------+-----+-------+\n"
98-
"| 0 | SLD D2O | 6.2e-06 | 6.35e-06 | 6.35e-06 | False | uniform | 0.0 | inf |\n"
99-
"+-------+---------+---------+----------+----------+-------+------------+-----+-------+\n\n"
95+
"+-------+---------+---------+----------+----------+-------+\n"
96+
"| index | name | min | value | max | fit |\n"
97+
"+-------+---------+---------+----------+----------+-------+\n"
98+
"| 0 | SLD D2O | 6.2e-06 | 6.35e-06 | 6.35e-06 | False |\n"
99+
"+-------+---------+---------+----------+----------+-------+\n\n"
100100
"Scalefactors: --------------------------------------------------------------------------------------\n\n"
101-
"+-------+---------------+------+-------+------+-------+------------+-----+-------+\n"
102-
"| index | name | min | value | max | fit | prior type | mu | sigma |\n"
103-
"+-------+---------------+------+-------+------+-------+------------+-----+-------+\n"
104-
"| 0 | Scalefactor 1 | 0.02 | 0.23 | 0.25 | False | uniform | 0.0 | inf |\n"
105-
"+-------+---------------+------+-------+------+-------+------------+-----+-------+\n\n"
101+
"+-------+---------------+------+-------+------+-------+\n"
102+
"| index | name | min | value | max | fit |\n"
103+
"+-------+---------------+------+-------+------+-------+\n"
104+
"| 0 | Scalefactor 1 | 0.02 | 0.23 | 0.25 | False |\n"
105+
"+-------+---------------+------+-------+------+-------+\n\n"
106106
"Background Parameters: -----------------------------------------------------------------------------\n\n"
107-
"+-------+--------------------+-------+-------+-------+-------+------------+-----+-------+\n"
108-
"| index | name | min | value | max | fit | prior type | mu | sigma |\n"
109-
"+-------+--------------------+-------+-------+-------+-------+------------+-----+-------+\n"
110-
"| 0 | Background Param 1 | 1e-07 | 1e-06 | 1e-05 | False | uniform | 0.0 | inf |\n"
111-
"+-------+--------------------+-------+-------+-------+-------+------------+-----+-------+\n\n"
107+
"+-------+--------------------+-------+-------+-------+-------+\n"
108+
"| index | name | min | value | max | fit |\n"
109+
"+-------+--------------------+-------+-------+-------+-------+\n"
110+
"| 0 | Background Param 1 | 1e-07 | 1e-06 | 1e-05 | False |\n"
111+
"+-------+--------------------+-------+-------+-------+-------+\n\n"
112112
"Backgrounds: ---------------------------------------------------------------------------------------\n\n"
113113
"+-------+--------------+----------+--------------------+---------+---------+---------+---------+\n"
114114
"| index | name | type | value 1 | value 2 | value 3 | value 4 | value 5 |\n"
115115
"+-------+--------------+----------+--------------------+---------+---------+---------+---------+\n"
116116
"| 0 | Background 1 | constant | Background Param 1 | | | | |\n"
117117
"+-------+--------------+----------+--------------------+---------+---------+---------+---------+\n\n"
118118
"Resolution Parameters: -----------------------------------------------------------------------------\n\n"
119-
"+-------+--------------------+------+-------+------+-------+------------+-----+-------+\n"
120-
"| index | name | min | value | max | fit | prior type | mu | sigma |\n"
121-
"+-------+--------------------+------+-------+------+-------+------------+-----+-------+\n"
122-
"| 0 | Resolution Param 1 | 0.01 | 0.03 | 0.05 | False | uniform | 0.0 | inf |\n"
123-
"+-------+--------------------+------+-------+------+-------+------------+-----+-------+\n\n"
119+
"+-------+--------------------+------+-------+------+-------+\n"
120+
"| index | name | min | value | max | fit |\n"
121+
"+-------+--------------------+------+-------+------+-------+\n"
122+
"| 0 | Resolution Param 1 | 0.01 | 0.03 | 0.05 | False |\n"
123+
"+-------+--------------------+------+-------+------+-------+\n\n"
124124
"Resolutions: ---------------------------------------------------------------------------------------\n\n"
125125
"+-------+--------------+----------+--------------------+---------+---------+---------+---------+\n"
126126
"| index | name | type | value 1 | value 2 | value 3 | value 4 | value 5 |\n"

0 commit comments

Comments
 (0)