GeneralManager 0.5.0__tar.gz → 0.5.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {generalmanager-0.5.0 → generalmanager-0.5.1}/GeneralManager.egg-info/PKG-INFO +1 -1
- {generalmanager-0.5.0 → generalmanager-0.5.1}/GeneralManager.egg-info/SOURCES.txt +1 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/PKG-INFO +1 -1
- {generalmanager-0.5.0 → generalmanager-0.5.1}/pyproject.toml +1 -1
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/interface/baseInterface.py +6 -6
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/manager/groupManager.py +34 -22
- generalmanager-0.5.1/tests/test_groupManager.py +319 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/GeneralManager.egg-info/dependency_links.txt +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/GeneralManager.egg-info/requires.txt +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/GeneralManager.egg-info/top_level.txt +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/LICENSE +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/README.md +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/setup.cfg +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/__init__.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/api/graphql.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/api/mutation.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/api/property.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/apps.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/auxiliary/__init__.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/auxiliary/argsToKwargs.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/auxiliary/filterParser.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/auxiliary/jsonEncoder.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/auxiliary/makeCacheKey.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/auxiliary/noneToZero.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/auxiliary/pathMapping.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/cache/cacheDecorator.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/cache/cacheTracker.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/cache/dependencyIndex.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/cache/modelDependencyCollector.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/cache/signals.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/factory/__init__.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/factory/autoFactory.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/factory/factories.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/factory/factoryMethods.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/interface/__init__.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/interface/calculationInterface.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/interface/databaseInterface.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/manager/__init__.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/manager/generalManager.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/manager/input.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/manager/meta.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/measurement/__init__.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/measurement/measurement.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/measurement/measurementField.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/permission/__init__.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/permission/basePermission.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/permission/fileBasedPermission.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/permission/managerBasedPermission.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/permission/permissionChecks.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/permission/permissionDataManager.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/rule/__init__.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/rule/handler.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/rule/rule.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/tests/test_argsToKwargs.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/tests/test_autoFactory.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/tests/test_basePermission.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/tests/test_cacheDecorator.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/tests/test_cacheTracker.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/tests/test_dependencyIndex.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/tests/test_factories.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/tests/test_factoryMethods.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/tests/test_filterParser.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/tests/test_generalManager.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/tests/test_generalManagerMeta.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/tests/test_graph_ql.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/tests/test_input.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/tests/test_jsonEncoder.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/tests/test_makeCacheKey.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/tests/test_managerBasedPermission.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/tests/test_measurement.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/tests/test_measurement_field.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/tests/test_modelDependencyCollector.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/tests/test_noneToZero.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/tests/test_rule_handler.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/tests/test_rules.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/tests/test_settings.py +0 -0
- {generalmanager-0.5.0 → generalmanager-0.5.1}/tests/test_signals.py +0 -0
{generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/interface/baseInterface.py
RENAMED
@@ -21,7 +21,7 @@ if TYPE_CHECKING:
|
|
21
21
|
from general_manager.manager.input import Input
|
22
22
|
from general_manager.manager.generalManager import GeneralManager
|
23
23
|
from general_manager.manager.meta import GeneralManagerMeta
|
24
|
-
from general_manager.manager.groupManager import
|
24
|
+
from general_manager.manager.groupManager import GroupManager, GroupBucket
|
25
25
|
|
26
26
|
|
27
27
|
GeneralManagerType = TypeVar("GeneralManagerType", bound="GeneralManager")
|
@@ -239,7 +239,7 @@ class Bucket(ABC, Generic[GeneralManagerType]):
|
|
239
239
|
@abstractmethod
|
240
240
|
def __iter__(
|
241
241
|
self,
|
242
|
-
) -> Generator[GeneralManagerType |
|
242
|
+
) -> Generator[GeneralManagerType | GroupManager[GeneralManagerType]]:
|
243
243
|
raise NotImplementedError
|
244
244
|
|
245
245
|
@abstractmethod
|
@@ -251,11 +251,11 @@ class Bucket(ABC, Generic[GeneralManagerType]):
|
|
251
251
|
raise NotImplementedError
|
252
252
|
|
253
253
|
@abstractmethod
|
254
|
-
def first(self) -> GeneralManagerType |
|
254
|
+
def first(self) -> GeneralManagerType | GroupManager[GeneralManagerType] | None:
|
255
255
|
raise NotImplementedError
|
256
256
|
|
257
257
|
@abstractmethod
|
258
|
-
def last(self) -> GeneralManagerType |
|
258
|
+
def last(self) -> GeneralManagerType | GroupManager[GeneralManagerType] | None:
|
259
259
|
raise NotImplementedError
|
260
260
|
|
261
261
|
@abstractmethod
|
@@ -269,7 +269,7 @@ class Bucket(ABC, Generic[GeneralManagerType]):
|
|
269
269
|
@abstractmethod
|
270
270
|
def get(
|
271
271
|
self, **kwargs: Any
|
272
|
-
) -> GeneralManagerType |
|
272
|
+
) -> GeneralManagerType | GroupManager[GeneralManagerType]:
|
273
273
|
raise NotImplementedError
|
274
274
|
|
275
275
|
@abstractmethod
|
@@ -277,7 +277,7 @@ class Bucket(ABC, Generic[GeneralManagerType]):
|
|
277
277
|
self, item: int | slice
|
278
278
|
) -> (
|
279
279
|
GeneralManagerType
|
280
|
-
|
|
280
|
+
| GroupManager[GeneralManagerType]
|
281
281
|
| Bucket[GeneralManagerType]
|
282
282
|
):
|
283
283
|
raise NotImplementedError
|
@@ -32,6 +32,15 @@ class GroupBucket(Bucket[GeneralManagerType]):
|
|
32
32
|
self._data = self.__buildGroupedManager(data)
|
33
33
|
self._basis_data = data
|
34
34
|
|
35
|
+
def __eq__(self, other: object) -> bool:
|
36
|
+
if not isinstance(other, self.__class__):
|
37
|
+
return False
|
38
|
+
return (
|
39
|
+
self._data == other._data
|
40
|
+
and self._manager_class == other._manager_class
|
41
|
+
and self._group_by_keys == other._group_by_keys
|
42
|
+
)
|
43
|
+
|
35
44
|
def __checkGroupByArguments(self, group_by_keys: tuple[str, ...]) -> None:
|
36
45
|
"""
|
37
46
|
This method checks if the given arguments are valid for the groupBy method.
|
@@ -50,7 +59,7 @@ class GroupBucket(Bucket[GeneralManagerType]):
|
|
50
59
|
def __buildGroupedManager(
|
51
60
|
self,
|
52
61
|
data: Bucket[GeneralManagerType],
|
53
|
-
) -> list[
|
62
|
+
) -> list[GroupManager[GeneralManagerType]]:
|
54
63
|
"""
|
55
64
|
This method builds the grouped manager.
|
56
65
|
It returns a GroupBucket with the grouped data.
|
@@ -63,11 +72,11 @@ class GroupBucket(Bucket[GeneralManagerType]):
|
|
63
72
|
group_by_values.add(json.dumps(group_by_value))
|
64
73
|
|
65
74
|
groups = []
|
66
|
-
for group_by_value in group_by_values:
|
75
|
+
for group_by_value in sorted(group_by_values):
|
67
76
|
group_by_value = json.loads(group_by_value)
|
68
77
|
grouped_manager_objects = data.filter(**group_by_value)
|
69
78
|
groups.append(
|
70
|
-
|
79
|
+
GroupManager(
|
71
80
|
self._manager_class, group_by_value, grouped_manager_objects
|
72
81
|
)
|
73
82
|
)
|
@@ -84,7 +93,7 @@ class GroupBucket(Bucket[GeneralManagerType]):
|
|
84
93
|
self._basis_data | other._basis_data,
|
85
94
|
)
|
86
95
|
|
87
|
-
def __iter__(self) -> Generator[
|
96
|
+
def __iter__(self) -> Generator[GroupManager[GeneralManagerType]]:
|
88
97
|
for grouped_manager in self._data:
|
89
98
|
yield grouped_manager
|
90
99
|
|
@@ -104,13 +113,13 @@ class GroupBucket(Bucket[GeneralManagerType]):
|
|
104
113
|
new_basis_data,
|
105
114
|
)
|
106
115
|
|
107
|
-
def first(self) ->
|
116
|
+
def first(self) -> GroupManager[GeneralManagerType] | None:
|
108
117
|
try:
|
109
118
|
return next(iter(self))
|
110
119
|
except StopIteration:
|
111
120
|
return None
|
112
121
|
|
113
|
-
def last(self) ->
|
122
|
+
def last(self) -> GroupManager[GeneralManagerType] | None:
|
114
123
|
items = list(self)
|
115
124
|
if items:
|
116
125
|
return items[-1]
|
@@ -122,7 +131,7 @@ class GroupBucket(Bucket[GeneralManagerType]):
|
|
122
131
|
def all(self) -> Bucket[GeneralManagerType]:
|
123
132
|
return self
|
124
133
|
|
125
|
-
def get(self, **kwargs: Any) ->
|
134
|
+
def get(self, **kwargs: Any) -> GroupManager[GeneralManagerType]:
|
126
135
|
first_value = self.filter(**kwargs).first()
|
127
136
|
if first_value is None:
|
128
137
|
raise ValueError(
|
@@ -132,7 +141,7 @@ class GroupBucket(Bucket[GeneralManagerType]):
|
|
132
141
|
|
133
142
|
def __getitem__(
|
134
143
|
self, item: int | slice
|
135
|
-
) ->
|
144
|
+
) -> GroupManager[GeneralManagerType] | GroupBucket[GeneralManagerType]:
|
136
145
|
if isinstance(item, int):
|
137
146
|
return self._data[item]
|
138
147
|
elif isinstance(item, slice):
|
@@ -162,12 +171,14 @@ class GroupBucket(Bucket[GeneralManagerType]):
|
|
162
171
|
if isinstance(key, str):
|
163
172
|
key = (key,)
|
164
173
|
if reverse:
|
165
|
-
sorted_data =
|
166
|
-
|
174
|
+
sorted_data = sorted(
|
175
|
+
self._data,
|
176
|
+
key=lambda x: tuple(getattr(x, k) for k in key),
|
177
|
+
reverse=True,
|
167
178
|
)
|
168
179
|
else:
|
169
|
-
sorted_data =
|
170
|
-
key=lambda x: tuple(
|
180
|
+
sorted_data = sorted(
|
181
|
+
self._data, key=lambda x: tuple(getattr(x, k) for k in key)
|
171
182
|
)
|
172
183
|
|
173
184
|
new_bucket = GroupBucket(
|
@@ -182,11 +193,13 @@ class GroupBucket(Bucket[GeneralManagerType]):
|
|
182
193
|
It returns a GroupBucket with the grouped data.
|
183
194
|
"""
|
184
195
|
return GroupBucket(
|
185
|
-
self._manager_class,
|
196
|
+
self._manager_class,
|
197
|
+
tuple([*self._group_by_keys, *group_by_keys]),
|
198
|
+
self._basis_data,
|
186
199
|
)
|
187
200
|
|
188
201
|
|
189
|
-
class
|
202
|
+
class GroupManager(Generic[GeneralManagerType]):
|
190
203
|
"""
|
191
204
|
This class is used to group the data of a GeneralManager.
|
192
205
|
It is used to create a new GeneralManager with the grouped data.
|
@@ -212,11 +225,6 @@ class GroupedManager(Generic[GeneralManagerType]):
|
|
212
225
|
and self._group_by_value == other._group_by_value
|
213
226
|
)
|
214
227
|
|
215
|
-
def __hash__(self) -> int:
|
216
|
-
return hash(
|
217
|
-
(self._manager_class, frozenset(self._group_by_value.items()), self._data)
|
218
|
-
)
|
219
|
-
|
220
228
|
def __repr__(self) -> str:
|
221
229
|
return f"{self.__class__.__name__}({self._manager_class}, {self._group_by_value}, {self._data})"
|
222
230
|
|
@@ -277,12 +285,16 @@ class GroupedManager(Generic[GeneralManagerType]):
|
|
277
285
|
for entry in total_data:
|
278
286
|
new_data.update(entry)
|
279
287
|
elif issubclass(data_type, str):
|
280
|
-
|
288
|
+
temp_data = []
|
289
|
+
for entry in total_data:
|
290
|
+
if entry not in temp_data:
|
291
|
+
temp_data.append(str(entry))
|
292
|
+
new_data = ", ".join(temp_data)
|
293
|
+
elif issubclass(data_type, bool):
|
294
|
+
new_data = any(total_data)
|
281
295
|
elif issubclass(data_type, (int, float, Measurement)):
|
282
296
|
new_data = sum(total_data)
|
283
297
|
elif issubclass(data_type, (datetime, date, time)):
|
284
298
|
new_data = max(total_data)
|
285
|
-
elif issubclass(data_type, bool):
|
286
|
-
new_data = any(total_data)
|
287
299
|
|
288
300
|
return new_data
|
@@ -0,0 +1,319 @@
|
|
1
|
+
# type: ignore
|
2
|
+
from datetime import date
|
3
|
+
from django.test import TestCase
|
4
|
+
from general_manager.api.graphql import GraphQLProperty
|
5
|
+
from general_manager.manager.groupManager import (
|
6
|
+
GroupBucket,
|
7
|
+
GroupManager,
|
8
|
+
)
|
9
|
+
from general_manager.measurement import Measurement
|
10
|
+
|
11
|
+
|
12
|
+
# Stub Interface to simulate attribute definitions
|
13
|
+
class DummyInterface:
|
14
|
+
attr_types = {
|
15
|
+
"a": {"type": int},
|
16
|
+
"b": {"type": str},
|
17
|
+
"c": {"type": list},
|
18
|
+
"date": {"type": date},
|
19
|
+
"flag": {"type": bool},
|
20
|
+
"items": {"type": dict},
|
21
|
+
}
|
22
|
+
|
23
|
+
@staticmethod
|
24
|
+
def getAttributes():
|
25
|
+
return {attr: {} for attr in DummyInterface.attr_types}
|
26
|
+
|
27
|
+
@staticmethod
|
28
|
+
def getAttributeTypes():
|
29
|
+
return DummyInterface.attr_types
|
30
|
+
|
31
|
+
|
32
|
+
# Stub Manager to use with GroupBucket
|
33
|
+
class DummyManager:
|
34
|
+
Interface = DummyInterface
|
35
|
+
|
36
|
+
def __init__(self, **attrs):
|
37
|
+
for name, value in attrs.items():
|
38
|
+
setattr(self, name, value)
|
39
|
+
|
40
|
+
@GraphQLProperty
|
41
|
+
def extraMethod(self) -> str:
|
42
|
+
return "extra method result"
|
43
|
+
|
44
|
+
|
45
|
+
# Simple list-based Bucket stub
|
46
|
+
class ListBucket(list):
|
47
|
+
def __init__(self, items):
|
48
|
+
super().__init__(items)
|
49
|
+
|
50
|
+
def filter(self, **kwargs):
|
51
|
+
# Return items matching all kwargs
|
52
|
+
return ListBucket(
|
53
|
+
[
|
54
|
+
item
|
55
|
+
for item in self
|
56
|
+
if all(getattr(item, k) == v for k, v in kwargs.items())
|
57
|
+
]
|
58
|
+
)
|
59
|
+
|
60
|
+
def exclude(self, **kwargs):
|
61
|
+
# Return items not matching any kwargs
|
62
|
+
return ListBucket(
|
63
|
+
[
|
64
|
+
item
|
65
|
+
for item in self
|
66
|
+
if not all(getattr(item, k) == v for k, v in kwargs.items())
|
67
|
+
]
|
68
|
+
)
|
69
|
+
|
70
|
+
def sort(self, key, **kwargs):
|
71
|
+
# Sort using given key function
|
72
|
+
return ListBucket(sorted(self, key=key))
|
73
|
+
|
74
|
+
def __or__(self, other):
|
75
|
+
# Combine two buckets
|
76
|
+
return ListBucket(list(self) + list(other))
|
77
|
+
|
78
|
+
|
79
|
+
class GroupBucketTests(TestCase):
|
80
|
+
# Test that non-string group_by arguments raise TypeError
|
81
|
+
def test_invalid_group_by_type_raises(self):
|
82
|
+
with self.assertRaises(TypeError):
|
83
|
+
GroupBucket(DummyManager, (123,), ListBucket([]))
|
84
|
+
|
85
|
+
# Test that invalid attribute names raise TypeError
|
86
|
+
def test_invalid_group_by_key_raises(self):
|
87
|
+
with self.assertRaises(TypeError):
|
88
|
+
GroupBucket(DummyManager, ("nonexistent",), ListBucket([]))
|
89
|
+
|
90
|
+
# Test grouping logic produces correct number of groups and keys
|
91
|
+
def test_build_grouped_manager(self):
|
92
|
+
items = [
|
93
|
+
DummyManager(
|
94
|
+
a=1, b="x", c=[1], date=date(2020, 1, 1), flag=True, items={"k": 1}
|
95
|
+
),
|
96
|
+
DummyManager(
|
97
|
+
a=1, b="x", c=[2], date=date(2021, 1, 1), flag=False, items={"k2": 2}
|
98
|
+
),
|
99
|
+
DummyManager(
|
100
|
+
a=2, b="y", c=[3], date=date(2019, 1, 1), flag=True, items={"k3": 3}
|
101
|
+
),
|
102
|
+
]
|
103
|
+
bucket = GroupBucket(DummyManager, ("a", "b"), ListBucket(items))
|
104
|
+
# There should be two groups: (1, 'x') and (2, 'y')
|
105
|
+
self.assertEqual(bucket.count(), 2)
|
106
|
+
keys = {(group.a, group.b) for group in bucket}
|
107
|
+
self.assertSetEqual(keys, {(1, "x"), (2, "y")})
|
108
|
+
|
109
|
+
# Test that __or__ combines two buckets correctly
|
110
|
+
def test_or_combines_buckets(self):
|
111
|
+
b1 = GroupBucket(DummyManager, ("a",), ListBucket([DummyManager(a=1)]))
|
112
|
+
b2 = GroupBucket(DummyManager, ("a",), ListBucket([DummyManager(a=2)]))
|
113
|
+
combined = b1 | b2
|
114
|
+
self.assertEqual(combined.count(), 2)
|
115
|
+
|
116
|
+
# Test that filter and exclude delegate to underlying bucket
|
117
|
+
def test_filter_and_exclude_delegate(self):
|
118
|
+
items = [DummyManager(a=1), DummyManager(a=2)]
|
119
|
+
gb = GroupBucket(DummyManager, ("a",), ListBucket(items))
|
120
|
+
filtered = gb.filter(a=1)
|
121
|
+
self.assertTrue(all(group.a == 1 for group in filtered))
|
122
|
+
|
123
|
+
excluded = gb.exclude(a=1)
|
124
|
+
self.assertTrue(all(group.a != 1 for group in excluded))
|
125
|
+
|
126
|
+
# Test indexing and slicing behavior
|
127
|
+
def test_getitem_and_slice(self):
|
128
|
+
items = [DummyManager(a=i) for i in (1, 1, 2, 2)]
|
129
|
+
gb = GroupBucket(DummyManager, ("a",), ListBucket(items))
|
130
|
+
# Single index returns a GroupManager
|
131
|
+
gm0 = gb[0]
|
132
|
+
self.assertIsInstance(gm0, GroupManager)
|
133
|
+
# Slice returns a GroupBucket with combined base data
|
134
|
+
slice_gb = gb[0:1]
|
135
|
+
self.assertIsInstance(slice_gb, GroupBucket)
|
136
|
+
self.assertEqual(slice_gb.count(), 1)
|
137
|
+
|
138
|
+
# Test get() method for present and missing values
|
139
|
+
def test_get_returns_and_raises(self):
|
140
|
+
items = [DummyManager(a=1), DummyManager(a=2)]
|
141
|
+
gb = GroupBucket(DummyManager, ("a",), ListBucket(items))
|
142
|
+
# Getting existing group
|
143
|
+
result = gb.get(a=2)
|
144
|
+
self.assertEqual(result.a, 2)
|
145
|
+
# Getting non-existing raises ValueError
|
146
|
+
with self.assertRaises(ValueError):
|
147
|
+
gb.get(a=3)
|
148
|
+
|
149
|
+
def test_last(self):
|
150
|
+
# Test that last() returns the last item based on the grouping key
|
151
|
+
items = [
|
152
|
+
DummyManager(a=1),
|
153
|
+
DummyManager(a=2),
|
154
|
+
DummyManager(a=5),
|
155
|
+
DummyManager(a=4),
|
156
|
+
]
|
157
|
+
gb = GroupBucket(DummyManager, ("a",), ListBucket(items))
|
158
|
+
result = gb.last()
|
159
|
+
self.assertEqual(result.a, 5)
|
160
|
+
|
161
|
+
def test_group_manager_data_order(self):
|
162
|
+
items = [
|
163
|
+
DummyManager(a=1),
|
164
|
+
DummyManager(a=2),
|
165
|
+
DummyManager(a=3),
|
166
|
+
DummyManager(a=4),
|
167
|
+
]
|
168
|
+
gb1 = GroupBucket(DummyManager, ("a",), ListBucket(items))
|
169
|
+
gb2 = GroupBucket(DummyManager, ("a",), ListBucket(items))
|
170
|
+
|
171
|
+
self.assertEqual(gb1, gb2)
|
172
|
+
for i in range(4):
|
173
|
+
self.assertEqual(gb1[i].a, gb2[i].a)
|
174
|
+
|
175
|
+
def test_group_manager_data_with_sorting(self):
|
176
|
+
# Test sorting within a GroupBucket
|
177
|
+
items = [
|
178
|
+
DummyManager(a=1, b="d"),
|
179
|
+
DummyManager(a=2, b="b"),
|
180
|
+
DummyManager(a=3, b="c"),
|
181
|
+
DummyManager(a=4, b="a"),
|
182
|
+
]
|
183
|
+
gb = GroupBucket(DummyManager, ("a",), ListBucket(items))
|
184
|
+
sorted_gm = gb.sort("b")
|
185
|
+
self.assertEqual(
|
186
|
+
[gm.b for gm in sorted_gm],
|
187
|
+
["a", "b", "c", "d"],
|
188
|
+
)
|
189
|
+
reverse_sorted_gm = gb.sort("b", reverse=True)
|
190
|
+
self.assertEqual(
|
191
|
+
[gm.b for gm in reverse_sorted_gm],
|
192
|
+
["d", "c", "b", "a"],
|
193
|
+
)
|
194
|
+
|
195
|
+
def test_group_manager_all(self):
|
196
|
+
# Test that all() returns a GroupManager with all items
|
197
|
+
items = [DummyManager(a=i) for i in range(5)]
|
198
|
+
gb = GroupBucket(DummyManager, ("a",), ListBucket(items))
|
199
|
+
all_gm = gb.all()
|
200
|
+
self.assertIsInstance(all_gm, GroupBucket)
|
201
|
+
self.assertEqual(len(all_gm), 5)
|
202
|
+
self.assertEqual(all_gm[0].a, 0)
|
203
|
+
self.assertEqual(all_gm[4].a, 4)
|
204
|
+
|
205
|
+
def test_group_manager_count(self):
|
206
|
+
# Test that count() returns the correct number of groups
|
207
|
+
items = [DummyManager(a=i) for i in range(5)]
|
208
|
+
gb = GroupBucket(DummyManager, ("a",), ListBucket(items))
|
209
|
+
self.assertEqual(gb.count(), 5)
|
210
|
+
|
211
|
+
def test_group_manager_contains(self):
|
212
|
+
# Test that __contains__ checks for group existence
|
213
|
+
items = [DummyManager(a=i) for i in range(5)]
|
214
|
+
gb = GroupBucket(DummyManager, ("a",), ListBucket(items))
|
215
|
+
self.assertTrue(items[0] in gb)
|
216
|
+
self.assertFalse(DummyManager(a=6) in gb)
|
217
|
+
|
218
|
+
def test_double_grouping(self):
|
219
|
+
# Test grouping by multiple attributes
|
220
|
+
items = [
|
221
|
+
DummyManager(a=1, b="x"),
|
222
|
+
DummyManager(a=1, b="y"),
|
223
|
+
DummyManager(a=2, b="x"),
|
224
|
+
]
|
225
|
+
gb = GroupBucket(DummyManager, ("a", "b"), ListBucket(items))
|
226
|
+
self.assertEqual(gb.count(), 3)
|
227
|
+
keys = {(group.a, group.b) for group in gb}
|
228
|
+
self.assertSetEqual(keys, {(1, "x"), (1, "y"), (2, "x")})
|
229
|
+
|
230
|
+
def test_serial_grouping(self):
|
231
|
+
# Test that serializing and deserializing works correctly
|
232
|
+
items = [
|
233
|
+
DummyManager(a=1, b="x", c=[1]),
|
234
|
+
DummyManager(a=1, b="y", c=[2]),
|
235
|
+
DummyManager(a=2, b="x", c=[3]),
|
236
|
+
]
|
237
|
+
gb = GroupBucket(DummyManager, ("a",), ListBucket(items))
|
238
|
+
|
239
|
+
self.assertEqual(gb.count(), 2)
|
240
|
+
gb = gb.group_by("b")
|
241
|
+
self.assertEqual(gb.count(), 3)
|
242
|
+
|
243
|
+
|
244
|
+
class GroupManagerCombineValueTests(TestCase):
|
245
|
+
def setUp(self):
|
246
|
+
self.original_attr_types = DummyInterface.attr_types.copy()
|
247
|
+
|
248
|
+
def tearDown(self) -> None:
|
249
|
+
DummyInterface.attr_types = self.original_attr_types
|
250
|
+
|
251
|
+
# Parametrized tests for combineValue on various data types
|
252
|
+
def helper_make_group_manager(self, values, value_type):
|
253
|
+
# Create dummy entries with attribute 'field' set to each value
|
254
|
+
entries = [DummyManager(field=v) for v in values]
|
255
|
+
bucket = ListBucket(entries)
|
256
|
+
# Temporarily inject type info for 'field'
|
257
|
+
DummyInterface.attr_types["field"] = {"type": value_type}
|
258
|
+
return GroupManager(DummyManager, {}, bucket)
|
259
|
+
|
260
|
+
def test_combine_integers_sum(self):
|
261
|
+
gm = self.helper_make_group_manager([1, 2, 3], int)
|
262
|
+
self.assertEqual(gm.combineValue("field"), 6)
|
263
|
+
|
264
|
+
def test_combine_strings_concat(self):
|
265
|
+
gm = self.helper_make_group_manager(["a", "b"], str)
|
266
|
+
self.assertEqual(gm.combineValue("field"), "a, b")
|
267
|
+
|
268
|
+
def test_combine_unique_strings_concat(self):
|
269
|
+
gm = self.helper_make_group_manager(["a", "b", "b", "a"], str)
|
270
|
+
self.assertEqual(gm.combineValue("field"), "a, b")
|
271
|
+
|
272
|
+
def test_combine_lists_extend(self):
|
273
|
+
gm = self.helper_make_group_manager([[1], [2, 3]], list)
|
274
|
+
self.assertEqual(gm.combineValue("field"), [1, 2, 3])
|
275
|
+
|
276
|
+
def test_combine_only_none(self):
|
277
|
+
gm = self.helper_make_group_manager([None, None], type(None))
|
278
|
+
self.assertIsNone(gm.combineValue("field"))
|
279
|
+
|
280
|
+
def test_combine_none_and_value(self):
|
281
|
+
gm = self.helper_make_group_manager([None, 1], int)
|
282
|
+
self.assertEqual(gm.combineValue("field"), 1)
|
283
|
+
|
284
|
+
def test_combine_dicts_merge(self):
|
285
|
+
gm = self.helper_make_group_manager([{"x": 1}, {"y": 2}], dict)
|
286
|
+
self.assertEqual(gm.combineValue("field"), {"x": 1, "y": 2})
|
287
|
+
|
288
|
+
def test_combine_bools_any(self):
|
289
|
+
gm = self.helper_make_group_manager([True, False], bool)
|
290
|
+
self.assertTrue(gm.combineValue("field"))
|
291
|
+
|
292
|
+
def test_combine_dates_max(self):
|
293
|
+
dates = [date(2020, 1, 1), date(2021, 1, 1)]
|
294
|
+
gm = self.helper_make_group_manager(dates, date)
|
295
|
+
self.assertEqual(gm.combineValue("field"), date(2021, 1, 1))
|
296
|
+
|
297
|
+
def test_combine_measurement_sum(self):
|
298
|
+
|
299
|
+
gm = self.helper_make_group_manager(
|
300
|
+
[Measurement(1, "m"), Measurement(2, "m")], Measurement
|
301
|
+
)
|
302
|
+
result = gm.combineValue("field")
|
303
|
+
self.assertEqual(result, Measurement(3, "m"))
|
304
|
+
|
305
|
+
def test_iterate_group_manager(self):
|
306
|
+
# Test that iterating over GroupManager yields correct items
|
307
|
+
DummyInterface.attr_types = {
|
308
|
+
"a": {"type": int},
|
309
|
+
"b": {"type": str},
|
310
|
+
}
|
311
|
+
items = [DummyManager(a=i, b=str(i**2)) for i in range(5)]
|
312
|
+
gb = GroupBucket(DummyManager, ("a",), ListBucket(items))
|
313
|
+
gm = gb.all()
|
314
|
+
self.assertEqual(len(list(gm)), 5)
|
315
|
+
for i, item in enumerate(gm):
|
316
|
+
self.assertEqual(
|
317
|
+
dict(item),
|
318
|
+
{"a": i, "b": str(i**2), "extraMethod": "extra method result"},
|
319
|
+
)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/cache/modelDependencyCollector.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/interface/calculationInterface.py
RENAMED
File without changes
|
{generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/interface/databaseInterface.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/measurement/measurement.py
RENAMED
File without changes
|
{generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/measurement/measurementField.py
RENAMED
File without changes
|
File without changes
|
{generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/permission/basePermission.py
RENAMED
File without changes
|
{generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/permission/fileBasedPermission.py
RENAMED
File without changes
|
File without changes
|
{generalmanager-0.5.0 → generalmanager-0.5.1}/src/general_manager/permission/permissionChecks.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|