summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/__init__.py0
-rw-r--r--core/admin.py129
-rw-r--r--core/context.py5
-rw-r--r--core/fileAbstraction.py43
-rw-r--r--core/forms.py48
-rw-r--r--core/imagekit_specs.py22
-rw-r--r--core/models.py665
-rw-r--r--core/models_survex.py66
-rw-r--r--core/randSent.py32
-rw-r--r--core/search.py39
-rw-r--r--core/templatetags/__init__.py0
-rw-r--r--core/templatetags/link.py8
-rw-r--r--core/templatetags/survex_markup.py52
-rw-r--r--core/templatetags/wiki_markup.py138
-rw-r--r--core/view_surveys.py161
-rw-r--r--core/views.py8
-rw-r--r--core/views_caves.py99
-rw-r--r--core/views_logbooks.py124
-rw-r--r--core/views_other.py155
-rw-r--r--core/views_survex.py167
20 files changed, 1961 insertions, 0 deletions
diff --git a/core/__init__.py b/core/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/core/__init__.py
diff --git a/core/admin.py b/core/admin.py
new file mode 100644
index 0000000..e304966
--- /dev/null
+++ b/core/admin.py
@@ -0,0 +1,129 @@
+from troggle.core.models import *
+from django.contrib import admin
+from feincms.admin import editor
+from django.forms import ModelForm
+import django.forms as forms
+from core.forms import LogbookEntryForm
+from django.http import HttpResponse
+from django.core import serializers
+from core.views_other import downloadLogbook
+#from troggle.reversion.admin import VersionAdmin #django-reversion version control
+
+#overriding admin save so we have the new since parsing field
+class TroggleModelAdmin(admin.ModelAdmin):
+ def save_model(self, request, obj, form, change):
+ obj.new_since_parsing=True
+ obj.save()
+
+class RoleInline(admin.TabularInline):
+ model = PersonRole
+ extra = 4
+
+class SurvexBlockAdmin(TroggleModelAdmin):
+ inlines = (RoleInline,)
+
+class ScannedImageInline(admin.TabularInline):
+ model = ScannedImage
+ extra = 4
+
+class SurveyAdmin(TroggleModelAdmin):
+ inlines = (ScannedImageInline,)
+ search_fields = ('expedition__year','wallet_number')
+
+class QMsFoundInline(admin.TabularInline):
+ model=QM
+ fk_name='found_by'
+
+class PhotoInline(admin.TabularInline):
+ model = Photo
+ exclude = ['is_mugshot' ]
+ extra = 1
+
+class PersonTripInline(admin.TabularInline):
+ model = PersonTrip
+ exclude = ['persontrip_next','Delete']
+ extra = 1
+
+#class LogbookEntryAdmin(VersionAdmin):
+class LogbookEntryAdmin(TroggleModelAdmin):
+ prepopulated_fields = {'slug':("title",)}
+ search_fields = ('title','expedition__year')
+ date_heirarchy = ('date')
+ inlines = (PersonTripInline, PhotoInline, QMsFoundInline)
+ form = LogbookEntryForm
+
+ actions=('export_logbook_entries_as_html','export_logbook_entries_as_txt')
+
+ def export_logbook_entries_as_html(modeladmin, request, queryset):
+ response=downloadLogbook(request=request, queryset=queryset, extension='html')
+ return response
+
+ def export_logbook_entries_as_txt(modeladmin, request, queryset):
+ response=downloadLogbook(request=request, queryset=queryset, extension='txt')
+ return response
+
+class PersonExpeditionInline(admin.TabularInline):
+ model = PersonExpedition
+ extra = 1
+
+class PersonAdmin(TroggleModelAdmin):
+ search_fields = ('first_name','last_name')
+ inlines = (PersonExpeditionInline,)
+
+class QMAdmin(TroggleModelAdmin):
+ search_fields = ('found_by__cave__kataster_number','number','found_by__date')
+ list_display = ('__unicode__','grade','found_by','ticked_off_by')
+ list_display_links = ('__unicode__',)
+ list_editable = ('found_by','ticked_off_by','grade')
+ list_per_page = 20
+
+class PersonExpeditionAdmin(TroggleModelAdmin):
+ search_fields = ('person__first_name','expedition__year')
+
+class CaveAdmin(TroggleModelAdmin):
+ search_fields = ('official_name','kataster_number','unofficial_number')
+ #inlines = (QMInline,)
+ extra = 4
+
+class SubcaveAdmin(editor.TreeEditorMixin,TroggleModelAdmin):
+ pass
+
+admin.site.register(Photo)
+admin.site.register(Subcave, SubcaveAdmin)
+admin.site.register(Cave, CaveAdmin)
+admin.site.register(Area)
+admin.site.register(OtherCaveName)
+admin.site.register(CaveAndEntrance)
+admin.site.register(SurveyStation)
+admin.site.register(Entrance)
+admin.site.register(SurvexBlock, SurvexBlockAdmin)
+admin.site.register(Expedition)
+admin.site.register(Person,PersonAdmin)
+admin.site.register(PersonRole)
+admin.site.register(PersonExpedition,PersonExpeditionAdmin)
+admin.site.register(Role)
+admin.site.register(LogbookEntry, LogbookEntryAdmin)
+#admin.site.register(PersonTrip)
+admin.site.register(QM, QMAdmin)
+admin.site.register(Survey, SurveyAdmin)
+admin.site.register(ScannedImage)
+
+def export_as_json(modeladmin, request, queryset):
+ response = HttpResponse(mimetype="text/json")
+ response['Content-Disposition'] = 'attachment; filename=troggle_output.json'
+ serializers.serialize("json", queryset, stream=response)
+ return response
+
+def export_as_xml(modeladmin, request, queryset):
+ response = HttpResponse(mimetype="text/xml")
+ response['Content-Disposition'] = 'attachment; filename=troggle_output.xml'
+ serializers.serialize("xml", queryset, stream=response)
+ return response
+
+admin.site.add_action(export_as_xml)
+admin.site.add_action(export_as_json)
+
+try:
+ mptt.register(Subcave, order_insertion_by=['name'])
+except mptt.AlreadyRegistered:
+ print "mptt already registered"
diff --git a/core/context.py b/core/context.py
new file mode 100644
index 0000000..a77de49
--- /dev/null
+++ b/core/context.py
@@ -0,0 +1,5 @@
+from django.conf import settings
+from core.models import Expedition
+
+def troggle_context(request):
+ return { 'settings':settings, 'Expedition':Expedition } \ No newline at end of file
diff --git a/core/fileAbstraction.py b/core/fileAbstraction.py
new file mode 100644
index 0000000..94b8b0c
--- /dev/null
+++ b/core/fileAbstraction.py
@@ -0,0 +1,43 @@
+import troggle.settings as settings
+import os
+import urllib
+
+def urljoin(x, y): return x + "/" + y
+
+def listdir(*path):
+ try:
+ strippedpath = [p for p in path if p]
+ root = os.path.join(settings.FILES, *strippedpath )
+ l = ""
+ #l = root + "\n"
+ isdir = os.path.isdir(root) #This seems to be required for os.path.isdir to work...
+ #l += str(isdir) + "\n"
+ for p in os.listdir(root):
+ if os.path.isdir(os.path.join(root, p)):
+ l += p + "/\n"
+
+ elif os.path.isfile(os.path.join(root, p)):
+ l += p + "\n"
+ #Ignore non-files and non-directories
+ return l
+ except:
+ if strippedpath:
+ c = reduce(urljoin, strippedpath)
+ else:
+ c = ""
+ c = c.replace("#", "%23")
+ print "FILE: ", settings.FILES + "listdir/" + c
+ return urllib.urlopen(settings.FILES + "listdir/" + c).read()
+
+def dirsAsList(*path):
+ return [d for d in listdir(*path).split("\n") if len(d) > 0 and d[-1] == "/"]
+
+def filesAsList(*path):
+ return [d for d in listdir(*path).split("\n") if len(d) > 0 and d[-1] != "/"]
+
+def readFile(*path):
+ try:
+ f = open(os.path.join(settings.FILES, *path))
+ except:
+ f = urllib.urlopen(settings.FILES + "download/" + reduce(urljoin, path))
+ return f.read() \ No newline at end of file
diff --git a/core/forms.py b/core/forms.py
new file mode 100644
index 0000000..929c4e9
--- /dev/null
+++ b/core/forms.py
@@ -0,0 +1,48 @@
+from django.forms import ModelForm
+from models import Cave, Person, LogbookEntry, QM
+import django.forms as forms
+from django.forms.formsets import formset_factory
+from django.contrib.admin.widgets import AdminDateWidget
+import string
+from datetime import date
+
+class CaveForm(ModelForm):
+ class Meta:
+ model = Cave
+
+class PersonForm(ModelForm):
+ class Meta:
+ model = Person
+
+class LogbookEntryForm(ModelForm):
+ class Meta:
+ model = LogbookEntry
+
+ def wikiLinkHints(LogbookEntry=None):
+ """
+ This function returns html-formatted paragraphs for each of the
+ wikilink types that are related to this logbookentry. Each paragraph
+ contains a list of all of the related wikilinks.
+
+ Perhaps an admin javascript solution would be better.
+ """
+ res = ["Please use the following wikilinks, which are related to this logbook entry:"]
+
+ res.append(r'</p><p style="float: left;"><b>QMs found:</b>')
+ for QM in LogbookEntry.instance.QMs_found.all():
+ res.append(QM.wiki_link())
+
+ res.append(r'</p><p style="float: left;"><b>QMs ticked off:</b>')
+ for QM in LogbookEntry.instance.QMs_ticked_off.all():
+ res.append(QM.wiki_link())
+
+# res.append(r'</p><p style="float: left; "><b>People</b>')
+# for persontrip in LogbookEntry.instance.persontrip_set.all():
+# res.append(persontrip.wiki_link())
+# res.append(r'</p>')
+
+ return string.join(res, r'<br />')
+
+ def __init__(self, *args, **kwargs):
+ super(LogbookEntryForm, self).__init__(*args, **kwargs)
+ self.fields['text'].help_text=self.wikiLinkHints() \ No newline at end of file
diff --git a/core/imagekit_specs.py b/core/imagekit_specs.py
new file mode 100644
index 0000000..fd2e0a1
--- /dev/null
+++ b/core/imagekit_specs.py
@@ -0,0 +1,22 @@
+from imagekit.specs import ImageSpec
+from imagekit import processors
+
+class ResizeThumb(processors.Resize):
+ width = 100
+ crop = False
+
+class ResizeDisplay(processors.Resize):
+ width = 600
+
+#class EnhanceThumb(processors.Adjustment):
+ #contrast = 1.2
+ #sharpness = 2
+
+class Thumbnail(ImageSpec):
+ access_as = 'thumbnail_image'
+ pre_cache = True
+ processors = [ResizeThumb]
+
+class Display(ImageSpec):
+ increment_count = True
+ processors = [ResizeDisplay]
diff --git a/core/models.py b/core/models.py
new file mode 100644
index 0000000..8e00941
--- /dev/null
+++ b/core/models.py
@@ -0,0 +1,665 @@
+import urllib, urlparse, string, os, datetime, logging
+import troggle.mptt as mptt
+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.conf import settings
+from decimal import Decimal, getcontext
+from django.core.urlresolvers import reverse
+from imagekit.models import ImageModel
+getcontext().prec=2 #use 2 significant figures for decimal calculations
+
+from models_survex import *
+
+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/expo/" + 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/expo/" + self.object_name().lower() + "/" + str(self.pk))
+
+
+ class Meta:
+ abstract = True
+
+class Expedition(TroggleModel):
+ year = models.CharField(max_length=20, unique=True)
+ name = models.CharField(max_length=100)
+ date_from = models.DateField(blank=True,null=True)
+ date_to = models.DateField(blank=True,null=True)
+
+ def __unicode__(self):
+ return self.year
+
+ class Meta:
+ ordering = ('-year',)
+ get_latest_by = 'date_from'
+
+ def get_absolute_url(self):
+ #return settings.URL_ROOT + "/expedition/%s" % self.year
+ return urlparse.urljoin(settings.URL_ROOT, reverse('expedition',args=[self.year]))
+
+
+ # lose these two functions (inelegant, and we may create a file with the dates that we can load from)
+ def GuessDateFrom(self):
+ try:
+ return self.logbookentry_set.order_by('date')[0].date
+ except IndexError:
+ pass
+
+ def GuessDateTo(self): # returns the date of the last logbook entry in the expedition
+ try:
+ return self.logbookentry_set.order_by('date')[-1].date
+ except IndexError:
+ pass
+
+ def ListDays(self):
+ if self.date_from and self.date_to:
+ res=[]
+ date=self.date_from
+ while date <= self.date_to:
+ res.append(date)
+ date+=datetime.timedelta(days=1)
+ return res
+ elif self.GuessDateFrom() and self.GuessDateTo(): # if we don't have the real dates, try it with the dates taken from the earliest and latest logbook entries
+ date=self.GuessDateFrom()
+ while date <= self.GuessDateTo():
+ res.append(date)
+ date+=datetime.timedelta(days=1)
+ return res
+
+
+
+
+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&uuml;r H&ouml;hlenkunde in Obersteier, a nearby Austrian caving club.")
+ 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()
+ 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"
+ class Meta:
+ 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
+
+# Below are no longer needed. Use {{ person.personexpedition_set.all.0.expedition }} for Firstexpedition, and {{ person.personexpedition_set.latest.expedition }} for Lastexpedition
+ # these ought to be possible by piping through |min in the template, or getting the first of an ordered list
+# def Firstexpedition(self):
+# return self.personexpedition_set.order_by('expedition')[0]
+# def Lastexpedition(self):
+# return self.personexpedition_set.order_by('-expedition')[0]
+
+ def notability(self):
+ notability = Decimal(0)
+ 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 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
+
+
+class PersonExpedition(TroggleModel):
+ expedition = models.ForeignKey(Expedition)
+ person = models.ForeignKey(Person)
+ date_from = models.DateField(blank=True,null=True)
+ date_to = models.DateField(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('survex_block'):
+ if res and res[-1]['survexpath'] == personrole.survex_block.survexpath:
+ res[-1]['roles'] += ", " + str(personrole.role)
+ else:
+ res.append({'date':personrole.survex_block.date, 'survexpath':personrole.survex_block.survexpath, 'roles':str(personrole.role)})
+ return res
+
+ class Meta:
+ ordering = ('expedition',)
+ #order_with_respect_to = 'expedition'
+ get_latest_by = 'expedition'
+
+ def GetPersonChronology(self):
+ res = { }
+ for persontrip in self.persontrip_set.all():
+ a = res.setdefault(persontrip.date, { })
+ a.setdefault("persontrips", [ ]).append(persontrip)
+ for personrole in self.personrole_set.all():
+ a = res.setdefault(personrole.survex_block.date, { })
+ b = a.setdefault("personroles", { })
+ survexpath = personrole.survex_block.survexpath
+
+ if b.get(survexpath):
+ b[survexpath] += ", " + str(personrole.role)
+ else:
+ b[survexpath] = str(personrole.role)
+# needs converting dict into list
+ return sorted(res.items())
+
+ # possibly not useful functions anyway -JT
+ # if you can find a better way to make the expo calendar table, be my guest. It isn't possible to do this logic in a django template without writing custom tags.-AC
+ def ListDays(self):
+ """
+ Returns a list of the days the person was on the expedition (i.e. the days that the PersonExpedition was in existance). Needed for expedition calendar.
+ """
+ if self.date_from and self.date_to:
+ res=[]
+ date=self.date_from
+ while date <= self.date_to:
+ res.append(date)
+ date+=datetime.timedelta(days=1)
+ return res
+
+ def ListDaysTF(self):
+ """
+ Returns a list of true / false values. Each value corresponds to one day on the expedition; True means the person was there, False means they weren't.
+ """
+ if self.date_from and self.date_to:
+ res=[]
+ for date in self.expedition.ListDays():
+ res.append(date in self.ListDays())
+ return res
+
+ 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 settings.URL_ROOT + '/personexpedition/' + str(self.person.first_name) + '_' + str(self.person.last_name) + '/' +self.expedition.year
+ return urlparse.urljoin(settings.URL_ROOT, reverse('personexpedition',kwargs={'first_name':self.person.first_name,'last_name':self.person.last_name,'year':self.expedition.year}))
+
+class LogbookEntry(TroggleModel):
+ date = models.DateField()
+ 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
+ title = models.CharField(max_length=200)
+ cave = models.ForeignKey('Cave',blank=True,null=True)
+ place = models.CharField(max_length=100,blank=True,null=True,help_text="Only use this if you haven't chosen a cave")
+ text = models.TextField()
+ slug = models.SlugField(max_length=50)
+ #href = models.CharField(max_length=100)
+
+
+ #logbookentry_next = models.ForeignKey('LogbookEntry', related_name='pnext', blank=True,null=True)
+ #logbookentry_prev = models.ForeignKey('LogbookEntry', related_name='pprev', blank=True,null=True)
+
+ class Meta:
+ verbose_name_plural = "Logbook Entries"
+ # several PersonTrips point in to this object
+ class Meta:
+ ordering = ('-date',)
+
+ 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)
+
+class PersonTrip(TroggleModel):
+ person_expedition = models.ForeignKey(PersonExpedition,null=True)
+
+ # this will be a foreign key of the place(s) the trip went through
+ # possibly a trip has a plurality of triplets pointing into it
+ #place = models.CharField(max_length=100)
+ #date = models.DateField()
+ time_underground = models.FloatField(help_text="In decimal hours")
+ logbook_entry = models.ForeignKey(LogbookEntry)
+ is_logbook_entry_author = models.BooleanField()
+
+ def date(self):
+ return self.logbook_entry.date
+
+ def place(self):
+ if self.logbook_entry.cave:
+ return self.logbook_entry.cave
+ else:
+ return self.logbook_entry.place
+
+ #persontrip_next = models.ForeignKey('PersonTrip', related_name='pnext', blank=True,null=True)
+ #persontrip_prev = models.ForeignKey('PersonTrip', related_name='pprev', blank=True,null=True)
+
+ def __unicode__(self):
+ return "%s %s (%s)" % (self.person_expedition, self.place(), self.date())
+
+ def get_persons_next_trip(self):
+ try:
+ return PersonTrip.objects.filter(person_expedition__person=self.person_expedition.person, person_expedition__date__gt=self.date)[0]
+ except:
+ return
+
+ def get_persons_previous_trip(self):
+ try:
+ return PersonTrip.objects.filter(person_expedition__person=self.person_expedition.person, person_expedition__date__lt=self.date)[0]
+ except:
+ return
+
+# def get_persons_previous_trip(self):
+#
+# move following classes into models_cave
+#
+
+class Area(TroggleModel):
+ short_name = models.CharField(max_length=100)
+ 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(TroggleModel):
+ 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 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)
+
+ #href = models.CharField(max_length=100)
+
+ 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):
+ if self.kataster_number:
+ if self.kat_area():
+ return self.kat_area() + u": " + self.kataster_number
+ else:
+ return unicode("l") + u": " + self.kataster_number
+ else:
+ if self.kat_area():
+ return self.kat_area() + u": " + self.unofficial_number
+ else:
+ return self.unofficial_number
+
+ def get_QMs(self):
+ return QM.objects.filter(found_by__cave=self)
+
+ 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 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 += "&ndash;" + prevR
+ else:
+ prevR = r
+ n = 0
+ res += r
+ if n == 0:
+ res += ", " + prevR
+ else:
+ res += "&ndash;" + prevR
+ return res
+
+ def nextQMnumber(self, year=datetime.date.today().year):
+ """
+ Given a cave and the current year, returns the next QM number.
+ """
+ res=QM.objects.filter(found_by__date__year=year, found_by__cave=self).order_by('-number')[0]
+ return res.number+1
+
+class OtherCaveName(TroggleModel):
+ name = models.CharField(max_length=160)
+ cave = models.ForeignKey(Cave)
+ def __unicode__(self):
+ return unicode(self.name)
+
+class SurveyStation(TroggleModel):
+ name = models.CharField(max_length=200)
+ def __unicode__(self):
+ return unicode(self.name)
+
+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', 'Retagged'),
+ ('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', 'Surveyed'),
+ ('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.ForeignKey(SurveyStation, blank=True,null=True, related_name="tag_station")
+ exact_station = models.ForeignKey(SurveyStation, blank=True,null=True, related_name="exact_station")
+ other_station = models.ForeignKey(SurveyStation, blank=True,null=True, related_name="other_station")
+ other_description = models.TextField(blank=True,null=True)
+ bearings = models.TextField(blank=True,null=True)
+ def __unicode__(self):
+ a = CaveAndEntrance.objects.filter(entrance = self)
+ name = u''
+ if self.name:
+ name = unicode(self.name) + u' '
+ if len(a) == 1:
+ return name + unicode(a[0])
+ return name + unicode(a)
+ 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]
+
+class Subcave(TroggleModel):
+ description = models.TextField(blank=True, null=True)
+ title = models.CharField(max_length=200, )
+ cave = models.ForeignKey('Cave', blank=True, null=True, help_text="Only the top-level subcave should be linked to a cave!")
+ parent = models.ForeignKey('self', null=True, blank=True, related_name='children')
+ #adjoining = models.ManyToManyField('Subcave',blank=True, null=True,)
+ legacy_description_path = models.CharField(max_length=600, blank=True, null=True)
+ def __unicode__(self):
+ return self.title
+
+ 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
+
+# This was the old way, before we were using django-mptt
+
+# def get_absolute_url(self):
+# urlString=self.name
+# if self.parent:
+# parent=self.parent
+# while parent: #recursively walk up the tree, adding parents to the left of the URL
+# urlString=parent.name+'/'+urlString
+# if parent.cave:
+# cave=parent.cave
+# parent=parent.parent
+# urlString='cave/'+unicode(cave.kataster_number)+'/'+urlString
+# else:
+# urlString='cave/'+unicode(self.cave.kataster_number)+'/'+urlString
+
+
+# return urlparse.urljoin(settings.URL_ROOT, urlString)
+try:
+ mptt.register(Subcave, order_insertion_by=['title'])
+except mptt.AlreadyRegistered:
+ print "mptt already registered"
+
+class QM(TroggleModel):
+ #based on qm.csv in trunk/expoweb/smkridge/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)
+ 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)
+ #the below are unneeded- instead use the date fields of the QM's trips
+ #dateFound = models.DateField(blank=True)
+ #dateKilled = models.DateField(blank=True)
+ def __unicode__(self):
+ QMnumber=str(self.found_by.cave)+'-'+str(self.found_by.date.year)+"-"+str(self.number)+self.grade
+ return str(QMnumber)
+
+ 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):
+ res = '[[cave:' + str(self.found_by.cave.kataster_number) + ' '
+ res += 'QM:' + str(self.found_by.date.year) + '-'
+ res += str(self.number) + self.grade + ']]'
+ return res
+
+photoFileStorage = FileSystemStorage(location=settings.PHOTOS_ROOT, base_url=settings.PHOTOS_URL)
+class Photo(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
+ number="%02d" % instance.survey.wallet_number + str(instance.survey.wallet_letter) #using %02d string formatting because convention was 2009#01
+ return os.path.join('./',year,year+r'#'+number,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('Subcave', 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')
diff --git a/core/models_survex.py b/core/models_survex.py
new file mode 100644
index 0000000..cf339f9
--- /dev/null
+++ b/core/models_survex.py
@@ -0,0 +1,66 @@
+from django.db import models
+from django.conf import settings
+import os
+
+class SurvexBlock(models.Model):
+ name = models.CharField(max_length=100, blank=True, null=True)
+ parent = models.ForeignKey('SurvexBlock', blank=True, null=True)
+ text = models.TextField()
+
+ # non-useful representation of incomplete data
+ start_year = models.IntegerField(blank=True, null=True)
+ start_month = models.IntegerField(blank=True, null=True)
+ start_day = models.IntegerField(blank=True, null=True)
+ end_year = models.IntegerField(blank=True, null=True)
+ end_month = models.IntegerField(blank=True, null=True)
+ end_day = models.IntegerField(blank=True, null=True)
+
+ date = models.DateField(blank=True, null=True)
+ survexpath = models.CharField(max_length=100)
+
+ # superfluous
+ person = models.ManyToManyField('Person', through='PersonRole', blank=True, null=True)
+
+ # code for where in the survex data files this block sits
+ begin_file = models.CharField(max_length=200)
+ begin_char = models.IntegerField()
+ end_file = models.CharField(max_length=200, blank=True, null=True)
+ end_char = models.IntegerField(blank=True, null=True)
+
+ class Meta:
+ ordering = ('date', 'survexpath')
+
+ def __unicode__(self):
+ return unicode(self.name)
+
+ def filecontents(self):
+ f = os.path.join(settings.SURVEX_DATA, self.begin_file)
+ fin = open(f, "rb")
+ res = fin.read().decode("latin1")
+ fin.close()
+ return res
+
+ def GetPersonroles(self):
+ res = [ ]
+ for personrole in self.personrole_set.order_by('personexpedition'):
+ if res and res[-1]['person'] == personrole.personexpedition.person:
+ res[-1]['roles'] += ", " + str(personrole.role)
+ else:
+ res.append({'person':personrole.personexpedition.person, 'expeditionyear':personrole.personexpedition.expedition.year, 'roles':str(personrole.role)})
+ print res
+ return res
+
+
+class PersonRole(models.Model):
+ personexpedition = models.ForeignKey('PersonExpedition')
+ person = models.ForeignKey('Person')
+ survex_block = models.ForeignKey('SurvexBlock')
+ role = models.ForeignKey('Role')
+ def __unicode__(self):
+ return unicode(self.person) + " - " + unicode(self.survex_block) + " - " + unicode(self.role)
+
+class Role(models.Model):
+ name = models.CharField(max_length=50)
+ def __unicode__(self):
+ return unicode(self.name)
+
diff --git a/core/randSent.py b/core/randSent.py
new file mode 100644
index 0000000..efcc005
--- /dev/null
+++ b/core/randSent.py
@@ -0,0 +1,32 @@
+import troggle.settings as settings
+from django import forms
+from troggle.core.models import LogbookEntry
+import random
+import re
+
+def weighted_choice(lst):
+ n = random.uniform(0,1)
+ for item, weight in lst:
+ if n < weight:
+ break
+ n = n - weight
+ return item
+
+def randomLogbookSentence():
+ randSent={}
+
+ # needs to handle empty logbooks without crashing
+
+ #Choose a random logbook entry
+ randSent['entry']=LogbookEntry.objects.order_by('?')[0]
+
+ #Choose again if there are no sentances (this happens if it is a placeholder entry)
+ while len(re.findall('[A-Z].*?\.',randSent['entry'].text))==0:
+ randSent['entry']=LogbookEntry.objects.order_by('?')[0]
+
+ #Choose a random sentence from that entry. Store the sentence as randSent['sentence'], and the number of that sentence in the entry as randSent['number']
+ sentenceList=re.findall('[A-Z].*?\.',randSent['entry'].text)
+ randSent['number']=random.randrange(0,len(sentenceList))
+ randSent['sentence']=sentenceList[randSent['number']]
+
+ return randSent
diff --git a/core/search.py b/core/search.py
new file mode 100644
index 0000000..5ec2ce2
--- /dev/null
+++ b/core/search.py
@@ -0,0 +1,39 @@
+import re
+
+from django.db.models import Q
+
+# search script from http://www.julienphalip.com/blog/2008/08/16/adding-search-django-site-snap/
+
+def normalize_query(query_string,
+ findterms=re.compile(r'"([^"]+)"|(\S+)').findall,
+ normspace=re.compile(r'\s{2,}').sub):
+ ''' Splits the query string in invidual keywords, getting rid of unecessary spaces
+ and grouping quoted words together.
+ Example:
+
+ >>> normalize_query(' some random words "with quotes " and spaces')
+ ['some', 'random', 'words', 'with quotes', 'and', 'spaces']
+
+ '''
+ return [normspace(' ', (t[0] or t[1]).strip()) for t in findterms(query_string)]
+
+def get_query(query_string, search_fields):
+ ''' Returns a query, that is a combination of Q objects. That combination
+ aims to search keywords within a model by testing the given search fields.
+
+ '''
+ query = None # Query to search for every search term
+ terms = normalize_query(query_string)
+ for term in terms:
+ or_query = None # Query to search for a given term in each field
+ for field_name in search_fields:
+ q = Q(**{"%s__icontains" % field_name: term})
+ if or_query is None:
+ or_query = q
+ else:
+ or_query = or_query | q
+ if query is None:
+ query = or_query
+ else:
+ query = query & or_query
+ return query \ No newline at end of file
diff --git a/core/templatetags/__init__.py b/core/templatetags/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/core/templatetags/__init__.py
diff --git a/core/templatetags/link.py b/core/templatetags/link.py
new file mode 100644
index 0000000..5308ace
--- /dev/null
+++ b/core/templatetags/link.py
@@ -0,0 +1,8 @@
+from django import template
+from django.utils.safestring import mark_safe
+
+register = template.Library()
+
+@register.filter()
+def link(value):
+ return mark_safe("<a href=\'%s\'>"%value.get_absolute_url()+unicode(value)+"</a>") \ No newline at end of file
diff --git a/core/templatetags/survex_markup.py b/core/templatetags/survex_markup.py
new file mode 100644
index 0000000..464a04b
--- /dev/null
+++ b/core/templatetags/survex_markup.py
@@ -0,0 +1,52 @@
+from django import template
+from django.utils.html import conditional_escape
+from django.template.defaultfilters import stringfilter
+from django.utils.safestring import mark_safe
+import re
+
+register = template.Library()
+
+# seems to add extra lines between the commented lines, which isn't so great.
+regexes = []
+regexes.append((re.compile(r"(;.*)$", re.IGNORECASE|re.MULTILINE),
+ r'<span class = "comment">\1</span>\n'))
+regexes.append((re.compile(r"^(\s*)(\*include)(\s+)([^\s]*)(.svx)$", re.IGNORECASE|re.MULTILINE),
+ r'\1<span class = "command">\2</span>\3<a href="\4.index">\4\5</a>'))
+regexes.append((re.compile(r"^(\s*)(\*include)(\s+)([^\s]*)$", re.IGNORECASE|re.MULTILINE),
+ r'\1<span class = "command">\2</span>\3<a href="\4.index">\4</a>'))
+regexes.append((re.compile(r"^(\s*)(\*team\s+(?:notes|tape|insts|pics))(\s+)(.*)$", re.IGNORECASE|re.MULTILINE),
+ r'\1<span class = "command">\2</span>\3\4'))
+regexes.append((re.compile(r"^(\s*)(\*(?:begin|end|copyright|date|entrance|equate|export|fix|prefix|require|SOLVE|title|truncate))(\s+)(.*)$", re.IGNORECASE|re.MULTILINE),
+ r'\1<span class = "command">\2</span>\3\4'))
+regexes.append((re.compile(r"^(\s*)(\*calibrate\s+(?:TAPE|COMPASS|CLINO|COUNTER|DEPTH|DECLINATION|X|Y|Z)+)(\s+)(.*)$", re.IGNORECASE|re.MULTILINE),
+ r'\1<span class = "command">\2</span>\3\4'))
+regexes.append((re.compile(r"^(\s*)(\*data\s+(?:DEFAULT|NORMAL|DIVING|CARTESIAN|TOPOFIL|CYLPOLAR|NOSURVEY|passage)(?:\s+station|\s+from|\s+to|\s+FROMDEPTH|\s+TODEPTH|\s+DEPTHCHANGE|\s+newline|\s+direction|\s+tape|\s+compass|\s+clino|\s+northing|\s+easting|\s+altitude|\s+length|\s+bearing|\s+gradient|\s+ignoreall|\sleft|\sright|\sup|\sdown)*)$", re.IGNORECASE|re.MULTILINE),
+ r'\1<span class = "command">\2</span>'))
+regexes.append((re.compile(r"^(\s*)(\*default\s+(?:CALIBRATE|DATA|UNITS)+)(\s+)(.*)$", re.IGNORECASE|re.MULTILINE),
+ r'\1<span class = "command">\2</span>\3\4'))
+regexes.append((re.compile(r"^(\s*)(\*flags\s+(?:DUPLICATE|SPLAY|SURFACE|not DUPLICATE|not SPLAY|not SURFACE))(\s+)(.*)$", re.IGNORECASE|re.MULTILINE),
+ r'\1<span class = "command">\2</span>\3\4'))
+regexes.append((re.compile(r"^(\s*)(\*infer\s+(?:plumbs|equates|exports))(\s+)(.*)$", re.IGNORECASE|re.MULTILINE),
+ r'\1<span class = "command">\2</span>\3\4'))
+regexes.append((re.compile(r"^(\s*)(\*instrument\s+(?:compass|clino|tape))(\s+)(.*)$", re.IGNORECASE|re.MULTILINE),
+ r'\1<span class = "command">\2</span>\3\4'))
+regexes.append((re.compile(r"^(\s*)(\*instrument\s+(?:compass|clino|tape))(\s+)(.*)$", re.IGNORECASE|re.MULTILINE),
+ r'\1<span class = "command">\2</span>\3\4'))
+regexes.append((re.compile(r"^(\s*)(\*sd\s+(?:TAPE|COMPASS|CLINO|COUNTER|DEPTH|DECLINATION|DX|DY|DZ))(\s+)(.*)$", re.IGNORECASE|re.MULTILINE),
+ r'\1<span class = "command">\2</span>\3\4'))
+regexes.append((re.compile(r"^(\s*)(\*set\s+(?:BLANK|COMMENT|DECIMAL|EOL|KEYWORD|MINUS|NAMES|OMIT|PLUS|ROOT|SEPARATOR))(\s+)(.*)$", re.IGNORECASE|re.MULTILINE),
+ r'\1<span class = "command">\2</span>\3\4'))
+regexes.append((re.compile(r"^(\s*)(\*units\s+(?:TAPE|LENGTH|COMPASS|BEARING|CLINO|GRADIENT|COUNTER|DEPTH|DECLINATION|X|Y|Z))(\s+)(.*)$", re.IGNORECASE|re.MULTILINE),
+ r'\1<span class = "command">\2</span>\3\4'))
+regexes.append((re.compile(r"^(.*)$", re.IGNORECASE|re.MULTILINE),
+ r'<div>\1&nbsp;</div>\n'))
+
+@register.filter()
+@stringfilter
+def survex_to_html(value, autoescape=None):
+ if autoescape:
+ value = conditional_escape(value)
+ for regex, sub in regexes:
+ print sub
+ value = regex.sub(sub, value)
+ return mark_safe(value) \ No newline at end of file
diff --git a/core/templatetags/wiki_markup.py b/core/templatetags/wiki_markup.py
new file mode 100644
index 0000000..07e6468
--- /dev/null
+++ b/core/templatetags/wiki_markup.py
@@ -0,0 +1,138 @@
+from django import template
+from django.utils.html import conditional_escape
+from django.template.defaultfilters import stringfilter
+from django.utils.safestring import mark_safe
+from django.conf import settings
+from core.models import QM, Photo
+import re, urlparse
+
+register = template.Library()
+if settings.URL_ROOT.endswith('/'):
+ url_root=settings.URL_ROOT[:-1]
+
+def wiki_list(line, listdepth):
+ l = ""
+ for d in listdepth:
+ l += d
+ mstar = re.match(l + "\*(.*)", line)
+ if mstar:
+ listdepth.append("\*")
+ return ("<ul>\n" + " " * len(listdepth) + "<li>%s</li>\n" % mstar.groups()[0], listdepth)
+ mhash = re.match(l + "#(.*)", line)
+ if mhash:
+ listdepth.append("#")
+ return ("<ol>\n" + " " * len(listdepth) + "<li>%s</li>\n" % mhash.groups()[0], listdepth)
+ mflat = re.match(l + "(.*)", line)
+ if mflat and listdepth:
+ return (" " * len(listdepth) + "<li>%s</li>\n" % mflat.groups()[0], listdepth)
+ if listdepth:
+ prev = listdepth.pop()
+ if prev == "\*":
+ t, l = wiki_list(line, listdepth)
+ return ("</ul>\n" + t, l)
+ if prev == "#":
+ t, l = wiki_list(line, listdepth)
+ return ("</ol>\n" + t, l)
+ return (line, listdepth)
+
+@register.filter()
+@stringfilter
+def wiki_to_html(value, autoescape=None):
+ #find paragraphs
+ outValue = ""
+ for paragraph in re.split("\n\s*?\n", value, re.DOTALL):
+ outValue += "<p>"
+ outValue += wiki_to_html_short(paragraph, autoescape)
+ outValue += "</p>\n"
+ return mark_safe(outValue)
+
+@register.filter()
+@stringfilter
+def wiki_to_html_short(value, autoescape=None):
+ """
+ This is the tag which turns wiki syntax into html. Aaron wonders
+ why it is called "short." It is long, and it operates on long things.
+ It even has a long name itself.
+ """
+ if autoescape:
+ value = conditional_escape(value)
+ #deescape doubly escaped characters
+ value = re.sub("&amp;(.*?);", r"&\1;", value, re.DOTALL)
+ #italics and bold
+ value = re.sub("&#39;&#39;&#39;&#39;([^']+)&#39;&#39;&#39;&#39;", r"<b><i>\1</i></b>", value, re.DOTALL)
+ value = re.sub("&#39;&#39;&#39;([^']+)&#39;&#39;&#39;", r"<b>\1</b>", value, re.DOTALL)
+ value = re.sub("&#39;&#39;([^']+)&#39;&#39;", r"<i>\1</i>", value, re.DOTALL)
+ #make cave links
+ value = re.sub("\[\[\s*cave:([^\s]+)\s*\s*\]\]", r'<a href="%s/cave/\1/">\1</a>' % url_root, value, re.DOTALL)
+ #make people links
+ value = re.sub("\[\[\s*person:(.+)\]\]",r'<a href="%s/person/\1/">\1</a>' % url_root, value, re.DOTALL)
+
+ #make qm links. this takes a little doing
+ qmMatchPattern="\[\[\s*cave:([^\s]+)\s*\s*\QM:(\d*)-(\d*)([ABCDX]?)\]\]"
+ def qmrepl(matchobj):
+ """
+ A function for replacing wikicode qm links with html qm links.
+ Given a matchobj matching a wikilink in the format
+ [[cave:204 QM:1999-24C]] where the grade (C) is optional.
+ If the QM does not exist, the function will return a link for creating it.
+ """
+ # if there are four matched groups, the fourth one should be the QM grade
+ if len(matchobj.groups())==4:
+ grade=matchobj.groups()[3]
+ else:
+ grade=''
+ qmdict={'urlroot':url_root,'cave':matchobj.groups()[0],'year':matchobj.groups()[1],'number':matchobj.groups()[2],'grade':grade}
+ try:
+ qm=QM.objects.get(found_by__cave__kataster_number=qmdict['cave'],found_by__date__year=qmdict['year'], number=qmdict['number'])
+ res=r'<a href=' + str(qm.get_absolute_url()) +'>' + str(qm) + '</a>'
+ except QM.DoesNotExist:
+ res = r'<a class="redtext" href="%(urlroot)s/cave/%(cave)s/%(year)s-%(number)s%(grade)s">%(cave)s:%(year)s-%(number)s%(grade)s</a>' % qmdict
+ return res
+ value = re.sub(qmMatchPattern,qmrepl, value, re.DOTALL)
+
+ #make photo links for [[photo:filename]] or [[photo:filename linktext]], and
+ #insert photos for [[display:left photo:filename]]
+ photoLinkPattern="\[\[\s*photo:(?P<photoName>[^\s]+)\s*(?P<linkText>.*)\]\]"
+ photoSrcPattern="\[\[\s*display:(?P<style>[^\s]+) photo:(?P<photoName>[^\s]+)\s*\]\]"
+ def photoLinkRepl(matchobj):
+ matchdict=matchobj.groupdict()
+ try:
+ linkText=matchdict['linkText']
+ except KeyError:
+ linkText=None
+
+ try:
+ photo=Photo.objects.get(file=matchdict['photoName'])
+ if not linkText:
+ linkText=str(photo)
+ res=r'<a href=' + photo.get_admin_url() +'>' + linkText + '</a>'
+ except Photo.DoesNotExist:
+ res = r'<a class="redtext" href="">make new photo</a>'
+ return res
+
+ def photoSrcRepl(matchobj):
+ matchdict=matchobj.groupdict()
+ style=matchdict['style']
+ try:
+ photo=Photo.objects.get(file=matchdict['photoName'])
+ res=r'<a href='+photo.file.url+'><img src=' + photo.thumbnail_image.url +' class='+style+' /></a>'
+ except Photo.DoesNotExist:
+ res = r'<a class="redtext" href="">make new photo</a>'
+ return res
+ value = re.sub(photoLinkPattern,photoLinkRepl, value, re.DOTALL)
+ value = re.sub(photoSrcPattern,photoSrcRepl, value, re.DOTALL)
+
+ #Make lists from lines starting with lists of [stars and hashes]
+ outValue = ""
+ listdepth = []
+ for line in value.split("\n"):
+ t, listdepth = wiki_list(line, listdepth)
+ outValue += t
+ for item in listdepth:
+ if item == "\*":
+ outValue += "</ul>\n"
+ elif item == "#":
+ outValue += "</ol>\n"
+ return mark_safe(outValue)
+
+wiki_to_html.needs_autoescape = True
diff --git a/core/view_surveys.py b/core/view_surveys.py
new file mode 100644
index 0000000..846b245
--- /dev/null
+++ b/core/view_surveys.py
@@ -0,0 +1,161 @@
+from django.conf import settings
+import fileAbstraction
+from django.shortcuts import render_to_response
+from django.http import HttpResponse, Http404
+import os
+import re
+
+# inline fileabstraction into here if it's not going to be useful anywhere else
+# keep things simple and ignore exceptions everywhere for now
+
+def getMimeType(extension):
+ try:
+ return {"txt": "text/plain",
+ "html": "text/html",
+ }[extension]
+ except:
+ print "unknown file type"
+ return "text/plain"
+
+
+def listdir(request, path):
+ #try:
+ return HttpResponse(fileAbstraction.listdir(path), mimetype = "text/plain")
+ #except:
+ # raise Http404
+
+def upload(request, path):
+ pass
+
+def download(request, path):
+ #try:
+
+ return HttpResponse(fileAbstraction.readFile(path), mimetype=getMimeType(path.split(".")[-1]))
+ #except:
+ # raise Http404
+
+
+#
+# julian's quick hack for something that works
+# could signal directories by ending with /, and forward cases where it's missing
+#
+extmimetypes = {".txt": "text/plain",
+ ".html": "text/html",
+ ".png": "image/png",
+ ".jpg": "image/jpeg",
+ }
+
+def jgtfile(request, f):
+ fp = os.path.join(settings.SURVEYS, f)
+ # could also surf through SURVEX_DATA
+
+ # directory listing
+ if os.path.isdir(fp):
+ listdirfiles = [ ]
+ listdirdirs = [ ]
+
+ for lf in sorted(os.listdir(fp)):
+ hpath = os.path.join(f, lf) # not absolute path
+ if lf[0] == "." or lf[-1] == "~":
+ continue
+
+ hpath = hpath.replace("\\", "/") # for windows users
+ href = hpath.replace("#", "%23") # '#' in file name annoyance
+
+ flf = os.path.join(fp, lf)
+ if os.path.isdir(flf):
+ nfiles = len([sf for sf in os.listdir(flf) if sf[0] != "."])
+ listdirdirs.append((href, hpath + "/", nfiles))
+ else:
+ listdirfiles.append((href, hpath, os.path.getsize(flf)))
+
+ upperdirs = [ ]
+ lf = f
+ while lf:
+ hpath = lf.replace("\\", "/") # for windows users
+ if hpath[-1] != "/":
+ hpath += "/"
+ href = hpath.replace("#", "%23")
+ lf = os.path.split(lf)[0]
+ upperdirs.append((href, hpath))
+ upperdirs.append(("", "/"))
+
+ return render_to_response('listdir.html', {'file':f, 'listdirfiles':listdirfiles, 'listdirdirs':listdirdirs, 'upperdirs':upperdirs, 'settings': settings})
+
+ # flat output of file when loaded
+ if os.path.isfile(fp):
+ ext = os.path.splitext(fp)[1].lower()
+ mimetype = extmimetypes.get(ext, "text/plain")
+ fin = open(fp)
+ ftext = fin.read()
+ fin.close()
+ return HttpResponse(ftext, mimetype=mimetype)
+
+ return HttpResponse("unknown file::%s::" % f, mimetype = "text/plain")
+
+
+def UniqueFile(fname):
+ while True:
+ if not os.path.exists(fname):
+ break
+ mname = re.match("(.*?)(?:-(\d+))?\.(png|jpg|jpeg)$(?i)", fname)
+ if mname:
+ fname = "%s-%d.%s" % (mname.group(1), int(mname.group(2) or "0") + 1, mname.group(3))
+ return fname
+
+
+# join it all up and then split them off for the directories that don't exist
+# anyway, this mkdir doesn't work
+def SaveImageInDir(name, imgdir, project, fdata, bbinary):
+ print ("hihihihi", fdata, settings.SURVEYS)
+ fimgdir = os.path.join(settings.SURVEYS, imgdir)
+ if not os.path.isdir(fimgdir):
+ print "*** Making directory", fimgdir
+ os.path.mkdir(fimgdir)
+ fprojdir = os.path.join(fimgdir, project)
+ if not os.path.isdir(fprojdir):
+ print "*** Making directory", fprojdir
+ os.path.mkdir(fprojdir)
+ print "hhh"
+
+ fname = os.path.join(fprojdir, name)
+ print fname, "fff"
+ fname = UniqueFile(fname)
+
+ p2, p1 = os.path.split(fname)
+ p3, p2 = os.path.split(p2)
+ p4, p3 = os.path.split(p3)
+ res = os.path.join(p3, p2, p1)
+
+ print "saving file", fname
+ fout = open(fname, (bbinary and "wb" or "w"))
+ fout.write(fdata.read())
+ fout.close()
+ res = os.path.join(imgdir, name)
+ return res.replace("\\", "/")
+
+
+# do we want to consider saving project/field rather than field/project
+def jgtuploadfile(request):
+ filesuploaded = [ ]
+ project, user, password, tunnelversion = request.POST["tunnelproject"], request.POST["tunneluser"], request.POST["tunnelpassword"], request.POST["tunnelversion"]
+ print (project, user, tunnelversion)
+ for uploadedfile in request.FILES.values():
+ if uploadedfile.field_name in ["tileimage", "backgroundimage"] and \
+ uploadedfile.content_type in ["image/png", "image/jpeg"]:
+ fname = user + "_" + re.sub("[\\\\/]", "-", uploadedfile.name) # very escaped \
+ print fname
+ fileuploaded = SaveImageInDir(fname, uploadedfile.field_name, project, uploadedfile, True)
+ filesuploaded.append(settings.URL_ROOT + "/jgtfile/" + fileuploaded)
+ if uploadedfile.field_name in ["sketch"] and \
+ uploadedfile.content_type in ["text/plain"]:
+ fname = user + "_" + re.sub("[\\\\/]", "-", uploadedfile.name) # very escaped \
+ print fname
+ fileuploaded = SaveImageInDir(fname, uploadedfile.field_name, project, uploadedfile, False)
+ filesuploaded.append(settings.URL_ROOT + "/jgtfile/" + fileuploaded)
+ #print "FF", request.FILES
+ #print ("FFF", request.FILES.values())
+ message = ""
+ print "gothere"
+ return render_to_response('fileupload.html', {'message':message, 'filesuploaded':filesuploaded, 'settings': settings})
+
diff --git a/core/views.py b/core/views.py
new file mode 100644
index 0000000..337989a
--- /dev/null
+++ b/core/views.py
@@ -0,0 +1,8 @@
+# primary namespace
+
+import view_surveys
+import views_caves
+import views_survex
+import views_logbooks
+import views_other
+
diff --git a/core/views_caves.py b/core/views_caves.py
new file mode 100644
index 0000000..f4327f3
--- /dev/null
+++ b/core/views_caves.py
@@ -0,0 +1,99 @@
+from troggle.core.models import Cave, CaveAndEntrance, Survey, Expedition, QM
+import troggle.core.models as models
+import troggle.settings as settings
+from django.forms.models import formset_factory
+import search
+from django.core.urlresolvers import reverse
+from troggle.alwaysUseRequestContext import render_response # see views_logbooks for explanation on this.
+from django.http import HttpResponseRedirect
+from django.conf import settings
+import re, urlparse
+
+def getCave(cave_id):
+ """Returns a cave object when given a cave name or number. It is used by views including cavehref, ent, and qm."""
+ try:
+ cave = Cave.objects.get(kataster_number=cave_id)
+ except Cave.DoesNotExist:
+ cave = Cave.objects.get(unofficial_number=cave_id)
+ return cave
+
+def caveindex(request):
+ caves = Cave.objects.all()
+ notablecavehrefs = [ "161", "204", "258", "76" ] # could detect notability by trips and notability of people who have been down them
+ notablecaves = [Cave.objects.get(kataster_number=kataster_number) for kataster_number in notablecavehrefs ]
+ return render_response(request,'caveindex.html', {'caves': caves, 'notablecaves':notablecaves})
+
+def cave(request, cave_id='', offical_name=''):
+ cave=getCave(cave_id)
+ if cave.non_public and not request.user.is_authenticated():
+ return render_response(request,'nonpublic.html', {'instance': cave})
+ else:
+ return render_response(request,'cave.html', {'cave': cave})
+
+def qm(request,cave_id,qm_id,year,grade=None):
+ year=int(year)
+ try:
+ qm=getCave(cave_id).get_QMs().get(number=qm_id,found_by__date__year=year)
+ return render_response(request,'qm.html',locals())
+
+ except QM.DoesNotExist:
+ url=urlparse.urljoin(settings.URL_ROOT, r'/admin/expo/qm/add/'+'?'+ r'number=' + qm_id)
+ if grade:
+ url += r'&grade=' + grade
+ return HttpResponseRedirect(url)
+
+
+def ent(request, cave_id, ent_letter):
+ cave = Cave.objects.filter(kataster_number = cave_id)[0]
+ cave_and_ent = CaveAndEntrance.objects.filter(cave = cave).filter(entrance_letter = ent_letter)[0]
+ return render_response(request,'entrance.html', {'cave': cave,
+ 'entrance': cave_and_ent.entrance,
+ 'letter': cave_and_ent.entrance_letter,})
+
+def survexblock(request, survexpath):
+ survexblock = models.SurvexBlock.objects.get(survexpath=survexpath)
+ #ftext = survexblock.filecontents()
+ ftext = survexblock.text
+ return render_response(request,'survexblock.html', {'survexblock':survexblock, 'ftext':ftext, })
+
+def subcave(request, cave_id, subcave):
+ print subcave
+ subcaveSeq=re.findall('(?:/)([^/]*)',subcave)
+ print subcaveSeq
+ cave=models.Cave.objects.get(kataster_number = cave_id)
+ subcave=models.Subcave.objects.get(title=subcaveSeq[0], cave=cave)
+ if len(subcaveSeq)>1:
+ for subcaveUrlSegment in subcaveSeq[1:]:
+ if subcaveUrlSegment:
+ subcave=subcave.children.get(title=subcaveUrlSegment)
+ print subcave
+ return render_response(request,'subcave.html', {'subcave': subcave,'cave':cave})
+
+def caveSearch(request):
+ query_string = ''
+ found_entries = None
+ if ('q' in request.GET) and request.GET['q'].strip():
+ query_string = request.GET['q']
+ entry_query = search.get_query(query_string, ['underground_description','official_name',])
+ found_entries = Cave.objects.filter(entry_query)
+
+ return render_response(request,'cavesearch.html',
+ { 'query_string': query_string, 'found_entries': found_entries,})
+
+def surveyindex(request):
+ surveys=Survey.objects.all()
+ expeditions=Expedition.objects.order_by("-year")
+ return render_response(request,'survey.html',locals())
+
+def survey(request,year,wallet_number):
+ surveys=Survey.objects.all()
+ expeditions=Expedition.objects.order_by("-year")
+ current_expedition=Expedition.objects.filter(year=year)[0]
+
+ if wallet_number!='':
+ current_survey=Survey.objects.filter(expedition=current_expedition,wallet_number=wallet_number)[0]
+ notes=current_survey.scannedimage_set.filter(contents='notes')
+ planSketches=current_survey.scannedimage_set.filter(contents='plan')
+ elevationSketches=current_survey.scannedimage_set.filter(contents='elevation')
+
+ return render_response(request,'survey.html', locals())
diff --git a/core/views_logbooks.py b/core/views_logbooks.py
new file mode 100644
index 0000000..5fa550c
--- /dev/null
+++ b/core/views_logbooks.py
@@ -0,0 +1,124 @@
+from django.shortcuts import render_to_response
+from troggle.core.models import Expedition, Person, PersonExpedition, PersonTrip, LogbookEntry
+import troggle.settings as settings
+from django.db import models
+from troggle.parsers.logbooks import LoadLogbookForExpedition
+from troggle.parsers.people import GetPersonExpeditionNameLookup
+from troggle.core.forms import PersonForm
+from django.core.urlresolvers import reverse
+from django.http import HttpResponseRedirect
+
+# Django uses Context, not RequestContext when you call render_to_response. We always want to use RequestContext, so that django adds the context from settings.TEMPLATE_CONTEXT_PROCESSORS. This way we automatically get necessary settings variables passed to each template. So we use a custom method, render_response instead of render_to_response. Hopefully future Django releases will make this unnecessary.
+from troggle.alwaysUseRequestContext import render_response
+
+import search
+import re
+
+@models.permalink #this allows the nice get_absolute_url syntax we are using
+
+def getNotablePersons():
+ notablepersons = []
+ for person in Person.objects.all():
+ if person.bisnotable():
+ notablepersons.append(person)
+ return notablepersons
+
+def personindex(request):
+ persons = Person.objects.all()
+ # From what I can tell, "persons" seems to be the table rows, while "personss" is the table columns. - AC 16 Feb 09
+ personss = [ ]
+ ncols = 5
+ nc = (len(persons) + ncols - 1) / ncols
+ for i in range(ncols):
+ personss.append(persons[i * nc: (i + 1) * nc])
+
+ notablepersons = []
+ for person in Person.objects.all():
+ if person.bisnotable():
+ notablepersons.append(person)
+
+ return render_response(request,'personindex.html', {'persons': persons, 'personss':personss, 'notablepersons':notablepersons, })
+
+def expedition(request, expeditionname):
+ year = int(expeditionname)
+ expedition = Expedition.objects.get(year=year)
+ expedition_next = Expedition.objects.filter(year=year+1) and Expedition.objects.get(year=year+1) or None
+ expedition_prev = Expedition.objects.filter(year=year-1) and Expedition.objects.get(year=year-1) or None
+ message = "No message"
+ if "reload" in request.GET:
+ message = LoadLogbookForExpedition(expedition)
+ #message = str(GetPersonExpeditionNameLookup(expedition).keys())
+ logbookentries = expedition.logbookentry_set.order_by('date')
+ return render_response(request,'expedition.html', {'expedition': expedition, 'expedition_next':expedition_next, 'expedition_prev':expedition_prev, 'logbookentries':logbookentries, 'message':message, })
+
+ def get_absolute_url(self):
+ return ('expedition', (expedition.year))
+
+def person(request, first_name='', last_name='', ):
+ person = Person.objects.get(first_name = first_name, last_name = last_name)
+
+ #This is for removing the reference to the user's profile, in case they set it to the wrong person
+ if request.method == 'GET':
+ if request.GET.get('clear_profile')=='True':
+ person.user=None
+ person.save()
+ return HttpResponseRedirect(reverse('profiles_select_profile'))
+
+ return render_response(request,'person.html', {'person': person, })
+
+ def get_absolute_url(self):
+ return settings.URL_ROOT + self.first_name + '_' + self.last_name
+
+#def person(request, name):
+# person = Person.objects.get(href=name)
+#
+
+def personexpedition(request, first_name='', last_name='', year=''):
+ person = Person.objects.get(first_name = first_name, last_name = last_name)
+ expedition = Expedition.objects.get(year=year)
+ personexpedition = person.personexpedition_set.get(expedition=expedition)
+ return render_response(request,'personexpedition.html', {'personexpedition': personexpedition, })
+
+def newQMlink(logbookentry):
+ biggestQMnumber=0
+ if logbookentry.cave:
+ for log in logbookentry.cave.logbookentry_set.all():
+ try:
+ biggestQMnumberInLog = logbookentry.QMs_found.order_by('-number')[0].number
+ except IndexError:
+ biggestQMnumberInLog = 0
+ if biggestQMnumberInLog > biggestQMnumber:
+ biggestQMnumber = biggestQMnumberInLog
+ else:
+ return None
+
+
+
+ nextQMnumber=biggestQMnumber+1
+ return settings.URL_ROOT + r'/admin/expo/qm/add/?' + r'found_by=' + str(logbookentry.pk) +'&number=' + str(nextQMnumber)
+
+def logbookentry(request, date, slug):
+ logbookentry = LogbookEntry.objects.filter(date=date, slug=slug)
+
+ if len(logbookentry)>1:
+ return render_response(request, 'object_list.html',{'object_list':logbookentry})
+ else:
+ logbookentry=logbookentry[0]
+ return render_response(request, 'logbookentry.html', {'logbookentry': logbookentry, 'newQMlink':newQMlink(logbookentry)})
+
+def logbookSearch(request, extra):
+ query_string = ''
+ found_entries = None
+ if ('q' in request.GET) and request.GET['q'].strip():
+ query_string = request.GET['q']
+ entry_query = search.get_query(query_string, ['text','title',])
+ found_entries = LogbookEntry.objects.filter(entry_query)
+
+ return render_response(request,'logbooksearch.html',
+ { 'query_string': query_string, 'found_entries': found_entries, })
+ #context_instance=RequestContext(request))
+
+def personForm(request,pk):
+ person=Person.objects.get(pk=pk)
+ form=PersonForm(instance=person)
+ return render_response(request,'personform.html', {'form':form,}) \ No newline at end of file
diff --git a/core/views_other.py b/core/views_other.py
new file mode 100644
index 0000000..b3da852
--- /dev/null
+++ b/core/views_other.py
@@ -0,0 +1,155 @@
+from troggle.core.models import Cave, Expedition, Person, LogbookEntry, PersonExpedition, PersonTrip, Photo
+import troggle.settings as settings
+from django import forms
+from django.template import loader, Context
+from django.db.models import Q
+import databaseReset
+import re
+import randSent
+from django.http import HttpResponse, HttpResponseRedirect
+from django.core.urlresolvers import reverse
+from troggle.alwaysUseRequestContext import render_response # see views_logbooks for explanation on this.
+from core.models import *
+
+def showrequest(request):
+ return HttpResponse(request.GET)
+
+def stats(request):
+ statsDict={}
+ statsDict['expoCount'] = int(Expedition.objects.count())
+ statsDict['caveCount'] = int(Cave.objects.count())
+ statsDict['personCount'] = int(Person.objects.count())
+ statsDict['logbookEntryCount'] = int(LogbookEntry.objects.count())
+ return render_response(request,'statistics.html', statsDict)
+
+def frontpage(request):
+ message = "no test message" #reverse('personn', kwargs={"name":"hkjhjh"})
+ if "reloadexpos" in request.GET:
+ message = LoadPersonsExpos()
+ message = "Reloaded personexpos"
+ if "reloadsurvex" in request.POST:
+ message = LoadAllSurvexBlocks()
+ message = "Reloaded survexblocks"
+
+ #'randSent':randSent.randomLogbookSentence(),
+ expeditions = Expedition.objects.order_by("-year")
+ logbookentry = LogbookEntry
+ cave = Cave
+ photo = Photo
+ from django.contrib.admin.templatetags import log
+ return render_response(request,'frontpage.html', locals())
+
+def todo(request):
+ message = "no test message" #reverse('personn', kwargs={"name":"hkjhjh"})
+ if "reloadexpos" in request.GET:
+ message = LoadPersonsExpos()
+ message = "Reloaded personexpos"
+ if "reloadsurvex" in request.POST:
+ message = LoadAllSurvexBlocks()
+ message = "Reloaded survexblocks"
+
+ #'randSent':randSent.randomLogbookSentence(),
+ expeditions = Expedition.objects.order_by("-year")
+ totallogbookentries = LogbookEntry.objects.count()
+ return render_response(request,'index.html', {'expeditions':expeditions, 'all':'all', 'totallogbookentries':totallogbookentries, "message":message})
+
+def calendar(request,year):
+ week=['S','S','M','T','W','T','F']
+ if year:
+ expedition=Expedition.objects.get(year=year)
+ PersonExpeditions=expedition.personexpedition_set.all()
+
+ return render_response(request,'calendar.html', locals())
+
+def controlPanel(request):
+ jobs_completed=[]
+ if request.method=='POST':
+ if request.user.is_superuser:
+
+ #importlist is mostly here so that things happen in the correct order.
+ #http post data seems to come in an unpredictable order, so we do it this way.
+ importlist=['reload_db', 'import_people', 'import_cavetab', 'import_logbooks', 'import_surveys', 'import_QMs']
+ databaseReset.make_dirs()
+ for item in importlist:
+ if item in request.POST:
+ print "running"+ " databaseReset."+item+"()"
+ exec "databaseReset."+item+"()"
+ jobs_completed.append(item)
+ else:
+ if request.user.is_authenticated(): #The user is logged in, but is not a superuser.
+ return render_response(request,'controlPanel.html', {'caves':Cave.objects.all(),'error':'You must be a superuser to use that feature.'})
+ else:
+ return HttpResponseRedirect(reverse('auth_login'))
+
+ return render_response(request,'controlPanel.html', {'caves':Cave.objects.all(),'expeditions':Expedition.objects.all(),'jobs_completed':jobs_completed})
+
+def downloadCavetab(request):
+ from export import tocavetab
+ response = HttpResponse(mimetype='text/csv')
+ response['Content-Disposition'] = 'attachment; filename=CAVETAB2.CSV'
+ tocavetab.writeCaveTab(response)
+ return response
+
+def downloadSurveys(request):
+ from export import tosurveys
+ response = HttpResponse(mimetype='text/csv')
+ response['Content-Disposition'] = 'attachment; filename=Surveys.csv'
+ tosurveys.writeCaveTab(response)
+ return response
+
+def downloadLogbook(request,year=None,extension=None,queryset=None):
+
+ if year:
+ expedition=Expedition.objects.get(year=year)
+ logbook_entries=LogbookEntry.objects.filter(expedition=expedition)
+ filename='logbook'+year
+ elif queryset:
+ logbook_entries=queryset
+ filename='logbook'
+ else:
+ return response(r"Error: Logbook downloader doesn't know what year you want")
+
+ if 'year' in request.GET:
+ year=request.GET['year']
+ if 'extension' in request.GET:
+ extension=request.GET['extension']
+
+
+
+ if extension =='txt':
+ response = HttpResponse(mimetype='text/plain')
+ style='2008'
+ elif extension == 'html':
+ response = HttpResponse(mimetype='text/html')
+ style='2005'
+
+ template='logbook'+style+'style.'+extension
+ response['Content-Disposition'] = 'attachment; filename='+filename+'.'+extension
+ t=loader.get_template(template)
+ c=Context({'logbook_entries':logbook_entries})
+ response.write(t.render(c))
+ return response
+
+
+def downloadQMs(request):
+ # Note to self: use get_cave method for the below
+ if request.method=='GET':
+ try:
+ cave=Cave.objects.get(kataster_number=request.GET['cave_id'])
+ except Cave.DoesNotExist:
+ cave=Cave.objects.get(name=cave_id)
+
+ from export import toqms
+
+ response = HttpResponse(mimetype='text/csv')
+ response['Content-Disposition'] = 'attachment; filename=qm.csv'
+ toqms.writeQmTable(response,cave)
+ return response
+
+def ajax_test(request):
+ post_text = request.POST['post_data']
+ return HttpResponse("{'response_text': '"+post_text+" recieved.'}",
+ mimetype="application/json")
+
+def eyecandy(request):
+ return render_response(request,'eyecandy.html', {})
diff --git a/core/views_survex.py b/core/views_survex.py
new file mode 100644
index 0000000..554ad4f
--- /dev/null
+++ b/core/views_survex.py
@@ -0,0 +1,167 @@
+from django import forms
+from django.http import HttpResponseRedirect, HttpResponse
+from django.shortcuts import render_to_response
+from django.http import HttpResponse, Http404
+import re
+import os
+import datetime
+import difflib
+
+import troggle.settings as settings
+
+
+def ReplaceTabs(stext):
+ res = [ ]
+ nsl = 0
+ for s in re.split("(\t|\n)", stext):
+ if s == "\t":
+ res.append(" " * (4 - (nsl % 4)))
+ nsl = 0
+ continue
+ if s == "\n":
+ nsl = 0
+ else:
+ nsl += len(s)
+ res.append(s)
+ return "".join(res)
+
+
+class SvxForm(forms.Form):
+ dirname = forms.CharField(widget=forms.TextInput(attrs={"readonly":True}))
+ filename = forms.CharField(widget=forms.TextInput(attrs={"readonly":True}))
+ datetime = forms.DateTimeField(widget=forms.TextInput(attrs={"readonly":True}))
+ outputtype = forms.CharField(widget=forms.TextInput(attrs={"readonly":True}))
+ code = forms.CharField(widget=forms.Textarea(attrs={"cols":150, "rows":18}))
+
+ def GetDiscCode(self):
+ fname = settings.SURVEX_DATA + self.data['filename'] + ".svx"
+ if not os.path.isfile(fname):
+ return None
+ fin = open(fname, "rb")
+ svxtext = fin.read().decode("latin1") # unicode(a, "latin1")
+ svxtext = ReplaceTabs(svxtext).strip()
+ fin.close()
+ return svxtext
+
+ def DiffCode(self, rcode):
+ code = self.GetDiscCode()
+ difftext = difflib.unified_diff(code.splitlines(), rcode.splitlines())
+ difflist = [ diffline.strip() for diffline in difftext if not re.match("\s*$", diffline) ]
+ return difflist
+
+ def SaveCode(self, rcode):
+ fname = settings.SURVEX_DATA + self.data['filename'] + ".svx"
+ if not os.path.isfile(fname):
+ return False
+ fout = open(fname, "w")
+ res = fout.write(rcode.encode("latin1"))
+ fout.close()
+ return True
+
+ def Process(self):
+ print "....\n\n\n....Processing\n\n\n"
+ cwd = os.getcwd()
+ os.chdir(os.path.split(settings.SURVEX_DATA + self.data['filename'])[0])
+ os.system(settings.CAVERN + " --log " + settings.SURVEX_DATA + self.data['filename'] + ".svx")
+ os.chdir(cwd)
+ fin = open(settings.SURVEX_DATA + self.data['filename'] + ".log", "rb")
+ log = fin.read()
+ fin.close()
+ log = re.sub("(?s).*?(Survey contains)", "\\1", log)
+ return log
+
+
+def svx(request, survex_file):
+ # get the basic data from the file given in the URL
+ dirname = os.path.split(survex_file)[0]
+ if dirname:
+ dirname += "/"
+ nowtime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+ outputtype = "normal"
+ form = SvxForm({'filename':survex_file, 'dirname':dirname, 'datetime':nowtime, 'outputtype':outputtype})
+
+ # if the form has been returned
+ difflist = [ ]
+ logmessage = ""
+ message = ""
+
+ if request.method == 'POST': # If the form has been submitted...
+ rform = SvxForm(request.POST) #
+ if rform.is_valid(): # All validation rules pass (how do we check it against the filename and users?)
+ rcode = rform.cleaned_data['code']
+ outputtype = rform.cleaned_data['outputtype']
+ difflist = form.DiffCode(rcode)
+ print "ssss", rform.data
+
+ if "revert" in rform.data:
+ pass
+ if "process" in rform.data:
+ if not difflist:
+ message = "OUTPUT FROM PROCESSING"
+ logmessage = form.Process()
+ print logmessage
+ else:
+ message = "SAVE FILE FIRST"
+ form.data['code'] = rcode
+ if "save" in rform.data:
+ print "sssavvving"
+ if form.SaveCode(rcode):
+ message = "SAVVVED"
+ # we will reload later
+ else:
+ message = "FAILED TO SAVE"
+ form.data['code'] = rcode
+ if "diff" in rform.data:
+ form.data['code'] = rcode
+
+
+ #process(survex_file)
+ if 'code' not in form.data:
+ form.data['code'] = form.GetDiscCode()
+
+ if not difflist:
+ difflist.append("none")
+ if message:
+ difflist.insert(0, message)
+
+ svxincludes = re.findall('\*include\s+"?(.*?)(?:\.svx)?"?\s*?\n(?i)', form.data['code'])
+
+ vmap = {'settings': settings,
+ 'has_3d': os.path.isfile(settings.SURVEX_DATA + survex_file + ".3d"),
+ 'title': survex_file,
+ 'svxincludes': svxincludes,
+ 'difflist': difflist,
+ 'logmessage':logmessage,
+ 'form':form}
+ if outputtype == "ajax":
+ return render_to_response('svxfiledifflistonly.html', vmap)
+ return render_to_response('svxfile.html', vmap)
+
+def Dsvx(request, survex_file):
+ svx = open(settings.SURVEX_DATA + survex_file + ".svx", "rb")
+ return HttpResponse(svx, mimetype="text")
+
+def threed(request, survex_file):
+ process(survex_file)
+ try:
+ threed = open(settings.SURVEX_DATA + survex_file + ".3d", "rb")
+ return HttpResponse(threed, mimetype="model/3d")
+ except:
+ log = open(settings.SURVEX_DATA + survex_file + ".log", "rb")
+ return HttpResponse(log, mimetype="text")
+
+def log(request, survex_file):
+ process(survex_file)
+ log = open(settings.SURVEX_DATA + survex_file + ".log", "rb")
+ return HttpResponse(log, mimetype="text")
+
+def err(request, survex_file):
+ process(survex_file)
+ err = open(settings.SURVEX_DATA + survex_file + ".err", "rb")
+ return HttpResponse(err, mimetype="text")
+
+def process(survex_file):
+ cwd = os.getcwd()
+ os.chdir(os.path.split(settings.SURVEX_DATA + survex_file)[0])
+ os.system(settings.CAVERN + " --log " + settings.SURVEX_DATA + survex_file + ".svx")
+ os.chdir(cwd)