Merge branch 'develop' into feature/statistic/html
This commit is contained in:
commit
b50a6668a6
@ -36,6 +36,7 @@ INSTALLED_APPS = [
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django_registration',
|
||||
'rest_framework',
|
||||
'main',
|
||||
]
|
||||
|
||||
@ -135,7 +136,6 @@ ACCOUNT_ACTIVATION_DAYS = 7
|
||||
LOGIN_REDIRECT_URL = '/'
|
||||
LOGOUT_REDIRECT_URL = '/'
|
||||
|
||||
|
||||
# Название_приложения.Название_файла.Название_класса_обработчика
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
'access_controller.auth.EmailAuthBackend',
|
||||
@ -183,3 +183,13 @@ ZENDESK_ROLES = {
|
||||
'engineer': 360005209000,
|
||||
'light_agent': 360005208980,
|
||||
}
|
||||
|
||||
REST_FRAMEWORK = {
|
||||
# Use Django's standard `django.contrib.auth` permissions,
|
||||
# or allow read-only access for unauthenticated users.
|
||||
'DEFAULT_PERMISSION_CLASSES': [
|
||||
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
|
||||
]
|
||||
}
|
||||
|
||||
ONE_DAY = 12 # Количество часов в 1 рабочем дне
|
||||
|
@ -16,16 +16,19 @@ Including another URLconf
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth import views as auth_views
|
||||
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 work_page, work_hand_over, work_become_engineer, \
|
||||
AdminPageView, statistic_page
|
||||
from main.urls import router
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls, name='admin'),
|
||||
path('', main_page, name='index'),
|
||||
path('accounts/profile/', profile_page, name='profile'),
|
||||
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_registration.backends.one_step.urls')),
|
||||
path('work/<int:id>', work_page, name="work"),
|
||||
@ -33,7 +36,8 @@ urlpatterns = [
|
||||
path('work/become_engineer/', work_become_engineer, name="work_become_engineer"),
|
||||
path('accounts/', include('django_registration.backends.activation.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 += [
|
||||
@ -58,3 +62,8 @@ urlpatterns += [
|
||||
name='password_reset_complete'
|
||||
),
|
||||
]
|
||||
|
||||
# Django REST
|
||||
urlpatterns += [
|
||||
path('api/', include(router.urls))
|
||||
]
|
||||
|
57
data.json
Normal file
57
data.json
Normal file
@ -0,0 +1,57 @@
|
||||
[
|
||||
{
|
||||
"model": "auth.user",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"password": "pbkdf2_sha256$216000$gHBBCr1jBELf$ZkEDW3IEd8Wij7u8vkv+0Eze32CS01bcaYWhcD9OIC4=",
|
||||
"last_login": null,
|
||||
"is_superuser": true,
|
||||
"username": "admin@gmail.com",
|
||||
"first_name": "",
|
||||
"last_name": "",
|
||||
"email": "admin@gmail.com",
|
||||
"is_staff": true,
|
||||
"is_active": true,
|
||||
"date_joined": "2021-03-10T16:38:56.303Z",
|
||||
"groups": [],
|
||||
"user_permissions": [33]
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "main.userprofile",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"name": "ZendeskAdmin",
|
||||
"user": 1,
|
||||
"role": "admin"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "auth.user",
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"password": "pbkdf2_sha256$216000$5qLJgrm2Quq9$KDBNNymVZXkUx0HKBPFst2m83kLe0egPBnkW7KnkORU=",
|
||||
"last_login": null,
|
||||
"is_superuser": false,
|
||||
"username": "123@test.ru",
|
||||
"first_name": "",
|
||||
"last_name": "",
|
||||
"email": "123@test.ru",
|
||||
"is_staff": false,
|
||||
"is_active": true,
|
||||
"date_joined": "2021-03-10T16:38:56.303Z",
|
||||
"groups": [],
|
||||
"user_permissions": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "main.userprofile",
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"name": "UserForAccessTest",
|
||||
"user": 2,
|
||||
"role": "agent",
|
||||
"custom_role_id": "360005209000"
|
||||
}
|
||||
}
|
||||
]
|
@ -1,11 +1,15 @@
|
||||
import os
|
||||
from datetime import timedelta, datetime, date
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from zenpy import Zenpy
|
||||
from zenpy.lib.exception import APIException
|
||||
|
||||
from main.models import UserProfile
|
||||
from main.models import UserProfile, RoleChangeLogs
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
|
||||
from access_controller.settings import ZENDESK_ROLES as ROLES
|
||||
|
||||
from access_controller.settings import ZENDESK_ROLES as ROLES, ONE_DAY
|
||||
|
||||
|
||||
class ZendeskAdmin:
|
||||
@ -28,7 +32,7 @@ class ZendeskAdmin:
|
||||
email: str = os.getenv('ACCESS_CONTROLLER_API_EMAIL')
|
||||
token: str = os.getenv('ACCESS_CONTROLLER_API_TOKEN')
|
||||
password: str = os.getenv('ACCESS_CONTROLLER_API_PASSWORD')
|
||||
_instance=None
|
||||
_instance = None
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if cls._instance is None:
|
||||
@ -72,7 +76,7 @@ class ZendeskAdmin:
|
||||
user = self.admin.users.search(email).values[0]
|
||||
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
|
||||
|
||||
@ -86,7 +90,7 @@ class ZendeskAdmin:
|
||||
Функция **get_user_org** возвращает организацию, к которой относится пользователь по его email
|
||||
"""
|
||||
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:
|
||||
"""
|
||||
@ -100,7 +104,7 @@ class ZendeskAdmin:
|
||||
|
||||
if self.email is None:
|
||||
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:
|
||||
self.credentials['token'] = self.token
|
||||
@ -144,9 +148,11 @@ def get_users_list() -> list:
|
||||
Функция **get_users_list** возвращает список пользователей Zendesk, относящихся к организации.
|
||||
"""
|
||||
zendesk = ZendeskAdmin()
|
||||
admin = zendesk.get_user(zendesk.email)
|
||||
org = next(zendesk.admin.users.organizations(user=admin))
|
||||
return zendesk.admin.organizations.users(org)
|
||||
|
||||
# У пользователей должна быть организация SYSTEM
|
||||
org = next(zendesk.admin.search(type='organization', name='SYSTEM'))
|
||||
users = zendesk.admin.organizations.users(org)
|
||||
return users
|
||||
|
||||
|
||||
def update_profile(user_profile: UserProfile) -> UserProfile:
|
||||
@ -156,6 +162,7 @@ def update_profile(user_profile: UserProfile) -> UserProfile:
|
||||
user = ZendeskAdmin().get_user(user_profile.user.email)
|
||||
user_profile.name = user.name
|
||||
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.save()
|
||||
|
||||
@ -191,3 +198,249 @@ def check_user_auth(email: str, password: str) -> bool:
|
||||
except APIException:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def update_user_in_model(profile, zendesk_user):
|
||||
profile.name = zendesk_user.name
|
||||
profile.role = zendesk_user.role
|
||||
profile.image = zendesk_user.photo['content_url'] if zendesk_user.photo else None
|
||||
profile.custom_role_id = zendesk_user.custom_role_id
|
||||
profile.save()
|
||||
|
||||
|
||||
def count_users(users) -> tuple:
|
||||
"""
|
||||
Функция подсчета количества сотрудников с ролями engineer и light_a
|
||||
|
||||
.. todo::
|
||||
this func counts users from all zendesk instead of just from a model:
|
||||
"""
|
||||
engineers, light_agents = 0, 0
|
||||
for user in users:
|
||||
if user.custom_role_id == ROLES['engineer']:
|
||||
engineers += 1
|
||||
elif user.custom_role_id == ROLES['light_agent']:
|
||||
light_agents += 1
|
||||
return engineers, light_agents
|
||||
|
||||
|
||||
def update_users_in_model():
|
||||
"""
|
||||
Обновляет пользователей в модели UserProfile по списку пользователей в организации
|
||||
"""
|
||||
users = get_users_list()
|
||||
for user in users:
|
||||
try:
|
||||
profile = User.objects.get(email=user.email).userprofile
|
||||
update_user_in_model(profile, user)
|
||||
except ObjectDoesNotExist:
|
||||
pass
|
||||
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)
|
||||
|
@ -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
|
||||
|
||||
|
||||
@ -14,7 +15,6 @@ class CustomRegistrationForm(RegistrationFormUniqueEmail):
|
||||
:param visible_fields.email: Поле для ввода email, зарегистирированного на Zendesk
|
||||
:type visible_fields.email: :class:`django_registration.forms.RegistrationFormUniqueEmail`
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs) -> RegistrationFormUniqueEmail:
|
||||
super().__init__(*args, **kwargs)
|
||||
for visible in self.visible_fields():
|
||||
@ -23,7 +23,7 @@ class CustomRegistrationForm(RegistrationFormUniqueEmail):
|
||||
visible.field.widget.attrs['class'] += 'form-control'
|
||||
else:
|
||||
visible.field.widget.attrs['class'] = 'form-control'
|
||||
if visible.html_name !='email':
|
||||
if visible.html_name != 'email':
|
||||
visible.field.required = False
|
||||
|
||||
class Meta(RegistrationFormUniqueEmail.Meta):
|
||||
@ -42,7 +42,8 @@ class AdminPageUsers(forms.Form):
|
||||
queryset=UserProfile.objects.filter(role='agent'),
|
||||
widget=forms.CheckboxSelectMultiple(
|
||||
attrs={
|
||||
'class': 'form-check-input'
|
||||
'class': 'form-check-input',
|
||||
|
||||
}
|
||||
),
|
||||
label=''
|
||||
@ -65,3 +66,31 @@ class CustomAuthenticationForm(AuthenticationForm):
|
||||
,
|
||||
'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',
|
||||
}
|
||||
),
|
||||
)
|
||||
|
61
main/migrations/0009_models_help_text.py
Normal file
61
main/migrations/0009_models_help_text.py
Normal 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),
|
||||
),
|
||||
]
|
17
main/migrations/0010_userprofile_meta.py
Normal file
17
main/migrations/0010_userprofile_meta.py
Normal 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'),)},
|
||||
),
|
||||
]
|
28
main/migrations/0011_auto_20210311_1734.py
Normal file
28
main/migrations/0011_auto_20210311_1734.py
Normal 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='Код роли пользователя'),
|
||||
),
|
||||
]
|
23
main/migrations/0012_auto_20210312_1225.py
Normal file
23
main/migrations/0012_auto_20210312_1225.py
Normal 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),
|
||||
),
|
||||
]
|
@ -7,8 +7,14 @@ from django.dispatch import receiver
|
||||
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='Пользователь')
|
||||
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='Аватарка')
|
||||
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):
|
||||
"""Модель для логирования изменений ролей пользователя"""
|
||||
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='Имя пользователя')
|
||||
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='Дата и время изменения роли')
|
||||
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='Кем была изменена роль')
|
||||
|
17
main/serializers.py
Normal file
17
main/serializers.py
Normal file
@ -0,0 +1,17 @@
|
||||
from django.contrib.auth.models import User
|
||||
from rest_framework import serializers
|
||||
from main.models import UserProfile
|
||||
|
||||
|
||||
class UserSerializer(serializers.HyperlinkedModelSerializer):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['email']
|
||||
|
||||
|
||||
class ProfileSerializer(serializers.HyperlinkedModelSerializer):
|
||||
user = UserSerializer()
|
||||
|
||||
class Meta:
|
||||
model = UserProfile
|
||||
fields = ['user', 'id', 'role', 'name']
|
@ -27,6 +27,7 @@
|
||||
|
||||
</style>
|
||||
{% block extra_css %}{% endblock %}
|
||||
{% block extra_scripts %}{% endblock %}
|
||||
</head>
|
||||
|
||||
<body class="d-flex flex-column h-100">
|
||||
|
@ -3,16 +3,18 @@
|
||||
<meta charset="utf-8">
|
||||
<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' %}">
|
||||
<img src="{% static 'main/img/logo.png' %}" width="30" height="30" class="d-inline-block align-top" alt="" loading="lazy">
|
||||
Access Controller
|
||||
<img src="{% static 'main/img/logo_real.png' %}" width="107" height="22" class="d-inline-block align-top" alt="" loading="lazy">
|
||||
<t style="color:#FFFFFF">Access Controller</t>
|
||||
</a>
|
||||
{% if request.user.is_authenticated %}
|
||||
<div class="btn-group" role="group" aria-label="Basic example">
|
||||
<a class="btn btn-secondary" href="{% url 'profile' %}">Профиль</a>
|
||||
{% if perms.main.has_control_access %}
|
||||
<a class="btn btn-secondary" href="{% url 'control' %}">Управление</a>
|
||||
{% else %}
|
||||
<a class="btn btn-secondary" href="{% url 'work' request.user.id %}">Запрос прав</a>
|
||||
{% endif %}
|
||||
<a class="btn btn-secondary" href="{% url 'logout' %}">Выйти</a>
|
||||
</div>
|
||||
|
@ -10,5 +10,5 @@
|
||||
|
||||
{% block content %}
|
||||
<br>
|
||||
<h4> Регистрация прошла успешно. <a href="/login/">Войти сейчас</a></h4>
|
||||
<h4> Регистрация прошла успешно. <a href="{% url 'login'%}">Войти сейчас</a></h4>
|
||||
{% endblock %}
|
||||
|
@ -10,11 +10,15 @@
|
||||
<link rel="stylesheet" href="{% static 'main/css/work.css' %}"/>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_scripts %}
|
||||
<script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
|
||||
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
||||
{% endblock%}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-md">
|
||||
<div class="new-section">
|
||||
<p class="row page-description">Основная информация о странице</p>
|
||||
</div>
|
||||
|
||||
{% block form %}
|
||||
<form method="post">
|
||||
@ -37,25 +41,23 @@
|
||||
<table class="light-table">
|
||||
|
||||
<thead>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>Role</th>
|
||||
<th>Name(link to profile)</th>
|
||||
<th>Checked</th>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tbody id="old_tbody">
|
||||
{% for user in users %}
|
||||
<tr>
|
||||
<td>{{ user.id }}</td>
|
||||
<td><a href="#">{{ user.name }}</a></td>
|
||||
<td>{{ user.user.email }}</td>
|
||||
<td>{{ user.role }}</td>
|
||||
<td><a href="#">{{ user.name }}</a></td>
|
||||
<td class="checkbox_field"></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
<tbody id="new_tbody"></tbody>
|
||||
</table>
|
||||
{% endblock%}
|
||||
|
||||
@ -103,6 +105,6 @@
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
||||
<script src="{% static 'main/js/control.js'%}"></script>
|
||||
<script src="{% static 'main/js/control.js'%}" type="text/babel"></script>
|
||||
|
||||
{% endblock %}
|
||||
|
49
main/templates/pages/stat.html
Normal file
49
main/templates/pages/stat.html
Normal 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 %}
|
||||
|
@ -22,13 +22,13 @@
|
||||
<h6 class="table-title">Список сотрудников с правами инженера</h6>
|
||||
<table class="light-table">
|
||||
<thead>
|
||||
<th>ID</th>
|
||||
<th>Email</th>
|
||||
<th>Name</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for engineer in engineers %}
|
||||
<tr>
|
||||
<td>{{ engineer.id }}</td>
|
||||
<td>{{ engineer.email }}</td>
|
||||
<td>{{ engineer.name }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
@ -30,7 +30,7 @@
|
||||
{% endif %}
|
||||
<div class="text-center">
|
||||
<button type="submit" class="btn btn-primary">Войти</button>
|
||||
<a href="password_reset" class="btn btn-link" style="display: block;">Забыли пароль?</a>
|
||||
<a href="{% url 'password_reset' %}" class="btn btn-link" style="display: block;">Забыли пароль?</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
6
main/urls.py
Normal file
6
main/urls.py
Normal file
@ -0,0 +1,6 @@
|
||||
from rest_framework.routers import DefaultRouter
|
||||
from main.views import UsersViewSet
|
||||
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register(r'users', UsersViewSet)
|
133
main/views.py
133
main/views.py
@ -1,8 +1,10 @@
|
||||
import logging
|
||||
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.models import User, Permission
|
||||
from django.contrib.auth.tokens import default_token_generator
|
||||
from django.contrib.auth.views import LoginView
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.handlers.wsgi import WSGIRequest
|
||||
@ -10,29 +12,25 @@ from django.http import HttpResponseRedirect, HttpResponse
|
||||
from django.shortcuts import render, get_list_or_404, redirect
|
||||
from django.urls import reverse_lazy, reverse
|
||||
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
|
||||
|
||||
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.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
|
||||
from django.core.exceptions import PermissionDenied
|
||||
|
||||
from access_controller.settings import ZENDESK_ROLES
|
||||
from zenpy import Zenpy
|
||||
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
|
||||
|
||||
content_type_temp = ContentType.objects.get_for_model(UserProfile)
|
||||
permission_temp, created = Permission.objects.get_or_create(
|
||||
codename='has_control_access',
|
||||
content_type=content_type_temp,
|
||||
)
|
||||
from access_controller.settings import EMAIL_HOST_USER, ZENDESK_ROLES
|
||||
|
||||
# Django REST
|
||||
from rest_framework import viewsets, status
|
||||
from main.serializers import ProfileSerializer
|
||||
from rest_framework.response import Response
|
||||
|
||||
|
||||
class CustomRegistrationView(RegistrationView):
|
||||
@ -128,10 +126,20 @@ def auth_user(request):
|
||||
|
||||
@login_required()
|
||||
def work_page(request, id):
|
||||
users = get_users_list()
|
||||
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 = {
|
||||
'engineers': UserProfile.objects.filter(role="admin"),
|
||||
'agents': UserProfile.objects.filter(role="agent"),
|
||||
'engineers': engineers,
|
||||
'agents': light_agents,
|
||||
'pagename': 'Управление правами'
|
||||
}
|
||||
return render(request, 'pages/work.html', context)
|
||||
@ -141,8 +149,9 @@ def work_page(request, id):
|
||||
@login_required()
|
||||
def work_hand_over(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)
|
||||
request.user.userprofile.role = "agent"
|
||||
request.user.userprofile.save()
|
||||
@ -152,10 +161,10 @@ def work_hand_over(request):
|
||||
@login_required()
|
||||
def work_become_engineer(request):
|
||||
zenpy_user, admin = auth_user(request)
|
||||
if zenpy_user.role == "agent" or zenpy_user.role == "end-user":
|
||||
zenpy_user.role = "admin"
|
||||
if zenpy_user.custom_role_id == ZENDESK_ROLES['light_agent']:
|
||||
zenpy_user.custom_role_id = ZENDESK_ROLES['engineer']
|
||||
admin.users.update(zenpy_user)
|
||||
request.user.userprofile.role = "admin"
|
||||
request.user.userprofile.role = "agent"
|
||||
request.user.userprofile.save()
|
||||
return HttpResponseRedirect(reverse('work', args=(request.user.id,)))
|
||||
|
||||
@ -179,10 +188,11 @@ class AdminPageView(LoginRequiredMixin, PermissionRequiredMixin, FormView):
|
||||
"""
|
||||
Функция установки ролей пользователям
|
||||
"""
|
||||
users = form.cleaned_data['users']
|
||||
if 'engineer' in self.request.POST:
|
||||
self.make_engineers(form.cleaned_data['users'])
|
||||
self.make_engineers(users)
|
||||
elif 'light_agent' in self.request.POST:
|
||||
self.make_light_agents(form.cleaned_data['users'])
|
||||
self.make_light_agents(users)
|
||||
return super().form_valid(form)
|
||||
|
||||
@staticmethod
|
||||
@ -193,32 +203,15 @@ class AdminPageView(LoginRequiredMixin, PermissionRequiredMixin, FormView):
|
||||
def make_light_agents(users):
|
||||
[make_light_agent(user) for user in users]
|
||||
|
||||
@staticmethod
|
||||
def count_users(users) -> tuple:
|
||||
"""
|
||||
Функция подсчета количества сотрудников с ролями engineer и light_a
|
||||
|
||||
.. todo::
|
||||
this func counts users from all zendesk instead of just from a model:
|
||||
"""
|
||||
engineers, light_agents = 0, 0
|
||||
for user in users:
|
||||
if user.custom_role_id == ZENDESK_ROLES['engineer']:
|
||||
engineers += 1
|
||||
elif user.custom_role_id == ZENDESK_ROLES['light_agent']:
|
||||
light_agents += 1
|
||||
return engineers, light_agents
|
||||
|
||||
def get_context_data(self, **kwargs) -> dict:
|
||||
"""
|
||||
Функция формирования контента страницы администратора (с проверкой прав доступа)
|
||||
"""
|
||||
if self.request.user.userprofile.role != 'admin':
|
||||
raise PermissionDenied
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['users'] = get_list_or_404(
|
||||
users = get_list_or_404(
|
||||
UserProfile, role='agent')
|
||||
context['engineers'], context['light_agents'] = self.count_users(get_users_list())
|
||||
context['users'] = users
|
||||
context['engineers'], context['light_agents'] = count_users(get_users_list())
|
||||
return context # TODO: need to get profile page url
|
||||
|
||||
|
||||
@ -227,3 +220,51 @@ class CustomLoginView(LoginView):
|
||||
Отображение страницы авторизации пользователя
|
||||
"""
|
||||
form_class = CustomAuthenticationForm
|
||||
|
||||
|
||||
class UsersViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
"""
|
||||
Класс для получения пользователей с помощью api
|
||||
"""
|
||||
queryset = UserProfile.objects.filter(role='agent')
|
||||
serializer_class = ProfileSerializer
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
users = update_users_in_model().values
|
||||
count = count_users(users)
|
||||
profiles = UserProfile.objects.filter(role='agent')
|
||||
serializer = self.get_serializer(profiles, many=True)
|
||||
return Response({
|
||||
'users': serializer.data,
|
||||
'engineers': count[0],
|
||||
'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)
|
||||
|
@ -3,6 +3,8 @@ Django==3.1.6
|
||||
Pillow==8.1.0
|
||||
zenpy~=2.0.24
|
||||
django_registration==3.1.1
|
||||
djangorestframework==3.12.2
|
||||
|
||||
|
||||
# Documentation
|
||||
Sphinx==3.4.3
|
||||
|
BIN
static/main/img/logo_real.png
Normal file
BIN
static/main/img/logo_real.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
@ -1,9 +1,83 @@
|
||||
"use strict";
|
||||
let checkboxes = document.getElementsByName("users");
|
||||
let fields = document.querySelectorAll(".checkbox_field");
|
||||
if (checkboxes.length == fields.length) {
|
||||
for (let i = 0; i < fields.length; ++i) {
|
||||
let el = checkboxes[i].cloneNode(true);
|
||||
fields[i].appendChild(el);
|
||||
|
||||
function move_checkboxes() {
|
||||
let checkboxes = document.getElementsByName("users");
|
||||
let fields = document.querySelectorAll(".checkbox_field");
|
||||
if (checkboxes.length == fields.length) {
|
||||
for (let i = 0; i < fields.length; ++i) {
|
||||
let el = checkboxes[i].cloneNode(true);
|
||||
fields[i].appendChild(el);
|
||||
}
|
||||
} else {
|
||||
alert(
|
||||
"Количество пользователей агентов не соответствует количеству полей в форме AdminPageUsers"
|
||||
);
|
||||
}
|
||||
}
|
||||
move_checkboxes();
|
||||
|
||||
// React
|
||||
class TableRow extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<tr>
|
||||
<td>
|
||||
<a href="#">{this.props.user.name}</a>
|
||||
</td>
|
||||
<td>{this.props.user.user.email}</td>
|
||||
<td>{this.props.user.role}</td>
|
||||
<td>
|
||||
<input
|
||||
type="checkbox"
|
||||
value={this.props.user.id}
|
||||
className="form-check-input"
|
||||
name="users"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TableBody extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
users: [],
|
||||
engineers: 0,
|
||||
light_agents: 0,
|
||||
};
|
||||
}
|
||||
|
||||
get_users() {
|
||||
axios.get("/api/users").then((response) => {
|
||||
this.setState({
|
||||
users: response.data.users,
|
||||
engineers: response.data.engineers,
|
||||
light_agents: response.data.light_agents,
|
||||
});
|
||||
let elements = document.querySelectorAll(".info-quantity-value");
|
||||
elements[0].innerHTML = this.state.engineers;
|
||||
elements[1].innerHTML = this.state.light_agents;
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.interval = setInterval(() => {
|
||||
this.get_users();
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.state.users.map((user, key) => (
|
||||
<TableRow user={user} key={key} />
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(<TableBody />, document.getElementById("new_tbody"));
|
||||
setTimeout(() => document.getElementById("old_tbody").remove(), 10000);
|
||||
|
Loading…
x
Reference in New Issue
Block a user