Compare commits

..

8 Commits

Author SHA1 Message Date
Sebastian Lohff c43c5c1e10 Move QSO no validation into model 2020-02-08 22:49:14 +01:00
Sebastian Lohff e049e45698 Remove broken check from QSO form
The check disallowed having a QSO with any callsign that is also a
reference. Obviously the user should also be able to log this kind
of QSO, even if it might not make sense. We don't judge.
2020-02-08 22:49:14 +01:00
Sebastian Lohff e825685105 Add REST API to webinterface 2020-02-08 22:42:26 +01:00
Sebastian Lohff ddb09d148e Add REST API to webinterface 2020-02-08 22:35:24 +01:00
Sebastian Lohff 782f5cbe32 Allow regTime in user to be blank as well
Without regTime being blank a user with regTime NULL cannot be edited
via the admin interface
2020-02-08 22:35:24 +01:00
Sebastian Lohff 99ab46272b Clarify choice of username at registration, again
Apparently it was still unclear to people what call they should use on
registration. Added extra message to explain this further.
2020-02-08 22:35:24 +01:00
Sebastian Lohff 92007bab02 clear_contest: make python3 ready, remove unused import 2020-02-08 22:35:24 +01:00
Sebastian Lohff 9efdeda3c6 Bump django version to 1.11 2020-02-08 22:35:24 +01:00
8 changed files with 92 additions and 19 deletions

View File

@ -1,6 +1,9 @@
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):
@ -39,20 +42,52 @@ class ReferenceSerializer(serializers.ModelSerializer):
class UserSerializer(serializers.ModelSerializer):
ref = ReferenceSerializer()
cat = EntryCategorySerializer()
# 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):
# owner = UserSerializer()
class Meta:
model = QSO
fields = ('id', 'owner', 'time', 'call', 'callRef', 'remarks')
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):

View File

@ -2,7 +2,7 @@ from django.conf.urls import include, url
from rest_framework import routers
from .views import ContestViewSet, BandViewSet, FrequencyViewSet, EntryCategoryViewSet, ReferenceViewSet, QSOViewSet, \
ShadowCallViewSet, UserProfileView
ShadowCallViewSet, UserProfileViewSet
router = routers.DefaultRouter()
router.register('contests', ContestViewSet)
@ -12,7 +12,7 @@ router.register('entrycategories', EntryCategoryViewSet)
router.register('references', ReferenceViewSet)
router.register('qsos', QSOViewSet, basename='qso')
router.register('shadowcalls', ShadowCallViewSet)
router.register('profile', UserProfileView, basename='profile')
router.register('profile', UserProfileViewSet, basename='profile')
urlpatterns = [
url(r'^', include(router.urls)),

View File

@ -1,5 +1,5 @@
from rest_framework import viewsets
from rest_framework.views import APIView
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import viewsets, generics, filters
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from rest_framework.response import Response
@ -9,49 +9,76 @@ from contest.models import Contest, Band, Frequency, EntryCategory, Reference, Q
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.ReadOnlyModelViewSet):
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 UserProfileView(APIView):
class UserProfileViewSet(generics.UpdateAPIView, viewsets.GenericViewSet):
permission_classes = [IsAuthenticated]
serializer_class = UserSerializer
def get(self, request, format=None):
def list(self, request, format=None):
user = request.user
serializer = UserSerializer(user)
return Response(serializer.data)
def get_queryset(self):
return self.request.user
class ShadowCallViewSet(viewsets.ReadOnlyModelViewSet):
permission_classes = [IsAdminUser]
queryset = ShadowCall.objects.all()
serializer_class = ShadowCallSerializer
filterset_fields = ['username', 'ref']

View File

@ -93,8 +93,6 @@ class QSOForm(forms.ModelForm):
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)

View File

@ -4,7 +4,7 @@ import datetime
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.core.validators import RegexValidator
from django.core.validators import RegexValidator, MinValueValidator, MaxValueValidator
from django.db.models import Q, signals
from .validators import CallUsernameValidator
@ -22,6 +22,11 @@ class Contest(models.Model):
def __str__(self):
return self.name
@classmethod
def get_current_contest(cls):
return cls.objects.get(id=1)
class Reference(models.Model):
name = models.CharField(max_length=20, unique=True, db_index=True)
description = models.TextField()
@ -141,6 +146,7 @@ class Frequency(models.Model):
return "Channel %s: %s MHz" % (self.channel, self.qrg)
class QSO(models.Model):
MAX_NO_VALUE = 1000000
reportValidator = RegexValidator("[1-5][1-9]")
class Meta:
@ -157,8 +163,9 @@ class QSO(models.Model):
reportTX = models.CharField(max_length=7, default=59, verbose_name='RS-S', validators=[reportValidator])
reportRX = models.CharField(max_length=7, default=59, verbose_name='RS-R', validators=[reportValidator])
ownNo = models.IntegerField(verbose_name='No')
otherNo = models.IntegerField(verbose_name='No-R', null=True, blank=True)
ownNo = models.IntegerField(verbose_name='No', validators=[MinValueValidator(1), MaxValueValidator(MAX_NO_VALUE)])
otherNo = models.IntegerField(verbose_name='No-R', null=True, blank=True,
validators=[MinValueValidator(1), MaxValueValidator(MAX_NO_VALUE)])
refStr = models.CharField(max_length=20, verbose_name="EXC")
ref = models.ForeignKey(Reference, models.SET_NULL, null=True, blank=True)

View File

@ -25,4 +25,3 @@ class CallLogValidator(validators.RegexValidator):
'e.g. DL7BST, DN1BER-1, DL/OE1FOO, DN1FTW-1/p'
)
flags = re.ASCII if six.PY3 else 0

View File

@ -42,6 +42,7 @@ INSTALLED_APPS = [
'django.contrib.staticfiles',
'crispy_forms',
'rest_framework',
'django_filters',
# local
'contest',
@ -143,3 +144,7 @@ CRISPY_TEMPLATE_PACK = 'bootstrap3'
MESSAGE_TAGS = {
messages.ERROR: 'danger',
}
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
}

View File

@ -1,2 +1,4 @@
Django==1.11
Django<1.12
django-crispy-forms
django-rest-framework
django-filter