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[A-Z-]+):(?: (?P.*))?$") qsore = re.compile(r"^(?P144|432)\s+(?P[A-Z]{2})\s+(?P\d{4}-\d{2}-\d{2} \d{4}) (?P[A-Z0-9/-]+)\s+(?P\d{2,3})\s+(?P[A-Z0-9-]+)\s+(?P[A-Z0-9/-]+)\s+(?P\d{2,3})\s+(?P[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})