306 lines
11 KiB
Python
306 lines
11 KiB
Python
import logging
|
|
from rest_framework.views import APIView
|
|
from rest_framework.response import Response
|
|
from gvsdsdk import StatsManager
|
|
from gvsds.apps.stats.serializers import (
|
|
StatMetricSerializer,
|
|
StatDimensionSerializer,
|
|
StatRecordSerializer,
|
|
StatRankingSerializer,
|
|
StatConfigSerializer,
|
|
MetricDefineSerializer,
|
|
DimensionDefineSerializer,
|
|
IncrementSerializer,
|
|
IncrementMultiSerializer,
|
|
StatQuerySerializer,
|
|
RankingGenerateSerializer,
|
|
RankingQuerySerializer,
|
|
ResetSerializer,
|
|
PurgeSerializer,
|
|
ConfigSerializer,
|
|
)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def _ok(data=None, msg='success'):
|
|
return {'code': 200, 'msg': msg, 'data': data}
|
|
|
|
|
|
def _err(msg, code=400):
|
|
return {'code': code, 'msg': msg, 'data': None}
|
|
|
|
|
|
def _get_tenant(request):
|
|
return request.headers.get('X-Tenant-UUID', '') or getattr(request, 'tenant_uuid', '')
|
|
|
|
|
|
class MetricConfigView(APIView):
|
|
|
|
def get(self, request):
|
|
tenant = _get_tenant(request)
|
|
if not tenant:
|
|
return Response(_err('缺少租户标识', 401), status=401)
|
|
metrics = StatsManager.ListMetrics(tenant, status=1)
|
|
return Response(_ok(StatMetricSerializer(metrics, many=True).data))
|
|
|
|
def post(self, request):
|
|
tenant = _get_tenant(request)
|
|
if not tenant:
|
|
return Response(_err('缺少租户标识', 401), status=401)
|
|
ser = MetricDefineSerializer(data=request.data)
|
|
if not ser.IsValid():
|
|
return Response(_err(ser.errors, 400), status=400)
|
|
metric = StatsManager.DefineMetric(
|
|
tenant_uuid=tenant,
|
|
metric_code=ser.ValidatedData['MetricCode'],
|
|
metric_name=ser.ValidatedData['MetricName'],
|
|
metric_type=ser.ValidatedData.get('MetricType', 'count'),
|
|
unit=ser.ValidatedData.get('Unit', '次'),
|
|
default_value=ser.ValidatedData.get('DefaultValue', 0),
|
|
)
|
|
return Response(_ok(StatMetricSerializer(metric).data), status=201)
|
|
|
|
|
|
class DimensionView(APIView):
|
|
|
|
def get(self, request):
|
|
tenant = _get_tenant(request)
|
|
if not tenant:
|
|
return Response(_err('缺少租户标识', 401), status=401)
|
|
dims = StatsManager.ListDimensions(tenant, status=1)
|
|
return Response(_ok(StatDimensionSerializer(dims, many=True).data))
|
|
|
|
def post(self, request):
|
|
tenant = _get_tenant(request)
|
|
if not tenant:
|
|
return Response(_err('缺少租户标识', 401), status=401)
|
|
ser = DimensionDefineSerializer(data=request.data)
|
|
if not ser.IsValid():
|
|
return Response(_err(ser.errors, 400), status=400)
|
|
dim = StatsManager.DefineDimension(
|
|
tenant_uuid=tenant,
|
|
dimension_code=ser.ValidatedData['DimensionCode'],
|
|
dimension_name=ser.ValidatedData['DimensionName'],
|
|
)
|
|
return Response(_ok(StatDimensionSerializer(dim).data), status=201)
|
|
|
|
|
|
class IncrementView(APIView):
|
|
|
|
def post(self, request):
|
|
tenant = _get_tenant(request)
|
|
if not tenant:
|
|
return Response(_err('缺少租户标识', 401), status=401)
|
|
ser = IncrementSerializer(data=request.data)
|
|
if not ser.IsValid():
|
|
return Response(_err(ser.errors, 400), status=400)
|
|
import datetime as _dt
|
|
_date = ser.ValidatedData.get('Date') or _dt.date.today()
|
|
_period_type = ser.ValidatedData.get('PeriodType', 'daily')
|
|
if _period_type == 'daily':
|
|
_period_key = _date.strftime('%Y-%m-%d')
|
|
elif _period_type == 'monthly':
|
|
_period_key = _date.strftime('%Y-%m')
|
|
elif _period_type == 'hourly':
|
|
_period_key = _date.strftime('%Y-%m-%dT%H')
|
|
else:
|
|
_period_key = str(_date)
|
|
record, _created = StatsManager.AtomicIncrement(
|
|
tenant_uuid=tenant,
|
|
period_type=_period_type,
|
|
period_key=_period_key,
|
|
metric_uuid=ser.ValidatedData['MetricUUID'],
|
|
dimension_values=ser.ValidatedData['DimensionValues'],
|
|
delta=ser.ValidatedData.get('Delta', 1),
|
|
)
|
|
return Response(_ok(StatRecordSerializer(record).data), status=201)
|
|
|
|
|
|
class IncrementMultiView(APIView):
|
|
|
|
def post(self, request):
|
|
tenant = _get_tenant(request)
|
|
if not tenant:
|
|
return Response(_err('缺少租户标识', 401), status=401)
|
|
ser = IncrementMultiSerializer(data=request.data)
|
|
if not ser.IsValid():
|
|
return Response(_err(ser.errors, 400), status=400)
|
|
import datetime as _dt
|
|
_date = ser.ValidatedData.get('Date') or _dt.date.today()
|
|
records = StatsManager.AtomicIncrementMulti(
|
|
tenant_uuid=tenant,
|
|
period_type='daily',
|
|
period_key=_date.strftime('%Y-%m-%d'),
|
|
increments=ser.ValidatedData['Increments'],
|
|
)
|
|
return Response(_ok(StatRecordSerializer(records, many=True).data), status=201)
|
|
|
|
|
|
class QueryView(APIView):
|
|
|
|
def get(self, request):
|
|
tenant = _get_tenant(request)
|
|
if not tenant:
|
|
return Response(_err('缺少租户标识', 401), status=401)
|
|
|
|
MetricUUID = request.query_params.get('MetricUUID', '')
|
|
StartDate = request.query_params.get('StartDate')
|
|
EndDate = request.query_params.get('EndDate')
|
|
PeriodType = request.query_params.get('PeriodType', 'daily')
|
|
|
|
if StartDate and EndDate:
|
|
import datetime
|
|
sd = datetime.date.fromisoformat(StartDate)
|
|
ed = datetime.date.fromisoformat(EndDate)
|
|
records = StatsManager.QueryRange(
|
|
tenant_uuid=tenant,
|
|
metric_uuid=MetricUUID,
|
|
period_type=PeriodType,
|
|
start_key=sd.strftime('%Y-%m-%d'),
|
|
end_key=ed.strftime('%Y-%m-%d'),
|
|
)
|
|
elif MetricUUID:
|
|
import datetime
|
|
records = StatsManager.QueryPeriod(
|
|
tenant_uuid=tenant,
|
|
metric_uuid=MetricUUID,
|
|
period_type='daily',
|
|
period_key=datetime.date.today().strftime('%Y-%m-%d'),
|
|
)
|
|
else:
|
|
return Response(_err('缺少查询参数', 400), status=400)
|
|
|
|
return Response(_ok(StatRecordSerializer(records, many=True).data))
|
|
|
|
|
|
class AggregateView(APIView):
|
|
|
|
def get(self, request):
|
|
tenant = _get_tenant(request)
|
|
if not tenant:
|
|
return Response(_err('缺少租户标识', 401), status=401)
|
|
|
|
MetricUUID = request.query_params.get('MetricUUID', '')
|
|
StartDate = request.query_params.get('StartDate')
|
|
EndDate = request.query_params.get('EndDate')
|
|
DimensionKey = request.query_params.get('DimensionKey', 'UserID')
|
|
TopN = int(request.query_params.get('TopN', 50))
|
|
|
|
if not MetricUUID or not StartDate or not EndDate:
|
|
return Response(_err('缺少必要参数', 400), status=400)
|
|
|
|
import datetime
|
|
sd = datetime.date.fromisoformat(StartDate)
|
|
ed = datetime.date.fromisoformat(EndDate)
|
|
results = StatsManager.AggregateByDimension(
|
|
tenant_uuid=tenant,
|
|
metric_uuid=MetricUUID,
|
|
period_type='daily',
|
|
start_key=sd.strftime('%Y-%m-%d'),
|
|
end_key=ed.strftime('%Y-%m-%d'),
|
|
dimension_key=DimensionKey,
|
|
)
|
|
return Response(_ok(results[:TopN]))
|
|
|
|
|
|
class RankingView(APIView):
|
|
|
|
def get(self, request):
|
|
tenant = _get_tenant(request)
|
|
if not tenant:
|
|
return Response(_err('缺少租户标识', 401), status=401)
|
|
|
|
RankingPeriod = request.query_params.get('RankingPeriod', 'daily')
|
|
RankingDate = request.query_params.get('RankingDate')
|
|
MetricUUID = request.query_params.get('MetricUUID', '')
|
|
TopN = request.query_params.get('TopN')
|
|
|
|
import datetime
|
|
rd = datetime.date.fromisoformat(RankingDate) if RankingDate else None
|
|
rankings = StatsManager.QueryRanking(
|
|
tenant_uuid=tenant,
|
|
ranking_period=RankingPeriod,
|
|
ranking_date=rd,
|
|
metric_uuid=MetricUUID or None,
|
|
top_n=int(TopN) if TopN else None,
|
|
)
|
|
return Response(_ok(StatRankingSerializer(rankings, many=True).data))
|
|
|
|
def post(self, request):
|
|
tenant = _get_tenant(request)
|
|
if not tenant:
|
|
return Response(_err('缺少租户标识', 401), status=401)
|
|
ser = RankingGenerateSerializer(data=request.data)
|
|
if not ser.IsValid():
|
|
return Response(_err(ser.errors, 400), status=400)
|
|
rankings = StatsManager.GenerateDailyRanking(
|
|
tenant_uuid=tenant,
|
|
metric_uuid=ser.ValidatedData['MetricUUID'],
|
|
ranking_date=ser.ValidatedData.get('RankingDate'),
|
|
dimension_filter=ser.ValidatedData.get('DimensionFilter'),
|
|
top_n=ser.ValidatedData.get('TopN', 50),
|
|
)
|
|
return Response(_ok(StatRankingSerializer(rankings, many=True).data), status=201)
|
|
|
|
|
|
class ResetView(APIView):
|
|
|
|
def post(self, request):
|
|
tenant = _get_tenant(request)
|
|
if not tenant:
|
|
return Response(_err('缺少租户标识', 401), status=401)
|
|
ser = ResetSerializer(data=request.data)
|
|
if not ser.IsValid():
|
|
return Response(_err(ser.errors, 400), status=400)
|
|
deleted = StatsManager.DailyReset(
|
|
tenant_uuid=tenant,
|
|
metric_uuid=ser.ValidatedData['MetricUUID'],
|
|
target_date=ser.ValidatedData.get('TargetDate'),
|
|
)
|
|
return Response(_ok({'deleted_count': deleted}))
|
|
|
|
|
|
class PurgeView(APIView):
|
|
|
|
def post(self, request):
|
|
tenant = _get_tenant(request)
|
|
if not tenant:
|
|
return Response(_err('缺少租户标识', 401), status=401)
|
|
ser = PurgeSerializer(data=request.data)
|
|
if not ser.IsValid():
|
|
return Response(_err(ser.errors, 400), status=400)
|
|
deleted = StatsManager.PurgeOld(
|
|
tenant_uuid=tenant,
|
|
before_date=ser.ValidatedData['BeforeDate'],
|
|
period_type=ser.ValidatedData.get('PeriodType', 'daily'),
|
|
)
|
|
return Response(_ok({'deleted_count': deleted}))
|
|
|
|
|
|
class StatConfigView(APIView):
|
|
|
|
def get(self, request):
|
|
tenant = _get_tenant(request)
|
|
if not tenant:
|
|
return Response(_err('缺少租户标识', 401), status=401)
|
|
configs = StatsManager.ListConfigs(tenant, status=1)
|
|
return Response(_ok(StatConfigSerializer(configs, many=True).data))
|
|
|
|
def post(self, request):
|
|
tenant = _get_tenant(request)
|
|
if not tenant:
|
|
return Response(_err('缺少租户标识', 401), status=401)
|
|
ser = ConfigSerializer(data=request.data)
|
|
if not ser.IsValid():
|
|
return Response(_err(ser.errors, 400), status=400)
|
|
config = StatsManager.ConfigureAutoReset(
|
|
tenant_uuid=tenant,
|
|
metric_uuid=ser.ValidatedData['MetricUUID'],
|
|
auto_reset=ser.ValidatedData.get('AutoReset', True),
|
|
reset_cron=ser.ValidatedData.get('ResetCron', '0 0 * * *'),
|
|
enable_ranking=ser.ValidatedData.get('EnableRanking', False),
|
|
ranking_top_n=ser.ValidatedData.get('RankingTopN', 50),
|
|
retention_days=ser.ValidatedData.get('RetentionDays', 365),
|
|
)
|
|
return Response(_ok(StatConfigSerializer(config).data), status=201) |