abra at Thu Dec 27 13:44:21 +04 2018
This commit is contained in:
1
Pipfile
1
Pipfile
@@ -9,6 +9,7 @@ sqlalchemy = "*"
|
|||||||
numpy = "*"
|
numpy = "*"
|
||||||
matplotlib = "*"
|
matplotlib = "*"
|
||||||
scipy = "*"
|
scipy = "*"
|
||||||
|
django-bootstrap-datepicker-plus = "*"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
|
|
||||||
|
10
Pipfile.lock
generated
10
Pipfile.lock
generated
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "6116399a31049347a1b4684b7d45038cd1bbc4c40c7f945196fd85072e90c483"
|
"sha256": "c6a6b8d3ebd478446b8e8800b29555ed874b559625670a9961406e358b897e06"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
@@ -31,6 +31,14 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==2.1.4"
|
"version": "==2.1.4"
|
||||||
},
|
},
|
||||||
|
"django-bootstrap-datepicker-plus": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:490058eba99d47f48a7d24fa78581c0e36375bdc7aa9605783eeb170d51fd0df",
|
||||||
|
"sha256:a8bc19cc6846f97ff1e6c447f4e0387881d16e8afa1e8bd7a652c19e545c566b"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==3.0.5"
|
||||||
|
},
|
||||||
"kiwisolver": {
|
"kiwisolver": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:0ee4ed8b3ae8f5f712b0aa9ebd2858b5b232f1b9a96b0943dceb34df2a223bc3",
|
"sha256:0ee4ed8b3ae8f5f712b0aa9ebd2858b5b232f1b9a96b0943dceb34df2a223bc3",
|
||||||
|
@@ -1,10 +1,13 @@
|
|||||||
{% extends 'web/base.html' %}
|
{% extends 'web/base.html' %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
|
{{ form.media }}
|
||||||
|
|
||||||
<form method="post">{% csrf_token %}
|
<form method="post">{% csrf_token %}
|
||||||
{{ form.as_p }}
|
{{ form.as_p }}
|
||||||
<input class="btn btn-primary" type="submit" value="Сохранить">
|
{# {% bootstrap_form form %}#}
|
||||||
</form>
|
|
||||||
|
<input class="btn btn-primary" type="submit" value="Сохранить">
|
||||||
|
</form>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@@ -81,10 +81,10 @@
|
|||||||
|
|
||||||
<h2>Активности</h2>
|
<h2>Активности</h2>
|
||||||
<ul>
|
<ul>
|
||||||
{% for activity in activities %}
|
{% for activities_and_pct in activities_and_pcts %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'user_charts_activity' user.id activity.id %}">
|
<a href="{% url 'user_charts_activity' user.id activities_and_pct.0.id %}">
|
||||||
{{ activity }}
|
{{ activities_and_pct.0 }} — {{ activities_and_pct.1|stringformat:".2f%%" }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@@ -29,6 +29,7 @@ ALLOWED_HOSTS = ['*']
|
|||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
|
'bootstrap_datepicker_plus',
|
||||||
'web.apps.WebConfig',
|
'web.apps.WebConfig',
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
|
@@ -28,7 +28,7 @@ class UserChartsPie(DetailView):
|
|||||||
|
|
||||||
sectors = sorted(list(total_time.items()), key=lambda x: x[1])
|
sectors = sorted(list(total_time.items()), key=lambda x: x[1])
|
||||||
|
|
||||||
ax.pie(x=[i[1] for i in sectors], labels=[i[0] for i in sectors])
|
ax.pie(x=[i[1] for i in sectors], labels=[i[0] for i in sectors], autopct='%1.0f%%')
|
||||||
|
|
||||||
buf = io.BytesIO()
|
buf = io.BytesIO()
|
||||||
fig.savefig(buf, format='png')
|
fig.savefig(buf, format='png')
|
||||||
@@ -59,6 +59,7 @@ class UserChartsActivityAll(DetailView):
|
|||||||
day_hours = day_seconds / timedelta(hours=1).total_seconds()
|
day_hours = day_seconds / timedelta(hours=1).total_seconds()
|
||||||
|
|
||||||
ax.bar(days, day_hours)
|
ax.bar(days, day_hours)
|
||||||
|
ax.hlines([np.mean(day_hours)], day_l, day_r, color='orange', lw=3)
|
||||||
plt.xticks(days, [f'{i:%m-%d}' for i in days], rotation=60)
|
plt.xticks(days, [f'{i:%m-%d}' for i in days], rotation=60)
|
||||||
plt.ylabel('кол-во часов')
|
plt.ylabel('кол-во часов')
|
||||||
|
|
||||||
|
14
web/forms.py
Normal file
14
web/forms.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import bootstrap_datepicker_plus
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
from web.models import ActivityLog
|
||||||
|
|
||||||
|
|
||||||
|
class ActivityLogCreateForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = ActivityLog
|
||||||
|
fields = ['user', 'activity', 'start_time', 'end_time']
|
||||||
|
widgets = {
|
||||||
|
'start_time': bootstrap_datepicker_plus.DateTimePickerInput(format='%Y-%m-%d %H:%M'),
|
||||||
|
'end_time': bootstrap_datepicker_plus.DateTimePickerInput(format='%Y-%m-%d %H:%M'),
|
||||||
|
}
|
@@ -26,14 +26,17 @@ class ActivityLog(models.Model):
|
|||||||
logged_at = models.DateTimeField(auto_now_add=True)
|
logged_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
if self.start_time is None or self.end_time is None:
|
||||||
|
raise ValidationError(f'Не указано время: {self}')
|
||||||
|
|
||||||
if self.start_time >= self.end_time:
|
if self.start_time >= self.end_time:
|
||||||
raise ValidationError(f'Illegal times: {self}')
|
raise ValidationError(f'Некорректное время: {self}')
|
||||||
|
|
||||||
for other_activity in ActivityLog.objects.filter(user=self.user).exclude(id=self.id):
|
for other_activity in ActivityLog.objects.filter(user=self.user).exclude(id=self.id):
|
||||||
l = max(self.start_time, other_activity.start_time)
|
l = max(self.start_time, other_activity.start_time)
|
||||||
r = min(self.end_time, other_activity.end_time)
|
r = min(self.end_time, other_activity.end_time)
|
||||||
if r > l:
|
if r > l:
|
||||||
raise ValidationError(f'Intersects with another activity: \n{self}\n{other_activity}\n{l}, {r}')
|
raise ValidationError(f'Пересекается с другой активностью: \n{self}\n{other_activity}\n{l}, {r}')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'ActivityLog[{self.user}, {self.activity}] {self.start_time}-{self.end_time} ({self.end_time - self.start_time})'
|
return f'ActivityLog[{self.user}, {self.activity}] {self.start_time}-{self.end_time} ({self.end_time - self.start_time})'
|
||||||
|
20
web/views.py
20
web/views.py
@@ -1,6 +1,8 @@
|
|||||||
|
import numpy as np
|
||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
from django.views.generic import CreateView, DeleteView, DetailView, ListView, UpdateView
|
from django.views.generic import CreateView, DeleteView, DetailView, ListView, UpdateView
|
||||||
|
|
||||||
|
from web.forms import ActivityLogCreateForm
|
||||||
from web.models import ActivityLog, User, Activity
|
from web.models import ActivityLog, User, Activity
|
||||||
|
|
||||||
|
|
||||||
@@ -23,12 +25,26 @@ class UserDetailView(DetailView):
|
|||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['activity_logs'] = ActivityLog.objects.filter(user=self.object).order_by('start_time')
|
context['activity_logs'] = ActivityLog.objects.filter(user=self.object).order_by('start_time')
|
||||||
context['activities'] = set(i.activity for i in ActivityLog.objects.filter(user=self.object))
|
context['activities'] = set(i.activity for i in ActivityLog.objects.filter(user=self.object))
|
||||||
|
|
||||||
|
total_times = {i: 0 for i in context['activities']}
|
||||||
|
total = 0
|
||||||
|
for i in context['activity_logs']:
|
||||||
|
total_times[i.activity] += (i.end_time - i.start_time).total_seconds()
|
||||||
|
total += (i.end_time - i.start_time).total_seconds()
|
||||||
|
|
||||||
|
keys = list(total_times.keys())
|
||||||
|
context['activities_and_pcts'] = [
|
||||||
|
(keys[i], total_times[keys[i]] / total * 100)
|
||||||
|
for i in reversed(np.argsort(list(total_times.values())))
|
||||||
|
]
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class ActivityLogCreateView(CreateView):
|
class ActivityLogCreateView(CreateView):
|
||||||
model = ActivityLog
|
model = ActivityLog
|
||||||
fields = ['activity', 'user', 'start_time', 'end_time']
|
# fields = ['activity', 'user', 'start_time', 'end_time']
|
||||||
|
form_class = ActivityLogCreateForm
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse('user', kwargs={
|
return reverse('user', kwargs={
|
||||||
@@ -66,5 +82,3 @@ class UserChartsActivity(DetailView):
|
|||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['activity'] = Activity.objects.filter(id=self.kwargs['activity_id']).first()
|
context['activity'] = Activity.objects.filter(id=self.kwargs['activity_id']).first()
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user