nsarchive 3.0.0b1__tar.gz → 3.0.0b2__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.
Files changed (28) hide show
  1. nsarchive-3.0.0b2/PKG-INFO +90 -0
  2. nsarchive-3.0.0b2/README.md +72 -0
  3. {nsarchive-3.0.0b1 → nsarchive-3.0.0b2}/nsarchive/__init__.py +3 -1
  4. nsarchive-3.0.0b2/nsarchive/errors/__init__.py +1 -0
  5. nsarchive-3.0.0b2/nsarchive/errors/_globals.py +29 -0
  6. nsarchive-3.0.0b2/nsarchive/interfaces/_economy.py +111 -0
  7. {nsarchive-3.0.0b1 → nsarchive-3.0.0b2}/nsarchive/interfaces/_entities.py +100 -14
  8. nsarchive-3.0.0b2/nsarchive/interfaces/_justice.py +274 -0
  9. nsarchive-3.0.0b2/nsarchive/interfaces/_state.py +339 -0
  10. {nsarchive-3.0.0b1 → nsarchive-3.0.0b2}/nsarchive/mandate.py +21 -2
  11. {nsarchive-3.0.0b1 → nsarchive-3.0.0b2}/nsarchive/models/base.py +108 -69
  12. nsarchive-3.0.0b2/nsarchive/models/economy.py +179 -0
  13. {nsarchive-3.0.0b1 → nsarchive-3.0.0b2}/nsarchive/models/entities.py +305 -68
  14. {nsarchive-3.0.0b1 → nsarchive-3.0.0b2}/nsarchive/models/justice.py +23 -2
  15. {nsarchive-3.0.0b1 → nsarchive-3.0.0b2}/nsarchive/models/republic.py +44 -4
  16. nsarchive-3.0.0b2/nsarchive/models/state.py +114 -0
  17. {nsarchive-3.0.0b1 → nsarchive-3.0.0b2}/pyproject.toml +1 -1
  18. nsarchive-3.0.0b1/PKG-INFO +0 -21
  19. nsarchive-3.0.0b1/README.md +0 -3
  20. nsarchive-3.0.0b1/nsarchive/interfaces/_economy.py +0 -363
  21. nsarchive-3.0.0b1/nsarchive/interfaces/_justice.py +0 -122
  22. nsarchive-3.0.0b1/nsarchive/interfaces/_state.py +0 -196
  23. nsarchive-3.0.0b1/nsarchive/models/economy.py +0 -250
  24. nsarchive-3.0.0b1/nsarchive/models/state.py +0 -75
  25. {nsarchive-3.0.0b1 → nsarchive-3.0.0b2}/LICENSE +0 -0
  26. {nsarchive-3.0.0b1 → nsarchive-3.0.0b2}/nsarchive/assets/default_avatar.png +0 -0
  27. {nsarchive-3.0.0b1 → nsarchive-3.0.0b2}/nsarchive/models/scale.py +0 -0
  28. {nsarchive-3.0.0b1 → nsarchive-3.0.0b2}/nsarchive/utils.py +0 -0
@@ -0,0 +1,90 @@
1
+ Metadata-Version: 2.3
2
+ Name: nsarchive
3
+ Version: 3.0.0b2
4
+ Summary: API-wrapper pour récupérer des données liées à Nation
5
+ License: GPL-3.0
6
+ Author: happex
7
+ Author-email: 110610727+okayhappex@users.noreply.github.com
8
+ Requires-Python: >=3.10,<4.0
9
+ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Requires-Dist: pillow (>=10.4,<11.0)
16
+ Requires-Dist: requests (>=2.31,<3.0)
17
+ Description-Content-Type: text/markdown
18
+
19
+ # NSArchive v3
20
+
21
+ ## Pré-requis
22
+
23
+ - Python 3.10 ou + (Python 3.13 si possible)
24
+ - Un serveur avec [nation.db](https://github.com/1nserv/nation-db)
25
+ - Deux barres de Twix ou une tasse de thé
26
+
27
+
28
+ ## Avant de démarrer
29
+
30
+ Dans la documentation, vous croiserez souvent des noms de classes comme `.User` ou autres similaires. Le «.» devant le nom de la classe signfie qu'elle appartient au module NSAv3, et qu'il faut donc les interprêter comme `nsarchive.User`. La seule exception est `NSID` qui ne sera pas précédé d'un point mais devra être interprêté de la même manière.
31
+
32
+
33
+ ## Installation
34
+
35
+ L'installation de NSAv3 se fait via pip:
36
+
37
+ ```sh
38
+ pip install nsarchive
39
+ ```
40
+
41
+ La dernière version de nsarchive devrait s'installer. Les dépendances requises pour nsarchive sont `pillow` et `requests` mais celles-ci devraient s'installer en même temps que le module. Vous pourriez également avoir besoin des modules `bcrypt` et `python-dotenv`, ceux-ci devront être installés manuellement.
42
+
43
+
44
+ ### Bonus: Environnement virtuel
45
+
46
+ Il est recommandé mais non obligatoire d'avoir un environnement virtuel (venv) pour votre projet. Sa création se fait comme ceci:
47
+
48
+ ```sh
49
+ python -m venv .venv
50
+ ```
51
+
52
+ N'oubliez pas de l'activer via cette commande pour powershell...
53
+
54
+ ```ps1
55
+ .venv\Scripts\Activate
56
+ ```
57
+
58
+ ...ou cette commande pour les terminaux type UNIX (Bash par exemple)
59
+
60
+ ```sh
61
+ source .venv/Scripts/Activate
62
+ ```
63
+
64
+ ## Prise en main
65
+
66
+ ### Identifier les objets
67
+
68
+ Les objets sont tous identifiables sur NSAv3. Ils ont un identifiant commun appelé NSID (`from nsarchive import NSID`). Cet identifiant n'est rien de plus qu'un nombre hexadécimal. Il peut être utilisé comme un string, dans un print ou un f-string par exemple. Cet identifiant est communément basé sur plusieurs valeurs fixes ou universelles, dont les deux plus fréquentes sont:
69
+ - L'ID Discord de l'objet concerné, dans le cas d'un utilisateur par exemple
70
+ - Le timestamp (secondes depuis 1970) du moment où il a été créé, dans le cas de la plupart des autres objets
71
+
72
+
73
+ ### Interfaces
74
+
75
+ Le module nsarchive est divisé en **4 interfaces**:
76
+ - [Entités](/docs/interfaces/entities.md) (membres, groupes, positions)
77
+ - [Économie](/docs/interfaces/economy.md) (comptes en banque, dettes)
78
+ - [Justice](/docs/interfaces/justice.md) (signalements, procès, sanctions)
79
+ - [État](/docs/interfaces/state.md) (votes, élections)
80
+
81
+ > Les interfaces État et Justice peuvent être confondues et désignées comme République, comme c'était le cas dans les versions précédentes.
82
+
83
+
84
+ Les interfaces ont toutes quatre rôles en commun:
85
+ - Vous authentifier
86
+ - Récupérer des objets
87
+ - Créer des objets
88
+ - Supprimer des objets (Entités uniquement)
89
+
90
+ Une documentation plus détaillée est disponible [ici](/docs/interfaces/README.md).
@@ -0,0 +1,72 @@
1
+ # NSArchive v3
2
+
3
+ ## Pré-requis
4
+
5
+ - Python 3.10 ou + (Python 3.13 si possible)
6
+ - Un serveur avec [nation.db](https://github.com/1nserv/nation-db)
7
+ - Deux barres de Twix ou une tasse de thé
8
+
9
+
10
+ ## Avant de démarrer
11
+
12
+ Dans la documentation, vous croiserez souvent des noms de classes comme `.User` ou autres similaires. Le «.» devant le nom de la classe signfie qu'elle appartient au module NSAv3, et qu'il faut donc les interprêter comme `nsarchive.User`. La seule exception est `NSID` qui ne sera pas précédé d'un point mais devra être interprêté de la même manière.
13
+
14
+
15
+ ## Installation
16
+
17
+ L'installation de NSAv3 se fait via pip:
18
+
19
+ ```sh
20
+ pip install nsarchive
21
+ ```
22
+
23
+ La dernière version de nsarchive devrait s'installer. Les dépendances requises pour nsarchive sont `pillow` et `requests` mais celles-ci devraient s'installer en même temps que le module. Vous pourriez également avoir besoin des modules `bcrypt` et `python-dotenv`, ceux-ci devront être installés manuellement.
24
+
25
+
26
+ ### Bonus: Environnement virtuel
27
+
28
+ Il est recommandé mais non obligatoire d'avoir un environnement virtuel (venv) pour votre projet. Sa création se fait comme ceci:
29
+
30
+ ```sh
31
+ python -m venv .venv
32
+ ```
33
+
34
+ N'oubliez pas de l'activer via cette commande pour powershell...
35
+
36
+ ```ps1
37
+ .venv\Scripts\Activate
38
+ ```
39
+
40
+ ...ou cette commande pour les terminaux type UNIX (Bash par exemple)
41
+
42
+ ```sh
43
+ source .venv/Scripts/Activate
44
+ ```
45
+
46
+ ## Prise en main
47
+
48
+ ### Identifier les objets
49
+
50
+ Les objets sont tous identifiables sur NSAv3. Ils ont un identifiant commun appelé NSID (`from nsarchive import NSID`). Cet identifiant n'est rien de plus qu'un nombre hexadécimal. Il peut être utilisé comme un string, dans un print ou un f-string par exemple. Cet identifiant est communément basé sur plusieurs valeurs fixes ou universelles, dont les deux plus fréquentes sont:
51
+ - L'ID Discord de l'objet concerné, dans le cas d'un utilisateur par exemple
52
+ - Le timestamp (secondes depuis 1970) du moment où il a été créé, dans le cas de la plupart des autres objets
53
+
54
+
55
+ ### Interfaces
56
+
57
+ Le module nsarchive est divisé en **4 interfaces**:
58
+ - [Entités](/docs/interfaces/entities.md) (membres, groupes, positions)
59
+ - [Économie](/docs/interfaces/economy.md) (comptes en banque, dettes)
60
+ - [Justice](/docs/interfaces/justice.md) (signalements, procès, sanctions)
61
+ - [État](/docs/interfaces/state.md) (votes, élections)
62
+
63
+ > Les interfaces État et Justice peuvent être confondues et désignées comme République, comme c'était le cas dans les versions précédentes.
64
+
65
+
66
+ Les interfaces ont toutes quatre rôles en commun:
67
+ - Vous authentifier
68
+ - Récupérer des objets
69
+ - Créer des objets
70
+ - Supprimer des objets (Entités uniquement)
71
+
72
+ Une documentation plus détaillée est disponible [ici](/docs/interfaces/README.md).
@@ -1,7 +1,7 @@
1
1
  """
2
2
  nsarchive - API-wrapper pour récupérer des données liées à Nation.
3
3
 
4
- Version: 3.0.0-beta.1
4
+ Version: 3.0.0-beta.2
5
5
  Date de sortie: 2025-08-06
6
6
  License: GPL-3.0
7
7
  Auteur : happex <110610727+okayhappex@users.noreply.github.com>
@@ -24,6 +24,8 @@ from .models.justice import *
24
24
 
25
25
  from .models.scale import *
26
26
 
27
+ from . import errors
28
+
27
29
  # Import des interfaces
28
30
  from .models.base import Interface
29
31
  from .interfaces._entities import EntityInterface
@@ -0,0 +1 @@
1
+ from . import _globals as globals
@@ -0,0 +1,29 @@
1
+ class ServerDownError(Exception):
2
+ def __init__(self, *args):
3
+ super().__init__(*args)
4
+
5
+ # 4XX
6
+
7
+ class MissingParamError(Exception): # 400
8
+ def __init__(self, *args):
9
+ super().__init__(*args)
10
+
11
+ class InvalidParamError(Exception): # 400
12
+ def __init__(self, *args):
13
+ super().__init__(*args)
14
+
15
+ class AuthError(Exception): # 401
16
+ def __init__(self, *args):
17
+ super().__init__(*args)
18
+
19
+ class PermissionError(Exception): # 403
20
+ def __init__(self, *args):
21
+ super().__init__(*args)
22
+
23
+ class NotFoundError(Exception): # 404
24
+ def __init__(self, *args):
25
+ super().__init__(*args)
26
+
27
+ class ConflictError(Exception): # 409
28
+ def __init__(self, *args):
29
+ super().__init__(*args)
@@ -0,0 +1,111 @@
1
+ import time
2
+
3
+ from ..models.base import *
4
+ from ..models.economy import *
5
+
6
+ from ..models import economy # Pour les default_headers
7
+
8
+ class EconomyInterface(Interface):
9
+ """Interface qui vous permettra d'interagir avec les comptes en banque et les transactions économiques."""
10
+
11
+ def __init__(self, url: str, token: str) -> None:
12
+ super().__init__(url, token)
13
+
14
+ economy.default_headers = self.default_headers
15
+
16
+ """
17
+ ---- COMPTES EN BANQUE ----
18
+ """
19
+
20
+ def get_account(self, id: NSID) -> BankAccount:
21
+ """
22
+ Récupère les informations d'un compte bancaire.
23
+
24
+ ## Paramètres
25
+ id: `NSID`\n
26
+ ID du compte.
27
+
28
+ ## Renvoie
29
+ - `.BankAccount`
30
+ """
31
+
32
+ id = NSID(id)
33
+ res = requests.get(f"{self.url}/bank/accounts/{id}", headers = self.default_headers)
34
+
35
+ if res.status_code == 200:
36
+ _data = res.json()
37
+ else:
38
+ res.raise_for_status()
39
+ return
40
+
41
+ if _data is None:
42
+ return None
43
+
44
+ account = BankAccount(id)
45
+ account._load(_data, self.url, self.default_headers)
46
+
47
+ return account
48
+
49
+ def save_account(self, account: BankAccount) -> str:
50
+ """
51
+ Sauvegarde un compte bancaire dans la base de données.
52
+
53
+ ## Paramètres
54
+ - account: `.BankAccount`\n
55
+ Compte à sauvegarder
56
+ """
57
+
58
+ _data = {
59
+ 'id': NSID(account.id),
60
+ 'amount': account.amount,
61
+ 'frozen': account.frozen,
62
+ 'owner_id': account.owner_id,
63
+ 'bank': account.bank,
64
+ 'income': account.income
65
+ }
66
+
67
+ res = requests.put(f"{self.url}/bank/register_account?owner={_data['owner_id']}", headers = self.default_headers, json = _data)
68
+
69
+ if res.status_code == 200:
70
+ account._url = f"{self.url}/bank/accounts/{account.id}"
71
+ account.id = res.json()['id']
72
+
73
+ return res.json()['digicode']
74
+ else:
75
+ res.raise_for_status()
76
+
77
+ def fetch_accounts(self, **query: typing.Any) -> list[BankAccount]:
78
+ """
79
+ Récupère une liste de comptes en banque en fonction d'une requête.
80
+
81
+ ## Paramètres
82
+ query: `**dict`\n
83
+ La requête pour filtrer les comptes.
84
+
85
+ ## Renvoie
86
+ - `list[.BankAccount]`
87
+ """
88
+
89
+ query = "&".join(f"{k}={ urllib.parse.quote(v) }" for k, v in query.items())
90
+
91
+ _res = requests.get(f"{self.url}/fetch/accounts?{query}", headers = self.default_headers)
92
+
93
+ if _res.status_code == 200:
94
+ _data = _res.json()
95
+ else:
96
+ _res.raise_for_status()
97
+ return []
98
+
99
+ res = []
100
+
101
+ for _acc in _data:
102
+ if not _acc: continue
103
+
104
+ account = BankAccount(_acc["owner_id"])
105
+
106
+ account.id = NSID(_acc['id'])
107
+ account._load(_acc, self.url, self.default_headers)
108
+
109
+ res.append(account)
110
+
111
+ return res
@@ -1,6 +1,8 @@
1
1
  from ..models.base import *
2
2
  from ..models.entities import *
3
3
 
4
+ from .. import errors
5
+
4
6
  from ..models import entities # Pour les default_headers
5
7
 
6
8
  class EntityInterface(Interface):
@@ -41,20 +43,35 @@ class EntityInterface(Interface):
41
43
  id = NSID(id)
42
44
 
43
45
  if _class == "user":
44
- _data = self._get_by_ID('individuals', id)
46
+ res = requests.get(f"{self.url}/model/individuals/{id}", headers = self.default_headers, json = {})
45
47
  elif _class == "group":
46
- _data = self._get_by_ID('organizations', id)
48
+ res = requests.get(f"{self.url}/model/organisations/{id}", headers = self.default_headers, json = {})
47
49
  else:
48
- _data = self._get_by_ID('entities', id)
50
+ res = requests.get(f"{self.url}/model/entities/{id}", headers = self.default_headers, json = {})
51
+
52
+
53
+ # ERREURS
54
+
55
+ if res.status_code == 404:
56
+ return
57
+
58
+ if 500 <= res.status_code < 600:
59
+ res.raise_for_status()
60
+
61
+ if not 200 <= res.status_code < 300:
62
+ print(res.json()['message'])
63
+ return
64
+
49
65
 
50
- if _data is None: # ID inexistant chez les entités
51
- return None
66
+ # TRAITEMENT
67
+
68
+ _data = res.json()
52
69
 
53
70
  if _data['_class'] == 'individuals':
54
71
  entity = User(id)
55
72
  elif _data['_class'] == 'organizations':
56
73
  entity = Organization(id)
57
- else:
74
+ else:
58
75
  entity = Entity(id)
59
76
 
60
77
  entity._load(_data, self.url, self.default_headers)
@@ -82,12 +99,37 @@ class EntityInterface(Interface):
82
99
  else:
83
100
  return
84
101
 
85
- self._put_in_db(
86
- f"/new_model/{_class}?id={id}&name={name}&position={position}&zone={zone}",
102
+ res = requests.put(
103
+ f"{self.url}/new_model/{_class}?id={id}&name={name}&position={position}&zone={zone}",
87
104
  headers = self.default_headers,
88
- use_PUT = True
105
+ json = {}
89
106
  )
90
107
 
108
+
109
+ # ERREURS
110
+
111
+ if 500 <= res.status_code < 600:
112
+ raise errors.globals.ServerDownError()
113
+
114
+ _data = res.json()
115
+
116
+ if res.status_code == 400:
117
+ if _data['message'] == "MissingParam":
118
+ raise errors.globals.MissingParamError(f"Missing parameter '{_data['param']}'.")
119
+ elif _data['message'] == "InvalidParam":
120
+ raise errors.globals.InvalidParamError(f"Invalid parameter '{_data['param']}'.")
121
+ elif _data['message'] == "InvalidToken":
122
+ raise errors.globals.AuthError("Token is not valid.")
123
+
124
+ elif res.status_code == 401:
125
+ raise errors.globals.AuthError(_data['message'])
126
+
127
+ elif res.status_code == 403:
128
+ raise errors.globals.PermissionError(_data['message'])
129
+
130
+
131
+ # TRAITEMENT
132
+
91
133
  entity = self.get_entity(id)
92
134
 
93
135
  if _class == "individuals":
@@ -112,8 +154,27 @@ class EntityInterface(Interface):
112
154
 
113
155
  res = requests.post(f"{entity._url}/delete", headers = self.default_headers)
114
156
 
115
- if res.status_code != 200:
116
- res.raise_for_status()
157
+ if 200 <= res.status_code < 300:
158
+ return
159
+
160
+ if 500 <= res.status_code < 600:
161
+ raise errors.globals.ServerDownError()
162
+
163
+ _data = res.json()
164
+
165
+ if res.status_code == 400:
166
+ if _data['message'] == "MissingParam":
167
+ raise errors.globals.MissingParamError(f"Missing parameter '{_data['param']}'.")
168
+ elif _data['message'] == "InvalidParam":
169
+ raise errors.globals.InvalidParamError(f"Invalid parameter '{_data['param']}'.")
170
+ elif _data['message'] == "InvalidToken":
171
+ raise errors.globals.AuthError("Token is not valid.")
172
+
173
+ elif res.status_code == 401:
174
+ raise errors.globals.AuthError(_data['message'])
175
+
176
+ elif res.status_code == 403:
177
+ raise errors.globals.PermissionError(_data['message'])
117
178
 
118
179
  def fetch_entities(self, **query: typing.Any) -> list[ Entity | User | Organization ]:
119
180
  """
@@ -172,10 +233,35 @@ class EntityInterface(Interface):
172
233
  - `.Position`
173
234
  """
174
235
 
175
- _data = self._get_by_ID('positions', id)
236
+ res = requests.get(f"{self.url}/model/positions/{id}", headers = self.default_headers)
237
+
238
+
239
+ # ERREURS
240
+
241
+ if 500 <= res.status_code < 600:
242
+ raise errors.globals.ServerDownError()
243
+
244
+ _data = res.json()
245
+
246
+ if res.status_code == 400:
247
+ if _data['message'] == "MissingParam":
248
+ raise errors.globals.MissingParamError(f"Missing parameter '{_data['param']}'.")
249
+ elif _data['message'] == "InvalidParam":
250
+ raise errors.globals.InvalidParamError(f"Invalid parameter '{_data['param']}'.")
251
+ elif _data['message'] == "InvalidToken":
252
+ raise errors.globals.AuthError("Token is not valid.")
253
+
254
+ elif res.status_code == 401:
255
+ raise errors.globals.AuthError(_data['message'])
256
+
257
+ elif res.status_code == 403:
258
+ raise errors.globals.PermissionError(_data['message'])
259
+
260
+ elif res.status_code == 404:
261
+ return
262
+
176
263
 
177
- if _data is None:
178
- return None
264
+ # TRAITEMENT
179
265
 
180
266
  position = Position(id)
181
267
  position._load(_data, self.url, self.default_headers)