ramifice 0.4.10__py3-none-any.whl → 0.4.12__py3-none-any.whl
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.
- ramifice/commons/general.py +13 -3
- ramifice/commons/many.py +36 -6
- ramifice/commons/one.py +26 -4
- ramifice/commons/tools.py +26 -8
- ramifice/fields/choice_float_dyn_field.py +2 -2
- ramifice/fields/choice_float_field.py +1 -1
- ramifice/fields/choice_float_mult_dyn_field.py +2 -2
- ramifice/fields/choice_float_mult_field.py +1 -1
- ramifice/fields/choice_int_dyn_field.py +2 -2
- ramifice/fields/choice_int_field.py +1 -1
- ramifice/fields/choice_int_mult_dyn_field.py +2 -2
- ramifice/fields/choice_int_mult_field.py +1 -1
- ramifice/fields/choice_text_dyn_field.py +2 -2
- ramifice/fields/choice_text_field.py +1 -1
- ramifice/fields/choice_text_mult_dyn_field.py +2 -2
- ramifice/fields/choice_text_mult_field.py +1 -1
- ramifice/fields/text_field.py +6 -0
- ramifice/models/decorator.py +18 -9
- ramifice/paladins/groups/choice_group.py +2 -1
- ramifice/paladins/groups/text_group.py +4 -4
- ramifice/paladins/tools.py +5 -4
- {ramifice-0.4.10.dist-info → ramifice-0.4.12.dist-info}/METADATA +19 -8
- {ramifice-0.4.10.dist-info → ramifice-0.4.12.dist-info}/RECORD +25 -25
- {ramifice-0.4.10.dist-info → ramifice-0.4.12.dist-info}/WHEEL +0 -0
- {ramifice-0.4.10.dist-info → ramifice-0.4.12.dist-info}/licenses/LICENSE +0 -0
ramifice/commons/general.py
CHANGED
@@ -7,6 +7,7 @@ from pymongo.asynchronous.command_cursor import AsyncCommandCursor
|
|
7
7
|
from pymongo.asynchronous.database import AsyncDatabase
|
8
8
|
|
9
9
|
from ..utils import globals, translations
|
10
|
+
from .tools import correct_mongo_filter
|
10
11
|
|
11
12
|
|
12
13
|
class GeneralMixin:
|
@@ -58,7 +59,10 @@ class GeneralMixin:
|
|
58
59
|
"""Gets an estimate of the count of documents in a collection using collection metadata."""
|
59
60
|
# Get collection for current model.
|
60
61
|
collection: AsyncCollection = globals.MONGO_DATABASE[cls.META["collection_name"]]
|
61
|
-
#
|
62
|
+
# Correcting filter.
|
63
|
+
if filter is not None:
|
64
|
+
filter = correct_mongo_filter(cls, filter)
|
65
|
+
|
62
66
|
return await collection.count_documents(
|
63
67
|
filter=filter,
|
64
68
|
session=session,
|
@@ -78,7 +82,10 @@ class GeneralMixin:
|
|
78
82
|
"""Runs an aggregation framework pipeline."""
|
79
83
|
# Get collection for current model.
|
80
84
|
collection: AsyncCollection = globals.MONGO_DATABASE[cls.META["collection_name"]]
|
81
|
-
#
|
85
|
+
# Correcting filter.
|
86
|
+
if pipeline is not None:
|
87
|
+
pipeline = correct_mongo_filter(cls, pipeline)
|
88
|
+
|
82
89
|
return await collection.aggregate(
|
83
90
|
pipeline=pipeline,
|
84
91
|
session=session,
|
@@ -103,7 +110,10 @@ class GeneralMixin:
|
|
103
110
|
"""
|
104
111
|
# Get collection for current model.
|
105
112
|
collection: AsyncCollection = globals.MONGO_DATABASE[cls.META["collection_name"]]
|
106
|
-
#
|
113
|
+
# Correcting filter.
|
114
|
+
if filter is not None:
|
115
|
+
filter = correct_mongo_filter(cls, filter)
|
116
|
+
|
107
117
|
return await collection.distinct(
|
108
118
|
key=key,
|
109
119
|
filter=filter,
|
ramifice/commons/many.py
CHANGED
@@ -7,9 +7,9 @@ from pymongo.asynchronous.collection import AsyncCollection
|
|
7
7
|
from pymongo.asynchronous.cursor import AsyncCursor, CursorType
|
8
8
|
from pymongo.results import DeleteResult
|
9
9
|
|
10
|
-
from ..utils import globals
|
10
|
+
from ..utils import globals, translations
|
11
11
|
from ..utils.errors import PanicError
|
12
|
-
from .tools import mongo_doc_to_raw_doc, password_to_none
|
12
|
+
from .tools import correct_mongo_filter, mongo_doc_to_raw_doc, password_to_none
|
13
13
|
|
14
14
|
|
15
15
|
class ManyMixin:
|
@@ -43,6 +43,9 @@ class ManyMixin:
|
|
43
43
|
"""Find documents."""
|
44
44
|
# Get collection for current model.
|
45
45
|
collection: AsyncCollection = globals.MONGO_DATABASE[cls.META["collection_name"]]
|
46
|
+
# Correcting filter.
|
47
|
+
if filter is not None:
|
48
|
+
filter = correct_mongo_filter(cls, filter)
|
46
49
|
# Get documents.
|
47
50
|
doc_list: list[dict[str, Any]] = []
|
48
51
|
cursor: AsyncCursor = collection.find(
|
@@ -108,6 +111,9 @@ class ManyMixin:
|
|
108
111
|
"""
|
109
112
|
# Get collection for current model.
|
110
113
|
collection: AsyncCollection = globals.MONGO_DATABASE[cls.META["collection_name"]]
|
114
|
+
# Correcting filter.
|
115
|
+
if filter is not None:
|
116
|
+
filter = correct_mongo_filter(cls, filter)
|
111
117
|
# Get documents.
|
112
118
|
doc_list: list[dict[str, Any]] = []
|
113
119
|
cursor: AsyncCursor = collection.find(
|
@@ -133,9 +139,18 @@ class ManyMixin:
|
|
133
139
|
session=session,
|
134
140
|
allow_disk_use=allow_disk_use,
|
135
141
|
)
|
136
|
-
|
142
|
+
inst_model_dict = {
|
143
|
+
key: val for key, val in cls().__dict__.items() if not callable(val) and not val.ignored
|
144
|
+
}
|
145
|
+
lang = translations.CURRENT_LOCALE
|
137
146
|
async for mongo_doc in cursor:
|
138
|
-
doc_list.append(
|
147
|
+
doc_list.append(
|
148
|
+
mongo_doc_to_raw_doc(
|
149
|
+
inst_model_dict,
|
150
|
+
mongo_doc,
|
151
|
+
lang,
|
152
|
+
)
|
153
|
+
)
|
139
154
|
return doc_list
|
140
155
|
|
141
156
|
@classmethod
|
@@ -166,6 +181,9 @@ class ManyMixin:
|
|
166
181
|
"""Find documents and convert to a json string."""
|
167
182
|
# Get collection for current model.
|
168
183
|
collection: AsyncCollection = globals.MONGO_DATABASE[cls.META["collection_name"]]
|
184
|
+
# Correcting filter.
|
185
|
+
if filter is not None:
|
186
|
+
filter = correct_mongo_filter(cls, filter)
|
169
187
|
# Get documents.
|
170
188
|
doc_list: list[dict[str, Any]] = []
|
171
189
|
cursor: AsyncCursor = collection.find(
|
@@ -191,9 +209,18 @@ class ManyMixin:
|
|
191
209
|
session=session,
|
192
210
|
allow_disk_use=allow_disk_use,
|
193
211
|
)
|
194
|
-
|
212
|
+
inst_model_dict = {
|
213
|
+
key: val for key, val in cls().__dict__.items() if not callable(val) and not val.ignored
|
214
|
+
}
|
215
|
+
lang = translations.CURRENT_LOCALE
|
195
216
|
async for mongo_doc in cursor:
|
196
|
-
doc_list.append(
|
217
|
+
doc_list.append(
|
218
|
+
mongo_doc_to_raw_doc(
|
219
|
+
inst_model_dict,
|
220
|
+
mongo_doc,
|
221
|
+
lang,
|
222
|
+
)
|
223
|
+
)
|
197
224
|
return json.dumps(doc_list) if len(doc_list) > 0 else None
|
198
225
|
|
199
226
|
@classmethod
|
@@ -217,6 +244,9 @@ class ManyMixin:
|
|
217
244
|
raise PanicError(msg)
|
218
245
|
# Get collection for current model.
|
219
246
|
collection: AsyncCollection = globals.MONGO_DATABASE[cls.META["collection_name"]]
|
247
|
+
# Correcting filter.
|
248
|
+
if filter is not None:
|
249
|
+
filter = correct_mongo_filter(cls, filter)
|
220
250
|
# Delete documents.
|
221
251
|
result: DeleteResult = await collection.delete_many(
|
222
252
|
filter=filter,
|
ramifice/commons/one.py
CHANGED
@@ -5,9 +5,9 @@ from typing import Any
|
|
5
5
|
from pymongo.asynchronous.collection import AsyncCollection
|
6
6
|
from pymongo.results import DeleteResult
|
7
7
|
|
8
|
-
from ..utils import globals
|
8
|
+
from ..utils import globals, translations
|
9
9
|
from ..utils.errors import PanicError
|
10
|
-
from .tools import mongo_doc_to_raw_doc, password_to_none
|
10
|
+
from .tools import correct_mongo_filter, mongo_doc_to_raw_doc, password_to_none
|
11
11
|
|
12
12
|
|
13
13
|
class OneMixin:
|
@@ -23,6 +23,9 @@ class OneMixin:
|
|
23
23
|
"""Find a single document."""
|
24
24
|
# Get collection for current model.
|
25
25
|
collection: AsyncCollection = globals.MONGO_DATABASE[cls.META["collection_name"]]
|
26
|
+
# Correcting filter.
|
27
|
+
if filter is not None:
|
28
|
+
filter = correct_mongo_filter(cls, filter)
|
26
29
|
# Get document.
|
27
30
|
mongo_doc = await collection.find_one(filter, *args, **kwargs)
|
28
31
|
if mongo_doc is not None:
|
@@ -42,13 +45,20 @@ class OneMixin:
|
|
42
45
|
"""Find a single document and converting to raw document."""
|
43
46
|
# Get collection for current model.
|
44
47
|
collection: AsyncCollection = globals.MONGO_DATABASE[cls.META["collection_name"]]
|
48
|
+
# Correcting filter.
|
49
|
+
if filter is not None:
|
50
|
+
filter = correct_mongo_filter(cls, filter)
|
45
51
|
# Get document.
|
46
52
|
raw_doc = None
|
47
53
|
mongo_doc = await collection.find_one(filter, *args, **kwargs)
|
54
|
+
inst_model_dict = {
|
55
|
+
key: val for key, val in cls().__dict__.items() if not callable(val) and not val.ignored
|
56
|
+
}
|
48
57
|
if mongo_doc is not None:
|
49
58
|
raw_doc = mongo_doc_to_raw_doc(
|
50
|
-
|
59
|
+
inst_model_dict,
|
51
60
|
mongo_doc,
|
61
|
+
translations.CURRENT_LOCALE,
|
52
62
|
)
|
53
63
|
return raw_doc
|
54
64
|
|
@@ -62,6 +72,9 @@ class OneMixin:
|
|
62
72
|
"""Find a single document and convert it to a Model instance."""
|
63
73
|
# Get collection for current model.
|
64
74
|
collection: AsyncCollection = globals.MONGO_DATABASE[cls.META["collection_name"]]
|
75
|
+
# Correcting filter.
|
76
|
+
if filter is not None:
|
77
|
+
filter = correct_mongo_filter(cls, filter)
|
65
78
|
# Get document.
|
66
79
|
inst_model = None
|
67
80
|
mongo_doc = await collection.find_one(filter, *args, **kwargs)
|
@@ -80,12 +93,15 @@ class OneMixin:
|
|
80
93
|
"""Find a single document and convert it to a JSON string."""
|
81
94
|
# Get collection for current model.
|
82
95
|
collection: AsyncCollection = globals.MONGO_DATABASE[cls.META["collection_name"]]
|
96
|
+
# Correcting filter.
|
97
|
+
if filter is not None:
|
98
|
+
filter = correct_mongo_filter(cls, filter)
|
83
99
|
# Get document.
|
84
100
|
json_str: str | None = None
|
85
101
|
mongo_doc = await collection.find_one(filter, *args, **kwargs)
|
86
102
|
if mongo_doc is not None:
|
87
103
|
# Convert document to Model instance.
|
88
|
-
inst_model = cls.from_mongo_doc(
|
104
|
+
inst_model = cls.from_mongo_doc(mongo_doc)
|
89
105
|
json_str = inst_model.to_json()
|
90
106
|
return json_str
|
91
107
|
|
@@ -110,6 +126,9 @@ class OneMixin:
|
|
110
126
|
raise PanicError(msg)
|
111
127
|
# Get collection for current model.
|
112
128
|
collection: AsyncCollection = globals.MONGO_DATABASE[cls.META["collection_name"]]
|
129
|
+
# Correcting filter.
|
130
|
+
if filter is not None:
|
131
|
+
filter = correct_mongo_filter(cls, filter)
|
113
132
|
# Get document.
|
114
133
|
result: DeleteResult = await collection.delete_one(
|
115
134
|
filter=filter,
|
@@ -144,6 +163,9 @@ class OneMixin:
|
|
144
163
|
raise PanicError(msg)
|
145
164
|
# Get collection for current model.
|
146
165
|
collection: AsyncCollection = globals.MONGO_DATABASE[cls.META["collection_name"]]
|
166
|
+
# Correcting filter.
|
167
|
+
if filter is not None:
|
168
|
+
filter = correct_mongo_filter(cls, filter)
|
147
169
|
# Get document.
|
148
170
|
mongo_doc: dict[str, Any] | None = await collection.find_one_and_delete(
|
149
171
|
filter=filter,
|
ramifice/commons/tools.py
CHANGED
@@ -1,12 +1,29 @@
|
|
1
1
|
"""Tool of Commons - A set of auxiliary methods."""
|
2
2
|
|
3
|
+
import json
|
3
4
|
from typing import Any
|
4
5
|
|
5
6
|
from babel.dates import format_date, format_datetime
|
7
|
+
from bson import json_util
|
6
8
|
|
7
9
|
from ..utils import translations
|
8
10
|
|
9
11
|
|
12
|
+
def correct_mongo_filter(cls_model: Any, filter: Any) -> Any:
|
13
|
+
"""Correcting filter of request.
|
14
|
+
|
15
|
+
Corrects `TextField` fields that require localization of translation.
|
16
|
+
"""
|
17
|
+
lang: str = translations.CURRENT_LOCALE
|
18
|
+
filter_json: str = json_util.dumps(filter)
|
19
|
+
filter_json = (
|
20
|
+
cls_model.META["regex_mongo_filter"]
|
21
|
+
.sub(rf'\g<field>.{lang}":', filter_json)
|
22
|
+
.replace('":.', ".")
|
23
|
+
)
|
24
|
+
return json_util.loads(filter_json)
|
25
|
+
|
26
|
+
|
10
27
|
def password_to_none(
|
11
28
|
field_name_and_type: dict[str, str],
|
12
29
|
mongo_doc: dict[str, Any],
|
@@ -19,8 +36,9 @@ def password_to_none(
|
|
19
36
|
|
20
37
|
|
21
38
|
def mongo_doc_to_raw_doc(
|
22
|
-
|
39
|
+
inst_model_dict: dict[str, Any],
|
23
40
|
mongo_doc: dict[str, Any],
|
41
|
+
lang: str,
|
24
42
|
) -> dict[str, Any]:
|
25
43
|
"""Convert the Mongo document to the raw document.
|
26
44
|
|
@@ -31,14 +49,14 @@ def mongo_doc_to_raw_doc(
|
|
31
49
|
datetime to str
|
32
50
|
"""
|
33
51
|
doc: dict[str, Any] = {}
|
34
|
-
|
35
|
-
|
52
|
+
for f_name, f_data in inst_model_dict.items():
|
53
|
+
field_type = f_data.field_type
|
36
54
|
value = mongo_doc[f_name]
|
37
55
|
if value is not None:
|
38
|
-
if
|
56
|
+
if field_type == "TextField" and f_data.multi_language:
|
39
57
|
value = value.get(lang, "") if value is not None else None
|
40
|
-
elif "Date" in
|
41
|
-
if "Time" in
|
58
|
+
elif "Date" in field_type:
|
59
|
+
if "Time" in field_type:
|
42
60
|
value = format_datetime(
|
43
61
|
datetime=value,
|
44
62
|
format="short",
|
@@ -50,9 +68,9 @@ def mongo_doc_to_raw_doc(
|
|
50
68
|
format="short",
|
51
69
|
locale=lang,
|
52
70
|
)
|
53
|
-
elif
|
71
|
+
elif field_type == "IDField":
|
54
72
|
value = str(value)
|
55
|
-
elif
|
73
|
+
elif field_type == "PasswordField":
|
56
74
|
value = None
|
57
75
|
doc[f_name] = value
|
58
76
|
return doc
|
@@ -69,9 +69,9 @@ class ChoiceFloatDynField(Field, ChoiceGroup, JsonMixin):
|
|
69
69
|
self.value: float | None = None
|
70
70
|
self.choices: dict[str, float] | None = None
|
71
71
|
|
72
|
-
def has_value(self,
|
72
|
+
def has_value(self, is_migrate: bool = False) -> bool:
|
73
73
|
"""Does the field value match the possible options in choices."""
|
74
|
-
if
|
74
|
+
if is_migrate:
|
75
75
|
return True
|
76
76
|
value = self.value
|
77
77
|
if value is not None:
|
@@ -80,7 +80,7 @@ class ChoiceFloatField(Field, ChoiceGroup, JsonMixin):
|
|
80
80
|
if not isinstance(readonly, bool):
|
81
81
|
raise AssertionError("Parameter `readonly` - Not а `bool` type!")
|
82
82
|
|
83
|
-
def has_value(self,
|
83
|
+
def has_value(self, is_migrate: bool = False) -> bool:
|
84
84
|
"""Does the field value match the possible options in choices."""
|
85
85
|
value = self.value
|
86
86
|
if value is None:
|
@@ -69,9 +69,9 @@ class ChoiceFloatMultDynField(Field, ChoiceGroup, JsonMixin):
|
|
69
69
|
self.value: list[float] | None = None
|
70
70
|
self.choices: dict[str, float] | None = None
|
71
71
|
|
72
|
-
def has_value(self,
|
72
|
+
def has_value(self, is_migrate: bool = False) -> bool:
|
73
73
|
"""Does the field value match the possible options in choices."""
|
74
|
-
if
|
74
|
+
if is_migrate:
|
75
75
|
return True
|
76
76
|
value = self.value
|
77
77
|
if value is not None:
|
@@ -91,7 +91,7 @@ class ChoiceFloatMultField(Field, ChoiceGroup, JsonMixin):
|
|
91
91
|
if not isinstance(readonly, bool):
|
92
92
|
raise AssertionError("Parameter `readonly` - Not а `bool` type!")
|
93
93
|
|
94
|
-
def has_value(self,
|
94
|
+
def has_value(self, is_migrate: bool = False) -> bool:
|
95
95
|
"""Does the field value match the possible options in choices."""
|
96
96
|
value = self.value
|
97
97
|
if value is None:
|
@@ -69,9 +69,9 @@ class ChoiceIntDynField(Field, ChoiceGroup, JsonMixin):
|
|
69
69
|
self.value: int | None = None
|
70
70
|
self.choices: dict[str, int] | None = None
|
71
71
|
|
72
|
-
def has_value(self,
|
72
|
+
def has_value(self, is_migrate: bool = False) -> bool:
|
73
73
|
"""Does the field value match the possible options in choices."""
|
74
|
-
if
|
74
|
+
if is_migrate:
|
75
75
|
return True
|
76
76
|
value = self.value
|
77
77
|
if value is not None:
|
@@ -80,7 +80,7 @@ class ChoiceIntField(Field, ChoiceGroup, JsonMixin):
|
|
80
80
|
if not isinstance(readonly, bool):
|
81
81
|
raise AssertionError("Parameter `readonly` - Not а `bool` type!")
|
82
82
|
|
83
|
-
def has_value(self,
|
83
|
+
def has_value(self, is_migrate: bool = False) -> bool:
|
84
84
|
"""Does the field value match the possible options in choices."""
|
85
85
|
value = self.value
|
86
86
|
if value is None:
|
@@ -69,9 +69,9 @@ class ChoiceIntMultDynField(Field, ChoiceGroup, JsonMixin):
|
|
69
69
|
self.value: list[int] | None = None
|
70
70
|
self.choices: dict[str, int] | None = None
|
71
71
|
|
72
|
-
def has_value(self,
|
72
|
+
def has_value(self, is_migrate: bool = False) -> bool:
|
73
73
|
"""Does the field value match the possible options in choices."""
|
74
|
-
if
|
74
|
+
if is_migrate:
|
75
75
|
return True
|
76
76
|
value = self.value
|
77
77
|
if value is not None:
|
@@ -91,7 +91,7 @@ class ChoiceIntMultField(Field, ChoiceGroup, JsonMixin):
|
|
91
91
|
if not isinstance(readonly, bool):
|
92
92
|
raise AssertionError("Parameter `readonly` - Not а `bool` type!")
|
93
93
|
|
94
|
-
def has_value(self,
|
94
|
+
def has_value(self, is_migrate: bool = False) -> bool:
|
95
95
|
"""Does the field value match the possible options in choices."""
|
96
96
|
value = self.value
|
97
97
|
if value is None:
|
@@ -69,9 +69,9 @@ class ChoiceTextDynField(Field, ChoiceGroup, JsonMixin):
|
|
69
69
|
self.value: str | None = None
|
70
70
|
self.choices: dict[str, str] | None = None
|
71
71
|
|
72
|
-
def has_value(self,
|
72
|
+
def has_value(self, is_migrate: bool = False) -> bool:
|
73
73
|
"""Does the field value match the possible options in choices."""
|
74
|
-
if
|
74
|
+
if is_migrate:
|
75
75
|
return True
|
76
76
|
value = self.value
|
77
77
|
if value is not None:
|
@@ -85,7 +85,7 @@ class ChoiceTextField(Field, ChoiceGroup, JsonMixin):
|
|
85
85
|
if not isinstance(readonly, bool):
|
86
86
|
raise AssertionError("Parameter `readonly` - Not а `bool` type!")
|
87
87
|
|
88
|
-
def has_value(self,
|
88
|
+
def has_value(self, is_migrate: bool = False) -> bool:
|
89
89
|
"""Does the field value match the possible options in choices."""
|
90
90
|
value = self.value
|
91
91
|
if value is None:
|
@@ -69,9 +69,9 @@ class ChoiceTextMultDynField(Field, ChoiceGroup, JsonMixin):
|
|
69
69
|
self.value: list[str] | None = None
|
70
70
|
self.choices: dict[str, str] | None = None
|
71
71
|
|
72
|
-
def has_value(self,
|
72
|
+
def has_value(self, is_migrate: bool = False) -> bool:
|
73
73
|
"""Does the field value match the possible options in choices."""
|
74
|
-
if
|
74
|
+
if is_migrate:
|
75
75
|
return True
|
76
76
|
value = self.value
|
77
77
|
if value is not None:
|
@@ -91,7 +91,7 @@ class ChoiceTextMultField(Field, ChoiceGroup, JsonMixin):
|
|
91
91
|
if not isinstance(readonly, bool):
|
92
92
|
raise AssertionError("Parameter `readonly` - Not а `bool` type!")
|
93
93
|
|
94
|
-
def has_value(self,
|
94
|
+
def has_value(self, is_migrate: bool = False) -> bool:
|
95
95
|
"""Does the field value match the possible options in choices."""
|
96
96
|
value = self.value
|
97
97
|
if value is None:
|
ramifice/fields/text_field.py
CHANGED
@@ -23,6 +23,8 @@ class TextField(Field, JsonMixin):
|
|
23
23
|
readonly: bool = False,
|
24
24
|
unique: bool = False,
|
25
25
|
maxlength: int = 256,
|
26
|
+
# Support for several language.
|
27
|
+
multi_language: bool = False,
|
26
28
|
):
|
27
29
|
if globals.DEBUG:
|
28
30
|
if not isinstance(maxlength, int):
|
@@ -55,6 +57,8 @@ class TextField(Field, JsonMixin):
|
|
55
57
|
raise AssertionError("Parameter `use_editor` - Not а `bool` type!")
|
56
58
|
if not isinstance(maxlength, int):
|
57
59
|
raise AssertionError("Parameter `maxlength` - Not а `int` type!")
|
60
|
+
if not isinstance(multi_language, bool):
|
61
|
+
raise AssertionError("Parameter `multi_language` - Not а `int` type!")
|
58
62
|
|
59
63
|
Field.__init__(
|
60
64
|
self,
|
@@ -78,6 +82,8 @@ class TextField(Field, JsonMixin):
|
|
78
82
|
self.textarea = textarea
|
79
83
|
self.use_editor = use_editor
|
80
84
|
self.maxlength = maxlength
|
85
|
+
# Support for several language.
|
86
|
+
self.multi_language = multi_language
|
81
87
|
|
82
88
|
def __len__(self) -> int:
|
83
89
|
"""Return length of field `value`."""
|
ramifice/models/decorator.py
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
"""Decorator for converting Python classes into Ramifice models."""
|
2
2
|
|
3
3
|
import os
|
4
|
+
import re
|
4
5
|
from typing import Any
|
5
6
|
|
6
7
|
from ..commons import QCommonsMixin
|
7
8
|
from ..fields import DateTimeField, IDField
|
8
9
|
from ..paladins import CheckMixin, QPaladinsMixin, ValidationMixin
|
9
10
|
from ..utils.errors import DoesNotMatchRegexError, PanicError
|
11
|
+
from ..utils.globals import REGEX
|
10
12
|
from ..utils.mixins.add_valid import AddValidMixin
|
11
13
|
from ..utils.mixins.hooks import HooksMixin
|
12
14
|
from ..utils.mixins.indexing import IndexMixin
|
13
|
-
from ..utils.globals import REGEX
|
14
15
|
from .model import Model
|
15
16
|
from .pseudo import PseudoModel
|
16
17
|
|
@@ -103,7 +104,7 @@ def model(
|
|
103
104
|
def caching(cls: Any, service_name: str, is_migrate_model: bool) -> dict[str, Any]:
|
104
105
|
"""Add additional metadata to `Model.META`."""
|
105
106
|
metadata: dict[str, Any] = {}
|
106
|
-
model_name = cls.__name__
|
107
|
+
model_name: str = cls.__name__
|
107
108
|
if REGEX["model_name"].match(model_name) is None:
|
108
109
|
raise DoesNotMatchRegexError("^[A-Z][a-zA-Z0-9]{0,24}$")
|
109
110
|
#
|
@@ -118,9 +119,12 @@ def caching(cls: Any, service_name: str, is_migrate_model: bool) -> dict[str, An
|
|
118
119
|
# Build data migration storage for dynamic fields.
|
119
120
|
data_dynamic_fields: dict[str, dict[str, str | int | float] | None] = {}
|
120
121
|
# Count all fields.
|
121
|
-
count_all_fields = 0
|
122
|
+
count_all_fields: int = 0
|
122
123
|
# Count fields for migrating.
|
123
|
-
count_fields_no_ignored = 0
|
124
|
+
count_fields_no_ignored: int = 0
|
125
|
+
# List of fields that support localization of translates.
|
126
|
+
# Hint: `TextField`
|
127
|
+
supported_lang_fields: list[str] = []
|
124
128
|
|
125
129
|
raw_model = cls()
|
126
130
|
raw_model.fields()
|
@@ -130,9 +134,9 @@ def caching(cls: Any, service_name: str, is_migrate_model: bool) -> dict[str, An
|
|
130
134
|
default_fields["created_at"] = DateTimeField()
|
131
135
|
default_fields["updated_at"] = DateTimeField()
|
132
136
|
fields = {**raw_model.__dict__, **default_fields}
|
133
|
-
for f_name,
|
134
|
-
if not callable(
|
135
|
-
f_type_str =
|
137
|
+
for f_name, f_data in fields.items():
|
138
|
+
if not callable(f_data):
|
139
|
+
f_type_str = f_data.__class__.__name__
|
136
140
|
# Count all fields.
|
137
141
|
count_all_fields += 1
|
138
142
|
# Get attributes value for fields of Model: id, name.
|
@@ -141,14 +145,14 @@ def caching(cls: Any, service_name: str, is_migrate_model: bool) -> dict[str, An
|
|
141
145
|
"name": f_name,
|
142
146
|
}
|
143
147
|
#
|
144
|
-
if not
|
148
|
+
if not f_data.ignored:
|
145
149
|
# Count fields for migrating.
|
146
150
|
if is_migrate_model:
|
147
151
|
count_fields_no_ignored += 1
|
148
152
|
# Get a dictionary of field names and types.
|
149
153
|
field_name_and_type[f_name] = f_type_str
|
150
154
|
# Build data migration storage for dynamic fields.
|
151
|
-
if "Dyn" in
|
155
|
+
if "Dyn" in f_data.field_type:
|
152
156
|
if not is_migrate_model:
|
153
157
|
msg = (
|
154
158
|
f"Model: `{cls.__module__}.{model_name}` > "
|
@@ -157,11 +161,16 @@ def caching(cls: Any, service_name: str, is_migrate_model: bool) -> dict[str, An
|
|
157
161
|
)
|
158
162
|
raise PanicError(msg)
|
159
163
|
data_dynamic_fields[f_name] = None
|
164
|
+
if f_data.field_type == "TextField" and f_data.multi_language:
|
165
|
+
supported_lang_fields.append(f_name)
|
160
166
|
|
161
167
|
metadata["field_name_and_type"] = field_name_and_type
|
162
168
|
metadata["field_attrs"] = field_attrs
|
163
169
|
metadata["data_dynamic_fields"] = data_dynamic_fields
|
164
170
|
metadata["count_all_fields"] = count_all_fields
|
165
171
|
metadata["count_fields_no_ignored"] = count_fields_no_ignored
|
172
|
+
metadata["regex_mongo_filter"] = re.compile(
|
173
|
+
rf'(?P<field>"(?:{"|".join(supported_lang_fields)})":)'
|
174
|
+
)
|
166
175
|
|
167
176
|
return metadata
|
@@ -26,6 +26,7 @@ class ChoiceGroupMixin:
|
|
26
26
|
def choice_group(self, params: dict[str, Any]) -> None:
|
27
27
|
"""Checking choice fields."""
|
28
28
|
field = params["field_data"]
|
29
|
+
is_migrate = params["is_migration_process"]
|
29
30
|
# Get current value.
|
30
31
|
value = field.value or field.__dict__.get("default") or None
|
31
32
|
|
@@ -37,7 +38,7 @@ class ChoiceGroupMixin:
|
|
37
38
|
params["result_map"][field.name] = None
|
38
39
|
return
|
39
40
|
# Does the field value match the possible options in choices.
|
40
|
-
if not field.has_value(
|
41
|
+
if not field.has_value(is_migrate):
|
41
42
|
err_msg = translations._("Your choice does not match the options offered !")
|
42
43
|
accumulate_error(err_msg, params)
|
43
44
|
# Insert result.
|
@@ -27,11 +27,11 @@ class TextGroupMixin:
|
|
27
27
|
field = params["field_data"]
|
28
28
|
field_name = field.name
|
29
29
|
field_type: str = field.field_type
|
30
|
-
|
30
|
+
is_multi_language: bool = (field_type == "TextField") and field.multi_language
|
31
31
|
# Get current value.
|
32
32
|
value = field.value or field.__dict__.get("default")
|
33
33
|
|
34
|
-
if
|
34
|
+
if is_multi_language:
|
35
35
|
if not isinstance(value, (str, dict, type(None))):
|
36
36
|
panic_type_error("str | dict | None", params)
|
37
37
|
else:
|
@@ -55,7 +55,7 @@ class TextGroupMixin:
|
|
55
55
|
value,
|
56
56
|
params,
|
57
57
|
field_name,
|
58
|
-
|
58
|
+
is_multi_language,
|
59
59
|
):
|
60
60
|
err_msg = translations._("Is not unique !")
|
61
61
|
accumulate_error(err_msg, params)
|
@@ -85,7 +85,7 @@ class TextGroupMixin:
|
|
85
85
|
accumulate_error(err_msg, params)
|
86
86
|
# Insert result.
|
87
87
|
if params["is_save"]:
|
88
|
-
if
|
88
|
+
if is_multi_language:
|
89
89
|
mult_lang_text: dict[str, str] = (
|
90
90
|
params["curr_doc"][field_name]
|
91
91
|
if params["is_update"]
|
ramifice/paladins/tools.py
CHANGED
@@ -15,9 +15,10 @@ def ignored_fields_to_none(inst_model: Any) -> None:
|
|
15
15
|
def refresh_from_mongo_doc(inst_model: Any, mongo_doc: dict[str, Any]) -> None:
|
16
16
|
"""Update object instance from Mongo document."""
|
17
17
|
lang: str = translations.CURRENT_LOCALE
|
18
|
+
model_dict = inst_model.__dict__
|
18
19
|
for name, data in mongo_doc.items():
|
19
|
-
field =
|
20
|
-
if field.field_type == "TextField":
|
20
|
+
field = model_dict[name]
|
21
|
+
if field.field_type == "TextField" and field.multi_language:
|
21
22
|
field.value = data.get(lang, "") if data is not None else None
|
22
23
|
elif field.group == "pass":
|
23
24
|
field.value = None
|
@@ -54,13 +55,13 @@ async def check_uniqueness(
|
|
54
55
|
value: str | int | float,
|
55
56
|
params: dict[str, Any],
|
56
57
|
field_name: str | None = None,
|
57
|
-
|
58
|
+
is_multi_language: bool = False,
|
58
59
|
) -> bool:
|
59
60
|
"""Check the uniqueness of the value in the collection."""
|
60
61
|
if not params["is_migrate_model"]:
|
61
62
|
return True
|
62
63
|
q_filter = None
|
63
|
-
if
|
64
|
+
if is_multi_language:
|
64
65
|
lang_filter = [{f"{field_name}.{lang}": value} for lang in translations.LANGUAGES]
|
65
66
|
q_filter = {
|
66
67
|
"$and": [
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ramifice
|
3
|
-
Version: 0.4.
|
3
|
+
Version: 0.4.12
|
4
4
|
Summary: ORM-like API MongoDB for Python language.
|
5
5
|
Project-URL: Homepage, https://github.com/kebasyaty/ramifice
|
6
6
|
Project-URL: Documentation, https://kebasyaty.github.io/ramifice/
|
@@ -12,7 +12,7 @@ Author-email: Gennady Kostyunin <kebasyaty@gmail.com>
|
|
12
12
|
License-Expression: MIT
|
13
13
|
License-File: LICENSE
|
14
14
|
Keywords: mongo,mongodb,orm,pymongo,ramifice
|
15
|
-
Classifier: Development Status ::
|
15
|
+
Classifier: Development Status :: 4 - Beta
|
16
16
|
Classifier: Intended Audience :: Developers
|
17
17
|
Classifier: License :: OSI Approved :: MIT License
|
18
18
|
Classifier: Programming Language :: Python :: 3
|
@@ -82,7 +82,7 @@ _For more information see [PyMongo](https://pypi.org/project/pymongo/ "PyMongo")
|
|
82
82
|
|
83
83
|
<p>
|
84
84
|
<a href="https://github.com/kebasyaty/ramifice" alt="Project Status">
|
85
|
-
<img src="https://raw.githubusercontent.com/kebasyaty/ramifice/v0/assets/project_status/project-status-
|
85
|
+
<img src="https://raw.githubusercontent.com/kebasyaty/ramifice/v0/assets/project_status/project-status-beta.svg"
|
86
86
|
alt="Project Status">
|
87
87
|
</a>
|
88
88
|
</p>
|
@@ -177,7 +177,10 @@ class User:
|
|
177
177
|
required=True,
|
178
178
|
unique=True,
|
179
179
|
)
|
180
|
-
self.first_name = TextField(
|
180
|
+
self.first_name = TextField(
|
181
|
+
label=gettext("First name"),
|
182
|
+
required=True,
|
183
|
+
)
|
181
184
|
self.last_name = TextField(
|
182
185
|
label=gettext("Last name"),
|
183
186
|
required=True,
|
@@ -187,8 +190,17 @@ class User:
|
|
187
190
|
required=True,
|
188
191
|
unique=True,
|
189
192
|
)
|
190
|
-
self.birthday = DateField(
|
191
|
-
|
193
|
+
self.birthday = DateField(
|
194
|
+
label=gettext("Birthday"),
|
195
|
+
)
|
196
|
+
self.description = TextField(
|
197
|
+
label=gettext("About yourself"),
|
198
|
+
# Support for several language.
|
199
|
+
multi_language=True,
|
200
|
+
)
|
201
|
+
self.password = PasswordField(
|
202
|
+
label=gettext("Password"),
|
203
|
+
)
|
192
204
|
self.сonfirm_password = PasswordField(
|
193
205
|
label=gettext("Confirm password"),
|
194
206
|
# If true, the value of this field is not saved in the database.
|
@@ -251,8 +263,7 @@ async def main():
|
|
251
263
|
print("User details:")
|
252
264
|
user_details = await User.find_one_to_raw_doc(
|
253
265
|
# {"_id": user.id.value}
|
254
|
-
|
255
|
-
{f"username.{translations.CURRENT_LOCALE}": user.username.value}
|
266
|
+
{f"username": user.username.value}
|
256
267
|
)
|
257
268
|
if user_details is not None:
|
258
269
|
pprint.pprint(user_details)
|
@@ -1,26 +1,26 @@
|
|
1
1
|
ramifice/__init__.py,sha256=IuI20h84QWyFftykvte_HLft2iVxwpu0K592NypMwfo,759
|
2
2
|
ramifice/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
ramifice/commons/__init__.py,sha256=FTG78sTU0ay0rvmrq5fUmVjL-I-S7wgQq3BLcyqnjrQ,433
|
4
|
-
ramifice/commons/general.py,sha256=
|
4
|
+
ramifice/commons/general.py,sha256=Vq0IMuaZj-BqwENTDMg_DouMjV8EjzDy181Ujtw3JQs,5358
|
5
5
|
ramifice/commons/indexes.py,sha256=hAcWKZ9MMgLbRtoQ6Af0b8r-PY0dbEYmMBl8z_d1DRo,3646
|
6
|
-
ramifice/commons/many.py,sha256=
|
7
|
-
ramifice/commons/one.py,sha256=
|
8
|
-
ramifice/commons/tools.py,sha256=
|
6
|
+
ramifice/commons/many.py,sha256=jkah1kO1CXWPB0xW0dbMelCFBSirRYkX9a333EcPE_M,9202
|
7
|
+
ramifice/commons/one.py,sha256=uZsgwg_GgA1VMZDPqhRFMmCdtvcv6xsL4DWbCOwGV3w,6730
|
8
|
+
ramifice/commons/tools.py,sha256=hCnB95nwSCUufig9cSA5Kj9jvqRIJJ2GUSzfUK4FNXM,2363
|
9
9
|
ramifice/commons/unit_manager.py,sha256=IkWqXu1PHHal0aGfx6zme81iXPeygxPqEWO-u_8RXoI,4356
|
10
10
|
ramifice/fields/__init__.py,sha256=yRfX7Tvpuh27Ggcx5u9e1RRYK7Wu59EVJYxxetmuP1w,1290
|
11
11
|
ramifice/fields/bool_field.py,sha256=WWubSwsFJZu8b3MviDd2zXnNYOQaYw8toklFNSTVFLA,2072
|
12
|
-
ramifice/fields/choice_float_dyn_field.py,sha256=
|
13
|
-
ramifice/fields/choice_float_field.py,sha256=
|
14
|
-
ramifice/fields/choice_float_mult_dyn_field.py,sha256=
|
15
|
-
ramifice/fields/choice_float_mult_field.py,sha256=
|
16
|
-
ramifice/fields/choice_int_dyn_field.py,sha256=
|
17
|
-
ramifice/fields/choice_int_field.py,sha256=
|
18
|
-
ramifice/fields/choice_int_mult_dyn_field.py,sha256=
|
19
|
-
ramifice/fields/choice_int_mult_field.py,sha256=
|
20
|
-
ramifice/fields/choice_text_dyn_field.py,sha256=
|
21
|
-
ramifice/fields/choice_text_field.py,sha256=
|
22
|
-
ramifice/fields/choice_text_mult_dyn_field.py,sha256=
|
23
|
-
ramifice/fields/choice_text_mult_field.py,sha256=
|
12
|
+
ramifice/fields/choice_float_dyn_field.py,sha256=Yrd9cNKLm0mhU-P3IojwFRLSbuxdp7f2NMGJhrRWsf8,3093
|
13
|
+
ramifice/fields/choice_float_field.py,sha256=GX7ygsflgXFwwU5Gacq8EsXTdzdgNxWfT5ys8z4s2K8,3675
|
14
|
+
ramifice/fields/choice_float_mult_dyn_field.py,sha256=zDaJ_-qSytIXfY4EH43QwPB0dtkQfHPCGSZI7VMDnoo,3156
|
15
|
+
ramifice/fields/choice_float_mult_field.py,sha256=TArh0D13F9JuJjFn7WDrBzAqDCL03T35z1nZB4T55Rw,4232
|
16
|
+
ramifice/fields/choice_int_dyn_field.py,sha256=pryP7KDvRexKBo9mQTwN61Rnyuhiiv_ZHw56wWFe_bs,3087
|
17
|
+
ramifice/fields/choice_int_field.py,sha256=OyuADapAIwlutYvAtu-g7hwmq251e9cHMqKP4dm9Du8,3663
|
18
|
+
ramifice/fields/choice_int_mult_dyn_field.py,sha256=BZbH0SUcJji6daazj3ovBYCaY1o00rMBbEFzCzDu7b4,3152
|
19
|
+
ramifice/fields/choice_int_mult_field.py,sha256=4PZx0Wv1R2Wq5JZWQNqqYi_4OX13KNwdxlwlyKqndZM,4226
|
20
|
+
ramifice/fields/choice_text_dyn_field.py,sha256=HcJXq9HHgglz_BTOpGfvXmxH2BGhXze_-QAampmTGnI,3083
|
21
|
+
ramifice/fields/choice_text_field.py,sha256=JHauvxmYIbxUMjCgOHI0IcBNT2zv2gPH0njp6qXlKr4,3868
|
22
|
+
ramifice/fields/choice_text_mult_dyn_field.py,sha256=GmoRveIQjidAV-NOmfEO2irlG6agRHpMKNUFVzKSGiM,3148
|
23
|
+
ramifice/fields/choice_text_mult_field.py,sha256=RHB38fNL4ppasE-x2jJE73s221sDZ-YX3-WgpYsI_kA,4222
|
24
24
|
ramifice/fields/color_field.py,sha256=NdYey3Og8hh7oK7yDljrG6K8frgqj4C2OTXFnJVAYFQ,3526
|
25
25
|
ramifice/fields/date_field.py,sha256=w875p6JwssKhEZJLBHmC8sE5wArn161rlQi0Zt-qEZo,5214
|
26
26
|
ramifice/fields/date_time_field.py,sha256=OGi-ED-Gm_AWGzczHA1qch59IuWy3KkVIJHQl3OlatU,5249
|
@@ -34,7 +34,7 @@ ramifice/fields/ip_field.py,sha256=tsxdwKd-MhkhnE1F2FmFg67jwh9Q-RvXECv1aG8PitE,3
|
|
34
34
|
ramifice/fields/password_field.py,sha256=m3n_qJRRHCtgyBC7nDB0dYBO_82Lp1Ws48d1_jxHA8o,3216
|
35
35
|
ramifice/fields/phone_field.py,sha256=5qZ_rR82M9cuW7Dv-D5d9jDaw7DJp9y60lRB4GEmi4w,3615
|
36
36
|
ramifice/fields/slug_field.py,sha256=9rH1VdxYPoK60QCKHRB91bA3ZmIeIuFmf6WttzB1yTI,2685
|
37
|
-
ramifice/fields/text_field.py,sha256=
|
37
|
+
ramifice/fields/text_field.py,sha256=pa5J1nTBC56JDU3mDyjy1Qofv8FLFUgACkNcZB6cUGQ,4107
|
38
38
|
ramifice/fields/url_field.py,sha256=DTy4B22juav9dzIrqKy3NzM2vuICHvDzIDbzkkyF1C0,3993
|
39
39
|
ramifice/fields/general/__init__.py,sha256=5OE0TwPQjvpB3lBPuEwKrhjR_1ehOWxB98OJP_n32MA,20
|
40
40
|
ramifice/fields/general/choice_group.py,sha256=TBJblwH8mB71wd1z2jcSs28H-zx3JZVBfkk4YCE1-pI,916
|
@@ -44,7 +44,7 @@ ramifice/fields/general/file_group.py,sha256=n45KfPzFI_l5hXoKkPDG0Q-0mdC2obExV-3
|
|
44
44
|
ramifice/fields/general/number_group.py,sha256=AqlCY-t6JHZ2QVBe7mk5nPt6z8M4VJ_RARRlSBoIxms,715
|
45
45
|
ramifice/fields/general/text_group.py,sha256=6GD2Fe6Toz6zZjAAlcy5rPVCAGh6Yn1ltdIrFg9RF18,1057
|
46
46
|
ramifice/models/__init__.py,sha256=h_QQ5rSJNZ-7kmx7wIFd8E8RmUS94b_x4jdwMbq8zm4,15
|
47
|
-
ramifice/models/decorator.py,sha256=
|
47
|
+
ramifice/models/decorator.py,sha256=U1ukWWq2voEwvKonQAzihqGYVsoyPrOQ2jDXJZ-UcMc,7251
|
48
48
|
ramifice/models/model.py,sha256=ZvgIbcuSXuAkBl_FofOZXGwWMwlqKB-PwTMyXiqK-Fo,6610
|
49
49
|
ramifice/models/pseudo.py,sha256=PhLQM4zXdaOtTSmNFzwN4twlUmdvA1D_-YOMJtaOIwM,6754
|
50
50
|
ramifice/paladins/__init__.py,sha256=PIP3AXI2KBRXNcLJUF0d7ygJ7VLOAxlhb4HRKQ9MGYY,516
|
@@ -53,11 +53,11 @@ ramifice/paladins/delete.py,sha256=tw50E98D5eFZ7gHGnh_8ztUB1LeTeWWKZvIcQqlgbF8,3
|
|
53
53
|
ramifice/paladins/password.py,sha256=w1XWh3bsncH1VTVjCLxyKI2waxMvltwcsPWW3V9Ib84,3027
|
54
54
|
ramifice/paladins/refrash.py,sha256=fw-9x_NKGzreipBt_F9KF6FTsYm9hSzfq4ubi1FHYrQ,1065
|
55
55
|
ramifice/paladins/save.py,sha256=EG0_v-imCQPax2pJIAXPoQcSL99g8abY2EeGJMvXBW4,3864
|
56
|
-
ramifice/paladins/tools.py,sha256=
|
56
|
+
ramifice/paladins/tools.py,sha256=fn1gve72oFRWDmFw9ESe5wd6bY5av5qyrrvWf0tw8Ns,2703
|
57
57
|
ramifice/paladins/validation.py,sha256=gcEJXIEPu1g7Z54vl14YTs5rCmxOEYsgQH1usFfyy7k,1730
|
58
58
|
ramifice/paladins/groups/__init__.py,sha256=hpqmWLsYAMvZHAbmMXluQSqLhkHOSTUAgLHyTM1LTYI,472
|
59
59
|
ramifice/paladins/groups/bool_group.py,sha256=oJc9mw9KGrnK_Pj7uXixYYQAJphcXLr_xSQv3PMUlcU,792
|
60
|
-
ramifice/paladins/groups/choice_group.py,sha256=
|
60
|
+
ramifice/paladins/groups/choice_group.py,sha256=NsOdPMHtnK-ITnSQxEteCeTFhk7MrJJ3wIcDEeOgodU,1791
|
61
61
|
ramifice/paladins/groups/date_group.py,sha256=B9usKKrHX16F1ckik60Xkub1tawgNENSHTk5Rt-K96k,3741
|
62
62
|
ramifice/paladins/groups/file_group.py,sha256=-xgtrLOTlKXc71Mnbu3I_LbYnTEd8OprnhtYcJbaDtg,2932
|
63
63
|
ramifice/paladins/groups/id_group.py,sha256=q5BeoM1e7mZmX0zVgemva9K-9ihJKBrX8kxvMh-uhhQ,1268
|
@@ -65,7 +65,7 @@ ramifice/paladins/groups/img_group.py,sha256=RoD_QnW0F0DAzrGQmRi8jMZnWy2IomlFn6A
|
|
65
65
|
ramifice/paladins/groups/num_group.py,sha256=6jT7nfIiVoQTlI2UdkcQOlHIDYjllFqSH7Nb87uJgzg,2274
|
66
66
|
ramifice/paladins/groups/pass_group.py,sha256=YU5a-NwohEutoEx2N5JmGfg8uPiYiW0XJ8XYsOih6eA,1859
|
67
67
|
ramifice/paladins/groups/slug_group.py,sha256=joBB5litljbv2h5JKEMzF71s_DKMWH6nzgThLiLZx2E,2307
|
68
|
-
ramifice/paladins/groups/text_group.py,sha256=
|
68
|
+
ramifice/paladins/groups/text_group.py,sha256=iftXyyqloQr9yJki__d0LwCEeerkcFquDHyI5rB8fio,4348
|
69
69
|
ramifice/utils/__init__.py,sha256=xixHoOX4ja5jIUZemem1qn4k4aonv3G3Q76azQK_pkU,43
|
70
70
|
ramifice/utils/errors.py,sha256=iuhq7fzpUmsOyeXeg2fJjta8yAuqlXLKsZVMpfUhtHE,1901
|
71
71
|
ramifice/utils/fixtures.py,sha256=qsv9cg06lc82XaRlhI1j5vftmOQTTwOcDXCg_lnpK04,3159
|
@@ -79,7 +79,7 @@ ramifice/utils/mixins/add_valid.py,sha256=TLOObedzXNA9eCylfAVbVCqIKE5sV-P5AdIN7a
|
|
79
79
|
ramifice/utils/mixins/hooks.py,sha256=33jvJRhfnJeL2Hd_YFXk3M_7wjqHaByU2wRjKyboL6s,914
|
80
80
|
ramifice/utils/mixins/indexing.py,sha256=Z0427HoaVRyNmSNN8Fx0mSICgAKV-gDdu3iR5qYUEbs,329
|
81
81
|
ramifice/utils/mixins/json_converter.py,sha256=WhigXyDAV-FfILaZuwvRFRIk0D90Rv3dG5t-mv5fVyc,1107
|
82
|
-
ramifice-0.4.
|
83
|
-
ramifice-0.4.
|
84
|
-
ramifice-0.4.
|
85
|
-
ramifice-0.4.
|
82
|
+
ramifice-0.4.12.dist-info/METADATA,sha256=u46bs5blVVIv_vh4jrW5ERLTFe7z8O_11-U-VK7UvLg,21965
|
83
|
+
ramifice-0.4.12.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
84
|
+
ramifice-0.4.12.dist-info/licenses/LICENSE,sha256=LrEL0aTZx90HDwFUQCJutORiDjJL9AnuVvCtspXIqt4,1095
|
85
|
+
ramifice-0.4.12.dist-info/RECORD,,
|
File without changes
|
File without changes
|