''' Created on 21 août 2024 @author: denis ''' import logging import asyncio from typing import Union from abc import ABC from reduct import Client, Bucket, BucketSettings #, QuotaType from reduct.time import unix_timestamp_from_any, TIME_PRECISION #, unix_timestamp_to_datetime from datetime import datetime, timedelta from django.utils import timezone from django.conf import settings logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) class ReductStoreBase(ABC): def __init__(self, url, api_token, name, quota_type=None, quota_size=1000_000_000): self.bucket_name = name self.client = Client(url, api_token=api_token) self.quota_type = quota_type self.quota_size = quota_size self.bucket: Bucket = asyncio.run(self.create_bucket()) logger.info(f"==== {url} token:{api_token}") async def create_bucket(self): settings = BucketSettings( quota_type=self.quota_type, quota_size=self.quota_size, exist_ok=True, ) return await self.client.create_bucket(self.bucket_name, settings, exist_ok=True) async def change_bucket(self, **settings): new_settings = BucketSettings(**settings) await self.bucket.set_settings(new_settings) async def remove_bucket(self): await self.bucket.remove() async def write(self, entry_name, data, timestamp=None, content_type=None, labels=None): await self.bucket.write(entry_name, data, timestamp=timestamp, content_type=content_type, labels=labels) def query(self, entry_name, start, stop, ttl=None, when=None): return self.bucket.query(entry_name, start=start, stop=stop, ttl=ttl, when=when) async def remove_query(self, entry_name, start, stop, when=None): return await self.bucket.remove_query(entry_name, start=start, stop=stop, when=when) async def read(self, entry_name, timestamp, head=False): async with self.bucket.read(entry_name, timestamp=timestamp, head=head) as record: return await record.read_all() async def record_content(self, entry_name, timestamp, head=False): async with self.bucket.read(entry_name, timestamp=timestamp, head=head) as record: content = await record.read_all() return record, content class ReductStore(ReductStoreBase): def __init__(self, name): super().__init__(settings.REDUCTSTORE_URL, settings.REDUCTSTORE_TOKEN, name=name) async def old_last_dates(client_db, entry_name='uuid'): oldest, latest = 0, 0 infos = await client_db.bucket.get_entry_list() for info in infos: if info.name == entry_name: oldest, latest = info.oldest_record, info.latest_record break return oldest, latest async def date_posterior_to(client_db, uuid: str, post: Union[int, str]): last = None oldest, latest = await old_last_dates(client_db, uuid) if oldest and post: dtpost = timezone.now() - timedelta(seconds=int(post)) ts = unix_timestamp_from_any(dtpost) last = ts if ts < latest else None return oldest, last async def dates_filter(client_db, uuid: str, begin: Union[int, datetime, float, str], end: Union[int, datetime, float, str] = None, duration: int = 0): oldest, latest = await old_last_dates(client_db, uuid) ts_from, ts_to = 0, 0 if latest: ts_from = unix_timestamp_from_any(begin) if begin else oldest if ts_from < oldest: ts_from = oldest if end is None: if not duration: ts_to = latest else: ts_to = ts_from + (duration * TIME_PRECISION) if ts_to > latest: ts_to = latest else: ts_to = unix_timestamp_from_any(end) if end else latest if ts_to > latest: ts_to = latest return ts_from, ts_to if ts_from