Compare commits
12 Commits
Author | SHA1 | Date |
---|---|---|
Sebastian Lohff | ff94b62212 | 2 years ago |
Sebastian Lohff | 94d2f5a8d8 | 4 years ago |
Sebastian Lohff | 59a4c73bd0 | 4 years ago |
Sebastian Lohff | c43c5c1e10 | 4 years ago |
Sebastian Lohff | e049e45698 | 4 years ago |
Sebastian Lohff | e825685105 | 4 years ago |
Sebastian Lohff | ddb09d148e | 4 years ago |
Sebastian Lohff | 782f5cbe32 | 4 years ago |
Sebastian Lohff | 99ab46272b | 4 years ago |
Sebastian Lohff | 92007bab02 | 4 years ago |
Sebastian Lohff | 9efdeda3c6 | 4 years ago |
Sebastian Lohff | 4b8a444f1f | 4 years ago |
@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ApiConfig(AppConfig):
|
||||
name = 'api'
|
@ -0,0 +1,3 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
@ -0,0 +1,98 @@
|
||||
from django import forms
|
||||
from django.utils import timezone
|
||||
from rest_framework import serializers
|
||||
|
||||
from contest.models import Contest, Band, Frequency, QSO, EntryCategory, User, ShadowCall, Reference
|
||||
from contest.validators import CallLogValidator
|
||||
|
||||
|
||||
class ContestSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Contest
|
||||
# FIXME: add callQrg
|
||||
fields = ('id', 'shortName', 'deadline', 'qsoStartTime', 'qsoEndTime', 'callQrg')
|
||||
|
||||
|
||||
class BandSerializer(serializers.ModelSerializer):
|
||||
# contest = ContestSerializer()
|
||||
|
||||
class Meta:
|
||||
model = Band
|
||||
fields = ('id', 'name', 'contest')
|
||||
|
||||
|
||||
class FrequencySerializer(serializers.ModelSerializer):
|
||||
# band = BandSerializer()
|
||||
|
||||
class Meta:
|
||||
model = Frequency
|
||||
fields = ('id', 'channel', 'qrg', 'band', 'note')
|
||||
|
||||
|
||||
class EntryCategorySerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = EntryCategory
|
||||
fields = ('id', 'name', 'description')
|
||||
|
||||
|
||||
class ReferenceSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Reference
|
||||
fields = ('id', 'name', 'description')
|
||||
|
||||
|
||||
class UserSerializer(serializers.ModelSerializer):
|
||||
# ref = ReferenceSerializer()
|
||||
# cat = EntryCategorySerializer()
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('id', 'ref', 'cat', 'location', 'opName', 'regTime', 'dncall', 'qrv2m', 'qrv70cm', 'extra2m70cm')
|
||||
read_only_fields = ('ref', 'location', 'regTime')
|
||||
|
||||
def validate(self, attrs):
|
||||
contest = Contest.get_current_contest()
|
||||
if contest.deadline < timezone.now():
|
||||
raise serializers.ValidationError("The deadline for changing the entry category has passed")
|
||||
|
||||
return attrs
|
||||
|
||||
|
||||
class QSOSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = QSO
|
||||
fields = ('id', 'owner', 'time', 'ownNo', 'band', 'call', 'reportTX', 'reportRX', 'refStr', 'remarks')
|
||||
read_only_fields = ('owner',)
|
||||
|
||||
def validate_call(self, value):
|
||||
val = value.strip().upper()
|
||||
try:
|
||||
CallLogValidator()(val)
|
||||
except forms.ValidationError as e:
|
||||
raise serializers.ValidationError({'errors': e.error_list})
|
||||
|
||||
return val
|
||||
|
||||
def validate(self, attrs):
|
||||
ownNo = attrs['ownNo']
|
||||
try:
|
||||
o = QSO.objects.get(owner=self.context['request'].user, ownNo=ownNo)
|
||||
if not (self.instance and self.instance.id and self.instance.id == o.id):
|
||||
raise serializers.ValidationError("You already logged a QSO with the number %s" % ownNo)
|
||||
except QSO.DoesNotExist:
|
||||
pass
|
||||
|
||||
band = attrs.get('band')
|
||||
if band:
|
||||
if band.contest.deadline < timezone.now():
|
||||
raise serializers.ValidationError("The deadline for logging and editing QSOs has passed")
|
||||
|
||||
return attrs
|
||||
|
||||
|
||||
class ShadowCallSerializer(serializers.ModelSerializer):
|
||||
ref = ReferenceSerializer()
|
||||
|
||||
class Meta:
|
||||
model = ShadowCall
|
||||
fields = ('id', 'username', 'ref', 'location', 'opName', 'regTime')
|
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
@ -0,0 +1,20 @@
|
||||
from django.conf.urls import include, url
|
||||
from rest_framework import routers
|
||||
|
||||
from .views import ContestViewSet, BandViewSet, FrequencyViewSet, EntryCategoryViewSet, ReferenceViewSet, QSOViewSet, \
|
||||
ShadowCallViewSet, UserProfileViewSet
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
router.register('contests', ContestViewSet)
|
||||
router.register('bands', BandViewSet)
|
||||
router.register('frequencies', FrequencyViewSet)
|
||||
router.register('entrycategories', EntryCategoryViewSet)
|
||||
router.register('references', ReferenceViewSet)
|
||||
router.register('qsos', QSOViewSet, basename='qso')
|
||||
router.register('shadowcalls', ShadowCallViewSet)
|
||||
router.register('profile', UserProfileViewSet, basename='profile')
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^', include(router.urls)),
|
||||
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
|
||||
]
|
@ -0,0 +1,91 @@
|
||||
from rest_framework.permissions import IsAuthenticated, IsAdminUser
|
||||
from rest_framework import viewsets
|
||||
|
||||
from .serializers import ContestSerializer, BandSerializer, FrequencySerializer, EntryCategorySerializer, \
|
||||
ReferenceSerializer, QSOSerializer, ShadowCallSerializer, UserSerializer
|
||||
from contest.models import Contest, Band, Frequency, EntryCategory, Reference, QSO, ShadowCall, User
|
||||
|
||||
|
||||
class ContestViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
"""
|
||||
Resource to list and view all available contests. Use `current/` to get the current Contest.
|
||||
"""
|
||||
queryset = Contest.objects.all()
|
||||
serializer_class = ContestSerializer
|
||||
filterset_fields = ['shortName']
|
||||
|
||||
def get_object(self):
|
||||
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
|
||||
if self.kwargs.get(lookup_url_kwarg) == "current":
|
||||
obj = Contest.get_current_contest()
|
||||
self.check_object_permissions(self.request, obj)
|
||||
else:
|
||||
obj = super(ContestViewSet, self).get_object()
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
class BandViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
queryset = Band.objects.all()
|
||||
serializer_class = BandSerializer
|
||||
filterset_fields = ['name', 'contest']
|
||||
|
||||
|
||||
class FrequencyViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
queryset = Frequency.objects.all()
|
||||
serializer_class = FrequencySerializer
|
||||
filterset_fields = ['band', 'channel']
|
||||
|
||||
|
||||
class EntryCategoryViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
queryset = EntryCategory.objects.all()
|
||||
serializer_class = EntryCategorySerializer
|
||||
filterset_fields = ['name']
|
||||
|
||||
|
||||
class ReferenceViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
permission_classes = [IsAdminUser]
|
||||
queryset = Reference.objects.all()
|
||||
serializer_class = ReferenceSerializer
|
||||
filterset_fields = ['name']
|
||||
|
||||
|
||||
class QSOViewSet(viewsets.ModelViewSet):
|
||||
permission_classes = [IsAuthenticated]
|
||||
serializer_class = QSOSerializer
|
||||
filterset_fields = ['time', 'ownNo', 'band', 'call', 'refStr']
|
||||
|
||||
def get_queryset(self):
|
||||
return QSO.objects.filter(owner=self.request.user)
|
||||
|
||||
def perform_create(self, serializer):
|
||||
return serializer.save(owner=self.request.user)
|
||||
|
||||
|
||||
class UserProfileViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
Resource to view user-profiles, currently restricted to the current user's profile.
|
||||
Use `me/` to get the profile of the currently logged in user.
|
||||
"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
serializer_class = UserSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
return User.objects.filter(id=self.request.user.id)
|
||||
|
||||
def get_object(self):
|
||||
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
|
||||
if self.kwargs.get(lookup_url_kwarg) == "me":
|
||||
obj = self.request.user
|
||||
self.check_object_permissions(self.request, obj)
|
||||
else:
|
||||
obj = super(ContestViewSet, self).get_object()
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
class ShadowCallViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
permission_classes = [IsAdminUser]
|
||||
queryset = ShadowCall.objects.all()
|
||||
serializer_class = ShadowCallSerializer
|
||||
filterset_fields = ['username', 'ref']
|
@ -1,160 +1,171 @@
|
||||
import re
|
||||
|
||||
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
|
||||
from .models import Contest, Band
|
||||
|
||||
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
|
||||
""" 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")
|
||||
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)
|
||||
def clean_data(self):
|
||||
rawData = self.cleaned_data["data"]
|
||||
parsedData = parseCBR(rawData)
|
||||
|
||||
return parsedData
|
||||
|
||||
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
|
||||
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})
|
||||
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})
|
||||
|
@ -1,174 +1,180 @@
|
||||
from django import forms
|
||||
from django.contrib.auth.forms import UserCreationForm
|
||||
from django.utils import timezone
|
||||
|
||||
from crispy_forms.helper import FormHelper
|
||||
from crispy_forms.layout import Submit, Layout
|
||||
from django import forms
|
||||
from django.contrib.auth.forms import UserCreationForm
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
|
||||
from .models import User, Reference, QSO, ShadowCall, EntryCategory, Contest
|
||||
from .validators import CallUsernameValidator, CallLogValidator
|
||||
|
||||
|
||||
class CustomUserCreationForm(UserCreationForm):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ("username", "email", "dncall", "qrv2m", "qrv70cm", "extra2m70cm")
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ("username", "email", "dncall", "qrv2m", "qrv70cm", "extra2m70cm")
|
||||
|
||||
username = forms.CharField(max_length=50, validators=[CallUsernameValidator()])
|
||||
email = forms.EmailField(required=True)
|
||||
|
||||
username = forms.CharField(max_length=50, validators=[CallUsernameValidator()])
|
||||
email = forms.EmailField(required=True)
|
||||
|
||||
class UpdateRefForm(forms.Form):
|
||||
existingRef = forms.ModelChoiceField(label="Existing Exchange", queryset=Reference.objects.all(), help_text="If exchange already exists, select it here.", required=False)
|
||||
newRefName = forms.CharField(max_length=50, label="New Exchange", help_text="Enter name of new exchange, if we should create a new", required=False)
|
||||
existingRef = forms.ModelChoiceField(label="Existing Exchange", queryset=Reference.objects.all(),
|
||||
help_text="If exchange already exists, select it here.", required=False)
|
||||
newRefName = forms.CharField(max_length=50, label="New Exchange",
|
||||
help_text="Enter name of new exchange, if we should create a new", required=False)
|
||||
|
||||
location = forms.CharField(max_length=128, label='Exact Location',
|
||||
help_text="E.g. MAR bei den Fahrstuehlen, TEL 15. OG", required=False)
|
||||
opName = forms.CharField(max_length=128, label='Operators', help_text="Name of operator(s)", required=False)
|
||||
regTime = forms.DateTimeField(label="Registration time", help_text="Time of Registration")
|
||||
|
||||
location = forms.CharField(max_length=128, label='Exact Location', help_text="E.g. MAR bei den Fahrstuehlen, TEL 15. OG", required=False)
|
||||
opName = forms.CharField(max_length=128, label='Operators', help_text="Name of operator(s)", required=False)
|
||||
regTime = forms.DateTimeField(label="Registration time", help_text="Time of Registration")
|
||||
def clean_newRefName(self):
|
||||
data = self.cleaned_data["newRefName"].strip().upper()
|
||||
|
||||
def clean_newRefName(self):
|
||||
data = self.cleaned_data["newRefName"].strip().upper()
|
||||
return data
|
||||
|
||||
return data
|
||||
def clean(self):
|
||||
cleaned_data = super(UpdateRefForm, self).clean()
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super(UpdateRefForm, self).clean()
|
||||
existingRef = cleaned_data.get("existingRef")
|
||||
newRefName = cleaned_data.get("newRefName")
|
||||
|
||||
existingRef = cleaned_data.get("existingRef")
|
||||
newRefName = cleaned_data.get("newRefName")
|
||||
if newRefName:
|
||||
try:
|
||||
ref = Reference.objects.get(name=newRefName)
|
||||
self.cleaned_data['newRefName'] = None
|
||||
self.cleaned_data['existingRef'] = ref
|
||||
except Reference.DoesNotExist:
|
||||
pass
|
||||
|
||||
if newRefName:
|
||||
try:
|
||||
ref = Reference.objects.get(name=newRefName)
|
||||
self.cleaned_data['newRefName'] = None
|
||||
self.cleaned_data['existingRef'] = ref
|
||||
except Reference.DoesNotExist:
|
||||
pass
|
||||
if existingRef and newRefName:
|
||||
raise forms.ValidationError("Select an existing exchange or create a new one, not both!")
|
||||
if not existingRef and not newRefName:
|
||||
raise forms.ValidationError("Select either an existing exchange or create a new one!")
|
||||
|
||||
if existingRef and newRefName:
|
||||
raise forms.ValidationError("Select an existing exchange or create a new one, not both!")
|
||||
if not existingRef and not newRefName:
|
||||
raise forms.ValidationError("Select either an existing exchange or create a new one!")
|
||||
|
||||
class UpdateCategoryForm(forms.Form):
|
||||
entry = forms.ModelChoiceField(label="Entry category", queryset=EntryCategory.objects.all())
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(UpdateCategoryForm, self).__init__(*args, **kwargs)
|
||||
entry = forms.ModelChoiceField(label="Entry category", queryset=EntryCategory.objects.all())
|
||||
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_class = "form-inline "
|
||||
self.helper.form_style = 'inline'
|
||||
self.helper.field_template = "bootstrap3/layout/inline_field.html"
|
||||
self.helper.action = reverse("profile")
|
||||
self.helper.add_input(Submit('submit', 'Set category'))
|
||||
self.helper.layout = Layout('entry')
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(UpdateCategoryForm, self).__init__(*args, **kwargs)
|
||||
|
||||
def clean(self):
|
||||
contest = Contest.objects.get(id=1)
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_class = "form-inline "
|
||||
self.helper.form_style = 'inline'
|
||||
self.helper.field_template = "bootstrap3/layout/inline_field.html"
|
||||
self.helper.action = reverse("profile")
|
||||
self.helper.add_input(Submit('submit', 'Set category'))
|
||||
self.helper.layout = Layout('entry')
|
||||
|
||||
def clean(self):
|
||||
contest = Contest.objects.get(id=1)
|
||||
|
||||
if contest.deadline < timezone.now():
|
||||
raise forms.ValidationError("The deadline for setting your contest category has passed")
|
||||
|
||||
if contest.deadline < timezone.now():
|
||||
raise forms.ValidationError("The deadline for setting your contest category has passed")
|
||||
|
||||
class QSOForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = QSO
|
||||
#fields = ["ownNo", "band", "call", "reportTX", "reportRX", "refStr", "otherNo", "remarks"]
|
||||
fields = ["ownNo", "band", "call", "reportTX", "reportRX", "refStr", "remarks"]
|
||||
class Meta:
|
||||
model = QSO
|
||||
# fields = ["ownNo", "band", "call", "reportTX", "reportRX", "refStr", "otherNo", "remarks"]
|
||||
fields = ["ownNo", "band", "call", "reportTX", "reportRX", "refStr", "remarks"]
|
||||
|
||||
def __init__(self, user, *args, **kwargs):
|
||||
super(QSOForm, self).__init__(*args, **kwargs)
|
||||
self.user = user
|
||||
def __init__(self, user, *args, **kwargs):
|
||||
super(QSOForm, self).__init__(*args, **kwargs)
|
||||
self.user = user
|
||||
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_id = "qso-log-form"
|
||||
#self.helper.form_class = "form-inline "
|
||||
#self.helper.form_class = "form-horizontal"
|
||||
#self.helper.form_style = 'inline'
|
||||
#self.helper.field_template = "bootstrap3/layout/inline_field.html"
|
||||
self.helper.action = reverse("contest:log")
|
||||
self.helper.add_input(Submit('submit', 'Log'))
|
||||
#self.helper.layout = Layout(
|
||||
# #*(QSOForm.Meta.fields + [ButtonHolder(Submit('submit', 'Submit', css_class='button white'))]))
|
||||
# *(QSOForm.Meta.fields + [FormActions(Submit('submit', 'Log!'))]))
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_id = "qso-log-form"
|
||||
# self.helper.form_class = "form-inline "
|
||||
# self.helper.form_class = "form-horizontal"
|
||||
# self.helper.form_style = 'inline'
|
||||
# self.helper.field_template = "bootstrap3/layout/inline_field.html"
|
||||
self.helper.action = reverse("contest:log")
|
||||
self.helper.add_input(Submit('submit', 'Log'))
|
||||
# self.helper.layout = Layout(
|
||||
# #*(QSOForm.Meta.fields + [ButtonHolder(Submit('submit', 'Submit', css_class='button white'))]))
|
||||
# *(QSOForm.Meta.fields + [FormActions(Submit('submit', 'Log!'))]))
|
||||
|
||||
def clean_call(self):
|
||||
data = self.cleaned_data["call"].upper().strip()
|
||||
|
||||
def clean_call(self):
|
||||
data = self.cleaned_data["call"].upper().strip()
|
||||
if Reference.objects.filter(name=data).count() > 0:
|
||||
raise forms.ValidationError("Reference already exists")
|
||||
try:
|
||||
CallLogValidator()(data)
|
||||
except forms.ValidationError:
|
||||
raise forms.ValidationError("Enter a valid callsign (1-2 chars, a number, 1-4 chars, maybe a /[A-Z])")
|
||||
|
||||
try:
|
||||
CallLogValidator()(data)
|
||||
except forms.ValidationError:
|
||||
raise forms.ValidationError("Enter a valid callsign (1-2 chars, a number, 1-4 chars, maybe a /[A-Z])")
|
||||
if data == self.user.username:
|
||||
raise forms.ValidationError("You cannot log QSOs with yourself")
|
||||
|
||||
if data == self.user.username:
|
||||
raise forms.ValidationError("You cannot log QSOs with yourself")
|
||||
return data
|
||||
|
||||
return data
|
||||
def clean_ownNo(self):
|
||||
data = self.cleaned_data["ownNo"]
|
||||
|
||||
def clean_ownNo(self):
|
||||
data = self.cleaned_data["ownNo"]
|
||||
if data < 1 or data > 100000:
|
||||
raise forms.ValidationError("Number has to be in range of [1, 1000000]")
|
||||
|
||||
if data < 1 or data > 100000:
|
||||
raise forms.ValidationError("Number has to be in range of [1, 1000000]")
|
||||
try:
|
||||
o = QSO.objects.get(owner=self.user.id, ownNo=data)
|
||||
if not (self.instance and self.instance.id and self.instance.id == o.id):
|
||||
raise forms.ValidationError("You already logged a QSO with the number %s" % data)
|
||||
except QSO.DoesNotExist:
|
||||
pass
|
||||
|
||||
try:
|
||||
o = QSO.objects.get(owner=self.user.id, ownNo=data)
|
||||
if not (self.instance and self.instance.id and self.instance.id == o.id):
|
||||
raise forms.ValidationError("You already logged a QSO with the number %s" % data)
|
||||
except QSO.DoesNotExist:
|
||||
pass
|
||||
return data
|
||||
|
||||
return data
|
||||
def clean_otherNo(self):
|
||||
data = self.cleaned_data["otherNo"]
|
||||
|
||||
def clean_otherNo(self):
|
||||
data = self.cleaned_data["otherNo"]
|
||||
if not data:
|
||||
# empty value
|
||||
return None
|
||||
|
||||
if not data:
|
||||
# empty value
|
||||
return None
|
||||
if data < 1 or data > 100000:
|
||||
raise forms.ValidationError("Number has to be in range of [1, 1000000]")
|
||||
|
||||
if data < 1 or data > 100000:
|
||||
raise forms.ValidationError("Number has to be in range of [1, 1000000]")
|
||||
return data
|
||||
|
||||
return data
|
||||
def clean_refStr(self):
|
||||
return self.cleaned_data["refStr"].upper()
|
||||
|
||||
def clean_refStr(self):
|
||||
return self.cleaned_data["refStr"].upper()
|
||||
def clean(self):
|
||||
cleaned_data = super(QSOForm, self).clean()
|
||||
band = cleaned_data.get("band")
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super(QSOForm, self).clean()
|
||||
band = cleaned_data.get("band")
|
||||
if band.contest.deadline < timezone.now():
|
||||
raise forms.ValidationError("The deadline for logging and editing QSOs has passed")
|
||||
|
||||
if band.contest.deadline < timezone.now():
|
||||
raise forms.ValidationError("The deadline for logging and editing QSOs has passed")
|
||||
|
||||
class QSOFormWithTime(QSOForm):
|
||||
class Meta:
|
||||
model = QSO
|
||||
#fields = ["time", "ownNo", "band", "call", "reportTX", "reportRX", "otherNo", "refStr", "remarks"]
|
||||
fields = ["time", "ownNo", "band", "call", "reportTX", "reportRX", "refStr", "remarks"]
|
||||
class Meta:
|
||||
model = QSO
|
||||
# fields = ["time", "ownNo", "band", "call", "reportTX", "reportRX", "otherNo", "refStr", "remarks"]
|
||||
fields = ["time", "ownNo", "band", "call", "reportTX", "reportRX", "refStr", "remarks"]
|
||||
|
||||
|
||||
class ShadowCallAddForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = ShadowCall
|
||||
fields = ['username']
|
||||
class Meta:
|
||||
model = ShadowCall
|
||||
fields = ['username']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ShadowCallAddForm, self).__init__(*args, **kwargs)
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ShadowCallAddForm, self).__init__(*args, **kwargs)
|
||||
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_class = "form-inline "
|
||||
self.helper.form_style = 'inline'
|
||||
self.helper.field_template = "bootstrap3/layout/inline_field.html"
|
||||
self.helper.action = reverse("contest:registerRefs")
|
||||
self.helper.add_input(Submit('submit', 'Add shadow'))
|
||||
self.helper.layout = Layout('username')
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_class = "form-inline "
|
||||
self.helper.form_style = 'inline'
|
||||
self.helper.field_template = "bootstrap3/layout/inline_field.html"
|
||||
self.helper.action = reverse("contest:registerRefs")
|
||||
self.helper.add_input(Submit('submit', 'Add shadow'))
|
||||
self.helper.layout = Layout('username')
|
||||
|
||||
def clean_username(self):
|
||||
data = self.cleaned_data["username"]
|
||||
if User.objects.filter(username=data).count() > 0:
|
||||
raise forms.ValidationError("A user with this call already exists, this is not a shadow!")
|
||||
def clean_username(self):
|
||||
data = self.cleaned_data["username"]
|
||||
if User.objects.filter(username=data).count() > 0:
|
||||
raise forms.ValidationError("A user with this call already exists, this is not a shadow!")
|
||||
|
||||
return data
|
||||
return data
|
||||
|
@ -1,18 +1,18 @@
|
||||
|
||||
def checkForShadowCall(sender, instance, created, raw, **kwargs):
|
||||
""" Check for existing shadow call. If present copy it's data and delete it. """
|
||||
if created:
|
||||
# to prevent circular imports we import ShadowCall here
|
||||
from .models import ShadowCall
|
||||
""" Check for existing shadow call. If present copy it's data and delete it. """
|
||||
if created:
|
||||
# to prevent circular imports we import ShadowCall here
|
||||
from .models import ShadowCall
|
||||
|
||||
try:
|
||||
shadow = ShadowCall.objects.get(username=instance.username)
|
||||
instance.ref = shadow.ref
|
||||
instance.location = shadow.location
|
||||
instance.opName = shadow.opName
|
||||
instance.regTime = shadow.regTime
|
||||
try:
|
||||
shadow = ShadowCall.objects.get(username=instance.username)
|
||||
instance.ref = shadow.ref
|
||||
instance.location = shadow.location
|
||||
instance.opName = shadow.opName
|
||||
instance.regTime = shadow.regTime
|
||||
|
||||
instance.save()
|
||||
shadow.delete()
|
||||
except ShadowCall.DoesNotExist:
|
||||
pass
|
||||
instance.save()
|
||||
shadow.delete()
|
||||
except ShadowCall.DoesNotExist:
|
||||
pass
|
||||
|
@ -1,2 +1,4 @@
|
||||
Django==1.10
|
||||
Django<1.12
|
||||
django-crispy-forms
|
||||
django-rest-framework
|
||||
django-filter
|
||||
|
Loading…
Reference in new issue