diff --git a/troggle/databaseReset.py b/troggle/databaseReset.py index 4b3c91c28..f2c3b51b6 100644 --- a/troggle/databaseReset.py +++ b/troggle/databaseReset.py @@ -1,33 +1,58 @@ import os +import time import settings os.environ['PYTHONPATH'] = settings.PYTHON_PATH os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' from django.core import management from django.db import connection - -cursor = connection.cursor() -cursor.execute("drop database %s" % settings.DATABASE_NAME) -cursor.execute("create database %s" % settings.DATABASE_NAME) -cursor.execute("ALTER DATABASE %s CHARACTER SET=utf8" % settings.DATABASE_NAME) -cursor.execute("USE %s" % settings.DATABASE_NAME) -management.call_command('syncdb') from django.contrib.auth.models import User -user = User.objects.create_user('m', 'm@m.com', 'm') -user.is_staff = True -user.is_superuser = True -user.save() -#Make directories that troggle requires -if not os.path.isdir(settings.PHOTOS_ROOT): - os.mkdir(settings.PHOTOS_ROOT) +def reload_db(): + cursor = connection.cursor() + cursor.execute("drop database %s" % settings.DATABASE_NAME) + cursor.execute("create database %s" % settings.DATABASE_NAME) + cursor.execute("ALTER DATABASE %s CHARACTER SET=utf8" % settings.DATABASE_NAME) + cursor.execute("USE %s" % settings.DATABASE_NAME) + management.call_command('syncdb') + user = User.objects.create_user('m', 'm@m.com', 'm') + user.is_staff = True + user.is_superuser = True + user.save() -import parsers.cavetab -parsers.cavetab.LoadCaveTab() -import parsers.people -parsers.people.LoadPersonsExpos() -import parsers.logbooks -parsers.logbooks.LoadLogbooks() -import parsers.survex -parsers.survex.LoadAllSurvexBlocks() -import parsers.QMs -import parsers.surveys +def make_dirs(): + """Make directories that troggle requires""" + if not os.path.isdir(settings.PHOTOS_ROOT): + os.mkdir(settings.PHOTOS_ROOT) + +def import_cavetab(): + import parsers.cavetab + parsers.cavetab.LoadCaveTab(logfile=settings.LOGFILE) + +def import_people(): + import parsers.people + parsers.people.LoadPersonsExpos() + +def import_logbooks(): + settings.LOGFILE.write('\nBegun importing logbooks at ' + time.asctime() +'\n'+'-'*60) + import parsers.logbooks + parsers.logbooks.LoadLogbooks() + +def import_survex(): + import parsers.survex + parsers.survex.LoadAllSurvexBlocks() + +def import_QMs(): + import parsers.QMs + +def import_surveys(): + import parsers.surveys + +def reset(): + reload_db() + make_dirs() + import_cavetab() + import_people() + import_logbooks() + import_survex() + import_QMs() + import_surveys() \ No newline at end of file diff --git a/troggle/expo/admin.py b/troggle/expo/admin.py index 7e773f6be..4416ddacf 100644 --- a/troggle/expo/admin.py +++ b/troggle/expo/admin.py @@ -5,18 +5,24 @@ import django.forms as forms from expo.forms import LogbookEntryForm #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(admin.ModelAdmin): +class SurvexBlockAdmin(TroggleModelAdmin): inlines = (RoleInline,) class ScannedImageInline(admin.TabularInline): model = ScannedImage extra = 4 -class SurveyAdmin(admin.ModelAdmin): +class SurveyAdmin(TroggleModelAdmin): inlines = (ScannedImageInline,) class QMInline(admin.TabularInline): @@ -34,7 +40,7 @@ class PersonTripInline(admin.TabularInline): extra = 1 #class LogbookEntryAdmin(VersionAdmin): -class LogbookEntryAdmin(admin.ModelAdmin): +class LogbookEntryAdmin(TroggleModelAdmin): prepopulated_fields = {'slug':("title",)} search_fields = ('title','expedition__year') inlines = (PersonTripInline, PhotoInline) @@ -47,20 +53,17 @@ class PersonExpeditionInline(admin.TabularInline): -class PersonAdmin(admin.ModelAdmin): +class PersonAdmin(TroggleModelAdmin): search_fields = ('first_name','last_name') inlines = (PersonExpeditionInline,) -class QMAdmin(admin.ModelAdmin): +class QMAdmin(TroggleModelAdmin): search_fields = ('found_by__cave__kataster_number','number') - def save_model(self, request, obj, form, change): - obj.new_since_parsing=True - obj.save() -class PersonExpeditionAdmin(admin.ModelAdmin): +class PersonExpeditionAdmin(TroggleModelAdmin): search_fields = ('person__first_name','expedition__year') -class CaveAdmin(admin.ModelAdmin): +class CaveAdmin(TroggleModelAdmin): search_fields = ('official_name','kataster_number','unofficial_number') #inlines = (QMInline,) extra = 4 @@ -68,6 +71,7 @@ class CaveAdmin(admin.ModelAdmin): admin.site.register(Photo) +admin.site.register(Subcave) admin.site.register(Cave, CaveAdmin) admin.site.register(Area) admin.site.register(OtherCaveName) diff --git a/troggle/expo/models.py b/troggle/expo/models.py index 2db803919..639e8b599 100644 --- a/troggle/expo/models.py +++ b/troggle/expo/models.py @@ -235,10 +235,10 @@ class LogbookEntry(TroggleModel): return "%s: (%s)" % (self.date, self.title) def get_next_by_id(self): - Logbook.objects.get(id=self.id+1) + LogbookEntry.objects.get(id=self.id+1) def get_previous_by_id(self): - Logbook.objects.get(id=self.id-1) + LogbookEntry.objects.get(id=self.id-1) class PersonTrip(TroggleModel): person_expedition = models.ForeignKey(PersonExpedition,null=True) @@ -448,13 +448,29 @@ class Entrance(TroggleModel): if f[0] == self.findability: return f[1] -class CaveArea(TroggleModel): +class Subcave(TroggleModel): description = models.TextField() - name = models.CharField(max_length=200, unique = True) - cave = models.ForeignKey('Cave') - parentArea = models.ForeignKey('CaveArea') - survexFile = models.CharField(max_length=200) - + name = 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('Subcave', blank=True, null=True,) + adjoining = models.ManyToManyField('Subcave',blank=True, null=True,) + survex_file = models.CharField(max_length=200, blank=True, null=True,) + + def __unicode__(self): + return self.name + + def get_absolute_url(self): + urlString=self.name + if self.parent: + parent=self.parent + while parent.parent: + urlString=parent.name+'/'+urlString + parent=parent.parent + urlString=unicode(parent.cave.kataster_number)+urlString + else: + urlString=unicode(self.cave.kataster_number)+urlString + + return settings.URL_ROOT + urlString class QM(TroggleModel): #based on qm.csv in trunk/expoweb/smkridge/204 which has the fields: @@ -473,7 +489,7 @@ class QM(TroggleModel): 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.OneToOneField(SurveyStation,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) diff --git a/troggle/expo/views_caves.py b/troggle/expo/views_caves.py index 8c1b50b31..02994b683 100644 --- a/troggle/expo/views_caves.py +++ b/troggle/expo/views_caves.py @@ -7,6 +7,7 @@ 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 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.""" @@ -51,10 +52,17 @@ def survexblock(request, survexpath): ftext = survexblock.text return render_response(request,'survexblock.html', {'survexblock':survexblock, 'ftext':ftext, }) -def caveArea(request, name): - cavearea = models.CaveArea.objects.get(name = name) - cave = cavearea.cave - return render_response(request,'cavearea.html', {'cavearea': cavearea, 'cave': cave,}) +def subcave(request, cave_id, subcave): + print subcave + subcaveSeq=re.findall('([a-zA-Z]*)(?:/)',subcave) + print subcaveSeq + cave=models.Cave.objects.filter(kataster_number = cave_id)[0] + subcave=models.Subcave.objects.get(name=subcaveSeq[0], cave=cave) + if len(subcaveSeq)>1: + for singleSubcave in subcaveSeq[1:]: + subcave=subcave.subcave_set.get(name=singleSubcave) + print subcave + return render_response(request,'subcave.html', {'subcave': subcave,}) def caveSearch(request): query_string = '' diff --git a/troggle/expo/views_logbooks.py b/troggle/expo/views_logbooks.py index 894be2a4a..cfebd35e6 100644 --- a/troggle/expo/views_logbooks.py +++ b/troggle/expo/views_logbooks.py @@ -5,6 +5,8 @@ from django.db import models from troggle.parsers.logbooks import LoadLogbookForExpedition from troggle.parsers.people import GetPersonExpeditionNameLookup from troggle.expo.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 @@ -52,8 +54,16 @@ def expedition(request, expeditionname): def get_absolute_url(self): return ('expedition', (expedition.year)) -def person(request, first_name='', last_name=''): +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): @@ -74,11 +84,11 @@ def newQMlink(logbookentry): if logbookentry.cave: for log in logbookentry.cave.logbookentry_set.all(): try: - biggestQMnumberInLog = logbookentry.QMs_found.order_by('-number')[0].number - except IndexError: + biggestQMnumberInLog = logbookentry.QMs_found.order_by('-number')[0].number + except IndexError: biggestQMnumberInLog = 0 - if biggestQMnumberInLog > biggestQMnumber: - biggestQMnumber = biggestQMnumberInLog + if biggestQMnumberInLog > biggestQMnumber: + biggestQMnumber = biggestQMnumberInLog else: return None @@ -97,8 +107,8 @@ def logbookSearch(request, extra): 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) + 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, }) diff --git a/troggle/expo/views_other.py b/troggle/expo/views_other.py index f83c6e200..728119a43 100644 --- a/troggle/expo/views_other.py +++ b/troggle/expo/views_other.py @@ -1,10 +1,9 @@ -from troggle.expo.models import Cave, Expedition, Person, LogbookEntry, PersonExpedition +from troggle.expo.models import Cave, Expedition, Person, LogbookEntry, PersonExpedition, PersonTrip, Photo import troggle.settings as settings from django import forms from django.db.models import Q -from troggle.parsers.people import LoadPersonsExpos +import databaseReset import re -from troggle.parsers.survex import LoadAllSurvexBlocks import randSent from django.http import HttpResponse @@ -27,7 +26,7 @@ def frontpage(request): if "reloadexpos" in request.GET: message = LoadPersonsExpos() message = "Reloaded personexpos" - if "reloadsurvex" in request.GET: + if "reloadsurvex" in request.POST: message = LoadAllSurvexBlocks() message = "Reloaded survexblocks" @@ -39,7 +38,17 @@ def frontpage(request): def calendar(request,year): week=['S','S','M','T','W','T','F'] if year: - expedition=Expedition.objects.get(year=year) - PersonExpeditions=expedition.personexpedition_set.all() - + expedition=Expedition.objects.get(year=year) + PersonExpeditions=expedition.personexpedition_set.all() + return render_response(request,'calendar.html', locals()) + +def controlPanel(request): + message = "no test message" #reverse('personn', kwargs={"name":"hkjhjh"}) + if request.method=='POST': + for item in request.POST: + if request.user.is_superuser and item!='item': + print "running"+ " databaseReset."+item+"()" + exec "databaseReset."+item+"()" + + return render_response(request,'controlPanel.html', ) \ No newline at end of file diff --git a/troggle/media/css/main3.css b/troggle/media/css/main3.css index 8cdcdcc1a..e0b3c4e96 100644 --- a/troggle/media/css/main3.css +++ b/troggle/media/css/main3.css @@ -7,10 +7,7 @@ dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td { - margin: 0; - padding: 0; - border: 0; - outline: 0; + font-weight: inherit; font-style: inherit; font-size: 100%; @@ -32,37 +29,19 @@ ul list-style: none; } -body -{ - background-color: white; - color: black; - font: 100% Verdana, Arial, Helvetica, sans-serif; - margin: 0; - padding: 0; - margin-left: auto; - margin-right: auto; -} - -div#content -{ - border: thin black dotted; - margin: 50px; -} - - - - div#footer { + position: fixed; + bottom: 0; clear:both; background-color:#999; - color:white; + color:red; text-align:center; margin-left:auto; margin-right:auto; } -img#frontPageBanner{position:relative;width:inherit;height:inherit;} + div.logbookentry { @@ -144,13 +123,7 @@ p.indent margin-left:10px; } -#expoHeader { - width:100%; - position:relative; - left:0; - right:0; - height:100px; -} + #currentLocation { float:right; background:#999; @@ -159,15 +132,6 @@ p.indent margin: 0px; padding: 0px; } -#expoHeaderText { - background:#999; - position:absolute; - bottom:0px; - clip:rect(10px auto auto auto) -/* filter:alpha(opacity=90); - -moz-opacity:.90; - opacity:.90; */ -} @@ -181,18 +145,6 @@ hr{ background:#000; } -div#expoHeader h1{ - position:relative; - bottom:-8px; - vertical-align:top; -} - -div#editLink { right:0px; top:0px; text-align: right; position: absolute; top:0; right:0; z-index:1; background:#999 } - -div#expoFinalDate { - display: inline; -} - div.centre img { vertical-align: middle; } h1 { text-align: center; font-size: 210%; display: inline;} @@ -240,30 +192,9 @@ table.bigfatborder { border-width: 6px; } table.trad td, table.trad th { margin: 0pt; border: 1px solid #aaa; border-color: #8d8d8d #c0c0c0 #c0c0c0 #8d8d8d; } -/*Divs for layout*/ -html, body, div.contents { -min-height: 100%; -height: 100%; -width:100%; -} -html>body, html>body div.contents { -height: auto; -} -body { -} -div.contents { -position: absolute; -top: 0; -right: 0; -} - - -div.main { -margin-bottom: 3em; -} - /* You are not expected to understand this. It is necessary. */ +/* The above is the most fucktarded comment I have ever read. AC, 24 APR 2009 */ table.centre { margin-left: auto; margin-right: auto; } table.centre td { text-align: left; } @@ -338,4 +269,92 @@ img.thumbnail { } br.clearfloat { clear:both; -} \ No newline at end of file +} + +#error { + color: red; +} + +div#header { + position:fixed; + left:100px; + right:100px; + top:0; + margin-left:auto; + margin-right:auto; + height:50px; + background-image: url( ../204plan.gif); + border-bottom:thin solid #000; + font-family: Arial, Helvetica, sans-serif; + font-variant: normal; +} + + +div#editLinks { + position:absolute; + background: #999; + bottom:0px; + right:0px; + font-family: "Courier New", Courier, monospace; + filter:alpha(opacity=75); + -moz-opacity:.75; + opacity:.75; + +} + +div#content { + margin-top: 50px; + margin-left: 100px; + margin-right: 100px; + padding: 10px; + +} + + +img#banner { + position:fixed; + width:100%; + bottom:0; + left:0; +} + +body { + background-color:#CCC; + padding-bottom:100px; + +} + +h1 { + margin-top:0; + margin-left:10px; + vertical-align:top; +} + + +#rightMargin { + position:absolute; + z-index:-2; + width:100px; + right:0px; + top:0px; + clip: rect(auto,auto,auto,auto); +} + +#leftMargin { + position:absolute; + z-index:-2; + width:100px; + top:0px; + left:0px; + clip: rect(auto,100px,auto,auto,); +} + +#footerLinks{ + position:fixed; + text-align: center; + bottom:0; + left:0; + width:100%; + background-color:#333 +} + diff --git a/troggle/media/eieshole.jpg b/troggle/media/eieshole.jpg new file mode 100644 index 000000000..061bb3613 Binary files /dev/null and b/troggle/media/eieshole.jpg differ diff --git a/troggle/media/expoBanner.gif b/troggle/media/expoBanner.gif new file mode 100644 index 000000000..03ad1f127 Binary files /dev/null and b/troggle/media/expoBanner.gif differ diff --git a/troggle/media/goesser.jpg b/troggle/media/goesser.jpg new file mode 100644 index 000000000..f40675b85 Binary files /dev/null and b/troggle/media/goesser.jpg differ diff --git a/troggle/parsers/QMs.py b/troggle/parsers/QMs.py index c7e67898e..6505ddd11 100644 --- a/troggle/parsers/QMs.py +++ b/troggle/parsers/QMs.py @@ -1,28 +1,40 @@ +# -*- coding: UTF-8 -*- + import csv import settings from expo.models import QM, LogbookEntry, Cave from datetime import * +from helpers import save_carefully import re -#sorry that the below code is ugly. I'll fix it sometime, really! - AC +def deleteQMs(): + QM.objects.all().delete() -QM.objects.all().delete() +def parseCaveQMs(cave,inputFile): + """Runs through the CSV file at inputFile (which is a relative path from expoweb) and saves each QM as a QM instance.""" -def parseCaveQMs(cave,pathToCSV): if cave=='stein': try: - steinBr=Cave.objects.get(official_name="Steinbrückenhöhle") + steinBr=Cave.objects.get(official_name="Steinbrückenhöhle") except Cave.DoesNotExist: print "Steinbruckenhoehle is not in the database. Please run parsers.cavetab first." return elif cave=='hauch': try: - hauchHl=Cave.objects.get(official_name="Hauchhöhle") + hauchHl=Cave.objects.get(official_name="Hauchhöhle") except Cave.DoesNotExist: print "Steinbruckenhoehle is not in the database. Please run parsers.cavetab first." - return - - qmPath = settings.EXPOWEB+pathToCSV + return + elif cave =='kh': + try: + kh=Cave.objects.get(official_name="Kaninchenhöhle") + except Cave.DoesNotExist: + print "Steinbruckenhoehle is not in the database. Please run parsers.cavetab first." + for file in inputFile: + parse_KH_QMs(kh, inputFile=file) + return + + qmPath = settings.EXPOWEB+inputFile qmCSVContents = open(qmPath,'r') dialect=csv.Sniffer().sniff(qmCSVContents.read()) qmCSVContents.seek(0,0) @@ -55,13 +67,54 @@ def parseCaveQMs(cave,pathToCSV): newQM.ticked_off_by=placeholder newQM.comment=line[6] - newQM.save() - print "QM "+str(newQM) + ' added to database\r', - except KeyError: + try: + preexistingQM=QM.objects.get(number=QMnum, found_by__date__year=year) #if we don't have this one in the DB, save it + if preexistingQM.new_since_parsing==False: #if the pre-existing QM has not been modified, overwrite it + preexistingQM.delete() + newQM.save() + print "overwriting " + str(preexistingQM) +"\r", + + else: # otherwise, print that it was ignored + print "preserving "+ str(preexistingQM) + ", which was edited in admin \r", + + except QM.DoesNotExist: #if there is no pre-existing QM, save the new one + newQM.save() + print "QM "+str(newQM) + ' added to database\r', + + except KeyError: #check on this one continue # except IndexError: # print "Index error in " + str(line) # continue -parseCaveQMs(cave='stein',pathToCSV=r"smkridge/204/qm.csv") -parseCaveQMs(cave='hauch',pathToCSV=r"smkridge/234/qm.csv") +def parse_KH_QMs(kh, inputFile): + """import QMs from the 1623-161 (Kaninchenhöhle) html pages + """ + khQMs=open(settings.EXPOWEB+inputFile,'r') + khQMs=khQMs.readlines() + for line in khQMs: + res=re.search('name=\"[CB](?P\d*)-(?P\d*)-(?P\d*).* (?P[ABDCV])
(?P.*)\[(?P.*)\]',line) + if res: + res=res.groupdict() + year=int(res['year']) + #check if placeholder exists for given year, create it if not + placeholder, hadToCreate = LogbookEntry.objects.get_or_create(date__year=year, title="placeholder for QMs in 161", text="QMs temporarily attached to this should be re-attached to their actual trips", defaults={"date": date((year), 1, 1),"cave":kh}) + lookupArgs={ + 'found_by':placeholder, + 'number':res['number'] + } + nonLookupArgs={ + 'grade':res['grade'], + 'nearest_station':res['nearest_station'], + 'location_description':res['description'] + } + + if + + save_carefully(QM,lookupArgs,nonLookupArgs) + + +parseCaveQMs(cave='kh', inputFile=r"smkridge/161/qmtodo.htm") +parseCaveQMs(cave='stein',inputFile=r"smkridge/204/qm.csv") +parseCaveQMs(cave='hauch',inputFile=r"smkridge/234/qm.csv") + diff --git a/troggle/parsers/cavetab.py b/troggle/parsers/cavetab.py index 8fc34d8eb..940d86787 100644 --- a/troggle/parsers/cavetab.py +++ b/troggle/parsers/cavetab.py @@ -9,6 +9,8 @@ import time import re import os +from troggle.helpers import save_carefully + ##format of CAVETAB2.CSV is KatasterNumber = 0 KatStatusCode = 1 @@ -136,15 +138,20 @@ def html_to_wiki(text): text2 = "" return out -def LoadCaveTab(): +def LoadCaveTab(logfile=None): cavetab = open(os.path.join(settings.EXPOWEB, "noinfo", "CAVETAB2.CSV")) caveReader = csv.reader(cavetab) caveReader.next() # Strip out column headers - + + if logfile: + logfile.write("Beginning to import caves from "+str(cavetab)+"\n"+"-"*60+"\n") + for katArea in ['1623', '1626']: if not models.Area.objects.filter(short_name = katArea): newArea = models.Area(short_name = katArea) newArea.save() + if logfile: + logfile.write("Added area "+str(newArea.short_name)+"\n") area1626 = models.Area.objects.filter(short_name = '1626')[0] area1623 = models.Area.objects.filter(short_name = '1623')[0] @@ -153,33 +160,43 @@ def LoadCaveTab(): if line[Area] == 'nonexistent': continue entranceLetters=[] #Used in caves that have mulitlple entrances, which are not described on seperate lines - if line[MultipleEntrances] == 'yes' or line[MultipleEntrances]=='': + if line[MultipleEntrances] == 'yes' or line[MultipleEntrances]=='': #When true, this line contains an actual cave, otherwise it is an extra entrance. args = {} + defaultArgs = {} + def addToArgs(CSVname, modelName): if line[CSVname]: args[modelName] = html_to_wiki(line[CSVname]) + + def addToDefaultArgs(CSVname, modelName): #This has to do with the non-destructive import. These arguments will be passed as the "default" dictionary in a get_or_create + if line[CSVname]: + defaultArgs[modelName] = html_to_wiki(line[CSVname]) + + # The attributes added using "addToArgs" will be used to look up an existing cave. Those added using "addToDefaultArgs" will not. addToArgs(KatasterNumber, "kataster_number") - addToArgs(KatStatusCode, "kataster_code") + addToDefaultArgs(KatStatusCode, "kataster_code") addToArgs(UnofficialNumber, "unofficial_number") addToArgs(Name, "official_name") - addToArgs(Comment, "notes") - addToArgs(Explorers, "explorers") - addToArgs(UndergroundDescription, "underground_description") - addToArgs(Equipment, "equipment") - addToArgs(KatasterStatus, "kataster_status") - addToArgs(References, "references") - addToArgs(UndergroundCentreLine, "underground_centre_line") - addToArgs(UndergroundDrawnSurvey, "survey") - addToArgs(Length, "length") - addToArgs(Depth, "depth") - addToArgs(Extent, "extent") - addToArgs(SurvexFile, "survex_file") - addToArgs(Notes, "notes") + addToDefaultArgs(Comment, "notes") + addToDefaultArgs(Explorers, "explorers") + addToDefaultArgs(UndergroundDescription, "underground_description") + addToDefaultArgs(Equipment, "equipment") + addToDefaultArgs(KatasterStatus, "kataster_status") + addToDefaultArgs(References, "references") + addToDefaultArgs(UndergroundCentreLine, "underground_centre_line") + addToDefaultArgs(UndergroundDrawnSurvey, "survey") + addToDefaultArgs(Length, "length") + addToDefaultArgs(Depth, "depth") + addToDefaultArgs(Extent, "extent") + addToDefaultArgs(SurvexFile, "survex_file") + addToDefaultArgs(Notes, "notes") - newCave = models.Cave(**args) - newCave.save() - - if line[Area]: + newCave, created=save_carefully(models.Cave, lookupAttribs=args, nonLookupAttribs=defaultArgs) + if logfile: + logfile.write("Added cave "+str(newCave)+"\n") + + #If we created a new cave, add the area to it. This does mean that if a cave's identifying features have not changed, areas will not be updated from csv. + if created and line[Area]: if line[Area] == "1626": newCave.area.add(area1626) else: @@ -190,16 +207,20 @@ def LoadCaveTab(): newArea = models.Area(short_name = line[Area], parent = area1623) newArea.save() newCave.area.add(newArea) - else: + elif created: newCave.area.add(area1623) - - newCave.save() - if line[UnofficialName]: - newUnofficialName = models.OtherCaveName(cave = newCave, name = line[UnofficialName]) - newUnofficialName.save() - - if line[MultipleEntrances] == '' or \ + newCave.save() + if logfile: + logfile.write("Added area "+line[Area]+" to cave "+str(newCave)+"\n") + + if created and line[UnofficialName]: + newUnofficialName = models.OtherCaveName(cave = newCave, name = line[UnofficialName]) + newUnofficialName.save() + if logfile: + logfile.write("Added unofficial name "+str(newUnofficialName)+" to cave "+str(newCave)+"\n") + + if created and line[MultipleEntrances] == '' or \ line[MultipleEntrances] == 'entrance' or \ line[MultipleEntrances] == 'last entrance': args = {} @@ -258,6 +279,8 @@ def LoadCaveTab(): addToArgs(Bearings, 'bearings') newEntrance = models.Entrance(**args) newEntrance.save() + if logfile: + logfile.write("Added entrance "+str(newEntrance)+"\n") if line[Entrances]: entrance_letter = line[Entrances] @@ -266,6 +289,8 @@ def LoadCaveTab(): newCaveAndEntrance = models.CaveAndEntrance(cave = newCave, entrance = newEntrance, entrance_letter = entrance_letter) newCaveAndEntrance.save() + if logfile: + logfile.write("Added CaveAndEntrance "+str(newCaveAndEntrance)+"\n") # lookup function modelled on GetPersonExpeditionNameLookup diff --git a/troggle/parsers/logbooks.py b/troggle/parsers/logbooks.py index 976d71aa5..7c8364a67 100644 --- a/troggle/parsers/logbooks.py +++ b/troggle/parsers/logbooks.py @@ -13,6 +13,7 @@ import re import datetime import os +from troggle.helpers import save_carefully # # When we edit logbook entries, allow a "?" after any piece of data to say we've frigged it and @@ -72,21 +73,23 @@ def GetTripCave(place): #need to be fuzzier about matching h noncaveplaces = [ "Journey", "Loser Plateau" ] def EnterLogIntoDbase(date, place, title, text, trippeople, expedition, logtime_underground): + """ saves a logbook entry and related persontrips """ trippersons, author = GetTripPersons(trippeople, expedition, logtime_underground) # tripCave = GetTripCave(place) - - lbo = models.LogbookEntry(date=date, place=place, title=title[:50], text=text, author=author, expedition=expedition) + # lplace = place.lower() if lplace not in noncaveplaces: - lbo.cave=GetCaveLookup().get(lplace) - #print "pppp %s |%s|" % (lplace, str(lbo.cave)) - - lbo.save() - #print "ttt", date, place + cave=GetCaveLookup().get(lplace) + + #Check for an existing copy of the current entry, and save + lookupAttribs={'date':date, 'title':title[:50]} + nonLookupAttribs={'place':place, 'text':text, 'author':author, 'expedition':expedition, 'cave':cave} + lbo, created=save_carefully(models.LogbookEntry, lookupAttribs, nonLookupAttribs) + for tripperson, time_underground in trippersons: - pto = models.PersonTrip(person_expedition = tripperson, place=place, date=date, time_underground=time_underground, - logbook_entry=lbo, is_logbook_entry_author=(tripperson == author)) - pto.save() + lookupAttribs={'person_expedition':tripperson, 'date':date} + nonLookupAttribs={'place':place,'time_underground':time_underground,'logbook_entry':lbo,'is_logbook_entry_author':(tripperson == author)} + save_carefully(models.PersonTrip, lookupAttribs, nonLookupAttribs) def ParseDate(tripdate, year): @@ -235,7 +238,7 @@ def Parseloghtml03(year, expedition, txt): yearlinks = [ ("2008", "2008/2008logbook.txt", Parselogwikitxt), - ("2007", "2007/2007logbook.txt", Parselogwikitxt), + #("2007", "2007/2007logbook.txt", Parselogwikitxt), ("2006", "2006/logbook/logbook_06.txt", Parselogwikitxt), ("2005", "2005/logbook.html", Parseloghtmltxt), ("2004", "2004/logbook.html", Parseloghtmltxt), @@ -299,15 +302,17 @@ def SetDatesFromLogbookEntries(expedition): # logbookentry.href = "%s" % logbookentry.date # logbookentry.save() # lprevlogbookentry = logbookentry - for logbookentry in expedition.logbookentry_set.all(): - logbookentry.slug = slugify(logbookentry.title) - logbookentry.save() + def LoadLogbookForExpedition(expedition): - print "deleting logbooks for", expedition - expedition.logbookentry_set.all().delete() - models.PersonTrip.objects.filter(person_expedition__expedition=expedition).delete() + """ Parses all logbook entries for one expedition """ + + #We're checking for stuff that's changed in admin before deleting it now. + #print "deleting logbooks for", expedition + #expedition.logbookentry_set.all().delete() + #models.PersonTrip.objects.filter(person_expedition__expedition=expedition).delete() + expowebbase = os.path.join(settings.EXPOWEB, "years") year = str(expedition.year) for lyear, lloc, parsefunc in yearlinks: @@ -322,7 +327,10 @@ def LoadLogbookForExpedition(expedition): def LoadLogbooks(): - models.LogbookEntry.objects.all().delete() + """ This is the master function for parsing all logbooks into the Troggle database. Requires yearlinks, which is a list of tuples for each expedition with expedition year, logbook path, and parsing function. """ + + #Deletion has been moved to a seperate function to enable the non-destructive importing + #models.LogbookEntry.objects.all().delete() expowebbase = os.path.join(settings.EXPOWEB, "years") #yearlinks = [ ("2001", "2001/log.htm", Parseloghtml01), ] #overwrite #yearlinks = [ ("1996", "1996/log.htm", Parseloghtml01),] # overwrite diff --git a/troggle/parsers/people.py b/troggle/parsers/people.py index 269f13b3b..23654d218 100644 --- a/troggle/parsers/people.py +++ b/troggle/parsers/people.py @@ -7,6 +7,7 @@ import re import datetime import os import shutil +from helpers import save_carefully # Julian: the below code was causing errors and it seems like a duplication of the above. Hope I haven't broken anything by commenting it. -Aaron # @@ -72,47 +73,45 @@ def LoadPersonsExpos(): print "Loading personexpeditions" models.Person.objects.all().delete() models.PersonExpedition.objects.all().delete() - 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) + #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: name = personline[header["Name"]] name = re.sub("<.*?>", "", name) mname = re.match("(\w+)(?:\s((?:van |ten )?\w+))?(?:\s\(([^)]*)\))?", name) nickname = mname.group(3) or "" - - person = models.Person(first_name=mname.group(1), last_name=(mname.group(2) or "")) - person.is_vfho = personline[header["VfHO member"]] - #person.Sethref() - #print "NNNN", person.href - is_guest = (personline[header["Guest"]] == "1") # this is really a per-expo catagory; not a permanent state - person.save() + + lookupAttribs={'first_name':mname.group(1), 'last_name':(mname.group(2) or "")} + nonLookupAttribs={'is_vfho':personline[header["VfHO member"]],} + person, created = save_carefully(models.Person, lookupAttribs=lookupAttribs, nonLookupAttribs=nonLookupAttribs) + parseMugShotAndBlurb(personline=personline, header=header, person=person) # make person expedition from table for year, attended in zip(headers, personline)[5:]: expedition = models.Expedition.objects.get(year=year) if attended == "1" or attended == "-1": - personexpedition = models.PersonExpedition(person=person, expedition=expedition, nickname=nickname, is_guest=is_guest) + personexpedition = models.PersonExpedition(person=person, expedition=expedition, nickname=nickname, is_guest=(personline[header["Guest"]] == "1")) personexpedition.save() # 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() + #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 diff --git a/troggle/registration/models.py b/troggle/registration/models.py index 4a211c179..d7e454456 100644 --- a/troggle/registration/models.py +++ b/troggle/registration/models.py @@ -121,14 +121,14 @@ class RegistrationManager(models.Manager): current_site = Site.objects.get_current() subject = render_to_string('registration/activation_email_subject.txt', - { 'site': current_site }) + { 'site': settings.URL_ROOT }) # Email subject *must not* contain newlines subject = ''.join(subject.splitlines()) message = render_to_string('registration/activation_email.txt', { 'activation_key': registration_profile.activation_key, 'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS, - 'site': current_site }) + 'site': settings.URL_ROOT }) send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [new_user.email]) user_registered.send(sender=self.model, user=new_user) diff --git a/troggle/save_carefully.py b/troggle/save_carefully.py new file mode 100644 index 000000000..1631618ca --- /dev/null +++ b/troggle/save_carefully.py @@ -0,0 +1,10 @@ +def save(objectType, lookupAttribs={}, nonLookupAttribs={}): + + instance, created=objectType.objects.get_or_create(defaults=nonLookupAttribs, **lookupAttribs) + + if not created and not instance.new_since_parsing: + for k, v in nonLookupAttribs.items(): #overwrite the existing attributes from the logbook text (except date and title) + setattr(instance, k, v) + instance.save() + + return instance \ No newline at end of file diff --git a/troggle/settings.py b/troggle/settings.py index aae32dd58..df5a5e297 100644 --- a/troggle/settings.py +++ b/troggle/settings.py @@ -35,7 +35,7 @@ USE_I18N = True # Examples: "http://foo.com/media/", "/media/". ADMIN_MEDIA_PREFIX = '/troggle/media-admin/' PHOTOS_ROOT = os.path.join(EXPOWEB, 'photos') -#MEDIA_URL = urlparse.urljoin(URL_ROOT , '/site_media/') +MEDIA_URL = urlparse.urljoin(URL_ROOT , '/site_media/') SURVEYS_URL = urlparse.urljoin(URL_ROOT , '/survey_scans/') PHOTOS_URL = urlparse.urljoin(URL_ROOT , '/photos/') SVX_URL = urlparse.urljoin(URL_ROOT , '/survex/') @@ -80,6 +80,7 @@ INSTALLED_APPS = ( 'django.contrib.redirects', #'photologue', #'troggle.reversion', + 'django_evolution', 'troggle.registration', 'troggle.profiles', 'troggle.expo' diff --git a/troggle/templates/base.html b/troggle/templates/base.html index bf316766d..c7fa4629b 100644 --- a/troggle/templates/base.html +++ b/troggle/templates/base.html @@ -5,28 +5,35 @@ {% block title %}THE TITLE{% endblock %} + + + {% block head %}{% endblock %} -
-
-
-

CUCC Expeditions to Austria: 1976 -

-
-

2009

-
-
-
-
- + {% block nav %} {% endblock %} @@ -39,7 +46,16 @@
- - + + + + + + + diff --git a/troggle/templates/cavearea.html b/troggle/templates/cavearea.html deleted file mode 100644 index 1ce41426c..000000000 --- a/troggle/templates/cavearea.html +++ /dev/null @@ -1,9 +0,0 @@ -{% extends "cavebase.html" %} -{% load wiki_markup %} - -{% block content %} -{{ cavearea.description|wiki_to_html_short }} -{{ cavearea.name|wiki_to_html_short }} -{{ cavearea.parentArea|wiki_to_html_short }} -{{ cavearea.survexFile|wiki_to_html_short }} -{% endblock %} \ No newline at end of file diff --git a/troggle/templates/controlPanel.html b/troggle/templates/controlPanel.html new file mode 100644 index 000000000..3fad8e61b --- /dev/null +++ b/troggle/templates/controlPanel.html @@ -0,0 +1,37 @@ +{% extends "base.html" %} +{% block content %} +
+ +

Dump:

+ + + +
Dump entire database and recreate tables:
+ +

Import (non-destructive):

+ + + + + + + + + +
caves from cavetab2.csv using parsers\cavetab.py
logbook entries using parsers\logbooks.py
people from folk.csv using parsers\people.py
QMs using parsers\QMs.py
survey scans using parsers\surveys.py
survex data using parsers\survex.py
+ + +

+ + + +

+
+ +

Export to csv:

+ + + + +
caves to cavetab2.csv
+{% endblock %} \ No newline at end of file diff --git a/troggle/templates/profiles/select_profile.html b/troggle/templates/profiles/select_profile.html index 7e3259799..810efd264 100644 --- a/troggle/templates/profiles/select_profile.html +++ b/troggle/templates/profiles/select_profile.html @@ -2,10 +2,37 @@ {% block content %} +

Troggle profile selection page

+ +Hello, {{ user }}. + +

+ +{% if user.person %} +This is where you go to associate a new user account with a pre-existing profile from old expo data. + +

+ +However, you already have a profile- your name should be {{user.person.first_name}} {{user.person.last_name}}. If you don't believe me, go see it at:

{{ user.person.get_absolute_url }}

or edit it at: +

{{ user.person.get_admin_url }} .

+ +If your account is associated with the wrong person's profile due to inebriation or incompetance during account setup, click here to dissasociate your profile from your user account. + +{% else %} + +You have an account in the system, but no profile. If you have been on expo before, please choose yourself from the list below. +

+
{{ form.as_p }} - +
+
+ +Yes, you could choose the wrong person if you want. That would be lame. Instead, do something that's actually funny. For example, fry a random object or maybe take some mac and cheese somewhere it doesn't usually get to go. Perhaps you can start a new tradition of laminating the expo leader. + + +{% endif %} {% if form.errors %}

Please correct the errors below

diff --git a/troggle/templates/registration/activate.html b/troggle/templates/registration/activate.html index f87d519b3..eb8ec6199 100644 --- a/troggle/templates/registration/activate.html +++ b/troggle/templates/registration/activate.html @@ -1,13 +1,20 @@ -{% extends "base.html" %} - -{% block title %} -registration_form.html | {{ block.super }} -{% endblock %} - -{% block header %} -

activate.html

-{% endblock %} - -{% block content %} -You are now activated. +{% extends "base.html" %} + +{% block title %} +New troggle account registered +{% endblock %} + +{% block header %} +

activate.html

+{% endblock %} + +{% block content %} + +

+Hello, {{user}}! Your account is now activated. You've also been logged in automatically for your convenience. Use the links in the upper right to control this in the future. +

+ +

+If you have been on the expedition in the past, you already have a profile in the system; click here to find it and link it to your account. Otherwise, please create yourself a new profile. +

{% endblock %} \ No newline at end of file diff --git a/troggle/templates/registration/activation_email.txt b/troggle/templates/registration/activation_email.txt index 3047d8f32..163e31383 100644 --- a/troggle/templates/registration/activation_email.txt +++ b/troggle/templates/registration/activation_email.txt @@ -1,3 +1,10 @@ -Activate your account in {{ expiration_days }} days... -{{ URL_ROOT }}{% url registration_activate activation_key %} +Hello, +Glad you're joining the CUCC EXPO team! Please go to + +{{ site }}{% url registration_activate activation_key %} + +to activate your account. Do this within {{ expiration_days }}, or else you'll have to sign up again. + +Yours, +The magical troggle \ No newline at end of file diff --git a/troggle/templates/registration/registration_complete.html b/troggle/templates/registration/registration_complete.html index 263cce820..552fa04e6 100644 --- a/troggle/templates/registration/registration_complete.html +++ b/troggle/templates/registration/registration_complete.html @@ -9,5 +9,5 @@ registration_complete.html | {{ block.super }} {% endblock %} {% block content %} -Thank you for signing up, {{ user.username }}. An email with the activation code has been sent to your inbox. If you have been on the expedition in the past, you already have a profile in the system; click here to find it and link it to your account. Otherwise, please create yourself a new profile. +Thank you for signing up. An email with the activation code has been sent to your inbox. {% endblock %} \ No newline at end of file diff --git a/troggle/urls.py b/troggle/urls.py index e0d2dd91b..0e0e93066 100644 --- a/troggle/urls.py +++ b/troggle/urls.py @@ -27,7 +27,7 @@ urlpatterns = patterns('', url(r'^survexblock/(.+)$', views_caves.survexblock, name="survexblock"), url(r'^cave/(?P[^/]+)/?$', views_caves.cave, name="cave"), - #url(r'^cavehref/(.+)$', views_caves.cave, name="cave"), + #url(r'^cavehref/(.+)$', views_caves.cave, name="cave"),url(r'cave'), url(r'^jgtfile/(.*)$', view_surveys.jgtfile, name="jgtfile"), url(r'^jgtuploadfile$', view_surveys.jgtuploadfile, name="jgtuploadfile"), @@ -39,7 +39,7 @@ urlpatterns = patterns('', url(r'^cave/(?P[^/]+)/?(?P[^/])$', ent), #(r'^cave/(?P[^/]+)/edit/$', edit_cave), #(r'^cavesearch', caveSearch), - url(r'^cavearea', caveArea, name="caveArea"), + url(r'^cave/(?P[^/]+)/(?P[a-zA-Z/]+)/?$', subcave, name="subcave"), url(r'^survex/(.*?)\.index$', views_survex.index, name="survexindex"), @@ -59,7 +59,9 @@ urlpatterns = patterns('', url(r'^survey/?$', surveyindex, name="survey"), url(r'^survey/(?P\d\d\d\d)\#(?P\d*)$', survey, name="survey"), - + + url(r'^controlpanel/?$', views_other.controlPanel, name="survey"), + (r'^admin/doc/?', include('django.contrib.admindocs.urls')), (r'^admin/(.*)', admin.site.root), #url(r'^admin/', include(admin.site.urls),name="admin"),