Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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...
commit d4f2be6cd4cc1ef3d0966c73ab78cc0185928efd 1 parent d191ec2
Daniel Poelzleithner authored
7  db/admin.py
... ...
@@ -0,0 +1,7 @@
  1
+from django.contrib import admin
  2
+from uberclock.db.models import *
  3
+
  4
+
  5
+admin.site.register(Detector)
  6
+admin.site.register(WakeupTime)
  7
+admin.site.register(Session)
29  db/models.py
... ...
@@ -1,7 +1,9 @@
1 1
 from django.db import models
2 2
 from uberclock.tools import ez_chronos
3 3
 from django.contrib.auth.models import User
  4
+from django.contrib import admin
4 5
 from django.conf import settings
  6
+from django.utils.dateformat import time_format
5 7
 import struct
6 8
 import logging
7 9
 import datetime
@@ -105,6 +107,30 @@ def week(self):
105 107
     def __repr__(self):
106 108
         return "<Session %s %s-%s>" %(self.user, self.start, self.stop)
107 109
 
  110
+    def __str__(self):
  111
+        return "Session %s %s-%s" %(self.user, self.start, self.stop)
  112
+
  113
+    def merge(self, source):
  114
+        source.entry_set.all().update(session=self)
  115
+        source.learndata_set.all().delete()
  116
+
  117
+        def cifn(key):
  118
+            var = getattr(source, key)
  119
+            if var and not getattr(self, key):
  120
+                setattr(self, key, var)
  121
+        # copy usefull variables
  122
+        for key in ["user", "wakeup", "rating"]:
  123
+            cifn(key)
  124
+
  125
+        if self.entry_set.all().count():
  126
+            self.start = self.entry_set.all().order_by('date')[0].date
  127
+            self.stop = self.entry_set.all().order_by('-date')[0].date
  128
+
  129
+        self.save()
  130
+        source.delete()
  131
+
  132
+
  133
+
108 134
     @property
109 135
     def length(self):
110 136
         s = (self.stop - self.start).seconds
@@ -122,6 +148,9 @@ class Entry(models.Model):
122 148
     def __repr__(self):
123 149
         return "<Entry %s %d>" %(self.date, self.value)
124 150
 
  151
+    def __unicode__(self):
  152
+        return u"Entry at %s: %s" %(self.date, time_format(self.date, settings.TIME_FORMAT))
  153
+
125 154
 
126 155
 class LearnData(models.Model):
127 156
     """
3  settings.py
@@ -82,6 +82,7 @@
82 82
     'piston.middleware.ConditionalMiddlewareCompatProxy',
83 83
     'piston.middleware.CommonMiddlewareCompatProxy',
84 84
     'django.contrib.sessions.middleware.SessionMiddleware',
  85
+    'django.contrib.messages.middleware.MessageMiddleware',
85 86
     'django.middleware.csrf.CsrfViewMiddleware',
86 87
     'django.contrib.auth.middleware.AuthenticationMiddleware',
87 88
     'django.contrib.messages.middleware.MessageMiddleware',
@@ -102,7 +103,7 @@
102 103
     'django.contrib.sessions',
103 104
     'django.contrib.sites',
104 105
     'django.contrib.markup',
105  
-#    'django.contrib.messages',
  106
+    'django.contrib.messages',
106 107
     # Uncomment the next line to enable the admin:
107 108
     'django.contrib.admin',
108 109
     'uberclock.db',
34  templates/base.html
@@ -15,7 +15,7 @@
15 15
   text-align: center;
16 16
   list-style-type: none;
17 17
 }
18  
-div.top {
  18
+div.menu {
19 19
   display: block;
20 20
 }
21 21
 body {
@@ -35,6 +35,14 @@
35 35
   color: white;
36 36
 }
37 37
 
  38
+#moreactions_box {
  39
+    border: 1px solid lightBlue;
  40
+}
  41
+
  42
+li {
  43
+    text-align: left;
  44
+    
  45
+}
38 46
 
39 47
   </style>
40 48
 
@@ -46,15 +54,23 @@
46 54
 {% block  header_extra %}{% endblock %}
47 55
 
48 56
 </head><body>
49  
-<div class="top">
50  
-<ul class="topmenu">
51  
-<li class="toplink"><a href="/">Home</a></li>
52  
-<li class="toplink"><a href="/set/">Set</a></li>
53  
-<li class="toplink"><a href="/stats/">Stats</a></li>
54  
-{% block menuextra %}{% endblock %}
55  
-</ul>
  57
+<div class="menu">
  58
+	<ul class="topmenu">
  59
+		<li class="toplink"><a href="/">Home</a></li>
  60
+		<li class="toplink"><a href="/set/">Set</a></li>
  61
+		<li class="toplink"><a href="/stats/">Stats</a></li>
  62
+		{% block menuextra %}{% endblock %}
  63
+	</ul>
56 64
 </div>
57  
-<br/><br/>
  65
+<br/>
  66
+{% if messages %}
  67
+<ul class="messages">
  68
+    {% for message in messages %}
  69
+    <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
  70
+    {% endfor %}
  71
+</ul>
  72
+{% endif %}
  73
+<br/>
58 74
 <div id="maindiv">
59 75
 {% block content %}{% endblock %}
60 76
 </div>
53  webclock/decorators.py
... ...
@@ -0,0 +1,53 @@
  1
+try:
  2
+    from functools import wraps
  3
+except ImportError:
  4
+    from django.utils.functional import wraps
  5
+    
  6
+from django.shortcuts import render_to_response
  7
+from django.template import RequestContext
  8
+    
  9
+def confirm_required(template_name, context_creator, key='__confirm__'):
  10
+    """
  11
+    Decorator for views that need confirmation page. For example, delete
  12
+    object view. Decorated view renders confirmation page defined by template
  13
+    'template_name'. If request.POST contains confirmation key, defined
  14
+    by 'key' parameter, then original view is executed.
  15
+    
  16
+    Context for confirmation page is created by function 'context_creator',
  17
+    which accepts same arguments as decorated view.
  18
+    
  19
+    Example
  20
+    -------
  21
+    
  22
+        def remove_file_context(request, id):
  23
+            file = get_object_or_404(Attachment, id=id)
  24
+            return RequestContext(request, {'file': file})
  25
+    
  26
+        @confirm_required('remove_file_confirm.html', remove_file_context)
  27
+        def remove_file_view(request, id):
  28
+            file = get_object_or_404(Attachment, id=id)
  29
+            file.delete()
  30
+            next_url = request.GET.get('next', '/')
  31
+            return HttpResponseRedirect(next_url)
  32
+            
  33
+    Example of HTML template
  34
+    ------------------------
  35
+    
  36
+        <h1>Remove file {{ file }}?</h1>
  37
+
  38
+        <form method="POST" action="">
  39
+            <input type="hidden" name="__confirm__" value="1" />
  40
+            <input type="submit" value="delete"/> <a href="{{ file.get_absolute_url }}">cancel</a>
  41
+        </form>
  42
+    
  43
+    """
  44
+    def decorator(func):
  45
+        def inner(request, *args, **kwargs):
  46
+            if request.POST.has_key(key):
  47
+                return func(request, *args, **kwargs)
  48
+            else:
  49
+                context = context_creator and context_creator(request, *args, **kwargs) \
  50
+                    or RequestContext(request)
  51
+                return render_to_response(template_name, context)
  52
+        return wraps(func)(inner)
  53
+    return decorator
16  webclock/templates/stats_detail_js.html
@@ -27,6 +27,20 @@
27 27
   <td style="text-align:center"> 
28 28
     <div class="jqPlot" id="chart2" style="height:120px; width:180px;"></div> 
29 29
     <button onclick="controllerPlot1.resetZoom()">Reset Zoom</button><br/><br/><br/>
  30
+    <div id="moreactions_box">
  31
+        <div id="moreactions_toggler"><a id="moreactions_toggle" href="">More Actions</a></div>
  32
+        <div id="moreactions">
  33
+            <ul>
  34
+                <li><a href="{% url admin:db_session_delete session.id %}">Delete</a></li>
  35
+                {% if prev %}
  36
+                    <li><a href="{% url stats_merge session.id prev.id %}">Merge with Previous</a></li>
  37
+                {% endif %}
  38
+                {% if next %}
  39
+                    <li><a href="{% url stats_merge session.id next.id %}">Merge with Next</a></li>
  40
+                {% endif %}
  41
+            </ul>
  42
+        </div>
  43
+    </div>
30 44
     <p>Select type to edit:<br/>
31 45
     <select id="selectType">
32 46
         <option value="wake">Wakeup</option>
@@ -152,7 +166,7 @@
152 166
 function plot() {
153 167
     //global plot1, plot2;
154 168
     plot1 = $.jqplot('chart1', [Points, Extra[0], Extra[1], Extra[2], Extra[3]], { 
155  
-        title:'{{ session.start|date }} - {{ session.stop|date }}', 
  169
+        title:'{{ session.start|date:"DATE_FORMAT" }}: {{ session.start|time }}  - {{ session.stop|time }}', 
156 170
         axes:{xaxis:{renderer:$.jqplot.DateAxisRenderer,
157 171
                      tickOptions:{formatString:'%H:%M:%S',
158 172
                                   angle: -30},
13  webclock/templates/webclock/confirm_merge.html
... ...
@@ -0,0 +1,13 @@
  1
+{% extends "base.html" %}
  2
+
  3
+
  4
+{% block content %}
  5
+<h1>Are you sure ?</h1>
  6
+You will merge session {{ source.id }} into {{ session.id }}.
  7
+This will destroy all aditional data known from the source session.
  8
+
  9
+<form method="POST" action="{% url stats_merge session.id source.id %}">
  10
+    <input type="hidden" name="__confirm__" value="1" />{% csrf_token %}
  11
+    <input type="submit" value="merge"/> <a href="{{ session.get_absolute_url }}">cancel</a>
  12
+</form>
  13
+{% endblock %}
4  webclock/urls.py
@@ -6,6 +6,8 @@
6 6
     (r'^$', views.index),
7 7
     (r'^stats/png_graph/(?P<session>\d+)/', views.png_graph),
8 8
     (r'^stats/png_graph/', views.png_graph),
9  
-    (r'^stats/(\d+)/', views.stats_detail),
  9
+    url(r'^stats/merge/(?P<session>\d+)/(?P<source>\d+)/', views.stats_merge, name="stats_merge"),
  10
+    url(r'^stats/delete/(?P<session>\d+)/', views.stats_delete, name="stats_delete"),
  11
+    url(r'^stats/(\d+)/', views.stats_detail, name="stats"),
10 12
     (r'^stats/', views.stats),
11 13
 )
55  webclock/views.py
@@ -2,12 +2,19 @@
2 2
 # Create your views here.
3 3
 import datetime, os, re
4 4
 
5  
-
  5
+from django.utils.translation import gettext as _
6 6
 from django.shortcuts import render_to_response, get_object_or_404
7 7
 from django.template import RequestContext
8  
-from django.http import HttpResponse
  8
+from django.http import HttpResponse, HttpResponseRedirect
9 9
 from django.conf import settings
  10
+from django.contrib import messages
  11
+from django.core.urlresolvers import reverse
  12
+
  13
+
  14
+from django.utils.translation import gettext as _
  15
+
10 16
 from uberclock.db.models import Entry, Session
  17
+from uberclock.webclock.decorators import confirm_required
11 18
 
12 19
 INDEX_PATH = os.path.join(os.path.dirname(__file__), 
13 20
                            "templates", "webclock", "index")
@@ -87,16 +94,14 @@ def stats_detail(request, session):
87 94
     now = datetime.datetime.now()
88 95
     sess = get_object_or_404(Session, id__exact=session)
89 96
 
90  
-    next = Session.objects.filter(id__exact=sess.id+1)
91  
-    if next: 
92  
-        next = next[0]
93  
-    else:
  97
+    try:
  98
+        next = Session.objects.filter(id__gt=sess.id)[0]
  99
+    except IndexError:
94 100
         next = None
95 101
 
96  
-    prev = Session.objects.filter(id__exact=sess.id-1)
97  
-    if prev: 
98  
-        prev = prev[0]
99  
-    else:
  102
+    try:
  103
+        prev = Session.objects.filter(id__lt=sess.id).order_by('-id')[0]
  104
+    except IndexError:
100 105
         prev = None
101 106
 
102 107
     typ = request.COOKIES.get("webclock_stats", "png")
@@ -129,13 +134,35 @@ def stats_detail(request, session):
129 134
     return ret
130 135
 
131 136
 
  137
+def merge_session_context(request, session, source):
  138
+    session = get_object_or_404(Session, id=session)
  139
+    source = get_object_or_404(Session, id=source)
  140
+    return RequestContext(request, {'session': session, 'source': source})
  141
+
  142
+@confirm_required('webclock/confirm_merge.html', merge_session_context)
  143
+def stats_merge(request, session, source):
  144
+
  145
+    sess = get_object_or_404(Session, id__exact=session)
  146
+    sess.merge(Session.objects.get(id__exact=source))
  147
+    
  148
+    messages.add_message(request, messages.INFO, _("Successfull merged"))
  149
+
  150
+    return HttpResponseRedirect(reverse("stats", args=(session,)))
  151
+
  152
+def stats_delete(request, session):
  153
+    pass
  154
+
  155
+
  156
+
132 157
 # file charts.py
133 158
 def png_graph(request, session=None):
134 159
     import random
135  
-    from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
136  
-    from matplotlib.figure import Figure
137  
-    from matplotlib.dates import DateFormatter
138  
-
  160
+    try:
  161
+        from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
  162
+        from matplotlib.figure import Figure
  163
+        from matplotlib.dates import DateFormatter
  164
+    except:
  165
+        return HttpResponse("Install matplotlib to see graphics", status=500)
139 166
 
140 167
     fig=Figure()
141 168
     ax=fig.add_subplot(111)

0 notes on commit d4f2be6

Please sign in to comment.
Something went wrong with that request. Please try again.