[svn] * Adding JS fill in next QM number via ajax.

* Slight models cleanup- get rid of TroggleImageModel class, use mixin instead.
* Collect various troggle shared functions into utils.py
This commit is contained in:
substantialnoninfringinguser 2009-07-04 08:27:49 +01:00
parent c132477f80
commit dd76a1a0be
9 changed files with 154 additions and 112 deletions

View File

@ -9,11 +9,16 @@ from django.core import serializers
from core.views_other import downloadLogbook
#from troggle.reversion.admin import VersionAdmin #django-reversion version control
#overriding admin save so we have the new since parsing field
class TroggleModelAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
"""overriding admin save to fill the new_since parsing_field"""
obj.new_since_parsing=True
obj.save()
class Media:
js = ('js/jquery.js','js/QM_helper.js')
class RoleInline(admin.TabularInline):
model = PersonRole
@ -33,7 +38,8 @@ class SurveyAdmin(TroggleModelAdmin):
class QMsFoundInline(admin.TabularInline):
model=QM
fk_name='found_by'
fields=('number','grade','location_description','comment')#need to add foreignkey to cave part
class PhotoInline(admin.TabularInline):
model = Photo
exclude = ['is_mugshot' ]
@ -61,6 +67,8 @@ class LogbookEntryAdmin(TroggleModelAdmin):
def export_logbook_entries_as_txt(modeladmin, request, queryset):
response=downloadLogbook(request=request, queryset=queryset, extension='txt')
return response
class PersonExpeditionInline(admin.TabularInline):
model = PersonExpedition

View File

@ -18,8 +18,8 @@ logging.basicConfig(level=logging.DEBUG,
filename=settings.LOGFILE,
filemode='w')
#This class is for adding fields and methods which all of our models will have.
class TroggleModel(models.Model):
"""This class is for adding fields and methods which all of our models will have."""
new_since_parsing = models.BooleanField(default=False, editable=False)
non_public = models.BooleanField(default=False)
def object_name(self):
@ -29,10 +29,22 @@ class TroggleModel(models.Model):
return urlparse.urljoin(settings.URL_ROOT, "/admin/core/" + self.object_name().lower() + "/" + str(self.pk))
class Meta:
abstract = True
abstract = True
class TroggleImageModel(ImageModel):
new_since_parsing = models.BooleanField(default=False, editable=False)
def object_name(self):
return self._meta.object_name
def get_admin_url(self):
return urlparse.urljoin(settings.URL_ROOT, "/admin/core/" + self.object_name().lower() + "/" + str(self.pk))
class Meta:
abstract = True
class Expedition(TroggleModel):
"""Represents a caving expedition"""
year = models.CharField(max_length=20, unique=True)
name = models.CharField(max_length=100)
date_from = models.DateField(blank=True,null=True)
@ -49,16 +61,15 @@ class Expedition(TroggleModel):
#return settings.URL_ROOT + "/expedition/%s" % self.year
return urlparse.urljoin(settings.URL_ROOT, reverse('expedition',args=[self.year]))
# lose these two functions (inelegant, and we may create a file with the dates that we can load from)
def GuessDateFrom(self):
"""Returns the date of the first logbook entry in the expedition"""
try:
return self.logbookentry_set.order_by('date')[0].date
except IndexError:
pass
def GuessDateTo(self):
"""Returns the date of the last logbook entry in the expedition"""
def GuessDateTo(self): # returns the date of the last logbook entry in the expedition
try:
return self.logbookentry_set.order_by('date')[-1].date
except IndexError:
@ -79,27 +90,42 @@ class Expedition(TroggleModel):
date+=datetime.timedelta(days=1)
return res
class Person(TroggleModel):
"""Represents a person, also used as the profile model"""
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
is_vfho = models.BooleanField(help_text="VFHO is the Vereines für Höhlenkunde in Obersteier, a nearby Austrian caving club.")
mug_shot = models.CharField(max_length=100, blank=True,null=True) #obsolete, remove soon
mug_shot = models.CharField(max_length=100, blank=True,null=True)
blurb = models.TextField(blank=True,null=True)
orderref = models.CharField(max_length=200) # for alphabetic
#href = models.CharField(max_length=200)
orderref = models.CharField(max_length=200) # for alphabetic
#the below have been removed and made methods. I'm not sure what the b in bisnotable stands for. - AC 16 Feb
#notability = models.FloatField() # for listing the top 20 people
#bisnotable = models.BooleanField()
user = models.OneToOneField(User, null=True, blank=True)
def get_absolute_url(self):
return urlparse.urljoin(settings.URL_ROOT,reverse('person',kwargs={'first_name':self.first_name,'last_name':self.last_name}))
class Meta:
verbose_name_plural = "People"
verbose_name_plural = "People"
class Meta:
ordering = ('orderref',) # "Wookey" makes too complex for: ('last_name', 'first_name')
def __unicode__(self):
if self.last_name:
return "%s %s" % (self.first_name, self.last_name)
return self.first_name
# Below are no longer needed. Use {{ person.personexpedition_set.all.0.expedition }} for Firstexpedition, and {{ person.personexpedition_set.latest.expedition }} for Lastexpedition
# these ought to be possible by piping through |min in the template, or getting the first of an ordered list
# def Firstexpedition(self):
# return self.personexpedition_set.order_by('expedition')[0]
# def Lastexpedition(self):
# return self.personexpedition_set.order_by('-expedition')[0]
def notability(self):
notability = Decimal(0)
@ -110,9 +136,18 @@ class Person(TroggleModel):
def bisnotable(self):
return self.notability() > Decimal(1)/Decimal(3)
#def Sethref(self):
#if self.last_name:
#self.href = self.first_name.lower() + "_" + self.last_name.lower()
#self.orderref = self.last_name + " " + self.first_name
#else:
# self.href = self.first_name.lower()
#self.orderref = self.first_name
#self.notability = 0.0 # set temporarily
class PersonExpedition(TroggleModel):
""""""
expedition = models.ForeignKey(Expedition)
person = models.ForeignKey(Person)
date_from = models.DateField(blank=True,null=True)
@ -199,9 +234,8 @@ class PersonExpedition(TroggleModel):
return urlparse.urljoin(settings.URL_ROOT, reverse('personexpedition',kwargs={'first_name':self.person.first_name,'last_name':self.person.last_name,'year':self.expedition.year}))
class LogbookEntry(TroggleModel):
"""Represents trips of all kinds. This is the central model of Troggle."""
date = models.DateField()
expedition = models.ForeignKey(Expedition,blank=True,null=True)
expedition = models.ForeignKey(Expedition,blank=True,null=True) # yes this is double-
author = models.ForeignKey(PersonExpedition,blank=True,null=True) # the person who writes it up doesn't have to have been on the trip.
# Re: the above- so this field should be "typist" or something, not "author". - AC 15 jun 09
title = models.CharField(max_length=200)
@ -211,7 +245,9 @@ class LogbookEntry(TroggleModel):
slug = models.SlugField(max_length=50)
class Meta:
verbose_name_plural = "Logbook Entries"
verbose_name_plural = "Logbook Entries"
# several PersonTrips point in to this object
class Meta:
ordering = ('-date',)
def get_absolute_url(self):
@ -226,6 +262,18 @@ class LogbookEntry(TroggleModel):
def get_previous_by_id(self):
LogbookEntry.objects.get(id=self.id-1)
def new_QM_number(self):
"""Returns """
if self.cave:
nextQMnumber=self.cave.new_QM_number(self.date.year)
else:
return none
return nextQMnumber
def new_QM_found_link(self):
"""Produces a link to a new QM with the next number filled in and this LogbookEntry set as 'found by' """
return settings.URL_ROOT + r'/admin/core/qm/add/?' + r'found_by=' + str(self.pk) +'&number=' + str(self.new_QM_number())
class PersonTrip(TroggleModel):
person_expedition = models.ForeignKey(PersonExpedition,null=True)
@ -246,6 +294,9 @@ class PersonTrip(TroggleModel):
else:
return self.logbook_entry.place
#persontrip_next = models.ForeignKey('PersonTrip', related_name='pnext', blank=True,null=True)
#persontrip_prev = models.ForeignKey('PersonTrip', related_name='pprev', blank=True,null=True)
def __unicode__(self):
return "%s %s (%s)" % (self.person_expedition, self.place(), self.date())
@ -261,6 +312,7 @@ class PersonTrip(TroggleModel):
except:
return
# def get_persons_previous_trip(self):
#
# move following classes into models_cave
#
@ -336,7 +388,15 @@ class Cave(TroggleModel):
def get_QMs(self):
return QM.objects.filter(found_by__cave=self)
def new_QM_number(self, year=datetime.date.today().year):
"""Given a cave and the current year, returns the next QM number."""
try:
res=QM.objects.filter(found_by__date__year=year, found_by__cave=self).order_by('-number')[0]
except IndexError:
return 1
return res.number+1
def kat_area(self):
for a in self.area.all():
if a.kat_area():
@ -373,13 +433,6 @@ class Cave(TroggleModel):
res += "–" + prevR
return res
def nextQMnumber(self, year=datetime.date.today().year):
"""
Given a cave and the current year, returns the next QM number.
"""
res=QM.objects.filter(found_by__date__year=year, found_by__cave=self).order_by('-number')[0]
return res.number+1
class OtherCaveName(TroggleModel):
name = models.CharField(max_length=160)
cave = models.ForeignKey(Cave)
@ -506,7 +559,7 @@ class QM(TroggleModel):
#"Number","Grade","Area","Description","Page reference","Nearest station","Completion description","Comment"
found_by = models.ForeignKey(LogbookEntry, related_name='QMs_found',blank=True, null=True )
ticked_off_by = models.ForeignKey(LogbookEntry, related_name='QMs_ticked_off',null=True,blank=True)
number = models.IntegerField(help_text="this is the sequential number in the year")
number = models.IntegerField(help_text="this is the sequential number in the year", )
GRADE_CHOICES=(
('A', 'A: Large obvious lead'),
('B', 'B: Average lead'),
@ -522,9 +575,7 @@ class QM(TroggleModel):
area = models.CharField(max_length=100,blank=True,null=True)
completion_description = models.TextField(blank=True,null=True)
comment=models.TextField(blank=True,null=True)
#the below are unneeded- instead use the date fields of the QM's trips
#dateFound = models.DateField(blank=True)
#dateKilled = models.DateField(blank=True)
def __unicode__(self):
QMnumber=str(self.found_by.cave)+'-'+str(self.found_by.date.year)+"-"+str(self.number)+self.grade
return str(QMnumber)
@ -546,7 +597,7 @@ class QM(TroggleModel):
return res
photoFileStorage = FileSystemStorage(location=settings.PHOTOS_ROOT, base_url=settings.PHOTOS_URL)
class Photo(ImageModel, TroggleModel):
class Photo(TroggleImageModel):
caption = models.CharField(max_length=1000,blank=True,null=True)
contains_logbookentry = models.ForeignKey(LogbookEntry,blank=True,null=True)
contains_person = models.ManyToManyField(Person,blank=True,null=True)
@ -578,7 +629,7 @@ def get_scan_path(instance, filename):
number="%02d" % instance.survey.wallet_number + str(instance.survey.wallet_letter) #using %02d string formatting because convention was 2009#01
return os.path.join('./',year,year+r'#'+number,instance.contents+str(instance.number_in_wallet)+r'.jpg')
class ScannedImage(ImageModel, TroggleModel):
class ScannedImage(TroggleImageModel):
file = models.ImageField(storage=scansFileStorage, upload_to=get_scan_path)
scanned_by = models.ForeignKey(Person,blank=True, null=True)
scanned_on = models.DateField(null=True)

View File

@ -31,7 +31,10 @@ class SurvexBlock(models.Model):
ordering = ('date', 'survexpath')
def __unicode__(self):
return unicode(self.name)
if self.name:
return unicode(self.name)
else:
return 'no name'
def filecontents(self):
f = os.path.join(settings.SURVEX_DATA, self.begin_file)

View File

@ -1,32 +0,0 @@
import troggle.settings as settings
from django import forms
from troggle.core.models import LogbookEntry
import random
import re
def weighted_choice(lst):
n = random.uniform(0,1)
for item, weight in lst:
if n < weight:
break
n = n - weight
return item
def randomLogbookSentence():
randSent={}
# needs to handle empty logbooks without crashing
#Choose a random logbook entry
randSent['entry']=LogbookEntry.objects.order_by('?')[0]
#Choose again if there are no sentances (this happens if it is a placeholder entry)
while len(re.findall('[A-Z].*?\.',randSent['entry'].text))==0:
randSent['entry']=LogbookEntry.objects.order_by('?')[0]
#Choose a random sentence from that entry. Store the sentence as randSent['sentence'], and the number of that sentence in the entry as randSent['number']
sentenceList=re.findall('[A-Z].*?\.',randSent['entry'].text)
randSent['number']=random.randrange(0,len(sentenceList))
randSent['sentence']=sentenceList[randSent['number']]
return randSent

View File

@ -5,12 +5,13 @@ from django.db import models
from troggle.parsers.logbooks import LoadLogbookForExpedition
from troggle.parsers.people import GetPersonExpeditionNameLookup
from troggle.core.forms import PersonForm
from django.core.urlresolvers import reverse
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
# Django uses Context, not RequestContext when you call render_to_response. We always want to use RequestContext, so that django adds the context from settings.TEMPLATE_CONTEXT_PROCESSORS. This way we automatically get necessary settings variables passed to each template. So we use a custom method, render_response instead of render_to_response. Hopefully future Django releases will make this unnecessary.
from troggle.alwaysUseRequestContext import render_response
from utils import render_with_context
import search
import re
@models.permalink #this allows the nice get_absolute_url syntax we are using
@ -36,7 +37,7 @@ def personindex(request):
if person.bisnotable():
notablepersons.append(person)
return render_with_context(request,'personindex.html', {'persons': persons, 'personss':personss, 'notablepersons':notablepersons, })
return render_response(request,'personindex.html', {'persons': persons, 'personss':personss, 'notablepersons':notablepersons, })
def expedition(request, expeditionname):
year = int(expeditionname)
@ -48,7 +49,7 @@ def expedition(request, expeditionname):
message = LoadLogbookForExpedition(expedition)
#message = str(GetPersonExpeditionNameLookup(expedition).keys())
logbookentries = expedition.logbookentry_set.order_by('date')
return render_with_context(request,'expedition.html', {'expedition': expedition, 'expedition_next':expedition_next, 'expedition_prev':expedition_prev, 'logbookentries':logbookentries, 'message':message, })
return render_response(request,'expedition.html', {'expedition': expedition, 'expedition_next':expedition_next, 'expedition_prev':expedition_prev, 'logbookentries':logbookentries, 'message':message, })
def get_absolute_url(self):
return ('expedition', (expedition.year))
@ -63,47 +64,22 @@ def person(request, first_name='', last_name='', ):
person.save()
return HttpResponseRedirect(reverse('profiles_select_profile'))
return render_with_context(request,'person.html', {'person': person, })
def get_absolute_url(self):
return settings.URL_ROOT + self.first_name + '_' + self.last_name
#def person(request, name):
# person = Person.objects.get(href=name)
#
return render_response(request,'person.html', {'person': person, })
def personexpedition(request, first_name='', last_name='', year=''):
person = Person.objects.get(first_name = first_name, last_name = last_name)
expedition = Expedition.objects.get(year=year)
personexpedition = person.personexpedition_set.get(expedition=expedition)
return render_with_context(request,'personexpedition.html', {'personexpedition': personexpedition, })
def newQMlink(logbookentry):
biggestQMnumber=0
if logbookentry.cave:
for log in logbookentry.cave.logbookentry_set.all():
try:
biggestQMnumberInLog = logbookentry.QMs_found.order_by('-number')[0].number
except IndexError:
biggestQMnumberInLog = 0
if biggestQMnumberInLog > biggestQMnumber:
biggestQMnumber = biggestQMnumberInLog
else:
return None
nextQMnumber=biggestQMnumber+1
return settings.URL_ROOT + r'/admin/expo/qm/add/?' + r'found_by=' + str(logbookentry.pk) +'&number=' + str(nextQMnumber)
return render_response(request,'personexpedition.html', {'personexpedition': personexpedition, })
def logbookentry(request, date, slug):
logbookentry = LogbookEntry.objects.filter(date=date, slug=slug)
if len(logbookentry)>1:
return render_with_context(request, 'object_list.html',{'object_list':logbookentry})
return render_response(request, 'object_list.html',{'object_list':logbookentry})
else:
logbookentry=logbookentry[0]
return render_with_context(request, 'logbookentry.html', {'logbookentry': logbookentry, 'newQMlink':newQMlink(logbookentry)})
return render_response(request, 'logbookentry.html', {'logbookentry': logbookentry})
def logbookSearch(request, extra):
query_string = ''
@ -113,11 +89,11 @@ def logbookSearch(request, extra):
entry_query = search.get_query(query_string, ['text','title',])
found_entries = LogbookEntry.objects.filter(entry_query)
return render_with_context(request,'logbooksearch.html',
return render_response(request,'logbooksearch.html',
{ 'query_string': query_string, 'found_entries': found_entries, })
#context_instance=RequestContext(request))
def personForm(request,pk):
person=Person.objects.get(pk=pk)
form=PersonForm(instance=person)
return render_with_context(request,'personform.html', {'form':form,})
return render_response(request,'personform.html', {'form':form,})

View File

@ -23,15 +23,9 @@ def stats(request):
return render_with_context(request,'statistics.html', statsDict)
def frontpage(request):
message = "no test message" #reverse('personn', kwargs={"name":"hkjhjh"})
if "reloadexpos" in request.GET:
message = LoadPersonsExpos()
message = "Reloaded personexpos"
if "reloadsurvex" in request.POST:
message = LoadAllSurvexBlocks()
message = "Reloaded survexblocks"
if request.user.is_authenticated:
return render_with_context(request,'tasks.html')
#'randSent':randSent.randomLogbookSentence(),
expeditions = Expedition.objects.order_by("-year")
logbookentry = LogbookEntry
cave = Cave
@ -153,3 +147,13 @@ def ajax_test(request):
def eyecandy(request):
return render_with_context(request,'eyecandy.html', {})
def ajax_QM_number(request):
if request.method=='POST':
cave=Cave.objects.get(id=request.POST['cave'])
print cave
exp=Expedition.objects.get(pk=request.POST['year'])
print exp
res=cave.new_QM_number(exp.year)
return HttpResponse(res)

20
media/js/QM_helper.js Normal file
View File

@ -0,0 +1,20 @@
$(document).ready(function(){
$('.number:first').append("<a href='javascript:void(0)' class='next_qm_link'>get next number</a>");
new_qm_link=function(e){
if ($('#id_cave:first')[0].value != "")
{
$.post('/newqmnumber/',{'cave':$('#id_cave')[0].value,'year':$('#id_expedition')[0].value}, function(data){
$('#id_QMs_found-0-number')[0].value=data;
});
}
else {
alert('Please choose a cave and try again.')
};
};
$('.next_qm_link').bind("click", new_qm_link);
});

View File

@ -37,7 +37,20 @@ def importSubcaves(cave):
logging.info("Added " + unicode(newSubcave) + " to " + unicode(cave))
except IOError:
logging.info("Subcave import couldn't open "+subcaveFilePath)
def getLinksInSubcaveDescription(subcave):
pattern='<a href=\"(.*?)\">(.*?)</a>'
if subcave.description:
return re.findall(pattern,subcave.description)
else:
return []
def getLinksInAllSubcaves():
bigList=[]
for subcave in Subcave.objects.all():
bigList+=getLinksInSubcaveDescription(subcave)
return bigList
def importAllSubcaves():
for cave in Cave.objects.all():
importSubcaves(cave)

View File

@ -1,5 +1,5 @@
from django.conf.urls.defaults import *
import troggle.settings as settings
from django.conf import settings
from core.views import * # flat import
from core.views_caves import *
@ -18,7 +18,7 @@ urlpatterns = patterns('',
url(r'^caves/?$', views_caves.caveindex, name="caveindex"),
url(r'^people/?$', views_logbooks.personindex, name="personindex"),
url(r'^newqmnumber/?$', views_other.ajax_QM_number, ),
#(r'^person/(?P<person_id>\d*)/?$', views_logbooks.person),
url(r'^person/(?P<first_name>[A-Z]*[a-z\-\']*)[^a-zA-Z]*(?P<last_name>[a-z\-\']*[^a-zA-Z]*[A-Z]*[a-z\-]*)/?', views_logbooks.person, name="person"),
#url(r'^person/(\w+_\w+)$', views_logbooks.person, name="person"),
@ -70,8 +70,7 @@ urlpatterns = patterns('',
url(r'^eyecandy$', views_other.eyecandy),
(r'^admin/doc/?', include('django.contrib.admindocs.urls')),
(r'^admin/(.*)/?', admin.site.root),
#url(r'^admin/', include(admin.site.urls),name="admin"),
(r'^admin/', include(admin.site.urls)),
(r'^accounts/', include('registration.urls')),
(r'^profiles/', include('profiles.urls')),