erioon 0.0.1__py3-none-any.whl → 0.0.2__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.
erioon/client.py CHANGED
@@ -1,106 +1,108 @@
1
+ import os
2
+ import json
1
3
  import requests
2
4
  from erioon.database import Database
3
5
 
4
6
  class ErioonClient:
5
- """
6
- Represents an authenticated Erioon client session.
7
-
8
- Handles user login via email and password, and stores either the user's ID
9
- on success or an error message on failure.
10
-
11
- Usage:
12
- >>> client = ErioonClient("<EMAIL>:<PASSWORD>")
13
- """
14
-
15
- def __init__(self, email, password, base_url="https://sdk.erioon.com"):
16
- """
17
- Initializes the client and attempts login.
18
-
19
- Args:
20
- email (str): User email address.
21
- password (str): User password.
22
- base_url (str): The base URL of the Erioon API.
23
- """
7
+ def __init__(self, email, password, base_url="http://127.0.0.1:5000"):
24
8
  self.email = email
25
9
  self.password = password
26
10
  self.base_url = base_url
27
11
  self.user_id = None
28
- self.error = None
12
+ self.error = None
13
+ self.token_path = os.path.expanduser(f"~/.erioon_token_{self._safe_filename(email)}")
29
14
 
30
15
  try:
31
- self.user_id = self.login()
16
+ self.user_id = self._load_or_login()
32
17
  except Exception as e:
33
18
  self.error = str(e)
34
19
 
35
- def login(self):
36
- """
37
- Sends a login request to the Erioon API.
20
+ def _safe_filename(self, text):
21
+ return "".join(c if c.isalnum() else "_" for c in text)
22
+
23
+ def _load_or_login(self):
24
+ # Try to load from local cache
25
+ if os.path.exists(self.token_path):
26
+ with open(self.token_path, "r") as f:
27
+ token_data = json.load(f)
28
+ user_id = token_data.get("user_id")
29
+ if user_id:
30
+ return user_id # ✅ Use cached value without API call
31
+
32
+ # Fallback: login and cache
33
+ return self._do_login_and_cache()
38
34
 
39
- Returns:
40
- str: User ID on successful login.
35
+ def _do_login_and_cache(self):
36
+ user_id = self._login()
37
+ with open(self.token_path, "w") as f:
38
+ json.dump({"user_id": user_id}, f)
39
+ return user_id
41
40
 
42
- Raises:
43
- Exception: If the login fails, with the error message from the server.
44
- """
41
+ def _login(self):
45
42
  url = f"{self.base_url}/login_with_credentials"
46
43
  payload = {"email": self.email, "password": self.password}
47
44
  headers = {"Content-Type": "application/json"}
48
45
 
49
46
  response = requests.post(url, json=payload, headers=headers)
50
-
51
47
  if response.status_code == 200:
52
48
  return response.text.strip()
53
49
  else:
54
- raise Exception(response.text.strip())
55
-
56
- def __str__(self):
57
- """
58
- Called when print() or str() is used on the object.
50
+ raise Exception("Invalid account")
59
51
 
60
- Returns:
61
- str: User ID if authenticated, or error message if not.
62
- """
63
- return self.user_id if self.user_id else self.error
64
-
65
- def __repr__(self):
66
- """
67
- Called in developer tools or when inspecting the object.
68
-
69
- Returns:
70
- str: Formatted string showing either user ID or error.
71
- """
72
- if self.user_id:
73
- return f"<ErioonClient user_id={self.user_id}>"
74
- else:
75
- return f"<ErioonClient error='{self.error}'>"
52
+ def _clear_cached_token(self):
53
+ if os.path.exists(self.token_path):
54
+ os.remove(self.token_path)
55
+ self.user_id = None
76
56
 
77
57
  def __getitem__(self, db_id):
78
- """
79
- Get a Database object by its ID.
80
-
81
- Calls the API to retrieve DB metadata and initializes a Database instance.
82
- """
83
58
  if not self.user_id:
84
59
  raise ValueError("Client not authenticated. Cannot access database.")
85
-
60
+
61
+ try:
62
+ return self._get_database_info(db_id)
63
+ except Exception as e:
64
+ err_msg = str(e).lower()
65
+ # Check if error is a database error mentioning the db_id
66
+ if f"database with {db_id.lower()}" in err_msg or "database" in err_msg:
67
+ # Try relogin once
68
+ self._clear_cached_token()
69
+ try:
70
+ self.user_id = self._do_login_and_cache()
71
+ except Exception:
72
+ return "Login error"
73
+
74
+ # Retry fetching database info
75
+ try:
76
+ return self._get_database_info(db_id)
77
+ except Exception as e2:
78
+ print(f"❌ Database with _id {db_id} ...")
79
+ # Optionally you could also return or raise the error here
80
+ return f"❌ Database with _id {db_id} ..."
81
+ else:
82
+ # Not a DB-related error, just propagate or raise
83
+ raise e
84
+
85
+
86
+ def _get_database_info(self, db_id):
86
87
  payload = {"user_id": self.user_id, "db_id": db_id}
87
88
  headers = {"Content-Type": "application/json"}
88
89
 
89
90
  response = requests.post(f"{self.base_url}/db_info", json=payload, headers=headers)
90
91
 
91
-
92
- if response.status_code != 200:
93
- db_error = response.json()
94
- return db_error["error"]
95
-
96
- db_info = response.json()
97
- return Database(db_info)
92
+ if response.status_code == 200:
93
+ db_info = response.json()
94
+ return Database(self.user_id, db_info)
95
+ else:
96
+ # Try parse error json
97
+ try:
98
+ error_json = response.json()
99
+ error_msg = error_json.get("error", response.text)
100
+ except Exception:
101
+ error_msg = response.text
102
+ raise Exception(error_msg)
98
103
 
99
104
  def __str__(self):
100
105
  return self.user_id if self.user_id else self.error
101
106
 
102
107
  def __repr__(self):
103
- if self.user_id:
104
- return f"<ErioonClient user_id={self.user_id}>"
105
- else:
106
- return f"<ErioonClient error='{self.error}'>"
108
+ return f"<ErioonClient user_id={self.user_id}>" if self.user_id else f"<ErioonClient error='{self.error}'>"
erioon/collection.py ADDED
@@ -0,0 +1,191 @@
1
+ import json
2
+ import requests
3
+ from colorama import Fore, Style, init
4
+
5
+ init(autoreset=True)
6
+
7
+ class Collection:
8
+ def __init__(
9
+ self,
10
+ user_id,
11
+ db_id,
12
+ coll_id,
13
+ metadata,
14
+ base_url: str = "http://127.0.0.1:5000",
15
+ ):
16
+ """
17
+ Initialize a Collection instance.
18
+
19
+ Args:
20
+ user_id (str): Authenticated user ID.
21
+ db_id (str): Database ID.
22
+ coll_id (str): Collection ID.
23
+ metadata (dict): Collection metadata.
24
+ base_url (str): Base URL of the Erioon API.
25
+ """
26
+ self.user_id = user_id
27
+ self.db_id = db_id
28
+ self.coll_id = coll_id
29
+ self.metadata = metadata
30
+ self.base_url = base_url.rstrip("/")
31
+
32
+ def _print_loading(self) -> None:
33
+ """Print a green loading message to the terminal."""
34
+ print(Fore.CYAN + "Erioon is loading..." + Style.RESET_ALL)
35
+
36
+ # ---------- READ ---------- #
37
+ def get_all(self):
38
+ """
39
+ Retrieve all documents from this collection.
40
+
41
+ Usage:
42
+ result = collection.get_all()
43
+ """
44
+ self._print_loading()
45
+ url = f"{self.base_url}/{self.user_id}/{self.db_id}/{self.coll_id}/get_all"
46
+ response = requests.get(url)
47
+ try:
48
+ response.raise_for_status()
49
+ return response.json()
50
+ except requests.HTTPError as e:
51
+ return {"status": "KO", "error": str(e), "response": response.json() if response.content else {}}
52
+
53
+ def get_specific(self, filters: dict | None = None, limit: int = 1000):
54
+ """
55
+ Retrieve documents matching filters.
56
+
57
+ Args:
58
+ filters (dict): Field/value pairs for filtering.
59
+ limit (int): Max number of docs to retrieve (max 500,000).
60
+
61
+ Usage:
62
+ result = collection.get_specific(filters={"name": "John"}, limit=100)
63
+ """
64
+ if limit > 500_000:
65
+ raise ValueError("Limit of 500,000 exceeded")
66
+ self._print_loading()
67
+
68
+ if filters is None:
69
+ filters = {}
70
+
71
+ url = f"{self.base_url}/{self.user_id}/{self.db_id}/{self.coll_id}/get_specific"
72
+ params = {**filters, "limit": limit}
73
+ response = requests.get(url, params=params)
74
+ try:
75
+ response.raise_for_status()
76
+ return response.json()
77
+ except requests.HTTPError as e:
78
+ return {"status": "KO", "error": str(e), "response": response.json() if response.content else {}}
79
+
80
+ # ---------- CREATE ---------- #
81
+ def insert_one(self, document: dict):
82
+ """
83
+ Insert a single document.
84
+
85
+ Args:
86
+ document (dict): The document to insert.
87
+
88
+ Usage:
89
+ new_doc = {"name": "Alice", "age": 25}
90
+ result = collection.insert_one(new_doc)
91
+ """
92
+ self._print_loading()
93
+ url = f"{self.base_url}/{self.user_id}/{self.db_id}/{self.coll_id}/insert_one"
94
+ response = requests.post(url, json=document, headers={"Content-Type": "application/json"})
95
+ try:
96
+ response.raise_for_status()
97
+ return response.json()
98
+ except requests.HTTPError as e:
99
+ return {"status": "KO", "error": str(e), "response": response.json() if response.content else {}}
100
+
101
+ def insert_many(self, documents: list):
102
+ """
103
+ Insert multiple documents at once.
104
+
105
+ Args:
106
+ documents (list): List of dicts representing documents.
107
+
108
+ Usage:
109
+ docs = [{"name": "A"}, {"name": "B"}]
110
+ result = collection.insert_many(docs)
111
+ """
112
+ self._print_loading()
113
+ url = f"{self.base_url}/{self.user_id}/{self.db_id}/{self.coll_id}/insert_many"
114
+ response = requests.post(url, json={"records": documents})
115
+ try:
116
+ response.raise_for_status()
117
+ return response.json()
118
+ except requests.HTTPError as e:
119
+ return {"status": "KO", "error": str(e), "response": response.json() if response.content else {}}
120
+
121
+ # ---------- DELETE ---------- #
122
+ def delete_one(self, filter_query: dict):
123
+ """
124
+ Delete a single document matching the query.
125
+
126
+ Args:
127
+ filter_query (dict): A query to match one document.
128
+
129
+ Usage:
130
+ result = collection.delete_one({"name": "John"})
131
+ """
132
+ self._print_loading()
133
+ url = f"{self.base_url}/{self.user_id}/{self.db_id}/{self.coll_id}/delete_one"
134
+ response = requests.delete(url, json=filter_query)
135
+ try:
136
+ response.raise_for_status()
137
+ return response.json()
138
+ except requests.HTTPError as e:
139
+ return {"status": "KO", "error": str(e), "response": response.json() if response.content else {}}
140
+
141
+ def delete_many(self, filter_query: dict):
142
+ """
143
+ Delete all documents matching the query.
144
+
145
+ Args:
146
+ filter_query (dict): A query to match multiple documents.
147
+
148
+ Usage:
149
+ result = collection.delete_many({"status": "inactive"})
150
+ """
151
+ self._print_loading()
152
+ url = f"{self.base_url}/{self.user_id}/{self.db_id}/{self.coll_id}/delete_many"
153
+ response = requests.delete(url, json=filter_query)
154
+ try:
155
+ response.raise_for_status()
156
+ return response.json()
157
+ except requests.HTTPError as e:
158
+ return {"status": "KO", "error": str(e), "response": response.json() if response.content else {}}
159
+
160
+ # ---------- UPDATE ---------- #
161
+ def update_query(self, filter_query: dict, update_query: dict):
162
+ """
163
+ Update documents matching a filter query.
164
+
165
+ Args:
166
+ filter_query (dict): Query to find documents.
167
+ update_query (dict): Update operations to apply.
168
+
169
+ Usage:
170
+ result = collection.update_query(
171
+ {"age": {"$gt": 30}},
172
+ {"$set": {"status": "senior"}}
173
+ )
174
+ """
175
+ self._print_loading()
176
+ url = f"{self.base_url}/{self.user_id}/{self.db_id}/{self.coll_id}/update_query"
177
+ response = requests.patch(url, json={"filter_query": filter_query, "update_query": update_query})
178
+ try:
179
+ response.raise_for_status()
180
+ return response.json()
181
+ except requests.HTTPError as e:
182
+ return {"status": "KO", "error": str(e), "response": response.json() if response.content else {}}
183
+
184
+ # ---------- dunder helpers ---------- #
185
+ def __str__(self) -> str:
186
+ """Return a human-readable JSON string of collection metadata."""
187
+ return json.dumps(self.metadata, indent=4)
188
+
189
+ def __repr__(self) -> str:
190
+ """Return a developer-friendly representation of the object."""
191
+ return f"<Collection coll_id={self.coll_id}>"
erioon/database.py CHANGED
@@ -1,17 +1,31 @@
1
1
  import json
2
+ from erioon.collection import Collection
2
3
 
3
4
  class Database:
4
- def __init__(self, metadata):
5
+ def __init__(self, user_id, metadata):
6
+ self.user_id = user_id
5
7
  self.metadata = metadata
8
+ self.db_id = metadata.get("database_info", {}).get("_id")
6
9
 
7
10
  def __getitem__(self, collection_id):
8
- """
9
- Will return a Collection object (to be implemented in next steps).
10
- """
11
- raise NotImplementedError("Collection access not implemented yet.")
11
+ try:
12
+ collections = self.metadata.get("database_info", {}).get("collections", {})
13
+ coll_meta = collections.get(collection_id)
14
+
15
+ if not coll_meta:
16
+ return "No collection found"
17
+
18
+ return Collection(
19
+ user_id=self.user_id,
20
+ db_id=self.db_id,
21
+ coll_id=collection_id,
22
+ metadata=coll_meta
23
+ )
24
+ except Exception:
25
+ return "Connection error"
12
26
 
13
27
  def __str__(self):
14
28
  return json.dumps(self.metadata, indent=4)
15
29
 
16
30
  def __repr__(self):
17
- return f"{self.metadata}"
31
+ return f"<Database db_id={self.db_id}>"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: erioon
3
- Version: 0.0.1
3
+ Version: 0.0.2
4
4
  Summary: Erioon SDF for Python
5
5
  Author: Zyber Pireci
6
6
  Author-email: zyber.pireci@erioon.com
@@ -0,0 +1,9 @@
1
+ erioon/auth.py,sha256=-a8g7JZKsQ_yir_iZMYbfhiBJT0hYDmfJSj6B1u3M84,704
2
+ erioon/client.py,sha256=D5QHIdEL-13-dAoT5CITqbW9RqKUGEfswtTY8kTZH8w,3990
3
+ erioon/collection.py,sha256=55CXEdB7ZmuqyHzHa39-SVRk2WJN-n_Yovb9rSlL-fg,6923
4
+ erioon/database.py,sha256=n3arbUCYzJ1WAIh_1hgjJjUuCpyRVRP1quXGkTVgrsQ,953
5
+ erioon-0.0.2.dist-info/LICENSE,sha256=xwnq3DNlZpQyteOK9HvtHRhMdYviXTTaCDljEodFRnQ,569
6
+ erioon-0.0.2.dist-info/METADATA,sha256=0KLMfL0VvGu3Z9igmof68t5jip8CRyUi2zOVmlqLF9Q,715
7
+ erioon-0.0.2.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
8
+ erioon-0.0.2.dist-info/top_level.txt,sha256=yjKEg85X5Q5ot46IMML_xukvIGG5YfdrLWcemjalItc,7
9
+ erioon-0.0.2.dist-info/RECORD,,
@@ -1,8 +0,0 @@
1
- erioon/auth.py,sha256=-a8g7JZKsQ_yir_iZMYbfhiBJT0hYDmfJSj6B1u3M84,704
2
- erioon/client.py,sha256=GNr1q7MM9oxkfWyXFV2wCb78hEIYtIYJc03ziUxQ8Vw,3249
3
- erioon/database.py,sha256=vGCx3SvVpeEHB2kj54VD637A0fHbgpl9DUxMPzQKNH8,465
4
- erioon-0.0.1.dist-info/LICENSE,sha256=xwnq3DNlZpQyteOK9HvtHRhMdYviXTTaCDljEodFRnQ,569
5
- erioon-0.0.1.dist-info/METADATA,sha256=mvV_1TjduIMHHqm5P7UDiNphkR4E42JHlUoadxZ9qjU,715
6
- erioon-0.0.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
7
- erioon-0.0.1.dist-info/top_level.txt,sha256=yjKEg85X5Q5ot46IMML_xukvIGG5YfdrLWcemjalItc,7
8
- erioon-0.0.1.dist-info/RECORD,,
File without changes