Files
dbstorage/dbapp/mainapp/views/errors_report.py

234 lines
9.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import json
from datetime import datetime, timedelta
from decimal import Decimal
from django.contrib.auth.mixins import LoginRequiredMixin
from mainapp.permissions import PermissionRequiredMixin
from django.views.generic import TemplateView, View
from django.http import JsonResponse
from django.db import transaction
from django.db.models import Sum
from mainapp.models import IssueType, DailyReport, DowntimePeriod, IssueMark
class ErrorsReportView(TemplateView, LoginRequiredMixin, PermissionRequiredMixin):
"""Страница отчётов об ошибках"""
template_name = 'mainapp/errors_report.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['issue_types'] = IssueType.objects.all().order_by('category', 'name')
context['errors'] = context['issue_types'].filter(category='error')
context['malfunctions'] = context['issue_types'].filter(category='malfunction')
context['full_width_page'] = True
return context
class ErrorsReportAPIView(View, LoginRequiredMixin, PermissionRequiredMixin):
"""API для получения данных отчётов"""
def get(self, request):
# Получаем параметры фильтрации
date_from = request.GET.get('date_from')
date_to = request.GET.get('date_to')
location_places = request.GET.getlist('location_place')
error_filters = request.GET.getlist('error_filter')
malfunction_filters = request.GET.getlist('malfunction_filter')
reports = DailyReport.objects.all()
if date_from:
reports = reports.filter(date__gte=date_from)
if date_to:
reports = reports.filter(date__lte=date_to)
if location_places:
reports = reports.filter(location_place__in=location_places)
# Фильтрация по ошибкам/неисправностям
if error_filters:
reports = reports.filter(issue_marks__issue_type_id__in=error_filters, issue_marks__is_present=True)
if malfunction_filters:
reports = reports.filter(issue_marks__issue_type_id__in=malfunction_filters, issue_marks__is_present=True)
reports = reports.prefetch_related('downtime_periods', 'issue_marks__issue_type').distinct()
# Получаем все типы ошибок/неисправностей
issue_types = IssueType.objects.all().order_by('category', 'name')
data = []
for report in reports:
# Формируем строку простоев
downtimes = []
for dt in report.downtime_periods.all():
downtimes.append({
'id': dt.id,
'start': dt.start_time.strftime('%H:%M'),
'end': dt.end_time.strftime('%H:%M'),
'reason': dt.reason
})
# Формируем отметки
marks_dict = {m.issue_type_id: m.is_present for m in report.issue_marks.all()}
row = {
'id': report.id,
'date': report.date.isoformat(),
'downtimes': downtimes,
'downtimes_display': '; '.join([f"{d['start']}-{d['end']} ({d['reason']})" for d in downtimes]),
'daily_work_hours': float(report.daily_work_hours),
'weekly_work_hours': float(report.weekly_work_hours),
'explanation': report.explanation or '',
'comment': report.comment or '',
'location_place': report.location_place or '',
'location_place_display': report.get_location_place_display() if report.location_place else '',
}
# Добавляем отметки по каждому типу
for it in issue_types:
row[f'issue_{it.id}'] = marks_dict.get(it.id, False)
data.append(row)
# Формируем информацию о колонках
columns = []
for it in issue_types:
columns.append({
'id': it.id,
'name': it.name,
'category': it.category,
'field': f'issue_{it.id}'
})
return JsonResponse({
'data': data,
'columns': columns
})
class ErrorsReportSaveAPIView(View):
"""API для сохранения отчёта"""
def post(self, request):
try:
data = json.loads(request.body)
except json.JSONDecodeError:
return JsonResponse({'success': False, 'error': 'Invalid JSON'}, status=400)
date_str = data.get('date')
if not date_str:
return JsonResponse({'success': False, 'error': 'Дата обязательна'}, status=400)
try:
report_date = datetime.strptime(date_str, '%Y-%m-%d').date()
except ValueError:
return JsonResponse({'success': False, 'error': 'Неверный формат даты'}, status=400)
report_id = data.get('id')
# Проверка на дублирование даты при создании новой записи
if not report_id:
if DailyReport.objects.filter(date=report_date).exists():
return JsonResponse({
'success': False,
'error': f'Запись за {report_date.strftime("%d.%m.%Y")} уже существует'
}, status=400)
with transaction.atomic():
if report_id:
# Обновление существующей записи
try:
report = DailyReport.objects.get(id=report_id)
report.date = report_date
report.daily_work_hours = Decimal(str(data.get('daily_work_hours', 0)))
report.explanation = data.get('explanation', '')
report.comment = data.get('comment', '')
report.location_place = data.get('location_place', 'kr')
report.save()
except DailyReport.DoesNotExist:
return JsonResponse({'success': False, 'error': 'Запись не найдена'}, status=404)
else:
# Создание новой записи
report = DailyReport.objects.create(
date=report_date,
daily_work_hours=Decimal(str(data.get('daily_work_hours', 0))),
explanation=data.get('explanation', ''),
comment=data.get('comment', ''),
location_place=data.get('location_place', 'kr'),
created_by=request.user if request.user.is_authenticated else None,
)
# Обновляем периоды простоя
downtimes = data.get('downtimes', [])
report.downtime_periods.all().delete()
for dt in downtimes:
if dt.get('start') and dt.get('end'):
DowntimePeriod.objects.create(
report=report,
start_time=dt['start'],
end_time=dt['end'],
reason=dt.get('reason', '')
)
# Обновляем отметки
issue_marks = data.get('issue_marks', {})
for issue_id_str, is_present in issue_marks.items():
issue_id = int(issue_id_str)
IssueMark.objects.update_or_create(
report=report,
issue_type_id=issue_id,
defaults={'is_present': is_present}
)
return JsonResponse({'success': True, 'id': report.id})
class ErrorsReportDeleteAPIView(View):
"""API для удаления отчёта"""
def post(self, request):
try:
data = json.loads(request.body)
except json.JSONDecodeError:
return JsonResponse({'success': False, 'error': 'Invalid JSON'}, status=400)
report_id = data.get('id')
if not report_id:
return JsonResponse({'success': False, 'error': 'ID отчёта обязателен'}, status=400)
try:
report = DailyReport.objects.get(id=report_id)
report.delete()
return JsonResponse({'success': True})
except DailyReport.DoesNotExist:
return JsonResponse({'success': False, 'error': 'Отчёт не найден'}, status=404)
class WeeklyHoursAPIView(View):
"""API для расчёта часов за неделю"""
def get(self, request):
date_str = request.GET.get('date')
if not date_str:
return JsonResponse({'error': 'Дата обязательна'}, status=400)
try:
target_date = datetime.strptime(date_str, '%Y-%m-%d').date()
except ValueError:
return JsonResponse({'error': 'Неверный формат даты'}, status=400)
# Находим начало недели (понедельник)
week_start = target_date - timedelta(days=target_date.weekday())
week_end = week_start + timedelta(days=6)
# Считаем сумму часов за неделю
total = DailyReport.objects.filter(
date__gte=week_start,
date__lte=target_date
).aggregate(total=Sum('daily_work_hours'))['total'] or 0
return JsonResponse({
'week_start': week_start.isoformat(),
'week_end': week_end.isoformat(),
'total_hours': float(total)
})