From dbbdaa89b9a14cf397066ca358ca7b4f6e4340ef Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 13 Feb 2025 11:26:27 +0800 Subject: [PATCH 1/7] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96has=5Fenvdata?= =?UTF-8?q?=E7=9A=84sql=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/em/views.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/apps/em/views.py b/apps/em/views.py index 439cf345..6b63144b 100644 --- a/apps/em/views.py +++ b/apps/em/views.py @@ -79,16 +79,11 @@ class EquipmentViewSet(CustomModelViewSet): from apps.enp.models import EnvData from apps.enp.serializers import EnvDataSerializer - # 子查询获取每个 equipment_id 对应的最大时间戳 - # 后面可以考虑从缓存里拿 - last_time_subquery = EnvData.objects.filter(equipment_id=OuterRef("equipment_id"), timex__gte=now_10_before, timex__lte=now).order_by("-timex").values("timex")[:1] - - # 主查询,获取每个 equipment_id 对应的完整记录 - last_envdata_qs = EnvData.objects.filter(equipment_id__in=data_ids, timex=Subquery(last_time_subquery)) - envdata = EnvDataSerializer(last_envdata_qs, many=True).data - envdata_dict = {item["equipment"]: item for item in envdata} + edata_qs = EnvData.objects.filter(equipment_id__in=data_ids, timex__gte=now_10_before, timex__lte=now).order_by("timex") + edata = EnvDataSerializer(edata_qs, many=True).data + edata_dict = {item["equipment"]: item for item in edata} for item in data: - item["envdata"] = envdata_dict.get(item["id"], {}) + item["envdata"] = edata_dict.get(item["id"], {}) return data @action(methods=["get"], detail=False, perms_map={"get": "*"}) From d7361a71df59bbdf5a510d7654b5690f37963023 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 13 Feb 2025 13:26:32 +0800 Subject: [PATCH 2/7] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96has=5Fenvdata?= =?UTF-8?q?=E7=9A=84sql=E6=9F=A5=E8=AF=A22?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/em/views.py | 16 +++++++--------- apps/enp/services.py | 27 ++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/apps/em/views.py b/apps/em/views.py index 6b63144b..a280f4a8 100644 --- a/apps/em/views.py +++ b/apps/em/views.py @@ -15,8 +15,8 @@ from django.db import transaction from apps.em.services import daoru_equipment from rest_framework.response import Response from django.conf import settings -from django.db.models import Count, Case, When, IntegerField, Max, OuterRef, Subquery -from datetime import datetime, timedelta +from django.db.models import Count, Case, When, IntegerField +from apps.enp.services import get_last_envdata # Create your views here. @@ -75,13 +75,11 @@ class EquipmentViewSet(CustomModelViewSet): if self.request.query_params.get("has_envdata", "no") == "yes": now = timezone.localtime() now_10_before = now - timezone.timedelta(minutes=10) - data_ids = [item["id"] for item in data] - from apps.enp.models import EnvData - from apps.enp.serializers import EnvDataSerializer - - edata_qs = EnvData.objects.filter(equipment_id__in=data_ids, timex__gte=now_10_before, timex__lte=now).order_by("timex") - edata = EnvDataSerializer(edata_qs, many=True).data - edata_dict = {item["equipment"]: item for item in edata} + edata_dict = {} + if data: + data_ids = [item["id"] for item in data] + edata = get_last_envdata(data_ids, now_10_before, now) + edata_dict = {item["equipment"]: item for item in edata} for item in data: item["envdata"] = edata_dict.get(item["id"], {}) return data diff --git a/apps/enp/services.py b/apps/enp/services.py index 7301287e..59c7b74c 100644 --- a/apps/enp/services.py +++ b/apps/enp/services.py @@ -1 +1,26 @@ -from openpyxl import load_workbook +from apps.enp.models import EnvData +from apps.enp.serializers import EnvDataSerializer +from datetime import datetime + +def get_last_envdata(eIds: list, time_start: datetime, time_end: datetime): + eIds_str = ",".join([f"'{item}'" for item in eIds]) # 将 ID 列表格式化为字符串 + time_start_str = time_start.strftime("%Y-%m-%d %H:%M:%S") + time_end_str = time_end.strftime("%Y-%m-%d %H:%M:%S") + esql = f''' + SELECT DISTINCT ON (equipment_id) * + FROM enp_envdata ee + WHERE equipment_id IN ({eIds_str}) + AND timex AT TIME ZONE 'Asia/Shanghai' >= '{time_start_str}' + AND timex AT TIME ZONE 'Asia/Shanghai' <= '{time_end_str}' + ORDER BY equipment_id, timex DESC + ''' + edata = EnvDataSerializer(EnvData.objects.raw(esql), many=True).data + + # esql ='''SELECT DISTINCT ON (equipment_id) * + # FROM enp_envdata ee + # WHERE equipment_id IN %s + # AND timex AT TIME ZONE 'Asia/Shanghai' >= %s + # AND timex AT TIME ZONE 'Asia/Shanghai' <= %s + # ORDER BY equipment_id, timex DESC''' + # res = query_all_dict(esql, [tuple(data_ids), now_10_before_str, now_str]) + return edata \ No newline at end of file From 61525d236e78d0661c68ed9f2acb8a3302ac3382 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 14 Feb 2025 10:56:40 +0800 Subject: [PATCH 3/7] =?UTF-8?q?feat:=20qct=20detail=E6=98=AF=E6=A3=80?= =?UTF-8?q?=E6=B5=8B=E9=A1=B9=E6=8C=89=E9=A1=BA=E5=BA=8F=E8=BF=94=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/qm/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/qm/models.py b/apps/qm/models.py index 1286e22a..582625f2 100644 --- a/apps/qm/models.py +++ b/apps/qm/models.py @@ -157,11 +157,11 @@ class Qct(CommonAModel): @property def qct_testitems(self): - return QctTestItem.objects.filter(qct=self) + return QctTestItem.objects.filter(qct=self).order_by("sort") @property def qct_defects(self): - return QctDefect.objects.filter(qct=self) + return QctDefect.objects.filter(qct=self).order_by("sort") @property def qct_mats(self): From e6b27950b258b18ef1700b9bcc67a3cbfc8b5dab Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 17 Feb 2025 09:34:48 +0800 Subject: [PATCH 4/7] =?UTF-8?q?feat:=20ftestitem=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=E8=AE=BE=E5=A4=87=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0043_ftestitem_test_equip.py | 20 +++++++++++++++++++ apps/qm/models.py | 1 + apps/qm/serializers.py | 2 +- 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 apps/qm/migrations/0043_ftestitem_test_equip.py diff --git a/apps/qm/migrations/0043_ftestitem_test_equip.py b/apps/qm/migrations/0043_ftestitem_test_equip.py new file mode 100644 index 00000000..fa8aaeee --- /dev/null +++ b/apps/qm/migrations/0043_ftestitem_test_equip.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.12 on 2025-02-17 01:33 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('em', '0021_auto_20241203_1531'), + ('qm', '0042_alter_ptest_testitem'), + ] + + operations = [ + migrations.AddField( + model_name='ftestitem', + name='test_equip', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='em.equipment', verbose_name='检测设备'), + ), + ] diff --git a/apps/qm/models.py b/apps/qm/models.py index 582625f2..7613dd05 100644 --- a/apps/qm/models.py +++ b/apps/qm/models.py @@ -314,6 +314,7 @@ class FtestItem(BaseModel): Ftest, verbose_name='关联检验', on_delete=models.CASCADE) testitem = models.ForeignKey( TestItem, verbose_name='质检项目', on_delete=models.CASCADE) + test_equip = models.ForeignKey(Equipment, verbose_name='检测设备', on_delete=models.SET_NULL, null=True, blank=True) test_user = models.ForeignKey(User, verbose_name='操作人', on_delete=models.CASCADE, null=True, blank=True) test_val = models.FloatField('测量值', null=True, blank=True) addto_wpr = models.BooleanField('加入WPR作为信息', default=False) diff --git a/apps/qm/serializers.py b/apps/qm/serializers.py index e29bf174..976d9e39 100644 --- a/apps/qm/serializers.py +++ b/apps/qm/serializers.py @@ -317,7 +317,7 @@ class FtestItemProcessSerializer(CustomModelSerializer): source='testitem.description', read_only=True) class Meta: model = FtestItem - fields = ["id", "testitem", "test_user", "test_val_json", "testitem_name", "testitem_description", "addto_wpr"] + fields = ["id", "testitem", "test_user", "test_val_json", "testitem_name", "testitem_description", "addto_wpr", "test_equip"] class FtestProcessSerializer(CustomModelSerializer): test_user_name = serializers.CharField( From 8cbb921969c703198481b10d102c65c9189cf7d2 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 17 Feb 2025 11:24:43 +0800 Subject: [PATCH 5/7] =?UTF-8?q?feat:=20=E5=85=89=E5=AD=90=E6=A3=92?= =?UTF-8?q?=E7=AE=A1=E7=BB=BC=E5=90=88=E7=BB=9F=E8=AE=A1=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/services_2.py | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/apps/wpm/services_2.py b/apps/wpm/services_2.py index 6795f089..5bd1bbce 100644 --- a/apps/wpm/services_2.py +++ b/apps/wpm/services_2.py @@ -228,7 +228,7 @@ def get_alldata_with_batch(batch: str): # 六车间工段生产数据 # 六车间工段生产数据 - mgroup_list = ["平头", "粘铁头", "粗中细磨", "抛光", "开槽"] + mgroup_list = ["平头", "粘铁头", "粗中细磨", "平磨", "掏管", "抛光", "开槽", "倒角"] for mgroup_name in mgroup_list: if mgroup_name == '粗中细磨': mgroups = Mgroup.objects.filter(name__in=['粗磨', '粗中磨', '粗中细磨']) @@ -276,9 +276,38 @@ def get_alldata_with_batch(batch: str): # data["六车间生产入库_日期"] = list(set(data["六车间生产入库_日期"])) # data["六车间生产入库_日期"] = ";".join([item.strftime("%Y-%m-%d") for item in data["六车间生产入库_日期"]]) - # 成品检验数据 ftestwork_count_fields = FtestWork.count_fields() - ftestwork_qs = FtestWork.objects.filter(batch=batch) + # 六车间中检数据 + ftestwork_qs = FtestWork.objects.filter(batch=batch, type="process") + if ftestwork_qs.exists(): + data["六车间中检_日期"] = [] + data['六车间中检_检验人'] = [] + for item in ftestwork_qs: + last_time = item.update_time if item.update_time > last_time else last_time + if item.test_date: + data["六车间中检_日期"].append(item.test_date) + if item.test_user: + data['六车间中检_检验人'].append(item.test_user) + for field in ftestwork_count_fields: + if field == 'count_notok_json': + for k, v in getattr(item, field).items(): + if f'六车间中检_{k}' not in data: + data[f'六车间中检_{k}'] = v + else: + data[f'六车间中检_{k}'] += v + else: + if getattr(item, field) > 0 or field in ["count", "count_ok"]: + if f'六车间中检_{field}' not in data: + data[f'六车间中检_{field}'] = getattr(item, field) + else: + data[f'六车间中检_{field}'] += getattr(item, field) + data["六车间中检_日期"] = list(set(data["六车间中检_日期"])) + data["六车间中检_日期"] = ";".join([item.strftime("%Y-%m-%d") for item in data["六车间中检_日期"]]) + data['六车间中检_检验人'] = list(set(data['六车间中检_检验人'])) + data['六车间中检_检验人'] = ";".join([item.name for item in data['六车间中检_检验人']]) + + # 成品检验数据 + ftestwork_qs = FtestWork.objects.filter(batch=batch, type="prod") if ftestwork_qs.exists(): data["成品检验_日期"] = [] data['成品检验_检验人'] = [] From 57e6e23e9539604e74fa2784f7980f5ccfc934af Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 17 Feb 2025 14:35:43 +0800 Subject: [PATCH 6/7] =?UTF-8?q?feat:=20equip=E6=B7=BB=E5=8A=A0=E9=87=87?= =?UTF-8?q?=E9=9B=86=E6=95=B0=E6=8D=AE=E8=AF=B7=E6=B1=82=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0022_equipment_cd_req_addr.py | 18 ++++++++++++++++++ apps/em/models.py | 1 + 2 files changed, 19 insertions(+) create mode 100644 apps/em/migrations/0022_equipment_cd_req_addr.py diff --git a/apps/em/migrations/0022_equipment_cd_req_addr.py b/apps/em/migrations/0022_equipment_cd_req_addr.py new file mode 100644 index 00000000..f25a41f0 --- /dev/null +++ b/apps/em/migrations/0022_equipment_cd_req_addr.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2025-02-17 06:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('em', '0021_auto_20241203_1531'), + ] + + operations = [ + migrations.AddField( + model_name='equipment', + name='cd_req_addr', + field=models.TextField(blank=True, null=True, verbose_name='采集数据的请求地址'), + ), + ] diff --git a/apps/em/models.py b/apps/em/models.py index ff7d6877..78946325 100644 --- a/apps/em/models.py +++ b/apps/em/models.py @@ -114,6 +114,7 @@ class Equipment(CommonBModel): power_kw = models.FloatField("功率", null=True, blank=True) coordinates = models.JSONField("坐标", default=dict, blank=True) + cd_req_addr = models.TextField("采集数据的请求地址", null=True, blank=True) class Meta: verbose_name = "设备信息" From 9f558acedb3de337859fa15733d3be54008513f0 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 17 Feb 2025 14:37:30 +0800 Subject: [PATCH 7/7] =?UTF-8?q?feat:=20base=20execute=5Fraw=5Fsql=E6=97=B6?= =?UTF-8?q?=E5=85=81=E8=AE=B8=E4=B8=8D=E8=BF=94=E5=9B=9E=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/utils/sql.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/utils/sql.py b/apps/utils/sql.py index 177be62d..6f9c8c7e 100644 --- a/apps/utils/sql.py +++ b/apps/utils/sql.py @@ -13,9 +13,11 @@ def execute_raw_sql(sql: str, params=None): cursor.execute(sql, params=params) else: cursor.execute(sql) - columns = [desc[0] for desc in cursor.description] - rows = cursor.fetchall() - return columns, rows + if cursor.description: + columns = [desc[0] for desc in cursor.description] + rows = cursor.fetchall() + return columns, rows + return [], [] def format_sqldata(columns, rows): return [columns] + rows, [dict(zip(columns, row)) for row in rows]