edit logbooks, new logbook format, increased database normalisation

This commit is contained in:
Martin Green
2011-05-01 19:32:41 +01:00
parent 1cc7f2d92e
commit b001df1f53
12 changed files with 486 additions and 45 deletions

View File

@@ -50,14 +50,13 @@ class PhotoInline(admin.TabularInline):
class PersonTripInline(admin.TabularInline):
model = PersonTrip
exclude = ['persontrip_next','Delete']
raw_id_fields = ('personexpedition',)
extra = 1
#class LogbookEntryAdmin(VersionAdmin):
class LogbookEntryAdmin(TroggleModelAdmin):
prepopulated_fields = {'slug':("title",)}
raw_id_fields = ('cave','author')
raw_id_fields = ('cave',)
search_fields = ('title','expedition__year')
date_heirarchy = ('date')
inlines = (PersonTripInline, PhotoInline, QMsFoundInline)
@@ -140,4 +139,4 @@ def export_as_xml(modeladmin, request, queryset):
return response
#admin.site.add_action(export_as_xml)
#admin.site.add_action(export_as_json)
#admin.site.add_action(export_as_json)

View File

@@ -1,10 +1,11 @@
from django.forms import ModelForm
from models import Cave, Person, LogbookEntry, QM
from models import Cave, Person, PersonExpedition, LogbookEntry, QM
import django.forms as forms
from django.forms.formsets import formset_factory
from django.contrib.admin.widgets import AdminDateWidget
import string
from datetime import date
from tinymce.widgets import TinyMCE
class CaveForm(ModelForm):
class Meta:
@@ -45,4 +46,43 @@ class LogbookEntryForm(ModelForm):
def __init__(self, *args, **kwargs):
super(LogbookEntryForm, self).__init__(*args, **kwargs)
self.fields['text'].help_text=self.wikiLinkHints()
self.fields['text'].help_text=self.wikiLinkHints()
def getTripForm(expedition):
class TripForm(forms.Form):
date = forms.DateField()
title = forms.CharField(max_length=200)
caves = [cave.reference() for cave in Cave.objects.all()]
caves.sort()
caves = ["-----"] + caves
cave = forms.ChoiceField([(c, c) for c in caves], required=False)
location = forms.CharField(max_length=200, required=False)
caveOrLocation = forms.ChoiceField([("cave", "Cave"), ("location", "Location")], widget = forms.widgets.RadioSelect())
html = forms.CharField(widget=TinyMCE(attrs={'cols': 80, 'rows': 30}))
def clean(self):
print dir(self)
if self.cleaned_data.get("caveOrLocation") == "cave" and not self.cleaned_data.get("cave"):
self._errors["cave"] = self.error_class(["This field is required"])
if self.cleaned_data.get("caveOrLocation") == "location" and not self.cleaned_data.get("location"):
self._errors["location"] = self.error_class(["This field is required"])
return self.cleaned_data
class PersonTripForm(forms.Form):
def get_name(pe):
if pe.nickname:
return pe.nickname
else:
return pe.person.first_name
names = [get_name(pe) for pe in PersonExpedition.objects.filter(expedition = expedition)]
names.sort()
names = ["-----"] + names
name = forms.ChoiceField([(n, n) for n in names])
TU = forms.FloatField(required=False)
author = forms.BooleanField(required=False)
PersonTripFormSet = formset_factory(PersonTripForm, extra=1)
return PersonTripFormSet, TripForm

View File

@@ -232,23 +232,27 @@ class PersonExpedition(TroggleModel):
# Single parsed entry from Logbook
#
class LogbookEntry(TroggleModel):
date = models.DateField()
expeditionday = models.ForeignKey("ExpeditionDay", null=True)
date = models.DateField()#MJG wants to turn this into a datetime such that multiple Logbook entries on the same day can be ordered.
expeditionday = models.ForeignKey("ExpeditionDay", null=True)#MJG wants to KILL THIS (redundant information)
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.
#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)
#MJG wants to KILL THIS, as it is typically redundant with PersonTrip.is_logbook_entry_author, in the rare it was not redundanty and of actually interest it could be added to the text.
title = models.CharField(max_length=settings.MAX_LOGBOOK_ENTRY_TITLE_LENGTH)
cave = models.ForeignKey('Cave',blank=True,null=True)
place = models.CharField(max_length=100,blank=True,null=True,help_text="Only use this if you haven't chosen a cave")
text = models.TextField()
slug = models.SlugField(max_length=50)
filename= models.CharField(max_length=200,null=True)
class Meta:
verbose_name_plural = "Logbook Entries"
verbose_name_plural = "Logbook Entries"
# several PersonTrips point in to this object
class Meta:
ordering = ('-date',)
def isLogbookEntry(self): # Function used in templates
return True
def get_absolute_url(self):
return urlparse.urljoin(settings.URL_ROOT, reverse('logbookentry',kwargs={'date':self.date,'slug':self.slug}))
@@ -282,22 +286,36 @@ class LogbookEntry(TroggleModel):
class PersonTrip(TroggleModel):
personexpedition = models.ForeignKey("PersonExpedition",null=True)
expeditionday = models.ForeignKey("ExpeditionDay")
date = models.DateField()
#expeditionday = models.ForeignKey("ExpeditionDay")#MJG wants to KILL THIS (redundant information)
#date = models.DateField() #MJG wants to KILL THIS (redundant information)
time_underground = models.FloatField(help_text="In decimal hours")
logbook_entry = models.ForeignKey(LogbookEntry)
is_logbook_entry_author = models.BooleanField()
# sequencing by person (difficult to solve locally)
persontrip_next = models.ForeignKey('PersonTrip', related_name='pnext', blank=True,null=True)
persontrip_prev = models.ForeignKey('PersonTrip', related_name='pprev', blank=True,null=True)
#persontrip_next = models.ForeignKey('PersonTrip', related_name='pnext', blank=True,null=True)#MJG wants to KILL THIS (and use funstion persontrip_next_auto)
#persontrip_prev = models.ForeignKey('PersonTrip', related_name='pprev', blank=True,null=True)#MJG wants to KILL THIS(and use funstion persontrip_prev_auto)
def persontrip_next(self):
futurePTs = PersonTrip.objects.filter(personexpedition = self.personexpedition, logbook_entry__date__gt = self.logbook_entry.date).order_by('logbook_entry__date').all()
if len(futurePTs) > 0:
return futurePTs[0]
else:
return None
def persontrip_prev(self):
pastPTs = PersonTrip.objects.filter(personexpedition = self.personexpedition, logbook_entry__date__lt = self.logbook_entry.date).order_by('-logbook_entry__date').all()
if len(pastPTs) > 0:
return pastPTs[0]
else:
return None
def place(self):
return self.logbook_entry.cave and self.logbook_entry.cave or self.logbook_entry.place
def __unicode__(self):
return "%s (%s)" % (self.personexpedition, self.date)
return "%s (%s)" % (self.personexpedition, self.logbook_entry.date)
@@ -350,7 +368,18 @@ class Cave(TroggleModel):
survex_file = models.CharField(max_length=100,blank=True,null=True)
description_file = models.CharField(max_length=200,blank=True,null=True)
#class Meta:
# unique_together = (("area", "kataster_number"), ("area", "unofficial_number"))
# FIXME Kataster Areas and CUCC defined sub areas need seperating
#href = models.CharField(max_length=100)
def reference(self):
if self.kataster_number:
return "%s-%s" % (self.kat_area(), self.kataster_number)
else:
return "%s-%s" % (self.kat_area(), self.unofficial_number)
def get_absolute_url(self):
if self.kataster_number:
@@ -421,6 +450,14 @@ class Cave(TroggleModel):
res += "–" + prevR
return res
def getCaveByReference(reference):
print reference
areaname, code = reference.split("-", 1)
area = Area.objects.get(short_name = areaname)
foundCaves = list(Cave.objects.filter(area = area, kataster_number = code).all()) + list(Cave.objects.filter(area = area, unofficial_number = code).all())
assert len(foundCaves) == 1
return foundCaves[0]
class OtherCaveName(TroggleModel):
name = models.CharField(max_length=160)
cave = models.ForeignKey(Cave)

View File

@@ -92,6 +92,9 @@ class SurvexBlock(models.Model):
class Meta:
ordering = ('id',)
def isSurvexBlock(self): # Function used in templates
return True
def __unicode__(self):
return self.name and unicode(self.name) or 'no name'
@@ -188,4 +191,4 @@ class TunnelFile(models.Model):
class Meta:
ordering = ('tunnelpath',)

View File

@@ -1,14 +1,18 @@
from django.shortcuts import render_to_response
from troggle.core.models import Expedition, Person, PersonExpedition, PersonTrip, LogbookEntry
from troggle.core.models import Expedition, Person, PersonExpedition, PersonTrip, LogbookEntry, SurvexBlock
import troggle.core.models as models
import troggle.settings as settings
import django.db.models
from troggle.parsers.logbooks import LoadLogbookForExpedition
from troggle.parsers.people import GetPersonExpeditionNameLookup
from troggle.core.forms import PersonForm
from troggle.core.forms import PersonForm, getTripForm
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect, HttpResponse
from django.template import Context, loader
from utils import render_with_context
import os.path
import troggle.parsers.logbooks as logbookparsers
from django.template.defaultfilters import slugify
# 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.
@@ -47,18 +51,23 @@ def expedition(request, expeditionname):
expedition = Expedition.objects.get(year=int(expeditionname))
expeditions = Expedition.objects.all()
personexpeditiondays = [ ]
dateditems = list(expedition.logbookentry_set.all()) + list(expedition.survexblock_set.all())
dates = list(set([item.date for item in dateditems]))
dates.sort()
for personexpedition in expedition.personexpedition_set.all():
prow = [ ]
for expeditionday in expedition.expeditionday_set.all():
pcell = { "persontrips":expeditionday.persontrip_set.filter(personexpedition=personexpedition) }
pcell["survexblocks"] = set([survexpersonrole.survexblock for survexpersonrole in expeditionday.survexpersonrole_set.filter(personexpedition=personexpedition)])
for date in dates:
pcell = { "persontrips": PersonTrip.objects.filter(personexpedition=personexpedition,
logbook_entry__date=date) }
pcell["survexblocks"] = set(SurvexBlock.objects.filter(survexpersonrole__personexpedition=personexpedition,
date = date))
prow.append(pcell)
personexpeditiondays.append({"personexpedition":personexpedition, "personrow":prow})
message = ""
if "reload" in request.GET:
message = LoadLogbookForExpedition(expedition)
return render_with_context(request,'expedition.html', {'expedition': expedition, 'expeditions':expeditions, 'personexpeditiondays':personexpeditiondays, 'message':message, 'settings':settings })
return render_with_context(request,'expedition.html', {'expedition': expedition, 'expeditions':expeditions, 'personexpeditiondays':personexpeditiondays, 'message':message, 'settings':settings, 'dateditems': dateditems })
def get_absolute_url(self):
return ('expedition', (expedition.year))
@@ -154,3 +163,78 @@ def experimental(request):
totalsurvexlength = sum([survexleg.tape for survexleg in survexlegs])
return render_with_context(request, 'experimental.html', { "nsurvexlegs":len(survexlegs), "totalsurvexlength":totalsurvexlength, "legsbyexpo":legsbyexpo })
def newLogbookEntry(request, expeditionyear, pdate = None, pslug = None):
expedition = Expedition.objects.get(year=expeditionyear)
PersonTripFormSet, TripForm = getTripForm(expedition)
if pslug and pdate:
previousdate = datetime.date(*[int(x) for x in pdate.split("-")])
previouslbe = LogbookEntry.objects.get(slug = pslug, date = previousdate, expedition__year = year)
assert previouslbe.filename
if request.method == 'POST': # If the form has been submitted...
tripForm = TripForm(request.POST) # A form bound to the POST data
personTripFormSet = PersonTripFormSet(request.POST)
dateStr = tripForm.cleaned_data["date"].strftime("%Y-%m-%d")
directory = os.path.join(settings.EXPOWEB,
"years",
expedition.year,
"autologbook")
filename = os.path.join(directory,
dateStr + "." + slugify(tripForm.cleaned_data["title"])[:50] + ".html")
if tripForm.is_valid() and personTripFormSet.is_valid(): # All validation rules pass
if not os.path.isdir(directory):
os.mkdir(directory)
if pslug and pdate:
delLogbookEntry(previouslbe)
f = open(filename, "w")
template = loader.get_template('dataformat/logbookentry.html')
context = Context({'trip': tripForm.cleaned_data,
'persons': personTripFormSet.cleaned_data,
'date': dateStr,
'expeditionyear': expeditionyear})
f.write(template.render(context))
f.close()
print logbookparsers.parseAutoLogBookEntry(filename)
return HttpResponseRedirect(reverse('expedition', args=[expedition.year])) # Redirect after POST
else:
if slug and date:
if lbe.cave:
tripForm = TripForm(date = previousdate,
title = previouslbe.title,
cave = previouslbe.cave.reference(),
location = None,
caveOrLocation = "cave",
html = previouslbe.text)
else:
tripForm = TripForm(date = previousdate,
title = previouslbe.title,
cave = None,
location = previouslbe.location,
caveOrLocation = "location",
html = previouslbe.text)
personTripFormSet = PersonTripFormSet(initial=[{"name": py.personexpedition.name(),
"TU": py.time_underground,
"author": py.is_logbook_entry_author}
for py in previouslbe.persontrip_set.all()])
else:
tripForm = TripForm() # An unbound form
personTripFormSet = PersonTripFormSet()
return render_with_context(request, 'newlogbookentry.html', {
'tripForm': tripForm,
'personTripFormSet': personTripFormSet,
})
def deleteLogbookEntry(request, expeditionyear, date = None, slug = None):
expedition = Expedition.objects.get(year=expeditionyear)
previousdate = datetime.date(*[int(x) for x in pdate.split("-")])
previouslbe = LogbookEntry.objects.get(slug = pslug, date = previousdate, expedition__year = year)
delLogbookEntry(previouslbe)
return HttpResponseRedirect(reverse('expedition', args=[expedition.year])) # Redirect after POST
def delLogbookEntry(lbe):
for pt in lbe.persontrip_set.all():
pt.delete()
lbe.delete()
os.delete(lbe.filename)