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: Интервалы работы пользователя
|
||||
:type statistic: :class:`dict`
|
||||
"""
|
||||
|
||||
def __init__(self, start_date, end_date, user_email, stat=None):
|
||||
self.display = None
|
||||
self.interval = None
|
||||
@ -543,31 +544,51 @@ class StatisticData:
|
||||
first_log, last_log = self.data[0], self.data[len(self.data) - 1]
|
||||
|
||||
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 отдельная функция
|
||||
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():
|
||||
self.statistic[last_log.change_time.date()] += (
|
||||
get_timedelta(None, timezone.now().time()) - get_timedelta(last_log)
|
||||
).total_seconds()
|
||||
else:
|
||||
self.statistic[last_log.change_time.date()] += (
|
||||
timedelta(days=1) - get_timedelta(last_log)).total_seconds()
|
||||
if self.end_date == timezone.now().date():
|
||||
self.statistic[self.end_date] = get_timedelta(None, timezone.now().time()).total_seconds()
|
||||
if last_log.new_role == ROLES['engineer']:
|
||||
self.post_engineer_logic(last_log)
|
||||
|
||||
for log_index in range(len(self.data) - 1): # TODO отдельная функция
|
||||
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()
|
||||
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))
|
||||
if last_log.change_time.date() == timezone.now().date():
|
||||
self.statistic[last_log.change_time.date()] += (
|
||||
get_timedelta(None, timezone.now().time()) - get_timedelta(last_log)
|
||||
).total_seconds()
|
||||
else:
|
||||
self.statistic[last_log.change_time.date()] += (
|
||||
timedelta(days=1) - get_timedelta(last_log)).total_seconds()
|
||||
if self.end_date == timezone.now().date():
|
||||
self.statistic[self.end_date] = get_timedelta(None, timezone.now().time()).total_seconds()
|
||||
|
||||
def prev_engineer_logic(self, first_log):
|
||||
"""
|
||||
Функция обрабатывает случай, когда нам изветсно что инженер начал работу до диапазона
|
||||
"""
|
||||
self.fill_daterange(max(User.objects.get(email=self.email).date_joined.date(), self.start_date),
|
||||
first_log.change_time.date())
|
||||
self.statistic[first_log.change_time.date()] += get_timedelta(first_log).total_seconds()
|
||||
|
||||
def fill_daterange(self, first: date, last: date, val: int = 24 * 3600) -> dict:
|
||||
"""
|
||||
@ -576,7 +597,6 @@ class StatisticData:
|
||||
:param first: Начальная дата интервала
|
||||
:param last: Последняя дата интервала
|
||||
:param val: Количество секунд в одном дне
|
||||
:return: Статистику пользователя с указанным количеством секунд в заданных днях
|
||||
"""
|
||||
for day in daterange(first, last):
|
||||
self.statistic[day] = val
|
||||
@ -584,8 +604,6 @@ class StatisticData:
|
||||
def clear_statistic(self) -> dict:
|
||||
"""
|
||||
Функция осуществляет обновление всех дней.
|
||||
|
||||
:return: Статистику пользователя с количеством рабочих секунд = 0
|
||||
"""
|
||||
self.statistic.clear()
|
||||
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_registration.forms import RegistrationFormUniqueEmail
|
||||
|
||||
from access_controller.settings import ZENDESK_ROLES
|
||||
from main.models import UserProfile
|
||||
|
||||
|
||||
@ -14,6 +13,7 @@ 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():
|
||||
@ -70,6 +70,16 @@ class CustomAuthenticationForm(AuthenticationForm):
|
||||
}
|
||||
|
||||
|
||||
INTERVAL_CHOICES = [
|
||||
('days', 'Дни'),
|
||||
('months', 'Месяцы')
|
||||
]
|
||||
DISPLAY_CHOICES = [
|
||||
('hours', 'Часы'),
|
||||
('days', 'Дни/Смены')
|
||||
]
|
||||
|
||||
|
||||
class StatisticForm(forms.Form):
|
||||
"""
|
||||
Форма отображения интервалов работы пользователя.
|
||||
@ -87,26 +97,47 @@ 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(
|
||||
widget=forms.EmailInput(
|
||||
attrs={
|
||||
'type': 'date',
|
||||
'placeholder': 'example@ngenix.ru',
|
||||
'class': 'form-control',
|
||||
'style': 'background-color:#f2f2f2;'
|
||||
}
|
||||
),
|
||||
)
|
||||
range_end = forms.DateField( # TODO: Переделать под html страницу
|
||||
label='Конец диапазона',
|
||||
interval = forms.ChoiceField(
|
||||
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(
|
||||
attrs={
|
||||
'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
|
||||
:return: адресация на страницу статистики
|
||||
"""
|
||||
if not request.user.is_superuser:
|
||||
return redirect('index')
|
||||
if not request.user.has_perm('main.has_control_access'):
|
||||
return PermissionDenied
|
||||
context = {
|
||||
'pagename': 'страница статистики',
|
||||
'errors': list(),
|
||||
@ -348,4 +348,4 @@ def statistic_page(request: WSGIRequest) -> HttpResponse:
|
||||
if request.method == 'GET':
|
||||
form = StatisticForm()
|
||||
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