From 7158a79a347d99fb1f8e94e034f2d79e08ad9172 Mon Sep 17 00:00:00 2001
From: goatchurch <devnull@localhost>
Date: Mon, 27 Jul 2009 13:43:43 +0100
Subject: [PATCH] [svn] full checkin.  animations disabled, sorry

---
 core/models.py                 | 146 ++++++++-------------------------
 core/models_survex.py          |  60 +++++++++++---
 core/views_logbooks.py         |  43 +++++++++-
 core/views_other.py            |  20 +++--
 core/views_survex.py           |  76 +++++++++++++++--
 media/css/dropdownNavStyle.css |   2 +-
 media/css/main3.css            |  22 +++--
 parsers/logbooks.py            |  29 +++----
 parsers/survex.py              |   5 +-
 urls.py                        |  23 +++---
 utils.py                       |   2 +-
 11 files changed, 255 insertions(+), 173 deletions(-)

diff --git a/core/models.py b/core/models.py
index a9ba8c1..08e4ecd 100644
--- a/core/models.py
+++ b/core/models.py
@@ -58,11 +58,14 @@ class TroggleImageModel(ImageModel):
     class Meta:
 	    abstract = True
 
+# 
+# single Expedition, usually seen by year
+#
 class Expedition(TroggleModel):
     year        = models.CharField(max_length=20, unique=True)
     name        = models.CharField(max_length=100)
-    date_from  = models.DateField(blank=True,null=True)
-    date_to    = models.DateField(blank=True,null=True)
+    date_from   = models.DateField(blank=True,null=True)
+    date_to     = models.DateField(blank=True,null=True)
     
     def __unicode__(self):
         return self.year
@@ -72,41 +75,13 @@ class Expedition(TroggleModel):
         get_latest_by = 'date_from'
     
     def get_absolute_url(self):
-        #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):
-	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
-	try:
-		return self.logbookentry_set.order_by('date')[-1].date
-	except IndexError:
-		pass
-
-    def ListDays(self):
-	if self.date_from and self.date_to:
-		res=[]
-		date=self.date_from
-		while date <= self.date_to:
-			res.append(date)
-			date+=datetime.timedelta(days=1)
-		return res
-	elif self.GuessDateFrom() and self.GuessDateTo(): 	# if we don't have the real dates, try it with the dates taken from the earliest and latest logbook entries
-		date=self.GuessDateFrom()
-		while date <= self.GuessDateTo():
-			res.append(date)
-			date+=datetime.timedelta(days=1)
-		return res
-
+        return urlparse.urljoin(settings.URL_ROOT, reverse('expedition', args=[self.year]))
     
 
 
+#
+# single Person, can go on many years
+#
 class Person(TroggleModel):
     first_name  = models.CharField(max_length=100)
     last_name   = models.CharField(max_length=100)
@@ -134,12 +109,6 @@ class Person(TroggleModel):
             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)
@@ -161,6 +130,9 @@ class Person(TroggleModel):
         #self.notability = 0.0  # set temporarily
         
 
+#
+# Person's attenance to one Expo
+#
 class PersonExpedition(TroggleModel):
     expedition  = models.ForeignKey(Expedition)
     person      = models.ForeignKey(Person)
@@ -189,52 +161,12 @@ class PersonExpedition(TroggleModel):
     class Meta:
         ordering = ('expedition',)
         #order_with_respect_to = 'expedition'
-	get_latest_by = 'expedition'
-    
-    def GetPersonChronology(self):
-        res = { }
-        for persontrip in self.persontrip_set.all():
-            a = res.setdefault(persontrip.date(), { })
-            a.setdefault("persontrips", [ ]).append(persontrip)
-        for personrole in self.personrole_set.all():
-            a = res.setdefault(personrole.survex_block.date, { })
-            b = a.setdefault("personroles", { })
-            survexpath = personrole.survex_block.survexpath
-            
-            if b.get(survexpath):
-                b[survexpath] += ", " + str(personrole.role)
-            else:
-                b[survexpath] = str(personrole.role)
-# needs converting dict into list            
-        return sorted(res.items())
-
-    # possibly not useful functions anyway -JT
-        # if you can find a better way to make the expo calendar table, be my guest. It isn't possible to do this logic in a django template without writing custom tags.-AC
-    def ListDays(self):
-        """
-        Returns a list of the days the person was on the expedition (i.e. the days that the PersonExpedition was in existance). Needed for expedition calendar.
-        """
-	if self.date_from and self.date_to:
-		res=[]
-		date=self.date_from
-		while date <= self.date_to:
-			res.append(date)
-			date+=datetime.timedelta(days=1)
-		return res
-
-    def ListDaysTF(self):
-        """
-        Returns a list of true / false values. Each value corresponds to one day on the expedition; True means the person was there, False means they weren't.
-        """
-	if self.date_from and self.date_to:
-		res=[]
-		for date in self.expedition.ListDays():
-			res.append(date in self.ListDays())
-		return res
+    get_latest_by = 'expedition'
 
     def __unicode__(self):
         return "%s: (%s)" % (self.person, self.expedition)
     
+    
     #why is the below a function in personexpedition, rather than in person? - AC 14 Feb 09
     def name(self):
         if self.nickname:
@@ -244,9 +176,12 @@ class PersonExpedition(TroggleModel):
         return self.person.first_name
 
     def get_absolute_url(self):
-        #return settings.URL_ROOT + '/personexpedition/' + str(self.person.first_name) + '_' + str(self.person.last_name) + '/' +self.expedition.year
-	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}))
+        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}))
 	
+    
+#
+# Single parsed entry from Logbook
+#    
 class LogbookEntry(TroggleModel):
     date    = models.DateField()
     expedition  = models.ForeignKey(Expedition,blank=True,null=True)  # yes this is double-
@@ -288,48 +223,35 @@ class LogbookEntry(TroggleModel):
         """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())
 
+
+#
+# Single Person going on a trip, which may or may not be written up (accounts for different T/U for people in same logbook entry)
+#
 class PersonTrip(TroggleModel):
     person_expedition = models.ForeignKey(PersonExpedition,null=True)
     
-        # this will be a foreign key of the place(s) the trip went through
-        # possibly a trip has a plurality of triplets pointing into it
-    #place           = models.CharField(max_length=100)  
-    #date            = models.DateField()    
+    date             = models.DateField()    
     time_underground = models.FloatField(help_text="In decimal hours")
     logbook_entry    = models.ForeignKey(LogbookEntry)
     is_logbook_entry_author = models.BooleanField()
     
-    def date(self):
-        return self.logbook_entry.date
-
+    
+    # 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)
+    
     def place(self):
-        if self.logbook_entry.cave:
-            return self.logbook_entry.cave
-        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)
+        return self.logbook_entry.cave and self.logbook_entry.cave or self.logbook_entry.place
 
     def __unicode__(self):
-        return "%s %s (%s)" % (self.person_expedition, self.place(), self.date())
+        return "%s (%s)" % (self.person_expedition, self.date)
 
-    def get_persons_next_trip(self):
-	try:
-            return PersonTrip.objects.filter(person_expedition__person=self.person_expedition.person, person_expedition__date__gt=self.date)[0]
-        except:
-	    return
 
-    def get_persons_previous_trip(self):
-	try:
-            return PersonTrip.objects.filter(person_expedition__person=self.person_expedition.person, person_expedition__date__lt=self.date)[0]
-        except:
-	    return
 
-#    def get_persons_previous_trip(self):
-#
+
+##########################################
 # move following classes into models_cave
-#
+##########################################
 
 class Area(TroggleModel):
     short_name = models.CharField(max_length=100)
diff --git a/core/models_survex.py b/core/models_survex.py
index 7c8d27e..ba43d93 100644
--- a/core/models_survex.py
+++ b/core/models_survex.py
@@ -2,6 +2,20 @@ from django.db import models
 from django.conf import settings
 import os
 
+
+###########################################################
+# These will allow browsing and editing of the survex data
+###########################################################
+
+
+# Needs to add: 
+#   SurvexFile
+#   Equates
+#   reloading
+
+#
+# Single SurvexBlock 
+# 
 class SurvexBlock(models.Model):
     name = models.CharField(max_length=100, blank=True, null=True)
     parent = models.ForeignKey('SurvexBlock', blank=True, null=True)
@@ -31,10 +45,10 @@ class SurvexBlock(models.Model):
         ordering = ('date', 'survexpath')
 
     def __unicode__(self):
-        if self.name:
-            return unicode(self.name)
-        else:
-            return 'no name'
+        return self.name and unicode(self.name) or 'no name'
+    
+    def filewithoutsvx(self):
+        return self.begin_file[:-4]
     
     def filecontents(self):
         f = os.path.join(settings.SURVEX_DATA, self.begin_file)
@@ -50,20 +64,40 @@ class SurvexBlock(models.Model):
                 res[-1]['roles'] += ", " + str(personrole.role)
             else:
                 res.append({'person':personrole.personexpedition.person, 'expeditionyear':personrole.personexpedition.expedition.year, 'roles':str(personrole.role)})
-        print res
         return res
 
 
-class PersonRole(models.Model):
-    personexpedition = models.ForeignKey('PersonExpedition')
-    person = models.ForeignKey('Person')
-    survex_block = models.ForeignKey('SurvexBlock')
-    role = models.ForeignKey('Role')
-    def __unicode__(self):
-        return unicode(self.person) + " - " + unicode(self.survex_block) + " - " + unicode(self.role)
-        
+#
+# Replace this with a choice string in PersonRole
+#
 class Role(models.Model):
     name = models.CharField(max_length=50)
     def __unicode__(self):
         return unicode(self.name)
+
+
+#
+# member of a SurvexBlock
+#
+class PersonRole(models.Model):
+    survex_block        = models.ForeignKey('SurvexBlock')
+    role                = models.ForeignKey('Role')  # to go
+    
+    ROLE_CHOICES = (
+        ('insts','Instruments'),
+        ('dog','Other'),
+        ('notes','Notes'),
+        ('pics','Pictures'),
+        ('tape','Tape measure'),
+        )
+    nrole = models.CharField(choices=ROLE_CHOICES, max_length=200, blank=True, null=True)
+
+    # increasing levels of precision
+    person              = models.ForeignKey('Person')
+    personexpedition    = models.ForeignKey('PersonExpedition')
+    persontrip          = models.ForeignKey('PersonTrip', blank=True, null=True)  
+    
+    def __unicode__(self):
+        return unicode(self.person) + " - " + unicode(self.survex_block) + " - " + unicode(self.role)
+        
     
diff --git a/core/views_logbooks.py b/core/views_logbooks.py
index e31f30f..07b5d57 100644
--- a/core/views_logbooks.py
+++ b/core/views_logbooks.py
@@ -22,6 +22,7 @@ def getNotablePersons():
                 notablepersons.append(person)
     return notablepersons		
 
+
 def personindex(request):
     persons = Person.objects.all()
     # From what I can tell, "persons" seems to be the table rows, while "personss" is the table columns. - AC 16 Feb 09
@@ -38,6 +39,7 @@ def personindex(request):
 
     return render_response(request,'personindex.html', {'persons': persons, 'personss':personss, 'notablepersons':notablepersons, })
 
+
 def expedition(request, expeditionname):
     year = int(expeditionname)
     expedition = Expedition.objects.get(year=year)
@@ -53,6 +55,7 @@ def expedition(request, expeditionname):
     def get_absolute_url(self):
         return ('expedition', (expedition.year))
 
+
 def person(request, first_name='', last_name='', ):
     person = Person.objects.get(first_name = first_name, last_name = last_name)
     
@@ -65,11 +68,45 @@ def person(request, first_name='', last_name='', ):
     
     return render_response(request,'person.html', {'person': person, })
 
+
+def GetPersonChronology(personexpedition):
+    res = { }
+    for persontrip in personexpedition.persontrip_set.all():
+        a = res.setdefault(persontrip.date, { })
+        a.setdefault("persontrips", [ ]).append(persontrip)
+
+    for personrole in personexpedition.personrole_set.all():
+        a = res.setdefault(personrole.survex_block.date, { })
+        b = a.setdefault("personroles", { })
+        survexpath = personrole.survex_block.survexpath
+        
+        if b.get(survexpath):
+            b[survexpath] += ", " + str(personrole.role)
+        else:
+            b[survexpath] = str(personrole.role)
+    
+    # build up the tables
+    rdates = res.keys()
+    rdates.sort()
+    
+    
+    res2 = [ ]
+    for rdate in rdates:
+        persontrips = res[rdate].get("persontrips", [])
+        personroles = list(res[rdate].get("personroles", {}).items())
+        for n in range(max(len(persontrips), len(personroles))):
+            res2.append(((n == 0 and rdate or ""), (n < len(persontrips) and persontrips[n]), (n < len(personroles) and personroles[n])))
+            
+    return res2
+
+
 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_response(request,'personexpedition.html', {'personexpedition': personexpedition, })
+    personchronology = GetPersonChronology(personexpedition)
+    return render_response(request,'personexpedition.html', {'personexpedition': personexpedition, 'personchronology':personchronology})
+
 
 def logbookentry(request, date, slug):
     logbookentry = LogbookEntry.objects.filter(date=date, slug=slug)
@@ -80,6 +117,7 @@ def logbookentry(request, date, slug):
         logbookentry=logbookentry[0]
         return render_response(request, 'logbookentry.html', {'logbookentry': logbookentry})
 
+
 def logbookSearch(request, extra):
     query_string = ''
     found_entries = None
@@ -95,4 +133,5 @@ def logbookSearch(request, extra):
 def personForm(request,pk):
     person=Person.objects.get(pk=pk)
     form=PersonForm(instance=person)
-    return render_response(request,'personform.html', {'form':form,})
\ No newline at end of file
+    return render_response(request,'personform.html', {'form':form,})
+
diff --git a/core/views_other.py b/core/views_other.py
index e3ecb10..b2b4a69 100644
--- a/core/views_other.py
+++ b/core/views_other.py
@@ -45,13 +45,23 @@ def todo(request):
     totallogbookentries = LogbookEntry.objects.count()
     return render_with_context(request,'index.html', {'expeditions':expeditions, 'all':'all', 'totallogbookentries':totallogbookentries, "message":message})
 
-def calendar(request,year):
+def calendar(request, year):
     week=['S','S','M','T','W','T','F']
-    if year:
-        expedition=Expedition.objects.get(year=year)
-        PersonExpeditions=expedition.personexpedition_set.all()
+    expedition = Expedition.objects.get(year=year)
+    personexpeditions = expedition.personexpedition_set.all()
     
-    return render_with_context(request,'calendar.html', locals())
+    listdays = [ ]   # the columns of the table
+    date = expedition.date_from
+    while date <= expedition.date_to:
+        listdays.append(date)
+        date += datetime.timedelta(days=1)
+                
+    personexpeditiondays = [ ]
+    for personexpedition in personexpeditions:
+        pelistdays = [ (personexpedition.date_from and (personexpedition.date_from <= date < personexpedition.date_to))  for date in listdays ]
+        personexpeditiondays.append([personexpedition, pelistdays])
+        
+    return render_with_context(request,'calendar.html', {"expedition":expedition, "listdays":listdays, "personexpeditiondays":personexpeditiondays})
 
 def controlPanel(request):
     jobs_completed=[]
diff --git a/core/views_survex.py b/core/views_survex.py
index 554ad4f..afdffcd 100644
--- a/core/views_survex.py
+++ b/core/views_survex.py
@@ -124,7 +124,8 @@ def svx(request, survex_file):
     if message:
         difflist.insert(0, message)
     
-    svxincludes = re.findall('\*include\s+"?(.*?)(?:\.svx)?"?\s*?\n(?i)', form.data['code'])
+    print [ form.data['code'] ]
+    svxincludes = re.findall('\*include\s+"?(.*?)(?:\.svx)?"?\s*?\n(?i)', form.data['code'] or "")
     
     vmap = {'settings': settings,
             'has_3d': os.path.isfile(settings.SURVEX_DATA + survex_file + ".3d"),
@@ -141,6 +142,16 @@ def Dsvx(request, survex_file):
     svx = open(settings.SURVEX_DATA + survex_file + ".svx", "rb")
     return HttpResponse(svx, mimetype="text")
 
+
+
+# The cavern running function
+def process(survex_file):
+    cwd = os.getcwd()
+    os.chdir(os.path.split(settings.SURVEX_DATA + survex_file)[0])
+    os.system(settings.CAVERN + " --log " + settings.SURVEX_DATA + survex_file + ".svx")
+    os.chdir(cwd)
+
+
 def threed(request, survex_file):
     process(survex_file)
     try:
@@ -160,8 +171,61 @@ def err(request, survex_file):
     err = open(settings.SURVEX_DATA + survex_file + ".err", "rb")
     return HttpResponse(err, mimetype="text")
 
-def process(survex_file):
-    cwd = os.getcwd()
-    os.chdir(os.path.split(settings.SURVEX_DATA + survex_file)[0])
-    os.system(settings.CAVERN + " --log " + settings.SURVEX_DATA + survex_file + ".svx")
-    os.chdir(cwd)
+
+
+def identifycavedircontents(gcavedir):
+    name = os.path.split(gcavedir)[1]
+    subdirs = [ ]
+    subsvx = [ ]
+    primesvx = None
+    for f in os.listdir(gcavedir):
+        if os.path.isdir(os.path.join(gcavedir, f)):
+            if f[0] != ".":
+                subdirs.append(f)
+        elif f[-4:] == ".svx":
+            nf = f[:-4]
+            if nf == name:
+                assert not primesvx
+                primesvx = nf
+            else:
+                subsvx.append(nf)
+        else:
+            assert re.match(".*?(?:.3d|.log|.err|.txt|.espec|~)$", f), (gcavedir, f)
+    subsvx.sort()
+    if primesvx:
+        subsvx.insert(0, primesvx)
+    return subdirs, subsvx
+                
+    
+
+# direct local non-database browsing through the svx file repositories
+# perhaps should use the database and have a reload button for it
+def survexcaveslist(request):
+    cavesdir = os.path.join(settings.SURVEX_DATA, "caves")
+    cavesdircontents = { }
+    
+    onefilecaves = [ ]
+    multifilecaves = [ ]
+        
+    # first sort the file list
+    fnumlist = [ (int(re.match("\d*", f).group(0) or "99999"), f)  for f in os.listdir(cavesdir) ]
+    fnumlist.sort()
+    
+    # go through the list and identify the contents of each cave directory
+    for num, cavedir in fnumlist:
+        gcavedir = os.path.join(cavesdir, cavedir)
+        if os.path.isdir(gcavedir) and cavedir[0] != ".":
+            subdirs, subsvx = identifycavedircontents(gcavedir)
+            survdirobj = [ ]
+            for lsubsvx in subsvx:
+                survdirobj.append(("caves/"+cavedir+"/"+lsubsvx, lsubsvx))
+            if len(survdirobj) == 1:
+                onefilecaves.append(survdirobj[0])
+            else:
+                multifilecaves.append((survdirobj[0], survdirobj[1:]))
+    
+    return render_to_response('svxfilecavelist.html', {'settings': settings, "onefilecaves":onefilecaves, "multifilecaves":multifilecaves})
+    
+    
+    
+    
\ No newline at end of file
diff --git a/media/css/dropdownNavStyle.css b/media/css/dropdownNavStyle.css
index 8ac9849..0cc0111 100644
--- a/media/css/dropdownNavStyle.css
+++ b/media/css/dropdownNavStyle.css
@@ -4,7 +4,7 @@ ul.dropdown a						{ text-decoration: none; }
 /* 
 	LEVEL ONE
 */
-ul.dropdown li                      { font-weight: bold; float:left; zoom: 1; background: none; padding:0px; margin:0px;}
+ul.dropdown li                      { font-weight: bold; float:left; zoom: 1; Dbackground: none; padding:0px; margin:0px;}
 ul.dropdown a:hover		            { color: black; }
 ul.dropdown a:active                { color: black; }
 ul.dropdown li a                    { display: block; padding: 4px 8px; border-right: 1px solid #333;
diff --git a/media/css/main3.css b/media/css/main3.css
index fae73df..f3a53ff 100644
--- a/media/css/main3.css
+++ b/media/css/main3.css
@@ -181,11 +181,17 @@ table#cavepage th#status	{ text-align: right; width: 25%; }
 }
 
 table {
-	border: thin solid silver;
+	border: thin solid black;
 	border-collapse: collapse;
 }
 td {
 	padding:0px;
+	border: thin solid black;
+}
+th 
+{
+    color:white;
+    background-color:black; 
 	border: thin solid silver;
 }
 
@@ -258,8 +264,8 @@ div#editLinks a{
 
 div#content {
 	margin-top: 50px;
-	margin-left: 120px;
-	margin-right: 120px;
+	Zmargin-left: 120px;
+	Zmargin-right: 120px;
 	padding-top: 10px;
 	padding-left: 5em;
 	padding-right: 5em;
@@ -269,14 +275,14 @@ div#content {
 
 
 .footer {
-	position:fixed;
+	Dposition:fixed;
 	width:100%;
 	bottom:0;
 	left:0;
 }
 
 body {
-	background-color:#000;
+	Zbackground-color:#000;
 	padding-bottom:100px;
 
 }
@@ -309,7 +315,7 @@ h1 {
 }
 
 #footerLinks{
-	position:fixed;
+	Dposition:fixed;
 	bottom:0px;
 	padding: 0px;
 	margin-left: 130px;
@@ -398,7 +404,7 @@ div#related h2
 
 div#related
 {
-	width:200px;
+	Zwidth:200px;
 	float:right;
 	border: thin solid black;
 	background:#EEEEEE;
@@ -407,7 +413,7 @@ div#related
 
 div#related table
 {
-	border-collapse:separate
+	Zborder-collapse:separate;
 }
 
 .addlink {
diff --git a/parsers/logbooks.py b/parsers/logbooks.py
index c6dfa79..9866c84 100644
--- a/parsers/logbooks.py
+++ b/parsers/logbooks.py
@@ -88,7 +88,8 @@ def EnterLogIntoDbase(date, place, title, text, trippeople, expedition, logtime_
 
     for tripperson, time_underground in trippersons:
         lookupAttribs={'person_expedition':tripperson, 'logbook_entry':lbo}
-        nonLookupAttribs={'time_underground':time_underground,'is_logbook_entry_author':(tripperson == author)}
+        nonLookupAttribs={'time_underground':time_underground, 'date':date, 'is_logbook_entry_author':(tripperson == author)}
+        print nonLookupAttribs
         save_carefully(models.PersonTrip, lookupAttribs, nonLookupAttribs)
 
 
@@ -100,7 +101,7 @@ def ParseDate(tripdate, year):
         assert mdatestandard.group(1) == year, (tripdate, year)
         year, month, day = int(mdatestandard.group(1)), int(mdatestandard.group(2)), int(mdatestandard.group(3))
     elif mdategoof:
-        assert not mdategoof.group(3) or mdategoof.group(3) == year[:2]
+        assert not mdategoof.group(3) or mdategoof.group(3) == year[:2], mdategoof.groups()
         yadd = int(year[:2]) * 100
         day, month, year = int(mdategoof.group(1)), int(mdategoof.group(2)), int(mdategoof.group(4)) + yadd
     else:
@@ -239,7 +240,7 @@ def Parseloghtml03(year, expedition, txt):
 
 yearlinks = [ 
                 ("2008", "2008/2008logbook.txt", Parselogwikitxt), 
-                #("2007", "2007/2007logbook.txt", Parselogwikitxt), 
+                ("2007", "2007/2007logbook.txt", Parselogwikitxt), 
                 ("2006", "2006/logbook/logbook_06.txt", Parselogwikitxt), 
                 ("2005", "2005/logbook.html", Parseloghtmltxt), 
                 ("2004", "2004/logbook.html", Parseloghtmltxt), 
@@ -268,16 +269,16 @@ def SetDatesFromLogbookEntries(expedition):
         personexpedition.date_to = max([persontrip.logbook_entry.date  for persontrip in persontrips] or [None])
         personexpedition.save()
 
-# The below is all unnecessary, just use the built in get_previous_by_date and get_next_by_date
-#        lprevpersontrip = None
-#        for persontrip in persontrips:
-#            persontrip.persontrip_prev = lprevpersontrip
-#            if lprevpersontrip:
-#                lprevpersontrip.persontrip_next = persontrip
-#                lprevpersontrip.save()
-#            persontrip.persontrip_next = None
-#            lprevpersontrip = persontrip
-#            persontrip.save()
+        # sequencing is difficult to do
+        lprevpersontrip = None
+        for persontrip in persontrips:
+            persontrip.persontrip_prev = lprevpersontrip
+            if lprevpersontrip:
+                lprevpersontrip.persontrip_next = persontrip
+                lprevpersontrip.save()
+            persontrip.persontrip_next = None
+            lprevpersontrip = persontrip
+            persontrip.save()
             
     # from trips rather than logbook entries, which may include events outside the expedition
     expedition.date_from = min([personexpedition.date_from  for personexpedition in expedition.personexpedition_set.all()  if personexpedition.date_from] or [None])
@@ -344,7 +345,7 @@ def LoadLogbooks():
     for year, lloc, parsefunc in yearlinks:
         expedition = models.Expedition.objects.filter(year = year)[0]
         fin = open(os.path.join(expowebbase, lloc))
-        txt = fin.read()
+        txt = fin.read().decode("latin1")
         fin.close()
         parsefunc(year, expedition, txt)
         SetDatesFromLogbookEntries(expedition)
diff --git a/parsers/survex.py b/parsers/survex.py
index 2236d7b..a332c81 100644
--- a/parsers/survex.py
+++ b/parsers/survex.py
@@ -68,7 +68,10 @@ def make_model(name, parent, iter_lines, sf, c, l):
     def saveEnd(survex_file, count):
           if m.start_year and team:
               try:
-                  exp = models.Expedition.objects.get(year = str(m.start_year))
+                  explist = models.Expedition.objects.filter(year = str(m.start_year))
+                  if not explist:
+                      return   # help hack
+                  exp = explist[0]
                   for file_, (role, names) in team:
                       if names.strip("\t").strip(" ") == "both" or names.strip("\t").strip(" ") == "Both":
                           names = reduce(lambda x, y: x + u" & " + y,
diff --git a/urls.py b/urls.py
index fd148c8..85a78a1 100644
--- a/urls.py
+++ b/urls.py
@@ -30,7 +30,6 @@ urlpatterns = patterns('',
     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'^survexblock/(.+)$',  views_caves.survexblock,    name="survexblock"),
     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/?$', object_list, {'queryset':CaveDescription.objects.all(),'template_name':'object_list.html'}, name="cavedescriptions"),
@@ -46,12 +45,6 @@ urlpatterns = patterns('',
     
     url(r'^cave/(?P<cave_id>[^/]+)/(?P<year>\d\d\d\d)-(?P<qm_id>\d*)(?P<grade>[ABCDX]?)?$', views_caves.qm, name="qm"),
     
-    #url(r'^survex/(.*?)\.index$', views_survex.index, name="survexindex"),
-    url(r'^survex/(?P<survex_file>.*?)\.svx$', svx, name="svx"),
-    (r'^survex/(?P<survex_file>.*)\.3d$', threed),
-    (r'^survex/(?P<survex_file>.*)\.log$', log),
-    (r'^survex/(?P<survex_file>.*)\.err$', err),
-
     
     url(r'^logbooksearch/(.*)/?$', views_logbooks.logbookSearch),
 
@@ -84,11 +77,21 @@ urlpatterns = patterns('',
     (r'^site_media/(?P<path>.*)$', 'django.views.static.serve',
         {'document_root': settings.MEDIA_ROOT, 'show_indexes': True}),
 
-    (r'^survey_files/listdir/(?P<path>.*)$', view_surveys.listdir),
-    (r'^survey_files/download/(?P<path>.*)$', view_surveys.download),
+        
+    url(r'^survexblock/(.+)$',                     views_caves.survexblock, name="survexblock"),
+    url(r'^survexfile/(?P<survex_file>.*?)\.svx$', views_survex.svx,        name="svx"),
+    url(r'^survexfile/(?P<survex_file>.*)\.3d$',   views_survex.threed,     name="threed"),
+    url(r'^survexfile/caves$',                     views_survex.survexcaveslist,name="survexcaveslist"),
+    (r'^survex/(?P<survex_file>.*)\.log$', log),
+    (r'^survex/(?P<survex_file>.*)\.err$', err),
+                
+    (r'^survey_files/listdir/(?P<path>.*)$',       view_surveys.listdir),
+    (r'^survey_files/download/(?P<path>.*)$',      view_surveys.download),
     #(r'^survey_files/upload/(?P<path>.*)$', view_surveys.upload),
 
-    (r'^survey_scans/(?P<path>.*)$', 'django.views.static.serve',
+    
+            
+     (r'^survey_scans/(?P<path>.*)$', 'django.views.static.serve',
         {'document_root': settings.SURVEY_SCANS, 'show_indexes':True}),
 
     (r'^photos/(?P<path>.*)$', 'django.views.static.serve',
diff --git a/utils.py b/utils.py
index 6f62b0c..1970c7b 100644
--- a/utils.py
+++ b/utils.py
@@ -99,7 +99,7 @@ def href_to_wikilinks(matchobj):
     object actually exists.
     """
     res=CaveDescription.objects.filter(long_name__icontains=matchobj.groupdict()['text'])
-    if res[0]:
+    if res and res[0]:
         return r'[[cavedescription:'+res[0].short_name+'|'+res[0].long_name+']]'
     else:
         return matchobj.group()