Permalink
Browse files

add merging and bugfixes

add merging of sessions if the connection got lost more then timeout minutes.
add deleting links into admin
some bugfixes
  • Loading branch information...
1 parent d191ec2 commit d4f2be6cd4cc1ef3d0966c73ab78cc0185928efd @poelzi committed Jul 12, 2010
View
7 db/admin.py
@@ -0,0 +1,7 @@
+from django.contrib import admin
+from uberclock.db.models import *
+
+
+admin.site.register(Detector)
+admin.site.register(WakeupTime)
+admin.site.register(Session)
View
29 db/models.py
@@ -1,7 +1,9 @@
from django.db import models
from uberclock.tools import ez_chronos
from django.contrib.auth.models import User
+from django.contrib import admin
from django.conf import settings
+from django.utils.dateformat import time_format
import struct
import logging
import datetime
@@ -105,6 +107,30 @@ def week(self):
def __repr__(self):
return "<Session %s %s-%s>" %(self.user, self.start, self.stop)
+ def __str__(self):
+ return "Session %s %s-%s" %(self.user, self.start, self.stop)
+
+ def merge(self, source):
+ source.entry_set.all().update(session=self)
+ source.learndata_set.all().delete()
+
+ def cifn(key):
+ var = getattr(source, key)
+ if var and not getattr(self, key):
+ setattr(self, key, var)
+ # copy usefull variables
+ for key in ["user", "wakeup", "rating"]:
+ cifn(key)
+
+ if self.entry_set.all().count():
+ self.start = self.entry_set.all().order_by('date')[0].date
+ self.stop = self.entry_set.all().order_by('-date')[0].date
+
+ self.save()
+ source.delete()
+
+
+
@property
def length(self):
s = (self.stop - self.start).seconds
@@ -122,6 +148,9 @@ class Entry(models.Model):
def __repr__(self):
return "<Entry %s %d>" %(self.date, self.value)
+ def __unicode__(self):
+ return u"Entry at %s: %s" %(self.date, time_format(self.date, settings.TIME_FORMAT))
+
class LearnData(models.Model):
"""
View
3 settings.py
@@ -82,6 +82,7 @@
'piston.middleware.ConditionalMiddlewareCompatProxy',
'piston.middleware.CommonMiddlewareCompatProxy',
'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
@@ -102,7 +103,7 @@
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.markup',
-# 'django.contrib.messages',
+ 'django.contrib.messages',
# Uncomment the next line to enable the admin:
'django.contrib.admin',
'uberclock.db',
View
34 templates/base.html
@@ -15,7 +15,7 @@
text-align: center;
list-style-type: none;
}
-div.top {
+div.menu {
display: block;
}
body {
@@ -35,6 +35,14 @@
color: white;
}
+#moreactions_box {
+ border: 1px solid lightBlue;
+}
+
+li {
+ text-align: left;
+
+}
</style>
@@ -46,15 +54,23 @@
{% block header_extra %}{% endblock %}
</head><body>
-<div class="top">
-<ul class="topmenu">
-<li class="toplink"><a href="/">Home</a></li>
-<li class="toplink"><a href="/set/">Set</a></li>
-<li class="toplink"><a href="/stats/">Stats</a></li>
-{% block menuextra %}{% endblock %}
-</ul>
+<div class="menu">
+ <ul class="topmenu">
+ <li class="toplink"><a href="/">Home</a></li>
+ <li class="toplink"><a href="/set/">Set</a></li>
+ <li class="toplink"><a href="/stats/">Stats</a></li>
+ {% block menuextra %}{% endblock %}
+ </ul>
</div>
-<br/><br/>
+<br/>
+{% if messages %}
+<ul class="messages">
+ {% for message in messages %}
+ <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
+ {% endfor %}
+</ul>
+{% endif %}
+<br/>
<div id="maindiv">
{% block content %}{% endblock %}
</div>
View
53 webclock/decorators.py
@@ -0,0 +1,53 @@
+try:
+ from functools import wraps
+except ImportError:
+ from django.utils.functional import wraps
+
+from django.shortcuts import render_to_response
+from django.template import RequestContext
+
+def confirm_required(template_name, context_creator, key='__confirm__'):
+ """
+ Decorator for views that need confirmation page. For example, delete
+ object view. Decorated view renders confirmation page defined by template
+ 'template_name'. If request.POST contains confirmation key, defined
+ by 'key' parameter, then original view is executed.
+
+ Context for confirmation page is created by function 'context_creator',
+ which accepts same arguments as decorated view.
+
+ Example
+ -------
+
+ def remove_file_context(request, id):
+ file = get_object_or_404(Attachment, id=id)
+ return RequestContext(request, {'file': file})
+
+ @confirm_required('remove_file_confirm.html', remove_file_context)
+ def remove_file_view(request, id):
+ file = get_object_or_404(Attachment, id=id)
+ file.delete()
+ next_url = request.GET.get('next', '/')
+ return HttpResponseRedirect(next_url)
+
+ Example of HTML template
+ ------------------------
+
+ <h1>Remove file {{ file }}?</h1>
+
+ <form method="POST" action="">
+ <input type="hidden" name="__confirm__" value="1" />
+ <input type="submit" value="delete"/> <a href="{{ file.get_absolute_url }}">cancel</a>
+ </form>
+
+ """
+ def decorator(func):
+ def inner(request, *args, **kwargs):
+ if request.POST.has_key(key):
+ return func(request, *args, **kwargs)
+ else:
+ context = context_creator and context_creator(request, *args, **kwargs) \
+ or RequestContext(request)
+ return render_to_response(template_name, context)
+ return wraps(func)(inner)
+ return decorator
View
16 webclock/templates/stats_detail_js.html
@@ -27,6 +27,20 @@
<td style="text-align:center">
<div class="jqPlot" id="chart2" style="height:120px; width:180px;"></div>
<button onclick="controllerPlot1.resetZoom()">Reset Zoom</button><br/><br/><br/>
+ <div id="moreactions_box">
+ <div id="moreactions_toggler"><a id="moreactions_toggle" href="">More Actions</a></div>
+ <div id="moreactions">
+ <ul>
+ <li><a href="{% url admin:db_session_delete session.id %}">Delete</a></li>
+ {% if prev %}
+ <li><a href="{% url stats_merge session.id prev.id %}">Merge with Previous</a></li>
+ {% endif %}
+ {% if next %}
+ <li><a href="{% url stats_merge session.id next.id %}">Merge with Next</a></li>
+ {% endif %}
+ </ul>
+ </div>
+ </div>
<p>Select type to edit:<br/>
<select id="selectType">
<option value="wake">Wakeup</option>
@@ -152,7 +166,7 @@
function plot() {
//global plot1, plot2;
plot1 = $.jqplot('chart1', [Points, Extra[0], Extra[1], Extra[2], Extra[3]], {
- title:'{{ session.start|date }} - {{ session.stop|date }}',
+ title:'{{ session.start|date:"DATE_FORMAT" }}: {{ session.start|time }} - {{ session.stop|time }}',
axes:{xaxis:{renderer:$.jqplot.DateAxisRenderer,
tickOptions:{formatString:'%H:%M:%S',
angle: -30},
View
13 webclock/templates/webclock/confirm_merge.html
@@ -0,0 +1,13 @@
+{% extends "base.html" %}
+
+
+{% block content %}
+<h1>Are you sure ?</h1>
+You will merge session {{ source.id }} into {{ session.id }}.
+This will destroy all aditional data known from the source session.
+
+<form method="POST" action="{% url stats_merge session.id source.id %}">
+ <input type="hidden" name="__confirm__" value="1" />{% csrf_token %}
+ <input type="submit" value="merge"/> <a href="{{ session.get_absolute_url }}">cancel</a>
+</form>
+{% endblock %}
View
4 webclock/urls.py
@@ -6,6 +6,8 @@
(r'^$', views.index),
(r'^stats/png_graph/(?P<session>\d+)/', views.png_graph),
(r'^stats/png_graph/', views.png_graph),
- (r'^stats/(\d+)/', views.stats_detail),
+ url(r'^stats/merge/(?P<session>\d+)/(?P<source>\d+)/', views.stats_merge, name="stats_merge"),
+ url(r'^stats/delete/(?P<session>\d+)/', views.stats_delete, name="stats_delete"),
+ url(r'^stats/(\d+)/', views.stats_detail, name="stats"),
(r'^stats/', views.stats),
)
View
55 webclock/views.py
@@ -2,12 +2,19 @@
# Create your views here.
import datetime, os, re
-
+from django.utils.translation import gettext as _
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
-from django.http import HttpResponse
+from django.http import HttpResponse, HttpResponseRedirect
from django.conf import settings
+from django.contrib import messages
+from django.core.urlresolvers import reverse
+
+
+from django.utils.translation import gettext as _
+
from uberclock.db.models import Entry, Session
+from uberclock.webclock.decorators import confirm_required
INDEX_PATH = os.path.join(os.path.dirname(__file__),
"templates", "webclock", "index")
@@ -87,16 +94,14 @@ def stats_detail(request, session):
now = datetime.datetime.now()
sess = get_object_or_404(Session, id__exact=session)
- next = Session.objects.filter(id__exact=sess.id+1)
- if next:
- next = next[0]
- else:
+ try:
+ next = Session.objects.filter(id__gt=sess.id)[0]
+ except IndexError:
next = None
- prev = Session.objects.filter(id__exact=sess.id-1)
- if prev:
- prev = prev[0]
- else:
+ try:
+ prev = Session.objects.filter(id__lt=sess.id).order_by('-id')[0]
+ except IndexError:
prev = None
typ = request.COOKIES.get("webclock_stats", "png")
@@ -129,13 +134,35 @@ def stats_detail(request, session):
return ret
+def merge_session_context(request, session, source):
+ session = get_object_or_404(Session, id=session)
+ source = get_object_or_404(Session, id=source)
+ return RequestContext(request, {'session': session, 'source': source})
+
+@confirm_required('webclock/confirm_merge.html', merge_session_context)
+def stats_merge(request, session, source):
+
+ sess = get_object_or_404(Session, id__exact=session)
+ sess.merge(Session.objects.get(id__exact=source))
+
+ messages.add_message(request, messages.INFO, _("Successfull merged"))
+
+ return HttpResponseRedirect(reverse("stats", args=(session,)))
+
+def stats_delete(request, session):
+ pass
+
+
+
# file charts.py
def png_graph(request, session=None):
import random
- from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
- from matplotlib.figure import Figure
- from matplotlib.dates import DateFormatter
-
+ try:
+ from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
+ from matplotlib.figure import Figure
+ from matplotlib.dates import DateFormatter
+ except:
+ return HttpResponse("Install matplotlib to see graphics", status=500)
fig=Figure()
ax=fig.add_subplot(111)

0 comments on commit d4f2be6

Please sign in to comment.