maposmatic-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Maposmatic-dev] [PATCH maposmatic] Job cancel feature


From: Maxime Petazzoni
Subject: [Maposmatic-dev] [PATCH maposmatic] Job cancel feature
Date: Sun, 20 Jun 2010 22:08:20 +0200

This change introduces the job cancel feature as requested in bug #28488
(https://savannah.nongnu.org/bugs/?28488).

To do so, a new field called 'nonce' is added to the MapRenderingJob
model object (which results in a new column in the database, see below
how to recreate/update a current database). A random string, called the
nonce, is generated when the job is created and stored alongside the
other information on this job. The user is then redirected to the
/jobs/ID/NONCE page, instead of simply /jobs/ID.

As long as the user provides the correct nonce at the end of the URL, he
will have the ability, if the job is still in the queue, to cancel the
job request. The button to cancel the request is shown if and only if
the user provides the matching nonce string in the URL, which is only
displayed when the job is created.

A new job state is also created (4, Cancelled) to match this new
cancelled state.

As long as the database change goes, the easiest if you are only doing
development is to drop your database and recreate it with syncdb. If you
need to keep your data, you can simply add the column to the
maposmatic_maprenderingjob table using the following statement:

  ALTER TABLE maposmatic_maprenderingjob ADD COLUMN
    nonce varchar(16) NOT NULL;

Signed-off-by: Maxime Petazzoni <address@hidden>
---
 www/maposmatic/forms.py                  |   19 ++++++++++++++++++
 www/maposmatic/helpers.py                |    4 +++
 www/maposmatic/models.py                 |   16 ++++++++++++++-
 www/maposmatic/templatetags/extratags.py |    3 ++
 www/maposmatic/views.py                  |   31 +++++++++++++++++++++++------
 www/media/job-cancelled.png              |  Bin 0 -> 6982 bytes
 www/media/style.css                      |    4 +++
 www/templates/maposmatic/job-page.html   |    2 +-
 www/templates/maposmatic/job.html        |   11 +++++++++-
 www/urls.py                              |    6 +++++
 10 files changed, 86 insertions(+), 10 deletions(-)
 create mode 100644 www/media/job-cancelled.png

diff --git a/www/maposmatic/forms.py b/www/maposmatic/forms.py
index ecbdea6..621fe14 100644
--- a/www/maposmatic/forms.py
+++ b/www/maposmatic/forms.py
@@ -151,3 +151,22 @@ class MapRecreateForm(forms.Form):
             cleaned_data["jobid"] = 0
 
         return cleaned_data
+
+class MapCancelForm(forms.Form):
+    """
+    The map cancel form, to cancel a job (when the user has the matching
+    nonce).
+    """
+
+    jobid = forms.IntegerField(widget=forms.HiddenInput, required=True)
+    jobnonce = forms.CharField(widget=forms.HiddenInput, required=True)
+
+    def clean(self):
+        cleaned_data = self.cleaned_data
+
+        try:
+            cleaned_data["jobid"] = int(cleaned_data.get("jobid", 0))
+        except ValueError:
+            cleaned_data["jobid"] = 0
+
+        return cleaned_data
diff --git a/www/maposmatic/helpers.py b/www/maposmatic/helpers.py
index 0a70e26..a8382f0 100644
--- a/www/maposmatic/helpers.py
+++ b/www/maposmatic/helpers.py
@@ -24,6 +24,8 @@
 
 import datetime
 import psycopg2
+import random
+import string
 
 from ocitysmap.coords import BoundingBox as OCMBoundingBox
 from www.maposmatic.models import MapRenderingJob
@@ -155,3 +157,5 @@ def get_letters():
     # who don't?
     return [chr(i) for i in xrange(ord('A'), ord('Z')+1)]
 
+def generate_nonce(length):
+    return ''.join(random.choice(string.letters) for i in xrange(length))
diff --git a/www/maposmatic/models.py b/www/maposmatic/models.py
index 16cf765..6f13c2d 100644
--- a/www/maposmatic/models.py
+++ b/www/maposmatic/models.py
@@ -75,9 +75,12 @@ class MapRenderingJob(models.Model):
         (0, 'Submitted'),
         (1, 'In progress'),
         (2, 'Done'),
-        (3, 'Done w/o files')
+        (3, 'Done w/o files'),
+        (4, 'Cancelled'),
         )
 
+    NONCE_SIZE = 16
+
     maptitle = models.CharField(max_length=256)
 
     # When rendering through administrative city is selected, the
@@ -101,6 +104,8 @@ class MapRenderingJob(models.Model):
     index_queue_at_submission = models.IntegerField()
     map_language = models.CharField(max_length=16)
 
+    nonce = models.CharField(max_length=NONCE_SIZE, blank=True)
+
     objects = MapRenderingJobManager()
 
     def __str__(self):
@@ -150,6 +155,8 @@ class MapRenderingJob(models.Model):
     def is_obsolete_ok(self):       return self.is_obsolete() and 
self.__is_ok()
     def is_obsolete_failed(self):   return self.is_obsolete() and not 
self.__is_ok()
 
+    def is_cancelled(self):         return self.status == 4
+
     def get_map_fileurl(self, format):
         return www.settings.RENDERING_RESULT_URL + "/" + self.files_prefix() + 
"." + format
 
@@ -221,6 +228,12 @@ class MapRenderingJob(models.Model):
         self.save()
         return removed, saved
 
+    def cancel(self):
+        self.status = 4
+        self.endofrendering_time = datetime.now()
+        self.resultmsg = 'rendering cancelled'
+        self.save()
+
     def get_thumbnail(self):
         thumbnail_file = os.path.join(www.settings.RENDERING_RESULT_PATH, 
self.files_prefix() + "_small.png")
         thumbnail_url = www.settings.RENDERING_RESULT_URL + "/" + 
self.files_prefix() + "_small.png"
@@ -244,3 +257,4 @@ class MapRenderingJob(models.Model):
 
     def get_absolute_url(self):
         return reverse('job-by-id', args=[self.id])
+
diff --git a/www/maposmatic/templatetags/extratags.py 
b/www/maposmatic/templatetags/extratags.py
index ad7804a..0eb02a7 100644
--- a/www/maposmatic/templatetags/extratags.py
+++ b/www/maposmatic/templatetags/extratags.py
@@ -46,6 +46,8 @@ def job_status_to_str(value, arg, autoescape=None):
         else:
             return _("Rendering failed, and the incomplete files were "
                       "removed. Please contact address@hidden")
+    elif value == 4:
+        return _("Rendering was cancelled by the user")
 
     return ''
 
@@ -56,6 +58,7 @@ def job_status_to_icon_name(value, arg, autoescape=None):
         if arg == 'ok':     return 'job-done'
     if value == 3 and arg == 'ok':
         if arg == 'ok':     return 'job-done-obsolete'
+    if value == 4:          return 'job-cancelled'
 
     return 'job-error'
 
diff --git a/www/maposmatic/views.py b/www/maposmatic/views.py
index c3ae2d3..d95a076 100644
--- a/www/maposmatic/views.py
+++ b/www/maposmatic/views.py
@@ -81,10 +81,11 @@ def new(request):
             job.map_language = form.cleaned_data.get('map_language')
             job.index_queue_at_submission = (models.MapRenderingJob.objects
                                              .queue_size())
+            job.nonce = 
helpers.generate_nonce(models.MapRenderingJob.NONCE_SIZE)
             job.save()
 
-            return HttpResponseRedirect(reverse('job-by-id',
-                                                args=[job.id]))
+            return HttpResponseRedirect(reverse('job-by-id-and-nonce',
+                                                args=[job.id, job.nonce]))
     else:
         form = forms.MapRenderingJobForm()
 
@@ -92,7 +93,7 @@ def new(request):
                               { 'form' : form },
                               
context_instance=MapOSMaticRequestContext(request))
 
-def job(request, job_id):
+def job(request, job_id, job_nonce=None):
     """The job details page.
 
     Args:
@@ -108,8 +109,9 @@ def job(request, job_id):
         refresh = www.settings.REFRESH_JOB_RENDERING
 
     return render_to_response('maposmatic/job-page.html',
-                              { 'job' : job, 'single': True,
-                                'redirected' : isredirected,
+                              { 'job': job, 'single': True,
+                                'redirected': isredirected,
+                                'nonce': job_nonce,
                                 'refresh': refresh, 'refresh_ms': 
(refresh*1000) },
                               
context_instance=MapOSMaticRequestContext(request))
 
@@ -253,10 +255,25 @@ def recreate(request):
             newjob.map_language = job.map_language
             newjob.index_queue_at_submission = (models.MapRenderingJob.objects
                                                .queue_size())
+            newjob.nonce = 
helpers.generate_nonce(models.MapRenderingJob.NONCE_SIZE)
             newjob.save()
 
-            return HttpResponseRedirect(reverse('job-by-id',
-                                                args=[newjob.id]))
+            return HttpResponseRedirect(reverse('job-by-id-and-nonce',
+                                                args=[newjob.id, 
newjob.nonce]))
+
+    return HttpResponseBadRequest("ERROR: Invalid request")
+
+def cancel(request):
+    if request.method == 'POST':
+        form = forms.MapCancelForm(request.POST)
+        if form.is_valid():
+            job = get_object_or_404(models.MapRenderingJob,
+                                    id=form.cleaned_data['jobid'],
+                                    nonce=form.cleaned_data['jobnonce'])
+            job.cancel()
+
+            return HttpResponseRedirect(reverse('job-by-id-and-nonce',
+                                                args=[job.id, job.nonce]))
 
     return HttpResponseBadRequest("ERROR: Invalid request")
 
diff --git a/www/media/job-cancelled.png b/www/media/job-cancelled.png
new file mode 100644
index 
0000000000000000000000000000000000000000..0168f3cb6904fda5a53b3da697c0d6b9abd02fa7
GIT binary patch
literal 6982
address@hidden(P)<h;3K|address@hidden;+S_h00001b5ch_0Itp)
z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01ejw01ejxLMWSf00007bV*G`2igV{
z5g;M!LtQ!m02;<gL_t(|+U=ctY+To!-#_Qxd67d7A2UOWl&FU#Q<5#qk65bZAP+m+
zbsHy6-9)address@hidden&u|SYy<HQn0C!3<!MS->(qbQ0N!3MIW-E?E^7By<bNxGZZ
zl4CvWNU|(address@hidden@<h<{_r+?hJ^yTo*%$*_i*xel9V5G}CXU_e8f9Ll)j|;yp
address@hidden&address@hidden>V15E*fjI$L1Lg%>67ZfhKhJ*#E(?&q
zCZIt%0e4Dx1b7tlv#;L$iq{K(Q7$)XdRoBi(yTxJ99;f20hRJY=<~q8knlwT_kGwi
zL04}NJ<%wg{rv>{BDD4PptiN6`n;$<FA4&r#E=rbR75Y9&=(fT&8EoCBw0wNn4g^`
address@hidden;siVnJPSMqr~si*h;>^w5!reNU0XKe33Wk5%9nlH
zD!Oids;cu3X=NznDbJ^wzc$9~c#Ns>NivztQVYPVz>A-SzrGb}>ZTGf0?PexK)@dY
z_W}g`em38^l?}V^p>4|+Xm1B0OrW!Q$YvpzgM0z<address@hidden)1QB|{gJ<address@hidden
zgJAnoo3dDdg$&uNW5h3AX6*763dN$;!dU^|{Usb90psr-0Vm)o2`>address@hidden
z?>)N^-94tH7YZ;t2lHvjWK6JEGtw-z<MBXu2-)3*=;?-l4{|w-xmo7kd!I{Ze?=;p
address@hidden;address@hidden;9W|A<SfXu?`)tgP%*VHGW
z5cGwiF9PKfa&C_F`<HqD%)2C}6BaP9De&dT;nLqp0{$y_2jDR9RlwH~3bFg)address@hidden
zgK)$&iW8G?eHsko#)Ix;MN}0r&<address@hidden;reO5nK>6<s
z0k6Oo;3Z(MswnJu;666pdk<<~9|address@hidden&+=E+EEn5x+r335F?*MpP6Co
z?7O^w_E#8YAK%d7;FECewkO~O{4)vv1L*Dwhxy2-K0&ar526v6nKi-Jv|9qYl(POk
z9uI8ZfQa-U6Vqg8lAL}0Z^)$Pzzpzy=d<vaYl)address@hidden<address@hidden
z4!Cd$re|DBeKlBewNmb;EyIA>d6-{-fpz#gf(#A~q7{pzld}Qfse>So!cT9j5pWFD
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
address@hidden)geKT#YpYnk7jfK
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
YAEfGSHmand9smFU07*qoM6N<$g1V(dQvd(}

literal 0
HcmV?d00001

diff --git a/www/media/style.css b/www/media/style.css
index 77a80c3..ecb304d 100644
--- a/www/media/style.css
+++ b/www/media/style.css
@@ -258,6 +258,10 @@ form.recreate {
   visibility: visible;
 }
 
+form.jobcancel {
+  float: right;
+}
+
 h2.jobtitle {
   margin: 0;
   padding: 0;
diff --git a/www/templates/maposmatic/job-page.html 
b/www/templates/maposmatic/job-page.html
index e7184ed..8812ccf 100644
--- a/www/templates/maposmatic/job-page.html
+++ b/www/templates/maposmatic/job-page.html
@@ -53,7 +53,7 @@
 
 {% if job.needs_waiting %}
 <p>
-&raquo; <a href="{% url job-by-id job.id %}">{% trans "Refresh the status" 
%}</a> {% blocktrans %}(the page will refresh automatically every {{ refresh }} 
seconds until the rendering is completed).{% endblocktrans %}
+&raquo; <a href="{% if nonce == job.nonce %}{% url job-by-id-and-nonce job.id 
job.nonce %}{% else %}{% url job-by-id job.id %}{% endif %}">{% trans "Refresh 
the status" %}</a> {% blocktrans %}(the page will refresh automatically every 
{{ refresh }} seconds until the rendering is completed).{% endblocktrans %}
 </p>
 
 <script type="text/javascript">
diff --git a/www/templates/maposmatic/job.html 
b/www/templates/maposmatic/job.html
index 65b63cb..c6858bc 100644
--- a/www/templates/maposmatic/job.html
+++ b/www/templates/maposmatic/job.html
@@ -45,6 +45,13 @@
     <img src="/smedia/{{ job.status|job_status_to_icon_name:job.resultmsg 
}}.png" title="{{ job.status|job_status_to_str:job.resultmsg }} ({{ job.status 
}})" />
   </td>
   <td class="info">
+    {% if job.is_waiting and nonce == job.nonce %}
+    <form method="post" action="{% url cancel %}" class="jobcancel">
+      <input type="hidden" name="jobid" value="{{ job.id }}" />
+      <input type="hidden" name="jobnonce" value="{{ job.nonce }}" />
+      <input type="submit" value="{% trans "Cancel request" %}" />
+    </form>
+    {% endif %}
     {% if job.administrative_city %}
       {% ifnotequal job.administrative_city job.maptitle %}<h3>{{ 
job.administrative_city }}</h3>{% endifnotequal %}
     {% else %}
@@ -59,6 +66,7 @@
     {% if not job.needs_waiting %}
       {% if job.is_done_ok or job.is_obsolete_ok %}{% trans "Completed on" 
%}{% endif %}
       {% if job.is_done_failed or job.is_obsolete_failed %}{% trans "Failed 
on" %}{% endif %}
+      {% if job.is_cancelled %}{% trans "Cancelled on" %}{% endif %}
       {{ job.endofrendering_time|date:"l d M Y\, H:i:s" }}{% if 
job.rendering_time_gt_1min %} ({% blocktrans with 
job.startofrendering_time|timesince:job.endofrendering_time as rendering 
%}rendering took {{ rendering }}{% endblocktrans %}){% endif %}.
 
     {% if job.has_output_files %}
@@ -75,5 +83,6 @@
     {% endif %}
     {% endif %}
   </td>
-  <td class="thumb">{% if job.get_thumbnail %}<img src="{{ job.get_thumbnail 
}}" />{% endif %}</td>
+
+  {% if job.get_thumbnail %}<td class="thumb"><img src="{{ job.get_thumbnail 
}}" /></td>{% endif %}
 </tr></tbody></table>
diff --git a/www/urls.py b/www/urls.py
index 4dadb98..378707f 100644
--- a/www/urls.py
+++ b/www/urls.py
@@ -42,6 +42,9 @@ urlpatterns = patterns('',
     url(r'^about/$', maposmatic.views.about,
         name='about'),
 
+    url(r'^jobs/(?P<job_id>\d+)/(?P<job_nonce>[A-Za-z]{16})$',
+        maposmatic.views.job,
+        name='job-by-id-and-nonce'),
     url(r'^jobs/(?P<job_id>\d+)$', maposmatic.views.job,
         name='job-by-id'),
     url(r'^jobs/$', maposmatic.views.all_jobs,
@@ -58,6 +61,9 @@ urlpatterns = patterns('',
     url(r'^recreate/$', maposmatic.views.recreate,
         name='recreate'),
 
+    url(r'^cancel/$', maposmatic.views.cancel,
+        name='cancel'),
+
     (r'^nominatim/([^/]*/)?(.*)$', maposmatic.views.query_nominatim),
 
     # Internationalization
-- 
1.6.3.3.341.g9b22d




reply via email to

[Prev in Thread] Current Thread [Next in Thread]