autonomous-app 0.2.24__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 +24 -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 -305
  13. autonomous/storage/imagestorage.py +9 -54
  14. autonomous/tasks/autotask.py +0 -25
  15. {autonomous_app-0.2.24.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.24.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 -102
  26. autonomous_app-0.2.24.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.24.dist-info → autonomous_app-0.3.0.dist-info}/LICENSE +0 -0
  34. {autonomous_app-0.2.24.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,217 +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
- from abc import ABC
2
+ import os
3
+ import urllib.parse
6
4
  from datetime import datetime
7
5
 
8
- from autonomous import log
9
- from autonomous.errors import DanglingReferenceError
10
-
11
- from .autoattribute import AutoAttribute
12
- from .orm import ORM
13
- from .serializer import AutoDecoder, AutoEncoder
14
-
15
-
16
- class DelayedModel:
17
- def __init__(self, model, pk):
18
- # log(model, pk)
19
- assert model
20
- assert pk
21
- module_name, class_name = model.rsplit(".", 1)
22
- try:
23
- module = importlib.import_module(module_name)
24
- model = getattr(module, class_name)
25
- except (ModuleNotFoundError, AttributeError) as e:
26
- log(e)
27
- raise DanglingReferenceError(model, pk, None)
28
- else:
29
- object.__setattr__(self, "_delayed_model", model)
30
- object.__setattr__(self, "_delayed_pk", pk)
31
- object.__setattr__(self, "_delayed_obj", None)
32
-
33
- def _instance(self):
34
- #### DO NOT TO ANY LOGGING IN THIS METHOD; IT CAUSES INFINITE RECURSION ####
35
- if not object.__getattribute__(self, "_delayed_obj"):
36
- _pk = object.__getattribute__(self, "_delayed_pk")
37
- _model = object.__getattribute__(self, "_delayed_model")
38
- _obj = _model.get(_pk)
39
- if not _pk or not _model or _obj is None:
40
- raise DanglingReferenceError(_model, _pk, _obj)
41
- else:
42
- object.__setattr__(self, "_delayed_obj", _obj)
43
-
44
- return object.__getattribute__(self, "_delayed_obj")
45
-
46
- # def __getattr__(self, name):
47
- # return getattr(self._instance(), name)
48
-
49
- def __getattribute__(self, name):
50
- # log(name)
51
- if name in [
52
- "_delayed_model",
53
- "_delayed_pk",
54
- "_delayed_obj",
55
- "_instance",
56
- ]:
57
- return object.__getattribute__(self, name)
58
- try:
59
- return object.__getattribute__(self._instance(), name)
60
- except DanglingReferenceError as e:
61
- log(e)
62
- return None
63
-
64
- def __setattr__(self, name, value):
65
- if name.startswith("_delayed"):
66
- object.__setattr__(self, name, value)
67
- else:
68
- setattr(self._instance(), name, value)
69
-
70
- def __delattr__(self, name):
71
- delattr(self._instance(), name)
72
-
73
- def __nonzero__(self):
74
- return bool(self._instance())
75
-
76
- def __str__(self):
77
- return str(self._instance().__dict__)
78
-
79
- def __repr__(self):
80
- msg = f"\n<<DelayedModel {self._delayed_model.__name__}:{self._delayed_pk}>>"
81
- return msg
82
-
83
- def __hash__(self):
84
- return hash(self._instance())
85
-
6
+ from bson import ObjectId
7
+ from mongoengine import Document, connect
8
+ from mongoengine.fields import DateTimeField
86
9
 
87
- class AutoModel(ABC):
88
- attributes = {}
89
- _table_name = ""
90
- _table = None
91
- _orm = ORM
92
-
93
- def __new__(cls, *args, **kwargs):
94
- """
95
- Create a new instance of the AutoModel.
96
-
97
- This method is responsible for creating a new instance of the AutoModel class.
98
- It sets default attributes, populates the object from the database if a primary key is provided,
99
- and handles additional keyword arguments.
100
-
101
- Args:
102
- cls: The class itself.
103
- *args: Positional arguments.
104
- **kwargs: Keyword arguments, including 'pk' for primary key.
105
-
106
- Returns:
107
- obj: The created AutoModel instance.
108
- """
109
- obj = super().__new__(cls)
110
- pk = kwargs.pop("pk", None)
111
- # set default attributes
112
- # Get model data from database
113
- result = cls.table().get(pk) or {}
10
+ from autonomous import log
114
11
 
115
- # set object attributes
116
- for k, v in cls.attributes.items():
117
- if isinstance(v, AutoAttribute):
118
- v = v.default
119
- setattr(obj, k, result.get(k, copy.deepcopy(v)))
120
- obj.pk = pk
121
- for key, val in list(kwargs.items()):
122
- if (
123
- getattr(cls, key, None)
124
- and getattr(cls, key).fset
125
- and f"_{key}" in cls.attributes
126
- ):
127
- kwargs[f"_{key}"] = kwargs.pop(key)
128
- obj.__dict__ |= kwargs
129
- # breakpoint()
130
- obj.__dict__ = AutoDecoder.decode(obj.__dict__)
131
- obj._automodel = obj.model_name(qualified=True)
132
- obj.last_updated = datetime.now()
133
- return obj
12
+ from .autoattr import DictAttr, ListAttr
134
13
 
135
- def __getattribute__(self, name):
136
- obj = super().__getattribute__(name)
137
- if not name.startswith("__"):
138
- if isinstance(obj, DelayedModel):
139
- try:
140
- result = obj._instance()
141
- except DanglingReferenceError as e:
142
- log(e)
143
- super().__setattr__(name, None)
144
- return None
145
- else:
146
- 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}")
147
20
 
148
- elif isinstance(obj, list):
149
- results = []
150
- scrubbed = False
151
- for i, item in enumerate(obj):
152
- if isinstance(item, DelayedModel):
153
- try:
154
- result = item._instance()
155
- except DanglingReferenceError as e:
156
- log(e)
157
- scrubbed = True
158
- else:
159
- results.append(result)
160
- if scrubbed:
161
- super().__setattr__(name, results)
162
- obj = results
163
21
 
164
- elif isinstance(obj, dict):
165
- results = {}
166
- scrubbed = False
167
- for key, item in obj.items():
168
- if isinstance(item, DelayedModel):
169
- try:
170
- result = item._instance()
171
- except DanglingReferenceError as e:
172
- log(e)
173
- scrubbed = True
174
- else:
175
- results[key] = result
176
- if scrubbed:
177
- super().__setattr__(name, results)
178
- obj = results
179
- return obj
22
+ class AutoModel(Document):
23
+ meta = {"abstract": True, "allow_inheritance": True}
24
+ last_updated = DateTimeField(default=datetime.now)
180
25
 
181
- def __str__(self) -> str:
182
- 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()
183
33
 
184
- def __repr__(self) -> str:
185
- """
186
- 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
+ # )
187
39
 
188
- Returns:
189
- str: A string representation of the AutoModel instance.
190
- """
191
- 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)
192
45
 
193
46
  def __eq__(self, other):
194
47
  return self.pk == other.pk if other else False
195
48
 
196
- @classmethod
197
- def load_model(cls, model, module=None):
198
- if not module:
199
- module = f"models.{model.lower()}"
200
- module = importlib.import_module(module)
201
- return getattr(module, model)
202
-
203
- @classmethod
204
- def table(cls):
205
- # breakpoint()
206
- if not cls._table or cls._table.name != cls.__name__:
207
- cls.attributes["pk"] = None
208
- cls.attributes["last_updated"] = datetime.now()
209
- cls.attributes["_automodel"] = AutoAttribute(
210
- "TEXT", default=cls.model_name(qualified=True)
211
- )
212
- cls._table = cls._orm(cls._table_name or cls.__name__, cls.attributes)
213
- return cls._table
214
-
215
49
  @classmethod
216
50
  def model_name(cls, qualified=False):
217
51
  """
@@ -222,74 +56,11 @@ class AutoModel(ABC):
222
56
  """
223
57
  return f"{cls.__module__}.{cls.__name__}" if qualified else cls.__name__
224
58
 
225
- @property
226
- def _id(self):
227
- """
228
- Get the primary key of this model.
229
-
230
- Returns:
231
- int: The primary key of this model.
232
- """
233
- return self.pk
234
-
235
- @_id.setter
236
- def _id(self, _id):
237
- """
238
- Get the primary key of this model.
239
-
240
- Returns:
241
- int: The primary key of this model.
242
- """
243
- self.pk = str(_id)
244
-
245
- def validate(self):
246
- """
247
- Validate this model.
248
- """
249
- for key, vattr in self.attributes.items():
250
- if isinstance(vattr, AutoAttribute):
251
- val = getattr(self, key)
252
- if vattr.type == "TEXT":
253
- if not isinstance(val, str):
254
- raise TypeError(
255
- f"{key} value must be a string, not {type(val)}"
256
- )
257
- elif vattr.type == "NUMERIC":
258
- if not isinstance(val, (int, float)):
259
- raise TypeError(
260
- f"{key} value must be a number, not {type(val)}"
261
- )
262
- elif vattr.type == "MODEL":
263
- # log(isinstance(val, (AutoModel, DelayedModel)), type(val))
264
- if val is not None and not isinstance(
265
- val, (AutoModel, DelayedModel)
266
- ):
267
- raise TypeError(
268
- f"{key} value must be an AutoModel or None, not {type(val)}"
269
- )
270
- else:
271
- raise ValueError(f"{key}: Invalid type {self.type}")
272
-
273
- if vattr.required and val is None:
274
- raise ValueError(f"{key} is required")
275
- if vattr.unique and len(self.search(**{key: val})) > 1:
276
- raise ValueError(f"{key} must be unique")
277
- if vattr.primary_key:
278
- self.pk = val
279
-
280
- def save(self):
281
- """
282
- Save this model to the database.
283
-
284
- Returns:
285
- int: The primary key (pk) of the saved model.
286
- """
287
- self.validate()
288
- serialized_obj = self.serialize()
289
- serialized_obj["pk"] = self.pk
290
- self.pk = self.table().save(serialized_obj)
291
-
292
- 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)
293
64
 
294
65
  @classmethod
295
66
  def get(cls, pk):
@@ -302,9 +73,13 @@ class AutoModel(ABC):
302
73
  Returns:
303
74
  AutoModel or None: The retrieved AutoModel instance, or None if not found.
304
75
  """
305
- table = cls.table()
306
- result = table.get(pk)
307
- return cls(**result) if result else None
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
308
83
 
309
84
  @classmethod
310
85
  def random(cls):
@@ -317,9 +92,11 @@ class AutoModel(ABC):
317
92
  Returns:
318
93
  AutoModel or None: The retrieved AutoModel instance, or None if not found.
319
94
  """
320
- result = cls.table().random()
321
- # breakpoint()
322
- 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
323
100
 
324
101
  @classmethod
325
102
  def all(cls):
@@ -329,10 +106,10 @@ class AutoModel(ABC):
329
106
  Returns:
330
107
  list: A list of AutoModel instances.
331
108
  """
332
- return [cls(**o) for o in cls.table().all()]
109
+ return list(cls.objects())
333
110
 
334
111
  @classmethod
335
- def search(cls, **kwargs):
112
+ def search(cls, _order_by=None, _limit=None, **kwargs):
336
113
  """
337
114
  Search for models containing the keyword values.
338
115
 
@@ -342,9 +119,15 @@ class AutoModel(ABC):
342
119
  Returns:
343
120
  list: A list of AutoModel instances that match the search criteria.
344
121
  """
345
- for k, v in kwargs.items():
346
- kwargs[k] = AutoEncoder.encode(v)
347
- 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)
348
131
 
349
132
  @classmethod
350
133
  def find(cls, **kwargs):
@@ -357,38 +140,20 @@ class AutoModel(ABC):
357
140
  Returns:
358
141
  AutoModel or None: The first matching AutoModel instance, or None if not found.
359
142
  """
360
- for k, v in kwargs.items():
361
- kwargs[k] = AutoEncoder.encode(v)
362
- attribs = cls.table().find(**kwargs)
363
- return cls(**attribs) if attribs else None
143
+ return cls.objects(**kwargs).first()
364
144
 
365
- def update(self, values):
145
+ def save(self):
366
146
  """
367
- 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.
368
151
  """
369
- if not isinstance(values, dict):
370
- raise ValueError("Values must be a dictionary")
371
- for k, v in values.items():
372
- if k in self.attributes or f"_{k}" in self.attributes:
373
- if getattr(self.__class__, k, None) and getattr(self.__class__, k).fset:
374
- getattr(self.__class__, k).fset(self, v)
375
- elif k in self.attributes:
376
- setattr(self, k, v)
377
- self.save()
152
+ # log(self.model_dump_json())
153
+ return super().save()
378
154
 
379
155
  def delete(self):
380
156
  """
381
157
  Delete this model from the database.
382
158
  """
383
- return self.table().delete(self.pk)
384
-
385
- def serialize(self):
386
- """
387
- Serialize this model to a dictionary.
388
-
389
- Returns:
390
- dict: A dictionary representation of the serialized model.
391
- """
392
- vars = {k: v for k, v in self.__dict__.items() if k in self.attributes}
393
- json_vars = AutoEncoder.encode(vars)
394
- 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