nsarchive 2.0.0a6__tar.gz → 2.0.0a8__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.
- {nsarchive-2.0.0a6 → nsarchive-2.0.0a8}/PKG-INFO +1 -1
- {nsarchive-2.0.0a6 → nsarchive-2.0.0a8}/nsarchive/cls/base.py +57 -3
- {nsarchive-2.0.0a6 → nsarchive-2.0.0a8}/nsarchive/cls/entities.py +6 -3
- {nsarchive-2.0.0a6 → nsarchive-2.0.0a8}/nsarchive/instances/_economy.py +1 -1
- {nsarchive-2.0.0a6 → nsarchive-2.0.0a8}/nsarchive/instances/_entities.py +22 -8
- {nsarchive-2.0.0a6 → nsarchive-2.0.0a8}/nsarchive/instances/_republic.py +5 -5
- nsarchive-2.0.0a8/nsarchive/utils.py +26 -0
- {nsarchive-2.0.0a6 → nsarchive-2.0.0a8}/pyproject.toml +1 -1
- nsarchive-2.0.0a6/nsarchive/utils/assets.py +0 -15
- {nsarchive-2.0.0a6 → nsarchive-2.0.0a8}/LICENSE +0 -0
- {nsarchive-2.0.0a6 → nsarchive-2.0.0a8}/README.md +0 -0
- {nsarchive-2.0.0a6 → nsarchive-2.0.0a8}/nsarchive/__init__.py +0 -0
- {nsarchive-2.0.0a6 → nsarchive-2.0.0a8}/nsarchive/assets/default_avatar.png +0 -0
- {nsarchive-2.0.0a6 → nsarchive-2.0.0a8}/nsarchive/cls/archives.py +0 -0
- {nsarchive-2.0.0a6 → nsarchive-2.0.0a8}/nsarchive/cls/economy.py +0 -0
- {nsarchive-2.0.0a6 → nsarchive-2.0.0a8}/nsarchive/cls/exceptions.py +0 -0
- {nsarchive-2.0.0a6 → nsarchive-2.0.0a8}/nsarchive/cls/republic.py +0 -0
@@ -1,3 +1,4 @@
|
|
1
|
+
import json
|
1
2
|
import typing
|
2
3
|
|
3
4
|
from supabase import Client
|
@@ -37,7 +38,7 @@ class Instance:
|
|
37
38
|
def __init__(self, client: Client):
|
38
39
|
self.db = client
|
39
40
|
|
40
|
-
def _select_from_db(self, table: str, key: str, value: str) -> list:
|
41
|
+
def _select_from_db(self, table: str, key: str = None, value: str = None) -> list:
|
41
42
|
"""
|
42
43
|
Récupère des données JSON d'une table Supabase en fonction de l'ID.
|
43
44
|
|
@@ -54,7 +55,10 @@ class Instance:
|
|
54
55
|
- `None` si aucune donnée n'est trouvée
|
55
56
|
"""
|
56
57
|
|
57
|
-
|
58
|
+
if key and value:
|
59
|
+
res = self.db.from_(table).select("*").eq(key, value).execute()
|
60
|
+
else:
|
61
|
+
res = self.db.from_(table).select("*").execute()
|
58
62
|
|
59
63
|
if res.data:
|
60
64
|
return res.data
|
@@ -117,9 +121,59 @@ class Instance:
|
|
117
121
|
if entity is not None:
|
118
122
|
matches.append(entity)
|
119
123
|
|
120
|
-
if
|
124
|
+
if query == {}:
|
125
|
+
matches = [ self._select_from_db(table) ]
|
126
|
+
|
127
|
+
if not matches or (len(matches) != len(query) and query != {}):
|
121
128
|
return []
|
122
129
|
|
123
130
|
_res = [ item for item in matches[0] if all(item in match for match in matches[1:]) ]
|
124
131
|
|
125
132
|
return _res
|
133
|
+
|
134
|
+
def _upload_to_storage(self, bucket: str, data: bytes, path: str, overwrite: bool = False, options: dict = {'content-type': 'image/png'}) -> dict:
|
135
|
+
"""
|
136
|
+
Envoie un fichier dans un bucket Supabase.
|
137
|
+
|
138
|
+
## Paramètres
|
139
|
+
bucket: `str`\n
|
140
|
+
Nom du bucket où le fichier sera stocké
|
141
|
+
data: `bytes`\n
|
142
|
+
Données à uploader
|
143
|
+
path: `str`\n
|
144
|
+
Chemin dans le bucket où le fichier sera stocké
|
145
|
+
|
146
|
+
## Renvoie
|
147
|
+
- `dict` contenant les informations de l'upload si réussi
|
148
|
+
- `None` en cas d'échec
|
149
|
+
"""
|
150
|
+
|
151
|
+
options["upsert"] = json.dumps(overwrite)
|
152
|
+
|
153
|
+
if len(data) > 5 * 1000 ** 3:
|
154
|
+
raise ValueError("La limite d'un fichier à upload est de 1Mo")
|
155
|
+
|
156
|
+
res = self.db.storage.from_(bucket).upload(path, data, options)
|
157
|
+
|
158
|
+
if res.json().get("error"):
|
159
|
+
print("Erreur lors de l'upload:", res["error"])
|
160
|
+
|
161
|
+
return res
|
162
|
+
|
163
|
+
def _download_from_storage(self, bucket: str, path: str) -> bytes:
|
164
|
+
"""
|
165
|
+
Télécharge un fichier depuis le stockage Supabase.
|
166
|
+
|
167
|
+
## Paramètres
|
168
|
+
bucket: `str`\n
|
169
|
+
Nom du bucket où il faut chercher le fichier
|
170
|
+
path: `str`\n
|
171
|
+
Chemin du fichier dans le bucket
|
172
|
+
|
173
|
+
## Renvoie
|
174
|
+
- Le fichier demandé en `bytes`
|
175
|
+
"""
|
176
|
+
|
177
|
+
res = self.db.storage.from_(bucket).download(path)
|
178
|
+
|
179
|
+
return res
|
@@ -3,7 +3,7 @@ import time
|
|
3
3
|
from .exceptions import *
|
4
4
|
from .base import NSID
|
5
5
|
|
6
|
-
from ..
|
6
|
+
from .. import utils
|
7
7
|
|
8
8
|
class PositionPermissions:
|
9
9
|
"""
|
@@ -38,6 +38,9 @@ class Position:
|
|
38
38
|
self.id = id
|
39
39
|
self.permissions: PositionPermissions = PositionPermissions()
|
40
40
|
|
41
|
+
def __repr__(self):
|
42
|
+
return self.id
|
43
|
+
|
41
44
|
class Entity:
|
42
45
|
def __init__(self, id: str | NSID) -> None:
|
43
46
|
self.id: NSID = NSID(id) # ID hexadécimal de l'entité (ou nom dans le cas de l'entreprise)
|
@@ -52,7 +55,7 @@ class Entity:
|
|
52
55
|
|
53
56
|
self.name = new_name
|
54
57
|
|
55
|
-
def set_position(self, position:
|
58
|
+
def set_position(self, position: Position) -> None:
|
56
59
|
self.position = position
|
57
60
|
|
58
61
|
def add_link(self, key: str, value: str | int) -> None:
|
@@ -135,7 +138,7 @@ class Organization(Entity):
|
|
135
138
|
super().__init__(NSID(id))
|
136
139
|
|
137
140
|
self.owner: Entity
|
138
|
-
self.avatar: bytes =
|
141
|
+
self.avatar: bytes = utils.open_asset('default_avatar.png')
|
139
142
|
|
140
143
|
self.certifications: dict = {}
|
141
144
|
self.members: list[GroupMember] = []
|
@@ -85,11 +85,12 @@ class EntityInstance(Instance):
|
|
85
85
|
|
86
86
|
entity.certifications = _data['certifications']
|
87
87
|
entity.parts = _data['parts']
|
88
|
+
entity.avatar = self._download_from_storage('organizations', f"avatars/{entity.id}")
|
88
89
|
else:
|
89
90
|
entity = Entity(id)
|
90
91
|
|
91
92
|
entity.name = _data['name']
|
92
|
-
entity.position = _data['position'] # Métier si c'est un utilisateur, domaine professionnel si c'est un collectif
|
93
|
+
entity.position = self.get_position(_data['position']) # Métier si c'est un utilisateur, domaine professionnel si c'est un collectif
|
93
94
|
entity.registerDate = _data['register_date']
|
94
95
|
|
95
96
|
for key, value in _data.get('additional', {}).items():
|
@@ -114,7 +115,7 @@ class EntityInstance(Instance):
|
|
114
115
|
_data = {
|
115
116
|
'id': entity.id,
|
116
117
|
'name': entity.name,
|
117
|
-
'position': entity.position,
|
118
|
+
'position': entity.position.id,
|
118
119
|
'register_date': entity.registerDate,
|
119
120
|
'additional': {},
|
120
121
|
}
|
@@ -136,7 +137,9 @@ class EntityInstance(Instance):
|
|
136
137
|
'position': member.permission_level
|
137
138
|
}
|
138
139
|
|
139
|
-
_data['members'] += [_member]
|
140
|
+
_data['members'] += [_member]
|
141
|
+
|
142
|
+
self._upload_to_storage('organizations', entity.avatar, f'/avatars/{entity.id}')
|
140
143
|
elif type(entity) == User:
|
141
144
|
_data['xp'] = entity.xp
|
142
145
|
_data['boosts'] = entity.boosts
|
@@ -155,7 +158,7 @@ class EntityInstance(Instance):
|
|
155
158
|
|
156
159
|
self._delete_by_ID('individuals' if isinstance(entity, User) else 'organizations', NSID(entity.id))
|
157
160
|
|
158
|
-
def fetch_entities(self, **query:
|
161
|
+
def fetch_entities(self, **query: typing.Any) -> list[ Entity | User | Organization ]:
|
159
162
|
"""
|
160
163
|
Récupère une liste d'entités en fonction d'une requête.
|
161
164
|
|
@@ -167,8 +170,20 @@ class EntityInstance(Instance):
|
|
167
170
|
- `list[Entity | User | Organization]`
|
168
171
|
"""
|
169
172
|
|
170
|
-
|
171
|
-
|
173
|
+
if "_type" in query.keys():
|
174
|
+
if query["_type"] == "individual":
|
175
|
+
del query["_type"]
|
176
|
+
_res = self.fetch('individuals', **query)
|
177
|
+
elif query["_type"] == "organization":
|
178
|
+
del query["_type"]
|
179
|
+
_res = self.fetch('organizations', **query)
|
180
|
+
else:
|
181
|
+
del query["_type"]
|
182
|
+
_res = self.fetch('individuals', **query)
|
183
|
+
_res.extend(self.fetch('organizations', **query))
|
184
|
+
else:
|
185
|
+
_res = self.fetch('individuals', **query)
|
186
|
+
_res.extend(self.fetch('organizations', **query))
|
172
187
|
|
173
188
|
return [ self.get_entity(NSID(entity['id'])) for entity in _res if entity is not None ]
|
174
189
|
|
@@ -186,7 +201,6 @@ class EntityInstance(Instance):
|
|
186
201
|
|
187
202
|
id = NSID(id)
|
188
203
|
groups = self.fetch_entities(_type = 'organization')
|
189
|
-
groups.extend(self.fetch_entities(_type = 'organization', owner_id = id))
|
190
204
|
|
191
205
|
for group in groups:
|
192
206
|
if group is None:
|
@@ -247,7 +261,7 @@ class EntityInstance(Instance):
|
|
247
261
|
elif type(archive) == Report:
|
248
262
|
_data['_type'] = "report"
|
249
263
|
else:
|
250
|
-
_data['_type'] = "
|
264
|
+
_data['_type'] = "action"
|
251
265
|
|
252
266
|
self._put_in_db('archives', _data)
|
253
267
|
|
@@ -14,8 +14,8 @@ class RepublicInstance(Instance):
|
|
14
14
|
Gère les interactions avec les votes, les archives de la république, et les fonctionnaires.
|
15
15
|
|
16
16
|
## Informations
|
17
|
-
- Résultats des votes: `.Vote | .
|
18
|
-
- Différentes institutions: `.
|
17
|
+
- Résultats des votes et procès: `.Vote | .Referendum | .Lawsuit`
|
18
|
+
- Différentes institutions: `.State | .Administration | .Government | .Assembly | .Court | .PoliceForces`
|
19
19
|
- Occupants des différents rôles et historique de leurs actions: `.Official`
|
20
20
|
"""
|
21
21
|
|
@@ -35,7 +35,7 @@ class RepublicInstance(Instance):
|
|
35
35
|
ID du vote.
|
36
36
|
|
37
37
|
## Renvoie
|
38
|
-
- `.Vote | .
|
38
|
+
- `.Vote | .Referendum | .Lawsuit`
|
39
39
|
"""
|
40
40
|
|
41
41
|
id = NSID(id)
|
@@ -60,7 +60,7 @@ class RepublicInstance(Instance):
|
|
60
60
|
|
61
61
|
return vote
|
62
62
|
|
63
|
-
def save_vote(self, vote: Vote | Referendum) -> None:
|
63
|
+
def save_vote(self, vote: Vote | Referendum | Lawsuit) -> None:
|
64
64
|
"""Sauvegarde un vote dans la base de données."""
|
65
65
|
|
66
66
|
vote.id = NSID(vote.id)
|
@@ -222,7 +222,7 @@ class RepublicInstance(Instance):
|
|
222
222
|
elif type(archive) == Demotion:
|
223
223
|
_data['_type'] = "demotion"
|
224
224
|
else:
|
225
|
-
_data['_type'] = "
|
225
|
+
_data['_type'] = "action"
|
226
226
|
|
227
227
|
self._put_in_db('archives', _data)
|
228
228
|
self._put_in_db('mandate', _data) # Ajouter les archives à celle du mandat actuel
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import io
|
2
|
+
import math
|
3
|
+
import os
|
4
|
+
from PIL import Image
|
5
|
+
|
6
|
+
def open_asset(path: str) -> bytes:
|
7
|
+
curr_dir = os.path.dirname(os.path.abspath(os.path.join(__file__)))
|
8
|
+
asset_path = os.path.join(curr_dir, 'assets', path)
|
9
|
+
|
10
|
+
image = Image.open(asset_path)
|
11
|
+
val = io.BytesIO()
|
12
|
+
|
13
|
+
image.save(val, format = 'PNG')
|
14
|
+
|
15
|
+
return val.getvalue()
|
16
|
+
|
17
|
+
def compress_image(data: bytes, _max: int = 1000 ** 2) -> bytes:
|
18
|
+
img = Image.open(io.BytesIO(data))
|
19
|
+
size = 2 * ( math.floor(math.sqrt(_max),) )
|
20
|
+
|
21
|
+
img.resize(size)
|
22
|
+
|
23
|
+
val = io.BytesIO()
|
24
|
+
img.save(val)
|
25
|
+
|
26
|
+
return val.getvalue()
|
@@ -1,15 +0,0 @@
|
|
1
|
-
import io
|
2
|
-
import os
|
3
|
-
from PIL import Image
|
4
|
-
|
5
|
-
def open(path: str) -> bytes:
|
6
|
-
curr_dir = os.path.dirname(os.path.abspath(os.path.join(__file__)))
|
7
|
-
parent_dir = os.path.dirname(curr_dir)
|
8
|
-
asset_path = os.path.join(parent_dir, 'assets', path)
|
9
|
-
|
10
|
-
image = Image.open(asset_path)
|
11
|
-
val = io.BytesIO()
|
12
|
-
|
13
|
-
image.save(val, format = 'PNG')
|
14
|
-
|
15
|
-
return val.getvalue()
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|