From d2392a3c74d2d4dc7d6d337e565b0a1d4e491380 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 15 Jun 2023 16:57:29 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84enm=20task=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/enm/migrations/0001_initial.py | 62 ++++++++++----------- apps/enm/models.py | 13 +++-- apps/enm/serializers.py | 13 ++++- apps/enm/tasks.py | 85 +++++++++++++++++++---------- apps/enm/urls.py | 3 +- apps/enm/views.py | 55 ++++++++++++++++++- 6 files changed, 158 insertions(+), 73 deletions(-) diff --git a/apps/enm/migrations/0001_initial.py b/apps/enm/migrations/0001_initial.py index cedb0997..9cdebe3b 100644 --- a/apps/enm/migrations/0001_initial.py +++ b/apps/enm/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.2.12 on 2023-06-14 07:27 +# Generated by Django 3.2.12 on 2023-06-15 08:27 from django.conf import settings from django.db import migrations, models @@ -34,6 +34,28 @@ class Migration(migrations.Migration): 'verbose_name': '测点集', }, ), + migrations.CreateModel( + name='Mpoint', + fields=[ + ('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')), + ('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')), + ('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')), + ('name', models.CharField(max_length=50, verbose_name='测点名称')), + ('code', models.CharField(max_length=50, unique=True, verbose_name='测点编号')), + ('unit', models.CharField(max_length=50, verbose_name='单位')), + ('cate', models.CharField(default='elec', max_length=50, verbose_name='分类')), + ('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mpoint_belong_dept', to='system.dept', verbose_name='所属部门')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mpoint_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('ep_belong', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mp_ep_belong', to='em.equipment', verbose_name='属于哪个设备')), + ('ep_monitored', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mp_ep_monitored', to='em.equipment', verbose_name='监测哪个设备')), + ('mgroup', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='enm.mgroup', verbose_name='所在集合')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mpoint_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'abstract': False, + }, + ), migrations.CreateModel( name='SfLog', fields=[ @@ -157,22 +179,19 @@ class Migration(migrations.Migration): }, ), migrations.CreateModel( - name='Mpoint', + name='MpointStat', fields=[ ('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')), ('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')), ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')), ('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')), - ('name', models.CharField(max_length=50, verbose_name='测点名称')), - ('code', models.CharField(max_length=50, unique=True, verbose_name='测点编号')), - ('unit', models.CharField(max_length=50, verbose_name='单位')), - ('cate', models.CharField(default='elec', max_length=50, verbose_name='分类')), - ('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mpoint_belong_dept', to='system.dept', verbose_name='所属部门')), - ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mpoint_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), - ('ep_belong', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mp_ep_belong', to='em.equipment', verbose_name='属于哪个设备')), - ('ep_monitored', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mp_ep_monitored', to='em.equipment', verbose_name='监测哪个设备')), - ('mgroup', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='enm.mgroup', verbose_name='所在集合')), - ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mpoint_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ('type', models.CharField(default='hour', help_text='year/month/day/hour', max_length=50, verbose_name='统计维度')), + ('year', models.PositiveSmallIntegerField(verbose_name='年')), + ('month', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='月')), + ('day', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='日')), + ('hour', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='时')), + ('val', models.FloatField(default=0, verbose_name='统计值')), + ('mpoint', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='enm.mpoint', verbose_name='关联测点')), ], options={ 'abstract': False, @@ -195,25 +214,6 @@ class Migration(migrations.Migration): 'abstract': False, }, ), - migrations.CreateModel( - name='HourStat', - fields=[ - ('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')), - ('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')), - ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')), - ('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')), - ('year', models.PositiveSmallIntegerField(verbose_name='年')), - ('month', models.PositiveSmallIntegerField(verbose_name='月')), - ('day', models.PositiveSmallIntegerField(verbose_name='日')), - ('hour', models.PositiveSmallIntegerField(verbose_name='时')), - ('val', models.FloatField(default=0, verbose_name='统计值')), - ('is_calculated', models.BooleanField(default=False, verbose_name='是否计算过')), - ('mpoint', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='enm.mpoint', verbose_name='关联测点')), - ], - options={ - 'abstract': False, - }, - ), migrations.CreateModel( name='Goal', fields=[ diff --git a/apps/enm/models.py b/apps/enm/models.py index 58afd33c..01b88b4b 100644 --- a/apps/enm/models.py +++ b/apps/enm/models.py @@ -94,16 +94,17 @@ class StSfLog(BaseModel): class Meta: ordering = ['sflog__start_time'] -class HourStat(BaseModel): - """小时统计表 + +class MpointStat(BaseModel): + """测点统计表 """ + type = models.CharField('统计维度', max_length=50, default='hour', help_text='year/month/day/hour') year = models.PositiveSmallIntegerField('年') - month = models.PositiveSmallIntegerField('月') - day = models.PositiveSmallIntegerField('日') - hour = models.PositiveSmallIntegerField('时') + month = models.PositiveSmallIntegerField('月', null=True, blank=True) + day = models.PositiveSmallIntegerField('日', null=True, blank=True) + hour = models.PositiveSmallIntegerField('时', null=True, blank=True) mpoint = models.ForeignKey(Mpoint, verbose_name='关联测点', on_delete=models.CASCADE) val = models.FloatField('统计值', default=0) - is_calculated = models.BooleanField('是否计算过', default=False) class Goal(CommonADModel): diff --git a/apps/enm/serializers.py b/apps/enm/serializers.py index 7aa70705..b6be4f07 100644 --- a/apps/enm/serializers.py +++ b/apps/enm/serializers.py @@ -1,5 +1,5 @@ from apps.utils.serializers import CustomModelSerializer -from apps.enm.models import Mpoint, Mgroup, MpLog, Team, SfLog, StLog +from apps.enm.models import Mpoint, Mgroup, MpLog, Team, SfLog, StLog, StSfLog from apps.system.models import Dept from apps.system.serializers import DeptSimpleSerializer from apps.utils.constants import EXCLUDE_FIELDS, EXCLUDE_FIELDS_BASE @@ -63,4 +63,13 @@ class SfLogSerializer(CustomModelSerializer): u_fields = self.Meta.update_fields new_data = {key: data[key] for key in u_fields if key in data} return super().to_internal_value(new_data) - return super().to_internal_value(data) \ No newline at end of file + return super().to_internal_value(data) + + +class StSfLogSerializer(CustomModelSerializer): + stlog_ = StLogSerializer(source='stlog', read_only=True) + sflog_ = SfLogSerializer(source='sflog', read_only=True) + class Meta: + model = StSfLog + fields = '__all__' + read_only_fields = EXCLUDE_FIELDS + ['stlog', 'sflog', 'is_current_down'] \ No newline at end of file diff --git a/apps/enm/tasks.py b/apps/enm/tasks.py index 3670bb54..599524ed 100644 --- a/apps/enm/tasks.py +++ b/apps/enm/tasks.py @@ -5,8 +5,14 @@ from celery import shared_task from apps.utils.sql import DbConnection from server.settings import get_sysconfig from django.core.cache import cache -from apps.enm.models import MpLog, Mpoint, HourStat +from apps.enm.models import MpLog, Mpoint, MpointStat import datetime +from django.db.models import Sum + +def get_current_and_previous_time(): + now = datetime.datetime.now() + pre = now - datetime.timedelta(hours=1) + return now, pre def get_current_and_previous_hour(): now = datetime.datetime.now() @@ -47,35 +53,54 @@ def get_tag_val(): mr_one.save() @shared_task(base=CustomTask) -def cal_hourstat(): +def cal_mpointstat_hour(mpointId: str, year: int, month: int, day: int, hour: int): """ - 计算小时统计量,默认计算本小时和上小时 + 计算某一测点, 某一时间点某一小时的统计值 """ - for mpoint in Mpoint.objects.all(): - c_t_r, p_t_r = get_current_and_previous_hour() - if mpoint.cate == 'elec': # 是否是电能 - # 计算本小时,可能不需要 - start_time = c_t_r[0] - params = {'mpoint': mpoint} - params['year'], params['month'], params['day'], params['hour'] = start_time.year, start_time.month, start_time.day, start_time.hour - mrs = MpLog.objects.filter(mpoint=mpoint, tag_update__gte=c_t_r[0], tag_update__lte=c_t_r[1]).order_by('tag_update') - val = 0 - if mrs.exists(): - val = mrs.last() - mrs.first() - hs, _ = HourStat.objects.get_or_create(**params, defaults=params) - hs.val = val - hs.save() + mpoint = Mpoint.objects.get(id=mpointId) + if mpoint.cate == 'elec': # 是否是电能 + params = {'mpoint': mpoint, 'type': 'hour'} + params['year'], params['month'], params['day'], params['hour'] = year, month, day, hour + mrs = MpLog.objects.filter( + mpoint=mpoint, + tag_update__year=params['year'], + tag_update__month=params['month'], + tag_update__day=params['day'], + tag_update__hour= params['hour']).order_by('tag_update') + val = 0 + if mrs.exists(): + val = mrs.last() - mrs.first() + ms, _ = MpointStat.objects.get_or_create(**params, defaults=params) + ms.val = val + ms.save() + # 更新更高级别的值 + sum_dict_day = MpointStat.objects.filter(type='hour', mpoint=mpoint, year=year, month=month, day=day).aggregate(sum=Sum('val')) + params_day = {'type':'day', 'mpoint': mpoint, 'year': year, 'month': month, 'day': day} + ms_day, _ = MpointStat.objects.get_or_create(**params_day, defaults=params_day) + ms_day.val = sum_dict_day['sum'] + ms_day.save() - # 计算上小时 - start_time2 = p_t_r[0] - params = {'mpoint': mpoint} - params['year'], params['month'], params['day'], params['hour'] = start_time2.year, start_time2.month, start_time2.day, start_time2.hour - hs, _ = HourStat.objects.get_or_create(**params, defaults=params) - if not hs.is_calculated: - mrs = MpLog.objects.filter(mpoint=mpoint, tag_update__gte=p_t_r[0], tag_update__lte=p_t_r[1]).order_by('tag_update') - val = 0 - if mrs.exists(): - val = mrs.last() - mrs.first() - hs.val = val - hs.is_calculated = True - hs.save() \ No newline at end of file + sum_dict_month = MpointStat.objects.filter(type='hour', mpoint=mpoint, year=year, month=month).aggregate(sum=Sum('val')) + params_month = {'type':'month', 'mpoint': mpoint, 'year': year, 'month': month} + ms_month, _ = MpointStat.objects.get_or_create(**params_month, defaults=params_month) + ms_month.val = sum_dict_month['sum'] + ms_month.save() + + sum_dict_year = MpointStat.objects.filter(type='hour', mpoint=mpoint, year=year).aggregate(sum=Sum('val')) + params_year = {'type':'year', 'mpoint': mpoint, 'year': year} + ms_year, _ = MpointStat.objects.get_or_create(**params_year, defaults=params_year) + ms_year.val = sum_dict_year['sum'] + ms_year.save() + +@shared_task(base=CustomTask) +def cal_mpointstats(is_now=1): + """ + 计算所有测点的统计值,默认当前小时 + """ + now, pre = get_current_and_previous_time() + if is_now: + for mpoint in Mpoint.objects.all(): + cal_mpointstat_hour.delay(mpoint.id, now.year, now.moth, now.day, now.hour) + else: + for mpoint in Mpoint.objects.all(): + cal_mpointstat_hour.delay(mpoint.id, pre.year, pre.month, pre.day, pre.hour) \ No newline at end of file diff --git a/apps/enm/urls.py b/apps/enm/urls.py index f5d2b3c2..8463ebf3 100644 --- a/apps/enm/urls.py +++ b/apps/enm/urls.py @@ -1,6 +1,6 @@ from django.urls import path, include from rest_framework.routers import DefaultRouter -from apps.enm.views import MpointViewSet, MgroupViewSet, TeamViewSet, MpLogViewSet, SfLogViewSet, StLogViewSet +from apps.enm.views import MpointViewSet, MgroupViewSet, TeamViewSet, MpLogViewSet, SfLogViewSet, StLogViewSet, StSfLogViewSet API_BASE_URL = 'api/enm/' HTML_BASE_URL = 'enm/' @@ -12,6 +12,7 @@ router.register('team', TeamViewSet, basename='team') router.register('mplog', MpLogViewSet, basename='mplog') router.register('sflog', SfLogViewSet, basename='sflog') router.register('stlog', StLogViewSet, basename='stlog') +router.register('stsflog', StSfLogViewSet, basename='stsflog') urlpatterns = [ path(API_BASE_URL, include(router.urls)), ] \ No newline at end of file diff --git a/apps/enm/views.py b/apps/enm/views.py index 2402ea48..d8ab37da 100644 --- a/apps/enm/views.py +++ b/apps/enm/views.py @@ -1,13 +1,18 @@ from django.shortcuts import render from apps.enm.models import Mpoint, Mgroup, Team, MpLog, SfLog, StLog, StSfLog from apps.utils.viewsets import CustomModelViewSet, GenericViewSet -from rest_framework.mixins import ListModelMixin -from apps.enm.serializers import MpointSerializer, TeamSerializer, MgroupSerializer, MpLogSerializer, SfLogSerializer, StLogSerializer +from rest_framework.mixins import ListModelMixin, UpdateModelMixin +from apps.enm.serializers import MpointSerializer, TeamSerializer, MgroupSerializer, MpLogSerializer, SfLogSerializer, StLogSerializer, StSfLogSerializer from rest_framework.exceptions import ParseError from django.db import transaction from django.db.models import Q class MgroupViewSet(CustomModelViewSet): + """ + list:测点集 + + 测点集 + """ queryset = Mgroup.objects.all() serializer_class = MgroupSerializer select_related_fields = ['create_by', 'belong_dept'] @@ -16,6 +21,11 @@ class MgroupViewSet(CustomModelViewSet): class TeamViewSet(CustomModelViewSet): + """ + list:班组 + + 班组 + """ queryset = Team.objects.all() serializer_class = TeamSerializer select_related_fields = ['belong_dept', 'leader'] @@ -24,6 +34,11 @@ class TeamViewSet(CustomModelViewSet): class MpointViewSet(CustomModelViewSet): + """ + list:测点 + + 测点 + """ queryset = Mpoint.objects.all() serializer_class = MpointSerializer select_related_fields = ['create_by', 'belong_dept', 'ep_monitored', 'ep_belong', 'mgroup'] @@ -32,6 +47,11 @@ class MpointViewSet(CustomModelViewSet): class MpLogViewSet(ListModelMixin, GenericViewSet): + """ + list:测点原始记录 + + 测点原始记录 + """ queryset = MpLog.objects.all() serializer_class = MpLogSerializer select_related_fields = ['mpoint'] @@ -39,6 +59,11 @@ class MpLogViewSet(ListModelMixin, GenericViewSet): class StLogViewSet(ListModelMixin, GenericViewSet): + """ + list:停机记录 + + 停机记录 + """ queryset = StLog.objects.all() serializer_class = StLogSerializer select_related_fields = ['mgroup'] @@ -47,6 +72,11 @@ class StLogViewSet(ListModelMixin, GenericViewSet): class SfLogViewSet(CustomModelViewSet): + """ + list:值班记录 + + 值班记录 + """ queryset = SfLog.objects.all() serializer_class = SfLogSerializer select_related_fields = ['mgroup', 'shift', 'team', 'leader'] @@ -66,4 +96,23 @@ class SfLogViewSet(CustomModelViewSet): stsflog = StSfLog.objects.filter(stlog=i).order_by('-sflog__start_time').first() if stsflog: stsflog.is_current_down = True - stsflog.save() \ No newline at end of file + stsflog.save() + + +class StSfLogViewSet(ListModelMixin, UpdateModelMixin, GenericViewSet): + """ + list:值班停机关系 + + 值班停机关系 + """ + queryset = StSfLog.objects.all() + serializer_class = StSfLogSerializer + select_related_fields = ['stlog', 'sflog'] + filterset_fields = ['stlog', 'sflog'] + search_fields = ['reason'] + + def filter_queryset(self, queryset): + params = self.request.query_params + if 'stlog' not in params or 'sflog' not in params: + raise ParseError('请指定所属停机或值班记录') + return super().filter_queryset(queryset) \ No newline at end of file