[cig-commits] r4411 - in cs/framework/trunk/cig/web: . seismo/events seismo/events/templates/events seismo/stations seismo/stations/templates/stations

leif at geodynamics.org leif at geodynamics.org
Wed Aug 23 20:29:20 PDT 2006


Author: leif
Date: 2006-08-23 20:29:20 -0700 (Wed, 23 Aug 2006)
New Revision: 4411

Added:
   cs/framework/trunk/cig/web/managers.py
   cs/framework/trunk/cig/web/middleware.py
   cs/framework/trunk/cig/web/models.py
   cs/framework/trunk/cig/web/seismo/events/templates/events/event_confirm_delete.html
Modified:
   cs/framework/trunk/cig/web/seismo/events/models.py
   cs/framework/trunk/cig/web/seismo/events/templates/events/event_form.html
   cs/framework/trunk/cig/web/seismo/events/templates/events/event_list.html
   cs/framework/trunk/cig/web/seismo/events/urls.py
   cs/framework/trunk/cig/web/seismo/events/views.py
   cs/framework/trunk/cig/web/seismo/stations/models.py
   cs/framework/trunk/cig/web/seismo/stations/templates/stations/station_list.html
   cs/framework/trunk/cig/web/seismo/stations/views.py
Log:
Added Luke Plant's get_current_user() hack, and a pair
of classes which use it.

Use RequestContext() consistently, as Django's generic views do.

Several bug fixes and tweaks.


Added: cs/framework/trunk/cig/web/managers.py
===================================================================
--- cs/framework/trunk/cig/web/managers.py	2006-08-24 02:47:01 UTC (rev 4410)
+++ cs/framework/trunk/cig/web/managers.py	2006-08-24 03:29:20 UTC (rev 4411)
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+#                              cig.web
+#
+# Copyright (c) 2006, California Institute of Technology
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+#
+#    * Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+#
+#    * Redistributions in binary form must reproduce the above
+#    copyright notice, this list of conditions and the following
+#    disclaimer in the documentation and/or other materials provided
+#    with the distribution.
+#
+#    * Neither the name of the California Institute of Technology nor
+#    the names of its contributors may be used to endorse or promote
+#    products derived from this software without specific prior
+#    written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+
+
+from django.db import models
+from django.db.models.fields import FieldDoesNotExist
+from middleware import get_current_user
+
+
+class CurrentUserManager(models.Manager):
+    """Use this to limit objects to those associated with the current
+    user.  For example:
+
+        ... 'queryset': MyModel.user_objects.all(), ...
+
+    """
+
+    # Based upon Django's CurrentSiteManager.  Requires that you add
+    # cig.web.middleware.ThreadLocals to your site's
+    # MIDDLEWARE_CLASSES setting.
+    
+    # This class has less value than models.CurrentUser, since any
+    # view using a "current user" query will usually be decorated with
+    # login_required(), and thus will have to be written-out as a
+    # custom view in 'views.py' anyway.
+    
+    def __init__(self, field_name='user'):
+        super(CurrentUserManager, self).__init__()
+        self.__field_name = field_name
+        self.__is_validated = False
+
+    def get_query_set(self):
+        if not self.__is_validated:
+            try:
+                self.model._meta.get_field(self.__field_name)
+            except FieldDoesNotExist:
+                raise ValueError, "%s couldn't find a field named %s in %s." % \
+                    (self.__class__.__name__, self.__field_name, self.model._meta.object_name)
+            self.__is_validated = True
+        return super(CurrentUserManager, self).get_query_set().filter(**{self.__field_name + '__id__exact': get_current_user().id})
+
+
+# end of file

Added: cs/framework/trunk/cig/web/middleware.py
===================================================================
--- cs/framework/trunk/cig/web/middleware.py	2006-08-24 02:47:01 UTC (rev 4410)
+++ cs/framework/trunk/cig/web/middleware.py	2006-08-24 03:29:20 UTC (rev 4411)
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+#                              cig.web
+#
+# Copyright (c) 2006, California Institute of Technology
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+#
+#    * Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+#
+#    * Redistributions in binary form must reproduce the above
+#    copyright notice, this list of conditions and the following
+#    disclaimer in the documentation and/or other materials provided
+#    with the distribution.
+#
+#    * Neither the name of the California Institute of Technology nor
+#    the names of its contributors may be used to endorse or promote
+#    products derived from this software without specific prior
+#    written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# "Django Admin Hack - Fields varying with user permissions"
+# by Luke Plant
+# with Python 2.3 patch by Joseph Kocherhans
+#
+# from http://lukeplant.me.uk/blog.php?id=1107301634
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+try:
+    # It wasn't until Python 2.4 that they realized they needed this...
+    from threading import local
+except ImportError:
+    # ...thankfully, Django includes a substitute.
+    from django.utils._threading_local import local
+
+_thread_locals = local()
+
+class You_need_to_add_ThreadLocals_to_MIDDLEWARE_CLASSES(object): pass
+
+def get_current_user():
+    return getattr(_thread_locals, 'user', You_need_to_add_ThreadLocals_to_MIDDLEWARE_CLASSES())
+
+# Add this to MIDDLEWARE_CLASSES.
+class ThreadLocals(object):
+    """Middleware that gets various objects from the request object
+    and saves them in thread local storage.
+
+    """
+
+    def process_request(self, request):
+        _thread_locals.user = getattr(request, 'user', None)
+
+
+# end of file

Added: cs/framework/trunk/cig/web/models.py
===================================================================
--- cs/framework/trunk/cig/web/models.py	2006-08-24 02:47:01 UTC (rev 4410)
+++ cs/framework/trunk/cig/web/models.py	2006-08-24 03:29:20 UTC (rev 4411)
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+#                              cig.web
+#
+# Copyright (c) 2006, California Institute of Technology
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+#
+#    * Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+#
+#    * Redistributions in binary form must reproduce the above
+#    copyright notice, this list of conditions and the following
+#    disclaimer in the documentation and/or other materials provided
+#    with the distribution.
+#
+#    * Neither the name of the California Institute of Technology nor
+#    the names of its contributors may be used to endorse or promote
+#    products derived from this software without specific prior
+#    written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+
+
+from middleware import get_current_user
+
+
+class CurrentUser(object):
+    """
+    Use in limit_choices_to to compare the field to the current user.  For example:
+
+        ... limit_choices_to = {'user__exact' : CurrentUser()} ...
+
+    Based upon Django's LazyDate.
+    
+    """
+    
+    def __str__(self):
+        return str(self.__get_value__())
+
+    def __get_value__(self):
+        return get_current_user()
+
+    def __getattr__(self, attr):
+        return getattr(self.__get_value__(), attr)
+
+
+# end of file

Modified: cs/framework/trunk/cig/web/seismo/events/models.py
===================================================================
--- cs/framework/trunk/cig/web/seismo/events/models.py	2006-08-24 02:47:01 UTC (rev 4410)
+++ cs/framework/trunk/cig/web/seismo/events/models.py	2006-08-24 03:29:20 UTC (rev 4411)
@@ -43,6 +43,8 @@
 
 
 from django.db import models
+from django.contrib.auth.models import User
+from cig.web.managers import CurrentUserManager
 
 
 class Region(models.Model):
@@ -82,6 +84,14 @@
     Mrp = models.FloatField(max_digits=19, decimal_places=10)
     Mtp = models.FloatField(max_digits=19, decimal_places=10)
 
+    # each user has a list of their own events
+    user = models.ForeignKey(User)
+
     def __str__(self): return self.eventName
 
+    # managers
+    objects = models.Manager()
+    user_objects = CurrentUserManager()
+
+
 # end of file

Added: cs/framework/trunk/cig/web/seismo/events/templates/events/event_confirm_delete.html
===================================================================
--- cs/framework/trunk/cig/web/seismo/events/templates/events/event_confirm_delete.html	2006-08-24 02:47:01 UTC (rev 4410)
+++ cs/framework/trunk/cig/web/seismo/events/templates/events/event_confirm_delete.html	2006-08-24 03:29:20 UTC (rev 4411)
@@ -0,0 +1,13 @@
+
+{% extends "events/base.html" %}
+
+{% block content %}
+
+<h1>Delete Event</h1>
+
+<form method="post" action=".">
+    <p>Are you sure you want to delete the event {{ object }}?
+    <p><input type="submit" value="Delete" />
+</form>
+
+{% endblock %}

Modified: cs/framework/trunk/cig/web/seismo/events/templates/events/event_form.html
===================================================================
--- cs/framework/trunk/cig/web/seismo/events/templates/events/event_form.html	2006-08-24 02:47:01 UTC (rev 4410)
+++ cs/framework/trunk/cig/web/seismo/events/templates/events/event_form.html	2006-08-24 03:29:20 UTC (rev 4411)
@@ -5,11 +5,17 @@
 
 <h1>Event</h1>
 
+<p>
 {% if form.has_errors %}
-<p><b class=error>Please correct the following error{{ form.error_dict|pluralize }}:</b>
+<b class=error>Please correct the following error{{ form.error_dict|pluralize }}:</b>
+{% else %}
+&nbsp;
 {% endif %}
 
 <form method="post" action=".">
+
+    <input type="hidden" name="user" value="{{ user.id }}">
+
     <table border="0">
 
         <tr>

Modified: cs/framework/trunk/cig/web/seismo/events/templates/events/event_list.html
===================================================================
--- cs/framework/trunk/cig/web/seismo/events/templates/events/event_list.html	2006-08-24 02:47:01 UTC (rev 4410)
+++ cs/framework/trunk/cig/web/seismo/events/templates/events/event_list.html	2006-08-24 03:29:20 UTC (rev 4411)
@@ -37,7 +37,7 @@
     <p>You have no events.</p>
 {% endif %}
 
-<p><a href="add/">Add event</a>
 <p><a href="search/">Search for an event</a>
+<p><a href="add/">Add an event manually</a>
 
 {% endblock %}

Modified: cs/framework/trunk/cig/web/seismo/events/urls.py
===================================================================
--- cs/framework/trunk/cig/web/seismo/events/urls.py	2006-08-24 02:47:01 UTC (rev 4410)
+++ cs/framework/trunk/cig/web/seismo/events/urls.py	2006-08-24 03:29:20 UTC (rev 4411)
@@ -47,7 +47,7 @@
 
 
 event_list_detail_args = {
-    'queryset': Event.objects.all(),
+    'queryset': Event.user_objects.all(),
     'allow_empty': True,
 }
 

Modified: cs/framework/trunk/cig/web/seismo/events/views.py
===================================================================
--- cs/framework/trunk/cig/web/seismo/events/views.py	2006-08-24 02:47:01 UTC (rev 4410)
+++ cs/framework/trunk/cig/web/seismo/events/views.py	2006-08-24 03:29:20 UTC (rev 4411)
@@ -44,6 +44,7 @@
 
 from cig.seismo.events import CMTSolution
 from django.shortcuts import render_to_response
+from django.template.context import RequestContext
 from HTMLParser import HTMLParser
 from models import DataSource, Region
 
@@ -52,7 +53,9 @@
     import urllib2
 
     if request.method == 'GET':
-        return render_to_response('events/event_search.html') 
+        return render_to_response('events/event_search.html',
+                                  {},
+                                  RequestContext(request, {})) 
 
     # Simply forward the search request to Harvard.
     query = request.POST.urlencode()
@@ -82,7 +85,8 @@
             r.save()
     
     return render_to_response('events/event_search_results.html',
-                              {'event_list': parser.cmtList }) 
+                              {'event_list': parser.cmtList },
+                              RequestContext(request, {})) 
 
 
 class HarvardCMTSearchResultsParser(HTMLParser):

Modified: cs/framework/trunk/cig/web/seismo/stations/models.py
===================================================================
--- cs/framework/trunk/cig/web/seismo/stations/models.py	2006-08-24 02:47:01 UTC (rev 4410)
+++ cs/framework/trunk/cig/web/seismo/stations/models.py	2006-08-24 03:29:20 UTC (rev 4411)
@@ -44,6 +44,7 @@
 
 from django.db import models
 from django.contrib.auth.models import User
+from cig.web.managers import CurrentUserManager
 
 
 STATION_STATUS_TYPES = (
@@ -58,6 +59,7 @@
 
 
 class Station(models.Model):
+    
     code = models.CharField(maxlength=10, primary_key=True) # 3-4 chars currently
     name = models.CharField(maxlength=100)
     network = models.ForeignKey(StationNetwork, null=True)
@@ -66,9 +68,14 @@
     longitude = models.FloatField(max_digits=19, decimal_places=10)
     elevation = models.FloatField(max_digits=19, decimal_places=10)
     bur = models.FloatField(max_digits=19, decimal_places=10, default=0.0)
-    users = models.ManyToManyField(User) # each user has a list of stations
+    
+    user = models.ManyToManyField(User) # each user has a list of stations
 
     def __str__(self): return self.code
 
+    # managers
+    objects = models.Manager()
+    user_objects = CurrentUserManager()
 
+
 # end of file

Modified: cs/framework/trunk/cig/web/seismo/stations/templates/stations/station_list.html
===================================================================
--- cs/framework/trunk/cig/web/seismo/stations/templates/stations/station_list.html	2006-08-24 02:47:01 UTC (rev 4410)
+++ cs/framework/trunk/cig/web/seismo/stations/templates/stations/station_list.html	2006-08-24 03:29:20 UTC (rev 4411)
@@ -10,9 +10,9 @@
     <li><a href="networks/">Browse station networks</a>
 </ul>
 
-<p><a href="gearth.kml"><img src="http://crust.geodynamics.org/~leif/kml.jpg" align="center"> View in Google Earth</a>
+{% if station_list %}
+    <p><a href="gearth.kml"><img src="http://crust.geodynamics.org/~leif/kml.jpg" align="center"> View in Google Earth</a>
 
-{% if station_list %}
     <form method="post" action="remove/">
         <table border="0">
             <tr>

Modified: cs/framework/trunk/cig/web/seismo/stations/views.py
===================================================================
--- cs/framework/trunk/cig/web/seismo/stations/views.py	2006-08-24 02:47:01 UTC (rev 4410)
+++ cs/framework/trunk/cig/web/seismo/stations/views.py	2006-08-24 03:29:20 UTC (rev 4411)
@@ -43,15 +43,17 @@
 
 
 from django import forms
+from django.contrib.auth.decorators import login_required
 from django.shortcuts import get_object_or_404, render_to_response
-from django.contrib.auth.decorators import login_required
-#from django.contrib.auth.models import User
+from django.template.context import RequestContext
 from models import Station, StationNetwork
 
 
 def my_stations(request):
-    station_list = Station.objects.filter(users__exact=request.user)
-    return render_to_response('stations/station_list.html', {'station_list': station_list })
+    station_list = Station.objects.filter(user__exact=request.user)
+    return render_to_response('stations/station_list.html',
+                              {'station_list': station_list },
+                              RequestContext(request, {}))
 my_stations = login_required(my_stations)
 
 
@@ -70,7 +72,7 @@
 
 
 def my_stations_gearth(request):
-    kwds = dict(queryset = Station.objects.filter(users__exact=request.user),
+    kwds = dict(queryset = Station.objects.filter(user__exact=request.user),
                 extra_context = {'name': 'My Stations'})
     return station_list_gearth(request, **kwds)
 my_stations_gearth = login_required(my_stations_gearth)
@@ -80,9 +82,11 @@
     stations = request.POST.getlist('stations')
     for code in stations:
         station = Station.objects.get(code=code)
-        station.users.add(request.user)
+        station.user.add(request.user)
         station.save()
-    return render_to_response('stations/station_add.html', {'stations': stations })
+    return render_to_response('stations/station_add.html',
+                              {'stations': stations },
+                               RequestContext(request, {}))
 add = login_required(add)
 
 
@@ -90,9 +94,11 @@
     stations = request.POST.getlist('stations')
     for code in stations:
         station = Station.objects.get(code=code)
-        station.users.remove(request.user)
+        station.user.remove(request.user)
         station.save()
-    return render_to_response('stations/station_remove.html', {'stations': stations })
+    return render_to_response('stations/station_remove.html',
+                              {'stations': stations },
+                              RequestContext(request, {}))
 remove = login_required(remove)
 
 
@@ -114,13 +120,16 @@
             manipulator.do_html2python(new_data)
             station_list = Station.objects.filter(code__startswith=new_data['code'])
             return render_to_response('stations/station_search_results.html',
-                                      {'station_list': station_list })
+                                      {'station_list': station_list },
+                                      RequestContext(request, {}))
     
     else: # GET
         errors = new_data = {}
 
     form = forms.FormWrapper(manipulator, new_data, errors)
-    return render_to_response('stations/station_search_code.html', {'form': form})
+    return render_to_response('stations/station_search_code.html',
+                              {'form': form},
+                               RequestContext(request, {}))
 
 
 class SearchByLocationManipulator(forms.Manipulator):
@@ -144,13 +153,16 @@
             manipulator.do_html2python(new_data)
             station_list = Station.objects.filter(code__startswith='pas')
             return render_to_response('stations/station_search_results.html',
-                                      {'station_list': station_list })
+                                      {'station_list': station_list },
+                                      RequestContext(request, {}))
     
     else: # GET
         errors = new_data = {}
 
     form = forms.FormWrapper(manipulator, new_data, errors)
-    return render_to_response('stations/station_search_location.html', {'form': form})
+    return render_to_response('stations/station_search_location.html',
+                              {'form': form},
+                              RequestContext(request, {}))
 
 
 def station_network_detail_gearth(request, code):



More information about the cig-commits mailing list