cqtu/contest/cbrparser.py

156 lines
4.7 KiB
Python
Raw Normal View History

2017-01-26 23:47:58 +01:00
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()
qsoData["band"] = "2m" if qsoData["band"] == 144 else "70cm"
qsoTime = timezone.datetime.strptime(qsoData["datetime"], "%Y-%m-%d %H%M")
qsoData["datetime"] = timezone.get_current_timezone().localize(qsoTime)
qsoData["no_s"] = qsoNo
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:
pass
else:
form = CBRForm()
else:
deadline = True
return render(request, "contest/uploadCBR.html", {"deadline": deadline, 'form': form, 'verifyData': verifyData, 'verifyErrors': verifyErrors, 'save': save, 'saved': saved})