from abc import ABC from logging import Logger from typing import Optional, Any, Dict from bson import ObjectId from libbot.cache.classes import Cache from classes.abstract import Cacheable from modules.utils import get_logger logger: Logger = get_logger(__name__) class BaseCacheable(Cacheable, ABC): """Base implementation of Cacheable used by all cachable classes.""" _id: ObjectId async def _set(self, cache: Optional[Cache] = None, **kwargs: Any) -> None: for key, value in kwargs.items(): if not hasattr(self, key): raise AttributeError() setattr(self, key, value) await self.__collection__.update_one({"_id": self._id}, {"$set": kwargs}) self._update_cache(cache) logger.info("Set attributes of %s to %s", self._id, kwargs) async def _remove(self, *args: str, cache: Optional[Cache] = None) -> None: attributes: Dict[str, Any] = {} for key in args: if not hasattr(self, key): raise AttributeError() default_value: Any = self.get_default_value(key) setattr(self, key, default_value) attributes[key] = default_value await self.__collection__.update_one({"_id": self._id}, {"$set": attributes}) self._update_cache(cache) logger.info("Reset attributes %s of %s to default values", args, self._id) def _update_cache(self, cache: Optional[Cache] = None) -> None: if cache is None: return object_dict: Dict[str, Any] = self.to_dict(json_compatible=True) if object_dict is not None: cache.set_json(self._get_cache_key(), object_dict) else: self._delete_cache(cache) def _delete_cache(self, cache: Optional[Cache] = None) -> None: if cache is None: return cache.delete(self._get_cache_key()) async def update( self, cache: Optional[Cache] = None, **kwargs: Any, ) -> None: """Update attribute(s) on the object and save the updated entry into the database. Args: cache (:obj:`Cache`, optional): Cache engine that will be used to update the cache. **kwargs (Any): Mapping of attributes in format `attribute_name=attribute_value` to update. Raises: AttributeError: Provided attribute does not exist in the class. """ await self._set(cache=cache, **kwargs) async def reset( self, *args: str, cache: Optional[Cache] = None, ) -> None: """Remove attribute(s) on the object, replace them with a default value and save the updated entry into the database. Args: *args (str): List of attributes to remove. cache (:obj:`Cache`, optional): Cache engine that will be used to update the cache. Raises: AttributeError: Provided attribute does not exist in the class. """ await self._remove(*args, cache=cache) async def purge(self, cache: Optional[Cache] = None) -> None: """Completely remove object data from database. Currently only removes the record from a respective collection. Args: cache (:obj:`Cache`, optional): Cache engine that will be used to update the cache. """ await self.__collection__.delete_one({"_id": self._id}) self._delete_cache(cache) logger.info("Purged %s from the database", self._id)