Merge branch 'develop' into feature/periodic

# Conflicts:
#	access_controller/settings.py
#	access_controller/urls.py
#	main/extra_func.py
#	main/views.py
This commit is contained in:
Yuriy Kulakov 2021-03-12 15:37:50 +03:00
commit d300cc2f06
15 changed files with 517 additions and 44 deletions

View File

@ -136,7 +136,6 @@ ACCOUNT_ACTIVATION_DAYS = 7
LOGIN_REDIRECT_URL = '/' LOGIN_REDIRECT_URL = '/'
LOGOUT_REDIRECT_URL = '/' LOGOUT_REDIRECT_URL = '/'
# Название_приложения.Названиеайла.Название_класса_обработчика # Название_приложения.Названиеайла.Название_класса_обработчика
AUTHENTICATION_BACKENDS = [ AUTHENTICATION_BACKENDS = [
'access_controller.auth.EmailAuthBackend', 'access_controller.auth.EmailAuthBackend',
@ -192,3 +191,5 @@ REST_FRAMEWORK = {
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly' 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
] ]
} }
ONE_DAY = 12 # Количество часов в 1 рабочем дне

View File

@ -16,8 +16,10 @@ Including another URLconf
from django.contrib import admin from django.contrib import admin
from django.contrib.auth import views as auth_views from django.contrib.auth import views as auth_views
from django.urls import path, include from django.urls import path, include
from main.views import work_page, work_hand_over, work_become_engineer, AdminPageView
from main.views import main_page, profile_page, CustomRegistrationView, CustomLoginView from main.views import main_page, profile_page, CustomRegistrationView, CustomLoginView
from main.views import work_page, work_hand_over, work_become_engineer, \
AdminPageView, statistic_page
from main.urls import router from main.urls import router
@ -26,7 +28,7 @@ urlpatterns = [
path('', main_page, name='index'), path('', main_page, name='index'),
path('accounts/profile/', profile_page, name='profile'), path('accounts/profile/', profile_page, name='profile'),
path('accounts/register/', CustomRegistrationView.as_view(), name='registration'), path('accounts/register/', CustomRegistrationView.as_view(), name='registration'),
path('accounts/login/', CustomLoginView.as_view(extra_context={}), name='login', ), # TODO add extra context path('accounts/login/', CustomLoginView.as_view(), name='login'),
path('accounts/', include('django.contrib.auth.urls')), path('accounts/', include('django.contrib.auth.urls')),
path('accounts/', include('django_registration.backends.one_step.urls')), path('accounts/', include('django_registration.backends.one_step.urls')),
path('work/<int:id>', work_page, name="work"), path('work/<int:id>', work_page, name="work"),
@ -34,8 +36,9 @@ urlpatterns = [
path('work/become_engineer/', work_become_engineer, name="work_become_engineer"), path('work/become_engineer/', work_become_engineer, name="work_become_engineer"),
path('accounts/', include('django_registration.backends.activation.urls')), path('accounts/', include('django_registration.backends.activation.urls')),
path('accounts/login/', include('django.contrib.auth.urls')), path('accounts/login/', include('django.contrib.auth.urls')),
path('control/', AdminPageView.as_view(), name='control') path('control/', AdminPageView.as_view(), name='control'),
] path('statistic/', statistic_page, name='statistic')
]
urlpatterns += [ urlpatterns += [
path( path(

View File

@ -1,14 +1,15 @@
import os import os
from datetime import timedelta, datetime, date
from django.contrib.auth.models import User from django.contrib.auth.models import User
from zenpy import Zenpy from zenpy import Zenpy
from zenpy.lib.exception import APIException from zenpy.lib.exception import APIException
from main.models import UserProfile from main.models import UserProfile, RoleChangeLogs
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from access_controller.settings import ZENDESK_ROLES from access_controller.settings import ZENDESK_ROLES as ROLES, ONE_DAY
class ZendeskAdmin: class ZendeskAdmin:
@ -75,7 +76,7 @@ class ZendeskAdmin:
user = self.admin.users.search(email).values[0] user = self.admin.users.search(email).values[0]
return user.photo['content_url'] if user.photo else None return user.photo['content_url'] if user.photo else None
def get_user(self, email: str) -> str: def get_user(self, email: str):
""" """
Функция **get_user** возвращает пользователя (объект) по его email Функция **get_user** возвращает пользователя (объект) по его email
@ -89,7 +90,7 @@ class ZendeskAdmin:
Функция **get_user_org** возвращает организацию, к которой относится пользователь по его email Функция **get_user_org** возвращает организацию, к которой относится пользователь по его email
""" """
user = self.admin.users.search(email).values[0] user = self.admin.users.search(email).values[0]
return user.organization.name return user.organization.name if user.organization else None
def create_admin(self) -> Zenpy: def create_admin(self) -> Zenpy:
""" """
@ -103,7 +104,7 @@ class ZendeskAdmin:
if self.email is None: if self.email is None:
raise ValueError('access_controller email not in env') raise ValueError('access_controller email not in env')
self.credentials['email'] = os.getenv('ACCESS_CONTROLLER_API_EMAIL') self.credentials['email'] = self.email
if self.token: if self.token:
self.credentials['token'] = self.token self.credentials['token'] = self.token
@ -132,14 +133,14 @@ def make_engineer(user_profile: UserProfile) -> UserProfile:
""" """
Функция **make_engineer** устанавливапет пользователю роль инженера. Функция **make_engineer** устанавливапет пользователю роль инженера.
""" """
update_role(user_profile, ZENDESK_ROLES['engineer']) update_role(user_profile, ROLES['engineer'])
def make_light_agent(user_profile: UserProfile) -> UserProfile: def make_light_agent(user_profile: UserProfile) -> UserProfile:
""" """
Функция **make_light_agent** устанавливапет пользователю роль легкого агента. Функция **make_light_agent** устанавливапет пользователю роль легкого агента.
""" """
update_role(user_profile, ZENDESK_ROLES['light_agent']) update_role(user_profile, ROLES['light_agent'])
def get_users_list() -> list: def get_users_list() -> list:
@ -161,6 +162,7 @@ def update_profile(user_profile: UserProfile) -> UserProfile:
user = ZendeskAdmin().get_user(user_profile.user.email) user = ZendeskAdmin().get_user(user_profile.user.email)
user_profile.name = user.name user_profile.name = user.name
user_profile.role = user.role user_profile.role = user.role
user_profile.custom_role_id = user.custom_role_id if user.custom_role_id else 0
user_profile.image = user.photo['content_url'] if user.photo else None user_profile.image = user.photo['content_url'] if user.photo else None
user_profile.save() user_profile.save()
@ -214,9 +216,9 @@ def count_users(users) -> tuple:
""" """
engineers, light_agents = 0, 0 engineers, light_agents = 0, 0
for user in users: for user in users:
if user.custom_role_id == ZENDESK_ROLES['engineer']: if user.custom_role_id == ROLES['engineer']:
engineers += 1 engineers += 1
elif user.custom_role_id == ZENDESK_ROLES['light_agent']: elif user.custom_role_id == ROLES['light_agent']:
light_agents += 1 light_agents += 1
return engineers, light_agents return engineers, light_agents
@ -233,3 +235,211 @@ def update_users_in_model():
except ObjectDoesNotExist: except ObjectDoesNotExist:
pass pass
return users return users
def daterange(start_date, end_date) -> list:
"""
Возвращает список дней с start_date по end_date исключая правую границу
"""
dates = []
for n in range(int((end_date - start_date).days)):
dates.append(start_date + timedelta(n))
return dates
def get_timedelta(log, time=None) -> timedelta:
"""
Возвращает объект класса timedelta, который хранит промежуток времени от начала суток до момента,
который находится в log (объект класса RoleChangeLogs) или в time(datetime.time), если введён
"""
if time is None:
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)
class StatisticData:
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()
if stat is None:
self._set_statistic()
else:
self.statistic = stat
def get_statistic(self):
"""
Вернуть словарь statistic с применением формата отображения и интеравала работы(если они есть)
None, если были ошибки при создании
"""
if self.is_valid_statistic():
stat = self.statistic
stat = self._use_display(stat)
stat = self._use_interval(stat)
return stat
else:
return None
def is_valid_statistic(self):
"""
Были ли ошибки при создании статистики
"""
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 stat
new_stat = {}
for key, item in stat.items():
if self.display == 'hours':
new_stat[key] = item / 3600
elif self.display == 'days':
new_stat[key] = item / (ONE_DAY * 3600)
return new_stat
def _use_interval(self, stat):
"""
Объединяет ключи и значения в соответствии с интервалом работы
"""
if not self.is_valid_statistic() or not self.interval:
return stat
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 # статистика изначально в днях
return 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
"""
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')
except User.DoesNotExist:
self.errors += ['Пользователь не найден']
def _set_statistic(self):
"""
Функция заполняет словарь, в котором ключ - дата, значение - кол-во проработанных в этот день секунд
"""
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 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 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()
if self.end_date == datetime.now().date():
self.statistic[self.end_date] = get_timedelta(None, datetime.now().time()).total_seconds()
for log_index in range(len(self.data) - 1):
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()
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.statistic.clear()
self.fill_daterange(self.start_date, self.end_date + timedelta(days=1), 0)

View File

@ -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
@ -14,7 +15,6 @@ class CustomRegistrationForm(RegistrationFormUniqueEmail):
:param visible_fields.email: Поле для ввода email, зарегистирированного на Zendesk :param visible_fields.email: Поле для ввода email, зарегистирированного на Zendesk
:type visible_fields.email: :class:`django_registration.forms.RegistrationFormUniqueEmail` :type visible_fields.email: :class:`django_registration.forms.RegistrationFormUniqueEmail`
""" """
def __init__(self, *args, **kwargs) -> RegistrationFormUniqueEmail: def __init__(self, *args, **kwargs) -> RegistrationFormUniqueEmail:
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
for visible in self.visible_fields(): for visible in self.visible_fields():
@ -23,7 +23,7 @@ class CustomRegistrationForm(RegistrationFormUniqueEmail):
visible.field.widget.attrs['class'] += 'form-control' visible.field.widget.attrs['class'] += 'form-control'
else: else:
visible.field.widget.attrs['class'] = 'form-control' visible.field.widget.attrs['class'] = 'form-control'
if visible.html_name !='email': if visible.html_name != 'email':
visible.field.required = False visible.field.required = False
class Meta(RegistrationFormUniqueEmail.Meta): class Meta(RegistrationFormUniqueEmail.Meta):
@ -42,7 +42,8 @@ class AdminPageUsers(forms.Form):
queryset=UserProfile.objects.filter(role='agent'), queryset=UserProfile.objects.filter(role='agent'),
widget=forms.CheckboxSelectMultiple( widget=forms.CheckboxSelectMultiple(
attrs={ attrs={
'class': 'form-check-input' 'class': 'form-check-input',
} }
), ),
label='' label=''
@ -65,3 +66,31 @@ class CustomAuthenticationForm(AuthenticationForm):
, ,
'inactive': "Аккаунт не активен.", 'inactive': "Аккаунт не активен.",
} }
class StatisticForm(forms.Form):
email = forms.EmailField(
label='Электроная почта',
)
interval = forms.CharField( # TODO: Переделать под html страницу
label='Интервал работы',
)
display_format = forms.CharField( # TODO: Переделать под html страницу
label='Формат отображения',
)
range_start = forms.DateField( # TODO: Переделать под html страницу
label='Начало диапазона',
widget=forms.DateInput(
attrs={
'type': 'date',
}
),
)
range_end = forms.DateField( # TODO: Переделать под html страницу
label='Конец диапазона',
widget=forms.DateInput(
attrs={
'type': 'date',
}
),
)

View File

@ -0,0 +1,61 @@
# Generated by Django 3.1.6 on 2021-03-11 08:00
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('main', '0008_auto_20210303_2305'),
]
operations = [
migrations.AlterField(
model_name='rolechangelogs',
name='change_time',
field=models.DateTimeField(help_text='Дата и время изменения роли'),
),
migrations.AlterField(
model_name='rolechangelogs',
name='changed_by',
field=models.ForeignKey(help_text='Кем была изменена роль', on_delete=django.db.models.deletion.CASCADE, related_name='changed_by', to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='rolechangelogs',
name='name',
field=models.TextField(help_text='Имя пользователя'),
),
migrations.AlterField(
model_name='rolechangelogs',
name='new_role',
field=models.TextField(help_text='Присвоенная роль'),
),
migrations.AlterField(
model_name='rolechangelogs',
name='user',
field=models.ForeignKey(help_text='Пользователь, которому присвоили другую роль', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='userprofile',
name='image',
field=models.URLField(blank=True, help_text='Аватарка', null=True),
),
migrations.AlterField(
model_name='userprofile',
name='name',
field=models.CharField(default='None', help_text='Имя пользователя на нашем сайте', max_length=100),
),
migrations.AlterField(
model_name='userprofile',
name='role',
field=models.CharField(default='None', help_text='Код роли пользователя', max_length=100),
),
migrations.AlterField(
model_name='userprofile',
name='user',
field=models.OneToOneField(help_text='Пользователь', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 3.1.6 on 2021-03-11 08:04
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('main', '0009_models_help_text'),
]
operations = [
migrations.AlterModelOptions(
name='userprofile',
options={'permissions': (('has_control_access', 'Can view admin page'),)},
),
]

View File

@ -0,0 +1,28 @@
# Generated by Django 3.1.6 on 2021-03-11 14:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('main', '0010_userprofile_meta'),
]
operations = [
migrations.AddField(
model_name='rolechangelogs',
name='old_role',
field=models.IntegerField(default=0, help_text='Старая роль'),
),
migrations.AlterField(
model_name='rolechangelogs',
name='new_role',
field=models.IntegerField(default=0, help_text='Присвоенная роль'),
),
migrations.AlterField(
model_name='userprofile',
name='role',
field=models.IntegerField(default=0, help_text='Код роли пользователя'),
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 3.1.6 on 2021-03-12 09:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('main', '0011_auto_20210311_1734'),
]
operations = [
migrations.AddField(
model_name='userprofile',
name='custom_role_id',
field=models.IntegerField(default=0, help_text='Код роли пользователя'),
),
migrations.AlterField(
model_name='userprofile',
name='role',
field=models.CharField(default='None', help_text='Глобальное имя роли пользователя', max_length=100),
),
]

View File

@ -7,8 +7,14 @@ from django.dispatch import receiver
class UserProfile(models.Model): class UserProfile(models.Model):
"""Модель профиля пользователя""" """Модель профиля пользователя"""
class Meta:
permissions = (
('has_control_access', 'Can view admin page'),
)
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.CharField(default='None', max_length=100, help_text='Глобальное имя роли пользователя')
custom_role_id = 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='Имя пользователя на нашем сайте')
@ -26,8 +32,11 @@ def save_user_profile(sender, instance, **kwargs):
class RoleChangeLogs(models.Model): 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='Имя пользователя')
new_role = models.TextField(help_text='Присвоенная роль') old_role = models.IntegerField(default=0, help_text='Старая роль')
new_role = models.IntegerField(default=0, 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='Кем была изменена роль')

View File

@ -3,16 +3,18 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<nav class="navbar navbar-light" style="background-color: #00FF00;"> <nav class="navbar navbar-light" style="background-color: #113A60;">
<a class="navbar-brand" href="{% url 'index' %}"> <a class="navbar-brand" href="{% url 'index' %}">
<img src="{% static 'main/img/logo.png' %}" width="30" height="30" class="d-inline-block align-top" alt="" loading="lazy"> <img src="{% static 'main/img/logo_real.png' %}" width="107" height="22" class="d-inline-block align-top" alt="" loading="lazy">
Access Controller <t style="color:#FFFFFF">Access Controller</t>
</a> </a>
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
<div class="btn-group" role="group" aria-label="Basic example"> <div class="btn-group" role="group" aria-label="Basic example">
<a class="btn btn-secondary" href="{% url 'profile' %}">Профиль</a> <a class="btn btn-secondary" href="{% url 'profile' %}">Профиль</a>
{% if perms.main.has_control_access %} {% if perms.main.has_control_access %}
<a class="btn btn-secondary" href="{% url 'control' %}">Управление</a> <a class="btn btn-secondary" href="{% url 'control' %}">Управление</a>
{% else %}
<a class="btn btn-secondary" href="{% url 'work' request.user.id %}">Запрос прав</a>
{% endif %} {% endif %}
<a class="btn btn-secondary" href="{% url 'logout' %}">Выйти</a> <a class="btn btn-secondary" href="{% url 'logout' %}">Выйти</a>
</div> </div>

View File

@ -10,5 +10,5 @@
{% block content %} {% block content %}
<br> <br>
<h4> Регистрация прошла успешно. <a href="/login/">Войти сейчас</a></h4> <h4> Регистрация прошла успешно. <a href="{% url 'login'%}">Войти сейчас</a></h4>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,49 @@
{% extends 'base/base.html' %}
{% load static %}
{% block title %}{{ pagename }}{% endblock %}
{% block heading %} Пример страницы статистики(палками не бейти плиз){% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="{% static 'main/css/work.css' %}">
{% endblock %}
{% block content %}
<div>
<form action="" method="post">
{% csrf_token %}
{% for field in form %}
{{field.label}}
{{field}}
<br>
{% endfor %}
<input type="submit">
</form>
<ul>
{% for error in errors %}
<li><span class="badge bg-danger">{{error}}</span></li>
{% endfor %}
</ul>
{%if form.errors%}
<ul>
{% for field, errors in form.errors.items %}
{% for error in errors %}
<li><span class="badge bg-danger">{{error}}</span></li>
{% endfor %}
{% endfor %}
</ul>
{%endif%}
<ul>
{% for warning in warnings %}
<li><span class="badge bg-warning">{{warning}}</span></li>
{% endfor %}
</ul>
{% for key,val in log_stats.items %}
<h3>{{key}} <b>|</b> {{val}}</h3>
<br>
{% endfor %}
</div>
{% endblock %}

View File

@ -22,13 +22,13 @@
<h6 class="table-title">Список сотрудников с правами инженера</h6> <h6 class="table-title">Список сотрудников с правами инженера</h6>
<table class="light-table"> <table class="light-table">
<thead> <thead>
<th>ID</th> <th>Email</th>
<th>Name</th> <th>Name</th>
</thead> </thead>
<tbody> <tbody>
{% for engineer in engineers %} {% for engineer in engineers %}
<tr> <tr>
<td>{{ engineer.id }}</td> <td>{{ engineer.email }}</td>
<td>{{ engineer.name }}</td> <td>{{ engineer.name }}</td>
</tr> </tr>
{% endfor %} {% endfor %}

View File

@ -1,8 +1,10 @@
import logging import logging
import os import os
from datetime import datetime
from django.contrib.auth.tokens import default_token_generator
from django.contrib.auth.forms import PasswordResetForm from django.contrib.auth.forms import PasswordResetForm
from django.contrib.auth.models import User, Permission
from django.contrib.auth.tokens import default_token_generator
from django.contrib.auth.views import LoginView from django.contrib.auth.views import LoginView
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.handlers.wsgi import WSGIRequest from django.core.handlers.wsgi import WSGIRequest
@ -10,21 +12,21 @@ from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import render, get_list_or_404, redirect from django.shortcuts import render, get_list_or_404, redirect
from django.urls import reverse_lazy, reverse from django.urls import reverse_lazy, reverse
from django.views.generic import FormView from django.views.generic import FormView
from zenpy import Zenpy
from access_controller.settings import EMAIL_HOST_USER
from main.extra_func import check_user_exist, update_profile, get_user_organization, \
make_engineer, make_light_agent, get_users_list, update_users_in_model, count_users
from django.contrib.auth.models import User, Permission
from main.models import UserProfile
from main.forms import CustomRegistrationForm, AdminPageUsers, CustomAuthenticationForm
from django_registration.views import RegistrationView from django_registration.views import RegistrationView
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from zenpy import Zenpy
from zenpy.lib.api_objects import User as ZenpyUser from zenpy.lib.api_objects import User as ZenpyUser
from main.extra_func import check_user_exist, update_profile, get_user_organization, \
make_engineer, make_light_agent, get_users_list, update_users_in_model, count_users, \
StatisticData
from main.models import UserProfile
from main.forms import AdminPageUsers, CustomRegistrationForm, CustomAuthenticationForm, StatisticForm
from access_controller.settings import EMAIL_HOST_USER, ZENDESK_ROLES
# Django REST # Django REST
from rest_framework import viewsets, status from rest_framework import viewsets, status
from main.serializers import ProfileSerializer from main.serializers import ProfileSerializer
@ -130,10 +132,20 @@ def auth_user(request):
@login_required() @login_required()
def work_page(request, id): def work_page(request, id):
users = get_users_list()
if request.user.id == id: if request.user.id == id:
engineers = []
light_agents = []
for user in users:
if user.custom_role_id == ZENDESK_ROLES['engineer']:
engineers.append(user)
elif user.custom_role_id == ZENDESK_ROLES['light_agent']:
light_agents.append(user)
context = { context = {
'engineers': UserProfile.objects.filter(role="admin"), 'engineers': engineers,
'agents': UserProfile.objects.filter(role="agent"), 'agents': light_agents,
'pagename': 'Управление правами' 'pagename': 'Управление правами'
} }
return render(request, 'pages/work.html', context) return render(request, 'pages/work.html', context)
@ -143,8 +155,9 @@ def work_page(request, id):
@login_required() @login_required()
def work_hand_over(request): def work_hand_over(request):
zenpy_user, admin = auth_user(request) zenpy_user, admin = auth_user(request)
if zenpy_user.role == "admin" or zenpy_user.role == "end-user":
zenpy_user.role = "agent" if zenpy_user.custom_role_id == ZENDESK_ROLES['engineer']:
zenpy_user.custom_role_id = ZENDESK_ROLES['light_agent']
admin.users.update(zenpy_user) admin.users.update(zenpy_user)
request.user.userprofile.role = "agent" request.user.userprofile.role = "agent"
request.user.userprofile.save() request.user.userprofile.save()
@ -154,10 +167,10 @@ def work_hand_over(request):
@login_required() @login_required()
def work_become_engineer(request): def work_become_engineer(request):
zenpy_user, admin = auth_user(request) zenpy_user, admin = auth_user(request)
if zenpy_user.role == "agent" or zenpy_user.role == "end-user": if zenpy_user.custom_role_id == ZENDESK_ROLES['light_agent']:
zenpy_user.role = "admin" zenpy_user.custom_role_id = ZENDESK_ROLES['engineer']
admin.users.update(zenpy_user) admin.users.update(zenpy_user)
request.user.userprofile.role = "admin" request.user.userprofile.role = "agent"
request.user.userprofile.save() request.user.userprofile.save()
return HttpResponseRedirect(reverse('work', args=(request.user.id,))) return HttpResponseRedirect(reverse('work', args=(request.user.id,)))
@ -233,3 +246,31 @@ class UsersViewSet(viewsets.ReadOnlyModelViewSet):
'light_agents': count[1] 'light_agents': count[1]
}) })
@login_required()
def statistic_page(request):
if not request.user.is_superuser:
return redirect('index')
context = {
'pagename': 'страница статистики',
'errors': list(),
}
if request.method == "POST":
form = StatisticForm(request.POST)
if form.is_valid():
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']
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()
context['form'] = form
return render(request, 'pages/stat.html', context)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB