Update statistic backend
This commit is contained in:
parent
49046022d7
commit
b96deab2cb
@ -1,5 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
from datetime import timedelta
|
from datetime import timedelta, datetime
|
||||||
|
|
||||||
from zenpy import Zenpy
|
from zenpy import Zenpy
|
||||||
from zenpy.lib.exception import APIException
|
from zenpy.lib.exception import APIException
|
||||||
@ -194,7 +194,9 @@ def check_user_auth(email: str, password: str) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
def daterange(start_date, end_date) -> list:
|
def daterange(start_date, end_date) -> list:
|
||||||
# Возвращает список дней с start_date по end_date исключая правую границу
|
"""
|
||||||
|
Возвращает список дней с start_date по end_date исключая правую границу
|
||||||
|
"""
|
||||||
dates = []
|
dates = []
|
||||||
for n in range(int((end_date - start_date).days)):
|
for n in range(int((end_date - start_date).days)):
|
||||||
dates.append(start_date + timedelta(n))
|
dates.append(start_date + timedelta(n))
|
||||||
@ -202,8 +204,60 @@ def daterange(start_date, end_date) -> list:
|
|||||||
|
|
||||||
|
|
||||||
def get_timedelta(log) -> timedelta:
|
def get_timedelta(log) -> timedelta:
|
||||||
# Возвращает объект класса timedelta, который хранит промежуток времени от начала суток до момента,
|
"""
|
||||||
# который находится в log(RoleChangeLogs)
|
Возвращает объект класса timedelta, который хранит промежуток времени от начала суток до момента,
|
||||||
|
который находится в log (объект класса RoleChangeLogs)
|
||||||
|
"""
|
||||||
time = log.change_time.time()
|
time = log.change_time.time()
|
||||||
time = timedelta(hours=time.hour, minutes=time.minute, seconds=time.second)
|
time = timedelta(hours=time.hour, minutes=time.minute, seconds=time.second)
|
||||||
return time
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
def get_statistic_from_data(data, start_date, end_date):
|
||||||
|
"""
|
||||||
|
Функция возвращает словарь, в котором ключ - дата, значение - кол-во проработанных в этот день секунд
|
||||||
|
data - массив объектов RoleChangeLogs, является списком логов пользователя
|
||||||
|
"""
|
||||||
|
if not data:
|
||||||
|
return None
|
||||||
|
# Обнуление всех дней
|
||||||
|
stat = {}
|
||||||
|
for day in daterange(start_date, end_date + timedelta(days=1)):
|
||||||
|
stat[day] = 0
|
||||||
|
first_log, last_log = data[0], data[len(data) - 1]
|
||||||
|
|
||||||
|
# Если инженер работал ещё до начала диапазона
|
||||||
|
if int(first_log.old_role) == ROLES['engineer']:
|
||||||
|
for day in daterange(start_date, first_log.change_time.date()):
|
||||||
|
stat[day] = 24 * 3600
|
||||||
|
stat[first_log.change_time.date()] += get_timedelta(first_log).total_seconds()
|
||||||
|
|
||||||
|
# Если инженер закончил работать после диапазона
|
||||||
|
if int(last_log.new_role) == ROLES['engineer']:
|
||||||
|
for day in daterange(last_log.change_time.date() + timedelta(days=1), end_date + timedelta(days=1)):
|
||||||
|
stat[day] = 24 * 3600
|
||||||
|
stat[last_log.change_time.date()] += (timedelta(days=1)-get_timedelta(last_log)).total_seconds()
|
||||||
|
# Цикл по логам
|
||||||
|
for log_index in range(len(data) - 1):
|
||||||
|
if int(data[log_index].new_role) == ROLES['engineer']:
|
||||||
|
current_log, next_log = data[log_index], data[log_index + 1]
|
||||||
|
# Если сессия закончилась НЕ в тот же день, что и началась
|
||||||
|
if current_log.change_time.date() != next_log.change_time.date():
|
||||||
|
stat[current_log.change_time.date()] += (timedelta(days=1) - get_timedelta(current_log)).total_seconds()
|
||||||
|
stat[next_log.change_time.date()] += get_timedelta(next_log).total_seconds()
|
||||||
|
# Если проработал несколько дней подряд, то заполнить эти дни по 24 часа
|
||||||
|
for day in daterange(current_log.change_time.date() + timedelta(days=1),next_log.change_time.date()):
|
||||||
|
stat[day] = 24 * 3600
|
||||||
|
# Если сессия закончилась в тот же день, что и началась
|
||||||
|
else:
|
||||||
|
elapsed_time = next_log.change_time - current_log.change_time
|
||||||
|
stat[current_log.change_time.date()] += elapsed_time.total_seconds()
|
||||||
|
return stat
|
||||||
|
@ -71,7 +71,10 @@ class StatisticForm(forms.Form):
|
|||||||
label='Электроная почта',
|
label='Электроная почта',
|
||||||
)
|
)
|
||||||
interval = forms.CharField( # TODO: Переделать под html страницу
|
interval = forms.CharField( # TODO: Переделать под html страницу
|
||||||
label='Выбор интервала',
|
label='Интервал работы',
|
||||||
|
)
|
||||||
|
display_format = forms.CharField( # TODO: Переделать под html страницу
|
||||||
|
label='Формат отображения',
|
||||||
)
|
)
|
||||||
range_start = forms.DateField( # TODO: Переделать под html страницу
|
range_start = forms.DateField( # TODO: Переделать под html страницу
|
||||||
label='Начало диапазона',
|
label='Начало диапазона',
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
{% block title %}{{ pagename }}{% endblock %}
|
{% block title %}{{ pagename }}{% endblock %}
|
||||||
|
|
||||||
{% block heading %}Статистика{% endblock %}
|
{% block heading %} Пример страницы статистики(палками не бейти плиз){% endblock %}
|
||||||
|
|
||||||
{% block extra_css %}
|
{% block extra_css %}
|
||||||
<link rel="stylesheet" href="{% static 'main/css/work.css' %}">
|
<link rel="stylesheet" href="{% static 'main/css/work.css' %}">
|
||||||
@ -21,10 +21,17 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
<input type="submit">
|
<input type="submit">
|
||||||
</form>
|
</form>
|
||||||
{% for key,val in stats_logs.items %}
|
{% for key,val in log_stats.items %}
|
||||||
<h3>{{key}} {{val}}</h3>
|
<h3>{{key}} <b>|</b> {{val}}</h3>
|
||||||
<br>
|
<br>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
{% for error in errors %}
|
||||||
|
<li>{{error}}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
127
main/views.py
127
main/views.py
@ -1,6 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from datetime import timedelta
|
from datetime import timedelta, date, datetime
|
||||||
|
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.contrib.auth.forms import PasswordResetForm
|
from django.contrib.auth.forms import PasswordResetForm
|
||||||
@ -21,7 +21,7 @@ from zenpy.lib.api_objects import User as ZenpyUser
|
|||||||
|
|
||||||
from access_controller.settings import EMAIL_HOST_USER, ZENDESK_ROLES
|
from access_controller.settings import EMAIL_HOST_USER, ZENDESK_ROLES
|
||||||
from main.extra_func import check_user_exist, update_profile, get_user_organization, make_engineer, make_light_agent, \
|
from main.extra_func import check_user_exist, update_profile, get_user_organization, make_engineer, make_light_agent, \
|
||||||
get_users_list, daterange, get_timedelta
|
get_users_list, last_day_of_month, get_statistic_from_data
|
||||||
from main.forms import AdminPageUsers, CustomRegistrationForm, CustomAuthenticationForm, StatisticForm
|
from main.forms import AdminPageUsers, CustomRegistrationForm, CustomAuthenticationForm, StatisticForm
|
||||||
from .models import UserProfile, RoleChangeLogs
|
from .models import UserProfile, RoleChangeLogs
|
||||||
|
|
||||||
@ -230,94 +230,57 @@ class CustomLoginView(LoginView):
|
|||||||
def statistic_page(request):
|
def statistic_page(request):
|
||||||
if not request.user.is_superuser:
|
if not request.user.is_superuser:
|
||||||
return redirect('index')
|
return redirect('index')
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'pagename': 'страница статистики',
|
'pagename': 'страница статистики',
|
||||||
|
'errors': [],
|
||||||
}
|
}
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = StatisticForm(request.POST)
|
form = StatisticForm(request.POST)
|
||||||
|
if not form.is_valid():
|
||||||
if form.is_valid():
|
context['errors'] += form.errors
|
||||||
start_date = form.cleaned_data['range_start']
|
|
||||||
end_date = form.cleaned_data['range_end']
|
|
||||||
|
|
||||||
try:
|
|
||||||
data = RoleChangeLogs.objects.filter(
|
|
||||||
change_time__range=[start_date, end_date],
|
|
||||||
user=User.objects.get(email=form.cleaned_data['email']),
|
|
||||||
).order_by('change_time')
|
|
||||||
except User.DoesNotExist:
|
|
||||||
context['errors'] = 'Пользователь не найден'
|
|
||||||
context['form'] = StatisticForm()
|
|
||||||
return render(request, 'pages/stat.html', context)
|
|
||||||
|
|
||||||
interval = form.cleaned_data['interval'] # интервал TODO: переделать под html-страницу
|
|
||||||
show = form.cleaned_data['display_format'] # формат отображения TODO: переделать под html-страницу
|
|
||||||
stat = {}
|
|
||||||
|
|
||||||
# Этот кусок кода будет заполнять массив stat, в котором ключ - дата, значение - кол-во секунд
|
|
||||||
# Далее будет заполнение контекса в зависимости от выбранного интервала(дни/месяцы) и формата отобржаения(дни/часы)
|
|
||||||
# (пока есть только отображение в часах, интервал в днях)
|
|
||||||
|
|
||||||
# Обнуление всех дней
|
|
||||||
for day in daterange(start_date, end_date + timedelta(days=1)):
|
|
||||||
stat[day] = 0
|
|
||||||
|
|
||||||
first_log = data[0]
|
|
||||||
last_log = data[len(data) - 1]
|
|
||||||
# Проеврка крайних случаев
|
|
||||||
# Если окажется, что инженер работал ещё до начала диапазона(мы видим, что он был инженером до диапазона)
|
|
||||||
if first_log.old_role == 'engineer':
|
|
||||||
for day in daterange(start_date, first_log.change_time.date()):
|
|
||||||
stat[day] = 24 * 3600
|
|
||||||
stat[first_log.change_time.date()] += get_timedelta(first_log.change_time.time()).total_seconds()
|
|
||||||
|
|
||||||
# Если окажется, что инженер закончил работать после диапазона(мы видим, что он был инженером и после диапазона)
|
|
||||||
if last_log.new_role == 'engineer':
|
|
||||||
for day in daterange(last_log.change_time.date() + timedelta(days=1),
|
|
||||||
end_date + timedelta(days=1)):
|
|
||||||
stat[day] = 24 * 3600
|
|
||||||
stat[last_log.change_time.date()] += (timedelta(days=1) - get_timedelta(last_log)).total_seconds()
|
|
||||||
|
|
||||||
# Цикл по логам
|
|
||||||
for log_index in range(len(data) - 1):
|
|
||||||
if data[log_index].new_role == 'engineer':
|
|
||||||
current_log, next_log = data[log_index], data[log_index + 1]
|
|
||||||
|
|
||||||
# Если сессия закончилась НЕ в тот же день, что и началась
|
|
||||||
if current_log.change_time.date() != next_log.change_time.date():
|
|
||||||
stat[current_log.change_time.date()] += (
|
|
||||||
timedelta(days=1) - get_timedelta(current_log)).total_seconds()
|
|
||||||
stat[next_log.change_time.date()] += get_timedelta(next_log).total_seconds()
|
|
||||||
|
|
||||||
# Если проработал несколько дней подряд, то заполнить эти дни по 24 часа
|
|
||||||
for day in daterange(current_log.change_time.date() + timedelta(days=1),
|
|
||||||
next_log.change_time.date()):
|
|
||||||
stat[day] = 24 * 3600
|
|
||||||
|
|
||||||
# Если сессия закончилась в тот же день, что и началась
|
|
||||||
else:
|
|
||||||
elapsed_time = next_log.change_time - current_log.change_time
|
|
||||||
stat[current_log.change_time.date()] += elapsed_time.total_seconds()
|
|
||||||
|
|
||||||
# Переделываем наши значения под формат отображения
|
|
||||||
for key, item in stat.items():
|
|
||||||
if show == 'hours':
|
|
||||||
stat[key] = item / 3600
|
|
||||||
elif show == 'days':
|
|
||||||
stat[key] = item / 86400
|
|
||||||
|
|
||||||
# Переделываем наши значения под формат отображения
|
|
||||||
if interval == 'days':
|
|
||||||
context['stats_logs'] = stat
|
|
||||||
elif interval == 'months':
|
|
||||||
pass
|
|
||||||
else:
|
else:
|
||||||
context['errors'] = form.errors
|
start_date, end_date, data = form.cleaned_data['range_start'], form.cleaned_data['range_end'], list()
|
||||||
|
if end_date < start_date or end_date > datetime.now().date():
|
||||||
|
context['errors'] += ['Конец диапазона должен быть больше начала диапазона и меньше текущего времени']
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
data = RoleChangeLogs.objects.filter(
|
||||||
|
change_time__range=[start_date, end_date + timedelta(days=1)],
|
||||||
|
user=User.objects.get(email=form.cleaned_data['email']),
|
||||||
|
).order_by('change_time')
|
||||||
|
except User.DoesNotExist:
|
||||||
|
context['errors'] = ['Пользователь не найден']
|
||||||
|
stats = get_statistic_from_data(data, start_date, end_date)
|
||||||
|
if stats is None:
|
||||||
|
context['errors'] += ['Не найдено изменений роли в указаном диапазоне']
|
||||||
|
interval = form.cleaned_data['interval'] # интервал работы TODO: переделать под html-страницу
|
||||||
|
show = form.cleaned_data['display_format'] # формат отображения
|
||||||
|
|
||||||
|
if not (show in ['hours', 'days']): # Работа с форматом отображения
|
||||||
|
context['errors'] += ['Формат отображения должен быть в часах или днях']
|
||||||
|
elif stats:
|
||||||
|
for key, item in stats.items():
|
||||||
|
if show == 'hours':
|
||||||
|
stats[key] = item / 3600
|
||||||
|
elif show == 'days':
|
||||||
|
stats[key] = item / 86400
|
||||||
|
|
||||||
|
if not (interval in ['days', 'months']): # Работа с интервалом работы
|
||||||
|
context['errors'] += ['Интервал работы должен быть в днях или месяцах']
|
||||||
|
elif interval == 'months' and stats: # Переделываем ключи под формат в прототипе(начало_месяца-конец_месяца)
|
||||||
|
new_stats = {}
|
||||||
|
for key, value in stats.items():
|
||||||
|
current_month_start, current_month_end = max(start_date,
|
||||||
|
date(year=key.year, month=key.month, day=1)), min(
|
||||||
|
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_stats.get(index):
|
||||||
|
new_stats[index] += value
|
||||||
|
else:
|
||||||
|
new_stats[index] = value
|
||||||
|
stats = new_stats
|
||||||
|
context['log_stats'] = stats if not context['errors'] else None
|
||||||
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/stat.html', context)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user