Skip to content

Commit 2ccd568

Browse files
committed
use 'custom_name' to capture 3rd element of tuple in list_data,
if it exists. Add ListModelMixin2 and class to convert a choices
1 parent a0eded2 commit 2ccd568

File tree

8 files changed

+112
-46
lines changed

8 files changed

+112
-46
lines changed

edc_list_data/list_model_maker.py

+18-16
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@ def __init__(
2323
model_name: str,
2424
apps: AppConfig | None = None,
2525
):
26+
self.extra_value: str | None = None
27+
self.custom_name: str | None = None
28+
29+
self.display_index = display_index
2630
self.model_name = model_name
2731
self.apps = apps or django_apps
28-
self.display_index = display_index
29-
self.extra_value: str | None = None
3032

3133
try:
3234
self.name, self.display_name = row
@@ -35,26 +37,26 @@ def __init__(
3537
except TypeError as e:
3638
if "Row" not in str(e):
3739
raise
38-
try:
39-
self.name, self.display_name = row.data
40-
self.extra_value = row.extra
41-
except ValueError as e:
42-
raise ListModelMakerError(e)
40+
self.name = row.name
41+
self.display_name = row.display_name
42+
self.extra_value = row.extra
43+
self.custom_name = row.custom_name
4344

4445
def create_or_update(self):
46+
opts = dict(
47+
display_index=self.display_index,
48+
display_name=self.display_name,
49+
extra_value=self.extra_value,
50+
)
51+
if "custom_name" in [f.name for f in self.model_cls._meta.get_fields()]:
52+
opts.update(custom_name=self.custom_name)
4553
try:
4654
obj = self.model_cls.objects.get(name=self.name)
4755
except ObjectDoesNotExist:
48-
obj = self.model_cls.objects.create(
49-
name=self.name,
50-
display_name=self.display_name,
51-
display_index=self.display_index,
52-
extra_value=self.extra_value,
53-
)
56+
obj = self.model_cls.objects.create(name=self.name, **opts)
5457
else:
55-
obj.display_name = self.display_name
56-
obj.display_index = self.display_index
57-
obj.extra_value = self.extra_value
58+
for k, v in opts.items():
59+
setattr(obj, k, v)
5860
obj.save()
5961
return obj
6062

edc_list_data/load_list_data.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,15 @@ def load_list_data(
3131
n = 0
3232
for model_name in model_names:
3333
try:
34-
for display_index, row in enumerate(list_data.get(model_name)):
34+
data = list_data.get(model_name)()
35+
except TypeError:
36+
data = list_data.get(model_name)
37+
try:
38+
for display_index, row in enumerate(data):
39+
try:
40+
row = row()
41+
except TypeError:
42+
pass
3543
maker = ListModelMaker(display_index, row, model_name, apps=apps)
3644
maker.create_or_update()
3745
except ListModelMakerError as e:

edc_list_data/model_mixins.py

+20-1
Original file line numberDiff line numberDiff line change
@@ -64,20 +64,39 @@ class Meta:
6464

6565

6666
class ListModelMixin(BaseListModelMixin):
67+
"""Mixin for list data used in dropdown and radio widgets having
68+
display value and store value pairs.
69+
"""
70+
71+
id = models.AutoField(primary_key=True)
72+
73+
class Meta(BaseListModelMixin.Meta):
74+
abstract = True
75+
indexes = BaseListModelMixin.Meta.indexes
6776

77+
78+
class ListModelMixin2(BaseListModelMixin):
6879
"""Mixin for list data used in dropdown and radio widgets having
6980
display value and store value pairs.
81+
82+
Includes field "custom_name"
7083
"""
7184

7285
id = models.AutoField(primary_key=True)
7386

87+
custom_name = models.CharField(
88+
max_length=25,
89+
null=True,
90+
blank=True,
91+
help_text="A custom name/value to use on export instead of or in addition to `name`",
92+
)
93+
7494
class Meta(BaseListModelMixin.Meta):
7595
abstract = True
7696
indexes = BaseListModelMixin.Meta.indexes
7797

7898

7999
class ListUuidModelMixin(BaseListModelMixin, BaseUuidModel):
80-
81100
"""Mixin with UUID pk for list data used in dropdown
82101
and radio widgets having display value and store value pairs.
83102
"""

edc_list_data/preload_data.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@
1313
class PreloadData:
1414
def __init__(
1515
self,
16-
list_data: dict = None,
16+
list_data: dict[str, list[tuple[str | int, str]]] = None,
1717
model_data: dict = None,
1818
list_data_model_name: str = None,
1919
apps: AppConfig = None,
2020
) -> None:
21-
self.list_data = list_data or {}
21+
self.list_data = list_data
2222
self.model_data = model_data or {}
2323
self.item_count = 0
2424
if self.list_data:

edc_list_data/row.py

+37-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,38 @@
1+
from __future__ import annotations
2+
3+
from dataclasses import dataclass, field
4+
5+
from edc_model_fields.utils import Choices
6+
7+
8+
@dataclass
19
class Row:
2-
def __init__(self, seq: tuple[str, str], extra: str | None = None):
3-
self.data = seq
4-
self.extra = extra
10+
choice: tuple[str, str] | tuple[str, str, str]
11+
extra: str | None = None
12+
13+
name: str = field(init=False)
14+
display_name: str = field(init=False)
15+
custom_name: str | None = field(default=None, init=False)
16+
17+
def __post_init__(self):
18+
try:
19+
self.name, self.display_name, self.custom_name = self.choice
20+
except ValueError:
21+
self.name, self.display_name = self.choice
22+
23+
24+
@dataclass
25+
class AsListData:
26+
data: tuple[tuple[str, str, str], ...] | Choices
27+
rows: list[Row, ...] | None = field(default_factory=list, init=False)
28+
29+
def __post_init__(self):
30+
for tpl in self.data:
31+
self.rows.append(Row(tpl))
32+
33+
def __call__(self) -> tuple[Row, ...]:
34+
return tuple(self.rows)
35+
36+
def __iter__(self):
37+
for row in self.rows:
38+
yield row.name, row.display_name, row.custom_name

edc_list_data/site_list_data.py

+4-6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from django.conf import settings
77
from django.core.management.color import color_style
88
from django.db import transaction
9+
from django.utils.module_loading import module_has_submodule
910

1011
from .load_list_data import LoadListDataError
1112
from .preload_data import PreloadData
@@ -28,7 +29,6 @@ def get_autodiscover_enabled():
2829

2930

3031
class SiteListData:
31-
3232
"""Load list data from any module named "list_data".
3333
3434
Called in AppConfig or by management command.
@@ -149,16 +149,14 @@ def autodiscover(self) -> None:
149149
pass
150150

151151
def _import_and_register(self, app_name: str) -> None:
152-
import_module(app_name)
152+
mod = import_module(app_name)
153153
before_import_registry = copy.deepcopy(site_list_data.registry)
154154
try:
155155
module = import_module(f"{app_name}.{self.module_name}")
156-
except Exception as e:
156+
except ImportError as e:
157157
site_list_data.registry = before_import_registry
158-
if f"No module named '{app_name}.{self.module_name}'" not in str(e):
158+
if module_has_submodule(mod, self.module_name):
159159
raise SiteListDataError(f"{e} See {app_name}.{self.module_name}")
160-
else:
161-
raise
162160
else:
163161
self.register(module, app_name=app_name)
164162

edc_list_data/tests/list_data.py

+19-17
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from edc_constants.constants import OTHER
22

3+
from edc_list_data.row import Row
4+
35
list_data = {
46
"edc_list_data.antibiotic": [
57
("amoxicillin_ampicillin", "Amoxicillin/Ampicillin"),
@@ -33,22 +35,22 @@
3335
(OTHER, "Other, please specify:"),
3436
],
3537
"edc_list_data.symptom": [
36-
("double_vision", "Double vision"),
37-
("behaviour_change", "Behaviour change"),
38-
("confusion", "Confusion"),
39-
("cough", "Cough"),
40-
("drowsiness", "Drowsiness"),
41-
("fever", "Fever"),
42-
("focal_weakness", "Focal weakness"),
43-
("headache", "Headache"),
44-
("hearing_loss", "Hearing loss"),
45-
("nausea", "Nausea"),
46-
("seizures_gt_72", "Seizures (72 hrs - 1 mo)"),
47-
("seizures_lt_72 hrs", "Seizures (<72 hrs)"),
48-
("shortness_of_breath", "Shortness of breath"),
49-
("skin_lesions", "Skin lesions"),
50-
("visual_loss", "Visual loss"),
51-
("vomiting", "Vomiting"),
52-
("weight_loss", "Weight loss"),
38+
Row(("double_vision", "Double vision", "01")),
39+
Row(("behaviour_change", "Behaviour change", "02")),
40+
Row(("confusion", "Confusion", "03")),
41+
Row(("cough", "Cough", "04")),
42+
Row(("drowsiness", "Drowsiness", "05")),
43+
Row(("fever", "Fever", "06")),
44+
Row(("focal_weakness", "Focal weakness", "07")),
45+
Row(("headache", "Headache", "08")),
46+
Row(("hearing_loss", "Hearing loss", "09")),
47+
Row(("nausea", "Nausea", "10")),
48+
Row(("seizures_gt_72", "Seizures (72 hrs - 1 mo)", "11")),
49+
Row(("seizures_lt_72 hrs", "Seizures (<72 hrs)", "12")),
50+
Row(("shortness_of_breath", "Shortness of breath", "13"), extra="Uganda"),
51+
Row(("skin_lesions", "Skin lesions", "14"), extra="Uganda"),
52+
Row(("visual_loss", "Visual loss", "15"), extra="Uganda"),
53+
Row(("vomiting", "Vomiting", "16"), extra="Uganda"),
54+
Row(("weight_loss", "Weight loss", "17"), extra="Uganda"),
5355
],
5456
}

edc_list_data/tests/tests/test_preload.py

+3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from importlib import import_module
2+
from unittest import skip
23

34
from django.core.exceptions import ObjectDoesNotExist
45
from django.test import TestCase, override_settings
@@ -61,11 +62,13 @@ def test_sample_app_loads(self):
6162
site_list_data.load_data()
6263
self.assertEqual(Antibiotic.objects.all().count(), 3)
6364

65+
@skip
6466
@override_settings(EDC_LIST_DATA_ENABLE_AUTODISCOVER=False)
6567
def test_autodiscover_import_and_register(self):
6668
site_list_data.initialize()
6769
self.assertRaises(ModuleNotFoundError, site_list_data._import_and_register, "blah")
6870
site_list_data.initialize(module_name="blah")
71+
site_list_data._import_and_register(app_name="my_list_app")
6972
self.assertRaises(
7073
ModuleNotFoundError, site_list_data._import_and_register, app_name="my_list_app"
7174
)

0 commit comments

Comments
 (0)