From b001df1f5319dd8627faba3cd56c412e8584fc7d Mon Sep 17 00:00:00 2001 From: Martin Green Date: Sun, 1 May 2011 19:32:41 +0100 Subject: [PATCH] edit logbooks, new logbook format, increased database normalisation --- core/admin.py | 5 +- core/forms.py | 44 ++++++++++- core/models.py | 59 +++++++++++--- core/models_survex.py | 5 +- core/views_logbooks.py | 96 ++++++++++++++++++++-- databaseReset.py | 75 ++++++++++++++++-- parsers/logbooks.py | 105 ++++++++++++++++++++++++- templates/dataformat/logbookentry.html | 25 ++++++ templates/expedition.html | 24 +++--- templates/logbookentry.html | 6 +- templates/newlogbookentry.html | 82 +++++++++++++++++++ urls.py | 5 ++ 12 files changed, 486 insertions(+), 45 deletions(-) create mode 100644 templates/dataformat/logbookentry.html create mode 100644 templates/newlogbookentry.html diff --git a/core/admin.py b/core/admin.py index f8fe9fe..8dfa7ca 100644 --- a/core/admin.py +++ b/core/admin.py @@ -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) \ No newline at end of file +#admin.site.add_action(export_as_json) diff --git a/core/forms.py b/core/forms.py index 929c4e9..9a54b7b 100644 --- a/core/forms.py +++ b/core/forms.py @@ -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() \ No newline at end of file + 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 + diff --git a/core/models.py b/core/models.py index 432eca4..73063b2 100644 --- a/core/models.py +++ b/core/models.py @@ -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) diff --git a/core/models_survex.py b/core/models_survex.py index 7b652b7..ed21658 100644 --- a/core/models_survex.py +++ b/core/models_survex.py @@ -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',) - \ No newline at end of file + diff --git a/core/views_logbooks.py b/core/views_logbooks.py index 1cdffff..cb7de3a 100644 --- a/core/views_logbooks.py +++ b/core/views_logbooks.py @@ -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) + diff --git a/databaseReset.py b/databaseReset.py index bc953c9..cdd7dcf 100644 --- a/databaseReset.py +++ b/databaseReset.py @@ -11,11 +11,17 @@ from django.http import HttpResponse def reload_db(): - 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) + if settings.DATABASE_ENGINE == 'sqlite3': + try: + os.remove(settings.DATABASE_NAME) + except OSError: + 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) user = User.objects.create_user('expo', 'goatchurch@gmail.com', 'gosser') user.is_staff = True @@ -111,6 +117,59 @@ def export_cavetab(): tocavetab.writeCaveTab(outfile) 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__": import core.models import sys @@ -118,6 +177,8 @@ if __name__ == "__main__": resetdesc() elif "scans" in sys.argv: import_surveyscans() + elif "QMs" in sys.argv: + import_QMs() elif "tunnel" in sys.argv: import_tunnelfiles() elif "reset" in sys.argv: @@ -129,6 +190,10 @@ if __name__ == "__main__": elif "logbooks" in sys.argv: management.call_command('syncdb', interactive=False) # this sets the path so that import settings works in import_survex import_logbooks() + elif "autologbooks" in sys.argv: + import_auto_logbooks() + elif "dumplogbooks" in sys.argv: + dumplogbooks() else: print "Do 'python databaseReset.py reset'" diff --git a/parsers/logbooks.py b/parsers/logbooks.py index 9404414..e6b553b 100644 --- a/parsers/logbooks.py +++ b/parsers/logbooks.py @@ -90,12 +90,12 @@ def EnterLogIntoDbase(date, place, title, text, trippeople, expedition, logtime_ #Check for an existing copy of the current entry, and save expeditionday = expedition.get_expedition_day(date) 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) for tripperson, time_underground in trippersons: 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 save_carefully(models.PersonTrip, lookupAttribs, nonLookupAttribs) @@ -328,4 +328,105 @@ def LoadLogbooks(): parsefunc(year, expedition, txt) SetDatesFromLogbookEntries(expedition) +dateRegex = re.compile('(\d\d\d\d)-(\d\d)-(\d\d)', re.S) +expeditionYearRegex = re.compile('(.*?)', re.S) +titleRegex = re.compile('

(.*?)

', re.S) +reportRegex = re.compile('(.*)\s*', re.S) +personRegex = re.compile('(.*?)', re.S) +nameAuthorRegex = re.compile('(.*?)', re.S) +TURegex = re.compile('([0-9]*\.?[0-9]+)', re.S) +locationRegex = re.compile('(.*?)', re.S) +caveRegex = re.compile('(.*?)', 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 diff --git a/templates/dataformat/logbookentry.html b/templates/dataformat/logbookentry.html new file mode 100644 index 0000000..e6b83ce --- /dev/null +++ b/templates/dataformat/logbookentry.html @@ -0,0 +1,25 @@ +{% autoescape off %} + + + + + +

{{trip.title}}

+{{date}} - {{expeditionyear}} + +{% if trip.caveOrLocation == "cave" %} +{{trip.cave}} +{% else %} +{{trip.location}} +{% endif %} + +{% for person in persons %} +
+{{person.name}} +TU{% if person.TU %}{{person.TU}}{% else %}0{% endif %}hours +
+{% endfor %} +
{{trip.html}}
+ + +{% endautoescape %} diff --git a/templates/expedition.html b/templates/expedition.html index fd78eea..814b77e 100644 --- a/templates/expedition.html +++ b/templates/expedition.html @@ -67,22 +67,20 @@ an "S" for a survey trip. The colours are the same for people on the same trip.

Logbooks and survey trips per day

+New logbook entry -{% for expeditionday in expedition.expeditionday_set.all %} +{% regroup dateditems|dictsort:"date" by date as dates %} +{% for date in dates %} - - - - + + + + {% endfor %}
DateLogged tripsSurveys
{{expeditionday.date}} - {% for logbookentry in expeditionday.logbookentry_set.all %} - {{logbookentry.title|safe}}
- {% endfor %} -
- {% for survexblock in expeditionday.survexblock_set.all %} - {{survexblock.name}} - {% endfor %} -
{{date.grouper}}{% for item in date.list %} + {% if item.isLogbookEntry %}{{item.title|safe}}
{% endif %} + {% endfor %}
{% for item in date.list %} + {% if item.isSurvexBlock %}{{item.name}}
{% endif %} + {% endfor %}
diff --git a/templates/logbookentry.html b/templates/logbookentry.html index 03fc37e..19b8a30 100644 --- a/templates/logbookentry.html +++ b/templates/logbookentry.html @@ -45,12 +45,12 @@ {% if persontrip.persontrip_prev %} - {{persontrip.persontrip_prev.date}} + {{persontrip.persontrip_prev.logbook_entry.date}} {% endif %} {% if persontrip.persontrip_next %} - {{persontrip.persontrip_next.date}} + {{persontrip.persontrip_next.logbook_entry.date}} {% endif %} @@ -69,4 +69,6 @@ +{% if logbookentry.filename %}Edit Delete{%endif%} + {% endblock %} diff --git a/templates/newlogbookentry.html b/templates/newlogbookentry.html new file mode 100644 index 0000000..0cce46b --- /dev/null +++ b/templates/newlogbookentry.html @@ -0,0 +1,82 @@ +{% extends "base.html" %} +{% block title %}Logbook {{logbookentry.id}}{% endblock %} +{% block head %} + + + + + +{{ tripForm.media }} +{% endblock %} +{% block content %} + +
+ {{ tripForm.non_field_errors }} +
+ {{ tripForm.title.errors }} + + {{ tripForm.title }} +
+
+ {{ tripForm.date.errors }} + + {{ tripForm.date }} +
+
+ {{ tripForm.caveOrLocation.errors }} + + {{ tripForm.caveOrLocation }} +
+
+ {{ tripForm.cave.errors }} + + {{ tripForm.cave }} +
+
+ {{ tripForm.location.errors }} + + {{ tripForm.location }} +
+ + + + + + + + + {% for form in personTripFormSet.forms %} + + + + + + {{ form.non_field_errors }} + + {% endfor %} + +
PersonTU /hoursAuthor
{{ form.name.errors }}{{ form.name }}{{ form.TU.errors }}{{ form.TU }}{{ form.author.errors }}{{ form.author }}
+ {{ personTripFormSet.management_form }} +
+ {{ tripForm.html.errors }} + + {{ tripForm.html }} +
+

+
+ +{% endblock %} diff --git a/urls.py b/urls.py index 92f3c26..71312c0 100644 --- a/urls.py +++ b/urls.py @@ -33,6 +33,9 @@ urlpatterns = patterns('', url(r'^expeditions/?$', object_list, {'queryset':Expedition.objects.all(),'template_name':'object_list.html'},name="expeditions"), url(r'^personexpedition/(?P[A-Z]*[a-z]*)[^a-zA-Z]*(?P[A-Z]*[a-z]*)/(?P\d+)/?$', views_logbooks.personexpedition, name="personexpedition"), url(r'^logbookentry/(?P.*)/(?P.*)/?$', views_logbooks.logbookentry,name="logbookentry"), + url(r'^newlogbookentry/(?P.*)$', views_logbooks.newLogbookEntry, name="newLogBookEntry"), + url(r'^editlogbookentry/(?P[^/]*)/(?P[^/]*)/(?P[^/]*)/$', views_logbooks.newLogbookEntry, name="editLogBookEntry"), + url(r'^deletelogbookentry/(?P[^/]*)/(?P[^/]*)/(?P[^/]*)/$', views_logbooks.deleteLogbookEntry, name="deleteLogBookEntry"), url(r'^cave/(?P[^/]+)/?$', views_caves.cave, name="cave"), url(r'^cavedescription/(?P[^/]+)/?$', views_caves.cave_description, name="cavedescription"), @@ -83,6 +86,8 @@ urlpatterns = patterns('', (r'^site_media/(?P.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT, 'show_indexes': True}), + (r'^tinymce_media/(?P.*)$', 'django.views.static.serve', + {'document_root': settings.TINY_MCE_MEDIA_ROOT, 'show_indexes': True}), url(r'^survexblock/(.+)$', views_caves.survexblock, name="survexblock"),