172 lines
6.3 KiB
Python
172 lines
6.3 KiB
Python
import re
|
|
|
|
from django.shortcuts import render
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.utils import timezone
|
|
from django.contrib import messages
|
|
from django import forms
|
|
from django.urls import reverse
|
|
from django.http import HttpResponseRedirect
|
|
|
|
from .forms import QSOFormWithTime
|
|
from .models import Contest, Band
|
|
|
|
|
|
def parseCBR(raw):
|
|
""" Parse a CBR file for the CQTU
|
|
|
|
Yes, this could be used for other tools, BUT you'd have to take
|
|
look at the regex and parsingfoo, as there is some cqtu specific foo
|
|
inside them.
|
|
"""
|
|
kvlinere = re.compile(r"^(?P<key>[A-Z-]+):(?: (?P<value>.*))?$")
|
|
qsore = re.compile(r"^(?P<band>144|432)\s+(?P<mode>[A-Z]{2})\s+(?P<datetime>\d{4}-\d{2}-\d{2} \d{4}) "
|
|
r"(?P<call_s>[A-Z0-9/-]+)\s+(?P<rst_s>\d{2,3})\s+(?P<exc_s>[A-Z0-9-]+)\s+"
|
|
r"(?P<call_r>[A-Z0-9/-]+)\s+(?P<rst_r>\d{2,3})\s+(?P<exc_r>[A-Z0-9-]+)\s+0$")
|
|
|
|
qsoNo = 1
|
|
info = {
|
|
"call": None,
|
|
"location": None,
|
|
"qsos": [],
|
|
}
|
|
|
|
for n, line in enumerate(raw.split("\n"), 1):
|
|
line = line.strip()
|
|
# ignore empty lines
|
|
if line == "":
|
|
continue
|
|
|
|
m = kvlinere.match(line)
|
|
if m:
|
|
k = m.group("key")
|
|
if k == "CALLSIGN":
|
|
info["call"] = m.group("value").strip().upper()
|
|
elif k == "LOCATION":
|
|
info["location"] = m.group("value").strip().upper()
|
|
elif k == "QSO":
|
|
q = m.group("value").strip()
|
|
# no-s FM date UTC-HHMM call rst-s exch-s call-r rst-r exch-r
|
|
# no-s / date UTC-HH-MM call rst-s
|
|
qm = qsore.search(q)
|
|
if qm:
|
|
qsoData = qm.groupdict()
|
|
qsoTime = timezone.datetime.strptime(qsoData["datetime"], "%Y-%m-%d %H%M")
|
|
qsoData["datetime"] = timezone.get_current_timezone().localize(qsoTime)
|
|
qsoData["no_s"] = qsoNo
|
|
|
|
if qsoData["band"] == "144":
|
|
qsoData["band"] = "2m"
|
|
elif qsoData["band"] == "432":
|
|
qsoData["band"] = "70cm"
|
|
else:
|
|
raise forms.ValidationError("Error parsing band, needs to be either 144 or 432 "
|
|
"(as we only support 2m and 70cm in this contest")
|
|
|
|
info["qsos"].append(qsoData)
|
|
|
|
if info["call"] != qsoData["call_s"]:
|
|
raise forms.ValidationError("Error in line %d: qso was not made by you? "
|
|
"(callsigns do not match)" % n)
|
|
if info["location"] != qsoData["exc_s"]:
|
|
raise forms.ValidationError("Error in line %d: exchange does not match your location? "
|
|
"(callsigns do not match)" % n)
|
|
else:
|
|
raise forms.ValidationError("Error in line %d: qso was broken, regex did not match" % n)
|
|
|
|
qsoNo += 1
|
|
elif k == "X-QSO":
|
|
qsoNo += 1
|
|
|
|
else:
|
|
raise forms.ValidationError("Error in line %d: could not parse \"KEY: value\" pair" % n)
|
|
|
|
return info
|
|
|
|
|
|
class CBRForm(forms.Form):
|
|
data = forms.CharField(widget=forms.Textarea, label="Cabrillo data",
|
|
help_text="Paste your cabrillo file contents here")
|
|
|
|
def clean_data(self):
|
|
rawData = self.cleaned_data["data"]
|
|
parsedData = parseCBR(rawData)
|
|
|
|
return parsedData
|
|
|
|
|
|
def checkCBRConsistency(contest, user, info):
|
|
errors = []
|
|
qsos = []
|
|
if user.username != info["call"]:
|
|
errors.append("You are not the owner of this logfile! (%s != %s)" % (user.username, info["call"]))
|
|
|
|
if user.ref.name != info["location"]:
|
|
errors.append("Location of logfile and registered exchange do not match! (%s != %s)" % (user.ref.name,
|
|
info["location"]))
|
|
|
|
for n, qsoData in enumerate(info["qsos"], 1):
|
|
qsoFormData = {
|
|
"time": qsoData["datetime"],
|
|
"call": qsoData["call_r"],
|
|
"band": Band.objects.get(contest=contest, name=qsoData["band"]).id,
|
|
"reportTX": qsoData["rst_s"],
|
|
"reportRX": qsoData["rst_r"],
|
|
"ownNo": qsoData["no_s"],
|
|
"otherNo": None,
|
|
"refStr": qsoData["exc_r"],
|
|
"remarks": "",
|
|
}
|
|
|
|
qsoForm = QSOFormWithTime(user, data=qsoFormData)
|
|
qsoForm.is_valid()
|
|
qsoForm.instance.owner = user
|
|
print(qsoForm.errors)
|
|
qsos.append((qsoForm.instance, qsoForm))
|
|
|
|
return qsos, errors
|
|
|
|
|
|
@login_required
|
|
def uploadCBR(request):
|
|
if not request.user.ref:
|
|
return HttpResponseRedirect(reverse("contest:index"))
|
|
|
|
contest = Contest.objects.get(id=1)
|
|
deadline = False
|
|
form = None
|
|
verifyData = []
|
|
verifyErrors = []
|
|
save = saved = False
|
|
|
|
if timezone.now() < contest.deadline:
|
|
if request.method == "POST":
|
|
form = CBRForm(data=request.POST)
|
|
if form.is_valid():
|
|
verifyData, verifyErrors = checkCBRConsistency(contest, request.user, form.cleaned_data["data"])
|
|
|
|
if request.POST.get("action") == "save":
|
|
save = True
|
|
if not verifyErrors:
|
|
cnt = 0
|
|
for qso, qsoForm in verifyData:
|
|
if qsoForm.is_valid():
|
|
qso.save()
|
|
cnt += 1
|
|
|
|
if cnt > 0:
|
|
messages.success(request, "%d QSOs have been saved from the cbr file" % cnt)
|
|
else:
|
|
messages.warnnig(request, "CBR file was parsed, but no QSOs could be saved, "
|
|
"as all cointained errors.")
|
|
|
|
return HttpResponseRedirect(reverse("contest:uploadCBR"))
|
|
else:
|
|
form = CBRForm()
|
|
else:
|
|
deadline = True
|
|
|
|
return render(request, "contest/uploadCBR.html",
|
|
{"deadline": deadline, 'form': form, 'verifyData': verifyData, 'verifyErrors': verifyErrors,
|
|
'save': save, 'saved': saved})
|