abra at Thu Dec 27 13:44:21 +04 2018

This commit is contained in:
2018-12-27 13:44:21 +04:00
parent da6260be34
commit 8109fc7619
9 changed files with 59 additions and 14 deletions

View File

@@ -9,6 +9,7 @@ sqlalchemy = "*"
numpy = "*" numpy = "*"
matplotlib = "*" matplotlib = "*"
scipy = "*" scipy = "*"
django-bootstrap-datepicker-plus = "*"
[dev-packages] [dev-packages]

10
Pipfile.lock generated
View File

@@ -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",

View File

@@ -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 }}
{# {% bootstrap_form form %}#}
<input class="btn btn-primary" type="submit" value="Сохранить"> <input class="btn btn-primary" type="submit" value="Сохранить">
</form> </form>
{% endblock %} {% endblock %}

View File

@@ -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 %}

View File

@@ -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',

View File

@@ -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
View 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'),
}

View File

@@ -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})'

View File

@@ -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