import time from typing import Optional from django.db.models import Q, Min, Max from django.core.cache import cache from rest_framework import serializers from counter.models import DeviceCount, DeviceInfo from mosquito.models import DeviceInfo as MosquitoDeviceInfo from mosquito.models import MosqPostStatistic, DevicePostStatistic class DeviceSourceException(Exception): """ source should be counter | mosquito """ def get_vol(device_id, cur_vol): vol_min_key = '{}_vol_min'.format(device_id) vol_max_key = '{}_vol_max'.format(device_id) vol_min = cache.get(vol_min_key) vol_max = cache.get(vol_max_key) if not vol_min: vol_min = DeviceCount.objects.filter(device_id=device_id).aggregate(vol_min=Min('vol'))['vol_min'] cache.set(vol_min_key, vol_min, 60 * 5) if not vol_max: vol_max = DeviceCount.objects.filter(device_id=device_id).aggregate(vol_max=Max('vol'))['vol_max'] cache.set(vol_max_key, vol_max, 60 * 5) vol = 100 * (int(cur_vol) - int(vol_min)) / (int(vol_max) - int(vol_min)) return '{}{}'.format(round(vol, 1), '%') def get_device_latest_by_cache(device_id, key="device_latest_{}"): """ Get latest device from cache """ latest_key = key.format(device_id) latest = cache.get(latest_key) if latest: return latest latest = DeviceCount.objects.filter(device_id=device_id).order_by('-data_time').first() if latest is None: return None s = DeviceCountBaseSerializer(latest) data = s.data # set cache before return set_device_latest_cache(latest_key, data) return data def set_device_latest_cache(key: str, value: serializers.ModelSerializer, timeout=60 * 10): cache.set(key, value, timeout) class DeviceCountBaseSerializer(serializers.ModelSerializer): class Meta: model = DeviceCount fields = '__all__' class DeviceInfoBaseSerializer(serializers.ModelSerializer): class Meta: model = MosquitoDeviceInfo fields = '__all__' device_source_map = { 'counter': DeviceCountBaseSerializer, 'mosquito': DeviceInfoBaseSerializer, } def get_device(device_id: str, source: str) -> Optional[dict]: """ Get device data serialized by DeviceCountBaseSerializer (counter) or DeviceInfoBaseSerializer (mosquito), For source 'counter', we get device from device_counter table not device_info, because the [lon, lat] info is located in device_counter. For source 'mosquito', we get device from mosquito/DeviceInfo model, due to the [point_x, point_y] is what we want. params: device_id, params: source, counter | mosquito return: dict """ device_key = 'device_lon_{}'.format(device_id) device = cache.get(device_key) if device: return device if source == 'counter': qs = DeviceCount.objects.filter(~Q(longitude=0), device_id=device_id).order_by('-data_time') elif source == 'mosquito': qs = MosquitoDeviceInfo.objects.filter(device_id=device_id) else: raise DeviceSourceException("source should be counter or mosquito") if qs.count() == 0: return None device = qs.first() s = device_source_map[source](device) print(s) data = s.data print(data) cache.set(device_key, data, 60 * 10) return data class DeviceCountSerializer(serializers.ModelSerializer): device_name = serializers.SerializerMethodField() mosq_count = serializers.SerializerMethodField() signal = serializers.SerializerMethodField() energy = serializers.SerializerMethodField() calc_time = serializers.SerializerMethodField() longitude = serializers.SerializerMethodField() latitude = serializers.SerializerMethodField() class Meta: model = DeviceCount fields = [ 'id', 'device_id', 'device_name', 'signal', 'mosq_count', 'energy', 'calc_time', 'longitude', 'latitude' ] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.device = None def get_mosq_count(self, obj): return obj.count def get_signal(self, obj): return obj.csq def get_energy(self, obj): if float(obj.vol) <= 100: return '{}{}'.format(round(float(obj.vol), 1), '%') return get_vol(obj.device_id, obj.vol) def get_calc_time(self, obj): return obj.data_time.strftime('%Y-%m-%d %H:%M:%S') def get_device_name(self, obj): device_id = obj.device_id device = cache.get(device_id) if device: self.device = device return device.device_name qs = MosquitoDeviceInfo.objects.filter(device_id=device_id) if qs.count() > 0: device = qs[0] self.device = device cache.set(device_id, device, 60 * 60 * 24) return device.device_name return None def get_longitude(self, obj): device = self.device if device: if device.longitude: return device.longitude return obj.longitude def get_latitude(self, obj): device = self.device if device: if device.latitude: return device.latitude return obj.latitude class DeviceCountWithInfoSerializer(DeviceCountSerializer): location_id = serializers.SerializerMethodField() weather_code = serializers.SerializerMethodField() point_x = serializers.SerializerMethodField() point_y = serializers.SerializerMethodField() status = serializers.SerializerMethodField() class Meta: model = DeviceCount fields = [ 'id', 'device_id', 'device_name', 'signal', 'mosq_count', 'energy', 'calc_time', 'longitude', 'latitude', 'location_id', 'weather_code', 'point_x', 'point_y', 'status' ] def get_location_id(self, obj): device = self.device if device: if device.location_id: return device.location_id return None def get_weather_code(self, obj): device = self.device if device: if device.weather_code: return device.weather_code return None def get_point_x(self, obj): device = self.device if device: if device.point_x: return device.point_x return None def get_point_y(self, obj): device = self.device if device: if device.point_y: return device.point_y return None def get_status(self, obj): device = self.device online = None if device: print(device) qs = DeviceInfo.objects.filter(device_id=device) online = qs[0].online return online class DeviceInfoSerializer(serializers.ModelSerializer): device_name = serializers.SerializerMethodField() status = serializers.SerializerMethodField() count = serializers.SerializerMethodField() signal = serializers.SerializerMethodField() energy = serializers.SerializerMethodField() coordinate = serializers.SerializerMethodField() time = serializers.SerializerMethodField() class Meta: model = DeviceInfo fields = [ 'device_id', 'device_name', 'chip_type', 'status', 'count', 'signal', 'energy', 'coordinate', 'time', ] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.latest = None self.cur_device = None def get_device_name(self, obj): qs = MosquitoDeviceInfo.objects.filter(device_id=obj.device_id) if qs.count() > 0: device = qs.first() self.cur_device = device return device.device_name return None @staticmethod def get_status(obj): return obj.online def get_count(self, obj) -> str: self.latest = get_device_latest_by_cache(obj.device_id) if self.latest: return self.latest['count'] return '0' def get_signal(self, obj) -> str: if self.latest: return self.latest['csq'] return '0' def get_energy(self, obj) -> str: if self.latest: if float(self.latest['vol']) <= 100: return '{}{}'.format(round(float(self.latest['vol']), 1), '%') return get_vol(obj.device_id, self.latest['vol']) return '0%' def get_coordinate(self, obj): if self.cur_device: lon, lat = [self.cur_device.longitude, self.cur_device.latitude] if lon and lat: return [lon, lat] # query = DeviceCount.objects.filter(~Q(longitude=0), device_id=obj.device_id).order_by('-data_time') device = get_device(obj.device_id, source='counter') if device: return [device['longitude'], device['latitude']] return None def get_time(self, obj): if self.latest: return self.latest['data_time'].replace('T', ' ').replace('Z', '') return None class LatestDailySerializer(serializers.ModelSerializer): class Meta: model = MosqPostStatistic fields = [ 'date', 'total', 'increment', ] class DeviceLogHistorySerializer(serializers.ModelSerializer): class Meta: model = DevicePostStatistic fields = [ 'device_id', 'total', 'date' ]