You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cqtu/contest/cbrparser.py

161 lines
5.9 KiB

from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from .models import Contest, Band
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
import re
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}) (?P<call_s>[A-Z0-9/-]+)\s+(?P<rst_s>\d{2,3})\s+(?P<exc_s>[A-Z0-9-]+)\s+(?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 = {
#"owner": user,
"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})