autonomous-app 0.2.25__py3-none-any.whl → 0.3.0__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 (34) 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 +280 -0
  7. autonomous/ai/oaiagent.py +25 -186
  8. autonomous/ai/textagent.py +35 -0
  9. autonomous/auth/autoauth.py +2 -2
  10. autonomous/auth/user.py +8 -10
  11. autonomous/model/autoattr.py +105 -0
  12. autonomous/model/automodel.py +70 -311
  13. autonomous/storage/imagestorage.py +9 -54
  14. autonomous/tasks/autotask.py +0 -25
  15. {autonomous_app-0.2.25.dist-info → autonomous_app-0.3.0.dist-info}/METADATA +7 -8
  16. autonomous_app-0.3.0.dist-info/RECORD +35 -0
  17. {autonomous_app-0.2.25.dist-info → autonomous_app-0.3.0.dist-info}/WHEEL +1 -1
  18. autonomous/db/__init__.py +0 -1
  19. autonomous/db/autodb.py +0 -86
  20. autonomous/db/table.py +0 -156
  21. autonomous/errors/__init__.py +0 -1
  22. autonomous/errors/danglingreferenceerror.py +0 -8
  23. autonomous/model/autoattribute.py +0 -20
  24. autonomous/model/orm.py +0 -86
  25. autonomous/model/serializer.py +0 -110
  26. autonomous_app-0.2.25.dist-info/RECORD +0 -36
  27. /autonomous/{storage → apis}/version_control/GHCallbacks.py +0 -0
  28. /autonomous/{storage → apis}/version_control/GHOrganization.py +0 -0
  29. /autonomous/{storage → apis}/version_control/GHRepo.py +0 -0
  30. /autonomous/{storage → apis}/version_control/GHVersionControl.py +0 -0
  31. /autonomous/{storage → apis}/version_control/__init__.py +0 -0
  32. /autonomous/{storage → utils}/markdown.py +0 -0
  33. {autonomous_app-0.2.25.dist-info → autonomous_app-0.3.0.dist-info}/LICENSE +0 -0
  34. {autonomous_app-0.2.25.dist-info → autonomous_app-0.3.0.dist-info}/top_level.txt +0 -0
autonomous/auth/user.py CHANGED
@@ -5,7 +5,7 @@ This module provides a User class that uses the OpenIDAuth class for authenticat
5
5
  from datetime import datetime
6
6
 
7
7
  from autonomous import log
8
- from autonomous.model.autoattribute import AutoAttribute
8
+ from autonomous.model.autoattr import DateTimeAttr, StringAttr
9
9
  from autonomous.model.automodel import AutoModel
10
10
 
11
11
 
@@ -14,15 +14,13 @@ class AutoUser(AutoModel):
14
14
  This class represents a user who can authenticate using OpenID.
15
15
  """
16
16
 
17
- attributes = {
18
- "name": AutoAttribute("TEXT"),
19
- "email": AutoAttribute("TEXT", required=True),
20
- "last_login": datetime.now(),
21
- "state": "unauthenticated",
22
- "provider": None,
23
- "role": "user",
24
- "token": None,
25
- }
17
+ name = StringAttr(default="Anonymous")
18
+ email = StringAttr(required=True)
19
+ last_login = DateTimeAttr(default=datetime.now)
20
+ state = StringAttr(default="unauthenticated")
21
+ provider = StringAttr()
22
+ role = StringAttr(default="guest")
23
+ token = StringAttr()
26
24
 
27
25
  def __eq__(self, other):
28
26
  return self.pk == other.pk
@@ -0,0 +1,105 @@
1
+ from mongoengine.base import (
2
+ get_document,
3
+ )
4
+ from mongoengine.fields import (
5
+ BooleanField,
6
+ DateTimeField,
7
+ DictField,
8
+ DoesNotExist,
9
+ EmailField,
10
+ EnumField,
11
+ FileField,
12
+ FloatField,
13
+ GenericReferenceField,
14
+ ImageField,
15
+ IntField,
16
+ ListField,
17
+ StringField,
18
+ )
19
+
20
+ from autonomous import log
21
+
22
+
23
+ class StringAttr(StringField):
24
+ pass
25
+
26
+
27
+ class IntAttr(IntField):
28
+ pass
29
+
30
+
31
+ class FloatAttr(FloatField):
32
+ pass
33
+
34
+
35
+ class BoolAttr(BooleanField):
36
+ pass
37
+
38
+
39
+ class DateTimeAttr(DateTimeField):
40
+ pass
41
+
42
+
43
+ class EmailAttr(EmailField):
44
+ pass
45
+
46
+
47
+ class FileAttr(FileField):
48
+ pass
49
+
50
+
51
+ class ImageAttr(ImageField):
52
+ pass
53
+
54
+
55
+ class ReferenceAttr(GenericReferenceField):
56
+ def __get__(self, instance, owner):
57
+ try:
58
+ # Attempt to retrieve the referenced document
59
+ return super().__get__(instance, owner)
60
+ except DoesNotExist:
61
+ # If the document doesn't exist, return None
62
+ return None
63
+
64
+
65
+ class ListAttr(ListField):
66
+ def clean_references(self, values):
67
+ safe_values = []
68
+ updated = False
69
+
70
+ for value in values:
71
+ try:
72
+ if isinstance(value, dict) and "_cls" in value:
73
+ doc_cls = get_document(value["_cls"])
74
+ value = doc_cls._get_db().dereference(value["_ref"])
75
+ if value:
76
+ safe_values.append(value)
77
+ else:
78
+ updated = True
79
+ except DoesNotExist:
80
+ updated = True
81
+ log("hi")
82
+ return safe_values, updated
83
+
84
+
85
+ class DictAttr(DictField):
86
+ def clean_references(self, values):
87
+ safe_values = {}
88
+ updated = False
89
+ for key, value in values.items():
90
+ try:
91
+ if isinstance(value, dict) and "_cls" in value:
92
+ doc_cls = get_document(value["_cls"])
93
+ value = doc_cls._get_db().dereference(value["_ref"])
94
+ if value:
95
+ safe_values[key] = value
96
+ else:
97
+ updated = True
98
+ except DoesNotExist:
99
+ updated = True
100
+
101
+ return safe_values, updated
102
+
103
+
104
+ class EnumAttr(EnumField):
105
+ pass
@@ -1,222 +1,51 @@
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())
90
-
6
+ from bson import ObjectId
7
+ from mongoengine import Document, connect
8
+ from mongoengine.fields import DateTimeField
91
9
 
92
- class AutoModel(ABC):
93
- attributes = {}
94
- _table_name = ""
95
- _table = None
96
- _orm = ORM
97
-
98
- def __new__(cls, *args, **kwargs):
99
- """
100
- Create a new instance of the AutoModel.
101
-
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 {}
10
+ from autonomous import log
119
11
 
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
12
+ from .autoattr import DictAttr, ListAttr
139
13
 
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)
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
+ db = os.getenv("DB_DB")
19
+ connect(host=f"mongodb://{username}:{password}@{host}:{port}")
152
20
 
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
21
 
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
22
+ class AutoModel(Document):
23
+ meta = {"abstract": True, "allow_inheritance": True}
24
+ last_updated = DateTimeField(default=datetime.now)
185
25
 
186
- def __str__(self) -> str:
187
- return self.__repr__()
26
+ def __init__(self, *args, **kwargs):
27
+ super().__init__(*args, **kwargs)
28
+ if kwargs.pop("pk", None):
29
+ self.reload()
30
+ for k, v in kwargs.items():
31
+ setattr(self, k, v)
32
+ self.last_updated = datetime.now()
188
33
 
189
- def __repr__(self) -> str:
190
- """
191
- Return a string representation of the AutoModel instance.
34
+ for field_name, field in self._fields.items():
35
+ value = getattr(self, field_name, None)
36
+ # log(
37
+ # f"Field: {field_name}, Type:{type(value)}, Value: {value}, {hasattr(field, "clean_references")}"
38
+ # )
192
39
 
193
- Returns:
194
- str: A string representation of the AutoModel instance.
195
- """
196
- return str(self.__dict__)
40
+ if hasattr(field, "clean_references") and value:
41
+ cleaned_values, updated = field.clean_references(value)
42
+ # log(f"Cleaned Values: {cleaned_values}")
43
+ if updated:
44
+ setattr(self, field_name, cleaned_values)
197
45
 
198
46
  def __eq__(self, other):
199
47
  return self.pk == other.pk if other else False
200
48
 
201
- @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)
207
-
208
- @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
219
-
220
49
  @classmethod
221
50
  def model_name(cls, qualified=False):
222
51
  """
@@ -227,74 +56,11 @@ class AutoModel(ABC):
227
56
  """
228
57
  return f"{cls.__module__}.{cls.__name__}" if qualified else cls.__name__
229
58
 
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
59
+ @classmethod
60
+ def load_model(cls, model):
61
+ module_name, model = model.rsplit(".", 1) if "." in model else ("models", model)
62
+ module = importlib.import_module(module_name)
63
+ return getattr(module, model)
298
64
 
299
65
  @classmethod
300
66
  def get(cls, pk):
@@ -307,10 +73,13 @@ class AutoModel(ABC):
307
73
  Returns:
308
74
  AutoModel or None: The retrieved AutoModel instance, or None if not found.
309
75
  """
310
- table = cls.table()
311
- result = table.get(pk)
312
- obj = cls(**result) if result else None
313
- return obj
76
+ if pk and isinstance(pk, str):
77
+ pk = ObjectId(pk)
78
+ try:
79
+ return cls.objects(pk=pk).get()
80
+ except Exception as e:
81
+ log(e)
82
+ return None
314
83
 
315
84
  @classmethod
316
85
  def random(cls):
@@ -323,9 +92,11 @@ class AutoModel(ABC):
323
92
  Returns:
324
93
  AutoModel or None: The retrieved AutoModel instance, or None if not found.
325
94
  """
326
- result = cls.table().random()
327
- # breakpoint()
328
- return cls(**result) if result else None
95
+ pipeline = [{"$sample": {"size": 1}}]
96
+
97
+ result = cls.objects.aggregate(pipeline)
98
+ random_document = next(result, None)
99
+ return cls._from_son(random_document) if random_document else None
329
100
 
330
101
  @classmethod
331
102
  def all(cls):
@@ -335,10 +106,10 @@ class AutoModel(ABC):
335
106
  Returns:
336
107
  list: A list of AutoModel instances.
337
108
  """
338
- return [cls(**o) for o in cls.table().all()]
109
+ return list(cls.objects())
339
110
 
340
111
  @classmethod
341
- def search(cls, **kwargs):
112
+ def search(cls, _order_by=None, _limit=None, **kwargs):
342
113
  """
343
114
  Search for models containing the keyword values.
344
115
 
@@ -348,9 +119,15 @@ class AutoModel(ABC):
348
119
  Returns:
349
120
  list: A list of AutoModel instances that match the search criteria.
350
121
  """
351
- for k, v in kwargs.items():
352
- kwargs[k] = AutoEncoder.encode(v)
353
- return [cls(**attribs) for attribs in cls.table().search(**kwargs)]
122
+ results = cls.objects(**kwargs)
123
+ if _order_by:
124
+ results = results.order_by(*_order_by)
125
+ if _limit:
126
+ if isinstance(_limit, list):
127
+ results = results[_limit[0] : _limit[-1]]
128
+ else:
129
+ results = results[:_limit]
130
+ return list(results)
354
131
 
355
132
  @classmethod
356
133
  def find(cls, **kwargs):
@@ -363,38 +140,20 @@ class AutoModel(ABC):
363
140
  Returns:
364
141
  AutoModel or None: The first matching AutoModel instance, or None if not found.
365
142
  """
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
143
+ return cls.objects(**kwargs).first()
370
144
 
371
- def update(self, values):
145
+ def save(self):
372
146
  """
373
- Delete this model from the database.
147
+ Save this model to the database.
148
+
149
+ Returns:
150
+ int: The primary key (pk) of the saved model.
374
151
  """
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()
152
+ # log(self.model_dump_json())
153
+ return super().save()
384
154
 
385
155
  def delete(self):
386
156
  """
387
157
  Delete this model from the database.
388
158
  """
389
- return self.table().delete(self.pk)
390
-
391
- def serialize(self):
392
- """
393
- Serialize this model to a dictionary.
394
-
395
- Returns:
396
- dict: A dictionary representation of the serialized model.
397
- """
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
159
+ return super().delete()
@@ -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