Добавил журнал ошибок
This commit is contained in:
@@ -85,6 +85,13 @@ from .source_requests import (
|
||||
SourceRequestAPIView,
|
||||
SourceRequestDetailAPIView,
|
||||
)
|
||||
from .errors_report import (
|
||||
ErrorsReportView,
|
||||
ErrorsReportAPIView,
|
||||
ErrorsReportSaveAPIView,
|
||||
ErrorsReportDeleteAPIView,
|
||||
WeeklyHoursAPIView,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
# Base
|
||||
@@ -170,4 +177,10 @@ __all__ = [
|
||||
'SourceRequestDeleteView',
|
||||
'SourceRequestAPIView',
|
||||
'SourceRequestDetailAPIView',
|
||||
# Errors Report
|
||||
'ErrorsReportView',
|
||||
'ErrorsReportAPIView',
|
||||
'ErrorsReportSaveAPIView',
|
||||
'ErrorsReportDeleteAPIView',
|
||||
'WeeklyHoursAPIView',
|
||||
]
|
||||
|
||||
218
dbapp/mainapp/views/errors_report.py
Normal file
218
dbapp/mainapp/views/errors_report.py
Normal file
@@ -0,0 +1,218 @@
|
||||
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')
|
||||
|
||||
reports = DailyReport.objects.all()
|
||||
|
||||
if date_from:
|
||||
reports = reports.filter(date__gte=date_from)
|
||||
if date_to:
|
||||
reports = reports.filter(date__lte=date_to)
|
||||
|
||||
reports = reports.prefetch_related('downtime_periods', 'issue_marks__issue_type')
|
||||
|
||||
# Получаем все типы ошибок/неисправностей
|
||||
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 '',
|
||||
}
|
||||
|
||||
# Добавляем отметки по каждому типу
|
||||
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.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', ''),
|
||||
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)
|
||||
})
|
||||
@@ -59,6 +59,7 @@ class UserPermissionsEditView(LoginRequiredMixin, PermissionRequiredMixin, View)
|
||||
'Транспондеры': [],
|
||||
'Тех. анализ': [],
|
||||
'Отметки': [],
|
||||
'Журнал ошибок': [],
|
||||
'Прочее': [],
|
||||
}
|
||||
|
||||
@@ -85,6 +86,8 @@ class UserPermissionsEditView(LoginRequiredMixin, PermissionRequiredMixin, View)
|
||||
permission_groups['Тех. анализ'].append(perm_data)
|
||||
elif code.startswith('mark_'):
|
||||
permission_groups['Отметки'].append(perm_data)
|
||||
elif code.startswith('errors_report_'):
|
||||
permission_groups['Журнал ошибок'].append(perm_data)
|
||||
else:
|
||||
permission_groups['Прочее'].append(perm_data)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user