forked from expo/troggle
Backed out changeset: e80a936faab6
This commit is contained in:
parent
e01507d541
commit
384b0438b4
@ -1,2 +0,0 @@
|
|||||||
def emptyfun():
|
|
||||||
return
|
|
847
core/models.py
847
core/models.py
@ -15,8 +15,849 @@ from django.template import Context, loader
|
|||||||
import settings
|
import settings
|
||||||
getcontext().prec=2 #use 2 significant figures for decimal calculations
|
getcontext().prec=2 #use 2 significant figures for decimal calculations
|
||||||
|
|
||||||
from troggle.core.models_survex import * #ancient models for both survex and other things
|
from troggle.core.models_survex import *
|
||||||
from troggle.core.models_old import *
|
|
||||||
|
|
||||||
|
|
||||||
from troggle.core.models_millenial import * #updated models are here
|
def get_related_by_wikilinks(wiki_text):
|
||||||
|
found=re.findall(settings.QM_PATTERN,wiki_text)
|
||||||
|
res=[]
|
||||||
|
for wikilink in found:
|
||||||
|
qmdict={'urlroot':settings.URL_ROOT,'cave':wikilink[2],'year':wikilink[1],'number':wikilink[3]}
|
||||||
|
try:
|
||||||
|
cave_slugs = CaveSlug.objects.filter(cave__kataster_number = qmdict['cave'])
|
||||||
|
qm=QM.objects.get(found_by__cave_slug__in = cave_slugs,
|
||||||
|
found_by__date__year = qmdict['year'],
|
||||||
|
number = qmdict['number'])
|
||||||
|
res.append(qm)
|
||||||
|
except QM.DoesNotExist:
|
||||||
|
print('fail on '+str(wikilink))
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
try:
|
||||||
|
logging.basicConfig(level=logging.DEBUG,
|
||||||
|
filename=settings.LOGFILE,
|
||||||
|
filemode='w')
|
||||||
|
except:
|
||||||
|
subprocess.call(settings.FIX_PERMISSIONS)
|
||||||
|
logging.basicConfig(level=logging.DEBUG,
|
||||||
|
filename=settings.LOGFILE,
|
||||||
|
filemode='w')
|
||||||
|
|
||||||
|
#This class is for adding fields and methods which all of our models will have.
|
||||||
|
class TroggleModel(models.Model):
|
||||||
|
new_since_parsing = models.BooleanField(default=False, editable=False)
|
||||||
|
non_public = models.BooleanField(default=False)
|
||||||
|
def object_name(self):
|
||||||
|
return self._meta.object_name
|
||||||
|
|
||||||
|
def get_admin_url(self):
|
||||||
|
return urlparse.urljoin(settings.URL_ROOT, "/admin/core/" + self.object_name().lower() + "/" + str(self.pk))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
class TroggleImageModel(ImageModel):
|
||||||
|
new_since_parsing = models.BooleanField(default=False, editable=False)
|
||||||
|
|
||||||
|
def object_name(self):
|
||||||
|
return self._meta.object_name
|
||||||
|
|
||||||
|
def get_admin_url(self):
|
||||||
|
return urlparse.urljoin(settings.URL_ROOT, "/admin/core/" + self.object_name().lower() + "/" + str(self.pk))
|
||||||
|
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
#
|
||||||
|
# single Expedition, usually seen by year
|
||||||
|
#
|
||||||
|
class Expedition(TroggleModel):
|
||||||
|
year = models.CharField(max_length=20, unique=True)
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.year
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ('-year',)
|
||||||
|
get_latest_by = 'year'
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return urlparse.urljoin(settings.URL_ROOT, reverse('expedition', args=[self.year]))
|
||||||
|
|
||||||
|
# construction function. should be moved out
|
||||||
|
def get_expedition_day(self, date):
|
||||||
|
expeditiondays = self.expeditionday_set.filter(date=date)
|
||||||
|
if expeditiondays:
|
||||||
|
assert len(expeditiondays) == 1
|
||||||
|
return expeditiondays[0]
|
||||||
|
res = ExpeditionDay(expedition=self, date=date)
|
||||||
|
res.save()
|
||||||
|
return res
|
||||||
|
|
||||||
|
def day_min(self):
|
||||||
|
res = self.expeditionday_set.all()
|
||||||
|
return res and res[0] or None
|
||||||
|
|
||||||
|
def day_max(self):
|
||||||
|
res = self.expeditionday_set.all()
|
||||||
|
return res and res[len(res) - 1] or None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ExpeditionDay(TroggleModel):
|
||||||
|
expedition = models.ForeignKey("Expedition")
|
||||||
|
date = models.DateField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ('date',)
|
||||||
|
|
||||||
|
def GetPersonTrip(self, personexpedition):
|
||||||
|
personexpeditions = self.persontrip_set.filter(expeditionday=self)
|
||||||
|
return personexpeditions and personexpeditions[0] or None
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# single Person, can go on many years
|
||||||
|
#
|
||||||
|
class Person(TroggleModel):
|
||||||
|
first_name = models.CharField(max_length=100)
|
||||||
|
last_name = models.CharField(max_length=100)
|
||||||
|
is_vfho = models.BooleanField(help_text="VFHO is the Vereines für Höhlenkunde in Obersteier, a nearby Austrian caving club.", default=False)
|
||||||
|
mug_shot = models.CharField(max_length=100, blank=True,null=True)
|
||||||
|
blurb = models.TextField(blank=True,null=True)
|
||||||
|
|
||||||
|
#href = models.CharField(max_length=200)
|
||||||
|
orderref = models.CharField(max_length=200) # for alphabetic
|
||||||
|
|
||||||
|
#the below have been removed and made methods. I'm not sure what the b in bisnotable stands for. - AC 16 Feb
|
||||||
|
#notability = models.FloatField() # for listing the top 20 people
|
||||||
|
#bisnotable = models.BooleanField(default=False)
|
||||||
|
user = models.OneToOneField(User, null=True, blank=True)
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return urlparse.urljoin(settings.URL_ROOT,reverse('person',kwargs={'first_name':self.first_name,'last_name':self.last_name}))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name_plural = "People"
|
||||||
|
ordering = ('orderref',) # "Wookey" makes too complex for: ('last_name', 'first_name')
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
if self.last_name:
|
||||||
|
return "%s %s" % (self.first_name, self.last_name)
|
||||||
|
return self.first_name
|
||||||
|
|
||||||
|
|
||||||
|
def notability(self):
|
||||||
|
notability = Decimal(0)
|
||||||
|
for personexpedition in self.personexpedition_set.all():
|
||||||
|
if not personexpedition.is_guest:
|
||||||
|
notability += Decimal(1) / (2012 - int(personexpedition.expedition.year))
|
||||||
|
return notability
|
||||||
|
|
||||||
|
def bisnotable(self):
|
||||||
|
return self.notability() > Decimal(1)/Decimal(3)
|
||||||
|
|
||||||
|
def surveyedleglength(self):
|
||||||
|
return sum([personexpedition.surveyedleglength() for personexpedition in self.personexpedition_set.all()])
|
||||||
|
|
||||||
|
def first(self):
|
||||||
|
return self.personexpedition_set.order_by('-expedition')[0]
|
||||||
|
def last(self):
|
||||||
|
return self.personexpedition_set.order_by('expedition')[0]
|
||||||
|
|
||||||
|
#def Sethref(self):
|
||||||
|
#if self.last_name:
|
||||||
|
#self.href = self.first_name.lower() + "_" + self.last_name.lower()
|
||||||
|
#self.orderref = self.last_name + " " + self.first_name
|
||||||
|
#else:
|
||||||
|
# self.href = self.first_name.lower()
|
||||||
|
#self.orderref = self.first_name
|
||||||
|
#self.notability = 0.0 # set temporarily
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Person's attenance to one Expo
|
||||||
|
#
|
||||||
|
class PersonExpedition(TroggleModel):
|
||||||
|
expedition = models.ForeignKey(Expedition)
|
||||||
|
person = models.ForeignKey(Person)
|
||||||
|
slugfield = models.SlugField(max_length=50,blank=True,null=True)
|
||||||
|
|
||||||
|
is_guest = models.BooleanField(default=False)
|
||||||
|
COMMITTEE_CHOICES = (
|
||||||
|
('leader','Expo leader'),
|
||||||
|
('medical','Expo medical officer'),
|
||||||
|
('treasurer','Expo treasurer'),
|
||||||
|
('sponsorship','Expo sponsorship coordinator'),
|
||||||
|
('research','Expo research coordinator'),
|
||||||
|
)
|
||||||
|
expo_committee_position = models.CharField(blank=True,null=True,choices=COMMITTEE_CHOICES,max_length=200)
|
||||||
|
nickname = models.CharField(max_length=100,blank=True,null=True)
|
||||||
|
|
||||||
|
def GetPersonroles(self):
|
||||||
|
res = [ ]
|
||||||
|
for personrole in self.personrole_set.order_by('survexblock'):
|
||||||
|
if res and res[-1]['survexpath'] == personrole.survexblock.survexpath:
|
||||||
|
res[-1]['roles'] += ", " + str(personrole.role)
|
||||||
|
else:
|
||||||
|
res.append({'date':personrole.survexblock.date, 'survexpath':personrole.survexblock.survexpath, 'roles':str(personrole.role)})
|
||||||
|
return res
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ('-expedition',)
|
||||||
|
#order_with_respect_to = '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:
|
||||||
|
return "%s (%s) %s" % (self.person.first_name, self.nickname, self.person.last_name)
|
||||||
|
if self.person.last_name:
|
||||||
|
return "%s %s" % (self.person.first_name, self.person.last_name)
|
||||||
|
return self.person.first_name
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
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}))
|
||||||
|
|
||||||
|
def surveyedleglength(self):
|
||||||
|
survexblocks = [personrole.survexblock for personrole in self.personrole_set.all() ]
|
||||||
|
return sum([survexblock.totalleglength for survexblock in set(survexblocks)])
|
||||||
|
|
||||||
|
# would prefer to return actual person trips so we could link to first and last ones
|
||||||
|
def day_min(self):
|
||||||
|
res = self.persontrip_set.aggregate(day_min=Min("expeditionday__date"))
|
||||||
|
return res["day_min"]
|
||||||
|
|
||||||
|
def day_max(self):
|
||||||
|
res = self.persontrip_set.all().aggregate(day_max=Max("expeditionday__date"))
|
||||||
|
return res["day_max"]
|
||||||
|
|
||||||
|
#
|
||||||
|
# Single parsed entry from Logbook
|
||||||
|
#
|
||||||
|
class LogbookEntry(TroggleModel):
|
||||||
|
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.
|
||||||
|
# Re: the above- so this field should be "typist" or something, not "author". - AC 15 jun 09
|
||||||
|
#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_slug = models.SlugField(max_length=50)
|
||||||
|
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"
|
||||||
|
# several PersonTrips point in to this object
|
||||||
|
ordering = ('-date',)
|
||||||
|
|
||||||
|
def __getattribute__(self, item):
|
||||||
|
if item == "cave": #Allow a logbookentries cave to be directly accessed despite not having a proper foreignkey
|
||||||
|
return CaveSlug.objects.get(slug = self.cave_slug).cave
|
||||||
|
return super(LogbookEntry, self).__getattribute__(item)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
if "cave" in kwargs.keys():
|
||||||
|
if kwargs["cave"] is not None:
|
||||||
|
kwargs["cave_slug"] = CaveSlug.objects.get(cave=kwargs["cave"], primary=True).slug
|
||||||
|
kwargs.pop("cave")
|
||||||
|
return super(LogbookEntry, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
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}))
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return "%s: (%s)" % (self.date, self.title)
|
||||||
|
|
||||||
|
def get_next_by_id(self):
|
||||||
|
LogbookEntry.objects.get(id=self.id+1)
|
||||||
|
|
||||||
|
def get_previous_by_id(self):
|
||||||
|
LogbookEntry.objects.get(id=self.id-1)
|
||||||
|
|
||||||
|
def new_QM_number(self):
|
||||||
|
"""Returns """
|
||||||
|
if self.cave:
|
||||||
|
nextQMnumber=self.cave.new_QM_number(self.date.year)
|
||||||
|
else:
|
||||||
|
return none
|
||||||
|
return nextQMnumber
|
||||||
|
|
||||||
|
def new_QM_found_link(self):
|
||||||
|
"""Produces a link to a new QM with the next number filled in and this LogbookEntry set as 'found by' """
|
||||||
|
return settings.URL_ROOT + r'/admin/core/qm/add/?' + r'found_by=' + str(self.pk) +'&number=' + str(self.new_QM_number())
|
||||||
|
|
||||||
|
def DayIndex(self):
|
||||||
|
return list(self.expeditionday.logbookentry_set.all()).index(self)
|
||||||
|
|
||||||
|
#
|
||||||
|
# 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):
|
||||||
|
personexpedition = models.ForeignKey("PersonExpedition",null=True)
|
||||||
|
|
||||||
|
#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(default=False)
|
||||||
|
|
||||||
|
|
||||||
|
# sequencing by person (difficult to solve locally)
|
||||||
|
#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.logbook_entry.date)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##########################################
|
||||||
|
# move following classes into models_cave
|
||||||
|
##########################################
|
||||||
|
|
||||||
|
class Area(TroggleModel):
|
||||||
|
short_name = models.CharField(max_length=100)
|
||||||
|
name = models.CharField(max_length=200, blank=True, null=True)
|
||||||
|
description = models.TextField(blank=True,null=True)
|
||||||
|
parent = models.ForeignKey('Area', blank=True, null=True)
|
||||||
|
def __unicode__(self):
|
||||||
|
if self.parent:
|
||||||
|
return unicode(self.parent) + u" - " + unicode(self.short_name)
|
||||||
|
else:
|
||||||
|
return unicode(self.short_name)
|
||||||
|
def kat_area(self):
|
||||||
|
if self.short_name in ["1623", "1626"]:
|
||||||
|
return self.short_name
|
||||||
|
elif self.parent:
|
||||||
|
return self.parent.kat_area()
|
||||||
|
|
||||||
|
class CaveAndEntrance(models.Model):
|
||||||
|
cave = models.ForeignKey('Cave')
|
||||||
|
entrance = models.ForeignKey('Entrance')
|
||||||
|
entrance_letter = models.CharField(max_length=20,blank=True,null=True)
|
||||||
|
def __unicode__(self):
|
||||||
|
return unicode(self.cave) + unicode(self.entrance_letter)
|
||||||
|
|
||||||
|
class CaveSlug(models.Model):
|
||||||
|
cave = models.ForeignKey('Cave')
|
||||||
|
slug = models.SlugField(max_length=50, unique = True)
|
||||||
|
primary = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
|
||||||
|
class Cave(TroggleModel):
|
||||||
|
# too much here perhaps,
|
||||||
|
official_name = models.CharField(max_length=160)
|
||||||
|
area = models.ManyToManyField(Area, blank=True, null=True)
|
||||||
|
kataster_code = models.CharField(max_length=20,blank=True,null=True)
|
||||||
|
kataster_number = models.CharField(max_length=10,blank=True, null=True)
|
||||||
|
unofficial_number = models.CharField(max_length=60,blank=True, null=True)
|
||||||
|
entrances = models.ManyToManyField('Entrance', through='CaveAndEntrance')
|
||||||
|
explorers = models.TextField(blank=True,null=True)
|
||||||
|
underground_description = models.TextField(blank=True,null=True)
|
||||||
|
equipment = models.TextField(blank=True,null=True)
|
||||||
|
references = models.TextField(blank=True,null=True)
|
||||||
|
survey = models.TextField(blank=True,null=True)
|
||||||
|
kataster_status = models.TextField(blank=True,null=True)
|
||||||
|
underground_centre_line = models.TextField(blank=True,null=True)
|
||||||
|
notes = models.TextField(blank=True,null=True)
|
||||||
|
length = models.CharField(max_length=100,blank=True,null=True)
|
||||||
|
depth = models.CharField(max_length=100,blank=True,null=True)
|
||||||
|
extent = 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)
|
||||||
|
url = models.CharField(max_length=200,blank=True,null=True)
|
||||||
|
filename = models.CharField(max_length=200)
|
||||||
|
|
||||||
|
|
||||||
|
#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)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ('kataster_code', 'unofficial_number')
|
||||||
|
|
||||||
|
def hassurvey(self):
|
||||||
|
if not self.underground_centre_line:
|
||||||
|
return "No"
|
||||||
|
if (self.survey.find("<img") > -1 or self.survey.find("<a") > -1 or self.survey.find("<IMG") > -1 or self.survey.find("<A") > -1):
|
||||||
|
return "Yes"
|
||||||
|
return "Missing"
|
||||||
|
|
||||||
|
def hassurveydata(self):
|
||||||
|
if not self.underground_centre_line:
|
||||||
|
return "No"
|
||||||
|
if self.survex_file:
|
||||||
|
return "Yes"
|
||||||
|
return "Missing"
|
||||||
|
|
||||||
|
def slug(self):
|
||||||
|
primarySlugs = self.caveslug_set.filter(primary = True)
|
||||||
|
if primarySlugs:
|
||||||
|
return primarySlugs[0].slug
|
||||||
|
else:
|
||||||
|
slugs = self.caveslug_set.filter()
|
||||||
|
if slugs:
|
||||||
|
return slugs[0].slug
|
||||||
|
|
||||||
|
def ours(self):
|
||||||
|
return bool(re.search(r'CUCC', self.explorers))
|
||||||
|
|
||||||
|
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:
|
||||||
|
href = self.kataster_number
|
||||||
|
elif self.unofficial_number:
|
||||||
|
href = self.unofficial_number
|
||||||
|
else:
|
||||||
|
href = official_name.lower()
|
||||||
|
#return settings.URL_ROOT + '/cave/' + href + '/'
|
||||||
|
return urlparse.urljoin(settings.URL_ROOT, reverse('cave',kwargs={'cave_id':href,}))
|
||||||
|
|
||||||
|
def __unicode__(self, sep = u": "):
|
||||||
|
return unicode(self.slug())
|
||||||
|
|
||||||
|
def get_QMs(self):
|
||||||
|
return QM.objects.filter(found_by__cave_slug=self.caveslug_set.all())
|
||||||
|
|
||||||
|
def new_QM_number(self, year=datetime.date.today().year):
|
||||||
|
"""Given a cave and the current year, returns the next QM number."""
|
||||||
|
try:
|
||||||
|
res=QM.objects.filter(found_by__date__year=year, found_by__cave=self).order_by('-number')[0]
|
||||||
|
except IndexError:
|
||||||
|
return 1
|
||||||
|
return res.number+1
|
||||||
|
|
||||||
|
def kat_area(self):
|
||||||
|
for a in self.area.all():
|
||||||
|
if a.kat_area():
|
||||||
|
return a.kat_area()
|
||||||
|
|
||||||
|
def entrances(self):
|
||||||
|
return CaveAndEntrance.objects.filter(cave=self)
|
||||||
|
|
||||||
|
def singleentrance(self):
|
||||||
|
return len(CaveAndEntrance.objects.filter(cave=self)) == 1
|
||||||
|
|
||||||
|
def entrancelist(self):
|
||||||
|
rs = []
|
||||||
|
res = ""
|
||||||
|
for e in CaveAndEntrance.objects.filter(cave=self):
|
||||||
|
rs.append(e.entrance_letter)
|
||||||
|
rs.sort()
|
||||||
|
prevR = None
|
||||||
|
n = 0
|
||||||
|
for r in rs:
|
||||||
|
if prevR:
|
||||||
|
if chr(ord(prevR) + 1 ) == r:
|
||||||
|
prevR = r
|
||||||
|
n += 1
|
||||||
|
else:
|
||||||
|
if n == 0:
|
||||||
|
res += ", " + prevR
|
||||||
|
else:
|
||||||
|
res += "–" + prevR
|
||||||
|
else:
|
||||||
|
prevR = r
|
||||||
|
n = 0
|
||||||
|
res += r
|
||||||
|
if n == 0:
|
||||||
|
res += ", " + prevR
|
||||||
|
else:
|
||||||
|
res += "–" + prevR
|
||||||
|
return res
|
||||||
|
|
||||||
|
def writeDataFile(self):
|
||||||
|
try:
|
||||||
|
f = open(os.path.join(settings.CAVEDESCRIPTIONS, self.filename), "w")
|
||||||
|
except:
|
||||||
|
subprocess.call(settings.FIX_PERMISSIONS)
|
||||||
|
f = open(os.path.join(settings.CAVEDESCRIPTIONS, self.filename), "w")
|
||||||
|
t = loader.get_template('dataformat/cave.xml')
|
||||||
|
c = Context({'cave': self})
|
||||||
|
u = t.render(c)
|
||||||
|
u8 = u.encode("utf-8")
|
||||||
|
f.write(u8)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
def getArea(self):
|
||||||
|
areas = self.area.all()
|
||||||
|
lowestareas = list(areas)
|
||||||
|
for area in areas:
|
||||||
|
if area.parent in areas:
|
||||||
|
try:
|
||||||
|
lowestareas.remove(area.parent)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return lowestareas[0]
|
||||||
|
|
||||||
|
def getCaveByReference(reference):
|
||||||
|
areaname, code = reference.split("-", 1)
|
||||||
|
print(areaname, code)
|
||||||
|
area = Area.objects.get(short_name = areaname)
|
||||||
|
print(area)
|
||||||
|
foundCaves = list(Cave.objects.filter(area = area, kataster_number = code).all()) + list(Cave.objects.filter(area = area, unofficial_number = code).all())
|
||||||
|
print(list(foundCaves))
|
||||||
|
assert len(foundCaves) == 1
|
||||||
|
return foundCaves[0]
|
||||||
|
|
||||||
|
class OtherCaveName(TroggleModel):
|
||||||
|
name = models.CharField(max_length=160)
|
||||||
|
cave = models.ForeignKey(Cave)
|
||||||
|
def __unicode__(self):
|
||||||
|
return unicode(self.name)
|
||||||
|
|
||||||
|
class EntranceSlug(models.Model):
|
||||||
|
entrance = models.ForeignKey('Entrance')
|
||||||
|
slug = models.SlugField(max_length=50, unique = True)
|
||||||
|
primary = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
class Entrance(TroggleModel):
|
||||||
|
name = models.CharField(max_length=100, blank=True,null=True)
|
||||||
|
entrance_description = models.TextField(blank=True,null=True)
|
||||||
|
explorers = models.TextField(blank=True,null=True)
|
||||||
|
map_description = models.TextField(blank=True,null=True)
|
||||||
|
location_description = models.TextField(blank=True,null=True)
|
||||||
|
approach = models.TextField(blank=True,null=True)
|
||||||
|
underground_description = models.TextField(blank=True,null=True)
|
||||||
|
photo = models.TextField(blank=True,null=True)
|
||||||
|
MARKING_CHOICES = (
|
||||||
|
('P', 'Paint'),
|
||||||
|
('P?', 'Paint (?)'),
|
||||||
|
('T', 'Tag'),
|
||||||
|
('T?', 'Tag (?)'),
|
||||||
|
('R', 'Needs Retag'),
|
||||||
|
('S', 'Spit'),
|
||||||
|
('S?', 'Spit (?)'),
|
||||||
|
('U', 'Unmarked'),
|
||||||
|
('?', 'Unknown'))
|
||||||
|
marking = models.CharField(max_length=2, choices=MARKING_CHOICES)
|
||||||
|
marking_comment = models.TextField(blank=True,null=True)
|
||||||
|
FINDABLE_CHOICES = (
|
||||||
|
('?', 'To be confirmed ...'),
|
||||||
|
('S', 'Coordinates'),
|
||||||
|
('L', 'Lost'),
|
||||||
|
('R', 'Refindable'))
|
||||||
|
findability = models.CharField(max_length=1, choices=FINDABLE_CHOICES, blank=True, null=True)
|
||||||
|
findability_description = models.TextField(blank=True,null=True)
|
||||||
|
alt = models.TextField(blank=True, null=True)
|
||||||
|
northing = models.TextField(blank=True, null=True)
|
||||||
|
easting = models.TextField(blank=True, null=True)
|
||||||
|
tag_station = models.TextField(blank=True, null=True)
|
||||||
|
exact_station = models.TextField(blank=True, null=True)
|
||||||
|
other_station = models.TextField(blank=True, null=True)
|
||||||
|
other_description = models.TextField(blank=True,null=True)
|
||||||
|
bearings = models.TextField(blank=True,null=True)
|
||||||
|
url = models.CharField(max_length=200,blank=True,null=True)
|
||||||
|
filename = models.CharField(max_length=200)
|
||||||
|
cached_primary_slug = models.CharField(max_length=200,blank=True,null=True)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return unicode(self.slug())
|
||||||
|
|
||||||
|
def exact_location(self):
|
||||||
|
return SurvexStation.objects.lookup(self.exact_station)
|
||||||
|
def other_location(self):
|
||||||
|
return SurvexStation.objects.lookup(self.other_station)
|
||||||
|
|
||||||
|
|
||||||
|
def find_location(self):
|
||||||
|
r = {'': 'To be entered ',
|
||||||
|
'?': 'To be confirmed:',
|
||||||
|
'S': '',
|
||||||
|
'L': 'Lost:',
|
||||||
|
'R': 'Refindable:'}[self.findability]
|
||||||
|
if self.tag_station:
|
||||||
|
try:
|
||||||
|
s = SurvexStation.objects.lookup(self.tag_station)
|
||||||
|
return r + "%0.0fE %0.0fN %0.0fAlt" % (s.x, s.y, s.z)
|
||||||
|
except:
|
||||||
|
return r + "%s Tag Station not in dataset" % self.tag_station
|
||||||
|
if self.exact_station:
|
||||||
|
try:
|
||||||
|
s = SurvexStation.objects.lookup(self.exact_station)
|
||||||
|
return r + "%0.0fE %0.0fN %0.0fAlt" % (s.x, s.y, s.z)
|
||||||
|
except:
|
||||||
|
return r + "%s Exact Station not in dataset" % self.tag_station
|
||||||
|
if self.other_station:
|
||||||
|
try:
|
||||||
|
s = SurvexStation.objects.lookup(self.other_station)
|
||||||
|
return r + "%0.0fE %0.0fN %0.0fAlt %s" % (s.x, s.y, s.z, self.other_description)
|
||||||
|
except:
|
||||||
|
return r + "%s Other Station not in dataset" % self.tag_station
|
||||||
|
if self.FINDABLE_CHOICES == "S":
|
||||||
|
r += "ERROR, Entrance has been surveyed but has no survex point"
|
||||||
|
if self.bearings:
|
||||||
|
return r + self.bearings
|
||||||
|
return r
|
||||||
|
|
||||||
|
def best_station(self):
|
||||||
|
if self.tag_station:
|
||||||
|
return self.tag_station
|
||||||
|
if self.exact_station:
|
||||||
|
return self.exact_station
|
||||||
|
if self.other_station:
|
||||||
|
return self.other_station
|
||||||
|
|
||||||
|
def has_photo(self):
|
||||||
|
if self.photo:
|
||||||
|
if (self.photo.find("<img") > -1 or self.photo.find("<a") > -1 or self.photo.find("<IMG") > -1 or self.photo.find("<A") > -1):
|
||||||
|
return "Yes"
|
||||||
|
else:
|
||||||
|
return "Missing"
|
||||||
|
else:
|
||||||
|
return "No"
|
||||||
|
|
||||||
|
def marking_val(self):
|
||||||
|
for m in self.MARKING_CHOICES:
|
||||||
|
if m[0] == self.marking:
|
||||||
|
return m[1]
|
||||||
|
def findability_val(self):
|
||||||
|
for f in self.FINDABLE_CHOICES:
|
||||||
|
if f[0] == self.findability:
|
||||||
|
return f[1]
|
||||||
|
|
||||||
|
def tag(self):
|
||||||
|
return SurvexStation.objects.lookup(self.tag_station)
|
||||||
|
|
||||||
|
def needs_surface_work(self):
|
||||||
|
return self.findability != "S" or not self.has_photo or self.marking != "T"
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
|
||||||
|
ancestor_titles='/'.join([subcave.title for subcave in self.get_ancestors()])
|
||||||
|
if ancestor_titles:
|
||||||
|
res = '/'.join((self.get_root().cave.get_absolute_url(), ancestor_titles, self.title))
|
||||||
|
|
||||||
|
else:
|
||||||
|
res = '/'.join((self.get_root().cave.get_absolute_url(), self.title))
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
def slug(self):
|
||||||
|
if not self.cached_primary_slug:
|
||||||
|
primarySlugs = self.entranceslug_set.filter(primary = True)
|
||||||
|
if primarySlugs:
|
||||||
|
self.cached_primary_slug = primarySlugs[0].slug
|
||||||
|
self.save()
|
||||||
|
else:
|
||||||
|
slugs = self.entranceslug_set.filter()
|
||||||
|
if slugs:
|
||||||
|
self.cached_primary_slug = slugs[0].slug
|
||||||
|
self.save()
|
||||||
|
return self.cached_primary_slug
|
||||||
|
|
||||||
|
def writeDataFile(self):
|
||||||
|
try:
|
||||||
|
f = open(os.path.join(settings.ENTRANCEDESCRIPTIONS, self.filename), "w")
|
||||||
|
except:
|
||||||
|
subprocess.call(settings.FIX_PERMISSIONS)
|
||||||
|
f = open(os.path.join(settings.ENTRANCEDESCRIPTIONS, self.filename), "w")
|
||||||
|
t = loader.get_template('dataformat/entrance.xml')
|
||||||
|
c = Context({'entrance': self})
|
||||||
|
u = t.render(c)
|
||||||
|
u8 = u.encode("utf-8")
|
||||||
|
f.write(u8)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
class CaveDescription(TroggleModel):
|
||||||
|
short_name = models.CharField(max_length=50, unique = True)
|
||||||
|
long_name = models.CharField(max_length=200, blank=True, null=True)
|
||||||
|
description = models.TextField(blank=True,null=True)
|
||||||
|
linked_subcaves = models.ManyToManyField("NewSubCave", blank=True,null=True)
|
||||||
|
linked_entrances = models.ManyToManyField("Entrance", blank=True,null=True)
|
||||||
|
linked_qms = models.ManyToManyField("QM", blank=True,null=True)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
if self.long_name:
|
||||||
|
return unicode(self.long_name)
|
||||||
|
else:
|
||||||
|
return unicode(self.short_name)
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return urlparse.urljoin(settings.URL_ROOT, reverse('cavedescription', args=(self.short_name,)))
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
"""
|
||||||
|
Overridden save method which stores wikilinks in text as links in database.
|
||||||
|
"""
|
||||||
|
super(CaveDescription, self).save()
|
||||||
|
qm_list=get_related_by_wikilinks(self.description)
|
||||||
|
for qm in qm_list:
|
||||||
|
self.linked_qms.add(qm)
|
||||||
|
super(CaveDescription, self).save()
|
||||||
|
|
||||||
|
class NewSubCave(TroggleModel):
|
||||||
|
name = models.CharField(max_length=200, unique = True)
|
||||||
|
def __unicode__(self):
|
||||||
|
return unicode(self.name)
|
||||||
|
|
||||||
|
class QM(TroggleModel):
|
||||||
|
#based on qm.csv in trunk/expoweb/1623/204 which has the fields:
|
||||||
|
#"Number","Grade","Area","Description","Page reference","Nearest station","Completion description","Comment"
|
||||||
|
found_by = models.ForeignKey(LogbookEntry, related_name='QMs_found',blank=True, null=True )
|
||||||
|
ticked_off_by = models.ForeignKey(LogbookEntry, related_name='QMs_ticked_off',null=True,blank=True)
|
||||||
|
#cave = models.ForeignKey(Cave)
|
||||||
|
#expedition = models.ForeignKey(Expedition)
|
||||||
|
|
||||||
|
number = models.IntegerField(help_text="this is the sequential number in the year", )
|
||||||
|
GRADE_CHOICES=(
|
||||||
|
('A', 'A: Large obvious lead'),
|
||||||
|
('B', 'B: Average lead'),
|
||||||
|
('C', 'C: Tight unpromising lead'),
|
||||||
|
('D', 'D: Dig'),
|
||||||
|
('X', 'X: Unclimbable aven')
|
||||||
|
)
|
||||||
|
grade = models.CharField(max_length=1, choices=GRADE_CHOICES)
|
||||||
|
location_description = models.TextField(blank=True)
|
||||||
|
#should be a foreignkey to surveystation
|
||||||
|
nearest_station_description = models.CharField(max_length=400,null=True,blank=True)
|
||||||
|
nearest_station = models.CharField(max_length=200,blank=True,null=True)
|
||||||
|
area = models.CharField(max_length=100,blank=True,null=True)
|
||||||
|
completion_description = models.TextField(blank=True,null=True)
|
||||||
|
comment=models.TextField(blank=True,null=True)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return u"%s %s" % (self.code(), self.grade)
|
||||||
|
|
||||||
|
def code(self):
|
||||||
|
return u"%s-%s-%s" % (unicode(self.found_by.cave)[6:], self.found_by.date.year, self.number)
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
#return settings.URL_ROOT + '/cave/' + self.found_by.cave.kataster_number + '/' + str(self.found_by.date.year) + '-' + '%02d' %self.number
|
||||||
|
return urlparse.urljoin(settings.URL_ROOT, reverse('qm',kwargs={'cave_id':self.found_by.cave.kataster_number,'year':self.found_by.date.year,'qm_id':self.number,'grade':self.grade}))
|
||||||
|
|
||||||
|
def get_next_by_id(self):
|
||||||
|
return QM.objects.get(id=self.id+1)
|
||||||
|
|
||||||
|
def get_previous_by_id(self):
|
||||||
|
return QM.objects.get(id=self.id-1)
|
||||||
|
|
||||||
|
def wiki_link(self):
|
||||||
|
return u"%s%s%s" % ('[[QM:',self.code(),']]')
|
||||||
|
|
||||||
|
photoFileStorage = FileSystemStorage(location=settings.PHOTOS_ROOT, base_url=settings.PHOTOS_URL)
|
||||||
|
class DPhoto(TroggleImageModel):
|
||||||
|
caption = models.CharField(max_length=1000,blank=True,null=True)
|
||||||
|
contains_logbookentry = models.ForeignKey(LogbookEntry,blank=True,null=True)
|
||||||
|
contains_person = models.ManyToManyField(Person,blank=True,null=True)
|
||||||
|
file = models.ImageField(storage=photoFileStorage, upload_to='.',)
|
||||||
|
is_mugshot = models.BooleanField(default=False)
|
||||||
|
contains_cave = models.ForeignKey(Cave,blank=True,null=True)
|
||||||
|
contains_entrance = models.ForeignKey(Entrance, related_name="photo_file",blank=True,null=True)
|
||||||
|
#nearest_survey_point = models.ForeignKey(SurveyStation,blank=True,null=True)
|
||||||
|
nearest_QM = models.ForeignKey(QM,blank=True,null=True)
|
||||||
|
lon_utm = models.FloatField(blank=True,null=True)
|
||||||
|
lat_utm = models.FloatField(blank=True,null=True)
|
||||||
|
|
||||||
|
class IKOptions:
|
||||||
|
spec_module = 'core.imagekit_specs'
|
||||||
|
cache_dir = 'thumbs'
|
||||||
|
image_field = 'file'
|
||||||
|
|
||||||
|
#content_type = models.ForeignKey(ContentType)
|
||||||
|
#object_id = models.PositiveIntegerField()
|
||||||
|
#location = generic.GenericForeignKey('content_type', 'object_id')
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.caption
|
||||||
|
|
||||||
|
scansFileStorage = FileSystemStorage(location=settings.SURVEY_SCANS, base_url=settings.SURVEYS_URL)
|
||||||
|
def get_scan_path(instance, filename):
|
||||||
|
year=instance.survey.expedition.year
|
||||||
|
#print("WN: ", type(instance.survey.wallet_number), instance.survey.wallet_number, instance.survey.wallet_letter)
|
||||||
|
number=str(instance.survey.wallet_number)
|
||||||
|
if str(instance.survey.wallet_letter) != "None":
|
||||||
|
number=str(instance.survey.wallet_letter) + number #two strings formatting because convention is 2009#01 or 2009#X01
|
||||||
|
return os.path.join('./',year,year+r'#'+number,str(instance.contents)+str(instance.number_in_wallet)+r'.jpg')
|
||||||
|
|
||||||
|
class ScannedImage(TroggleImageModel):
|
||||||
|
file = models.ImageField(storage=scansFileStorage, upload_to=get_scan_path)
|
||||||
|
scanned_by = models.ForeignKey(Person,blank=True, null=True)
|
||||||
|
scanned_on = models.DateField(null=True)
|
||||||
|
survey = models.ForeignKey('Survey')
|
||||||
|
contents = models.CharField(max_length=20,choices=(('notes','notes'),('plan','plan_sketch'),('elevation','elevation_sketch')))
|
||||||
|
number_in_wallet = models.IntegerField(null=True)
|
||||||
|
lon_utm = models.FloatField(blank=True,null=True)
|
||||||
|
lat_utm = models.FloatField(blank=True,null=True)
|
||||||
|
|
||||||
|
class IKOptions:
|
||||||
|
spec_module = 'core.imagekit_specs'
|
||||||
|
cache_dir = 'thumbs'
|
||||||
|
image_field = 'file'
|
||||||
|
#content_type = models.ForeignKey(ContentType)
|
||||||
|
#object_id = models.PositiveIntegerField()
|
||||||
|
#location = generic.GenericForeignKey('content_type', 'object_id')
|
||||||
|
|
||||||
|
#This is an ugly hack to deal with the #s in our survey scan paths. The correct thing is to write a custom file storage backend which calls urlencode on the name for making file.url but not file.path.
|
||||||
|
def correctURL(self):
|
||||||
|
return string.replace(self.file.url,r'#',r'%23')
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return get_scan_path(self,'')
|
||||||
|
|
||||||
|
class Survey(TroggleModel):
|
||||||
|
expedition = models.ForeignKey('Expedition') #REDUNDANT (logbook_entry)
|
||||||
|
wallet_number = models.IntegerField(blank=True,null=True)
|
||||||
|
wallet_letter = models.CharField(max_length=1,blank=True,null=True)
|
||||||
|
comments = models.TextField(blank=True,null=True)
|
||||||
|
location = models.CharField(max_length=400,blank=True,null=True) #REDUNDANT
|
||||||
|
subcave = models.ForeignKey('NewSubCave', blank=True, null=True)
|
||||||
|
#notes_scan = models.ForeignKey('ScannedImage',related_name='notes_scan',blank=True, null=True) #Replaced by contents field of ScannedImage model
|
||||||
|
survex_block = models.OneToOneField('SurvexBlock',blank=True, null=True)
|
||||||
|
logbook_entry = models.ForeignKey('LogbookEntry')
|
||||||
|
centreline_printed_on = models.DateField(blank=True, null=True)
|
||||||
|
centreline_printed_by = models.ForeignKey('Person',related_name='centreline_printed_by',blank=True,null=True)
|
||||||
|
#sketch_scan = models.ForeignKey(ScannedImage,blank=True, null=True) #Replaced by contents field of ScannedImage model
|
||||||
|
tunnel_file = models.FileField(upload_to='surveyXMLfiles',blank=True, null=True)
|
||||||
|
tunnel_main_sketch = models.ForeignKey('Survey',blank=True,null=True)
|
||||||
|
integrated_into_main_sketch_on = models.DateField(blank=True,null=True)
|
||||||
|
integrated_into_main_sketch_by = models.ForeignKey('Person' ,related_name='integrated_into_main_sketch_by', blank=True,null=True)
|
||||||
|
rendered_image = models.ImageField(upload_to='renderedSurveys',blank=True,null=True)
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.expedition.year+"#"+"%02d" % int(self.wallet_number)
|
||||||
|
|
||||||
|
def notes(self):
|
||||||
|
return self.scannedimage_set.filter(contents='notes')
|
||||||
|
|
||||||
|
def plans(self):
|
||||||
|
return self.scannedimage_set.filter(contents='plan')
|
||||||
|
|
||||||
|
def elevations(self):
|
||||||
|
return self.scannedimage_set.filter(contents='elevation')
|
||||||
|
@ -1,864 +0,0 @@
|
|||||||
import urllib, urlparse, string, os, datetime, logging, re
|
|
||||||
import subprocess
|
|
||||||
from django.forms import ModelForm
|
|
||||||
from django.db import models
|
|
||||||
from django.contrib import admin
|
|
||||||
from django.core.files.storage import FileSystemStorage
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
|
||||||
from django.db.models import Min, Max
|
|
||||||
from django.conf import settings
|
|
||||||
from decimal import Decimal, getcontext
|
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
from imagekit.models import ImageModel
|
|
||||||
from django.template import Context, loader
|
|
||||||
import settings
|
|
||||||
getcontext().prec=2 #use 2 significant figures for decimal calculations
|
|
||||||
|
|
||||||
from troggle.core.models_survex import *
|
|
||||||
|
|
||||||
from troggle.core.models_millenial import *
|
|
||||||
|
|
||||||
def get_related_by_wikilinks(wiki_text):
|
|
||||||
found=re.findall(settings.QM_PATTERN,wiki_text)
|
|
||||||
res=[]
|
|
||||||
for wikilink in found:
|
|
||||||
qmdict={'urlroot':settings.URL_ROOT,'cave':wikilink[2],'year':wikilink[1],'number':wikilink[3]}
|
|
||||||
try:
|
|
||||||
cave_slugs = CaveSlug.objects.filter(cave__kataster_number = qmdict['cave'])
|
|
||||||
qm=QM.objects.get(found_by__cave_slug__in = cave_slugs,
|
|
||||||
found_by__date__year = qmdict['year'],
|
|
||||||
number = qmdict['number'])
|
|
||||||
res.append(qm)
|
|
||||||
except QM.DoesNotExist:
|
|
||||||
print('fail on '+str(wikilink))
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
try:
|
|
||||||
logging.basicConfig(level=logging.DEBUG,
|
|
||||||
filename=settings.LOGFILE,
|
|
||||||
filemode='w')
|
|
||||||
except:
|
|
||||||
subprocess.call(settings.FIX_PERMISSIONS)
|
|
||||||
logging.basicConfig(level=logging.DEBUG,
|
|
||||||
filename=settings.LOGFILE,
|
|
||||||
filemode='w')
|
|
||||||
|
|
||||||
#This class is for adding fields and methods which all of our models will have.
|
|
||||||
class TroggleModel(models.Model):
|
|
||||||
new_since_parsing = models.BooleanField(default=False, editable=False)
|
|
||||||
non_public = models.BooleanField(default=False)
|
|
||||||
def object_name(self):
|
|
||||||
return self._meta.object_name
|
|
||||||
|
|
||||||
def get_admin_url(self):
|
|
||||||
return urlparse.urljoin(settings.URL_ROOT, "/admin/core/" + self.object_name().lower() + "/" + str(self.pk))
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
class TroggleImageModel(ImageModel):
|
|
||||||
new_since_parsing = models.BooleanField(default=False, editable=False)
|
|
||||||
|
|
||||||
def object_name(self):
|
|
||||||
return self._meta.object_name
|
|
||||||
|
|
||||||
def get_admin_url(self):
|
|
||||||
return urlparse.urljoin(settings.URL_ROOT, "/admin/core/" + self.object_name().lower() + "/" + str(self.pk))
|
|
||||||
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
#
|
|
||||||
# single Expedition, usually seen by year
|
|
||||||
#
|
|
||||||
class Expedition(TroggleModel):
|
|
||||||
year = models.CharField(max_length=20, unique=True)
|
|
||||||
name = models.CharField(max_length=100)
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return self.year
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
ordering = ('-year',)
|
|
||||||
get_latest_by = 'year'
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
return urlparse.urljoin(settings.URL_ROOT, reverse('expedition', args=[self.year]))
|
|
||||||
|
|
||||||
# construction function. should be moved out
|
|
||||||
def get_expedition_day(self, date):
|
|
||||||
expeditiondays = self.expeditionday_set.filter(date=date)
|
|
||||||
if expeditiondays:
|
|
||||||
assert len(expeditiondays) == 1
|
|
||||||
return expeditiondays[0]
|
|
||||||
res = ExpeditionDay(expedition=self, date=date)
|
|
||||||
res.save()
|
|
||||||
return res
|
|
||||||
|
|
||||||
def day_min(self):
|
|
||||||
res = self.expeditionday_set.all()
|
|
||||||
return res and res[0] or None
|
|
||||||
|
|
||||||
def day_max(self):
|
|
||||||
res = self.expeditionday_set.all()
|
|
||||||
return res and res[len(res) - 1] or None
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ExpeditionDay(TroggleModel):
|
|
||||||
expedition = models.ForeignKey("Expedition")
|
|
||||||
date = models.DateField()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
ordering = ('date',)
|
|
||||||
|
|
||||||
def GetPersonTrip(self, personexpedition):
|
|
||||||
personexpeditions = self.persontrip_set.filter(expeditionday=self)
|
|
||||||
return personexpeditions and personexpeditions[0] or None
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# single Person, can go on many years
|
|
||||||
#
|
|
||||||
class Person(TroggleModel):
|
|
||||||
first_name = models.CharField(max_length=100)
|
|
||||||
last_name = models.CharField(max_length=100)
|
|
||||||
is_vfho = models.BooleanField(help_text="VFHO is the Vereines für Höhlenkunde in Obersteier, a nearby Austrian caving club.", default=False)
|
|
||||||
mug_shot = models.CharField(max_length=100, blank=True,null=True)
|
|
||||||
blurb = models.TextField(blank=True,null=True)
|
|
||||||
|
|
||||||
#href = models.CharField(max_length=200)
|
|
||||||
orderref = models.CharField(max_length=200) # for alphabetic
|
|
||||||
|
|
||||||
#the below have been removed and made methods. I'm not sure what the b in bisnotable stands for. - AC 16 Feb
|
|
||||||
#notability = models.FloatField() # for listing the top 20 people
|
|
||||||
#bisnotable = models.BooleanField(default=False)
|
|
||||||
user = models.OneToOneField(User, null=True, blank=True)
|
|
||||||
def get_absolute_url(self):
|
|
||||||
return urlparse.urljoin(settings.URL_ROOT,reverse('person',kwargs={'first_name':self.first_name,'last_name':self.last_name}))
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name_plural = "People"
|
|
||||||
ordering = ('orderref',) # "Wookey" makes too complex for: ('last_name', 'first_name')
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
if self.last_name:
|
|
||||||
return "%s %s" % (self.first_name, self.last_name)
|
|
||||||
return self.first_name
|
|
||||||
|
|
||||||
|
|
||||||
def notability(self):
|
|
||||||
notability = Decimal(0)
|
|
||||||
for personexpedition in self.personexpedition_set.all():
|
|
||||||
if not personexpedition.is_guest:
|
|
||||||
notability += Decimal(1) / (2012 - int(personexpedition.expedition.year))
|
|
||||||
return notability
|
|
||||||
|
|
||||||
def bisnotable(self):
|
|
||||||
return self.notability() > Decimal(1)/Decimal(3)
|
|
||||||
|
|
||||||
def surveyedleglength(self):
|
|
||||||
return sum([personexpedition.surveyedleglength() for personexpedition in self.personexpedition_set.all()])
|
|
||||||
|
|
||||||
def first(self):
|
|
||||||
return self.personexpedition_set.order_by('-expedition')[0]
|
|
||||||
def last(self):
|
|
||||||
return self.personexpedition_set.order_by('expedition')[0]
|
|
||||||
|
|
||||||
#def Sethref(self):
|
|
||||||
#if self.last_name:
|
|
||||||
#self.href = self.first_name.lower() + "_" + self.last_name.lower()
|
|
||||||
#self.orderref = self.last_name + " " + self.first_name
|
|
||||||
#else:
|
|
||||||
# self.href = self.first_name.lower()
|
|
||||||
#self.orderref = self.first_name
|
|
||||||
#self.notability = 0.0 # set temporarily
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Person's attenance to one Expo
|
|
||||||
#
|
|
||||||
class PersonExpedition(TroggleModel):
|
|
||||||
expedition = models.ForeignKey(Expedition)
|
|
||||||
person = models.ForeignKey(Person)
|
|
||||||
slugfield = models.SlugField(max_length=50,blank=True,null=True)
|
|
||||||
|
|
||||||
is_guest = models.BooleanField(default=False)
|
|
||||||
COMMITTEE_CHOICES = (
|
|
||||||
('leader','Expo leader'),
|
|
||||||
('medical','Expo medical officer'),
|
|
||||||
('treasurer','Expo treasurer'),
|
|
||||||
('sponsorship','Expo sponsorship coordinator'),
|
|
||||||
('research','Expo research coordinator'),
|
|
||||||
)
|
|
||||||
expo_committee_position = models.CharField(blank=True,null=True,choices=COMMITTEE_CHOICES,max_length=200)
|
|
||||||
nickname = models.CharField(max_length=100,blank=True,null=True)
|
|
||||||
|
|
||||||
def GetPersonroles(self):
|
|
||||||
res = [ ]
|
|
||||||
for personrole in self.personrole_set.order_by('survexblock'):
|
|
||||||
if res and res[-1]['survexpath'] == personrole.survexblock.survexpath:
|
|
||||||
res[-1]['roles'] += ", " + str(personrole.role)
|
|
||||||
else:
|
|
||||||
res.append({'date':personrole.survexblock.date, 'survexpath':personrole.survexblock.survexpath, 'roles':str(personrole.role)})
|
|
||||||
return res
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
ordering = ('-expedition',)
|
|
||||||
#order_with_respect_to = '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:
|
|
||||||
return "%s (%s) %s" % (self.person.first_name, self.nickname, self.person.last_name)
|
|
||||||
if self.person.last_name:
|
|
||||||
return "%s %s" % (self.person.first_name, self.person.last_name)
|
|
||||||
return self.person.first_name
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
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}))
|
|
||||||
|
|
||||||
def surveyedleglength(self):
|
|
||||||
survexblocks = [personrole.survexblock for personrole in self.personrole_set.all() ]
|
|
||||||
return sum([survexblock.totalleglength for survexblock in set(survexblocks)])
|
|
||||||
|
|
||||||
# would prefer to return actual person trips so we could link to first and last ones
|
|
||||||
def day_min(self):
|
|
||||||
res = self.persontrip_set.aggregate(day_min=Min("expeditionday__date"))
|
|
||||||
return res["day_min"]
|
|
||||||
|
|
||||||
def day_max(self):
|
|
||||||
res = self.persontrip_set.all().aggregate(day_max=Max("expeditionday__date"))
|
|
||||||
return res["day_max"]
|
|
||||||
|
|
||||||
#
|
|
||||||
# Single parsed entry from Logbook
|
|
||||||
#
|
|
||||||
class LogbookEntry(TroggleModel):
|
|
||||||
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.
|
|
||||||
# Re: the above- so this field should be "typist" or something, not "author". - AC 15 jun 09
|
|
||||||
#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_slug = models.SlugField(max_length=50)
|
|
||||||
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"
|
|
||||||
# several PersonTrips point in to this object
|
|
||||||
ordering = ('-date',)
|
|
||||||
|
|
||||||
def __getattribute__(self, item):
|
|
||||||
if item == "cave": #Allow a logbookentries cave to be directly accessed despite not having a proper foreignkey
|
|
||||||
return CaveSlug.objects.get(slug = self.cave_slug).cave
|
|
||||||
return super(LogbookEntry, self).__getattribute__(item)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
if "cave" in kwargs.keys():
|
|
||||||
if kwargs["cave"] is not None:
|
|
||||||
kwargs["cave_slug"] = CaveSlug.objects.get(cave=kwargs["cave"], primary=True).slug
|
|
||||||
kwargs.pop("cave")
|
|
||||||
return super(LogbookEntry, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
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}))
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return "%s: (%s)" % (self.date, self.title)
|
|
||||||
|
|
||||||
def get_next_by_id(self):
|
|
||||||
LogbookEntry.objects.get(id=self.id+1)
|
|
||||||
|
|
||||||
def get_previous_by_id(self):
|
|
||||||
LogbookEntry.objects.get(id=self.id-1)
|
|
||||||
|
|
||||||
def new_QM_number(self):
|
|
||||||
"""Returns """
|
|
||||||
if self.cave:
|
|
||||||
nextQMnumber=self.cave.new_QM_number(self.date.year)
|
|
||||||
else:
|
|
||||||
return none
|
|
||||||
return nextQMnumber
|
|
||||||
|
|
||||||
def new_QM_found_link(self):
|
|
||||||
"""Produces a link to a new QM with the next number filled in and this LogbookEntry set as 'found by' """
|
|
||||||
return settings.URL_ROOT + r'/admin/core/qm/add/?' + r'found_by=' + str(self.pk) +'&number=' + str(self.new_QM_number())
|
|
||||||
|
|
||||||
def DayIndex(self):
|
|
||||||
return list(self.expeditionday.logbookentry_set.all()).index(self)
|
|
||||||
|
|
||||||
#
|
|
||||||
# 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):
|
|
||||||
personexpedition = models.ForeignKey("PersonExpedition",null=True)
|
|
||||||
|
|
||||||
#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(default=False)
|
|
||||||
|
|
||||||
|
|
||||||
# sequencing by person (difficult to solve locally)
|
|
||||||
#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.logbook_entry.date)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
##########################################
|
|
||||||
# move following classes into models_cave
|
|
||||||
##########################################
|
|
||||||
|
|
||||||
class Area(TroggleModel):
|
|
||||||
short_name = models.CharField(max_length=100)
|
|
||||||
name = models.CharField(max_length=200, blank=True, null=True)
|
|
||||||
description = models.TextField(blank=True,null=True)
|
|
||||||
parent = models.ForeignKey('Area', blank=True, null=True)
|
|
||||||
def __unicode__(self):
|
|
||||||
if self.parent:
|
|
||||||
return unicode(self.parent) + u" - " + unicode(self.short_name)
|
|
||||||
else:
|
|
||||||
return unicode(self.short_name)
|
|
||||||
def kat_area(self):
|
|
||||||
if self.short_name in ["1623", "1626"]:
|
|
||||||
return self.short_name
|
|
||||||
elif self.parent:
|
|
||||||
return self.parent.kat_area()
|
|
||||||
|
|
||||||
class CaveAndEntrance(models.Model):
|
|
||||||
cave = models.ForeignKey('Cave')
|
|
||||||
entrance = models.ForeignKey('Entrance')
|
|
||||||
entrance_letter = models.CharField(max_length=20,blank=True,null=True)
|
|
||||||
def __unicode__(self):
|
|
||||||
return unicode(self.cave) + unicode(self.entrance_letter)
|
|
||||||
|
|
||||||
class CaveSlug(models.Model):
|
|
||||||
cave = models.ForeignKey('Cave')
|
|
||||||
slug = models.SlugField(max_length=50, unique = True)
|
|
||||||
primary = models.BooleanField(default=False)
|
|
||||||
|
|
||||||
|
|
||||||
class Cave(TroggleModel):
|
|
||||||
# too much here perhaps,
|
|
||||||
official_name = models.CharField(max_length=160)
|
|
||||||
area = models.ManyToManyField(Area, blank=True, null=True)
|
|
||||||
kataster_code = models.CharField(max_length=20,blank=True,null=True)
|
|
||||||
kataster_number = models.CharField(max_length=10,blank=True, null=True)
|
|
||||||
unofficial_number = models.CharField(max_length=60,blank=True, null=True)
|
|
||||||
entrances = models.ManyToManyField('Entrance', through='CaveAndEntrance')
|
|
||||||
explorers = models.TextField(blank=True,null=True)
|
|
||||||
underground_description = models.TextField(blank=True,null=True)
|
|
||||||
equipment = models.TextField(blank=True,null=True)
|
|
||||||
references = models.TextField(blank=True,null=True)
|
|
||||||
survey = models.TextField(blank=True,null=True)
|
|
||||||
kataster_status = models.TextField(blank=True,null=True)
|
|
||||||
underground_centre_line = models.TextField(blank=True,null=True)
|
|
||||||
notes = models.TextField(blank=True,null=True)
|
|
||||||
length = models.CharField(max_length=100,blank=True,null=True)
|
|
||||||
depth = models.CharField(max_length=100,blank=True,null=True)
|
|
||||||
extent = 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)
|
|
||||||
url = models.CharField(max_length=200,blank=True,null=True)
|
|
||||||
filename = models.CharField(max_length=200)
|
|
||||||
|
|
||||||
|
|
||||||
#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)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
ordering = ('kataster_code', 'unofficial_number')
|
|
||||||
|
|
||||||
def hassurvey(self):
|
|
||||||
if not self.underground_centre_line:
|
|
||||||
return "No"
|
|
||||||
if (self.survey.find("<img") > -1 or self.survey.find("<a") > -1 or self.survey.find("<IMG") > -1 or self.survey.find("<A") > -1):
|
|
||||||
return "Yes"
|
|
||||||
return "Missing"
|
|
||||||
|
|
||||||
def hassurveydata(self):
|
|
||||||
if not self.underground_centre_line:
|
|
||||||
return "No"
|
|
||||||
if self.survex_file:
|
|
||||||
return "Yes"
|
|
||||||
return "Missing"
|
|
||||||
|
|
||||||
def slug(self):
|
|
||||||
primarySlugs = self.caveslug_set.filter(primary = True)
|
|
||||||
if primarySlugs:
|
|
||||||
return primarySlugs[0].slug
|
|
||||||
else:
|
|
||||||
slugs = self.caveslug_set.filter()
|
|
||||||
if slugs:
|
|
||||||
return slugs[0].slug
|
|
||||||
|
|
||||||
def ours(self):
|
|
||||||
return bool(re.search(r'CUCC', self.explorers))
|
|
||||||
|
|
||||||
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:
|
|
||||||
href = self.kataster_number
|
|
||||||
elif self.unofficial_number:
|
|
||||||
href = self.unofficial_number
|
|
||||||
else:
|
|
||||||
href = official_name.lower()
|
|
||||||
#return settings.URL_ROOT + '/cave/' + href + '/'
|
|
||||||
return urlparse.urljoin(settings.URL_ROOT, reverse('cave',kwargs={'cave_id':href,}))
|
|
||||||
|
|
||||||
def __unicode__(self, sep = u": "):
|
|
||||||
return unicode(self.slug())
|
|
||||||
|
|
||||||
def get_QMs(self):
|
|
||||||
return QM.objects.filter(found_by__cave_slug=self.caveslug_set.all())
|
|
||||||
|
|
||||||
def new_QM_number(self, year=datetime.date.today().year):
|
|
||||||
"""Given a cave and the current year, returns the next QM number."""
|
|
||||||
try:
|
|
||||||
res=QM.objects.filter(found_by__date__year=year, found_by__cave=self).order_by('-number')[0]
|
|
||||||
except IndexError:
|
|
||||||
return 1
|
|
||||||
return res.number+1
|
|
||||||
|
|
||||||
def kat_area(self):
|
|
||||||
for a in self.area.all():
|
|
||||||
if a.kat_area():
|
|
||||||
return a.kat_area()
|
|
||||||
|
|
||||||
def entrances(self):
|
|
||||||
return CaveAndEntrance.objects.filter(cave=self)
|
|
||||||
|
|
||||||
def singleentrance(self):
|
|
||||||
return len(CaveAndEntrance.objects.filter(cave=self)) == 1
|
|
||||||
|
|
||||||
def entrancelist(self):
|
|
||||||
rs = []
|
|
||||||
res = ""
|
|
||||||
for e in CaveAndEntrance.objects.filter(cave=self):
|
|
||||||
rs.append(e.entrance_letter)
|
|
||||||
rs.sort()
|
|
||||||
prevR = None
|
|
||||||
n = 0
|
|
||||||
for r in rs:
|
|
||||||
if prevR:
|
|
||||||
if chr(ord(prevR) + 1 ) == r:
|
|
||||||
prevR = r
|
|
||||||
n += 1
|
|
||||||
else:
|
|
||||||
if n == 0:
|
|
||||||
res += ", " + prevR
|
|
||||||
else:
|
|
||||||
res += "–" + prevR
|
|
||||||
else:
|
|
||||||
prevR = r
|
|
||||||
n = 0
|
|
||||||
res += r
|
|
||||||
if n == 0:
|
|
||||||
res += ", " + prevR
|
|
||||||
else:
|
|
||||||
res += "–" + prevR
|
|
||||||
return res
|
|
||||||
|
|
||||||
def writeDataFile(self):
|
|
||||||
try:
|
|
||||||
f = open(os.path.join(settings.CAVEDESCRIPTIONS, self.filename), "w")
|
|
||||||
except:
|
|
||||||
subprocess.call(settings.FIX_PERMISSIONS)
|
|
||||||
f = open(os.path.join(settings.CAVEDESCRIPTIONS, self.filename), "w")
|
|
||||||
t = loader.get_template('dataformat/cave.xml')
|
|
||||||
c = Context({'cave': self})
|
|
||||||
u = t.render(c)
|
|
||||||
u8 = u.encode("utf-8")
|
|
||||||
f.write(u8)
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
def getArea(self):
|
|
||||||
areas = self.area.all()
|
|
||||||
lowestareas = list(areas)
|
|
||||||
for area in areas:
|
|
||||||
if area.parent in areas:
|
|
||||||
try:
|
|
||||||
lowestareas.remove(area.parent)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
return lowestareas[0]
|
|
||||||
|
|
||||||
def getCaveByReference(reference):
|
|
||||||
areaname, code = reference.split("-", 1)
|
|
||||||
print(areaname, code)
|
|
||||||
area = Area.objects.get(short_name = areaname)
|
|
||||||
print(area)
|
|
||||||
foundCaves = list(Cave.objects.filter(area = area, kataster_number = code).all()) + list(Cave.objects.filter(area = area, unofficial_number = code).all())
|
|
||||||
print(list(foundCaves))
|
|
||||||
assert len(foundCaves) == 1
|
|
||||||
return foundCaves[0]
|
|
||||||
|
|
||||||
class OtherCaveName(TroggleModel):
|
|
||||||
name = models.CharField(max_length=160)
|
|
||||||
cave = models.ForeignKey(Cave)
|
|
||||||
def __unicode__(self):
|
|
||||||
return unicode(self.name)
|
|
||||||
|
|
||||||
class EntranceSlug(models.Model):
|
|
||||||
entrance = models.ForeignKey('Entrance')
|
|
||||||
slug = models.SlugField(max_length=50, unique = True)
|
|
||||||
primary = models.BooleanField(default=False)
|
|
||||||
|
|
||||||
class Entrance(TroggleModel):
|
|
||||||
name = models.CharField(max_length=100, blank=True,null=True)
|
|
||||||
entrance_description = models.TextField(blank=True,null=True)
|
|
||||||
explorers = models.TextField(blank=True,null=True)
|
|
||||||
map_description = models.TextField(blank=True,null=True)
|
|
||||||
location_description = models.TextField(blank=True,null=True)
|
|
||||||
approach = models.TextField(blank=True,null=True)
|
|
||||||
underground_description = models.TextField(blank=True,null=True)
|
|
||||||
photo = models.TextField(blank=True,null=True)
|
|
||||||
MARKING_CHOICES = (
|
|
||||||
('P', 'Paint'),
|
|
||||||
('P?', 'Paint (?)'),
|
|
||||||
('T', 'Tag'),
|
|
||||||
('T?', 'Tag (?)'),
|
|
||||||
('R', 'Needs Retag'),
|
|
||||||
('S', 'Spit'),
|
|
||||||
('S?', 'Spit (?)'),
|
|
||||||
('U', 'Unmarked'),
|
|
||||||
('?', 'Unknown'))
|
|
||||||
marking = models.CharField(max_length=2, choices=MARKING_CHOICES)
|
|
||||||
marking_comment = models.TextField(blank=True,null=True)
|
|
||||||
FINDABLE_CHOICES = (
|
|
||||||
('?', 'To be confirmed ...'),
|
|
||||||
('S', 'Coordinates'),
|
|
||||||
('L', 'Lost'),
|
|
||||||
('R', 'Refindable'))
|
|
||||||
findability = models.CharField(max_length=1, choices=FINDABLE_CHOICES, blank=True, null=True)
|
|
||||||
findability_description = models.TextField(blank=True,null=True)
|
|
||||||
alt = models.TextField(blank=True, null=True)
|
|
||||||
northing = models.TextField(blank=True, null=True)
|
|
||||||
easting = models.TextField(blank=True, null=True)
|
|
||||||
tag_station = models.TextField(blank=True, null=True)
|
|
||||||
exact_station = models.TextField(blank=True, null=True)
|
|
||||||
other_station = models.TextField(blank=True, null=True)
|
|
||||||
other_description = models.TextField(blank=True,null=True)
|
|
||||||
bearings = models.TextField(blank=True,null=True)
|
|
||||||
url = models.CharField(max_length=200,blank=True,null=True)
|
|
||||||
filename = models.CharField(max_length=200)
|
|
||||||
cached_primary_slug = models.CharField(max_length=200,blank=True,null=True)
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return unicode(self.slug())
|
|
||||||
|
|
||||||
def exact_location(self):
|
|
||||||
return SurvexStation.objects.lookup(self.exact_station)
|
|
||||||
def other_location(self):
|
|
||||||
return SurvexStation.objects.lookup(self.other_station)
|
|
||||||
|
|
||||||
|
|
||||||
def find_location(self):
|
|
||||||
r = {'': 'To be entered ',
|
|
||||||
'?': 'To be confirmed:',
|
|
||||||
'S': '',
|
|
||||||
'L': 'Lost:',
|
|
||||||
'R': 'Refindable:'}[self.findability]
|
|
||||||
if self.tag_station:
|
|
||||||
try:
|
|
||||||
s = SurvexStation.objects.lookup(self.tag_station)
|
|
||||||
return r + "%0.0fE %0.0fN %0.0fAlt" % (s.x, s.y, s.z)
|
|
||||||
except:
|
|
||||||
return r + "%s Tag Station not in dataset" % self.tag_station
|
|
||||||
if self.exact_station:
|
|
||||||
try:
|
|
||||||
s = SurvexStation.objects.lookup(self.exact_station)
|
|
||||||
return r + "%0.0fE %0.0fN %0.0fAlt" % (s.x, s.y, s.z)
|
|
||||||
except:
|
|
||||||
return r + "%s Exact Station not in dataset" % self.tag_station
|
|
||||||
if self.other_station:
|
|
||||||
try:
|
|
||||||
s = SurvexStation.objects.lookup(self.other_station)
|
|
||||||
return r + "%0.0fE %0.0fN %0.0fAlt %s" % (s.x, s.y, s.z, self.other_description)
|
|
||||||
except:
|
|
||||||
return r + "%s Other Station not in dataset" % self.tag_station
|
|
||||||
if self.FINDABLE_CHOICES == "S":
|
|
||||||
r += "ERROR, Entrance has been surveyed but has no survex point"
|
|
||||||
if self.bearings:
|
|
||||||
return r + self.bearings
|
|
||||||
return r
|
|
||||||
|
|
||||||
def best_station(self):
|
|
||||||
if self.tag_station:
|
|
||||||
return self.tag_station
|
|
||||||
if self.exact_station:
|
|
||||||
return self.exact_station
|
|
||||||
if self.other_station:
|
|
||||||
return self.other_station
|
|
||||||
|
|
||||||
def has_photo(self):
|
|
||||||
if self.photo:
|
|
||||||
if (self.photo.find("<img") > -1 or self.photo.find("<a") > -1 or self.photo.find("<IMG") > -1 or self.photo.find("<A") > -1):
|
|
||||||
return "Yes"
|
|
||||||
else:
|
|
||||||
return "Missing"
|
|
||||||
else:
|
|
||||||
return "No"
|
|
||||||
|
|
||||||
def marking_val(self):
|
|
||||||
for m in self.MARKING_CHOICES:
|
|
||||||
if m[0] == self.marking:
|
|
||||||
return m[1]
|
|
||||||
def findability_val(self):
|
|
||||||
for f in self.FINDABLE_CHOICES:
|
|
||||||
if f[0] == self.findability:
|
|
||||||
return f[1]
|
|
||||||
|
|
||||||
def tag(self):
|
|
||||||
return SurvexStation.objects.lookup(self.tag_station)
|
|
||||||
|
|
||||||
def needs_surface_work(self):
|
|
||||||
return self.findability != "S" or not self.has_photo or self.marking != "T"
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
|
|
||||||
ancestor_titles='/'.join([subcave.title for subcave in self.get_ancestors()])
|
|
||||||
if ancestor_titles:
|
|
||||||
res = '/'.join((self.get_root().cave.get_absolute_url(), ancestor_titles, self.title))
|
|
||||||
|
|
||||||
else:
|
|
||||||
res = '/'.join((self.get_root().cave.get_absolute_url(), self.title))
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
def slug(self):
|
|
||||||
if not self.cached_primary_slug:
|
|
||||||
primarySlugs = self.entranceslug_set.filter(primary = True)
|
|
||||||
if primarySlugs:
|
|
||||||
self.cached_primary_slug = primarySlugs[0].slug
|
|
||||||
self.save()
|
|
||||||
else:
|
|
||||||
slugs = self.entranceslug_set.filter()
|
|
||||||
if slugs:
|
|
||||||
self.cached_primary_slug = slugs[0].slug
|
|
||||||
self.save()
|
|
||||||
return self.cached_primary_slug
|
|
||||||
|
|
||||||
def writeDataFile(self):
|
|
||||||
try:
|
|
||||||
f = open(os.path.join(settings.ENTRANCEDESCRIPTIONS, self.filename), "w")
|
|
||||||
except:
|
|
||||||
subprocess.call(settings.FIX_PERMISSIONS)
|
|
||||||
f = open(os.path.join(settings.ENTRANCEDESCRIPTIONS, self.filename), "w")
|
|
||||||
t = loader.get_template('dataformat/entrance.xml')
|
|
||||||
c = Context({'entrance': self})
|
|
||||||
u = t.render(c)
|
|
||||||
u8 = u.encode("utf-8")
|
|
||||||
f.write(u8)
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
class CaveDescription(TroggleModel):
|
|
||||||
short_name = models.CharField(max_length=50, unique = True)
|
|
||||||
long_name = models.CharField(max_length=200, blank=True, null=True)
|
|
||||||
description = models.TextField(blank=True,null=True)
|
|
||||||
linked_subcaves = models.ManyToManyField("NewSubCave", blank=True,null=True)
|
|
||||||
linked_entrances = models.ManyToManyField("Entrance", blank=True,null=True)
|
|
||||||
linked_qms = models.ManyToManyField("QM", blank=True,null=True)
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
if self.long_name:
|
|
||||||
return unicode(self.long_name)
|
|
||||||
else:
|
|
||||||
return unicode(self.short_name)
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
return urlparse.urljoin(settings.URL_ROOT, reverse('cavedescription', args=(self.short_name,)))
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
"""
|
|
||||||
Overridden save method which stores wikilinks in text as links in database.
|
|
||||||
"""
|
|
||||||
super(CaveDescription, self).save()
|
|
||||||
qm_list=get_related_by_wikilinks(self.description)
|
|
||||||
for qm in qm_list:
|
|
||||||
self.linked_qms.add(qm)
|
|
||||||
super(CaveDescription, self).save()
|
|
||||||
|
|
||||||
class NewSubCave(TroggleModel):
|
|
||||||
name = models.CharField(max_length=200, unique = True)
|
|
||||||
def __unicode__(self):
|
|
||||||
return unicode(self.name)
|
|
||||||
|
|
||||||
class QM(TroggleModel):
|
|
||||||
#based on qm.csv in trunk/expoweb/1623/204 which has the fields:
|
|
||||||
#"Number","Grade","Area","Description","Page reference","Nearest station","Completion description","Comment"
|
|
||||||
found_by = models.ForeignKey(LogbookEntry, related_name='QMs_found',blank=True, null=True )
|
|
||||||
ticked_off_by = models.ForeignKey(LogbookEntry, related_name='QMs_ticked_off',null=True,blank=True)
|
|
||||||
#cave = models.ForeignKey(Cave)
|
|
||||||
#expedition = models.ForeignKey(Expedition)
|
|
||||||
|
|
||||||
number = models.IntegerField(help_text="this is the sequential number in the year", )
|
|
||||||
GRADE_CHOICES=(
|
|
||||||
('A', 'A: Large obvious lead'),
|
|
||||||
('B', 'B: Average lead'),
|
|
||||||
('C', 'C: Tight unpromising lead'),
|
|
||||||
('D', 'D: Dig'),
|
|
||||||
('X', 'X: Unclimbable aven')
|
|
||||||
)
|
|
||||||
grade = models.CharField(max_length=1, choices=GRADE_CHOICES)
|
|
||||||
location_description = models.TextField(blank=True)
|
|
||||||
#should be a foreignkey to surveystation
|
|
||||||
nearest_station_description = models.CharField(max_length=400,null=True,blank=True)
|
|
||||||
nearest_station = models.CharField(max_length=200,blank=True,null=True)
|
|
||||||
area = models.CharField(max_length=100,blank=True,null=True)
|
|
||||||
completion_description = models.TextField(blank=True,null=True)
|
|
||||||
comment=models.TextField(blank=True,null=True)
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return u"%s %s" % (self.code(), self.grade)
|
|
||||||
|
|
||||||
def code(self):
|
|
||||||
return u"%s-%s-%s" % (unicode(self.found_by.cave)[6:], self.found_by.date.year, self.number)
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
#return settings.URL_ROOT + '/cave/' + self.found_by.cave.kataster_number + '/' + str(self.found_by.date.year) + '-' + '%02d' %self.number
|
|
||||||
return urlparse.urljoin(settings.URL_ROOT, reverse('qm',kwargs={'cave_id':self.found_by.cave.kataster_number,'year':self.found_by.date.year,'qm_id':self.number,'grade':self.grade}))
|
|
||||||
|
|
||||||
def get_next_by_id(self):
|
|
||||||
return QM.objects.get(id=self.id+1)
|
|
||||||
|
|
||||||
def get_previous_by_id(self):
|
|
||||||
return QM.objects.get(id=self.id-1)
|
|
||||||
|
|
||||||
def wiki_link(self):
|
|
||||||
return u"%s%s%s" % ('[[QM:',self.code(),']]')
|
|
||||||
|
|
||||||
photoFileStorage = FileSystemStorage(location=settings.PHOTOS_ROOT, base_url=settings.PHOTOS_URL)
|
|
||||||
class DPhoto(TroggleImageModel):
|
|
||||||
caption = models.CharField(max_length=1000,blank=True,null=True)
|
|
||||||
contains_logbookentry = models.ForeignKey(LogbookEntry,blank=True,null=True)
|
|
||||||
contains_person = models.ManyToManyField(Person,blank=True,null=True)
|
|
||||||
file = models.ImageField(storage=photoFileStorage, upload_to='.',)
|
|
||||||
is_mugshot = models.BooleanField(default=False)
|
|
||||||
contains_cave = models.ForeignKey(Cave,blank=True,null=True)
|
|
||||||
contains_entrance = models.ForeignKey(Entrance, related_name="photo_file",blank=True,null=True)
|
|
||||||
#nearest_survey_point = models.ForeignKey(SurveyStation,blank=True,null=True)
|
|
||||||
nearest_QM = models.ForeignKey(QM,blank=True,null=True)
|
|
||||||
lon_utm = models.FloatField(blank=True,null=True)
|
|
||||||
lat_utm = models.FloatField(blank=True,null=True)
|
|
||||||
|
|
||||||
class IKOptions:
|
|
||||||
spec_module = 'core.imagekit_specs'
|
|
||||||
cache_dir = 'thumbs'
|
|
||||||
image_field = 'file'
|
|
||||||
|
|
||||||
#content_type = models.ForeignKey(ContentType)
|
|
||||||
#object_id = models.PositiveIntegerField()
|
|
||||||
#location = generic.GenericForeignKey('content_type', 'object_id')
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return self.caption
|
|
||||||
|
|
||||||
scansFileStorage = FileSystemStorage(location=settings.SURVEY_SCANS, base_url=settings.SURVEYS_URL)
|
|
||||||
def get_scan_path(instance, filename):
|
|
||||||
year=instance.survey.expedition.year
|
|
||||||
#print("WN: ", type(instance.survey.wallet_number), instance.survey.wallet_number, instance.survey.wallet_letter)
|
|
||||||
number=str(instance.survey.wallet_number)
|
|
||||||
if str(instance.survey.wallet_letter) != "None":
|
|
||||||
number=str(instance.survey.wallet_letter) + number #two strings formatting because convention is 2009#01 or 2009#X01
|
|
||||||
return os.path.join('./',year,year+r'#'+number,str(instance.contents)+str(instance.number_in_wallet)+r'.jpg')
|
|
||||||
|
|
||||||
class ScannedImage(TroggleImageModel):
|
|
||||||
file = models.ImageField(storage=scansFileStorage, upload_to=get_scan_path)
|
|
||||||
scanned_by = models.ForeignKey(Person,blank=True, null=True)
|
|
||||||
scanned_on = models.DateField(null=True)
|
|
||||||
survey = models.ForeignKey('Survey')
|
|
||||||
contents = models.CharField(max_length=20,choices=(('notes','notes'),('plan','plan_sketch'),('elevation','elevation_sketch')))
|
|
||||||
number_in_wallet = models.IntegerField(null=True)
|
|
||||||
lon_utm = models.FloatField(blank=True,null=True)
|
|
||||||
lat_utm = models.FloatField(blank=True,null=True)
|
|
||||||
|
|
||||||
class IKOptions:
|
|
||||||
spec_module = 'core.imagekit_specs'
|
|
||||||
cache_dir = 'thumbs'
|
|
||||||
image_field = 'file'
|
|
||||||
#content_type = models.ForeignKey(ContentType)
|
|
||||||
#object_id = models.PositiveIntegerField()
|
|
||||||
#location = generic.GenericForeignKey('content_type', 'object_id')
|
|
||||||
|
|
||||||
#This is an ugly hack to deal with the #s in our survey scan paths. The correct thing is to write a custom file storage backend which calls urlencode on the name for making file.url but not file.path.
|
|
||||||
def correctURL(self):
|
|
||||||
return string.replace(self.file.url,r'#',r'%23')
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return get_scan_path(self,'')
|
|
||||||
|
|
||||||
class Survey(TroggleModel):
|
|
||||||
expedition = models.ForeignKey('Expedition') #REDUNDANT (logbook_entry)
|
|
||||||
wallet_number = models.IntegerField(blank=True,null=True)
|
|
||||||
wallet_letter = models.CharField(max_length=1,blank=True,null=True)
|
|
||||||
comments = models.TextField(blank=True,null=True)
|
|
||||||
location = models.CharField(max_length=400,blank=True,null=True) #REDUNDANT
|
|
||||||
subcave = models.ForeignKey('NewSubCave', blank=True, null=True)
|
|
||||||
#notes_scan = models.ForeignKey('ScannedImage',related_name='notes_scan',blank=True, null=True) #Replaced by contents field of ScannedImage model
|
|
||||||
survex_block = models.OneToOneField('SurvexBlock',blank=True, null=True)
|
|
||||||
logbook_entry = models.ForeignKey('LogbookEntry')
|
|
||||||
centreline_printed_on = models.DateField(blank=True, null=True)
|
|
||||||
centreline_printed_by = models.ForeignKey('Person',related_name='centreline_printed_by',blank=True,null=True)
|
|
||||||
#sketch_scan = models.ForeignKey(ScannedImage,blank=True, null=True) #Replaced by contents field of ScannedImage model
|
|
||||||
tunnel_file = models.FileField(upload_to='surveyXMLfiles',blank=True, null=True)
|
|
||||||
tunnel_main_sketch = models.ForeignKey('Survey',blank=True,null=True)
|
|
||||||
integrated_into_main_sketch_on = models.DateField(blank=True,null=True)
|
|
||||||
integrated_into_main_sketch_by = models.ForeignKey('Person' ,related_name='integrated_into_main_sketch_by', blank=True,null=True)
|
|
||||||
rendered_image = models.ImageField(upload_to='renderedSurveys',blank=True,null=True)
|
|
||||||
def __unicode__(self):
|
|
||||||
return self.expedition.year+"#"+"%02d" % int(self.wallet_number)
|
|
||||||
|
|
||||||
def notes(self):
|
|
||||||
return self.scannedimage_set.filter(contents='notes')
|
|
||||||
|
|
||||||
def plans(self):
|
|
||||||
return self.scannedimage_set.filter(contents='plan')
|
|
||||||
|
|
||||||
def elevations(self):
|
|
||||||
return self.scannedimage_set.filter(contents='elevation')
|
|
@ -1,66 +0,0 @@
|
|||||||
from django.db import models
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
from troggle.core.methods_millenial import *
|
|
||||||
|
|
||||||
#
|
|
||||||
# This file was created in 2019
|
|
||||||
# It's a result of massive frustration with cluttered database of troggle
|
|
||||||
# Maximal clarity of code was primary goal (previous code had very little comments)
|
|
||||||
# Maximal speed of database rebuild was secondary goal
|
|
||||||
#
|
|
||||||
|
|
||||||
#
|
|
||||||
# The following file will tell you what fields and methods are avaliable inside this database
|
|
||||||
# be carefull you might miss some! ManyToMany fields can be used from the far end as well
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Naming conventions:
|
|
||||||
# (Upper/lower convention)
|
|
||||||
# Class names are writen Udddd_ddd_dddM - they finish with M for backwards compatibility
|
|
||||||
# Fields/methods are written lower_lower_lower
|
|
||||||
#
|
|
||||||
|
|
||||||
class PersonM(models.Model): #instance of this class corresponds to one physical peson
|
|
||||||
name = models.CharField(max_length=100) #just name, talk to wookey if you diagree
|
|
||||||
surveys_made = models.ManyToManyField('SurveyM', related_name='people_surveyed') #links to survey objects that this person made (made=:survex says so)
|
|
||||||
expos_attended = models.ManyToManyField('ExpeditionM', related_name='people_attended') #expos attended by this person (attended=:folk.csv says so)
|
|
||||||
logbook_entries_written = models.ManyToManyField('Logbook_entryM', related_name='people_wrote') #links to logbook chuncks created by a person
|
|
||||||
|
|
||||||
class CaveM(models.Model): #instance of this class corresponds to one 'thing' that people call cave
|
|
||||||
entrance = models.CharField(max_length=100) #UTM string describing ONE(!) entrance. Purpose = findability
|
|
||||||
title = models.TextField() #title given to the topmost survey in survex, numeric name otherwise c.f. name (e.g. 'Fishface')
|
|
||||||
name = models.TextField() #name given to the topmost survey in survex (e.g. '2017-cucc-28')
|
|
||||||
surveys = models.ManyToManyField('SurveyM', related_name='cave_parent') #links to surveys objects that this cave contains
|
|
||||||
survex_file = models.TextField() #gives path to top level survex file
|
|
||||||
total_length = models.FloatField() #holds total length of this cave (as given by cavern)
|
|
||||||
total_depth = models.FloatField() #holds total depth of this cave (as given by cavern)
|
|
||||||
description = models.TextField() #holds decription of the cave
|
|
||||||
def top_camp_distance(self): #returns distance of this cave from topcamp
|
|
||||||
return 0
|
|
||||||
def top_camp_bearing(self): #returns bearing to this cave from topcamp in format 235.5 (float north-based azimuth)
|
|
||||||
return 0
|
|
||||||
def top_camp_bearing_letter(self): #returns bearing to this cave from topcamp in format e.g. 'NE'
|
|
||||||
return 0
|
|
||||||
def last_visit(self): #returns Survey class instance of the most recent visit
|
|
||||||
return 0
|
|
||||||
|
|
||||||
class ExpeditionM(models.Model): #instance of this class corresponds to one expo (usually one year)
|
|
||||||
date = models.CharField(max_length=100) #date in format YYYY.MM.DD-YYYY.MM.DD
|
|
||||||
|
|
||||||
|
|
||||||
class SurveyM(models.Model): #instance of this class corresponds to one .svx file - one trip
|
|
||||||
date = models.CharField(max_length=100) #date of the trip in format YYYY.MM.DD (dated:=date given by .svx file)
|
|
||||||
maxdepth = models.FloatField() #represents max depth of a node in this survey
|
|
||||||
|
|
||||||
class Logbook_entryM(models.Model): #instance of this class corresponds to one bit of logbook (c.f. expo.survex.com/years/2015/logbook.html or simil)
|
|
||||||
date = models.CharField(max_length=100) #date as typed into logbook
|
|
||||||
title = models.TextField() #contents of the logbook chunk
|
|
||||||
|
|
||||||
class Parser_messageM(models.Model): #instance of this class contains one error or warining message produce by any of the parsers
|
|
||||||
parsername = models.CharField(max_length = 20) #name of parser
|
|
||||||
content = models.TextField() #content of message
|
|
||||||
message_type = models.CharField(max_length = 10) # [Error,Info] or similar
|
|
||||||
|
|
@ -1,862 +0,0 @@
|
|||||||
import urllib, urlparse, string, os, datetime, logging, re
|
|
||||||
import subprocess
|
|
||||||
from django.forms import ModelForm
|
|
||||||
from django.db import models
|
|
||||||
from django.contrib import admin
|
|
||||||
from django.core.files.storage import FileSystemStorage
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
|
||||||
from django.db.models import Min, Max
|
|
||||||
from django.conf import settings
|
|
||||||
from decimal import Decimal, getcontext
|
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
from imagekit.models import ImageModel
|
|
||||||
from django.template import Context, loader
|
|
||||||
import settings
|
|
||||||
getcontext().prec=2 #use 2 significant figures for decimal calculations
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_related_by_wikilinks(wiki_text):
|
|
||||||
found=re.findall(settings.QM_PATTERN,wiki_text)
|
|
||||||
res=[]
|
|
||||||
for wikilink in found:
|
|
||||||
qmdict={'urlroot':settings.URL_ROOT,'cave':wikilink[2],'year':wikilink[1],'number':wikilink[3]}
|
|
||||||
try:
|
|
||||||
cave_slugs = CaveSlug.objects.filter(cave__kataster_number = qmdict['cave'])
|
|
||||||
qm=QM.objects.get(found_by__cave_slug__in = cave_slugs,
|
|
||||||
found_by__date__year = qmdict['year'],
|
|
||||||
number = qmdict['number'])
|
|
||||||
res.append(qm)
|
|
||||||
except QM.DoesNotExist:
|
|
||||||
print('fail on '+str(wikilink))
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
try:
|
|
||||||
logging.basicConfig(level=logging.DEBUG,
|
|
||||||
filename=settings.LOGFILE,
|
|
||||||
filemode='w')
|
|
||||||
except:
|
|
||||||
subprocess.call(settings.FIX_PERMISSIONS)
|
|
||||||
logging.basicConfig(level=logging.DEBUG,
|
|
||||||
filename=settings.LOGFILE,
|
|
||||||
filemode='w')
|
|
||||||
|
|
||||||
#This class is for adding fields and methods which all of our models will have.
|
|
||||||
class TroggleModel(models.Model):
|
|
||||||
new_since_parsing = models.BooleanField(default=False, editable=False)
|
|
||||||
non_public = models.BooleanField(default=False)
|
|
||||||
def object_name(self):
|
|
||||||
return self._meta.object_name
|
|
||||||
|
|
||||||
def get_admin_url(self):
|
|
||||||
return urlparse.urljoin(settings.URL_ROOT, "/admin/core/" + self.object_name().lower() + "/" + str(self.pk))
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
class TroggleImageModel(ImageModel):
|
|
||||||
new_since_parsing = models.BooleanField(default=False, editable=False)
|
|
||||||
|
|
||||||
def object_name(self):
|
|
||||||
return self._meta.object_name
|
|
||||||
|
|
||||||
def get_admin_url(self):
|
|
||||||
return urlparse.urljoin(settings.URL_ROOT, "/admin/core/" + self.object_name().lower() + "/" + str(self.pk))
|
|
||||||
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
#
|
|
||||||
# single Expedition, usually seen by year
|
|
||||||
#
|
|
||||||
class Expedition(TroggleModel):
|
|
||||||
year = models.CharField(max_length=20, unique=True)
|
|
||||||
name = models.CharField(max_length=100)
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return self.year
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
ordering = ('-year',)
|
|
||||||
get_latest_by = 'year'
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
return urlparse.urljoin(settings.URL_ROOT, reverse('expedition', args=[self.year]))
|
|
||||||
|
|
||||||
# construction function. should be moved out
|
|
||||||
def get_expedition_day(self, date):
|
|
||||||
expeditiondays = self.expeditionday_set.filter(date=date)
|
|
||||||
if expeditiondays:
|
|
||||||
assert len(expeditiondays) == 1
|
|
||||||
return expeditiondays[0]
|
|
||||||
res = ExpeditionDay(expedition=self, date=date)
|
|
||||||
res.save()
|
|
||||||
return res
|
|
||||||
|
|
||||||
def day_min(self):
|
|
||||||
res = self.expeditionday_set.all()
|
|
||||||
return res and res[0] or None
|
|
||||||
|
|
||||||
def day_max(self):
|
|
||||||
res = self.expeditionday_set.all()
|
|
||||||
return res and res[len(res) - 1] or None
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ExpeditionDay(TroggleModel):
|
|
||||||
expedition = models.ForeignKey("Expedition")
|
|
||||||
date = models.DateField()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
ordering = ('date',)
|
|
||||||
|
|
||||||
def GetPersonTrip(self, personexpedition):
|
|
||||||
personexpeditions = self.persontrip_set.filter(expeditionday=self)
|
|
||||||
return personexpeditions and personexpeditions[0] or None
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# single Person, can go on many years
|
|
||||||
#
|
|
||||||
class Person(TroggleModel):
|
|
||||||
first_name = models.CharField(max_length=100)
|
|
||||||
last_name = models.CharField(max_length=100)
|
|
||||||
is_vfho = models.BooleanField(help_text="VFHO is the Vereines für Höhlenkunde in Obersteier, a nearby Austrian caving club.", default=False)
|
|
||||||
mug_shot = models.CharField(max_length=100, blank=True,null=True)
|
|
||||||
blurb = models.TextField(blank=True,null=True)
|
|
||||||
|
|
||||||
#href = models.CharField(max_length=200)
|
|
||||||
orderref = models.CharField(max_length=200) # for alphabetic
|
|
||||||
|
|
||||||
#the below have been removed and made methods. I'm not sure what the b in bisnotable stands for. - AC 16 Feb
|
|
||||||
#notability = models.FloatField() # for listing the top 20 people
|
|
||||||
#bisnotable = models.BooleanField(default=False)
|
|
||||||
user = models.OneToOneField(User, null=True, blank=True)
|
|
||||||
def get_absolute_url(self):
|
|
||||||
return urlparse.urljoin(settings.URL_ROOT,reverse('person',kwargs={'first_name':self.first_name,'last_name':self.last_name}))
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name_plural = "People"
|
|
||||||
ordering = ('orderref',) # "Wookey" makes too complex for: ('last_name', 'first_name')
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
if self.last_name:
|
|
||||||
return "%s %s" % (self.first_name, self.last_name)
|
|
||||||
return self.first_name
|
|
||||||
|
|
||||||
|
|
||||||
def notability(self):
|
|
||||||
notability = Decimal(0)
|
|
||||||
for personexpedition in self.personexpedition_set.all():
|
|
||||||
if not personexpedition.is_guest:
|
|
||||||
notability += Decimal(1) / (2012 - int(personexpedition.expedition.year))
|
|
||||||
return notability
|
|
||||||
|
|
||||||
def bisnotable(self):
|
|
||||||
return self.notability() > Decimal(1)/Decimal(3)
|
|
||||||
|
|
||||||
def surveyedleglength(self):
|
|
||||||
return sum([personexpedition.surveyedleglength() for personexpedition in self.personexpedition_set.all()])
|
|
||||||
|
|
||||||
def first(self):
|
|
||||||
return self.personexpedition_set.order_by('-expedition')[0]
|
|
||||||
def last(self):
|
|
||||||
return self.personexpedition_set.order_by('expedition')[0]
|
|
||||||
|
|
||||||
#def Sethref(self):
|
|
||||||
#if self.last_name:
|
|
||||||
#self.href = self.first_name.lower() + "_" + self.last_name.lower()
|
|
||||||
#self.orderref = self.last_name + " " + self.first_name
|
|
||||||
#else:
|
|
||||||
# self.href = self.first_name.lower()
|
|
||||||
#self.orderref = self.first_name
|
|
||||||
#self.notability = 0.0 # set temporarily
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Person's attenance to one Expo
|
|
||||||
#
|
|
||||||
class PersonExpedition(TroggleModel):
|
|
||||||
expedition = models.ForeignKey(Expedition)
|
|
||||||
person = models.ForeignKey(Person)
|
|
||||||
slugfield = models.SlugField(max_length=50,blank=True,null=True)
|
|
||||||
|
|
||||||
is_guest = models.BooleanField(default=False)
|
|
||||||
COMMITTEE_CHOICES = (
|
|
||||||
('leader','Expo leader'),
|
|
||||||
('medical','Expo medical officer'),
|
|
||||||
('treasurer','Expo treasurer'),
|
|
||||||
('sponsorship','Expo sponsorship coordinator'),
|
|
||||||
('research','Expo research coordinator'),
|
|
||||||
)
|
|
||||||
expo_committee_position = models.CharField(blank=True,null=True,choices=COMMITTEE_CHOICES,max_length=200)
|
|
||||||
nickname = models.CharField(max_length=100,blank=True,null=True)
|
|
||||||
|
|
||||||
def GetPersonroles(self):
|
|
||||||
res = [ ]
|
|
||||||
for personrole in self.personrole_set.order_by('survexblock'):
|
|
||||||
if res and res[-1]['survexpath'] == personrole.survexblock.survexpath:
|
|
||||||
res[-1]['roles'] += ", " + str(personrole.role)
|
|
||||||
else:
|
|
||||||
res.append({'date':personrole.survexblock.date, 'survexpath':personrole.survexblock.survexpath, 'roles':str(personrole.role)})
|
|
||||||
return res
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
ordering = ('-expedition',)
|
|
||||||
#order_with_respect_to = '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:
|
|
||||||
return "%s (%s) %s" % (self.person.first_name, self.nickname, self.person.last_name)
|
|
||||||
if self.person.last_name:
|
|
||||||
return "%s %s" % (self.person.first_name, self.person.last_name)
|
|
||||||
return self.person.first_name
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
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}))
|
|
||||||
|
|
||||||
def surveyedleglength(self):
|
|
||||||
survexblocks = [personrole.survexblock for personrole in self.personrole_set.all() ]
|
|
||||||
return sum([survexblock.totalleglength for survexblock in set(survexblocks)])
|
|
||||||
|
|
||||||
# would prefer to return actual person trips so we could link to first and last ones
|
|
||||||
def day_min(self):
|
|
||||||
res = self.persontrip_set.aggregate(day_min=Min("expeditionday__date"))
|
|
||||||
return res["day_min"]
|
|
||||||
|
|
||||||
def day_max(self):
|
|
||||||
res = self.persontrip_set.all().aggregate(day_max=Max("expeditionday__date"))
|
|
||||||
return res["day_max"]
|
|
||||||
|
|
||||||
#
|
|
||||||
# Single parsed entry from Logbook
|
|
||||||
#
|
|
||||||
class LogbookEntry(TroggleModel):
|
|
||||||
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.
|
|
||||||
# Re: the above- so this field should be "typist" or something, not "author". - AC 15 jun 09
|
|
||||||
#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_slug = models.SlugField(max_length=50)
|
|
||||||
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"
|
|
||||||
# several PersonTrips point in to this object
|
|
||||||
ordering = ('-date',)
|
|
||||||
|
|
||||||
def __getattribute__(self, item):
|
|
||||||
if item == "cave": #Allow a logbookentries cave to be directly accessed despite not having a proper foreignkey
|
|
||||||
return CaveSlug.objects.get(slug = self.cave_slug).cave
|
|
||||||
return super(LogbookEntry, self).__getattribute__(item)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
if "cave" in kwargs.keys():
|
|
||||||
if kwargs["cave"] is not None:
|
|
||||||
kwargs["cave_slug"] = CaveSlug.objects.get(cave=kwargs["cave"], primary=True).slug
|
|
||||||
kwargs.pop("cave")
|
|
||||||
return super(LogbookEntry, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
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}))
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return "%s: (%s)" % (self.date, self.title)
|
|
||||||
|
|
||||||
def get_next_by_id(self):
|
|
||||||
LogbookEntry.objects.get(id=self.id+1)
|
|
||||||
|
|
||||||
def get_previous_by_id(self):
|
|
||||||
LogbookEntry.objects.get(id=self.id-1)
|
|
||||||
|
|
||||||
def new_QM_number(self):
|
|
||||||
"""Returns """
|
|
||||||
if self.cave:
|
|
||||||
nextQMnumber=self.cave.new_QM_number(self.date.year)
|
|
||||||
else:
|
|
||||||
return none
|
|
||||||
return nextQMnumber
|
|
||||||
|
|
||||||
def new_QM_found_link(self):
|
|
||||||
"""Produces a link to a new QM with the next number filled in and this LogbookEntry set as 'found by' """
|
|
||||||
return settings.URL_ROOT + r'/admin/core/qm/add/?' + r'found_by=' + str(self.pk) +'&number=' + str(self.new_QM_number())
|
|
||||||
|
|
||||||
def DayIndex(self):
|
|
||||||
return list(self.expeditionday.logbookentry_set.all()).index(self)
|
|
||||||
|
|
||||||
#
|
|
||||||
# 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):
|
|
||||||
personexpedition = models.ForeignKey("PersonExpedition",null=True)
|
|
||||||
|
|
||||||
#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(default=False)
|
|
||||||
|
|
||||||
|
|
||||||
# sequencing by person (difficult to solve locally)
|
|
||||||
#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.logbook_entry.date)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
##########################################
|
|
||||||
# move following classes into models_cave
|
|
||||||
##########################################
|
|
||||||
|
|
||||||
class Area(TroggleModel):
|
|
||||||
short_name = models.CharField(max_length=100)
|
|
||||||
name = models.CharField(max_length=200, blank=True, null=True)
|
|
||||||
description = models.TextField(blank=True,null=True)
|
|
||||||
parent = models.ForeignKey('Area', blank=True, null=True)
|
|
||||||
def __unicode__(self):
|
|
||||||
if self.parent:
|
|
||||||
return unicode(self.parent) + u" - " + unicode(self.short_name)
|
|
||||||
else:
|
|
||||||
return unicode(self.short_name)
|
|
||||||
def kat_area(self):
|
|
||||||
if self.short_name in ["1623", "1626"]:
|
|
||||||
return self.short_name
|
|
||||||
elif self.parent:
|
|
||||||
return self.parent.kat_area()
|
|
||||||
|
|
||||||
class CaveAndEntrance(models.Model):
|
|
||||||
cave = models.ForeignKey('Cave')
|
|
||||||
entrance = models.ForeignKey('Entrance')
|
|
||||||
entrance_letter = models.CharField(max_length=20,blank=True,null=True)
|
|
||||||
def __unicode__(self):
|
|
||||||
return unicode(self.cave) + unicode(self.entrance_letter)
|
|
||||||
|
|
||||||
class CaveSlug(models.Model):
|
|
||||||
cave = models.ForeignKey('Cave')
|
|
||||||
slug = models.SlugField(max_length=50, unique = True)
|
|
||||||
primary = models.BooleanField(default=False)
|
|
||||||
|
|
||||||
|
|
||||||
class Cave(TroggleModel):
|
|
||||||
# too much here perhaps,
|
|
||||||
official_name = models.CharField(max_length=160)
|
|
||||||
area = models.ManyToManyField(Area, blank=True, null=True)
|
|
||||||
kataster_code = models.CharField(max_length=20,blank=True,null=True)
|
|
||||||
kataster_number = models.CharField(max_length=10,blank=True, null=True)
|
|
||||||
unofficial_number = models.CharField(max_length=60,blank=True, null=True)
|
|
||||||
entrances = models.ManyToManyField('Entrance', through='CaveAndEntrance')
|
|
||||||
explorers = models.TextField(blank=True,null=True)
|
|
||||||
underground_description = models.TextField(blank=True,null=True)
|
|
||||||
equipment = models.TextField(blank=True,null=True)
|
|
||||||
references = models.TextField(blank=True,null=True)
|
|
||||||
survey = models.TextField(blank=True,null=True)
|
|
||||||
kataster_status = models.TextField(blank=True,null=True)
|
|
||||||
underground_centre_line = models.TextField(blank=True,null=True)
|
|
||||||
notes = models.TextField(blank=True,null=True)
|
|
||||||
length = models.CharField(max_length=100,blank=True,null=True)
|
|
||||||
depth = models.CharField(max_length=100,blank=True,null=True)
|
|
||||||
extent = 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)
|
|
||||||
url = models.CharField(max_length=200,blank=True,null=True)
|
|
||||||
filename = models.CharField(max_length=200)
|
|
||||||
|
|
||||||
|
|
||||||
#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)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
ordering = ('kataster_code', 'unofficial_number')
|
|
||||||
|
|
||||||
def hassurvey(self):
|
|
||||||
if not self.underground_centre_line:
|
|
||||||
return "No"
|
|
||||||
if (self.survey.find("<img") > -1 or self.survey.find("<a") > -1 or self.survey.find("<IMG") > -1 or self.survey.find("<A") > -1):
|
|
||||||
return "Yes"
|
|
||||||
return "Missing"
|
|
||||||
|
|
||||||
def hassurveydata(self):
|
|
||||||
if not self.underground_centre_line:
|
|
||||||
return "No"
|
|
||||||
if self.survex_file:
|
|
||||||
return "Yes"
|
|
||||||
return "Missing"
|
|
||||||
|
|
||||||
def slug(self):
|
|
||||||
primarySlugs = self.caveslug_set.filter(primary = True)
|
|
||||||
if primarySlugs:
|
|
||||||
return primarySlugs[0].slug
|
|
||||||
else:
|
|
||||||
slugs = self.caveslug_set.filter()
|
|
||||||
if slugs:
|
|
||||||
return slugs[0].slug
|
|
||||||
|
|
||||||
def ours(self):
|
|
||||||
return bool(re.search(r'CUCC', self.explorers))
|
|
||||||
|
|
||||||
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:
|
|
||||||
href = self.kataster_number
|
|
||||||
elif self.unofficial_number:
|
|
||||||
href = self.unofficial_number
|
|
||||||
else:
|
|
||||||
href = official_name.lower()
|
|
||||||
#return settings.URL_ROOT + '/cave/' + href + '/'
|
|
||||||
return urlparse.urljoin(settings.URL_ROOT, reverse('cave',kwargs={'cave_id':href,}))
|
|
||||||
|
|
||||||
def __unicode__(self, sep = u": "):
|
|
||||||
return unicode(self.slug())
|
|
||||||
|
|
||||||
def get_QMs(self):
|
|
||||||
return QM.objects.filter(found_by__cave_slug=self.caveslug_set.all())
|
|
||||||
|
|
||||||
def new_QM_number(self, year=datetime.date.today().year):
|
|
||||||
"""Given a cave and the current year, returns the next QM number."""
|
|
||||||
try:
|
|
||||||
res=QM.objects.filter(found_by__date__year=year, found_by__cave=self).order_by('-number')[0]
|
|
||||||
except IndexError:
|
|
||||||
return 1
|
|
||||||
return res.number+1
|
|
||||||
|
|
||||||
def kat_area(self):
|
|
||||||
for a in self.area.all():
|
|
||||||
if a.kat_area():
|
|
||||||
return a.kat_area()
|
|
||||||
|
|
||||||
def entrances(self):
|
|
||||||
return CaveAndEntrance.objects.filter(cave=self)
|
|
||||||
|
|
||||||
def singleentrance(self):
|
|
||||||
return len(CaveAndEntrance.objects.filter(cave=self)) == 1
|
|
||||||
|
|
||||||
def entrancelist(self):
|
|
||||||
rs = []
|
|
||||||
res = ""
|
|
||||||
for e in CaveAndEntrance.objects.filter(cave=self):
|
|
||||||
rs.append(e.entrance_letter)
|
|
||||||
rs.sort()
|
|
||||||
prevR = None
|
|
||||||
n = 0
|
|
||||||
for r in rs:
|
|
||||||
if prevR:
|
|
||||||
if chr(ord(prevR) + 1 ) == r:
|
|
||||||
prevR = r
|
|
||||||
n += 1
|
|
||||||
else:
|
|
||||||
if n == 0:
|
|
||||||
res += ", " + prevR
|
|
||||||
else:
|
|
||||||
res += "–" + prevR
|
|
||||||
else:
|
|
||||||
prevR = r
|
|
||||||
n = 0
|
|
||||||
res += r
|
|
||||||
if n == 0:
|
|
||||||
res += ", " + prevR
|
|
||||||
else:
|
|
||||||
res += "–" + prevR
|
|
||||||
return res
|
|
||||||
|
|
||||||
def writeDataFile(self):
|
|
||||||
try:
|
|
||||||
f = open(os.path.join(settings.CAVEDESCRIPTIONS, self.filename), "w")
|
|
||||||
except:
|
|
||||||
subprocess.call(settings.FIX_PERMISSIONS)
|
|
||||||
f = open(os.path.join(settings.CAVEDESCRIPTIONS, self.filename), "w")
|
|
||||||
t = loader.get_template('dataformat/cave.xml')
|
|
||||||
c = Context({'cave': self})
|
|
||||||
u = t.render(c)
|
|
||||||
u8 = u.encode("utf-8")
|
|
||||||
f.write(u8)
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
def getArea(self):
|
|
||||||
areas = self.area.all()
|
|
||||||
lowestareas = list(areas)
|
|
||||||
for area in areas:
|
|
||||||
if area.parent in areas:
|
|
||||||
try:
|
|
||||||
lowestareas.remove(area.parent)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
return lowestareas[0]
|
|
||||||
|
|
||||||
def getCaveByReference(reference):
|
|
||||||
areaname, code = reference.split("-", 1)
|
|
||||||
print(areaname, code)
|
|
||||||
area = Area.objects.get(short_name = areaname)
|
|
||||||
print(area)
|
|
||||||
foundCaves = list(Cave.objects.filter(area = area, kataster_number = code).all()) + list(Cave.objects.filter(area = area, unofficial_number = code).all())
|
|
||||||
print(list(foundCaves))
|
|
||||||
assert len(foundCaves) == 1
|
|
||||||
return foundCaves[0]
|
|
||||||
|
|
||||||
class OtherCaveName(TroggleModel):
|
|
||||||
name = models.CharField(max_length=160)
|
|
||||||
cave = models.ForeignKey(Cave)
|
|
||||||
def __unicode__(self):
|
|
||||||
return unicode(self.name)
|
|
||||||
|
|
||||||
class EntranceSlug(models.Model):
|
|
||||||
entrance = models.ForeignKey('Entrance')
|
|
||||||
slug = models.SlugField(max_length=50, unique = True)
|
|
||||||
primary = models.BooleanField(default=False)
|
|
||||||
|
|
||||||
class Entrance(TroggleModel):
|
|
||||||
name = models.CharField(max_length=100, blank=True,null=True)
|
|
||||||
entrance_description = models.TextField(blank=True,null=True)
|
|
||||||
explorers = models.TextField(blank=True,null=True)
|
|
||||||
map_description = models.TextField(blank=True,null=True)
|
|
||||||
location_description = models.TextField(blank=True,null=True)
|
|
||||||
approach = models.TextField(blank=True,null=True)
|
|
||||||
underground_description = models.TextField(blank=True,null=True)
|
|
||||||
photo = models.TextField(blank=True,null=True)
|
|
||||||
MARKING_CHOICES = (
|
|
||||||
('P', 'Paint'),
|
|
||||||
('P?', 'Paint (?)'),
|
|
||||||
('T', 'Tag'),
|
|
||||||
('T?', 'Tag (?)'),
|
|
||||||
('R', 'Needs Retag'),
|
|
||||||
('S', 'Spit'),
|
|
||||||
('S?', 'Spit (?)'),
|
|
||||||
('U', 'Unmarked'),
|
|
||||||
('?', 'Unknown'))
|
|
||||||
marking = models.CharField(max_length=2, choices=MARKING_CHOICES)
|
|
||||||
marking_comment = models.TextField(blank=True,null=True)
|
|
||||||
FINDABLE_CHOICES = (
|
|
||||||
('?', 'To be confirmed ...'),
|
|
||||||
('S', 'Coordinates'),
|
|
||||||
('L', 'Lost'),
|
|
||||||
('R', 'Refindable'))
|
|
||||||
findability = models.CharField(max_length=1, choices=FINDABLE_CHOICES, blank=True, null=True)
|
|
||||||
findability_description = models.TextField(blank=True,null=True)
|
|
||||||
alt = models.TextField(blank=True, null=True)
|
|
||||||
northing = models.TextField(blank=True, null=True)
|
|
||||||
easting = models.TextField(blank=True, null=True)
|
|
||||||
tag_station = models.TextField(blank=True, null=True)
|
|
||||||
exact_station = models.TextField(blank=True, null=True)
|
|
||||||
other_station = models.TextField(blank=True, null=True)
|
|
||||||
other_description = models.TextField(blank=True,null=True)
|
|
||||||
bearings = models.TextField(blank=True,null=True)
|
|
||||||
url = models.CharField(max_length=200,blank=True,null=True)
|
|
||||||
filename = models.CharField(max_length=200)
|
|
||||||
cached_primary_slug = models.CharField(max_length=200,blank=True,null=True)
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return unicode(self.slug())
|
|
||||||
|
|
||||||
def exact_location(self):
|
|
||||||
return SurvexStation.objects.lookup(self.exact_station)
|
|
||||||
def other_location(self):
|
|
||||||
return SurvexStation.objects.lookup(self.other_station)
|
|
||||||
|
|
||||||
|
|
||||||
def find_location(self):
|
|
||||||
r = {'': 'To be entered ',
|
|
||||||
'?': 'To be confirmed:',
|
|
||||||
'S': '',
|
|
||||||
'L': 'Lost:',
|
|
||||||
'R': 'Refindable:'}[self.findability]
|
|
||||||
if self.tag_station:
|
|
||||||
try:
|
|
||||||
s = SurvexStation.objects.lookup(self.tag_station)
|
|
||||||
return r + "%0.0fE %0.0fN %0.0fAlt" % (s.x, s.y, s.z)
|
|
||||||
except:
|
|
||||||
return r + "%s Tag Station not in dataset" % self.tag_station
|
|
||||||
if self.exact_station:
|
|
||||||
try:
|
|
||||||
s = SurvexStation.objects.lookup(self.exact_station)
|
|
||||||
return r + "%0.0fE %0.0fN %0.0fAlt" % (s.x, s.y, s.z)
|
|
||||||
except:
|
|
||||||
return r + "%s Exact Station not in dataset" % self.tag_station
|
|
||||||
if self.other_station:
|
|
||||||
try:
|
|
||||||
s = SurvexStation.objects.lookup(self.other_station)
|
|
||||||
return r + "%0.0fE %0.0fN %0.0fAlt %s" % (s.x, s.y, s.z, self.other_description)
|
|
||||||
except:
|
|
||||||
return r + "%s Other Station not in dataset" % self.tag_station
|
|
||||||
if self.FINDABLE_CHOICES == "S":
|
|
||||||
r += "ERROR, Entrance has been surveyed but has no survex point"
|
|
||||||
if self.bearings:
|
|
||||||
return r + self.bearings
|
|
||||||
return r
|
|
||||||
|
|
||||||
def best_station(self):
|
|
||||||
if self.tag_station:
|
|
||||||
return self.tag_station
|
|
||||||
if self.exact_station:
|
|
||||||
return self.exact_station
|
|
||||||
if self.other_station:
|
|
||||||
return self.other_station
|
|
||||||
|
|
||||||
def has_photo(self):
|
|
||||||
if self.photo:
|
|
||||||
if (self.photo.find("<img") > -1 or self.photo.find("<a") > -1 or self.photo.find("<IMG") > -1 or self.photo.find("<A") > -1):
|
|
||||||
return "Yes"
|
|
||||||
else:
|
|
||||||
return "Missing"
|
|
||||||
else:
|
|
||||||
return "No"
|
|
||||||
|
|
||||||
def marking_val(self):
|
|
||||||
for m in self.MARKING_CHOICES:
|
|
||||||
if m[0] == self.marking:
|
|
||||||
return m[1]
|
|
||||||
def findability_val(self):
|
|
||||||
for f in self.FINDABLE_CHOICES:
|
|
||||||
if f[0] == self.findability:
|
|
||||||
return f[1]
|
|
||||||
|
|
||||||
def tag(self):
|
|
||||||
return SurvexStation.objects.lookup(self.tag_station)
|
|
||||||
|
|
||||||
def needs_surface_work(self):
|
|
||||||
return self.findability != "S" or not self.has_photo or self.marking != "T"
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
|
|
||||||
ancestor_titles='/'.join([subcave.title for subcave in self.get_ancestors()])
|
|
||||||
if ancestor_titles:
|
|
||||||
res = '/'.join((self.get_root().cave.get_absolute_url(), ancestor_titles, self.title))
|
|
||||||
|
|
||||||
else:
|
|
||||||
res = '/'.join((self.get_root().cave.get_absolute_url(), self.title))
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
def slug(self):
|
|
||||||
if not self.cached_primary_slug:
|
|
||||||
primarySlugs = self.entranceslug_set.filter(primary = True)
|
|
||||||
if primarySlugs:
|
|
||||||
self.cached_primary_slug = primarySlugs[0].slug
|
|
||||||
self.save()
|
|
||||||
else:
|
|
||||||
slugs = self.entranceslug_set.filter()
|
|
||||||
if slugs:
|
|
||||||
self.cached_primary_slug = slugs[0].slug
|
|
||||||
self.save()
|
|
||||||
return self.cached_primary_slug
|
|
||||||
|
|
||||||
def writeDataFile(self):
|
|
||||||
try:
|
|
||||||
f = open(os.path.join(settings.ENTRANCEDESCRIPTIONS, self.filename), "w")
|
|
||||||
except:
|
|
||||||
subprocess.call(settings.FIX_PERMISSIONS)
|
|
||||||
f = open(os.path.join(settings.ENTRANCEDESCRIPTIONS, self.filename), "w")
|
|
||||||
t = loader.get_template('dataformat/entrance.xml')
|
|
||||||
c = Context({'entrance': self})
|
|
||||||
u = t.render(c)
|
|
||||||
u8 = u.encode("utf-8")
|
|
||||||
f.write(u8)
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
class CaveDescription(TroggleModel):
|
|
||||||
short_name = models.CharField(max_length=50, unique = True)
|
|
||||||
long_name = models.CharField(max_length=200, blank=True, null=True)
|
|
||||||
description = models.TextField(blank=True,null=True)
|
|
||||||
linked_subcaves = models.ManyToManyField("NewSubCave", blank=True,null=True)
|
|
||||||
linked_entrances = models.ManyToManyField("Entrance", blank=True,null=True)
|
|
||||||
linked_qms = models.ManyToManyField("QM", blank=True,null=True)
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
if self.long_name:
|
|
||||||
return unicode(self.long_name)
|
|
||||||
else:
|
|
||||||
return unicode(self.short_name)
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
return urlparse.urljoin(settings.URL_ROOT, reverse('cavedescription', args=(self.short_name,)))
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
"""
|
|
||||||
Overridden save method which stores wikilinks in text as links in database.
|
|
||||||
"""
|
|
||||||
super(CaveDescription, self).save()
|
|
||||||
qm_list=get_related_by_wikilinks(self.description)
|
|
||||||
for qm in qm_list:
|
|
||||||
self.linked_qms.add(qm)
|
|
||||||
super(CaveDescription, self).save()
|
|
||||||
|
|
||||||
class NewSubCave(TroggleModel):
|
|
||||||
name = models.CharField(max_length=200, unique = True)
|
|
||||||
def __unicode__(self):
|
|
||||||
return unicode(self.name)
|
|
||||||
|
|
||||||
class QM(TroggleModel):
|
|
||||||
#based on qm.csv in trunk/expoweb/1623/204 which has the fields:
|
|
||||||
#"Number","Grade","Area","Description","Page reference","Nearest station","Completion description","Comment"
|
|
||||||
found_by = models.ForeignKey(LogbookEntry, related_name='QMs_found',blank=True, null=True )
|
|
||||||
ticked_off_by = models.ForeignKey(LogbookEntry, related_name='QMs_ticked_off',null=True,blank=True)
|
|
||||||
#cave = models.ForeignKey(Cave)
|
|
||||||
#expedition = models.ForeignKey(Expedition)
|
|
||||||
|
|
||||||
number = models.IntegerField(help_text="this is the sequential number in the year", )
|
|
||||||
GRADE_CHOICES=(
|
|
||||||
('A', 'A: Large obvious lead'),
|
|
||||||
('B', 'B: Average lead'),
|
|
||||||
('C', 'C: Tight unpromising lead'),
|
|
||||||
('D', 'D: Dig'),
|
|
||||||
('X', 'X: Unclimbable aven')
|
|
||||||
)
|
|
||||||
grade = models.CharField(max_length=1, choices=GRADE_CHOICES)
|
|
||||||
location_description = models.TextField(blank=True)
|
|
||||||
#should be a foreignkey to surveystation
|
|
||||||
nearest_station_description = models.CharField(max_length=400,null=True,blank=True)
|
|
||||||
nearest_station = models.CharField(max_length=200,blank=True,null=True)
|
|
||||||
area = models.CharField(max_length=100,blank=True,null=True)
|
|
||||||
completion_description = models.TextField(blank=True,null=True)
|
|
||||||
comment=models.TextField(blank=True,null=True)
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return u"%s %s" % (self.code(), self.grade)
|
|
||||||
|
|
||||||
def code(self):
|
|
||||||
return u"%s-%s-%s" % (unicode(self.found_by.cave)[6:], self.found_by.date.year, self.number)
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
#return settings.URL_ROOT + '/cave/' + self.found_by.cave.kataster_number + '/' + str(self.found_by.date.year) + '-' + '%02d' %self.number
|
|
||||||
return urlparse.urljoin(settings.URL_ROOT, reverse('qm',kwargs={'cave_id':self.found_by.cave.kataster_number,'year':self.found_by.date.year,'qm_id':self.number,'grade':self.grade}))
|
|
||||||
|
|
||||||
def get_next_by_id(self):
|
|
||||||
return QM.objects.get(id=self.id+1)
|
|
||||||
|
|
||||||
def get_previous_by_id(self):
|
|
||||||
return QM.objects.get(id=self.id-1)
|
|
||||||
|
|
||||||
def wiki_link(self):
|
|
||||||
return u"%s%s%s" % ('[[QM:',self.code(),']]')
|
|
||||||
|
|
||||||
photoFileStorage = FileSystemStorage(location=settings.PHOTOS_ROOT, base_url=settings.PHOTOS_URL)
|
|
||||||
class DPhoto(TroggleImageModel):
|
|
||||||
caption = models.CharField(max_length=1000,blank=True,null=True)
|
|
||||||
contains_logbookentry = models.ForeignKey(LogbookEntry,blank=True,null=True)
|
|
||||||
contains_person = models.ManyToManyField(Person,blank=True,null=True)
|
|
||||||
file = models.ImageField(storage=photoFileStorage, upload_to='.',)
|
|
||||||
is_mugshot = models.BooleanField(default=False)
|
|
||||||
contains_cave = models.ForeignKey(Cave,blank=True,null=True)
|
|
||||||
contains_entrance = models.ForeignKey(Entrance, related_name="photo_file",blank=True,null=True)
|
|
||||||
#nearest_survey_point = models.ForeignKey(SurveyStation,blank=True,null=True)
|
|
||||||
nearest_QM = models.ForeignKey(QM,blank=True,null=True)
|
|
||||||
lon_utm = models.FloatField(blank=True,null=True)
|
|
||||||
lat_utm = models.FloatField(blank=True,null=True)
|
|
||||||
|
|
||||||
class IKOptions:
|
|
||||||
spec_module = 'core.imagekit_specs'
|
|
||||||
cache_dir = 'thumbs'
|
|
||||||
image_field = 'file'
|
|
||||||
|
|
||||||
#content_type = models.ForeignKey(ContentType)
|
|
||||||
#object_id = models.PositiveIntegerField()
|
|
||||||
#location = generic.GenericForeignKey('content_type', 'object_id')
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return self.caption
|
|
||||||
|
|
||||||
scansFileStorage = FileSystemStorage(location=settings.SURVEY_SCANS, base_url=settings.SURVEYS_URL)
|
|
||||||
def get_scan_path(instance, filename):
|
|
||||||
year=instance.survey.expedition.year
|
|
||||||
#print("WN: ", type(instance.survey.wallet_number), instance.survey.wallet_number, instance.survey.wallet_letter)
|
|
||||||
number=str(instance.survey.wallet_number)
|
|
||||||
if str(instance.survey.wallet_letter) != "None":
|
|
||||||
number=str(instance.survey.wallet_letter) + number #two strings formatting because convention is 2009#01 or 2009#X01
|
|
||||||
return os.path.join('./',year,year+r'#'+number,str(instance.contents)+str(instance.number_in_wallet)+r'.jpg')
|
|
||||||
|
|
||||||
class ScannedImage(TroggleImageModel):
|
|
||||||
file = models.ImageField(storage=scansFileStorage, upload_to=get_scan_path)
|
|
||||||
scanned_by = models.ForeignKey(Person,blank=True, null=True)
|
|
||||||
scanned_on = models.DateField(null=True)
|
|
||||||
survey = models.ForeignKey('Survey')
|
|
||||||
contents = models.CharField(max_length=20,choices=(('notes','notes'),('plan','plan_sketch'),('elevation','elevation_sketch')))
|
|
||||||
number_in_wallet = models.IntegerField(null=True)
|
|
||||||
lon_utm = models.FloatField(blank=True,null=True)
|
|
||||||
lat_utm = models.FloatField(blank=True,null=True)
|
|
||||||
|
|
||||||
class IKOptions:
|
|
||||||
spec_module = 'core.imagekit_specs'
|
|
||||||
cache_dir = 'thumbs'
|
|
||||||
image_field = 'file'
|
|
||||||
#content_type = models.ForeignKey(ContentType)
|
|
||||||
#object_id = models.PositiveIntegerField()
|
|
||||||
#location = generic.GenericForeignKey('content_type', 'object_id')
|
|
||||||
|
|
||||||
#This is an ugly hack to deal with the #s in our survey scan paths. The correct thing is to write a custom file storage backend which calls urlencode on the name for making file.url but not file.path.
|
|
||||||
def correctURL(self):
|
|
||||||
return string.replace(self.file.url,r'#',r'%23')
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return get_scan_path(self,'')
|
|
||||||
|
|
||||||
class Survey(TroggleModel):
|
|
||||||
expedition = models.ForeignKey('Expedition') #REDUNDANT (logbook_entry)
|
|
||||||
wallet_number = models.IntegerField(blank=True,null=True)
|
|
||||||
wallet_letter = models.CharField(max_length=1,blank=True,null=True)
|
|
||||||
comments = models.TextField(blank=True,null=True)
|
|
||||||
location = models.CharField(max_length=400,blank=True,null=True) #REDUNDANT
|
|
||||||
subcave = models.ForeignKey('NewSubCave', blank=True, null=True)
|
|
||||||
#notes_scan = models.ForeignKey('ScannedImage',related_name='notes_scan',blank=True, null=True) #Replaced by contents field of ScannedImage model
|
|
||||||
survex_block = models.OneToOneField('SurvexBlock',blank=True, null=True)
|
|
||||||
logbook_entry = models.ForeignKey('LogbookEntry')
|
|
||||||
centreline_printed_on = models.DateField(blank=True, null=True)
|
|
||||||
centreline_printed_by = models.ForeignKey('Person',related_name='centreline_printed_by',blank=True,null=True)
|
|
||||||
#sketch_scan = models.ForeignKey(ScannedImage,blank=True, null=True) #Replaced by contents field of ScannedImage model
|
|
||||||
tunnel_file = models.FileField(upload_to='surveyXMLfiles',blank=True, null=True)
|
|
||||||
tunnel_main_sketch = models.ForeignKey('Survey',blank=True,null=True)
|
|
||||||
integrated_into_main_sketch_on = models.DateField(blank=True,null=True)
|
|
||||||
integrated_into_main_sketch_by = models.ForeignKey('Person' ,related_name='integrated_into_main_sketch_by', blank=True,null=True)
|
|
||||||
rendered_image = models.ImageField(upload_to='renderedSurveys',blank=True,null=True)
|
|
||||||
def __unicode__(self):
|
|
||||||
return self.expedition.year+"#"+"%02d" % int(self.wallet_number)
|
|
||||||
|
|
||||||
def notes(self):
|
|
||||||
return self.scannedimage_set.filter(contents='notes')
|
|
||||||
|
|
||||||
def plans(self):
|
|
||||||
return self.scannedimage_set.filter(contents='plan')
|
|
||||||
|
|
||||||
def elevations(self):
|
|
||||||
return self.scannedimage_set.filter(contents='elevation')
|
|
@ -40,62 +40,6 @@ survextemplatefile = """; Locn: Totes Gebirge, Austria - Loser/Augst-Eck Plateau
|
|||||||
|
|
||||||
*end [surveyname]"""
|
*end [surveyname]"""
|
||||||
|
|
||||||
|
|
||||||
def millenialcaves(request):
|
|
||||||
cavesdir = os.path.join(settings.SURVEX_DATA, "caves-1623")
|
|
||||||
#cavesdircontents = { }
|
|
||||||
|
|
||||||
onefilecaves = [ ]
|
|
||||||
multifilecaves = [ ]
|
|
||||||
subdircaves = [ ]
|
|
||||||
|
|
||||||
millenialcaves = [ ]
|
|
||||||
|
|
||||||
|
|
||||||
# go through the list and identify the contents of each cave directory
|
|
||||||
for cavedir in os.listdir(cavesdir):
|
|
||||||
if cavedir in ["144", "40"]: #????? RW
|
|
||||||
continue
|
|
||||||
|
|
||||||
gcavedir = os.path.join(cavesdir, cavedir) #directory od 'large' cave
|
|
||||||
|
|
||||||
if os.path.isdir(gcavedir) and cavedir[0] != ".":
|
|
||||||
subdirs, subsvx = identifycavedircontents(gcavedir)
|
|
||||||
survdirobj = [ ]
|
|
||||||
|
|
||||||
for lsubsvx in subsvx:
|
|
||||||
survdirobj.append(("caves-1623/"+cavedir+"/"+lsubsvx, lsubsvx))
|
|
||||||
|
|
||||||
# caves with subdirectories
|
|
||||||
if subdirs:
|
|
||||||
subsurvdirs = [ ]
|
|
||||||
for subdir in subdirs:
|
|
||||||
dsubdirs, dsubsvx = identifycavedircontents(os.path.join(gcavedir, subdir))
|
|
||||||
assert not dsubdirs
|
|
||||||
lsurvdirobj = [ ]
|
|
||||||
for lsubsvx in dsubsvx:
|
|
||||||
lsurvdirobj.append(("caves-1623/"+cavedir+"/"+subdir+"/"+lsubsvx, lsubsvx))
|
|
||||||
subsurvdirs.append((lsurvdirobj[0], lsurvdirobj[1:]))
|
|
||||||
subdircaves.append((cavedir, (survdirobj[0], survdirobj[1:]), subsurvdirs))
|
|
||||||
|
|
||||||
# multifile caves
|
|
||||||
elif len(survdirobj) > 1:
|
|
||||||
multifilecaves.append((survdirobj[0], survdirobj[1:]))
|
|
||||||
# single file caves
|
|
||||||
else:
|
|
||||||
#print("survdirobj = ")
|
|
||||||
#print(survdirobj)
|
|
||||||
onefilecaves.append(survdirobj[0])
|
|
||||||
|
|
||||||
caves = Cave.objects.all()
|
|
||||||
|
|
||||||
return render_to_response('millenialcaves.html', {'settings': settings , 'caves':caves , "onefilecaves":onefilecaves, "multifilecaves":multifilecaves, "subdircaves":subdircaves })
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def ReplaceTabs(stext):
|
def ReplaceTabs(stext):
|
||||||
res = [ ]
|
res = [ ]
|
||||||
|
@ -1,117 +0,0 @@
|
|||||||
import os
|
|
||||||
import time
|
|
||||||
import settings
|
|
||||||
os.environ['PYTHONPATH'] = settings.PYTHON_PATH
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings')
|
|
||||||
from django.core import management
|
|
||||||
from django.db import connection
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.http import HttpResponse
|
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
from troggle.core.models import Cave, Entrance
|
|
||||||
from troggle.core.models import PersonM, SurveyM, CaveM, ExpeditionM, Logbook_entryM
|
|
||||||
import troggle.flatpages.models
|
|
||||||
|
|
||||||
databasename=settings.DATABASES['default']['NAME']
|
|
||||||
expouser=settings.EXPOUSER
|
|
||||||
expouserpass=settings.EXPOUSERPASS
|
|
||||||
expouseremail=settings.EXPOUSER_EMAIL
|
|
||||||
|
|
||||||
def destroy():
|
|
||||||
if settings.DATABASES['default']['ENGINE'] == 'django.db.backends.sqlite3':
|
|
||||||
try:
|
|
||||||
os.remove(databasename)
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
cursor = connection.cursor()
|
|
||||||
cursor.execute("DROP DATABASE %s" % databasename)
|
|
||||||
cursor.execute("CREATE DATABASE %s" % databasename)
|
|
||||||
cursor.execute("ALTER DATABASE %s CHARACTER SET=utf8" % databasename)
|
|
||||||
cursor.execute("USE %s" % databasename)
|
|
||||||
management.call_command('syncdb', interactive=False)
|
|
||||||
user = User.objects.create_user(expouser, expouseremail, expouserpass)
|
|
||||||
user.is_staff = True
|
|
||||||
user.is_superuser = True
|
|
||||||
user.save()
|
|
||||||
print('Nuked the database and rebuilt it. You savage monster')
|
|
||||||
|
|
||||||
def gracefull_flush():
|
|
||||||
CaveM.objects.all().delete()
|
|
||||||
PersonM.objects.all().delete()
|
|
||||||
SurveyM.objects.all().delete()
|
|
||||||
ExpeditionM.objects.all().delete()
|
|
||||||
Logbook_entryM.objects.all().delete()
|
|
||||||
print('Deleted contents of the database, ready to load new stuff :)')
|
|
||||||
|
|
||||||
def load_redirects():
|
|
||||||
for oldURL, newURL in [("indxal.htm", reverse("caveindex"))]:
|
|
||||||
f = troggle.flatpages.models.Redirect(originalURL = oldURL, newURL = newURL)
|
|
||||||
f.save()
|
|
||||||
|
|
||||||
def load_caves():
|
|
||||||
import troggle.parsers.cavesM
|
|
||||||
troggle.parsers.cavesM.load()
|
|
||||||
|
|
||||||
def load_all():
|
|
||||||
load_caves()
|
|
||||||
load_surveys()
|
|
||||||
load_people()
|
|
||||||
load_redirects()
|
|
||||||
load_links()
|
|
||||||
print('Loaded everything. Your database is ready to go :)')
|
|
||||||
|
|
||||||
|
|
||||||
def help():
|
|
||||||
print("""Usage is 'python databaseResetM.py <command>'
|
|
||||||
where command is:
|
|
||||||
UNLOADERS:
|
|
||||||
gracefull_flush - flushes new (M-style) databases contents but keeps tables existing
|
|
||||||
destroy - destroys entire database and builds empty tables
|
|
||||||
|
|
||||||
LOADERS:
|
|
||||||
load_all - loads all tables and links
|
|
||||||
load_caves - loads all caves
|
|
||||||
load_surveys - loads all surveys (corresponds to .svx files)
|
|
||||||
load_people - loads all people
|
|
||||||
load_redirects - load page redirects
|
|
||||||
load_links - loads links between classes (run last! can't link non-existent things)
|
|
||||||
|
|
||||||
OTHER:
|
|
||||||
help - displays this page
|
|
||||||
----------------
|
|
||||||
This is a new version of database management written by RW 2019
|
|
||||||
----------------
|
|
||||||
""")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
import troggle.core.models
|
|
||||||
import sys
|
|
||||||
import django
|
|
||||||
django.setup()
|
|
||||||
if "destroy" in sys.argv:
|
|
||||||
destroy()
|
|
||||||
elif "gracefull_flush" in sys.argv:
|
|
||||||
gracefull_flush()
|
|
||||||
|
|
||||||
elif "load_all" in sys.argv:
|
|
||||||
load_all()
|
|
||||||
elif "load_caves" in sys.argv:
|
|
||||||
load_caves()
|
|
||||||
elif "load_surveys" in sys.argv:
|
|
||||||
load_surveys()
|
|
||||||
elif "load_people" in sys.argv:
|
|
||||||
load_people()
|
|
||||||
elif "load_redirects" in sys.argv:
|
|
||||||
load_redirects()
|
|
||||||
elif "load_links" in sys.argv:
|
|
||||||
load_links()
|
|
||||||
elif "help" in sys.argv:
|
|
||||||
help()
|
|
||||||
else:
|
|
||||||
print("%s not recognised" % sys.argv)
|
|
||||||
help()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
|||||||
body {
|
|
||||||
all: initial;
|
|
||||||
font-size: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
div#inputf {
|
|
||||||
display: inline-block;
|
|
||||||
width: 300px;
|
|
||||||
text-align: justify;
|
|
||||||
margin-top: 0px;
|
|
||||||
margin-bottom: 5px
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu, ul#links{
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-spacing: 0;
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
th {
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: #bbb
|
|
||||||
}
|
|
||||||
|
|
||||||
th, td {
|
|
||||||
padding: 16px;
|
|
||||||
max-height: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr:nth-child(even) {
|
|
||||||
background-color: #f2f2f2
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
|
|
||||||
margin-right: 80px;
|
|
||||||
margin-left: 80px;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 300px
|
|
||||||
}
|
|
||||||
span#mono {
|
|
||||||
font-family: monospace;
|
|
||||||
background-color: #eee;
|
|
||||||
font-size: 120%;
|
|
||||||
}
|
|
@ -1,158 +0,0 @@
|
|||||||
function filterTable(tablename)
|
|
||||||
{
|
|
||||||
table = document.getElementById(tablename);
|
|
||||||
|
|
||||||
mindepth = document.getElementById("CaveDepthMin").value;
|
|
||||||
maxdepth = document.getElementById("CaveDepthMax").value;
|
|
||||||
if(mindepth==0)mindepth=-999999;
|
|
||||||
if(maxdepth==0)maxdepth= 999999;
|
|
||||||
|
|
||||||
minlength = document.getElementById("CaveLengthMin").value;
|
|
||||||
maxlength = document.getElementById("CaveLengthMax").value;
|
|
||||||
if(minlength==0)minlength=-999999;
|
|
||||||
if(maxlength==0)maxlength= 999999;
|
|
||||||
|
|
||||||
visitdate = document.getElementById("VisitDate").value;
|
|
||||||
|
|
||||||
visitor = document.getElementById("Visitor").value;
|
|
||||||
|
|
||||||
cavename = document.getElementById("CaveName").value.toLowerCase();
|
|
||||||
|
|
||||||
incomplete = document.getElementById("Incomplete").checked;
|
|
||||||
|
|
||||||
var regexmode = false;
|
|
||||||
if(visitor[0]=='/' && visitor[visitor.length-1]=='/')
|
|
||||||
{
|
|
||||||
regexmode = true;
|
|
||||||
visitor = new RegExp(visitor.substr(1,visitor.length-2));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
visitor.toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
rows = table.rows;
|
|
||||||
for(i=1; i< rows.length; i++)
|
|
||||||
{
|
|
||||||
name = (rows[i].getElementsByTagName("TD")[0]).innerHTML.toLowerCase();
|
|
||||||
|
|
||||||
depth = (rows[i].getElementsByTagName("TD")[1]).innerHTML.toLowerCase();
|
|
||||||
depth = Number(depth.replace(/[^0-9.]/g,''));
|
|
||||||
|
|
||||||
length = (rows[i].getElementsByTagName("TD")[2]).innerHTML.toLowerCase();
|
|
||||||
length = Number(length.replace(/[^0-9.]/g,''));
|
|
||||||
|
|
||||||
date = (rows[i].getElementsByTagName("TD")[3]).innerHTML.toLowerCase();
|
|
||||||
|
|
||||||
recentvisitor = (rows[i].getElementsByTagName("TD")[4]).innerHTML.toLowerCase();
|
|
||||||
|
|
||||||
if(cavename != "" && !name.includes(cavename))
|
|
||||||
{
|
|
||||||
rows[i].style.visibility = "collapse";
|
|
||||||
}
|
|
||||||
if(depth<mindepth || depth>maxdepth)
|
|
||||||
{
|
|
||||||
rows[i].style.visibility = "collapse";
|
|
||||||
}
|
|
||||||
if(length<minlength || length>maxlength)
|
|
||||||
{
|
|
||||||
rows[i].style.visibility = "collapse";
|
|
||||||
}
|
|
||||||
if(date < visitdate)
|
|
||||||
{
|
|
||||||
rows[i].style.visibility = "collapse";
|
|
||||||
}
|
|
||||||
if(visitor != "" && regexmode && !visitor.test(recentvisitor))
|
|
||||||
{
|
|
||||||
rows[i].style.visibility = "collapse";
|
|
||||||
}
|
|
||||||
if(visitor != "" && !regexmode && !recentvisitor.includes(visitor))
|
|
||||||
{
|
|
||||||
rows[i].style.visibility = "collapse";
|
|
||||||
}
|
|
||||||
|
|
||||||
crow=rows[i].getElementsByTagName("TD");
|
|
||||||
for(var j=0; j<crow.length; j++)
|
|
||||||
{
|
|
||||||
if(crow[j].innerHTML == "" && incomplete)
|
|
||||||
{
|
|
||||||
rows[i].style.visibility = "collapse";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function filterTableReset(tablename)
|
|
||||||
{
|
|
||||||
table = document.getElementById(tablename);
|
|
||||||
rows = table.rows;
|
|
||||||
for(i=1; i< rows.length; i++)
|
|
||||||
{
|
|
||||||
rows[i].style.visibility = "visible";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function isOrdered(kvarray,numeric)
|
|
||||||
{
|
|
||||||
for(var i=0;i<kvarray.length-1;i++)
|
|
||||||
{
|
|
||||||
if(numeric==1 && Number(kvarray[i][0])>Number(kvarray[i+1][0]))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if(numeric!=1 && kvarray[i][0]>kvarray[i+1][0])
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function sortTable(n, tablename, numeric) {
|
|
||||||
table = document.getElementById(tablename);
|
|
||||||
rows = table.rows;
|
|
||||||
var ordering = [];
|
|
||||||
var i;
|
|
||||||
|
|
||||||
//construct key-value pairs for sorting
|
|
||||||
for(i = 1; i < rows.length; i++) //remember header rows
|
|
||||||
{
|
|
||||||
key = rows[i].getElementsByTagName("TD")[n];
|
|
||||||
key = key.innerHTML.toLowerCase();
|
|
||||||
if(numeric==1)
|
|
||||||
{
|
|
||||||
key=key.replace(/[^0-9.]/g,'')
|
|
||||||
}
|
|
||||||
ordering.push([key,i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ascending = isOrdered(ordering,numeric);
|
|
||||||
|
|
||||||
//sort either numerically or alphabetically
|
|
||||||
if(numeric==1)
|
|
||||||
{
|
|
||||||
ordering.sort((x,y) => Number(x[0])-Number(y[0]));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ordering.sort(); //sorts alphabetically
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ascending) ordering.reverse();
|
|
||||||
|
|
||||||
for(i = 0; i < ordering.length; i++) //add sorted list at the end of the table
|
|
||||||
{
|
|
||||||
var keyval = ordering[i];
|
|
||||||
id = keyval[1]; //get rownumber of n^th sorted value
|
|
||||||
cln = rows[id].cloneNode(true); //deep clone of current node
|
|
||||||
table.insertBefore(cln,null); //add n^th row at the end
|
|
||||||
}
|
|
||||||
for(i = 1; i < ordering.length+1; i++) //remove unsorted nodes
|
|
||||||
{
|
|
||||||
table.deleteRow(1);// 0 -> header; 1 -> first row
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,129 +0,0 @@
|
|||||||
|
|
||||||
import troggle.core.models as models #import models for various objects
|
|
||||||
from django.conf import settings
|
|
||||||
import xml.etree.ElementTree as ET #this is used to parse XML's
|
|
||||||
import subprocess
|
|
||||||
import re
|
|
||||||
|
|
||||||
#
|
|
||||||
# This parser has to find several things:
|
|
||||||
# There are files of .html format in expoweb area - they contain some of the important information
|
|
||||||
# There is a similar number of .svx files in loser are - they contain all the measurements
|
|
||||||
#
|
|
||||||
# Previous version was incredibly slow due to various shitty ideas about finding things
|
|
||||||
# and overelayance on python when handling regular expressions, new version delegates heavy lifting to shell
|
|
||||||
# and handles more sophisticated bits only
|
|
||||||
#
|
|
||||||
|
|
||||||
def load():
|
|
||||||
print('Hi! I\'m caves parser. Ready to work')
|
|
||||||
|
|
||||||
print('Loading caves of 1623 area')
|
|
||||||
loadarea('caves-1623/')
|
|
||||||
|
|
||||||
|
|
||||||
def loadarea(areacode):
|
|
||||||
|
|
||||||
|
|
||||||
print('Searching all cave dirs files')
|
|
||||||
basedir = settings.SURVEX_DATA+areacode
|
|
||||||
|
|
||||||
bash('cavern -o'+settings.SURVEX_DATA+' '+settings.SURVEX_DATA+'1623-and-1626.svx')
|
|
||||||
|
|
||||||
cavedirs = bash("find "+basedir+" -maxdepth 1 -type d").splitlines() #this command finds all directories
|
|
||||||
print('Obtained list of directories! (#dirs='+str(len(cavedirs))+')')
|
|
||||||
ndirs = len(cavedirs) #remember number of dirs for nice debug output
|
|
||||||
|
|
||||||
for cavedir in cavedirs:
|
|
||||||
if cavedir==basedir:
|
|
||||||
continue #skip the basedir - a non-proper subdirectory
|
|
||||||
cavename = bash('echo '+cavedir+' | rev | cut -f1 -d \'/\' | rev').splitlines()[0] #get final bit of the directory
|
|
||||||
|
|
||||||
test = bash('if [ ! -f '+cavedir+'/'+cavename+'.svx ] ; then echo MISSING; fi')#test for file exisence
|
|
||||||
if 'MISSING' in test: #send error message to the database
|
|
||||||
msg = models.Parser_messageM(parsername='caves',content=cavedir+'/'+cavename+' MISSING!',message_type='warn')
|
|
||||||
print('Cave missing'+cavename+' :(')
|
|
||||||
msg.save()
|
|
||||||
continue
|
|
||||||
fullname=cavedir+'/'+cavename+'.svx'
|
|
||||||
print('Found cave:'+cavename)
|
|
||||||
cavernout = bash('cavern -q '+fullname) #make cavern process the thing
|
|
||||||
if 'cavern: error:' in cavernout:
|
|
||||||
msg = models.Parser_messageM(parsername='caves',content=cavedir+'/'+cavename+' Survex file messed up!',message_type='warn')
|
|
||||||
print('Fucked svx'+cavename+' :(')
|
|
||||||
msg.save()
|
|
||||||
continue
|
|
||||||
|
|
||||||
cavernout = cavernout.splitlines()
|
|
||||||
depth = float(([x for x in cavernout if ('Total vertical length' in x)][0].split()[-1])[:-2])
|
|
||||||
length = float(([x for x in cavernout if ('Total length' in x)][0].split()[6])[:-1])
|
|
||||||
surveyname = bash('cat '+fullname+' | grep \'\*begin\' | head -n1 | cut -f2 -d \' \' ').splitlines().pop()
|
|
||||||
title = (bash('cat '+fullname+' | grep \'\*title\' | head -n1 | cut -f2 -d \' \' ').splitlines() or ["Not found"])[0]
|
|
||||||
print((('depth','length','surv name'),(depth,length,surveyname)))
|
|
||||||
print('dump3d '+settings.SURVEX_DATA+'1623-and-1626.3d | grep NODE | grep \'\\[\\.'+surveyname+'.*\\]\'')
|
|
||||||
nodes = bash('dump3d '+settings.SURVEX_DATA+'1623-and-1626.3d | grep NODE | grep \'\\[.*\\.'+surveyname+'.*\\]\'').splitlines()
|
|
||||||
entran = [x for x in nodes if ('ENTRANCE' in x) ]
|
|
||||||
print(nodes)
|
|
||||||
|
|
||||||
|
|
||||||
newcave = models.CaveM(survex_file = fullname, total_length = length, name=title, total_depth = depth)
|
|
||||||
newcave.save()
|
|
||||||
#end of reading survex masterfiles
|
|
||||||
|
|
||||||
print ("Reading cave descriptions")
|
|
||||||
cavefiles = bash('find '+settings.CAVEDESCRIPTIONS+' -name \'*.html\'').splitlines()
|
|
||||||
for fn in cavefiles:
|
|
||||||
f = open(fn, "r")
|
|
||||||
print(fn)
|
|
||||||
contents = f.read()
|
|
||||||
|
|
||||||
desc = extractXML(contents,'underground_description')
|
|
||||||
name = re.search(r'>.*<',extractXML(contents,'caveslug')).group()[6:-1]
|
|
||||||
|
|
||||||
if desc==None or name==None:
|
|
||||||
msg = models.Parser_messageM(parsername='caves',content=fn+' Description meesed up!',message_type='warn')
|
|
||||||
print('Fucked description '+fn+' :(')
|
|
||||||
msg.save()
|
|
||||||
continue
|
|
||||||
|
|
||||||
|
|
||||||
updatecave = models.CaveM.objects.filter(survex_file__icontains='/'+name+'.svx')
|
|
||||||
if len(updatecave)>1:
|
|
||||||
print('Non unique solution - skipping. Name:'+name)
|
|
||||||
elif len(updatecave)==0:
|
|
||||||
print('Cave with no survex data'+name)
|
|
||||||
newcave = models.CaveM(description = desc, name = name)
|
|
||||||
newcave.save()
|
|
||||||
else: #exaclty one match
|
|
||||||
updatecave = updatecave[0]
|
|
||||||
updatecave.description = desc
|
|
||||||
if updatecave.name=="Not found":
|
|
||||||
updatecave.name=name
|
|
||||||
updatecave.title=name
|
|
||||||
updatecave.save()
|
|
||||||
|
|
||||||
|
|
||||||
#end of reading cave descriptions
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def extractXML(contents,tag):
|
|
||||||
#find correct lines
|
|
||||||
lines = contents.splitlines()
|
|
||||||
beg = [x for x in lines if ('<'+tag+'>' in x)]
|
|
||||||
end = [x for x in lines if ('</'+tag+'>' in x)]
|
|
||||||
if (not beg) or (not end):
|
|
||||||
return None
|
|
||||||
begi = lines.index(beg[0])
|
|
||||||
endi = lines.index(end[0])
|
|
||||||
if endi!=begi:
|
|
||||||
segment = '\n'.join(lines[begi:endi+1])
|
|
||||||
else:
|
|
||||||
segment = lines[begi:endi+1]
|
|
||||||
return segment[0]
|
|
||||||
|
|
||||||
|
|
||||||
def bash(cmd): #calls command in bash shell, returns output
|
|
||||||
process = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE)
|
|
||||||
output, error = process.communicate()
|
|
||||||
return output
|
|
@ -62,6 +62,8 @@ def LoadPersonsExpos():
|
|||||||
|
|
||||||
# make persons
|
# make persons
|
||||||
print "Loading personexpeditions"
|
print "Loading personexpeditions"
|
||||||
|
#expoers2008 = """Edvin Deadman,Kathryn Hopkins,Djuke Veldhuis,Becka Lawson,Julian Todd,Natalie Uomini,Aaron Curtis,Tony Rooke,Ollie Stevens,Frank Tully,Martin Jahnke,Mark Shinwell,Jess Stirrups,Nial Peters,Serena Povia,Olly Madge,Steve Jones,Pete Harley,Eeva Makiranta,Keith Curtis""".split(",")
|
||||||
|
#expomissing = set(expoers2008)
|
||||||
|
|
||||||
for personline in personreader:
|
for personline in personreader:
|
||||||
name = personline[header["Name"]]
|
name = personline[header["Name"]]
|
||||||
@ -83,7 +85,36 @@ def LoadPersonsExpos():
|
|||||||
nonLookupAttribs = {'nickname':nickname, 'is_guest':(personline[header["Guest"]] == "1")}
|
nonLookupAttribs = {'nickname':nickname, 'is_guest':(personline[header["Guest"]] == "1")}
|
||||||
save_carefully(models.PersonExpedition, lookupAttribs, nonLookupAttribs)
|
save_carefully(models.PersonExpedition, lookupAttribs, nonLookupAttribs)
|
||||||
|
|
||||||
|
|
||||||
|
# this fills in those people for whom 2008 was their first expo
|
||||||
|
#print "Loading personexpeditions 2008"
|
||||||
|
#for name in expomissing:
|
||||||
|
# firstname, lastname = name.split()
|
||||||
|
# is_guest = name in ["Eeva Makiranta", "Keith Curtis"]
|
||||||
|
# print "2008:", name
|
||||||
|
# persons = list(models.Person.objects.filter(first_name=firstname, last_name=lastname))
|
||||||
|
# if not persons:
|
||||||
|
# person = models.Person(first_name=firstname, last_name = lastname, is_vfho = False, mug_shot = "")
|
||||||
|
# #person.Sethref()
|
||||||
|
# person.save()
|
||||||
|
# else:
|
||||||
|
# person = persons[0]
|
||||||
|
# expedition = models.Expedition.objects.get(year="2008")
|
||||||
|
# personexpedition = models.PersonExpedition(person=person, expedition=expedition, nickname="", is_guest=is_guest)
|
||||||
|
# personexpedition.save()
|
||||||
|
|
||||||
|
#Notability is now a method of person. Makes no sense to store it in the database; it would need to be recalculated every time something changes. - AC 16 Feb 09
|
||||||
|
# could rank according to surveying as well
|
||||||
|
#print "Setting person notability"
|
||||||
|
#for person in models.Person.objects.all():
|
||||||
|
#person.notability = 0.0
|
||||||
|
#for personexpedition in person.personexpedition_set.all():
|
||||||
|
#if not personexpedition.is_guest:
|
||||||
|
#person.notability += 1.0 / (2012 - int(personexpedition.expedition.year))
|
||||||
|
#person.bisnotable = person.notability > 0.3 # I don't know how to filter by this
|
||||||
|
#person.save()
|
||||||
|
|
||||||
|
|
||||||
# used in other referencing parser functions
|
# used in other referencing parser functions
|
||||||
# expedition name lookup cached for speed (it's a very big list)
|
# expedition name lookup cached for speed (it's a very big list)
|
||||||
Gpersonexpeditionnamelookup = { }
|
Gpersonexpeditionnamelookup = { }
|
||||||
|
@ -1,123 +0,0 @@
|
|||||||
<html>
|
|
||||||
<head>
|
|
||||||
|
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="{{ settings.MEDIA_URL }}/css/cavetables.css">
|
|
||||||
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th>Cave</th>
|
|
||||||
<th>Components</th>
|
|
||||||
<th>aaa</th>
|
|
||||||
<th>bbb</th>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
|
|
||||||
{% for subdircave, cavefiles, subsurvdirs in subdircaves %}
|
|
||||||
<tr>
|
|
||||||
<td><b><a href="{% url "svx" cavefiles.0.0 %}">{{cavefiles.0.1}}</a></b></td>
|
|
||||||
<td>
|
|
||||||
root: {% for cavepath, cavename in cavefiles.1 %}
|
|
||||||
<a href="{% url "svx" cavepath %}">{{cavename}}</a>
|
|
||||||
{% endfor %}
|
|
||||||
<br>
|
|
||||||
{% for primarycavefile, subcavefiles in subsurvdirs %}
|
|
||||||
{{ primarycavefile.1 }}:
|
|
||||||
{% for cavepath, cavename in subcavefiles %}
|
|
||||||
<a href="{% url "svx" cavepath %}">{{cavename}}</a>
|
|
||||||
{% endfor %}
|
|
||||||
<br>
|
|
||||||
{% endfor %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
{% for primarycavefile, subcavefiles in multifilecaves %}
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<a href="{% url "survexcavessingle" primarycavefile.1 %}">{{primarycavefile.1}}</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% for cavepath, cavename in subcavefiles %}
|
|
||||||
<a href="{% url "svx" cavepath %}">{{cavename}}</a>
|
|
||||||
{% endfor %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
======================= OLD STUFF ==============================
|
|
||||||
|
|
||||||
<td><a href="{% url "svx" cavefiles.0.0 %}">{{cavefiles.0.1}}</a></td>
|
|
||||||
|
|
||||||
<p><a href="#cdir">caves with subdirectories</a> | <a href="#cmult">caves with multiple files</a> | <a href="#csing">caves with single files</a></p>
|
|
||||||
|
|
||||||
<h3><a href="/survexfile/all.svx">Link to all.svx for processing</a></h3>
|
|
||||||
|
|
||||||
<h2 id="cdir">Caves with subdirectories</h2>
|
|
||||||
|
|
||||||
{% for subdircave, cavefiles, subsurvdirs in subdircaves %}
|
|
||||||
<h3>{{cavefiles.0.1}} - <a href="{% url "survexcavessingle" cavefiles.0.1 %}">dates and explorers</a></h3>
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td><b><a href="{% url "svx" cavefiles.0.0 %}">{{cavefiles.0.1}}</a></b></td>
|
|
||||||
<td>
|
|
||||||
{% for cavepath, cavename in cavefiles.1 %}
|
|
||||||
<a href="{% url "svx" cavepath %}">{{cavename}}</a>
|
|
||||||
{% endfor %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
{% for primarycavefile, subcavefiles in subsurvdirs %}
|
|
||||||
<tr>
|
|
||||||
<td><a href="{% url "svx" primarycavefile.0 %}">{{primarycavefile.1}}</a></td>
|
|
||||||
<td>
|
|
||||||
{% for cavepath, cavename in subcavefiles %}
|
|
||||||
<a href="{% url "svx" cavepath %}">{{cavename}}</a>
|
|
||||||
{% endfor %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
|
|
||||||
<h2 id="cmult">Caves of multiple files</h2>
|
|
||||||
<table>
|
|
||||||
<tr><th>Dates and explorers</th><th>Survex files</th></tr>
|
|
||||||
{% for primarycavefile, subcavefiles in multifilecaves %}
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<a href="{% url "survexcavessingle" primarycavefile.1 %}">{{primarycavefile.1}}</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a href="{% url "svx" primarycavefile.0 %}">{{primarycavefile.1}}</a> -
|
|
||||||
{% for cavepath, cavename in subcavefiles %}
|
|
||||||
<a href="{% url "svx" cavepath %}">{{cavename}}</a>
|
|
||||||
{% endfor %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<h2 id="csing">Caves of one file</h2>
|
|
||||||
<p>
|
|
||||||
{% for cavepath, cavename in onefilecaves %}
|
|
||||||
<a href="{% url "svx" cavepath %}">{{cavename}}</a>
|
|
||||||
{% endfor %}
|
|
||||||
</p>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<script type="text/javascript" src="scripts/TableSort.js"></script>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
|
|
5
urls.py
5
urls.py
@ -22,8 +22,9 @@ admin.autodiscover()
|
|||||||
|
|
||||||
actualurlpatterns = patterns('',
|
actualurlpatterns = patterns('',
|
||||||
|
|
||||||
|
url(r'^testingurl/?$' , views_caves.millenialcaves, name="testing"),
|
||||||
url(r'^millenialcaves/?$', views_survex.millenialcaves, name="millenialcaves"),
|
|
||||||
|
url(r'^millenialcaves/?$', views_caves.millenialcaves, name="millenialcaves"),
|
||||||
|
|
||||||
url(r'^troggle$', views_other.frontpage, name="frontpage"),
|
url(r'^troggle$', views_other.frontpage, name="frontpage"),
|
||||||
url(r'^todo/$', views_other.todo, name="todo"),
|
url(r'^todo/$', views_other.todo, name="todo"),
|
||||||
|
Loading…
Reference in New Issue
Block a user