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): class PersonTripInline(admin.TabularInline):
model = PersonTrip model = PersonTrip
exclude = ['persontrip_next','Delete']
raw_id_fields = ('personexpedition',) raw_id_fields = ('personexpedition',)
extra = 1 extra = 1
#class LogbookEntryAdmin(VersionAdmin): #class LogbookEntryAdmin(VersionAdmin):
class LogbookEntryAdmin(TroggleModelAdmin): class LogbookEntryAdmin(TroggleModelAdmin):
prepopulated_fields = {'slug':("title",)} prepopulated_fields = {'slug':("title",)}
raw_id_fields = ('cave','author') raw_id_fields = ('cave',)
search_fields = ('title','expedition__year') search_fields = ('title','expedition__year')
date_heirarchy = ('date') date_heirarchy = ('date')
inlines = (PersonTripInline, PhotoInline, QMsFoundInline) inlines = (PersonTripInline, PhotoInline, QMsFoundInline)
@ -140,4 +139,4 @@ def export_as_xml(modeladmin, request, queryset):
return response return response
#admin.site.add_action(export_as_xml) #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 django.forms import ModelForm
from models import Cave, Person, LogbookEntry, QM from models import Cave, Person, PersonExpedition, LogbookEntry, QM
import django.forms as forms import django.forms as forms
from django.forms.formsets import formset_factory from django.forms.formsets import formset_factory
from django.contrib.admin.widgets import AdminDateWidget from django.contrib.admin.widgets import AdminDateWidget
import string import string
from datetime import date from datetime import date
from tinymce.widgets import TinyMCE
class CaveForm(ModelForm): class CaveForm(ModelForm):
class Meta: class Meta:
@ -45,4 +46,43 @@ class LogbookEntryForm(ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(LogbookEntryForm, self).__init__(*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 # Single parsed entry from Logbook
# #
class LogbookEntry(TroggleModel): class LogbookEntry(TroggleModel):
date = models.DateField() 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) 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- 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 # 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) 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") 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() text = models.TextField()
slug = models.SlugField(max_length=50) slug = models.SlugField(max_length=50)
filename= models.CharField(max_length=200,null=True)
class Meta: class Meta:
verbose_name_plural = "Logbook Entries" verbose_name_plural = "Logbook Entries"
# several PersonTrips point in to this object # several PersonTrips point in to this object
class Meta:
ordering = ('-date',) ordering = ('-date',)
def isLogbookEntry(self): # Function used in templates
return True
def get_absolute_url(self): def get_absolute_url(self):
return urlparse.urljoin(settings.URL_ROOT, reverse('logbookentry',kwargs={'date':self.date,'slug':self.slug})) 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): class PersonTrip(TroggleModel):
personexpedition = models.ForeignKey("PersonExpedition",null=True) personexpedition = models.ForeignKey("PersonExpedition",null=True)
expeditionday = models.ForeignKey("ExpeditionDay") #expeditionday = models.ForeignKey("ExpeditionDay")#MJG wants to KILL THIS (redundant information)
date = models.DateField() #date = models.DateField() #MJG wants to KILL THIS (redundant information)
time_underground = models.FloatField(help_text="In decimal hours") time_underground = models.FloatField(help_text="In decimal hours")
logbook_entry = models.ForeignKey(LogbookEntry) logbook_entry = models.ForeignKey(LogbookEntry)
is_logbook_entry_author = models.BooleanField() is_logbook_entry_author = models.BooleanField()
# sequencing by person (difficult to solve locally) # sequencing by person (difficult to solve locally)
persontrip_next = models.ForeignKey('PersonTrip', related_name='pnext', 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) #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): def place(self):
return self.logbook_entry.cave and self.logbook_entry.cave or self.logbook_entry.place return self.logbook_entry.cave and self.logbook_entry.cave or self.logbook_entry.place
def __unicode__(self): 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) survex_file = models.CharField(max_length=100,blank=True,null=True)
description_file = models.CharField(max_length=200,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) #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): def get_absolute_url(self):
if self.kataster_number: if self.kataster_number:
@ -421,6 +450,14 @@ class Cave(TroggleModel):
res += "–" + prevR res += "–" + prevR
return res 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): class OtherCaveName(TroggleModel):
name = models.CharField(max_length=160) name = models.CharField(max_length=160)
cave = models.ForeignKey(Cave) cave = models.ForeignKey(Cave)

View File

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

View File

@ -1,14 +1,18 @@
from django.shortcuts import render_to_response 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.core.models as models
import troggle.settings as settings import troggle.settings as settings
import django.db.models import django.db.models
from troggle.parsers.logbooks import LoadLogbookForExpedition from troggle.parsers.logbooks import LoadLogbookForExpedition
from troggle.parsers.people import GetPersonExpeditionNameLookup 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.core.urlresolvers import reverse
from django.http import HttpResponseRedirect, HttpResponse from django.http import HttpResponseRedirect, HttpResponse
from django.template import Context, loader
from utils import render_with_context 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. # 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)) expedition = Expedition.objects.get(year=int(expeditionname))
expeditions = Expedition.objects.all() expeditions = Expedition.objects.all()
personexpeditiondays = [ ] 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(): for personexpedition in expedition.personexpedition_set.all():
prow = [ ] prow = [ ]
for expeditionday in expedition.expeditionday_set.all(): for date in dates:
pcell = { "persontrips":expeditionday.persontrip_set.filter(personexpedition=personexpedition) } pcell = { "persontrips": PersonTrip.objects.filter(personexpedition=personexpedition,
pcell["survexblocks"] = set([survexpersonrole.survexblock for survexpersonrole in expeditionday.survexpersonrole_set.filter(personexpedition=personexpedition)]) logbook_entry__date=date) }
pcell["survexblocks"] = set(SurvexBlock.objects.filter(survexpersonrole__personexpedition=personexpedition,
date = date))
prow.append(pcell) prow.append(pcell)
personexpeditiondays.append({"personexpedition":personexpedition, "personrow":prow}) personexpeditiondays.append({"personexpedition":personexpedition, "personrow":prow})
message = "" message = ""
if "reload" in request.GET: if "reload" in request.GET:
message = LoadLogbookForExpedition(expedition) 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): def get_absolute_url(self):
return ('expedition', (expedition.year)) return ('expedition', (expedition.year))
@ -154,3 +163,78 @@ def experimental(request):
totalsurvexlength = sum([survexleg.tape for survexleg in survexlegs]) totalsurvexlength = sum([survexleg.tape for survexleg in survexlegs])
return render_with_context(request, 'experimental.html', { "nsurvexlegs":len(survexlegs), "totalsurvexlength":totalsurvexlength, "legsbyexpo":legsbyexpo }) 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)

View File

@ -11,11 +11,17 @@ from django.http import HttpResponse
def reload_db(): def reload_db():
cursor = connection.cursor() if settings.DATABASE_ENGINE == 'sqlite3':
cursor.execute("drop database %s" % settings.DATABASE_NAME) try:
cursor.execute("create database %s" % settings.DATABASE_NAME) os.remove(settings.DATABASE_NAME)
cursor.execute("ALTER DATABASE %s CHARACTER SET=utf8" % settings.DATABASE_NAME) except OSError:
cursor.execute("USE %s" % settings.DATABASE_NAME) pass
else:
cursor = connection.cursor()
cursor.execute("DROP DATABASE %s" % settings.DATABASE_NAME)
cursor.execute("CREATE DATABASE %s" % settings.DATABASE_NAME)
cursor.execute("ALTER DATABASE %s CHARACTER SET=utf8" % settings.DATABASE_NAME)
cursor.execute("USE %s" % settings.DATABASE_NAME)
management.call_command('syncdb', interactive=False) management.call_command('syncdb', interactive=False)
user = User.objects.create_user('expo', 'goatchurch@gmail.com', 'gosser') user = User.objects.create_user('expo', 'goatchurch@gmail.com', 'gosser')
user.is_staff = True user.is_staff = True
@ -111,6 +117,59 @@ def export_cavetab():
tocavetab.writeCaveTab(outfile) tocavetab.writeCaveTab(outfile)
outfile.close() outfile.close()
def import_auto_logbooks():
import parsers.logbooks
import os
for pt in core.models.PersonTrip.objects.all():
pt.delete()
for lbe in core.models.LogbookEntry.objects.all():
lbe.delete()
for expedition in core.models.Expedition.objects.all():
directory = os.path.join(settings.EXPOWEB,
"years",
expedition.year,
"autologbook")
for root, dirs, filenames in os.walk(directory):
for filename in filenames:
print os.path.join(root, filename)
parsers.logbooks.parseAutoLogBookEntry(os.path.join(root, filename))
#Temporary function until definative source of data transfered.
from django.template.defaultfilters import slugify
from django.template import Context, loader
def dumplogbooks():
def get_name(pe):
if pe.nickname:
return pe.nickname
else:
return pe.person.first_name
for lbe in core.models.LogbookEntry.objects.all():
dateStr = lbe.date.strftime("%Y-%m-%d")
directory = os.path.join(settings.EXPOWEB,
"years",
lbe.expedition.year,
"autologbook")
if not os.path.isdir(directory):
os.mkdir(directory)
filename = os.path.join(directory,
dateStr + "." + slugify(lbe.title)[:50] + ".html")
if lbe.cave:
print lbe.cave.reference()
trip = {"title": lbe.title, "html":lbe.text, "cave": lbe.cave.reference(), "caveOrLocation": "cave"}
else:
trip = {"title": lbe.title, "html":lbe.text, "location":lbe.place, "caveOrLocation": "location"}
pts = [pt for pt in lbe.persontrip_set.all() if pt.personexpedition]
persons = [{"name": get_name(pt.personexpedition), "TU": pt.time_underground, "author": pt.is_logbook_entry_author} for pt in pts]
f = open(filename, "wb")
template = loader.get_template('dataformat/logbookentry.html')
context = Context({'trip': trip,
'persons': persons,
'date': dateStr,
'expeditionyear': lbe.expedition.year})
output = template.render(context)
f.write(unicode(output).encode( "utf-8" ))
f.close()
if __name__ == "__main__": if __name__ == "__main__":
import core.models import core.models
import sys import sys
@ -118,6 +177,8 @@ if __name__ == "__main__":
resetdesc() resetdesc()
elif "scans" in sys.argv: elif "scans" in sys.argv:
import_surveyscans() import_surveyscans()
elif "QMs" in sys.argv:
import_QMs()
elif "tunnel" in sys.argv: elif "tunnel" in sys.argv:
import_tunnelfiles() import_tunnelfiles()
elif "reset" in sys.argv: elif "reset" in sys.argv:
@ -129,6 +190,10 @@ if __name__ == "__main__":
elif "logbooks" in sys.argv: elif "logbooks" in sys.argv:
management.call_command('syncdb', interactive=False) # this sets the path so that import settings works in import_survex management.call_command('syncdb', interactive=False) # this sets the path so that import settings works in import_survex
import_logbooks() import_logbooks()
elif "autologbooks" in sys.argv:
import_auto_logbooks()
elif "dumplogbooks" in sys.argv:
dumplogbooks()
else: else:
print "Do 'python databaseReset.py reset'" print "Do 'python databaseReset.py reset'"

View File

@ -90,12 +90,12 @@ def EnterLogIntoDbase(date, place, title, text, trippeople, expedition, logtime_
#Check for an existing copy of the current entry, and save #Check for an existing copy of the current entry, and save
expeditionday = expedition.get_expedition_day(date) expeditionday = expedition.get_expedition_day(date)
lookupAttribs={'date':date, 'title':title} lookupAttribs={'date':date, 'title':title}
nonLookupAttribs={'place':place, 'text':text, 'author':author, 'expedition':expedition, 'expeditionday':expeditionday, 'cave':cave, 'slug':slugify(title)[:50]} nonLookupAttribs={'place':place, 'text':text, 'expedition':expedition, 'cave':cave, 'slug':slugify(title)[:50]}
lbo, created=save_carefully(models.LogbookEntry, lookupAttribs, nonLookupAttribs) lbo, created=save_carefully(models.LogbookEntry, lookupAttribs, nonLookupAttribs)
for tripperson, time_underground in trippersons: for tripperson, time_underground in trippersons:
lookupAttribs={'personexpedition':tripperson, 'logbook_entry':lbo} lookupAttribs={'personexpedition':tripperson, 'logbook_entry':lbo}
nonLookupAttribs={'time_underground':time_underground, 'date':date, 'expeditionday':expeditionday, 'is_logbook_entry_author':(tripperson == author)} nonLookupAttribs={'time_underground':time_underground, 'is_logbook_entry_author':(tripperson == author)}
#print nonLookupAttribs #print nonLookupAttribs
save_carefully(models.PersonTrip, lookupAttribs, nonLookupAttribs) save_carefully(models.PersonTrip, lookupAttribs, nonLookupAttribs)
@ -328,4 +328,105 @@ def LoadLogbooks():
parsefunc(year, expedition, txt) parsefunc(year, expedition, txt)
SetDatesFromLogbookEntries(expedition) SetDatesFromLogbookEntries(expedition)
dateRegex = re.compile('<span\s+class="date">(\d\d\d\d)-(\d\d)-(\d\d)</span>', re.S)
expeditionYearRegex = re.compile('<span\s+class="expeditionyear">(.*?)</span>', re.S)
titleRegex = re.compile('<H1>(.*?)</H1>', re.S)
reportRegex = re.compile('<div\s+class="report">(.*)</div>\s*</body>', re.S)
personRegex = re.compile('<div\s+class="person">(.*?)</div>', re.S)
nameAuthorRegex = re.compile('<span\s+class="name(,author|)">(.*?)</span>', re.S)
TURegex = re.compile('<span\s+class="TU">([0-9]*\.?[0-9]+)</span>', re.S)
locationRegex = re.compile('<span\s+class="location">(.*?)</span>', re.S)
caveRegex = re.compile('<span\s+class="cave">(.*?)</span>', re.S)
def parseAutoLogBookEntry(filename):
errors = []
f = open(filename, "r")
contents = f.read()
f.close()
dateMatch = dateRegex.search(contents)
if dateMatch:
year, month, day = [int(x) for x in dateMatch.groups()]
date = datetime.date(year, month, day)
else:
errors.append("Date could not be found")
expeditionYearMatch = expeditionYearRegex.search(contents)
if expeditionYearMatch:
try:
expedition = models.Expedition.objects.get(year = expeditionYearMatch.groups()[0])
personExpeditionNameLookup = GetPersonExpeditionNameLookup(expedition)
except models.Expedition.DoesNotExist:
errors.append("Expedition not in database")
else:
errors.append("Expediton Year could not be parsed")
titleMatch = titleRegex.search(contents)
if titleMatch:
title, = titleMatch.groups()
if len(title) > settings.MAX_LOGBOOK_ENTRY_TITLE_LENGTH:
errors.append("Title too long")
else:
errors.append("Title could not be found")
caveMatch = caveRegex.search(contents)
if caveMatch:
caveRef, = caveMatch.groups()
try:
cave = models.getCaveByReference(caveRef)
except AssertionError:
cave = None
errors.append("Cave not found in database")
else:
cave = None
locationMatch = locationRegex.search(contents)
if locationMatch:
location, = locationMatch.groups()
else:
location = None
if cave is None and location is None:
errors.append("Location nor cave could not be found")
reportMatch = reportRegex.search(contents)
if reportMatch:
report, = reportMatch.groups()
else:
errors.append("Contents could not be found")
if errors:
return errors # Easiest to bail out at this point as we need to make sure that we know which expedition to look for people from.
people = []
for personMatch in personRegex.findall(contents):
nameAuthorMatch = nameAuthorRegex.search(contents)
if nameAuthorMatch:
author, name = nameAuthorMatch.groups()
if name.lower() in personExpeditionNameLookup:
personExpo = personExpeditionNameLookup[name.lower()]
else:
errors.append("Person could not be found in database")
author = bool(author)
else:
errors.append("Persons name could not be found")
TUMatch = TURegex.search(contents)
if TUMatch:
TU, = TUMatch.groups()
else:
errors.append("TU could not be found")
if not errors:
people.append((name, author, TU))
if errors:
return errors # Bail out before commiting to the database
logbookEntry = models.LogbookEntry(date = date,
expedition = expedition,
title = title, cave = cave, place = location,
text = report, slug = slugify(title)[:50],
filename = filename)
logbookEntry.save()
for name, author, TU in people:
models.PersonTrip(personexpedition = personExpo,
time_underground = TU,
logbook_entry = logbookEntry,
is_logbook_entry_author = author).save()
print logbookEntry

View File

@ -0,0 +1,25 @@
{% autoescape off %}
<html>
<head>
<style type="text/css">.author {text-decoration:underline}</style>
</head>
<body>
<H1>{{trip.title}}</H1>
<span class="date">{{date}}</span> - <span class="expeditionyear">{{expeditionyear}}</span>
{% if trip.caveOrLocation == "cave" %}
<span class="cave">{{trip.cave}}</span>
{% else %}
<span class="location">{{trip.location}}</span>
{% endif %}
{% for person in persons %}
<div class="person">
<span class="name{% if person.author %} author{% endif %}">{{person.name}}</span>
TU<span class="TU">{% if person.TU %}{{person.TU}}{% else %}0{% endif %}</span>hours
</div>
{% endfor %}
<div class="report">{{trip.html}}</div>
</body>
</html>
{% endautoescape %}

View File

@ -67,22 +67,20 @@ an "S" for a survey trip. The colours are the same for people on the same trip.
<form action="" method="GET"><input type="submit" name="reload" value="Reload"></form> <form action="" method="GET"><input type="submit" name="reload" value="Reload"></form>
<h3>Logbooks and survey trips per day</h3> <h3>Logbooks and survey trips per day</h3>
<a href="{% url newLogBookEntry expeditionyear=expedition.year %}">New logbook entry</a>
<table class="expeditionlogbooks"> <table class="expeditionlogbooks">
<tr><th>Date</th><th>Logged trips</th><th>Surveys</th></tr> <tr><th>Date</th><th>Logged trips</th><th>Surveys</th></tr>
{% for expeditionday in expedition.expeditionday_set.all %} {% regroup dateditems|dictsort:"date" by date as dates %}
{% for date in dates %}
<tr> <tr>
<td>{{expeditionday.date}}</td> <td>{{date.grouper}}</td>
<td> <td>{% for item in date.list %}
{% for logbookentry in expeditionday.logbookentry_set.all %} {% if item.isLogbookEntry %}<a href="{{ item.get_absolute_url }}">{{item.title|safe}}</a><br/>{% endif %}
<a href="{{ logbookentry.get_absolute_url }}">{{logbookentry.title|safe}}</a><br/> {% endfor %}</td>
{% endfor %} <td>{% for item in date.list %}
</td> {% if item.isSurvexBlock %}<a href="{% url svx item.survexfile.path %}">{{item.name}}</a><br/>{% endif %}
<td> {% endfor %}</td>
{% for survexblock in expeditionday.survexblock_set.all %} </tr>
<a href="{% url svx survexblock.survexfile.path %}">{{survexblock.name}}</a>
{% endfor %}
</td>
</tr>
{% endfor %} {% endfor %}
</table> </table>

View File

@ -45,12 +45,12 @@
<td> <td>
{% if persontrip.persontrip_prev %} {% if persontrip.persontrip_prev %}
<a href="{{ persontrip.persontrip_prev.logbook_entry.get_absolute_url }}">{{persontrip.persontrip_prev.date}}</a> <a href="{{ persontrip.persontrip_prev.logbook_entry.get_absolute_url }}">{{persontrip.persontrip_prev.logbook_entry.date}}</a>
{% endif %} {% endif %}
</td> </td>
<td> <td>
{% if persontrip.persontrip_next %} {% if persontrip.persontrip_next %}
<a href="{{ persontrip.persontrip_next.logbook_entry.get_absolute_url }}">{{persontrip.persontrip_next.date}}</a> <a href="{{ persontrip.persontrip_next.logbook_entry.get_absolute_url }}">{{persontrip.persontrip_next.logbook_entry.date}}</a>
{% endif %} {% endif %}
</td> </td>
@ -69,4 +69,6 @@
</div> </div>
</div> </div>
{% if logbookentry.filename %}<a href="{% url editlogbookentry year=logbookentry.year pdate=logbookentry.date pslug=logbookentry.slug %}">Edit</a> <a href="{% url deletelogbookentry year=logbookentry.year date=logbookentry.date slug=logbookentry.slug %}">Delete</a>{%endif%}
{% endblock %} {% endblock %}

View File

@ -0,0 +1,82 @@
{% extends "base.html" %}
{% block title %}Logbook {{logbookentry.id}}{% endblock %}
{% block head %}
<script>
$(function() {
$("#id_date").datepicker({dateFormat: "yy-mm-dd"});
$('.persontrips tbody tr').formset();
$(":radio[name*='caveOrLocation']").change(setLocationType);
$(setLocationType());
function setLocationType () {
$("#cave").hide();
$("#location").hide();
$("#" + $(":radio[name*='caveOrLocation']:checked")[0].value).show();
};
});
</script>
<link rel="stylesheet" href="{{ settings.MEDIA_URL }}css/ui-lightness/jquery-ui-1.8.12.custom.css" type="text/css" media="all" />
<script src="{{ settings.MEDIA_URL }}js/jquery-ui-1.8.12.custom.min.js" type="text/javascript"></script>
<script src="{{ settings.MEDIA_URL }}js/jquery.formset.min.js" type="text/javascript"></script>
<script src="{{ settings.TINY_MCE_MEDIA_URL }}tiny_mce.js" type="text/javascript"></script>
{{ tripForm.media }}
{% endblock %}
{% block content %}
<form action="" method="post">
{{ tripForm.non_field_errors }}
<div class="fieldWrapper">
{{ tripForm.title.errors }}
<label for="id_title">Title:</label>
{{ tripForm.title }}
</div>
<div class="fieldWrapper">
{{ tripForm.date.errors }}
<label for="id_date">Date:</label>
{{ tripForm.date }}
</div>
<div class="fieldWrapper">
{{ tripForm.caveOrLocation.errors }}
<label for="id_caveOrLocation">Location Type:</label>
{{ tripForm.caveOrLocation }}
</div>
<div class="fieldWrapper" id="cave">
{{ tripForm.cave.errors }}
<label for="id_cave">Cave:</label>
{{ tripForm.cave }}
</div>
<div class="fieldWrapper" id="location">
{{ tripForm.location.errors }}
<label for="id_location">Location:</label>
{{ tripForm.location }}
</div>
<table class="persontrips" border="0" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<th>Person</th>
<th>TU /hours</th>
<th>Author</th>
<th></th>
</tr>
{% for form in personTripFormSet.forms %}
<tr>
<td>{{ form.name.errors }}{{ form.name }}</td>
<td>{{ form.TU.errors }}{{ form.TU }}</td>
<td>{{ form.author.errors }}{{ form.author }}</td>
<td></td>
{{ form.non_field_errors }}
</tr>
{% endfor %}
</tbody>
</table>
{{ personTripFormSet.management_form }}
<div class="fieldWrapper">
{{ tripForm.html.errors }}
<label for="id_date">Content:</label>
{{ tripForm.html }}
</div>
<p><input type="submit" value="Sumbit Trip Report" /></p>
</form>
{% endblock %}

View File

@ -33,6 +33,9 @@ urlpatterns = patterns('',
url(r'^expeditions/?$', object_list, {'queryset':Expedition.objects.all(),'template_name':'object_list.html'},name="expeditions"), url(r'^expeditions/?$', object_list, {'queryset':Expedition.objects.all(),'template_name':'object_list.html'},name="expeditions"),
url(r'^personexpedition/(?P<first_name>[A-Z]*[a-z]*)[^a-zA-Z]*(?P<last_name>[A-Z]*[a-z]*)/(?P<year>\d+)/?$', views_logbooks.personexpedition, name="personexpedition"), url(r'^personexpedition/(?P<first_name>[A-Z]*[a-z]*)[^a-zA-Z]*(?P<last_name>[A-Z]*[a-z]*)/(?P<year>\d+)/?$', views_logbooks.personexpedition, name="personexpedition"),
url(r'^logbookentry/(?P<date>.*)/(?P<slug>.*)/?$', views_logbooks.logbookentry,name="logbookentry"), url(r'^logbookentry/(?P<date>.*)/(?P<slug>.*)/?$', views_logbooks.logbookentry,name="logbookentry"),
url(r'^newlogbookentry/(?P<expeditionyear>.*)$', views_logbooks.newLogbookEntry, name="newLogBookEntry"),
url(r'^editlogbookentry/(?P<expeditionyear>[^/]*)/(?P<pdate>[^/]*)/(?P<pslug>[^/]*)/$', views_logbooks.newLogbookEntry, name="editLogBookEntry"),
url(r'^deletelogbookentry/(?P<expeditionyear>[^/]*)/(?P<date>[^/]*)/(?P<slug>[^/]*)/$', views_logbooks.deleteLogbookEntry, name="deleteLogBookEntry"),
url(r'^cave/(?P<cave_id>[^/]+)/?$', views_caves.cave, name="cave"), url(r'^cave/(?P<cave_id>[^/]+)/?$', views_caves.cave, name="cave"),
url(r'^cavedescription/(?P<cavedescription_name>[^/]+)/?$', views_caves.cave_description, name="cavedescription"), url(r'^cavedescription/(?P<cavedescription_name>[^/]+)/?$', views_caves.cave_description, name="cavedescription"),
@ -83,6 +86,8 @@ urlpatterns = patterns('',
(r'^site_media/(?P<path>.*)$', 'django.views.static.serve', (r'^site_media/(?P<path>.*)$', 'django.views.static.serve',
{'document_root': settings.MEDIA_ROOT, 'show_indexes': True}), {'document_root': settings.MEDIA_ROOT, 'show_indexes': True}),
(r'^tinymce_media/(?P<path>.*)$', 'django.views.static.serve',
{'document_root': settings.TINY_MCE_MEDIA_ROOT, 'show_indexes': True}),
url(r'^survexblock/(.+)$', views_caves.survexblock, name="survexblock"), url(r'^survexblock/(.+)$', views_caves.survexblock, name="survexblock"),