diff --git a/core/admin.py b/core/admin.py
index bfbd727..b7c0d2b 100644
--- a/core/admin.py
+++ b/core/admin.py
@@ -47,11 +47,11 @@ class SurvexBlockAdmin(TroggleModelAdmin):
inlines = (RoleInline,)
-class QMsFoundInline(admin.TabularInline):
- model = QM
- fk_name = "found_by"
- fields = ("number", "grade", "location_description", "comment") # need to add foreignkey to cave part
- extra = 1
+# class QMsFoundInline(admin.TabularInline):
+ # model = QM
+ # fk_name = "found_by"
+ # fields = ("number", "grade", "location_description", "comment") # need to add foreignkey to cave part
+ # extra = 1
class PersonLogEntryInline(admin.TabularInline):
@@ -64,7 +64,7 @@ class LogbookEntryAdmin(TroggleModelAdmin):
prepopulated_fields = {"slug": ("title",)}
search_fields = ("title", "expedition__year")
date_heirarchy = "date"
- inlines = (PersonLogEntryInline, QMsFoundInline)
+ # inlines = (PersonLogEntryInline, QMsFoundInline)
class Media:
css = {"all": ("css/troggleadmin.css",)} # this does not exist
@@ -91,12 +91,12 @@ class PersonAdmin(TroggleModelAdmin):
class QMAdmin(TroggleModelAdmin):
- search_fields = ("found_by__cave__kataster_number", "number", "found_by__date")
- list_display = ("__str__", "grade", "found_by", "ticked_off_by")
+ search_fields = ("number", "expoyear")
+ list_display = ("__str__", "grade")
list_display_links = ("__str__",)
- list_editable = ("found_by", "ticked_off_by", "grade")
- list_per_page = 20
- raw_id_fields = ("found_by", "ticked_off_by")
+ # list_editable = ("comment", "page_ref", "grade")
+ # list_per_page = 20
+ # raw_id_fields = ("found_by", "ticked_off_by")
class PersonExpeditionAdmin(TroggleModelAdmin):
diff --git a/core/models/caves.py b/core/models/caves.py
index 6492698..8b23ceb 100644
--- a/core/models/caves.py
+++ b/core/models/caves.py
@@ -1,4 +1,5 @@
import os
+import os
import re
from collections import defaultdict
from datetime import datetime, timezone
@@ -161,14 +162,27 @@ class Cave(TroggleModel):
def __str__(self, sep=": "):
return str(self.slug())
- def get_QMs(self):
+ def get_open_QMs(self):
"""Searches for all QMs that reference this cave."""
# qms = self.qm_set.all().order_by('expoyear', 'block__date')
qms = QM.objects.filter(cave=self).order_by(
"expoyear", "block__date"
) # a QuerySet, see https://docs.djangoproject.com/en/dev/ref/models/querysets/#order-by
- return qms # a QuerySet
+ qmsopen = qms.filter(ticked=False)
+ return qmsopen # a QuerySet
+ def get_ticked_QMs(self):
+ """Searches for all QMs that reference this cave."""
+ qms = QM.objects.filter(cave=self).order_by(
+ "expoyear", "block__date"
+ )
+ qmticked = qms.filter(ticked=True)
+ return qmticked # a QuerySet
+
+ def get_QMs(self):
+ qms = self.get_open_QMs() | self.get_ticked_QMs() # set union operation
+ return qms # a QuerySet
+
def kat_area(self):
for a in self.area.all():
if a.kat_area():
diff --git a/core/models/logbooks.py b/core/models/logbooks.py
index 226f8e5..b594945 100644
--- a/core/models/logbooks.py
+++ b/core/models/logbooks.py
@@ -144,38 +144,19 @@ class QM(TroggleModel):
number = models.IntegerField(
help_text="this is the sequential number in the year, only unique for CSV imports",
)
+ grade = models.CharField(max_length=1, blank=True, null=True, help_text="A/B/C/D/X")
cave = models.ForeignKey("Cave", related_name="QMs", blank=True, null=True, on_delete=models.SET_NULL)
block = models.ForeignKey("SurvexBlock", null=True, on_delete=models.SET_NULL) # only for QMs from survex files
blockname = models.TextField(blank=True, null=True) # NB truncated copy of survexblock name with last char added
- expoyear = models.CharField(
- max_length=4, blank=True, null=True
- ) # could change to datetime if logbooks similarly chnaged
- found_by = models.ForeignKey(
- LogbookEntry, related_name="QMs_found", blank=True, null=True, on_delete=models.SET_NULL
- )
- ticked = models.BooleanField(
- default=False
- ) # for ticked QMs not attached to a logbook entry, should imply completion_description has text
- ticked_off_by = models.ForeignKey(
- LogbookEntry, related_name="QMs_ticked_off", blank=True, null=True, on_delete=models.SET_NULL
- ) # unused, ever?!
-
- GRADE_CHOICES = (
- ("A", "A: Large obvious lead"),
- ("B", "B: Average lead"),
- ("C", "C: Tight unpromising lead"),
- ("D", "D: Dig"),
- ("X", "X: Unclimbable or horrid"),
- ("V", "V: Vertical"),
- ) # also seen "?" in imported data - see urls.py
- grade = models.CharField(max_length=1, choices=GRADE_CHOICES)
+ expoyear = models.CharField(max_length=4, blank=True, null=True)
+ ticked = models.BooleanField(default=False)
location_description = models.TextField(blank=True)
- nearest_station_description = models.CharField(max_length=400, blank=True, null=True)
+ completion_description = models.TextField(blank=True)
+ completion_date = models.DateField(blank=True, null=True)
nearest_station_name = models.CharField(max_length=200, blank=True, null=True)
resolution_station_name = models.CharField(max_length=200, blank=True, null=True)
- nearest_station = models.ForeignKey("SurvexStation", blank=True, null=True, on_delete=models.SET_NULL)
area = models.CharField(max_length=100, blank=True, null=True)
- completion_description = models.TextField(blank=True, null=True)
+ page_ref = models.TextField(blank=True, null=True)
comment = models.TextField(blank=True, null=True)
def __str__(self):
@@ -228,8 +209,8 @@ class QM(TroggleModel):
),
)
- def get_next_by_id(self):
+ def get_next_by_id(self): # called in template
return QM.objects.get(id=self.id + 1)
- def get_previous_by_id(self):
+ def get_previous_by_id(self): # called in template
return QM.objects.get(id=self.id - 1)
diff --git a/core/views/caves.py b/core/views/caves.py
index fa5e7ef..c866718 100644
--- a/core/views/caves.py
+++ b/core/views/caves.py
@@ -536,9 +536,11 @@ def caveQMs(request, slug):
def qm(request, cave_id, qm_id, year, grade=None, blockname=None):
"""Reports on one specific QM
Fixed and working July 2022, for both CSV imported QMs
- needs refactoring though.
+
+ Needs refactoring though! Uses extremely baroque way of getting the QMs instead of querying for QM objects
+ directly, presumably as a result of a baroque history.
- 290 has several QMS with the same number, grade, year (2108) and first 8 chars of the survexblock. This crashes things.
+ Many caves have several QMS with the same number, grade, year (2018) and first 8 chars of the survexblock. This crashes things, so the terminal char of the survexblock name was added
"""
year = int(year)
@@ -547,7 +549,7 @@ def qm(request, cave_id, qm_id, year, grade=None, blockname=None):
# CSV import QMs, use old technique
try:
c = getCave(cave_id)
- manyqms = c.get_QMs()
+ manyqms = c.get_open_QMs() | c.get_ticked_QMs() # set union operation
qm = manyqms.get(number=qm_id, expoyear=year)
return render(request, "qm.html", {"qm": qm})
except QM.DoesNotExist:
@@ -565,12 +567,12 @@ def qm(request, cave_id, qm_id, year, grade=None, blockname=None):
qmslug = f"{cave_id}-{year}-{blockname=}{qm_id}{grade}"
print(f"{qmslug=}")
c = getCave(cave_id)
- manyqms = c.get_QMs()
+ manyqms = c.get_open_QMs() | c.get_ticked_QMs() # set union operation
qmqs = manyqms.filter(expoyear=year, blockname=blockname, number=qm_id, grade=grade)
if len(qmqs) > 1:
for q in qmqs:
print(qmqs)
- message = f"Multiple QMs with the same cave, year, number, grade AND first 8 chars of the survexblock name. (Could be caused by incomplete databasereset). Fix this in the survex file(s). {cave_id=} {year=} {qm_id=} {blockname=}"
+ message = f"Multiple QMs with the same cave, year, number, grade AND first-several+terminal chars of the survexblock name. (Could be caused by incomplete databasereset). Fix this in the survex file(s). {cave_id=} {year=} {qm_id=} {blockname=}"
return render(request, "errors/generic.html", {"message": message})
else:
qm = qmqs.get(expoyear=year, blockname=blockname, number=qm_id, grade=grade)
@@ -588,7 +590,7 @@ def qm(request, cave_id, qm_id, year, grade=None, blockname=None):
{"badslug": f"Failed get {cave_id=} {year=} {qm_id=} {grade=} {blockname=}"},
)
except MultipleObjectsReturned:
- message = f"Multiple QMs with the same cave, year, number, grade AND first 8 chars of the survexblock name. (Could be caused by incomplete databasereset). Fix this in the survex file(s). {cave_id=} {year=} {qm_id=} {blockname=}"
+ message = f"Multiple QMs with the same cave, year, number, grade AND first-several+terminal chars of the survexblock name. (Could be caused by incomplete databasereset). Fix this in the survex file(s). {cave_id=} {year=} {qm_id=} {blockname=}"
return render(request, "errors/generic.html", {"message": message})
except QM.DoesNotExist:
# raise
diff --git a/md5bash.sh b/md5bash.sh
new file mode 100644
index 0000000..9a2ed8f
--- /dev/null
+++ b/md5bash.sh
@@ -0,0 +1,79 @@
+#!/bin/bash
+
+# read in the input string from command line arguments
+input=$1
+
+# pad the input string with a single "1" bit
+padded_input="$input"$(echo -ne '\x80')
+
+# pad the input string to a multiple of 512 bits (64 bytes)
+while (( $(echo -n "$padded_input" | wc -c) % 64 != 56 ))
+do
+ padded_input="$padded_input"$(echo -ne '\x00')
+done
+
+# append the length of the input string (in bits) as a 64-bit little-endian integer
+length=$(echo -n "$input" | wc -c)
+length=$(echo "obase=16; $length * 8" | bc | xxd -p -c 16 | tac | tr -d '\n')
+while (( $(echo -n "$length" | wc -c) < 16 ))
+do
+ length="$length"0
+done
+padded_input="$padded_input"$(echo -ne "$length" | xxd -r -p)
+
+# initialize the buffer (A, B, C, D)
+A="67452301"
+B="efcdab89"
+C="98badcfe"
+D="10325476"
+
+# process the input in 512-bit (64-byte) chunks
+for (( i=0; i<$(echo -n "$padded_input" | wc -c)/64; i++ ))
+do
+ chunk=$(echo -n "$padded_input" | dd bs=64 skip=$i count=1 2>/dev/null | xxd -p -c 64)
+
+ # initialize the message schedule (M)
+ M=()
+ for (( j=0; j<16; j++ ))
+ do
+ word=$(echo -ne "${chunk:$j*8:8}" | xxd -r -p | od -An -tu4 -v)
+ M+=($word)
+ done
+ for (( j=16; j<64; j++ ))
+ do
+ word1=${M[j-15]}
+ s0=$(( (word1>>7 | word1<<25) ^ (word1>>18 | word1<<14) ^ (word1>>3) ))
+
+ word2=${M[j-2]}
+ s1=$(( (word2>>17 | word2<<15) ^ (word2>>19 | word2<<13) ^ (word2>>10) ))
+
+ M+=($((M[j-16] + s0 + M[j-7] + s1)))
+ done
+
+ # initialize the working variables
+ AA=$A
+ BB=$B
+ CC=$C
+ DD=$D
+
+ # round 1
+ for (( j=0; j<16; j++ ))
+ do
+ F=$(( (B & C) | (~B & D) ))
+ g=$j
+ dTemp=$((D))
+ D=$((C))
+ C=$((B))
+ B=$((B + ((A + F + M[g] + 0xd76aa478) & 0xffffffff)))
+ A=$((dTemp))
+ done
+
+ # round 2
+ for (( j=16; j<32; j++ ))
+ do
+ F=$(( (D & B) | (~D & C) ))
+ g=$(( (5*j + 1) % 16 ))
+ dTemp=$((D))
+ D=$((C))
+ C=$((B))
+
diff --git a/parsers/QMs.py b/parsers/QMs.py
index 7a553be..f3044ff 100644
--- a/parsers/QMs.py
+++ b/parsers/QMs.py
@@ -65,7 +65,6 @@ def parseCaveQMs(cave, inputFile, ticked=False):
nqms = parse_KH_QMs(kh, inputFile=inputFile, ticked=ticked)
return nqms
- # qmPath = settings.EXPOWEB+inputFile
qmPath = Path(settings.EXPOWEB, inputFile)
with open(qmPath, "r") as qmCSVContents:
@@ -76,15 +75,17 @@ def parseCaveQMs(cave, inputFile, ticked=False):
n = 0
nqms = 0
for line in qmReader:
+ #"Number","Grade","Area","Description","Page reference","Nearest survey station","Completion description","Comment"
try:
n += 1
year = int(line[0][1:5])
f"PH_{int(year)}_{int(n):02d}"
QMnum = re.match(r".*?-\d*?-X?(?P For how to set up your own QMs, see Adding QMs .
For full explanation of the current status of the QM system(s), see scriptsqms page.
{% endblock %}
{% block content %}
- {% if cave.get_QMs %}
- Extant
-
- {% for QM in cave.get_QMs %}
- {% if QM.ticked %}
- {% else %}
+
Open leads
+ {% for QM in cave.get_open_QMs %}
+
+§ QM.nearest_station_name
+☆ QM.resolution_station_name
Ticked off
-
- {% for QM in cave.get_QMs %}
- {% if QM.ticked %}
+
{% for QM in cave.get_ticked_QMs %}
-{% endif %}
Previous QM | +QM {{qm|safe}} | +Next QM | +
---|
Explanation of Grade letters: Handbook - QM grades + +{% if qm.block %} +
For full explanation of the current status of the QM system(s), see scriptsqms page. - -
Previous QM | -QM {{qm|safe}} | -Next QM | -
---|
Explanation of Grade letters: Handbook - QM grades - -{% if qm.block %} -