PyDataToolsKit 1.0.0__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.
- PyData/classes/repository.py +154 -0
- PyData/database.py +39 -0
- PyData/entity.py +31 -0
- PyData/id.py +38 -0
- pydatatoolskit-1.0.0.dist-info/METADATA +5 -0
- pydatatoolskit-1.0.0.dist-info/RECORD +8 -0
- pydatatoolskit-1.0.0.dist-info/WHEEL +5 -0
- pydatatoolskit-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from datetime import date, timedelta
|
|
5
|
+
from typing import Generic, TypeVar, Any
|
|
6
|
+
|
|
7
|
+
from Decorators.functions.retry import Retry #type: ignore
|
|
8
|
+
from Decorators.functions.validateType import ValidateType #type: ignore
|
|
9
|
+
|
|
10
|
+
T = TypeVar("T")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Repository(Generic[T]):
|
|
14
|
+
"""Persists entities as a JSON list and provides basic CRUD operations."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, path: str, entity_type: type[T]):
|
|
17
|
+
self.path = path
|
|
18
|
+
self.entity_type = entity_type
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
self.last_time = os.path.getmtime(self.path)
|
|
22
|
+
except FileNotFoundError:
|
|
23
|
+
self.last_time = 0
|
|
24
|
+
|
|
25
|
+
self.loaded = None
|
|
26
|
+
self.load()
|
|
27
|
+
|
|
28
|
+
def load(self):
|
|
29
|
+
|
|
30
|
+
if os.path.getmtime(self.path) != self.last_time and self.loaded is not None or self.loaded is None:
|
|
31
|
+
with open(self.path, "r", encoding="utf-8") as f:
|
|
32
|
+
datas = json.load(f)
|
|
33
|
+
self.last_time = os.path.getmtime(self.path)
|
|
34
|
+
|
|
35
|
+
instances = []
|
|
36
|
+
|
|
37
|
+
for data in datas:
|
|
38
|
+
|
|
39
|
+
if callable(getattr(self.entity_type, "builder", None)):
|
|
40
|
+
instance = self.entity_type.builder(data)
|
|
41
|
+
|
|
42
|
+
if isinstance(data, dict) and data.get("ID") is not None and hasattr(instance, "ID"):
|
|
43
|
+
instance.ID = data["ID"]
|
|
44
|
+
reserve_id = getattr(type(instance), "_reserve_id", None)
|
|
45
|
+
if callable(reserve_id):
|
|
46
|
+
reserve_id(data["ID"])
|
|
47
|
+
|
|
48
|
+
instances.append(instance)
|
|
49
|
+
|
|
50
|
+
self.loaded = instances
|
|
51
|
+
return self.loaded
|
|
52
|
+
else:
|
|
53
|
+
return self.loaded
|
|
54
|
+
|
|
55
|
+
@Retry(cathed_exception=json.JSONDecodeError, amount=3)
|
|
56
|
+
def save(self):
|
|
57
|
+
|
|
58
|
+
if self.loaded is not None:
|
|
59
|
+
with open(self.path, "w", encoding="utf-8") as f:
|
|
60
|
+
json.dump([e.toJson() for e in self.loaded if callable(getattr(e, "toJson", None))], f, indent=4)
|
|
61
|
+
|
|
62
|
+
def create(self, entity: Any):
|
|
63
|
+
|
|
64
|
+
if isinstance(entity, self.entity_type):
|
|
65
|
+
if self.loaded is None:
|
|
66
|
+
self.loaded = self.load()
|
|
67
|
+
|
|
68
|
+
self.loaded.append(entity)
|
|
69
|
+
self.save()
|
|
70
|
+
else:
|
|
71
|
+
raise TypeError(f"L'entité est de type {type(entity)}. Le type de la base de données est {self.entity_type}")
|
|
72
|
+
|
|
73
|
+
@ValidateType()
|
|
74
|
+
def update(self, given_id: int, entity: Any):
|
|
75
|
+
|
|
76
|
+
if isinstance(entity, self.entity_type):
|
|
77
|
+
|
|
78
|
+
if self.loaded is None:
|
|
79
|
+
self.loaded = self.load()
|
|
80
|
+
|
|
81
|
+
target = next((e for e in self.loaded if hasattr(e, "ID") and e.ID == given_id), None)
|
|
82
|
+
|
|
83
|
+
if target is not None:
|
|
84
|
+
for index, targets in enumerate(self.loaded):
|
|
85
|
+
if targets.ID == target.ID:
|
|
86
|
+
entity.ID = target.ID
|
|
87
|
+
self.loaded[index] = entity
|
|
88
|
+
break
|
|
89
|
+
|
|
90
|
+
self.save()
|
|
91
|
+
|
|
92
|
+
else:
|
|
93
|
+
raise NameError("Le nom donné ne correspond à aucune entité")
|
|
94
|
+
|
|
95
|
+
else:
|
|
96
|
+
raise TypeError(f"L'entité est de type {type(entity)}. Le type de la base de données est {self.entity_type}")
|
|
97
|
+
|
|
98
|
+
@ValidateType()
|
|
99
|
+
def find_by_id(self, given_id: int):
|
|
100
|
+
|
|
101
|
+
if self.loaded is None:
|
|
102
|
+
self.loaded = self.load()
|
|
103
|
+
|
|
104
|
+
target = next((e for e in self.loaded if hasattr(e, "ID") and e.ID == given_id), None)
|
|
105
|
+
if target is None:
|
|
106
|
+
raise ValueError(f"L'id {given_id} ne correspond à aucune entité")
|
|
107
|
+
|
|
108
|
+
else:
|
|
109
|
+
return target
|
|
110
|
+
|
|
111
|
+
def clear_from_date(self):
|
|
112
|
+
|
|
113
|
+
if self.loaded is None:
|
|
114
|
+
self.loaded = self.load()
|
|
115
|
+
|
|
116
|
+
today = date.today
|
|
117
|
+
|
|
118
|
+
targets = []
|
|
119
|
+
|
|
120
|
+
for entity in self.loaded:
|
|
121
|
+
if hasattr(entity, "date"):
|
|
122
|
+
|
|
123
|
+
if entity.date < today:
|
|
124
|
+
targets.append(entity)
|
|
125
|
+
|
|
126
|
+
for target in targets:
|
|
127
|
+
self.loaded.remove(target)
|
|
128
|
+
|
|
129
|
+
self.save()
|
|
130
|
+
|
|
131
|
+
@ValidateType()
|
|
132
|
+
def delete(self, given_id: int):
|
|
133
|
+
|
|
134
|
+
if self.loaded is None:
|
|
135
|
+
self.loaded = self.load()
|
|
136
|
+
|
|
137
|
+
target_index = next(
|
|
138
|
+
(index for index, entity in enumerate(self.loaded) if hasattr(entity, "ID") and entity.ID == given_id),
|
|
139
|
+
None,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
if target_index is None:
|
|
143
|
+
raise ValueError(f"L'id {given_id} ne correspond à aucune entité")
|
|
144
|
+
|
|
145
|
+
del self.loaded[target_index]
|
|
146
|
+
self.save()
|
|
147
|
+
|
|
148
|
+
def ensure_file(self):
|
|
149
|
+
try:
|
|
150
|
+
with open(self.path, "r", encoding="utf-8") as f:
|
|
151
|
+
pass
|
|
152
|
+
except FileNotFoundError:
|
|
153
|
+
with open(self.path, "w", encoding="utf-8") as f:
|
|
154
|
+
json.dump([], f, indent=4)
|
PyData/database.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import TypeVar
|
|
3
|
+
|
|
4
|
+
from .classes.repository import Repository
|
|
5
|
+
|
|
6
|
+
T = TypeVar("T")
|
|
7
|
+
|
|
8
|
+
def Database(path: str, entity_type: type[T]):
|
|
9
|
+
"""
|
|
10
|
+
Mets à disposition:
|
|
11
|
+
- save : Sauvegarde d'entité
|
|
12
|
+
- delete_by_name: Suppression par le nom si l'entité a cet attribut
|
|
13
|
+
- load: Renvoie la base complète
|
|
14
|
+
- find_by_name : Renvoie une entité par son nom si elle a cet attribut
|
|
15
|
+
- update_by_name: Modifie une entité existante par son nom si elle a cet attribut
|
|
16
|
+
- clear_from_date: Supprime les entité ayant une date dépassée
|
|
17
|
+
- ensure_file: Créer le fichier json à partir du chemin préciser
|
|
18
|
+
"""
|
|
19
|
+
def decorator(cls):
|
|
20
|
+
|
|
21
|
+
def __init__(self, *args, **kwargs):
|
|
22
|
+
return Repository.__init__(
|
|
23
|
+
self,
|
|
24
|
+
path=path,
|
|
25
|
+
entity_type=entity_type
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
repository = type(
|
|
29
|
+
cls.__name__,
|
|
30
|
+
(cls, Repository),
|
|
31
|
+
{
|
|
32
|
+
"__init__" : __init__,
|
|
33
|
+
"path" : path,
|
|
34
|
+
"entity_type" : entity_type
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
return repository
|
|
39
|
+
return decorator
|
PyData/entity.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import pygame
|
|
2
|
+
from enum import Enum
|
|
3
|
+
|
|
4
|
+
def Entity():
|
|
5
|
+
|
|
6
|
+
def decorator(cls):
|
|
7
|
+
|
|
8
|
+
def toJson(self):
|
|
9
|
+
result = {}
|
|
10
|
+
|
|
11
|
+
for key, value in vars(self).items():
|
|
12
|
+
if isinstance(value, pygame.Rect):
|
|
13
|
+
result[key] = {
|
|
14
|
+
"x": value.x,
|
|
15
|
+
"y": value.y,
|
|
16
|
+
"width": value.width,
|
|
17
|
+
"height": value.height
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
elif isinstance(value, Enum):
|
|
21
|
+
result[key] = value.value
|
|
22
|
+
|
|
23
|
+
elif hasattr(value, "toJson"):
|
|
24
|
+
result[key] = value.toJson()
|
|
25
|
+
else:
|
|
26
|
+
result[key] = value
|
|
27
|
+
|
|
28
|
+
return result
|
|
29
|
+
cls.toJson = toJson
|
|
30
|
+
return cls
|
|
31
|
+
return decorator
|
PyData/id.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
def Id(file: str):
|
|
4
|
+
def decorator(cls):
|
|
5
|
+
cls._ID = 1
|
|
6
|
+
|
|
7
|
+
def reserve_id(entity_id):
|
|
8
|
+
if isinstance(entity_id, int):
|
|
9
|
+
cls._ID = max(cls._ID, entity_id + 1)
|
|
10
|
+
|
|
11
|
+
cls._reserve_id = staticmethod(reserve_id)
|
|
12
|
+
|
|
13
|
+
with open(file, "r", encoding="utf-8") as f:
|
|
14
|
+
|
|
15
|
+
for entity in json.load(f):
|
|
16
|
+
|
|
17
|
+
entity_id = entity.get("ID", None)
|
|
18
|
+
|
|
19
|
+
if entity_id is not None:
|
|
20
|
+
reserve_id(entity_id)
|
|
21
|
+
|
|
22
|
+
original_init = cls.__init__
|
|
23
|
+
|
|
24
|
+
def __init__(self, *args, **kwargs):
|
|
25
|
+
original_init(self, *args, **kwargs)
|
|
26
|
+
|
|
27
|
+
entity_id = getattr(self, "ID", None)
|
|
28
|
+
if entity_id is not None:
|
|
29
|
+
reserve_id(entity_id)
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
self.ID = cls._ID
|
|
33
|
+
cls._ID += 1
|
|
34
|
+
|
|
35
|
+
cls.__init__ = __init__
|
|
36
|
+
return cls
|
|
37
|
+
|
|
38
|
+
return decorator
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
PyData/database.py,sha256=arGKuv1T6lRxABSpyvPYX_DXcZJ8mnQb54SFhu4CjWY,1175
|
|
2
|
+
PyData/entity.py,sha256=850bck_8Yh9EneDSwuUL3SbV2zntHmdZGIguB5eWHTc,832
|
|
3
|
+
PyData/id.py,sha256=qfIfi-tFYrEw0eVu_iwPFg4b2qXJJrecN6od-MvlkzA,942
|
|
4
|
+
PyData/classes/repository.py,sha256=sQu_if8_N9dQGCpiekAmW7QiIM3AvXD2MHzq8alTlJg,4897
|
|
5
|
+
pydatatoolskit-1.0.0.dist-info/METADATA,sha256=F-9nmZQ9dK4yQHA_mGhBTJusY7VCK8oDk3R0D2kXJoY,171
|
|
6
|
+
pydatatoolskit-1.0.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
7
|
+
pydatatoolskit-1.0.0.dist-info/top_level.txt,sha256=sjkXs1KOSOZFRyjGnuA56SAYB48pQ_RWqKA5j7sFBZ4,7
|
|
8
|
+
pydatatoolskit-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
PyData
|