From b825bcdff3596341b28246c7e45db5927348bd91 Mon Sep 17 00:00:00 2001 From: Sokurov Idar Date: Thu, 11 Mar 2021 17:30:55 +0300 Subject: [PATCH] Update class StatisticData --- main/extra_func.py | 186 +++++++++++++++++++++++++-------------------- main/forms.py | 3 +- main/models.py | 6 +- main/views.py | 25 +++--- 4 files changed, 116 insertions(+), 104 deletions(-) diff --git a/main/extra_func.py b/main/extra_func.py index c47e6a1..167fb09 100644 --- a/main/extra_func.py +++ b/main/extra_func.py @@ -204,80 +204,32 @@ def last_day_of_month(day): class StatisticData: - def __init__(self, start_date, end_date, user_email): - self.errors = list() - self.data = None - self.statistic = dict() + def __init__(self, start_date, end_date, user_email, stat=None): + self.display = None + self.interval = None self.start_date = start_date self.end_date = end_date self.email = user_email + self.errors = list() + self.warnings = list() + self.data = dict() + self.statistic = dict() self._set_data() - 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: - stat[index] = value - 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 + if stat is None: + self._set_statistic() else: - return None + self.statistic = stat def get_statistic(self): """ - Вернуть словарь statistic или None, если были ошибки при создании + Вернуть словарь statistic с применением формата отображения и интеравала работы(если они есть) + None, если были ошибки при создании """ if self.is_valid_statistic(): - return self.statistic.copy() + stat = self.statistic + self._use_display(stat) + self._use_interval(stat) + return stat else: return None @@ -287,22 +239,96 @@ class StatisticData: """ 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): """ - Были ли ошибки при создании объекта + Были ли ошибки при получении логов """ 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): """ - Получение списка из лог-ов в диапазоне дат start_date-end_date для пользователя с почтой email + Получение логов в диапазоне дат start_date-end_date для пользователя с почтой email """ + if not self.check_time(): + self.errors += ['Конец диапазона должен быть позже начала диапазона и раньше текущего времени'] + return 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 += ['Пользователь не найден'] @@ -310,43 +336,35 @@ class StatisticData: """ Функция заполняет словарь, в котором ключ - дата, значение - кол-во проработанных в этот день секунд """ - if not self.get_data(): - self.errors += ['Не обнаружены изменения роли в данном промежутке'] - return None - 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] - - # Если инженер работал ещё до начала диапазона - if int(first_log.old_role) == ROLES['engineer']: + if 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']: + if 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']: + if 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 дне + Заполение диапазона дат значением val + по умолчанию val = кол-во секунд в 1 дне """ + self.statistic.clear() for day in daterange(first, last): self.statistic[day] = val diff --git a/main/forms.py b/main/forms.py index e98b7e1..53eb63a 100644 --- a/main/forms.py +++ b/main/forms.py @@ -2,6 +2,7 @@ from django import forms from django.contrib.auth.forms import AuthenticationForm from django_registration.forms import RegistrationFormUniqueEmail +from access_controller.settings import ZENDESK_ROLES from main.models import UserProfile @@ -38,7 +39,7 @@ class AdminPageUsers(forms.Form): """ users = forms.ModelMultipleChoiceField( - queryset=UserProfile.objects.filter(role='agent'), + queryset=UserProfile.objects.all(), widget=forms.CheckboxSelectMultiple( attrs={ 'class': 'form-check-input' diff --git a/main/models.py b/main/models.py index 74aa5ba..fa2583d 100644 --- a/main/models.py +++ b/main/models.py @@ -8,7 +8,7 @@ class UserProfile(models.Model): """Модель профиля пользователя""" 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='Аватарка') 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='Пользователь, которому присвоили другую роль') name = models.TextField(help_text='Имя пользователя') - old_role = models.TextField(help_text='Старая роль') - new_role = models.TextField(help_text='Присвоенная роль') + old_role = models.IntegerField(help_text='Старая роль') + new_role = models.IntegerField(help_text='Присвоенная роль') change_time = models.DateTimeField(help_text='Дата и время изменения роли') changed_by = models.ForeignKey(to=User, on_delete=models.CASCADE, related_name='changed_by', help_text='Кем была изменена роль') diff --git a/main/views.py b/main/views.py index bb9434f..372adc9 100644 --- a/main/views.py +++ b/main/views.py @@ -243,27 +243,20 @@ def statistic_page(request): return redirect('index') context = { 'pagename': 'страница статистики', - 'errors': [], } if request.method == "POST": form = StatisticForm(request.POST) 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'] - if end_date < start_date or end_date > datetime.now().date(): - context['errors'] += ['Конец диапазона должен быть позже начала диапазона и раньше текущего времени'] - else: - 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() + Data = StatisticData(start_date, end_date, form.cleaned_data['email']) + Data.set_display(show) + Data.set_interval(interval) + stats = Data.get_statistic() + if Data.errors: + context['errors'] = Data.errors + if Data.warnings: + context['warnings'] = Data.warnings context['log_stats'] = stats if not context['errors'] else None if request.method == 'GET': form = StatisticForm()