Update class StatisticData
This commit is contained in:
parent
5efec2641a
commit
b825bcdff3
@ -204,80 +204,32 @@ def last_day_of_month(day):
|
|||||||
|
|
||||||
|
|
||||||
class StatisticData:
|
class StatisticData:
|
||||||
def __init__(self, start_date, end_date, user_email):
|
def __init__(self, start_date, end_date, user_email, stat=None):
|
||||||
self.errors = list()
|
self.display = None
|
||||||
self.data = None
|
self.interval = None
|
||||||
self.statistic = dict()
|
|
||||||
self.start_date = start_date
|
self.start_date = start_date
|
||||||
self.end_date = end_date
|
self.end_date = end_date
|
||||||
self.email = user_email
|
self.email = user_email
|
||||||
|
self.errors = list()
|
||||||
|
self.warnings = list()
|
||||||
|
self.data = dict()
|
||||||
|
self.statistic = dict()
|
||||||
self._set_data()
|
self._set_data()
|
||||||
|
if stat is None:
|
||||||
self._set_statistic()
|
self._set_statistic()
|
||||||
|
|
||||||
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:
|
else:
|
||||||
stat[index] = value
|
self.statistic = stat
|
||||||
elif interval == 'days':
|
|
||||||
stat = self.get_statistic()
|
|
||||||
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(self):
|
|
||||||
"""
|
|
||||||
Вернуть данные
|
|
||||||
data - массив объектов RoleChangeLogs, является списком логов пользователя
|
|
||||||
"""
|
|
||||||
if self.is_valid_data():
|
|
||||||
return self.data
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_statistic(self):
|
def get_statistic(self):
|
||||||
"""
|
"""
|
||||||
Вернуть словарь statistic или None, если были ошибки при создании
|
Вернуть словарь statistic с применением формата отображения и интеравала работы(если они есть)
|
||||||
|
None, если были ошибки при создании
|
||||||
"""
|
"""
|
||||||
if self.is_valid_statistic():
|
if self.is_valid_statistic():
|
||||||
return self.statistic.copy()
|
stat = self.statistic
|
||||||
|
self._use_display(stat)
|
||||||
|
self._use_interval(stat)
|
||||||
|
return stat
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -287,22 +239,96 @@ class StatisticData:
|
|||||||
"""
|
"""
|
||||||
return not self.errors and self.statistic
|
return not self.errors and self.statistic
|
||||||
|
|
||||||
|
def set_interval(self, interval):
|
||||||
|
"""
|
||||||
|
Устанавливает интервал работы
|
||||||
|
"""
|
||||||
|
if interval not in ['months', 'days']:
|
||||||
|
self.errors += ['Интервал работы должен быть в днях или месяцах']
|
||||||
|
return False
|
||||||
|
self.interval = interval
|
||||||
|
return True
|
||||||
|
|
||||||
|
def set_display(self, display_format):
|
||||||
|
"""
|
||||||
|
Устанавливает формат отображения
|
||||||
|
"""
|
||||||
|
if display_format not in ['days', 'hours']:
|
||||||
|
self.errors += ['Формат отображения должен быть в часах или днях']
|
||||||
|
return False
|
||||||
|
self.display = display_format
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
"""
|
||||||
|
Вернуть данные
|
||||||
|
data - массив объектов RoleChangeLogs, является списком логов пользователя
|
||||||
|
data может быть пустым списком
|
||||||
|
"""
|
||||||
|
if self.is_valid_data():
|
||||||
|
return self.data
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
def is_valid_data(self):
|
def is_valid_data(self):
|
||||||
"""
|
"""
|
||||||
Были ли ошибки при создании объекта
|
Были ли ошибки при получении логов
|
||||||
"""
|
"""
|
||||||
return not self.errors
|
return not self.errors
|
||||||
|
|
||||||
|
def _use_display(self, stat):
|
||||||
|
"""
|
||||||
|
Приводит данные к формату отображения
|
||||||
|
"""
|
||||||
|
if not self.is_valid_statistic() or not self.display:
|
||||||
|
return
|
||||||
|
for key, item in self.statistic.items():
|
||||||
|
if self.display == 'hours':
|
||||||
|
self.statistic[key] = item / 3600
|
||||||
|
elif self.display == 'days':
|
||||||
|
self.statistic[key] = item / 86400
|
||||||
|
|
||||||
|
def _use_interval(self, stat):
|
||||||
|
"""
|
||||||
|
Объединяет ключи и значения в соответствии с интервалом работы
|
||||||
|
"""
|
||||||
|
if not self.is_valid_statistic() or not self.interval:
|
||||||
|
return
|
||||||
|
new_stat = {}
|
||||||
|
if self.interval == 'months':
|
||||||
|
# Переделываем ключи под формат('начало_месяца - конец_месяца')
|
||||||
|
for key, value in stat.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 new_stat.get(index):
|
||||||
|
new_stat[index] += value
|
||||||
|
else:
|
||||||
|
new_stat[index] = value
|
||||||
|
elif self.interval == 'days':
|
||||||
|
new_stat = stat # статистика изначально в днях
|
||||||
|
self.statistic = new_stat
|
||||||
|
|
||||||
|
def check_time(self):
|
||||||
|
"""
|
||||||
|
Проверка на правильность введенного времени
|
||||||
|
"""
|
||||||
|
if self.end_date < self.start_date or self.end_date > datetime.now().date():
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def _set_data(self):
|
def _set_data(self):
|
||||||
"""
|
"""
|
||||||
Получение списка из лог-ов в диапазоне дат start_date-end_date для пользователя с почтой email
|
Получение логов в диапазоне дат start_date-end_date для пользователя с почтой email
|
||||||
"""
|
"""
|
||||||
|
if not self.check_time():
|
||||||
|
self.errors += ['Конец диапазона должен быть позже начала диапазона и раньше текущего времени']
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
self.data = RoleChangeLogs.objects.filter(
|
self.data = RoleChangeLogs.objects.filter(
|
||||||
change_time__range=[self.start_date, self.end_date + timedelta(days=1)],
|
change_time__range=[self.start_date, self.end_date + timedelta(days=1)],
|
||||||
user=User.objects.get(email=self.email),
|
user=User.objects.get(email=self.email),
|
||||||
).order_by('change_time')
|
).order_by('change_time')
|
||||||
self.errors.clear()
|
|
||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
self.errors += ['Пользователь не найден']
|
self.errors += ['Пользователь не найден']
|
||||||
|
|
||||||
@ -310,43 +336,35 @@ class StatisticData:
|
|||||||
"""
|
"""
|
||||||
Функция заполняет словарь, в котором ключ - дата, значение - кол-во проработанных в этот день секунд
|
Функция заполняет словарь, в котором ключ - дата, значение - кол-во проработанных в этот день секунд
|
||||||
"""
|
"""
|
||||||
if not self.get_data():
|
|
||||||
self.errors += ['Не обнаружены изменения роли в данном промежутке']
|
|
||||||
return None
|
|
||||||
|
|
||||||
self.clear_statistic()
|
self.clear_statistic()
|
||||||
|
if not self.get_data():
|
||||||
|
self.warnings += ['Не обнаружены изменения роли в данном промежутке']
|
||||||
|
return None
|
||||||
first_log, last_log = self.data[0], self.data[len(self.data) - 1]
|
first_log, last_log = self.data[0], self.data[len(self.data) - 1]
|
||||||
|
if first_log.old_role == ROLES['engineer']:
|
||||||
# Если инженер работал ещё до начала диапазона
|
|
||||||
if int(first_log.old_role) == ROLES['engineer']:
|
|
||||||
self.fill_daterange(self.start_date, first_log.change_time.date())
|
self.fill_daterange(self.start_date, first_log.change_time.date())
|
||||||
self.statistic[first_log.change_time.date()] += get_timedelta(first_log).total_seconds()
|
self.statistic[first_log.change_time.date()] += get_timedelta(first_log).total_seconds()
|
||||||
|
if last_log.new_role == ROLES['engineer']:
|
||||||
# Если инженер закончил работать после диапазона
|
|
||||||
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.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()
|
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):
|
for log_index in range(len(self.data) - 1):
|
||||||
if int(self.data[log_index].new_role) == ROLES['engineer']:
|
if self.data[log_index].new_role == ROLES['engineer']:
|
||||||
current_log, next_log = self.data[log_index], self.data[log_index + 1]
|
current_log, next_log = self.data[log_index], self.data[log_index + 1]
|
||||||
# Если сессия закончилась НЕ в тот же день, что и началась
|
|
||||||
if current_log.change_time.date() != next_log.change_time.date():
|
if current_log.change_time.date() != next_log.change_time.date():
|
||||||
self.statistic[current_log.change_time.date()] += (
|
self.statistic[current_log.change_time.date()] += (
|
||||||
timedelta(days=1) - get_timedelta(current_log)).total_seconds()
|
timedelta(days=1) - get_timedelta(current_log)).total_seconds()
|
||||||
self.statistic[next_log.change_time.date()] += get_timedelta(next_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())
|
self.fill_daterange(current_log.change_time.date() + timedelta(days=1), next_log.change_time.date())
|
||||||
|
|
||||||
# Если сессия закончилась в тот же день, что и началась
|
|
||||||
else:
|
else:
|
||||||
elapsed_time = next_log.change_time - current_log.change_time
|
elapsed_time = next_log.change_time - current_log.change_time
|
||||||
self.statistic[current_log.change_time.date()] += elapsed_time.total_seconds()
|
self.statistic[current_log.change_time.date()] += elapsed_time.total_seconds()
|
||||||
|
|
||||||
def fill_daterange(self, first, last, val=24 * 3600):
|
def fill_daterange(self, first, last, val=24 * 3600):
|
||||||
"""
|
"""
|
||||||
Заполение диапазона дат значением val, по умолчанию val = кол-во секунд в 1 дне
|
Заполение диапазона дат значением val
|
||||||
|
по умолчанию val = кол-во секунд в 1 дне
|
||||||
"""
|
"""
|
||||||
|
self.statistic.clear()
|
||||||
for day in daterange(first, last):
|
for day in daterange(first, last):
|
||||||
self.statistic[day] = val
|
self.statistic[day] = val
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ from django import forms
|
|||||||
from django.contrib.auth.forms import AuthenticationForm
|
from django.contrib.auth.forms import AuthenticationForm
|
||||||
from django_registration.forms import RegistrationFormUniqueEmail
|
from django_registration.forms import RegistrationFormUniqueEmail
|
||||||
|
|
||||||
|
from access_controller.settings import ZENDESK_ROLES
|
||||||
from main.models import UserProfile
|
from main.models import UserProfile
|
||||||
|
|
||||||
|
|
||||||
@ -38,7 +39,7 @@ class AdminPageUsers(forms.Form):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
users = forms.ModelMultipleChoiceField(
|
users = forms.ModelMultipleChoiceField(
|
||||||
queryset=UserProfile.objects.filter(role='agent'),
|
queryset=UserProfile.objects.all(),
|
||||||
widget=forms.CheckboxSelectMultiple(
|
widget=forms.CheckboxSelectMultiple(
|
||||||
attrs={
|
attrs={
|
||||||
'class': 'form-check-input'
|
'class': 'form-check-input'
|
||||||
|
@ -8,7 +8,7 @@ class UserProfile(models.Model):
|
|||||||
"""Модель профиля пользователя"""
|
"""Модель профиля пользователя"""
|
||||||
|
|
||||||
user = models.OneToOneField(to=User, on_delete=models.CASCADE, help_text='Пользователь')
|
user = models.OneToOneField(to=User, on_delete=models.CASCADE, help_text='Пользователь')
|
||||||
role = models.CharField(default='None', max_length=100, help_text='Код роли пользователя')
|
role = models.IntegerField(default=0, help_text='Код роли пользователя')
|
||||||
image = models.URLField(null=True, blank=True, help_text='Аватарка')
|
image = models.URLField(null=True, blank=True, help_text='Аватарка')
|
||||||
name = models.CharField(default='None', max_length=100, help_text='Имя пользователя на нашем сайте')
|
name = models.CharField(default='None', max_length=100, help_text='Имя пользователя на нашем сайте')
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ class RoleChangeLogs(models.Model):
|
|||||||
"""Модель для логирования изменений ролей пользователя"""
|
"""Модель для логирования изменений ролей пользователя"""
|
||||||
user = models.ForeignKey(to=User, on_delete=models.CASCADE, help_text='Пользователь, которому присвоили другую роль')
|
user = models.ForeignKey(to=User, on_delete=models.CASCADE, help_text='Пользователь, которому присвоили другую роль')
|
||||||
name = models.TextField(help_text='Имя пользователя')
|
name = models.TextField(help_text='Имя пользователя')
|
||||||
old_role = models.TextField(help_text='Старая роль')
|
old_role = models.IntegerField(help_text='Старая роль')
|
||||||
new_role = models.TextField(help_text='Присвоенная роль')
|
new_role = models.IntegerField(help_text='Присвоенная роль')
|
||||||
change_time = models.DateTimeField(help_text='Дата и время изменения роли')
|
change_time = models.DateTimeField(help_text='Дата и время изменения роли')
|
||||||
changed_by = models.ForeignKey(to=User, on_delete=models.CASCADE, related_name='changed_by', help_text='Кем была изменена роль')
|
changed_by = models.ForeignKey(to=User, on_delete=models.CASCADE, related_name='changed_by', help_text='Кем была изменена роль')
|
||||||
|
@ -243,27 +243,20 @@ def statistic_page(request):
|
|||||||
return redirect('index')
|
return redirect('index')
|
||||||
context = {
|
context = {
|
||||||
'pagename': 'страница статистики',
|
'pagename': 'страница статистики',
|
||||||
'errors': [],
|
|
||||||
}
|
}
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = StatisticForm(request.POST)
|
form = StatisticForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
start_date, end_date, stats = form.cleaned_data['range_start'], form.cleaned_data['range_end'], dict()
|
start_date, end_date = form.cleaned_data['range_start'], form.cleaned_data['range_end']
|
||||||
interval, show = form.cleaned_data['interval'], form.cleaned_data['display_format']
|
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 = StatisticData(start_date, end_date, form.cleaned_data['email'])
|
Data = StatisticData(start_date, end_date, form.cleaned_data['email'])
|
||||||
|
Data.set_display(show)
|
||||||
|
Data.set_interval(interval)
|
||||||
stats = Data.get_statistic()
|
stats = Data.get_statistic()
|
||||||
if Data.errors:
|
if Data.errors:
|
||||||
context['errors'] += Data.pop_errors()
|
context['errors'] = Data.errors
|
||||||
else:
|
if Data.warnings:
|
||||||
stats = Data.use_display(show)
|
context['warnings'] = Data.warnings
|
||||||
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
|
context['log_stats'] = stats if not context['errors'] else None
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
form = StatisticForm()
|
form = StatisticForm()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user