Merge branch 'feature/statistic/main' into 'develop'
Feature/statistic/main See merge request 2020-2021/online/s101/group-02/access_controller!42
This commit is contained in:
commit
5001d1c71d
BIN
layouts/statistic/statistic.png
Normal file
BIN
layouts/statistic/statistic.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 65 KiB |
@ -381,6 +381,7 @@ class StatisticData:
|
|||||||
:param statistic: Интервалы работы пользователя
|
:param statistic: Интервалы работы пользователя
|
||||||
:type statistic: :class:`dict`
|
:type statistic: :class:`dict`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, start_date, end_date, user_email, stat=None):
|
def __init__(self, start_date, end_date, user_email, stat=None):
|
||||||
self.display = None
|
self.display = None
|
||||||
self.interval = None
|
self.interval = None
|
||||||
@ -543,9 +544,33 @@ class StatisticData:
|
|||||||
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 first_log.old_role == ROLES['engineer']:
|
||||||
self.statistic[first_log.change_time.date()] += get_timedelta(first_log).total_seconds()
|
self.prev_engineer_logic(first_log)
|
||||||
|
|
||||||
if last_log.new_role == ROLES['engineer']: # TODO отдельная функция
|
if last_log.new_role == ROLES['engineer']:
|
||||||
|
self.post_engineer_logic(last_log)
|
||||||
|
|
||||||
|
for log_index in range(len(self.data) - 1):
|
||||||
|
if self.data[log_index].new_role == ROLES['engineer']:
|
||||||
|
self.engineer_logic(log_index)
|
||||||
|
|
||||||
|
def engineer_logic(self, log_index):
|
||||||
|
"""
|
||||||
|
Функция обрабатывает основную часть работы инженера
|
||||||
|
"""
|
||||||
|
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 post_engineer_logic(self, last_log):
|
||||||
|
"""
|
||||||
|
Функция обрабатывает случай, когда нам изветсно что инженер работал и после диапазона
|
||||||
|
"""
|
||||||
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))
|
||||||
if last_log.change_time.date() == timezone.now().date():
|
if last_log.change_time.date() == timezone.now().date():
|
||||||
self.statistic[last_log.change_time.date()] += (
|
self.statistic[last_log.change_time.date()] += (
|
||||||
@ -557,17 +582,13 @@ class StatisticData:
|
|||||||
if self.end_date == timezone.now().date():
|
if self.end_date == timezone.now().date():
|
||||||
self.statistic[self.end_date] = get_timedelta(None, timezone.now().time()).total_seconds()
|
self.statistic[self.end_date] = get_timedelta(None, timezone.now().time()).total_seconds()
|
||||||
|
|
||||||
for log_index in range(len(self.data) - 1): # TODO отдельная функция
|
def prev_engineer_logic(self, first_log):
|
||||||
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()] += (
|
self.fill_daterange(max(User.objects.get(email=self.email).date_joined.date(), self.start_date),
|
||||||
timedelta(days=1) - get_timedelta(current_log)).total_seconds()
|
first_log.change_time.date())
|
||||||
self.statistic[next_log.change_time.date()] += get_timedelta(next_log).total_seconds()
|
self.statistic[first_log.change_time.date()] += get_timedelta(first_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: date, last: date, val: int = 24 * 3600) -> dict:
|
def fill_daterange(self, first: date, last: date, val: int = 24 * 3600) -> dict:
|
||||||
"""
|
"""
|
||||||
@ -576,7 +597,6 @@ class StatisticData:
|
|||||||
:param first: Начальная дата интервала
|
:param first: Начальная дата интервала
|
||||||
:param last: Последняя дата интервала
|
:param last: Последняя дата интервала
|
||||||
:param val: Количество секунд в одном дне
|
:param val: Количество секунд в одном дне
|
||||||
:return: Статистику пользователя с указанным количеством секунд в заданных днях
|
|
||||||
"""
|
"""
|
||||||
for day in daterange(first, last):
|
for day in daterange(first, last):
|
||||||
self.statistic[day] = val
|
self.statistic[day] = val
|
||||||
@ -584,8 +604,6 @@ class StatisticData:
|
|||||||
def clear_statistic(self) -> dict:
|
def clear_statistic(self) -> dict:
|
||||||
"""
|
"""
|
||||||
Функция осуществляет обновление всех дней.
|
Функция осуществляет обновление всех дней.
|
||||||
|
|
||||||
:return: Статистику пользователя с количеством рабочих секунд = 0
|
|
||||||
"""
|
"""
|
||||||
self.statistic.clear()
|
self.statistic.clear()
|
||||||
self.fill_daterange(self.start_date, self.end_date + timedelta(days=1), 0)
|
self.fill_daterange(self.start_date, self.end_date + timedelta(days=1), 0)
|
||||||
|
@ -2,7 +2,6 @@ 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,6 +13,7 @@ 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():
|
||||||
@ -70,6 +70,16 @@ class CustomAuthenticationForm(AuthenticationForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
INTERVAL_CHOICES = [
|
||||||
|
('days', 'Дни'),
|
||||||
|
('months', 'Месяцы')
|
||||||
|
]
|
||||||
|
DISPLAY_CHOICES = [
|
||||||
|
('hours', 'Часы'),
|
||||||
|
('days', 'Дни/Смены')
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class StatisticForm(forms.Form):
|
class StatisticForm(forms.Form):
|
||||||
"""
|
"""
|
||||||
Форма отображения интервалов работы пользователя.
|
Форма отображения интервалов работы пользователя.
|
||||||
@ -87,26 +97,47 @@ class StatisticForm(forms.Form):
|
|||||||
"""
|
"""
|
||||||
email = forms.EmailField(
|
email = forms.EmailField(
|
||||||
label='Электроная почта',
|
label='Электроная почта',
|
||||||
)
|
widget=forms.EmailInput(
|
||||||
interval = forms.CharField( # TODO: Переделать под html страницу
|
|
||||||
label='Интервал работы',
|
|
||||||
)
|
|
||||||
display_format = forms.CharField( # TODO: Переделать под html страницу
|
|
||||||
label='Формат отображения',
|
|
||||||
)
|
|
||||||
range_start = forms.DateField( # TODO: Переделать под html страницу
|
|
||||||
label='Начало диапазона',
|
|
||||||
widget=forms.DateInput(
|
|
||||||
attrs={
|
attrs={
|
||||||
'type': 'date',
|
'placeholder': 'example@ngenix.ru',
|
||||||
|
'class': 'form-control',
|
||||||
|
'style': 'background-color:#f2f2f2;'
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
range_end = forms.DateField( # TODO: Переделать под html страницу
|
interval = forms.ChoiceField(
|
||||||
label='Конец диапазона',
|
label='Выберите интервалы времени работы',
|
||||||
|
choices=INTERVAL_CHOICES,
|
||||||
|
widget=forms.RadioSelect(
|
||||||
|
attrs={
|
||||||
|
'class': 'btn-check',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
display_format = forms.ChoiceField(
|
||||||
|
label='Выберите формат отображения',
|
||||||
|
choices=DISPLAY_CHOICES,
|
||||||
|
widget=forms.RadioSelect(
|
||||||
|
attrs={
|
||||||
|
'class': 'btn-check',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
range_start = forms.DateField(
|
||||||
|
label='Начало статистики',
|
||||||
widget=forms.DateInput(
|
widget=forms.DateInput(
|
||||||
attrs={
|
attrs={
|
||||||
'type': 'date',
|
'type': 'date',
|
||||||
|
'class': 'btn btn-secondary text-primary bg-white',
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
range_end = forms.DateField(
|
||||||
|
label='Конец статистики',
|
||||||
|
widget=forms.DateInput(
|
||||||
|
attrs={
|
||||||
|
'type': 'date',
|
||||||
|
'class': 'btn btn-secondary text-primary bg-white',
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
{% 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 %}
|
|
||||||
|
|
106
main/templates/pages/statistic.html
Normal file
106
main/templates/pages/statistic.html
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
{% extends 'base/base.html' %}
|
||||||
|
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block title %}{{ pagename }}{% endblock %}
|
||||||
|
|
||||||
|
{% block heading %} Страницы просмотра статистики{% endblock %}
|
||||||
|
|
||||||
|
{% block content%}
|
||||||
|
<div class="mt-5">
|
||||||
|
<div class="container-fluid" style="font-size:2rem">
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-auto">
|
||||||
|
{{ form.email.label }}
|
||||||
|
</div>
|
||||||
|
<div class="col-auto mt-4">
|
||||||
|
{{ form.email }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row g-3 mt-4">
|
||||||
|
<div class="col-auto">
|
||||||
|
{{ form.interval.label }}
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
{% for radio in form.interval%}
|
||||||
|
{{ radio.tag }}
|
||||||
|
<label class="btn btn-secondary text-primary bg-white" for="{{ radio.id_for_label }}">
|
||||||
|
{{ radio.choice_label }}
|
||||||
|
</label>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row g-3 mt-4">
|
||||||
|
<div class="col-auto">
|
||||||
|
{{ form.display_format.label }}
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
{% for radio in form.display_format%}
|
||||||
|
{{ radio.tag }}
|
||||||
|
<label class="btn btn-secondary text-primary bg-white" for="{{ radio.id_for_label }}">
|
||||||
|
{{ radio.choice_label }}
|
||||||
|
</label>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row g-3 mt-4">
|
||||||
|
<div class="col-auto">
|
||||||
|
{{ form.range_start.label}}
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class='col-sm-7'>
|
||||||
|
{{ form.range_start}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row g-3 mt-4">
|
||||||
|
<div class="col-auto">
|
||||||
|
{{ form.range_end.label}}
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class='col-sm-7'>
|
||||||
|
{{ form.range_end}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row text-center">
|
||||||
|
<div class="col-12">
|
||||||
|
<button type="submit" class="btn btn-primary bg-white text-primary">Посмотреть статистику</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
{% for error in errors %}
|
||||||
|
<li><span class="badge bg-danger">{{error}}</span></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
<ul>
|
||||||
|
{% for warning in warnings %}
|
||||||
|
<li><span class="badge bg-warning">{{warning}}</span></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
<div class="container-fluid">
|
||||||
|
<table class="table table-bordered text-center text-secondary mt-5" style="background-color:#f2f2f2;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td scope="col">Пользователи/Даты</td>
|
||||||
|
{% for date in log_stats.keys %}
|
||||||
|
<td scope="col">{{date}}</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>{{ form.email.value }}</td>
|
||||||
|
{% for time in log_stats.values %}
|
||||||
|
<td>{{time}}</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -325,8 +325,8 @@ def statistic_page(request: WSGIRequest) -> HttpResponse:
|
|||||||
:param request: данные о пользователе: email, время и интервал работы. Данные получаем через forms.StatisticForm
|
:param request: данные о пользователе: email, время и интервал работы. Данные получаем через forms.StatisticForm
|
||||||
:return: адресация на страницу статистики
|
:return: адресация на страницу статистики
|
||||||
"""
|
"""
|
||||||
if not request.user.is_superuser:
|
if not request.user.has_perm('main.has_control_access'):
|
||||||
return redirect('index')
|
return PermissionDenied
|
||||||
context = {
|
context = {
|
||||||
'pagename': 'страница статистики',
|
'pagename': 'страница статистики',
|
||||||
'errors': list(),
|
'errors': list(),
|
||||||
@ -348,4 +348,4 @@ def statistic_page(request: WSGIRequest) -> HttpResponse:
|
|||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
form = StatisticForm()
|
form = StatisticForm()
|
||||||
context['form'] = form
|
context['form'] = form
|
||||||
return render(request, 'pages/stat.html', context)
|
return render(request, 'pages/statistic.html', context)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user