From b96deab2cb4a51e88f2f38b817aa21c888a77b43 Mon Sep 17 00:00:00 2001 From: Sokurov Idar Date: Mon, 8 Mar 2021 21:07:09 +0300 Subject: [PATCH] Update statistic backend --- main/extra_func.py | 62 ++++++++++++++-- main/forms.py | 5 +- main/templates/pages/stat.html | 13 +++- main/views.py | 127 ++++++++++++--------------------- 4 files changed, 117 insertions(+), 90 deletions(-) diff --git a/main/extra_func.py b/main/extra_func.py index 8d5abfd..03fd61a 100644 --- a/main/extra_func.py +++ b/main/extra_func.py @@ -1,5 +1,5 @@ import os -from datetime import timedelta +from datetime import timedelta, datetime from zenpy import Zenpy from zenpy.lib.exception import APIException @@ -194,7 +194,9 @@ def check_user_auth(email: str, password: str) -> bool: def daterange(start_date, end_date) -> list: - # Возвращает список дней с start_date по end_date исключая правую границу + """ + Возвращает список дней с start_date по end_date исключая правую границу + """ dates = [] for n in range(int((end_date - start_date).days)): dates.append(start_date + timedelta(n)) @@ -202,8 +204,60 @@ def daterange(start_date, end_date) -> list: def get_timedelta(log) -> timedelta: - # Возвращает объект класса timedelta, который хранит промежуток времени от начала суток до момента, - # который находится в log(RoleChangeLogs) + """ + Возвращает объект класса timedelta, который хранит промежуток времени от начала суток до момента, + который находится в log (объект класса RoleChangeLogs) + """ time = log.change_time.time() time = timedelta(hours=time.hour, minutes=time.minute, seconds=time.second) return time + + +def last_day_of_month(day): + """ + Возвращает последний день любого месяца + """ + next_month = day.replace(day=28) + timedelta(days=4) + return next_month - timedelta(days=next_month.day) + + +def get_statistic_from_data(data, start_date, end_date): + """ + Функция возвращает словарь, в котором ключ - дата, значение - кол-во проработанных в этот день секунд + data - массив объектов RoleChangeLogs, является списком логов пользователя + """ + if not data: + return None + # Обнуление всех дней + stat = {} + for day in daterange(start_date, end_date + timedelta(days=1)): + stat[day] = 0 + first_log, last_log = data[0], data[len(data) - 1] + + # Если инженер работал ещё до начала диапазона + if int(first_log.old_role) == ROLES['engineer']: + for day in daterange(start_date, first_log.change_time.date()): + stat[day] = 24 * 3600 + stat[first_log.change_time.date()] += get_timedelta(first_log).total_seconds() + + # Если инженер закончил работать после диапазона + if int(last_log.new_role) == ROLES['engineer']: + for day in daterange(last_log.change_time.date() + timedelta(days=1), end_date + timedelta(days=1)): + stat[day] = 24 * 3600 + stat[last_log.change_time.date()] += (timedelta(days=1)-get_timedelta(last_log)).total_seconds() + # Цикл по логам + for log_index in range(len(data) - 1): + if int(data[log_index].new_role) == ROLES['engineer']: + current_log, next_log = data[log_index], data[log_index + 1] + # Если сессия закончилась НЕ в тот же день, что и началась + if current_log.change_time.date() != next_log.change_time.date(): + stat[current_log.change_time.date()] += (timedelta(days=1) - get_timedelta(current_log)).total_seconds() + stat[next_log.change_time.date()] += get_timedelta(next_log).total_seconds() + # Если проработал несколько дней подряд, то заполнить эти дни по 24 часа + for day in daterange(current_log.change_time.date() + timedelta(days=1),next_log.change_time.date()): + stat[day] = 24 * 3600 + # Если сессия закончилась в тот же день, что и началась + else: + elapsed_time = next_log.change_time - current_log.change_time + stat[current_log.change_time.date()] += elapsed_time.total_seconds() + return stat diff --git a/main/forms.py b/main/forms.py index 7046b35..e98b7e1 100644 --- a/main/forms.py +++ b/main/forms.py @@ -71,7 +71,10 @@ class StatisticForm(forms.Form): label='Электроная почта', ) interval = forms.CharField( # TODO: Переделать под html страницу - label='Выбор интервала', + label='Интервал работы', + ) + display_format = forms.CharField( # TODO: Переделать под html страницу + label='Формат отображения', ) range_start = forms.DateField( # TODO: Переделать под html страницу label='Начало диапазона', diff --git a/main/templates/pages/stat.html b/main/templates/pages/stat.html index ea5e793..2409e0f 100644 --- a/main/templates/pages/stat.html +++ b/main/templates/pages/stat.html @@ -4,7 +4,7 @@ {% block title %}{{ pagename }}{% endblock %} -{% block heading %}Статистика{% endblock %} +{% block heading %} Пример страницы статистики(палками не бейти плиз){% endblock %} {% block extra_css %} @@ -21,10 +21,17 @@ {% endfor %} - {% for key,val in stats_logs.items %} -

{{key}} {{val}}

+ {% for key,val in log_stats.items %} +

{{key}} | {{val}}


{% endfor %} + + + {% endblock %} diff --git a/main/views.py b/main/views.py index 79c14bc..f7a6967 100644 --- a/main/views.py +++ b/main/views.py @@ -1,6 +1,6 @@ import logging import os -from datetime import timedelta +from datetime import timedelta, date, datetime from django.contrib.auth.decorators import login_required from django.contrib.auth.forms import PasswordResetForm @@ -21,7 +21,7 @@ from zenpy.lib.api_objects import User as ZenpyUser from access_controller.settings import EMAIL_HOST_USER, ZENDESK_ROLES from main.extra_func import check_user_exist, update_profile, get_user_organization, make_engineer, make_light_agent, \ - get_users_list, daterange, get_timedelta + get_users_list, last_day_of_month, get_statistic_from_data from main.forms import AdminPageUsers, CustomRegistrationForm, CustomAuthenticationForm, StatisticForm from .models import UserProfile, RoleChangeLogs @@ -230,94 +230,57 @@ class CustomLoginView(LoginView): def statistic_page(request): if not request.user.is_superuser: return redirect('index') - context = { 'pagename': 'страница статистики', + 'errors': [], } - if request.method == "POST": form = StatisticForm(request.POST) - - if form.is_valid(): - start_date = form.cleaned_data['range_start'] - end_date = form.cleaned_data['range_end'] - - try: - data = RoleChangeLogs.objects.filter( - change_time__range=[start_date, end_date], - user=User.objects.get(email=form.cleaned_data['email']), - ).order_by('change_time') - except User.DoesNotExist: - context['errors'] = 'Пользователь не найден' - context['form'] = StatisticForm() - return render(request, 'pages/stat.html', context) - - interval = form.cleaned_data['interval'] # интервал TODO: переделать под html-страницу - show = form.cleaned_data['display_format'] # формат отображения TODO: переделать под html-страницу - stat = {} - - # Этот кусок кода будет заполнять массив stat, в котором ключ - дата, значение - кол-во секунд - # Далее будет заполнение контекса в зависимости от выбранного интервала(дни/месяцы) и формата отобржаения(дни/часы) - # (пока есть только отображение в часах, интервал в днях) - - # Обнуление всех дней - for day in daterange(start_date, end_date + timedelta(days=1)): - stat[day] = 0 - - first_log = data[0] - last_log = data[len(data) - 1] - # Проеврка крайних случаев - # Если окажется, что инженер работал ещё до начала диапазона(мы видим, что он был инженером до диапазона) - if first_log.old_role == 'engineer': - for day in daterange(start_date, first_log.change_time.date()): - stat[day] = 24 * 3600 - stat[first_log.change_time.date()] += get_timedelta(first_log.change_time.time()).total_seconds() - - # Если окажется, что инженер закончил работать после диапазона(мы видим, что он был инженером и после диапазона) - if last_log.new_role == 'engineer': - for day in daterange(last_log.change_time.date() + timedelta(days=1), - end_date + timedelta(days=1)): - stat[day] = 24 * 3600 - stat[last_log.change_time.date()] += (timedelta(days=1) - get_timedelta(last_log)).total_seconds() - - # Цикл по логам - for log_index in range(len(data) - 1): - if data[log_index].new_role == 'engineer': - current_log, next_log = data[log_index], data[log_index + 1] - - # Если сессия закончилась НЕ в тот же день, что и началась - if current_log.change_time.date() != next_log.change_time.date(): - stat[current_log.change_time.date()] += ( - timedelta(days=1) - get_timedelta(current_log)).total_seconds() - stat[next_log.change_time.date()] += get_timedelta(next_log).total_seconds() - - # Если проработал несколько дней подряд, то заполнить эти дни по 24 часа - for day in daterange(current_log.change_time.date() + timedelta(days=1), - next_log.change_time.date()): - stat[day] = 24 * 3600 - - # Если сессия закончилась в тот же день, что и началась - else: - elapsed_time = next_log.change_time - current_log.change_time - stat[current_log.change_time.date()] += elapsed_time.total_seconds() - - # Переделываем наши значения под формат отображения - for key, item in stat.items(): - if show == 'hours': - stat[key] = item / 3600 - elif show == 'days': - stat[key] = item / 86400 - - # Переделываем наши значения под формат отображения - if interval == 'days': - context['stats_logs'] = stat - elif interval == 'months': - pass + if not form.is_valid(): + context['errors'] += form.errors else: - context['errors'] = form.errors + start_date, end_date, data = form.cleaned_data['range_start'], form.cleaned_data['range_end'], list() + if end_date < start_date or end_date > datetime.now().date(): + context['errors'] += ['Конец диапазона должен быть больше начала диапазона и меньше текущего времени'] + else: + try: + data = RoleChangeLogs.objects.filter( + change_time__range=[start_date, end_date + timedelta(days=1)], + user=User.objects.get(email=form.cleaned_data['email']), + ).order_by('change_time') + except User.DoesNotExist: + context['errors'] = ['Пользователь не найден'] + stats = get_statistic_from_data(data, start_date, end_date) + if stats is None: + context['errors'] += ['Не найдено изменений роли в указаном диапазоне'] + interval = form.cleaned_data['interval'] # интервал работы TODO: переделать под html-страницу + show = form.cleaned_data['display_format'] # формат отображения + if not (show in ['hours', 'days']): # Работа с форматом отображения + context['errors'] += ['Формат отображения должен быть в часах или днях'] + elif stats: + for key, item in stats.items(): + if show == 'hours': + stats[key] = item / 3600 + elif show == 'days': + stats[key] = item / 86400 + + if not (interval in ['days', 'months']): # Работа с интервалом работы + context['errors'] += ['Интервал работы должен быть в днях или месяцах'] + elif interval == 'months' and stats: # Переделываем ключи под формат в прототипе(начало_месяца-конец_месяца) + new_stats = {} + for key, value in stats.items(): + current_month_start, current_month_end = max(start_date, + date(year=key.year, month=key.month, day=1)), min( + end_date, last_day_of_month(date(year=key.year, month=key.month, day=1))) + index = ' - '.join([str(current_month_start), str(current_month_end)]) + if new_stats.get(index): + new_stats[index] += value + else: + new_stats[index] = value + stats = new_stats + context['log_stats'] = stats if not context['errors'] else None if request.method == 'GET': form = StatisticForm() - context['form'] = form return render(request, 'pages/stat.html', context)