updated docs
This commit is contained in:
@@ -10,7 +10,7 @@ This module provides a class for managing Redis key-value operations with suppor
|
||||
|
||||
import json
|
||||
from typing import Union, Dict, List, Optional, Any, ClassVar
|
||||
from datetime import datetime
|
||||
from Services.Redis.conn import redis_cli
|
||||
|
||||
|
||||
class RedisKeyError(Exception):
|
||||
@@ -44,23 +44,21 @@ class RedisRow:
|
||||
|
||||
key: ClassVar[Union[str, bytes]]
|
||||
value: ClassVar[Any]
|
||||
delimiter: ClassVar[str] = ":"
|
||||
delimiter: str = ":"
|
||||
expires_at: Optional[dict] = {"seconds": 60 * 60 * 30}
|
||||
expires_at_string: Optional[str]
|
||||
|
||||
@classmethod
|
||||
def get_expiry_time(cls) -> int | None:
|
||||
def get_expiry_time(self) -> int | None:
|
||||
"""Calculate expiry time in seconds from kwargs."""
|
||||
time_multipliers = {"days": 86400, "hours": 3600, "minutes": 60, "seconds": 1}
|
||||
if cls.expires_at:
|
||||
if self.expires_at:
|
||||
return sum(
|
||||
int(cls.expires_at.get(unit, 0)) * multiplier
|
||||
int(self.expires_at.get(unit, 0)) * multiplier
|
||||
for unit, multiplier in time_multipliers.items()
|
||||
)
|
||||
return
|
||||
|
||||
@classmethod
|
||||
def merge(cls, set_values: List[Union[str, bytes]]) -> None:
|
||||
def merge(self, set_values: List[Union[str, bytes]]) -> None:
|
||||
"""
|
||||
Merge list of values into a single delimited key.
|
||||
|
||||
@@ -83,7 +81,7 @@ class RedisRow:
|
||||
value = value.decode()
|
||||
merged.append(str(value))
|
||||
|
||||
cls.key = cls.delimiter.join(merged).encode()
|
||||
self.key = self.delimiter.join(merged).encode()
|
||||
|
||||
@classmethod
|
||||
def regex(cls, list_keys: List[Union[str, bytes, None]]) -> str:
|
||||
@@ -120,12 +118,11 @@ class RedisRow:
|
||||
# Add wildcard if first key was None
|
||||
if list_keys[0] is None:
|
||||
pattern = f"*{cls.delimiter}{pattern}"
|
||||
if "*" not in pattern:
|
||||
if "*" not in pattern and any([list_key is None for list_key in list_keys]):
|
||||
pattern = f"{pattern}:*"
|
||||
return pattern
|
||||
|
||||
@classmethod
|
||||
def parse(cls) -> List[str]:
|
||||
def parse(self) -> List[str]:
|
||||
"""
|
||||
Parse the key into its component parts.
|
||||
|
||||
@@ -137,14 +134,13 @@ class RedisRow:
|
||||
>>> RedisRow.parse()
|
||||
['users', '123', 'profile']
|
||||
"""
|
||||
if not cls.key:
|
||||
if not self.key:
|
||||
return []
|
||||
|
||||
key_str = cls.key.decode() if isinstance(cls.key, bytes) else cls.key
|
||||
return key_str.split(cls.delimiter)
|
||||
key_str = self.key.decode() if isinstance(self.key, bytes) else self.key
|
||||
return key_str.split(self.delimiter)
|
||||
|
||||
@classmethod
|
||||
def feed(cls, value: Union[bytes, Dict, List, str]) -> None:
|
||||
def feed(self, value: Union[bytes, Dict, List, str]) -> None:
|
||||
"""
|
||||
Convert and store value in JSON format.
|
||||
|
||||
@@ -161,18 +157,17 @@ class RedisRow:
|
||||
"""
|
||||
try:
|
||||
if isinstance(value, (dict, list)):
|
||||
cls.value = json.dumps(value)
|
||||
self.value = json.dumps(value)
|
||||
elif isinstance(value, bytes):
|
||||
cls.value = json.dumps(json.loads(value.decode()))
|
||||
self.value = json.dumps(json.loads(value.decode()))
|
||||
elif isinstance(value, str):
|
||||
cls.value = value
|
||||
self.value = value
|
||||
else:
|
||||
raise RedisValueError(f"Unsupported value type: {type(value)}")
|
||||
except json.JSONDecodeError as e:
|
||||
raise RedisValueError(f"Invalid JSON format: {str(e)}")
|
||||
|
||||
@classmethod
|
||||
def modify(cls, add_dict: Dict) -> None:
|
||||
def modify(self, add_dict: Dict) -> None:
|
||||
"""
|
||||
Modify existing data by merging with new dictionary.
|
||||
|
||||
@@ -187,15 +182,17 @@ class RedisRow:
|
||||
"""
|
||||
if not isinstance(add_dict, dict):
|
||||
raise RedisValueError("modify() requires a dictionary argument")
|
||||
|
||||
current_data = cls.data if cls.data else {}
|
||||
current_data = self.row if self.row else {}
|
||||
if not isinstance(current_data, dict):
|
||||
raise RedisValueError("Cannot modify non-dictionary data")
|
||||
current_data = {
|
||||
**current_data,
|
||||
**add_dict,
|
||||
}
|
||||
self.feed(current_data)
|
||||
self.save()
|
||||
|
||||
cls.feed({**current_data, **add_dict})
|
||||
|
||||
@classmethod
|
||||
def save(cls):
|
||||
def save(self):
|
||||
"""
|
||||
Save the data to Redis with optional expiration.
|
||||
|
||||
@@ -204,29 +201,28 @@ class RedisRow:
|
||||
RedisValueError: If value is not set
|
||||
"""
|
||||
import arrow
|
||||
from Services.Redis.conn import redis_cli
|
||||
|
||||
if not cls.key:
|
||||
if not self.key:
|
||||
raise RedisKeyError("Cannot save data without a key")
|
||||
if not cls.value:
|
||||
if not self.value:
|
||||
raise RedisValueError("Cannot save empty data")
|
||||
|
||||
if cls.expires_at:
|
||||
redis_cli.setex(name=cls.redis_key, time=cls.expires_at, value=cls.value)
|
||||
cls.expires_at_string = str(
|
||||
if self.expires_at:
|
||||
redis_cli.setex(
|
||||
name=self.redis_key, time=self.get_expiry_time(), value=self.value
|
||||
)
|
||||
self.expires_at_string = str(
|
||||
arrow.now()
|
||||
.shift(seconds=cls.get_expiry_time())
|
||||
.shift(seconds=self.get_expiry_time())
|
||||
.format("YYYY-MM-DD HH:mm:ss")
|
||||
)
|
||||
return cls.value
|
||||
return self.value
|
||||
redis_cli.set(name=self.redis_key, value=self.value)
|
||||
self.expires_at = None
|
||||
self.expires_at_string = None
|
||||
return self.value
|
||||
|
||||
redis_cli.set(name=cls.redis_key, value=cls.value)
|
||||
cls.expires_at = None
|
||||
cls.expires_at_string = None
|
||||
return cls.value
|
||||
|
||||
@classmethod
|
||||
def remove(cls, key: str) -> None:
|
||||
def remove(self, key: str) -> None:
|
||||
"""
|
||||
Remove a key from the stored dictionary.
|
||||
|
||||
@@ -237,16 +233,24 @@ class RedisRow:
|
||||
KeyError: If key doesn't exist
|
||||
RedisValueError: If stored value is not a dictionary
|
||||
"""
|
||||
current_data = cls.data
|
||||
current_data = self.row
|
||||
if not isinstance(current_data, dict):
|
||||
raise RedisValueError("Cannot remove key from non-dictionary data")
|
||||
|
||||
try:
|
||||
current_data.pop(key)
|
||||
cls.feed(current_data)
|
||||
self.feed(current_data)
|
||||
self.save()
|
||||
except KeyError:
|
||||
raise KeyError(f"Key '{key}' not found in stored data")
|
||||
|
||||
def delete(self) -> None:
|
||||
"""Delete the key from Redis."""
|
||||
try:
|
||||
redis_cli.delete(self.redis_key)
|
||||
except Exception as e:
|
||||
print(f"Error deleting key: {str(e)}")
|
||||
|
||||
@property
|
||||
def keys(self) -> str:
|
||||
"""
|
||||
@@ -257,8 +261,7 @@ class RedisRow:
|
||||
"""
|
||||
return self.key.decode() if isinstance(self.key, bytes) else self.key
|
||||
|
||||
@classmethod
|
||||
def set_key(cls, key: Union[str, bytes]) -> None:
|
||||
def set_key(self, key: Union[str, bytes]) -> None:
|
||||
"""
|
||||
Set key ensuring bytes format.
|
||||
|
||||
@@ -267,7 +270,7 @@ class RedisRow:
|
||||
"""
|
||||
if not key:
|
||||
raise RedisKeyError("Cannot set empty key")
|
||||
cls.key = key if isinstance(key, bytes) else str(key).encode()
|
||||
self.key = key if isinstance(key, bytes) else str(key).encode()
|
||||
|
||||
@property
|
||||
def redis_key(self) -> bytes:
|
||||
@@ -280,7 +283,7 @@ class RedisRow:
|
||||
return self.key if isinstance(self.key, bytes) else str(self.key).encode()
|
||||
|
||||
@property
|
||||
def data(self) -> Union[Dict, List]:
|
||||
def row(self) -> Union[Dict, List]:
|
||||
"""
|
||||
Get stored value as Python object.
|
||||
|
||||
@@ -290,6 +293,7 @@ class RedisRow:
|
||||
try:
|
||||
return json.loads(self.value)
|
||||
except json.JSONDecodeError as e:
|
||||
# return self.value
|
||||
raise RedisValueError(f"Invalid JSON format in stored value: {str(e)}")
|
||||
|
||||
@property
|
||||
@@ -302,5 +306,5 @@ class RedisRow:
|
||||
"""
|
||||
return {
|
||||
"keys": self.keys,
|
||||
"value": self.data,
|
||||
"value": self.row,
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ class RedisResponse:
|
||||
self.data_type = "dict"
|
||||
elif isinstance(data, list):
|
||||
self.data_type = "list"
|
||||
elif isinstance(data, RedisRow):
|
||||
self.data_type = "row"
|
||||
elif data is None:
|
||||
self.data_type = None
|
||||
self.error = error
|
||||
@@ -30,12 +32,16 @@ class RedisResponse:
|
||||
"status": self.status,
|
||||
"message": self.message,
|
||||
"count": self.count,
|
||||
"dataType": self.data_type,
|
||||
"dataType": getattr(self, "data_type", None),
|
||||
}
|
||||
if isinstance(data, RedisRow):
|
||||
return {"data": {data.keys: data.data}, **main_dict}
|
||||
dict_return = {data.keys: data.row}
|
||||
dict_return.update(dict(main_dict))
|
||||
return dict_return
|
||||
elif isinstance(data, list):
|
||||
return {"data": {row.keys: row.data for row in data}, **main_dict}
|
||||
dict_return = {row.keys: row.data for row in data}
|
||||
dict_return.update(dict(main_dict))
|
||||
return dict_return
|
||||
|
||||
@property
|
||||
def all(self) -> Union[Optional[List[RedisRow]]]:
|
||||
@@ -43,11 +49,20 @@ class RedisResponse:
|
||||
|
||||
@property
|
||||
def count(self) -> int:
|
||||
return len(self.all)
|
||||
print()
|
||||
row = self.all
|
||||
if isinstance(row, list):
|
||||
return len(row)
|
||||
elif isinstance(row, RedisRow):
|
||||
return 1
|
||||
|
||||
@property
|
||||
def first(self) -> Union[RedisRow, None]:
|
||||
print("self.data", self.data)
|
||||
if self.data:
|
||||
return self.data[0]
|
||||
if isinstance(self.data, list):
|
||||
return self.data[0]
|
||||
elif isinstance(self.data, RedisRow):
|
||||
return self.row
|
||||
self.status = False
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user