nsarchive 0.2a0__py3-none-any.whl → 1.0.1__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.
nsarchive/__init__.py CHANGED
@@ -2,137 +2,699 @@ import time
2
2
 
3
3
  import deta
4
4
 
5
+ from .cls.base import *
5
6
  from .cls.entities import *
6
- from .cls.votes import *
7
+ from .cls.archives import *
8
+ from .cls.republic import *
9
+ from .cls.economy import *
7
10
 
8
11
  from .cls.exceptions import *
9
12
 
10
13
  class EntityInstance:
14
+ """
15
+ Instance qui vous permettra d'interagir avec les profils des membres ainsi que les différents métiers et secteurs d'activité.
16
+
17
+ ## Informations disponibles
18
+ - Profil des membres et des entreprises: `.User | .Organization | .Entity`
19
+ - Participation d'un membre à différent votes: `.User | .Organization | .Entity`
20
+ - Appartenance et permissions d'un membre dans un groupe: `.GroupMember.MemberPermissions`
21
+ - Position légale et permissions d'une entité: `.Position.Permissions`
22
+ - Sanctions et modifications d'une entité: `.Action[ .AdminAction | .Sanction ]`
23
+ """
11
24
  def __init__(self, token: str) -> None:
12
25
  self.db = deta.Deta(token)
13
26
  self.base = self.db.Base('entities')
14
27
  self.electors = self.db.Base('electors')
28
+ self.archives = self.db.Base('archives')
29
+ self.avatars = self.db.Drive('avatars')
30
+ self.positions = self.db.Base('positions') # Liste des métiers
31
+
32
+ def get_entity(self, id: str | NSID) -> User | Organization | Entity:
33
+ """
34
+ Fonction permettant de récupérer le profil public d'une entité.\n
35
+
36
+ ## Paramètres
37
+ id: `NSID`\n
38
+ ID héxadécimal de l'entité à récupérer
39
+
40
+ ## Renvoie
41
+ - `.User` dans le cas où l'entité choisie est un membre
42
+ - `.Organization` dans le cas où c'est un groupe
43
+ - `.Entity` dans le cas où c'est indéterminé
44
+ """
15
45
 
16
- def get_entity(self, id: str) -> User | Organization | Entity:
17
- id = id.upper()
46
+ id = NSID(id)
18
47
  _data = self.base.get(id)
48
+ _votes = self.electors.get(id)
19
49
 
20
50
  if _data is None:
21
51
  return Entity("0")
22
52
 
23
53
  if _data['_type'] == 'user':
24
54
  entity = User(id)
55
+
56
+ entity.xp = _data['xp']
57
+ entity.boosts = _data['boosts']
58
+ entity.votes = [ NSID(vote) for vote in _votes['votes'] ]
25
59
  elif _data['_type'] == 'organization':
26
60
  entity = Organization(id)
61
+
62
+ entity.owner = self.get_entity(NSID(_data['owner_id']))
63
+
64
+ for _member in _data['members']:
65
+ member = GroupMember(_member['id'])
66
+ member.permissions.__dict__ = _member['permissions']
67
+
68
+ try:
69
+ entity.avatar = self.avatars.get(id).read()
70
+ except:
71
+ entity.avatar = None
72
+
73
+ entity.certifications = _data['certifications']
27
74
  else:
28
75
  entity = Entity(id)
29
76
 
30
77
  entity.name = _data['name']
31
78
  entity.legalPosition = _data['legalPosition'] # Métier si c'est un utilisateur, domaine professionnel si c'est un collectif
32
79
  entity.registerDate = _data['registerDate']
33
- entity.xp = _data['xp']
34
80
 
35
- if type(entity) == Organization:
36
- entity.owner = self.get_entity(_data['owner_id'].upper())
37
- entity.members = [
38
- self.get_entity(_id) for _id in _data['members']
39
- ]
40
-
41
- entity.certifications = _data['certifications']
42
- elif type(entity) == User:
43
- entity.boosts = _data['boosts']
44
-
45
81
  return entity
46
82
 
47
83
  def save_entity(self, entity: Entity) -> None:
84
+ """
85
+ Fonction permettant de créer ou modifier une entité.
86
+
87
+ ## Paramètres
88
+ entity: `.Entity` ( `.User | .Organization` )
89
+ L'entité à sauvegarder
90
+ """
91
+
92
+ entity.id = NSID(entity.id)
93
+
48
94
  _base = self.base
49
95
  _data = {
50
96
  '_type': 'user' if type(entity) == User else 'organization' if type(entity) == Organization else 'unknown',
51
97
  'name': entity.name,
52
98
  'legalPosition': entity.legalPosition,
53
- 'registerDate': entity.registerDate,
54
- 'xp': entity.xp
99
+ 'registerDate': entity.registerDate
55
100
  }
56
101
 
57
102
  if type(entity) == Organization:
58
- _data['owner_id'] = entity.owner.id.upper() if entity.owner else Entity("0")
59
- _data['members'] = [ member.id.upper() for member in entity.members ] if entity.members else []
103
+ _data['owner_id'] = NSID(entity.owner.id) if entity.owner else NSID("0")
104
+ _data['members'] = []
60
105
  _data['certifications'] = entity.certifications
106
+
107
+ for member in entity.members:
108
+ _member = {
109
+ 'id': NSID(member.id),
110
+ 'permissions': member.permissions.__dict__.copy()
111
+ }
112
+
113
+ _data['members'] += _member
114
+
115
+ self.avatars.put(name = entity.id, data = entity.avatar)
61
116
  elif type(entity) == User:
117
+ _data['xp'] = entity.xp
62
118
  _data['boosts'] = entity.boosts
63
119
 
64
- _base.put(_data, entity.id.upper(), expire_in = 3 * 31536000) # Données supprimées tous les trois ans
120
+ _base.put(_data, entity.id, expire_in = 3 * 31536000) # Données supprimées tous les trois ans
65
121
 
66
- def get_elector(self, id: str) -> Elector:
67
- id = id.upper()
68
- _data = self.electors.get(id)
69
-
70
- if _data is None:
71
- return Elector('0')
72
-
73
- elector = Elector(id)
74
- elector.votes = _data['votes']
122
+ def delete_entity(self, entity: Entity) -> None:
123
+ """
124
+ Fonction permettant de supprimer le profil d'une entité
75
125
 
76
- return elector
126
+ ## Paramètres
127
+ entity: `.Entity` ( `.User | .Organization` )
128
+ L'entité à supprimer
129
+ """
77
130
 
78
- def save_elector(self, elector: Elector):
79
- _data = {
80
- "votes": elector.votes
81
- }
131
+ self.base.delete(NSID(entity.id))
132
+
133
+ if type(entity) == Organization:
134
+ self.avatars.delete(NSID(entity.id))
135
+
136
+ def fetch_entities(self, query: dict = None, listquery: dict | None = None) -> list[ Entity | User | Organization ]:
137
+ """
138
+ Récupère une liste d'entités en fonction d'une requête.
82
139
 
83
- self.electors.put(_data, elector.id.upper())
140
+ ## Paramètres
141
+ query: `dict`
142
+ La requête pour filtrer les entités.
143
+ listquery: `dict | None`
144
+ Requête secondaire pour n'afficher que les listes contenant un certain élément.
84
145
 
85
- def fetch(self, query = None, listquery: dict | None = None) -> list:
146
+ ## Renvoie
147
+ - `list[Entity | User | Organization]`
148
+ """
86
149
  _res = self.base.fetch(query).items
87
150
 
88
151
  if listquery is not None:
89
152
  for item in _res:
90
- for target, value in listquery:
153
+ for target, value in listquery.items():
91
154
  if value not in item[target]:
92
155
  _res.remove(item)
156
+
157
+ return [ self.get_entity(NSID(entity['key'])) for entity in _res ]
158
+
159
+ def get_entity_groups(self, id: str | NSID) -> list[Organization]:
160
+ """
161
+ Récupère les groupes auxquels appartient une entité.
162
+
163
+ ## Paramètres
164
+ id: `str | NSID`
165
+ ID de l'entité.
166
+
167
+ ## Renvoie
168
+ - `list[Organization]`
169
+ """
170
+
171
+ id = NSID(id)
172
+ groups = self.fetch_entities({'_type': 'organization'}, {'members': id})
173
+
174
+ return groups
175
+
176
+ def get_position(self, id: str) -> Position:
177
+ """
178
+ Récupère une position légale (métier, domaine professionnel).
179
+
180
+ ## Paramètres
181
+ id: `str`
182
+ ID de la position (SENSIBLE À LA CASSE !)
183
+
184
+ ## Renvoie
185
+ - `.Position`
186
+ """
187
+
188
+ _data = self.positions.get(id)
189
+
190
+ if _data is None:
191
+ raise RessourceNotFoundError(f"No position with ID {id}")
192
+
193
+ position = Position(id)
194
+ position.name = _data['name']
195
+
196
+ for _permission in _data['permissions']:
197
+ position.permissions.__setattr__(_permission, True)
198
+
199
+ return position
200
+
201
+ def _add_archive(self, archive: Action) -> None:
202
+ """
203
+ Ajoute une archive d'une action (modification au sein d'un groupe ou sanction) dans la base de données.
204
+ """
205
+
206
+ archive.id = NSID(archive.id)
207
+ archive.author = NSID(archive.author)
208
+ archive.target = NSID(archive.target)
209
+
210
+ _data = archive.__dict__.copy()
211
+
212
+ if type(archive) == Sanction:
213
+ _data['type'] = "sanction"
214
+ elif type(archive) == AdminAction:
215
+ _data['type'] = "adminaction"
216
+ else:
217
+ _data['type'] = "unknown"
93
218
 
94
- return _res
219
+ self.archives.put(key = archive.id, data = _data)
220
+
221
+ def _get_archive(self, id: str | NSID) -> Action | Sanction | AdminAction:
222
+ """
223
+ Récupère une archive spécifique.
224
+
225
+ ## Paramètres
226
+ id: `str | NSID`
227
+ ID de l'archive.
228
+
229
+ ## Renvoie
230
+ - `.Action | .Sanction | .AdminAction`
231
+ """
95
232
 
96
- def get_entity_groups(self, id: int) -> list[Organization]:
97
- groups = self.fetch({'_type': 'organization'}, {'members': str(id)})
233
+ id = NSID(id)
234
+ _data = self.archives.get(id)
235
+
236
+ if _data is None:
237
+ return None
98
238
 
99
- return [ self.get_entity(int(group['id'], 16)) for group in groups ]
239
+ if _data['type'] == "sanction": # Mute, ban, GAV, kick, détention, prune (xp seulement)
240
+ archive = Sanction(_data['author'], _data['target'])
241
+
242
+ archive.details = _data['details']
243
+ archive.major = _data['major']
244
+ archive.duration = _data['duration']
245
+ elif _data['type'] == "adminaction": # Renommage, promotion, démotion (au niveau de l'état)
246
+ archive = AdminAction(_data['author'], _data['target'])
247
+
248
+ archive.details = _data['details']
249
+ archive.new_state = _data['new_state']
250
+ else:
251
+ archive = Action(_data['author'], _data['target'])
252
+
253
+ archive.id = id
254
+ archive.action = _data['action']
255
+ archive.date = _data['date']
256
+
257
+ return archive
258
+
259
+ def _fetch_archives(self, **query) -> list[ Action | Sanction | AdminAction ]:
260
+ """
261
+ Récupère une liste d'archives correspondant à la requête.
262
+
263
+ ## Paramètres
264
+ query: `dict`
265
+ Requête pour filtrer les archives.
266
+
267
+ ## Renvoie
268
+ - `list[Action | Sanction | AdminAction]`
269
+ """
270
+
271
+ _res = self.archives.fetch(query).items
272
+
273
+ return [ self._get_archive(archive['key']) for archive in _res ]
100
274
 
101
275
  class RepublicInstance:
276
+ """
277
+ Gère les interactions avec les votes, les archives de la république, et les fonctionnaires.
278
+
279
+ ## Informations
280
+ - Résultats des votes: `.Vote | .ClosedVote`
281
+ - Différentes institutions: `.Institutions | .Administration | .Government | .Assembly | .Court | .PoliceForces`
282
+ - Occupants des différents rôles et historique de leurs actions: `.Official`
283
+ """
284
+
102
285
  def __init__(self, token: str) -> None:
286
+ """Initialise une nouvelle RepublicInstance avec un token Deta."""
287
+
103
288
  self.db = deta.Deta(token)
104
289
  self.votes = self.db.Base('votes')
290
+ self.archives = self.db.Base('archives')
291
+ self.mandate = self.db.Base('mandate')
292
+ self.functions = self.db.Base('functions') # Liste des fonctionnaires
293
+
294
+ def get_vote(self, id: str | NSID) -> Vote | ClosedVote:
295
+ """
296
+ Récupère un vote spécifique.
297
+
298
+ ## Paramètres
299
+ id: `str | NSID`
300
+ ID du vote.
105
301
 
106
- def get_vote(self, id: str) -> Vote | ClosedVote:
107
- id = id.upper()
302
+ ## Renvoie
303
+ - `.Vote | .ClosedVote`
304
+ """
305
+
306
+ id = NSID(id)
108
307
  _data = self.votes.get(id)
109
308
 
110
309
  if _data is None:
111
- return None
112
-
310
+ raise RessourceNotFoundError(f"The vote #{id} does not exist.")
311
+
113
312
  if _data['_type'] == 'open':
114
313
  vote = Vote(id, _data['title'], tuple(_data['choices'].keys()))
115
314
  elif _data['_type'] == 'closed':
116
315
  vote = ClosedVote(id, _data['title'])
117
316
  else:
118
317
  vote = Vote('0', 'Unknown Vote', ())
119
-
318
+
120
319
  vote.author = _data['author']
121
320
  vote.startDate = _data['startDate']
122
321
  vote.endDate = _data['endDate']
123
322
  vote.choices = _data['choices']
124
-
323
+
125
324
  return vote
126
325
 
127
- def save_vote(self, vote: Vote | ClosedVote):
128
- _base = self.base
326
+ def save_vote(self, vote: Vote | ClosedVote) -> None:
327
+ """Sauvegarde un vote dans la base de données."""
328
+
329
+ vote.id = NSID(vote.id)
330
+
129
331
  _data = {
130
332
  '_type': 'open' if type(vote) == Vote else 'closed' if type(vote) == ClosedVote else 'unknown',
131
333
  'title': vote.title,
132
- 'author': vote.author,
334
+ 'author': NSID(vote.author),
133
335
  'startDate': vote.startDate,
134
336
  'endDate': vote.endDate,
135
337
  'choices': vote.choices
136
338
  }
137
339
 
138
- _base.put(_data, vote.id.upper())
340
+ self.votes.put(_data, vote.id)
341
+
342
+ def get_official(self, id: str | NSID, current_mandate: bool = True) -> Official:
343
+ """
344
+ Récupère les informations d'un fonctionnaire (mandats, contributions).
345
+
346
+ ## Paramètres
347
+ id: `str | NSID`
348
+ ID du fonctionnaire.
349
+ current_mandate: `bool`
350
+ Indique si l'on doit récupérer le mandat actuel ou les anciens mandats.
351
+
352
+ ## Renvoie
353
+ - `.Official`
354
+ """
355
+
356
+ id = NSID(id)
357
+
358
+ archives = self.mandate if current_mandate else self.archives
359
+
360
+ _contributions = archives.fetch({'author': id, 'type': 'contrib'}).items
361
+ _mandates = archives.fetch({'target': id, 'type': 'election'}).items\
362
+ + archives.fetch({'target': id, 'type': 'promotion'}).items
363
+
364
+ user = Official(id)
365
+ for mandate in _mandates:
366
+ if mandate['position'].startswith('MIN'):
367
+ mandate['position'] = 'MIN'
368
+
369
+ try:
370
+ user.mandates[mandate['position']] += 1
371
+ except KeyError:
372
+ user.mandates[mandate['position']] = 1
373
+
374
+ for contrib in _contributions:
375
+ try:
376
+ user.contributions[contrib['action']] += 1
377
+ except KeyError:
378
+ user.contributions[contrib['action']] = 1
379
+
380
+ return user
381
+
382
+ def get_institutions(self) -> Organization:
383
+ """Récupère l'état actuel des institutions de la république."""
384
+
385
+ admin = Administration()
386
+ gov = Government(Official('0'))
387
+ assembly = Assembly()
388
+ court = Court()
389
+ police_forces = PoliceForces()
390
+
391
+ _admins = self.functions.get('ADMIN')
392
+ admin.members = [ self.get_official(user) for user in _admins['users'] ]
393
+ admin.president = self.get_official('F7DB60DD1C4300A') # happex (remplace Kheops pour l'instant)
394
+
395
+ gov.president = self.get_official(self.functions.get('PRE_REP')['users'][0])
396
+
397
+ minister = lambda code : self.get_official(self.functions.get(f'MIN_{code}')['users'][0])
398
+ gov.prime_minister = minister('PRIM')
399
+ gov.economy_minister = minister('ECO')
400
+ gov.inner_minister = minister('INN')
401
+ gov.press_minister = minister('AUD')
402
+ gov.justice_minister = minister('JUS')
403
+ gov.outer_minister = minister('OUT')
404
+
405
+ assembly.president = self.get_official(self.functions.get('PRE_AS')['users'][0])
406
+ assembly.members = [ self.get_official(id) for id in self.functions.get('REPR')['users'] ]
407
+
408
+ court.president = gov.justice_minister
409
+ court.members = [ self.get_official(id) for id in self.functions.get('JUDGE')['users'] ]
410
+
411
+ police_forces.president = gov.inner_minister
412
+ police_forces.members = [ self.get_official(id) for id in self.functions.get('POLICE')['users'] ]
413
+
414
+ instits = Institutions()
415
+ instits.administration = admin
416
+ instits.government = gov
417
+ instits.court = court
418
+ instits.assembly = assembly
419
+ instits.police = police_forces
420
+
421
+ return instits
422
+
423
+ def update_institutions(self, institutions: Institutions):
424
+ """
425
+ Fonction communément appelée après un vote législatif ou une nomination.\n
426
+ Celle-ci met à jour: Le gouvernement (président, ministres), les différents députés et leur président, les différents juges, les différents policiers.\n
427
+
428
+ ## Paramètres
429
+ institutions: `.Institutions`\n
430
+ Le nouvel état des institutions, à sauvegarder.
431
+ """
432
+
433
+ get_ids = lambda institution : [ member.id for member in institutions.__getattribute__(institution).members ]
434
+
435
+ self.functions.put(key = 'ADMIN', data = { 'users': get_ids('administration') })
436
+ self.functions.put(key = 'REPR', data = { 'users': get_ids('assembly') })
437
+ self.functions.put(key = 'JUDGE', data = { 'users': get_ids('court') })
438
+ self.functions.put(key = 'POLICE', data = { 'users': get_ids('police') })
439
+
440
+ self.functions.put(key = 'PRE_AS', data = { 'users': [ institutions.assembly.president.id ] })
441
+ self.functions.put(key = 'PRE_REP', data = { 'users': [ institutions.government.president.id ] })
442
+
443
+ self.functions.put(key = 'MIN_PRIM', data = { 'users': [ institutions.government.prime_minister.id ] })
444
+ self.functions.put(key = 'MIN_INN', data = { 'users': [ institutions.government.inner_minister.id ] })
445
+ self.functions.put(key = 'MIN_JUS', data = { 'users': [ institutions.government.justice_minister.id ] })
446
+ self.functions.put(key = 'MIN_ECO', data = { 'users': [ institutions.government.economy_minister.id ] })
447
+ self.functions.put(key = 'MIN_AUD', data = { 'users': [ institutions.government.press_minister.id ] })
448
+ self.functions.put(key = 'MIN_OUT', data = { 'users': [ institutions.government.outer_minister.id ] })
449
+
450
+ def new_mandate(self, institutions: Institutions, weeks: int = 4) -> None:
451
+ """
452
+ Fonction qui amène à supprimer toutes les archives du mandat précédent
453
+ """
454
+
455
+ for item in self.mandate.fetch().items:
456
+ if item['date'] >= round(time.time()) - weeks * 604800: # On évite de supprimer les informations écrites lors de la période définie
457
+ self.mandate.delete(item['id'])
458
+
459
+ self.update_institutions(institutions)
460
+
461
+ def _add_archive(self, archive: Action) -> None:
462
+ """Ajoute une archive d'une action (élection, promotion, ou rétrogradation) dans la base de données."""
463
+
464
+ archive.id = NSID(archive.id)
465
+ _data = archive.__dict__.copy()
466
+
467
+ if type(archive) == Election:
468
+ _data['type'] = "election"
469
+ elif type(archive) == Promotion:
470
+ _data['type'] = "promotion"
471
+ elif type(archive) == Demotion:
472
+ _data['type'] = "demotion"
473
+ else:
474
+ _data['type'] = "unknown"
475
+
476
+ self.archives.put(key = archive.id, data = _data)
477
+ self.mandate.put(key = archive.id, data = _data) # Ajouter les archives à celle du mandat actuel
478
+
479
+ def _get_archive(self, id: str | NSID) -> Action | Election | Promotion | Demotion:
480
+ """
481
+ Récupère une archive spécifique.
482
+
483
+ ## Paramètres
484
+ id: `str | NSID`
485
+ ID de l'archive.
486
+
487
+ ## Renvoie
488
+ - `.Action | .Election | .Promotion | .Demotion`
489
+ """
490
+
491
+ id = NSID(id)
492
+ _data = self.archives.get(id)
493
+
494
+ if _data is None:
495
+ return None
496
+
497
+ if _data['type'] == "election":
498
+ archive = Election(_data['author'], _data['target'], _data['position'])
499
+
500
+ archive.positive_votes = _data['positive_votes']
501
+ archive.total_votes = _data['total_votes']
502
+ elif _data['type'] == "promotion":
503
+ archive = Promotion(_data['author'], _data['target'], _data['position'])
504
+ elif _data['type'] == "demotion":
505
+ archive = Demotion(_data['author'], _data['target'])
506
+
507
+ archive.reason = _data['reason']
508
+ else:
509
+ archive = Action(_data['author'], _data['target'])
510
+
511
+ archive.id = id
512
+ archive.action = _data['action']
513
+ archive.date = _data['date']
514
+
515
+ return archive
516
+
517
+ def _fetch_archives(self, **query) -> list[ Action | Election | Promotion | Demotion ]:
518
+ """
519
+ Récupère une liste d'archives correspondant à la requête.
520
+
521
+ ## Paramètres
522
+ query: `dict`
523
+ Requête pour filtrer les archives.
524
+
525
+ ## Renvoie
526
+ - `list[Action | Election | Promotion | Demotion]`
527
+ """
528
+
529
+ _res = self.archives.fetch(query).items
530
+
531
+ return [ self._get_archive(archive['key']) for archive in _res ]
532
+
533
+ class BankInstance:
534
+ """Gère les interactions avec les comptes bancaires, les transactions, et le marché."""
535
+
536
+ def __init__(self, token: str) -> None:
537
+ self.db = deta.Deta(token)
538
+ self.archives = self.db.Base('archives')
539
+ self.accounts = self.db.Base('accounts')
540
+ self.registry = self.db.Base('banks')
541
+ self.marketplace = self.db.Base('shop')
542
+
543
+ def get_account(self, id: str | NSID) -> BankAccount:
544
+ """
545
+ Récupère les informations d'un compte bancaire.
546
+
547
+ ## Paramètres
548
+ id: `str | NSID`
549
+ ID du compte.
550
+
551
+ ## Renvoie
552
+ - `.BankAccount`
553
+ """
554
+
555
+ id = NSID(id)
556
+ _data = self.accounts.get(id)
557
+
558
+ if _data is None:
559
+ return None
560
+
561
+ account = BankAccount(id)
562
+ account.amount = _data['amount']
563
+ account.locked = _data['locked']
564
+ account.owner = _data['owner_id']
565
+ account.bank = _data['bank']
566
+
567
+ return account
568
+
569
+ def save_account(self, account: BankAccount):
570
+ """Sauvegarde un compte bancaire dans la base de données."""
571
+
572
+ _data = {
573
+ 'amount': account.amount,
574
+ 'locked': account.locked,
575
+ 'owner_id': account.owner,
576
+ 'bank': account.bank
577
+ }
578
+
579
+ self.accounts.put(_data, NSID(account.id))
580
+
581
+ def lock_account(self, account: BankAccount):
582
+ """Verrouille un compte bancaire pour empêcher toute transaction."""
583
+
584
+ account.id = account.id.upper()
585
+ account.locked = True
586
+
587
+ self.save_account(account)
588
+
589
+ def get_item(self, id: str | NSID) -> Item | None:
590
+ """
591
+ Récupère un item du marché.
592
+
593
+ ## Paramètres
594
+ id: `str | NSID`
595
+ ID de l'item.
596
+
597
+ ## Renvoie
598
+ - `.Item | None`
599
+ """
600
+
601
+ id = NSID(id)
602
+
603
+ _data = self.marketplace.get(id)
604
+
605
+ if _data is None:
606
+ return None
607
+
608
+ item = Item(id)
609
+ item.title = _data['title']
610
+ item.emoji = _data['emoji']
611
+ item.seller_id = _data['seller']
612
+ item.price = _data['price']
613
+
614
+ return item
615
+
616
+ def save_item(self, item: Item) -> None:
617
+ """Sauvegarde un item dans la base de données du marché."""
618
+
619
+ item.id = NSID(item.id)
620
+
621
+ _data = item.__dict__.copy()
622
+
623
+ self.marketplace.put(key = item.id, data = _data)
624
+
625
+ def delete_item(self, item: Item) -> None:
626
+ """Supprime un item du marché."""
627
+
628
+ item.id = NSID(item.id)
629
+ self.marketplace.delete(item.id)
630
+
631
+ def _add_archive(self, archive: Action):
632
+ """Ajoute une archive d'une transaction ou d'une vente dans la base de données."""
633
+
634
+ archive.id = NSID(archive.id)
635
+ archive.author = NSID(archive.author)
636
+ archive.target = NSID(archive.target)
637
+
638
+ _data = archive.__dict__.copy()
639
+
640
+ if type(archive) == Transaction:
641
+ _data['type'] = "transaction"
642
+ archive.currency = archive.currency.upper()
643
+ elif type(archive) == Sale:
644
+ _data['type'] = "sale"
645
+ else:
646
+ _data['type'] = "unknown"
647
+
648
+ self.archives.put(key = archive.id, data = _data)
649
+
650
+ def _get_archive(self, id: str | NSID) -> Action | Transaction:
651
+ """
652
+ Récupère une archive spécifique.
653
+
654
+ ## Paramètres
655
+ id: `str | NSID`
656
+ ID de l'archive.
657
+
658
+ ## Renvoie
659
+ - `.Action | .Transaction`
660
+ """
661
+
662
+ id = NSID(id)
663
+ _data = self.archives.get(id)
664
+
665
+ if _data is None:
666
+ return None
667
+
668
+ if _data['type'] == "transaction":
669
+ archive = Transaction(_data['author'], _data['target'], _data['amount'])
670
+
671
+ archive.reason = _data['reason']
672
+ archive.currency = _data['currency']
673
+ elif _data['type'] == "sale":
674
+ archive = Sale(_data['author'], _data['target'])
675
+
676
+ archive.price = _data['price']
677
+ else:
678
+ archive = Action(_data['author'], _data['target'])
679
+
680
+ archive.id = id
681
+ archive.action = _data['action']
682
+ archive.date = _data['date']
683
+
684
+ return archive
685
+
686
+ def _fetch_archives(self, **query) -> list[ Action | Transaction ]:
687
+ """
688
+ Récupère une liste d'archives correspondant à la requête.
689
+
690
+ ## Paramètres
691
+ query: `dict`
692
+ Requête pour filtrer les archives.
693
+
694
+ ## Renvoie
695
+ - `list[Action | Transaction]`
696
+ """
697
+
698
+ _res = self.archives.fetch(query).items
699
+
700
+ return [ self._get_archive(archive['key']) for archive in _res ]
Binary file
@@ -0,0 +1,70 @@
1
+ import time
2
+
3
+ from .base import *
4
+
5
+ class Action:
6
+ def __init__(self, author: str | NSID = '0', target: str | NSID = '0') -> None:
7
+ self.date: int = round(time.time())
8
+
9
+ self.id: NSID = NSID(self.date)
10
+ self.action: str = ""
11
+ self.author: NSID = NSID(author)
12
+ self.target: NSID = NSID(target)
13
+
14
+
15
+ # Entities
16
+
17
+ class Sanction(Action):
18
+ def __init__(self, author: str | NSID, target: str | NSID) -> None:
19
+ super().__init__(author, target)
20
+
21
+ self.details: str = ""
22
+ self.major: bool = False # Sanction majeure ou non
23
+ self.duration: int = 0 # Durée en secondes, 0 = définitif
24
+
25
+ class AdminAction(Action):
26
+ def __init__(self, author: str | NSID, target: str | NSID) -> None:
27
+ super().__init__(author, target)
28
+
29
+ self.details: str = ""
30
+ self.new_state: str | int | bool = None
31
+
32
+
33
+ # Community
34
+
35
+ class Election(Action):
36
+ def __init__(self, author: str | NSID, target: str | NSID, position: str) -> None:
37
+ super().__init__(author, target)
38
+
39
+ self.position: str = position
40
+ self.positive_votes: int = 0
41
+ self.total_votes: int = 0
42
+
43
+ class Promotion(Action):
44
+ def __init__(self, author: str | NSID, target: str | NSID, position: str) -> None:
45
+ super().__init__(author, target)
46
+
47
+ self.position: str = position
48
+
49
+ class Demotion(Action):
50
+ def __init__(self, author: str | NSID, target: str | NSID) -> None:
51
+ super().__init__(author, target)
52
+
53
+ self.reason: str = None
54
+
55
+
56
+ # Bank
57
+
58
+ class Transaction(Action):
59
+ def __init__(self, author: str | NSID, target: str | NSID, amount: int) -> None:
60
+ super().__init__(author, target)
61
+
62
+ self.amount: int = amount
63
+ self.currency: str = 'HC'
64
+ self.reason: str = None
65
+
66
+ class Sale(Action):
67
+ def __init__(self, author: str | NSID = '0', target: str | NSID = '0') -> None:
68
+ super().__init__(author, target)
69
+
70
+ self.price: int = 0
nsarchive/cls/base.py ADDED
@@ -0,0 +1,27 @@
1
+ class NSID(str):
2
+ unknown = "0"
3
+ admin = "01"
4
+ gov = "02"
5
+ assembly = "03"
6
+ court = "04"
7
+ office = "05"
8
+ hexabank = "06"
9
+ archives = "07"
10
+
11
+ maintenance_committee = "101"
12
+ audiovisual_department = "102"
13
+ interior_department = "103"
14
+
15
+ def __new__(cls, value):
16
+ if type(value) == int:
17
+ value = hex(value)
18
+ elif type(value) in (str, NSID):
19
+ value = hex(int(value, 16))
20
+ else:
21
+ return TypeError(f"<{value}> is not NSID serializable")
22
+
23
+ if value.startswith("0x"):
24
+ value = value[2:]
25
+
26
+ instance = super(NSID, cls).__new__(cls, value.upper())
27
+ return instance
@@ -0,0 +1,20 @@
1
+ from .base import *
2
+
3
+ class BankAccount:
4
+ def __init__(self, id: str | NSID) -> None:
5
+ self.id: NSID = NSID(id)
6
+ self.owner: NSID = NSID(0)
7
+ self.amount: int = 0
8
+ self.locked: bool = False
9
+ self.bank: str = "HexaBank"
10
+
11
+ self.income: int = 0
12
+
13
+ class Item:
14
+ def __init__(self, id: str | NSID) -> None:
15
+ self.id: NSID = NSID(id)
16
+ self.title: str = "Unknown Object"
17
+ self.emoji: str = ":light_bulb:"
18
+
19
+ self.seller_id: NSID = NSID(0)
20
+ self.price: int = 0
nsarchive/cls/entities.py CHANGED
@@ -1,14 +1,69 @@
1
+ import io
1
2
  import time
2
3
 
3
4
  from .exceptions import *
5
+ from .base import *
4
6
 
7
+ from ..utils import assets
8
+
9
+ class PositionPermissions:
10
+ """
11
+ Permissions d'une position à l'échelle du serveur. Certaines sont attribuées selon l'appartenance à divers groupes ayant une position précise
12
+ """
13
+
14
+ def __init__(self) -> None:
15
+ # Membres
16
+ self.approve_laws = False # Approuver ou désapprouver les lois proposées (vaut aussi pour la Constitution)
17
+ self.buy_items = False # Acheter des items depuis le marketplace
18
+ self.create_organizations = False # Créer une organisation
19
+ self.edit_constitution = False # Proposer une modification de la Constitution
20
+ self.edit_laws = False # Proposer une modification des différents textes de loi
21
+ self.manage_entities = False # Gérer les membres et les organisations
22
+ self.manage_national_channel = False # Prendre la parole sur la chaîne nationale et avoir une priorité de passage sur les autres chaînes
23
+ self.manage_state_budgets = False # Gérer les différents budgets de l'État
24
+ self.moderate_members = False # Envoyer des membres en garde à vue, en détention ou toute autre sanction non présente sur le client Discord
25
+ self.propose_new_laws = self.edit_constitution # Proposer un nouveau texte de loi pris en charge par la Constitution
26
+ self.publish_official_messages = False # Publier un message sous l'identité du bot Serveur
27
+ self.sell_items = False # Vendre des objets ou services sur le marketplace
28
+ self.vote_president = False # Participer aux élections présidentielles
29
+ self.vote_representatives = False # Participer aux élections législatives
30
+
31
+ def edit(self, **permissions: bool) -> None:
32
+ for perm in permissions.values():
33
+ self.__setattr__(*perm)
34
+
35
+ class Position:
36
+ def __init__(self, id: str = 'inconnu') -> None:
37
+ self.name: str = "Inconnue"
38
+ self.id = id
39
+ self.permissions: PositionPermissions = PositionPermissions()
5
40
  class Entity:
6
- def __init__(self, id: str):
7
- self.id: str = id # ID hexadécimal de l'entité
8
- self.name: str = 'Entité Inconnue'
41
+ def __init__(self, id: str | NSID) -> None:
42
+ self.id: NSID = NSID(id) # ID hexadécimal de l'entité (ou nom dans le cas de l'entreprise)
43
+ self.name: str = "Entité Inconnue"
9
44
  self.registerDate: int = 0
10
- self.legalPosition: int = 'Normal'
45
+ self.legalPosition: Position = Position()
46
+
47
+ def set_name(self, new_name: str) -> None:
48
+ if len(new_name) > 32:
49
+ raise NameTooLongError(f"Name length mustn't exceed 32 characters.")
50
+
51
+ self.name = new_name
52
+
53
+ def set_position(self, position: str) -> None:
54
+ self.legalPosition = position
55
+
56
+ class User(Entity):
57
+ def __init__(self, id: str | NSID) -> None:
58
+ super().__init__(NSID(id))
59
+
11
60
  self.xp: int = 0
61
+ self.boosts: dict[str, int] = {}
62
+ self.permissions: PositionPermissions = PositionPermissions() # Elles seront définies en récupérant les permissions de sa position
63
+ self.votes: list[str] = []
64
+
65
+ def add_vote(self, id: str | NSID):
66
+ self.votes.append(NSID(id))
12
67
 
13
68
  def get_level(self) -> None:
14
69
  i = 0
@@ -16,75 +71,78 @@ class Entity:
16
71
  i += 1
17
72
 
18
73
  return i
19
-
20
- def rename(self, new_name: str) -> None:
21
- if len(new_name) > 32:
22
- raise NameTooLongError(f"Name length mustn't exceed 32 characters.")
23
-
24
- self.name = new_name
25
-
26
- def set_position(self, position: str) -> None:
27
- self.legalPosition = position
28
-
74
+
29
75
  def add_xp(self, amount: int) -> None:
30
76
  boost = 0 if 0 in self.boosts.values() else max(list(self.boosts.values()) + [ 1 ])
31
77
 
32
78
  self.xp += amount * boost
33
-
34
- class User(Entity):
35
- def __init__(self, id: str):
36
- super().__init__(id)
37
79
 
38
- self.boosts: dict[str, int] = {}
39
-
40
80
  def edit_boost(self, name: str, multiplier: int = -1) -> None:
41
81
  if multiplier >= 0:
42
82
  self.boosts[name] = multiplier
43
83
  else:
44
84
  del self.boosts[name]
45
85
 
86
+ class MemberPermissions:
87
+ """
88
+ Permissions d'un utilisateur à l'échelle d'un groupe
89
+ """
90
+
91
+ def __init__(self) -> None:
92
+ self.manage_organization = False # Renommer ou changer le logo
93
+ self.manage_members = False # Virer quelqu'un d'une entreprise, l'y inviter, changer ses rôles
94
+ self.manage_roles = False # Promouvoir ou rétrograder les membres
95
+
96
+ def edit(self, **permissions: bool) -> None:
97
+ for perm in permissions.values():
98
+ self.__setattr__(*perm)
99
+
100
+ class GroupMember():
101
+ def __init__(self, id: str | NSID) -> None:
102
+ self.id: NSID = NSID(id)
103
+ self.permissions: MemberPermissions = MemberPermissions()
104
+ self.position: str = 'membre'
105
+
106
+ class Official:
107
+ def __init__(self, id: str | NSID) -> None:
108
+ self.id: NSID = NSID(id)
109
+
110
+ self.mandates: int = {
111
+ 'PRE_REP': 0, # Président de la République
112
+ 'MIN': 0, # Différents ministres
113
+ 'PRE_AS': 0, # Président de l'Assemblée Nationale
114
+ 'JUDGE': 0, # Juge
115
+ 'REPR': 0 # Député
116
+ }
117
+
118
+ self.contributions: dict = {
119
+ 'project': 0,
120
+ 'approved_project': 0,
121
+ 'admin_action': 0,
122
+ 'law_vote': 0
123
+ }
124
+
46
125
  class Organization(Entity):
47
- def __init__(self, id: str):
48
- super().__init__(id)
126
+ def __init__(self, id: str | NSID) -> None:
127
+ super().__init__(NSID(id))
49
128
 
50
129
  self.owner: Entity
51
130
  self.certifications: dict = {}
52
- self.members: list = []
53
-
131
+ self.members: list[GroupMember] = []
132
+ self.avatar: bytes = assets.open('default_avatar.png')
133
+
54
134
  def add_certification(self, certif: str) -> None:
55
135
  self.certifications[certif] = round(time.time())
56
-
57
- def add_member(self, member: User) -> None:
136
+
137
+ def add_member(self, member: GroupMember) -> None:
58
138
  self.members.append(member)
59
-
60
- def remove_member(self, member: User) -> None:
139
+
140
+ def remove_member(self, member: GroupMember) -> None:
61
141
  if member in self.members:
62
142
  self.members.remove(member)
63
-
143
+
64
144
  def set_owner(self, member: User) -> None:
65
145
  self.owner = member
66
146
 
67
- class Elector(User):
68
- def __init__(self, id: str) -> None:
69
- self.id: str = id
70
- self.votes: list[str] = []
71
-
72
- class FunctionalUser(User):
73
- def __init__(self, id: str):
74
- super().__init__(id)
75
-
76
- self.permissions: dict = {
77
- 'approve_project': False,
78
- 'create_org': False,
79
- 'destroy_gov': False,
80
- 'destroy_org': False,
81
- 'propose_projects': False
82
- }
83
-
84
- self.mandates: int = 0
85
- self.contribs: dict = {
86
- 'projects': 0,
87
- 'approved_projects': 0,
88
- 'admin_actions': 0,
89
- 'law_votes': 0
90
- }
147
+ def get_member_id(self) -> list[str]:
148
+ return [ member.id for member in self.members ]
@@ -8,9 +8,18 @@ class EntityTypeError(Exception):
8
8
  def __init__(self, *args: object) -> None:
9
9
  super().__init__(*args)
10
10
 
11
+ class AvatarTooLongError(Exception):
12
+ def __init__(self, *args: object) -> None:
13
+ super().__init__(*args)
11
14
 
12
15
  # Exceptions pour le vote
13
16
 
14
17
  class AlreadyVotedError(Exception):
18
+ def __init__(self, *args: object) -> None:
19
+ super().__init__(*args)
20
+
21
+ # Ressource pas trouvée
22
+
23
+ class RessourceNotFoundError(Exception):
15
24
  def __init__(self, *args: object) -> None:
16
25
  super().__init__(*args)
@@ -0,0 +1,60 @@
1
+ from .entities import *
2
+
3
+ # Votes
4
+
5
+ class Vote:
6
+ def __init__(self, id: str | NSID, title: str, choices: tuple[str]) -> None:
7
+ self.id: NSID = NSID(id)
8
+ self.title: str = title
9
+ self.choices = { choice : 0 for choice in choices }
10
+ self.author: str = '0'
11
+ self.startDate: int = 0
12
+ self.endDate: int = 0
13
+
14
+ class ClosedVote(Vote):
15
+ def __init__(self, id: str | NSID, title: str) -> None:
16
+ super().__init__(id, title, ('yes', 'no', 'blank'))
17
+
18
+
19
+ # Institutions (defs)
20
+
21
+ class Administration:
22
+ def __init__(self) -> None:
23
+ self.president: Official
24
+ self.members: list[Official]
25
+
26
+ class Government:
27
+ def __init__(self, president: Official) -> None:
28
+ self.president: Official = president
29
+ self.prime_minister: Official
30
+
31
+ self.inner_minister: Official
32
+ self.economy_minister: Official
33
+ self.justice_minister: Official
34
+ self.press_minister: Official
35
+ self.outer_minister: Official
36
+
37
+ class Assembly:
38
+ def __init__(self) -> None:
39
+ self.president: Official
40
+ self.members: list[Official]
41
+
42
+ class Court:
43
+ def __init__(self) -> None:
44
+ self.president: Official
45
+ # On discutera de la mise en place d'un potentiel président. Pour l'instant c'est le Ministre de la Justice
46
+ self.members: list[Official]
47
+
48
+ class PoliceForces:
49
+ def __init__(self) -> None:
50
+ self.president: Official
51
+ self.members: list[Official]
52
+
53
+ class Institutions:
54
+ def __init__(self) -> None:
55
+ self.administration: Administration
56
+ self.government: Government
57
+ self.court: Court
58
+ self.assembly: Assembly
59
+ self.police: PoliceForces
60
+
@@ -0,0 +1,15 @@
1
+ import io
2
+ import os
3
+ from PIL import Image
4
+
5
+ def open(path: str) -> Image:
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()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nsarchive
3
- Version: 0.2a0
3
+ Version: 1.0.1
4
4
  Summary:
5
5
  License: GPL-3.0
6
6
  Author: happex
@@ -11,6 +11,8 @@ Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3.10
12
12
  Classifier: Programming Language :: Python :: 3.11
13
13
  Classifier: Programming Language :: Python :: 3.12
14
+ Requires-Dist: deta (>=1.2,<2.0)
15
+ Requires-Dist: pillow (>=10.4,<11.0)
14
16
  Description-Content-Type: text/markdown
15
17
 
16
18
  # nsarchive
@@ -76,7 +78,7 @@ entity_instance.save_entity(entity)
76
78
  Vous pouvez rechercher des entités avec des critères spécifiques.
77
79
 
78
80
  ```python
79
- entities = entity_instance.fetch(query = {'name': 'Alice'})
81
+ entities = entity_instance.fetch_entity(query = {'name': 'Alice'})
80
82
  for entity in entities:
81
83
  print(entity['name'])
82
84
  ```
@@ -0,0 +1,13 @@
1
+ nsarchive/__init__.py,sha256=WAGTyFj-bQEgkz7tW8KTCIYqEMKq6RvjP6sp-9RaGp0,23899
2
+ nsarchive/assets/default_avatar.png,sha256=n-4vG_WPke8LvbY3ZU6oA-H-OtRoIu7woKnRq9DCIlI,51764
3
+ nsarchive/cls/archives.py,sha256=P3xOg7D7og-Vnf3VQWeIwmJRD9g7dVQBYChvMIMM6i8,2008
4
+ nsarchive/cls/base.py,sha256=z7NTvrtbeaUF1T_o-J7PL2N8axkmJek9EoKcQ8fTF9I,715
5
+ nsarchive/cls/economy.py,sha256=RjFu3VzNunazslbd4Ia5p1Jr-jJP5CvIcbVOzkDTwTU,549
6
+ nsarchive/cls/entities.py,sha256=zz0JTphgtXpCIVH8dgb3hxc55nxwvXF3m2yIXgp_Q9k,5743
7
+ nsarchive/cls/exceptions.py,sha256=QN6Qn7cxTkGoC4lO50hBAq4gZCgo7scQvCkb-xKl6Xs,692
8
+ nsarchive/cls/republic.py,sha256=FphoO9TBE9hyfdjcePjQHqBw7NFT74f-avE9T5qTTuE,1750
9
+ nsarchive/utils/assets.py,sha256=hd0STSpa0yT-OJlUyI_wCYXJqcBiUMQds2pZTXNNg9c,382
10
+ nsarchive-1.0.1.dist-info/LICENSE,sha256=aFLFZg6LEJFpTlNQ8su3__jw4GfV-xWBmC1cePkKZVw,35802
11
+ nsarchive-1.0.1.dist-info/METADATA,sha256=CdilT_3C-x4b20ccl_uwdyiWEEAXotfrJsrhQXlKgI4,5629
12
+ nsarchive-1.0.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
13
+ nsarchive-1.0.1.dist-info/RECORD,,
nsarchive/cls/votes.py DELETED
@@ -1,16 +0,0 @@
1
- import time
2
-
3
- from .entities import FunctionalUser
4
-
5
- class Vote:
6
- def __init__(self, id: str, title: str, choices: tuple[str]) -> None:
7
- self.id: str = id
8
- self.title: str = title
9
- self.choices = { choice : 0 for choice in choices }
10
- self.author: FunctionalUser
11
- self.startDate: int = 0
12
- self.endDate: int = 0
13
-
14
- class ClosedVote(Vote):
15
- def __init__(self, id: str, title: str) -> None:
16
- super().__init__(id, title, ('yes', 'no', 'blank'))
@@ -1,8 +0,0 @@
1
- nsarchive/__init__.py,sha256=0eu5Tf7ZuUmMQeeZ001ePNWBisoz1njUA2__Ruy6wjU,4600
2
- nsarchive/cls/entities.py,sha256=mhBHZBtYK1E2dfTDoFHNdw1uu5QsnOc-Utuu5EjtV7c,2556
3
- nsarchive/cls/exceptions.py,sha256=TrH9PvHhVZi7wap9ZfBLGRWJY3OBCYgWAMnco5uadYY,420
4
- nsarchive/cls/votes.py,sha256=vASgf9ies8YPPlKBoaQdB5jB_Sp4GS_Onl8hv3_7HJU,507
5
- nsarchive-0.2a0.dist-info/LICENSE,sha256=aFLFZg6LEJFpTlNQ8su3__jw4GfV-xWBmC1cePkKZVw,35802
6
- nsarchive-0.2a0.dist-info/METADATA,sha256=xlZA4CPlK_RrfnzpnPApuAKhrTZgPBef9TI-8VxplIw,5552
7
- nsarchive-0.2a0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
8
- nsarchive-0.2a0.dist-info/RECORD,,