autonomous-app 0.2.25__py3-none-any.whl → 0.3.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.
Files changed (59) hide show
  1. autonomous/__init__.py +5 -2
  2. autonomous/ai/audioagent.py +32 -0
  3. autonomous/ai/imageagent.py +31 -0
  4. autonomous/ai/jsonagent.py +40 -0
  5. autonomous/ai/models/__init__.py +0 -0
  6. autonomous/ai/models/openai.py +308 -0
  7. autonomous/ai/oaiagent.py +20 -194
  8. autonomous/ai/textagent.py +35 -0
  9. autonomous/auth/autoauth.py +11 -11
  10. autonomous/auth/user.py +24 -11
  11. autonomous/db/__init__.py +41 -0
  12. autonomous/db/base/__init__.py +33 -0
  13. autonomous/db/base/common.py +62 -0
  14. autonomous/db/base/datastructures.py +476 -0
  15. autonomous/db/base/document.py +1230 -0
  16. autonomous/db/base/fields.py +767 -0
  17. autonomous/db/base/metaclasses.py +468 -0
  18. autonomous/db/base/utils.py +22 -0
  19. autonomous/db/common.py +79 -0
  20. autonomous/db/connection.py +472 -0
  21. autonomous/db/context_managers.py +313 -0
  22. autonomous/db/dereference.py +291 -0
  23. autonomous/db/document.py +1141 -0
  24. autonomous/db/errors.py +165 -0
  25. autonomous/db/fields.py +2732 -0
  26. autonomous/db/mongodb_support.py +24 -0
  27. autonomous/db/pymongo_support.py +80 -0
  28. autonomous/db/queryset/__init__.py +28 -0
  29. autonomous/db/queryset/base.py +2033 -0
  30. autonomous/db/queryset/field_list.py +88 -0
  31. autonomous/db/queryset/manager.py +58 -0
  32. autonomous/db/queryset/queryset.py +189 -0
  33. autonomous/db/queryset/transform.py +527 -0
  34. autonomous/db/queryset/visitor.py +189 -0
  35. autonomous/db/signals.py +59 -0
  36. autonomous/logger.py +3 -0
  37. autonomous/model/autoattr.py +120 -0
  38. autonomous/model/automodel.py +121 -308
  39. autonomous/storage/imagestorage.py +9 -54
  40. autonomous/tasks/autotask.py +0 -25
  41. {autonomous_app-0.2.25.dist-info → autonomous_app-0.3.1.dist-info}/METADATA +7 -8
  42. autonomous_app-0.3.1.dist-info/RECORD +60 -0
  43. {autonomous_app-0.2.25.dist-info → autonomous_app-0.3.1.dist-info}/WHEEL +1 -1
  44. autonomous/db/autodb.py +0 -86
  45. autonomous/db/table.py +0 -156
  46. autonomous/errors/__init__.py +0 -1
  47. autonomous/errors/danglingreferenceerror.py +0 -8
  48. autonomous/model/autoattribute.py +0 -20
  49. autonomous/model/orm.py +0 -86
  50. autonomous/model/serializer.py +0 -110
  51. autonomous_app-0.2.25.dist-info/RECORD +0 -36
  52. /autonomous/{storage → apis}/version_control/GHCallbacks.py +0 -0
  53. /autonomous/{storage → apis}/version_control/GHOrganization.py +0 -0
  54. /autonomous/{storage → apis}/version_control/GHRepo.py +0 -0
  55. /autonomous/{storage → apis}/version_control/GHVersionControl.py +0 -0
  56. /autonomous/{storage → apis}/version_control/__init__.py +0 -0
  57. /autonomous/{storage → utils}/markdown.py +0 -0
  58. {autonomous_app-0.2.25.dist-info → autonomous_app-0.3.1.dist-info}/LICENSE +0 -0
  59. {autonomous_app-0.2.25.dist-info → autonomous_app-0.3.1.dist-info}/top_level.txt +0 -0
@@ -1,221 +1,57 @@
1
- # opt : Optional[str] # for optional attributes
2
- # default : Optional[str] = "value" # for default values
3
- import copy
4
1
  import importlib
5
-
6
- # import traceback
7
- from abc import ABC
2
+ import os
3
+ import urllib.parse
8
4
  from datetime import datetime
9
5
 
10
- from autonomous import log
11
- from autonomous.errors import DanglingReferenceError
12
-
13
- from .autoattribute import AutoAttribute
14
- from .orm import ORM
15
- from .serializer import AutoDecoder, AutoEncoder
16
-
17
-
18
- class DelayedModel:
19
- def __init__(self, model, pk):
20
- # log(model, pk)
21
- assert model
22
- assert pk
23
- module_name, class_name = model.rsplit(".", 1)
24
- try:
25
- module = importlib.import_module(module_name)
26
- model = getattr(module, class_name)
27
- except (ModuleNotFoundError, AttributeError) as e:
28
- # stack = traceback.extract_stack()
29
- # function_names = [
30
- # f"{frame.filename}:{frame.lineno} - {frame.name} "
31
- # for frame in stack[:-1]
32
- # if "__" not in frame.filename
33
- # ]
34
- # log(e, *function_names)
35
- raise DanglingReferenceError(model, pk, None)
36
- else:
37
- object.__setattr__(self, "_delayed_model", model)
38
- object.__setattr__(self, "_delayed_pk", pk)
39
- object.__setattr__(self, "_delayed_obj", None)
40
-
41
- def _instance(self):
42
- #### DO NOT TO ANY LOGGING IN THIS METHOD; IT CAUSES INFINITE RECURSION ####
43
- if not object.__getattribute__(self, "_delayed_obj"):
44
- _pk = object.__getattribute__(self, "_delayed_pk")
45
- _model = object.__getattribute__(self, "_delayed_model")
46
- _obj = _model.get(_pk)
47
- if not _pk or not _model or _obj is None:
48
- raise DanglingReferenceError(_model, _pk, _obj)
49
- else:
50
- object.__setattr__(self, "_delayed_obj", _obj)
51
-
52
- return object.__getattribute__(self, "_delayed_obj")
53
-
54
- def __getattribute__(self, name):
55
- # log(name)
56
- if name in [
57
- "_delayed_model",
58
- "_delayed_pk",
59
- "_delayed_obj",
60
- "_instance",
61
- ]:
62
- return object.__getattribute__(self, name)
63
- try:
64
- return object.__getattribute__(self._instance(), name)
65
- except DanglingReferenceError as e:
66
- log(e)
67
- return None
68
-
69
- def __setattr__(self, name, value):
70
- if name.startswith("_delayed"):
71
- object.__setattr__(self, name, value)
72
- else:
73
- setattr(self._instance(), name, value)
74
-
75
- def __delattr__(self, name):
76
- delattr(self._instance(), name)
77
-
78
- def __nonzero__(self):
79
- return bool(self._instance())
80
-
81
- def __str__(self):
82
- return str(self._instance().__dict__)
83
-
84
- def __repr__(self):
85
- msg = f"\n<<DelayedModel {self._delayed_model.__name__}:{self._delayed_pk}>>"
86
- return msg
87
-
88
- def __hash__(self):
89
- return hash(self._instance())
6
+ from bson import ObjectId
90
7
 
8
+ from autonomous import log
9
+ from autonomous.db import Document, connect, signals
10
+ from autonomous.db.errors import ValidationError
11
+ from autonomous.db.fields import DateTimeField
12
+ from autonomous.model.autoattr import DictAttr, ListAttr
91
13
 
92
- class AutoModel(ABC):
93
- attributes = {}
94
- _table_name = ""
95
- _table = None
96
- _orm = ORM
14
+ host = os.getenv("DB_HOST", "db")
15
+ port = os.getenv("DB_PORT", 27017)
16
+ password = urllib.parse.quote_plus(str(os.getenv("DB_PASSWORD")))
17
+ username = urllib.parse.quote_plus(str(os.getenv("DB_USERNAME")))
18
+ dbname = os.getenv("DB_DB")
19
+ # log(f"Connecting to MongoDB at {host}:{port} with {username}:{password} for {dbname}")
20
+ connect(host=f"mongodb://{username}:{password}@{host}:{port}/{dbname}?authSource=admin")
21
+ # log(f"{db}")
97
22
 
98
- def __new__(cls, *args, **kwargs):
99
- """
100
- Create a new instance of the AutoModel.
101
23
 
102
- This method is responsible for creating a new instance of the AutoModel class.
103
- It sets default attributes, populates the object from the database if a primary key is provided,
104
- and handles additional keyword arguments.
105
-
106
- Args:
107
- cls: The class itself.
108
- *args: Positional arguments.
109
- **kwargs: Keyword arguments, including 'pk' for primary key.
110
-
111
- Returns:
112
- obj: The created AutoModel instance.
113
- """
114
- obj = super().__new__(cls)
115
- pk = kwargs.pop("pk", None)
116
- # set default attributes
117
- # Get model data from database
118
- result = cls.table().get(pk) or {}
119
-
120
- # set object attributes
121
- for k, v in cls.attributes.items():
122
- if isinstance(v, AutoAttribute):
123
- v = v.default
124
- setattr(obj, k, result.get(k, copy.deepcopy(v)))
125
- obj.pk = pk
126
- for key, val in list(kwargs.items()):
127
- if (
128
- getattr(cls, key, None)
129
- and getattr(cls, key).fset
130
- and f"_{key}" in cls.attributes
131
- ):
132
- kwargs[f"_{key}"] = kwargs.pop(key)
133
- obj.__dict__ |= kwargs
134
- # breakpoint()
135
- obj.__dict__ = AutoDecoder.decode(obj.__dict__)
136
- obj._automodel = obj.model_name(qualified=True)
137
- obj.last_updated = datetime.now()
138
- return obj
139
-
140
- def __getattribute__(self, name):
141
- obj = super().__getattribute__(name)
142
- if not name.startswith("__"):
143
- if isinstance(obj, DelayedModel):
144
- try:
145
- result = obj._instance()
146
- except DanglingReferenceError as e:
147
- log(e)
148
- super().__setattr__(name, None)
149
- return None
150
- else:
151
- super().__setattr__(name, result)
152
-
153
- elif isinstance(obj, list):
154
- results = []
155
- scrubbed = False
156
- for i, item in enumerate(obj):
157
- if isinstance(item, DelayedModel):
158
- try:
159
- result = item._instance()
160
- except DanglingReferenceError as e:
161
- log(e)
162
- scrubbed = True
163
- else:
164
- results.append(result)
165
- if scrubbed:
166
- super().__setattr__(name, results)
167
- obj = results
168
-
169
- elif isinstance(obj, dict):
170
- results = {}
171
- scrubbed = False
172
- for key, item in obj.items():
173
- if isinstance(item, DelayedModel):
174
- try:
175
- result = item._instance()
176
- except DanglingReferenceError as e:
177
- log(e)
178
- scrubbed = True
179
- else:
180
- results[key] = result
181
- if scrubbed:
182
- super().__setattr__(name, results)
183
- obj = results
184
- return obj
185
-
186
- def __str__(self) -> str:
187
- return self.__repr__()
188
-
189
- def __repr__(self) -> str:
190
- """
191
- Return a string representation of the AutoModel instance.
192
-
193
- Returns:
194
- str: A string representation of the AutoModel instance.
195
- """
196
- return str(self.__dict__)
24
+ class AutoModel(Document):
25
+ meta = {"abstract": True, "allow_inheritance": True, "strict": False}
26
+ last_updated = DateTimeField(default=datetime.now)
197
27
 
198
28
  def __eq__(self, other):
199
29
  return self.pk == other.pk if other else False
200
30
 
201
31
  @classmethod
202
- def load_model(cls, model, module=None):
203
- if not module:
204
- module = f"models.{model.lower()}"
205
- module = importlib.import_module(module)
206
- return getattr(module, model)
32
+ def auto_pre_init(cls, sender, document, **kwargs):
33
+ values = kwargs.pop("values", None)
34
+ if pk := values.get("pk") or values.get("id"):
35
+ # Try to load the existing document from the database
36
+ if existing_doc := sender._get_collection().find_one({"_id": ObjectId(pk)}):
37
+ # Update the current instance with the existing data
38
+ existing_doc.pop("_id", None)
39
+ existing_doc.pop("_cls", None)
40
+ for k, v in existing_doc.items():
41
+ if not values.get(k):
42
+ values[k] = v
207
43
 
208
44
  @classmethod
209
- def table(cls):
210
- # breakpoint()
211
- if not cls._table or cls._table.name != cls.__name__:
212
- cls.attributes["pk"] = None
213
- cls.attributes["last_updated"] = datetime.now()
214
- cls.attributes["_automodel"] = AutoAttribute(
215
- "TEXT", default=cls.model_name(qualified=True)
216
- )
217
- cls._table = cls._orm(cls._table_name or cls.__name__, cls.attributes)
218
- return cls._table
45
+ def _auto_pre_init(cls, sender, document, **kwargs):
46
+ sender.auto_pre_init(sender, document, **kwargs)
47
+
48
+ @classmethod
49
+ def auto_post_init(cls, sender, document, **kwargs):
50
+ document.last_updated = datetime.now()
51
+
52
+ @classmethod
53
+ def _auto_post_init(cls, sender, document, **kwargs):
54
+ sender.auto_post_init(sender, document, **kwargs)
219
55
 
220
56
  @classmethod
221
57
  def model_name(cls, qualified=False):
@@ -227,74 +63,13 @@ class AutoModel(ABC):
227
63
  """
228
64
  return f"{cls.__module__}.{cls.__name__}" if qualified else cls.__name__
229
65
 
230
- @property
231
- def _id(self):
232
- """
233
- Get the primary key of this model.
234
-
235
- Returns:
236
- int: The primary key of this model.
237
- """
238
- return self.pk
239
-
240
- @_id.setter
241
- def _id(self, _id):
242
- """
243
- Get the primary key of this model.
244
-
245
- Returns:
246
- int: The primary key of this model.
247
- """
248
- self.pk = str(_id)
249
-
250
- def validate(self):
251
- """
252
- Validate this model.
253
- """
254
- for key, vattr in self.attributes.items():
255
- if isinstance(vattr, AutoAttribute):
256
- val = getattr(self, key)
257
- if vattr.type == "TEXT":
258
- if not isinstance(val, str):
259
- raise TypeError(
260
- f"{key} value must be a string, not {type(val)}"
261
- )
262
- elif vattr.type == "NUMERIC":
263
- if not isinstance(val, (int, float)):
264
- raise TypeError(
265
- f"{key} value must be a number, not {type(val)}"
266
- )
267
- elif vattr.type == "MODEL":
268
- # log(isinstance(val, (AutoModel, DelayedModel)), type(val))
269
- if val is not None and not isinstance(
270
- val, (AutoModel, DelayedModel)
271
- ):
272
- raise TypeError(
273
- f"{key} value must be an AutoModel or None, not {type(val)}"
274
- )
275
- else:
276
- raise ValueError(f"{key}: Invalid type {self.type}")
277
-
278
- if vattr.required and val is None:
279
- raise ValueError(f"{key} is required")
280
- if vattr.unique and len(self.search(**{key: val})) > 1:
281
- raise ValueError(f"{key} must be unique")
282
- if vattr.primary_key:
283
- self.pk = val
284
-
285
- def save(self):
286
- """
287
- Save this model to the database.
288
-
289
- Returns:
290
- int: The primary key (pk) of the saved model.
291
- """
292
- self.validate()
293
- serialized_obj = self.serialize()
294
- serialized_obj["pk"] = self.pk
295
- self.pk = self.table().save(serialized_obj)
296
-
297
- return self.pk
66
+ @classmethod
67
+ def load_model(cls, model):
68
+ module_name, model = (
69
+ model.rsplit(".", 1) if "." in model else (f"models.{model.lower()}", model)
70
+ )
71
+ module = importlib.import_module(module_name)
72
+ return getattr(module, model)
298
73
 
299
74
  @classmethod
300
75
  def get(cls, pk):
@@ -307,10 +82,22 @@ class AutoModel(ABC):
307
82
  Returns:
308
83
  AutoModel or None: The retrieved AutoModel instance, or None if not found.
309
84
  """
310
- table = cls.table()
311
- result = table.get(pk)
312
- obj = cls(**result) if result else None
313
- return obj
85
+
86
+ if isinstance(pk, str):
87
+ pk = ObjectId(pk)
88
+ elif isinstance(pk, dict) and "$oid" in pk:
89
+ pk = ObjectId(pk["$oid"])
90
+ try:
91
+ return cls.objects.get(id=pk)
92
+ except cls.DoesNotExist as e:
93
+ log(f"Model {cls.__name__} with pk {pk} not found : {e}")
94
+ return None
95
+ except ValidationError as e:
96
+ log(f"Model Validation failure {cls.__name__} [{pk}]: {e}")
97
+ return None
98
+ except Exception as e:
99
+ log(f"Error getting model {cls.__name__} with pk {pk}: {e}", _print=True)
100
+ raise e
314
101
 
315
102
  @classmethod
316
103
  def random(cls):
@@ -323,9 +110,11 @@ class AutoModel(ABC):
323
110
  Returns:
324
111
  AutoModel or None: The retrieved AutoModel instance, or None if not found.
325
112
  """
326
- result = cls.table().random()
327
- # breakpoint()
328
- return cls(**result) if result else None
113
+ pipeline = [{"$sample": {"size": 1}}]
114
+
115
+ result = cls.objects.aggregate(pipeline)
116
+ random_document = next(result, None)
117
+ return cls._from_son(random_document) if random_document else None
329
118
 
330
119
  @classmethod
331
120
  def all(cls):
@@ -335,10 +124,10 @@ class AutoModel(ABC):
335
124
  Returns:
336
125
  list: A list of AutoModel instances.
337
126
  """
338
- return [cls(**o) for o in cls.table().all()]
127
+ return list(cls.objects())
339
128
 
340
129
  @classmethod
341
- def search(cls, **kwargs):
130
+ def search(cls, _order_by=None, _limit=None, **kwargs):
342
131
  """
343
132
  Search for models containing the keyword values.
344
133
 
@@ -348,9 +137,15 @@ class AutoModel(ABC):
348
137
  Returns:
349
138
  list: A list of AutoModel instances that match the search criteria.
350
139
  """
351
- for k, v in kwargs.items():
352
- kwargs[k] = AutoEncoder.encode(v)
353
- return [cls(**attribs) for attribs in cls.table().search(**kwargs)]
140
+ results = cls.objects(**kwargs)
141
+ if _order_by:
142
+ results = results.order_by(*_order_by)
143
+ if _limit:
144
+ if isinstance(_limit, list):
145
+ results = results[_limit[0] : _limit[-1]]
146
+ else:
147
+ results = results[:_limit]
148
+ return list(results)
354
149
 
355
150
  @classmethod
356
151
  def find(cls, **kwargs):
@@ -363,38 +158,56 @@ class AutoModel(ABC):
363
158
  Returns:
364
159
  AutoModel or None: The first matching AutoModel instance, or None if not found.
365
160
  """
366
- for k, v in kwargs.items():
367
- kwargs[k] = AutoEncoder.encode(v)
368
- attribs = cls.table().find(**kwargs)
369
- return cls(**attribs) if attribs else None
161
+ return cls.objects(**kwargs).first()
370
162
 
371
- def update(self, values):
163
+ @classmethod
164
+ def auto_pre_save(cls, sender, document, **kwargs):
372
165
  """
373
- Delete this model from the database.
166
+ Post-save hook for this model.
374
167
  """
375
- if not isinstance(values, dict):
376
- raise ValueError("Values must be a dictionary")
377
- for k, v in values.items():
378
- if k in self.attributes or f"_{k}" in self.attributes:
379
- if getattr(self.__class__, k, None) and getattr(self.__class__, k).fset:
380
- getattr(self.__class__, k).fset(self, v)
381
- elif k in self.attributes:
382
- setattr(self, k, v)
383
- self.save()
168
+ pass
384
169
 
385
- def delete(self):
170
+ @classmethod
171
+ def _auto_pre_save(cls, sender, document, **kwargs):
386
172
  """
387
- Delete this model from the database.
173
+ Post-save hook for this model.
388
174
  """
389
- return self.table().delete(self.pk)
175
+ sender.auto_pre_save(sender, document, **kwargs)
390
176
 
391
- def serialize(self):
177
+ def save(self):
392
178
  """
393
- Serialize this model to a dictionary.
179
+ Save this model to the database.
394
180
 
395
181
  Returns:
396
- dict: A dictionary representation of the serialized model.
182
+ int: The primary key (pk) of the saved model.
397
183
  """
398
- vars = {k: v for k, v in self.__dict__.items() if k in self.attributes}
399
- json_vars = AutoEncoder.encode(vars)
400
- return json_vars
184
+ # log(self.to_json())
185
+ obj = super().save()
186
+ self.pk = obj.pk
187
+ return self.pk
188
+
189
+ @classmethod
190
+ def auto_post_save(cls, sender, document, **kwargs):
191
+ """
192
+ Post-save hook for this model.
193
+ """
194
+ pass
195
+
196
+ @classmethod
197
+ def _auto_post_save(cls, sender, document, **kwargs):
198
+ """
199
+ Post-save hook for this model.
200
+ """
201
+ sender.auto_post_save(sender, document, **kwargs)
202
+
203
+ def delete(self):
204
+ """
205
+ Delete this model from the database.
206
+ """
207
+ return super().delete()
208
+
209
+
210
+ signals.pre_init.connect(AutoModel._auto_pre_init)
211
+ signals.post_init.connect(AutoModel._auto_post_init)
212
+ signals.pre_save.connect(AutoModel._auto_pre_save)
213
+ signals.post_save.connect(AutoModel._auto_post_save)
@@ -1,4 +1,3 @@
1
- import glob
2
1
  import io
3
2
  import os
4
3
  import shutil
@@ -15,12 +14,6 @@ class ImageStorage:
15
14
  def __init__(self, path="static/images"):
16
15
  self.base_path = path
17
16
 
18
- def scan_storage(self, path=None):
19
- for root, dirs, files in os.walk(path or self.base_path):
20
- for file in files:
21
- if file == "orig.webp":
22
- yield os.path.join(root, file)
23
-
24
17
  @classmethod
25
18
  def _get_key(cls, folder="", pkey=None):
26
19
  if folder and not folder.endswith("/"):
@@ -75,10 +68,10 @@ class ImageStorage:
75
68
  if not asset_id:
76
69
  return ""
77
70
  original_path = f"{self.get_path(asset_id)}"
78
- # log(f"Getting image: {asset_id}", original_path)
71
+ # log(f"Getting image: {asset_id}.{size}", original_path)
79
72
  if not os.path.exists(original_path):
80
73
  log(f"Original image not found: {original_path}")
81
- return None
74
+ return ""
82
75
  file_path = f"{original_path}/{size}.webp"
83
76
  # log(file_path)
84
77
  result_url = f"/{file_path}"
@@ -88,11 +81,7 @@ class ImageStorage:
88
81
  # os.path.exists(original_path),
89
82
  # os.path.exists(file_path),
90
83
  # )
91
- if (
92
- size != "orig"
93
- and os.path.exists(original_path)
94
- and not os.path.exists(file_path)
95
- ):
84
+ if size != "orig" and not os.path.exists(file_path):
96
85
  # If the file doesn't exist, create it
97
86
  if result := self._resize_image(asset_id, size):
98
87
  with open(file_path, "wb") as asset:
@@ -115,7 +104,10 @@ class ImageStorage:
115
104
 
116
105
  def get_path(self, asset_id):
117
106
  if asset_id:
118
- return os.path.join(self.base_path, f"{asset_id}")
107
+ asset_path = asset_id.replace(".", "/")
108
+ if asset_path.endswith("/"):
109
+ asset_path = asset_path[:-1]
110
+ return os.path.join(self.base_path, f"{asset_path}")
119
111
  else:
120
112
  return self.base_path
121
113
 
@@ -132,45 +124,8 @@ class ImageStorage:
132
124
  return imgs
133
125
 
134
126
  def remove(self, asset_id):
135
- if not asset_id:
136
- return False
137
- file_path = self.get_path(asset_id)
138
- if os.path.isdir(file_path):
139
- print(f"Removing {file_path}")
140
- # return shutil.rmtree(file_path, ignore_errors=True)
141
- return False
142
-
143
- def clear_cached(self, asset_id):
144
127
  file_path = self.get_path(asset_id)
145
128
  if os.path.isdir(file_path):
146
- for file in glob.glob(os.path.join(file_path, "*")):
147
- if os.path.basename(file) != "orig.webp":
148
- os.remove(file)
149
- return False
150
-
151
- def rotate(self, asset_id, amount=-90):
152
- file_path = self.get_path(asset_id)
153
- log(asset_id)
154
- with Image.open(f"{file_path}/orig.webp") as img:
155
- # Rotate the image 90 degrees
156
- rotated_img = img.rotate(amount, expand=True)
157
- # Save the rotated image
158
- log(img, rotated_img)
159
- # img = img.copy()
160
- # img_byte_arr = io.BytesIO()
161
- # img.save(img_byte_arr, )
162
- self.clear_cached(asset_id)
163
- rotated_img.save(f"{file_path}/orig.webp", format="WEBP")
164
- return False
165
-
166
- def flip(self, asset_id, flipx=True, flipy=True):
167
- file_path = self.get_path(asset_id)
168
- with Image.open(f"{file_path}/orig.webp") as img:
169
- if flipx:
170
- rotated_img = img.transpose(Image.FLIP_LEFT_RIGHT)
171
- if flipy:
172
- rotated_img = img.transpose(Image.FLIP_TOP_BOTTOM)
173
- # Save the rotated image
174
- rotated_img.save(f"{file_path}/orig.webp", format="WEBP")
175
- self.clear_cached(asset_id)
129
+ shutil.rmtree(file_path)
130
+ return True
176
131
  return False
@@ -1,31 +1,12 @@
1
1
  import importlib
2
2
  import os
3
3
  import subprocess
4
- import time
5
4
 
6
5
  from redis import Redis
7
6
  from rq import Queue, Worker
8
- from rq.job import Job
9
7
 
10
8
  from autonomous import log
11
9
 
12
- # class AutoJob(Job):
13
- # def perform(self):
14
- # start = time.time()
15
- # result = super().perform()
16
- # end = time.time()
17
- # log.info(
18
- # "Job Finished",
19
- # key=self.key,
20
- # seconds=(end - start),
21
- # method=self.func_name,
22
- # data_size=len(self._data),
23
- # queue=self.origin,
24
- # enqueued_at=self.enqueued_at.timestamp() if self.enqueued_at else None,
25
- # started_at=self.started_at.timestamp() if self.started_at else None,
26
- # )
27
- # return result
28
-
29
10
 
30
11
  class AutoTask:
31
12
  def __init__(self, job):
@@ -161,9 +142,3 @@ class AutoTasks:
161
142
  def clear(self):
162
143
  AutoTasks.queue.empty()
163
144
  AutoTasks.all_tasks = []
164
-
165
-
166
- # if __name__ == "__main__":
167
- # autotasks = AutoTasks()
168
- # for _ in range(autotasks.workers):
169
- # create_worker(autotasks.queue.name)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: autonomous-app
3
- Version: 0.2.25
3
+ Version: 0.3.1
4
4
  Summary: Containerized application framework built on Flask with additional libraries and tools for rapid development of web applications.
5
5
  Author-email: Steven A Moore <samoore@binghamton.edu>
6
6
  License: MIT License
@@ -25,27 +25,26 @@ License: MIT License
25
25
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
26
  SOFTWARE.
27
27
  Project-URL: homepage, https://github.com/Sallenmoore/autonomous
28
- Classifier: Programming Language :: Python :: 3.11
28
+ Classifier: Programming Language :: Python :: 3.12
29
29
  Classifier: License :: OSI Approved :: MIT License
30
30
  Classifier: Operating System :: OS Independent
31
- Requires-Python: >=3.11
31
+ Requires-Python: >=3.12
32
32
  Description-Content-Type: text/markdown
33
33
  License-File: LICENSE
34
34
  Requires-Dist: Flask
35
- Requires-Dist: icecream
36
35
  Requires-Dist: setuptools
37
36
  Requires-Dist: python-dotenv
37
+ Requires-Dist: blinker
38
38
  Requires-Dist: PyGithub
39
39
  Requires-Dist: pygit2
40
- Requires-Dist: cloudinary
41
40
  Requires-Dist: pillow
42
- Requires-Dist: pymongo
41
+ Requires-Dist: redis
43
42
  Requires-Dist: jsmin
44
43
  Requires-Dist: requests
45
44
  Requires-Dist: gunicorn
46
45
  Requires-Dist: Authlib
47
- Requires-Dist: openai>=1.21.1
48
- Requires-Dist: validators
46
+ Requires-Dist: rq
47
+ Requires-Dist: openai>=1.42
49
48
  Requires-Dist: dateparser
50
49
  Requires-Dist: python-slugify
51
50