Refactor statistic page with class StatisticData
This commit is contained in:
parent
ce55ec61e6
commit
5efec2641a
@ -1,5 +1,5 @@
|
||||
import os
|
||||
from datetime import timedelta, datetime
|
||||
from datetime import timedelta, datetime, date
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from zenpy import Zenpy
|
||||
@ -203,58 +203,155 @@ def last_day_of_month(day):
|
||||
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]
|
||||
class StatisticData:
|
||||
def __init__(self, start_date, end_date, user_email):
|
||||
self.errors = list()
|
||||
self.data = None
|
||||
self.statistic = dict()
|
||||
self.start_date = start_date
|
||||
self.end_date = end_date
|
||||
self.email = user_email
|
||||
self._set_data()
|
||||
self._set_statistic()
|
||||
|
||||
# Если инженер работал ещё до начала диапазона
|
||||
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()
|
||||
def use_interval(self, interval):
|
||||
"""
|
||||
Объединяет ключи и значения в соответствии с интервалом работы
|
||||
"""
|
||||
if not self.is_valid_statistic():
|
||||
return None
|
||||
if not (interval in ['days', 'months']):
|
||||
self.errors += ['Интервал работы должен быть в днях или месяцах']
|
||||
return None
|
||||
stat = {}
|
||||
if interval == 'months':
|
||||
# Переделываем ключи под формат, как в прототипе('начало_месяца - конец_месяца')
|
||||
for key, value in self.get_statistic().items():
|
||||
current_month_start = max(self.start_date, date(year=key.year, month=key.month, day=1))
|
||||
current_month_end = min(self.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 stat.get(index):
|
||||
stat[index] += value
|
||||
else:
|
||||
stat[index] = value
|
||||
elif interval == 'days':
|
||||
stat = self.get_statistic()
|
||||
return stat
|
||||
|
||||
# Если инженер закончил работать после диапазона
|
||||
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
|
||||
def use_display(self, display_format):
|
||||
"""
|
||||
Приводит данные к формату отображения, возвращает их
|
||||
"""
|
||||
if not self.get_statistic():
|
||||
return None
|
||||
if not (display_format in ['hours', 'days']):
|
||||
self.errors += ['Формат отображения должен быть в часах или днях']
|
||||
return None
|
||||
for key, item in self.statistic.items():
|
||||
if display_format == 'hours':
|
||||
self.statistic[key] = item / 3600
|
||||
elif display_format == 'days':
|
||||
self.statistic[key] = item / 86400
|
||||
return self.statistic.copy()
|
||||
|
||||
def pop_errors(self):
|
||||
"""
|
||||
Возвращает все текущие ошибки
|
||||
"""
|
||||
errors = self.errors.copy()
|
||||
self.errors.clear()
|
||||
return errors
|
||||
|
||||
def get_data_logs(context, start_date, end_date, email):
|
||||
"""
|
||||
Функция возвращает список из лог-ов в диапазоне дат start_date-end_date для пользователя с почтой email
|
||||
"""
|
||||
data = []
|
||||
try:
|
||||
data = RoleChangeLogs.objects.filter(
|
||||
change_time__range=[start_date, end_date + timedelta(days=1)],
|
||||
user=User.objects.get(email=email),
|
||||
).order_by('change_time')
|
||||
except User.DoesNotExist:
|
||||
context['errors'] = ['Пользователь не найден']
|
||||
return data
|
||||
def get_data(self):
|
||||
"""
|
||||
Вернуть данные
|
||||
data - массив объектов RoleChangeLogs, является списком логов пользователя
|
||||
"""
|
||||
if self.is_valid_data():
|
||||
return self.data
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_statistic(self):
|
||||
"""
|
||||
Вернуть словарь statistic или None, если были ошибки при создании
|
||||
"""
|
||||
if self.is_valid_statistic():
|
||||
return self.statistic.copy()
|
||||
else:
|
||||
return None
|
||||
|
||||
def is_valid_statistic(self):
|
||||
"""
|
||||
Были ли ошибки при создании статистики
|
||||
"""
|
||||
return not self.errors and self.statistic
|
||||
|
||||
def is_valid_data(self):
|
||||
"""
|
||||
Были ли ошибки при создании объекта
|
||||
"""
|
||||
return not self.errors
|
||||
|
||||
def _set_data(self):
|
||||
"""
|
||||
Получение списка из лог-ов в диапазоне дат start_date-end_date для пользователя с почтой email
|
||||
"""
|
||||
try:
|
||||
self.data = RoleChangeLogs.objects.filter(
|
||||
change_time__range=[self.start_date, self.end_date + timedelta(days=1)],
|
||||
user=User.objects.get(email=self.email),
|
||||
).order_by('change_time')
|
||||
self.errors.clear()
|
||||
except User.DoesNotExist:
|
||||
self.errors += ['Пользователь не найден']
|
||||
|
||||
def _set_statistic(self):
|
||||
"""
|
||||
Функция заполняет словарь, в котором ключ - дата, значение - кол-во проработанных в этот день секунд
|
||||
"""
|
||||
if not self.get_data():
|
||||
self.errors += ['Не обнаружены изменения роли в данном промежутке']
|
||||
return None
|
||||
|
||||
self.clear_statistic()
|
||||
first_log, last_log = self.data[0], self.data[len(self.data) - 1]
|
||||
|
||||
# Если инженер работал ещё до начала диапазона
|
||||
if int(first_log.old_role) == ROLES['engineer']:
|
||||
self.fill_daterange(self.start_date, first_log.change_time.date())
|
||||
self.statistic[first_log.change_time.date()] += get_timedelta(first_log).total_seconds()
|
||||
|
||||
# Если инженер закончил работать после диапазона
|
||||
if int(last_log.new_role) == ROLES['engineer']:
|
||||
self.fill_daterange(last_log.change_time.date() + timedelta(days=1), self.end_date + timedelta(days=1))
|
||||
self.statistic[last_log.change_time.date()] += (timedelta(days=1) - get_timedelta(last_log)).total_seconds()
|
||||
# Цикл по логам
|
||||
for log_index in range(len(self.data) - 1):
|
||||
if int(self.data[log_index].new_role) == ROLES['engineer']:
|
||||
current_log, next_log = self.data[log_index], self.data[log_index + 1]
|
||||
# Если сессия закончилась НЕ в тот же день, что и началась
|
||||
if current_log.change_time.date() != next_log.change_time.date():
|
||||
self.statistic[current_log.change_time.date()] += (
|
||||
timedelta(days=1) - get_timedelta(current_log)).total_seconds()
|
||||
self.statistic[next_log.change_time.date()] += get_timedelta(next_log).total_seconds()
|
||||
# Если проработал несколько дней подряд, то заполнить эти дни по 24 часа
|
||||
self.fill_daterange(current_log.change_time.date() + timedelta(days=1), next_log.change_time.date())
|
||||
|
||||
# Если сессия закончилась в тот же день, что и началась
|
||||
else:
|
||||
elapsed_time = next_log.change_time - current_log.change_time
|
||||
self.statistic[current_log.change_time.date()] += elapsed_time.total_seconds()
|
||||
|
||||
def fill_daterange(self, first, last, val=24 * 3600):
|
||||
"""
|
||||
Заполение диапазона дат значением val, по умолчанию val = кол-во секунд в 1 дне
|
||||
"""
|
||||
for day in daterange(first, last):
|
||||
self.statistic[day] = val
|
||||
|
||||
def clear_statistic(self):
|
||||
"""
|
||||
Обнуление всех дней
|
||||
"""
|
||||
self.fill_daterange(self.start_date, self.end_date + timedelta(days=1), 0)
|
||||
|
@ -31,6 +31,15 @@
|
||||
<li>{{error}}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{%if form.errors%}
|
||||
<ul>
|
||||
{% for field, errors in form.errors.items %}
|
||||
{% for error in errors %}
|
||||
<li>{{error}}</li>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{%endif%}
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import logging
|
||||
import os
|
||||
from datetime import date, datetime
|
||||
from datetime import 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, last_day_of_month, get_statistic_from_data, get_data_logs
|
||||
get_users_list, StatisticData
|
||||
from main.forms import AdminPageUsers, CustomRegistrationForm, CustomAuthenticationForm, StatisticForm
|
||||
from .models import UserProfile
|
||||
|
||||
@ -247,42 +247,23 @@ def statistic_page(request):
|
||||
}
|
||||
if request.method == "POST":
|
||||
form = StatisticForm(request.POST)
|
||||
if not form.is_valid():
|
||||
context['errors'] += form.errors
|
||||
else:
|
||||
start_date, end_date, data = form.cleaned_data['range_start'], form.cleaned_data['range_end'], list()
|
||||
if form.is_valid():
|
||||
start_date, end_date, stats = form.cleaned_data['range_start'], form.cleaned_data['range_end'], dict()
|
||||
interval, show = form.cleaned_data['interval'], form.cleaned_data['display_format']
|
||||
if end_date < start_date or end_date > datetime.now().date():
|
||||
context['errors'] += ['Конец диапазона должен быть позже начала диапазона и раньше текущего времени']
|
||||
else:
|
||||
data = get_data_logs(context, start_date, end_date, form.cleaned_data['email'])
|
||||
stats = get_statistic_from_data(data, start_date, end_date)
|
||||
if stats is None:
|
||||
context['errors'] += ['Не найдено изменений роли в указаном диапазоне']
|
||||
interval, show = form.cleaned_data['interval'], 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 = max(start_date, date(year=key.year, month=key.month, day=1))
|
||||
current_month_end = 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
|
||||
|
||||
Data = StatisticData(start_date, end_date, form.cleaned_data['email'])
|
||||
stats = Data.get_statistic()
|
||||
if Data.errors:
|
||||
context['errors'] += Data.pop_errors()
|
||||
else:
|
||||
stats = Data.use_display(show)
|
||||
if stats is None:
|
||||
context['errors'] += Data.pop_errors()
|
||||
stats = Data.use_interval(interval)
|
||||
if stats is None:
|
||||
context['errors'] += Data.pop_errors()
|
||||
context['log_stats'] = stats if not context['errors'] else None
|
||||
if request.method == 'GET':
|
||||
form = StatisticForm()
|
||||
|
Loading…
x
Reference in New Issue
Block a user