From ff94b6221265b30539f57c5276183040ca2bfc34 Mon Sep 17 00:00:00 2001 From: Sebastian Lohff Date: Sat, 22 Jan 2022 16:40:30 +0100 Subject: [PATCH] Pep8 fixes Reformat codebase a bit so it is easier to work with. --- clear_contest.py | 1 - contest/admin.py | 3 +++ contest/cbrparser.py | 37 ++++++++++++++++++++++------------- contest/forms.py | 42 ++++++++++++++++++++++++---------------- contest/models.py | 45 ++++++++++++++++++++++++++++--------------- contest/validators.py | 2 -- contest/views.py | 31 ++++++++++++++++++----------- cqtu/settings.py | 6 ------ cqtu/urls.py | 10 ---------- 9 files changed, 101 insertions(+), 76 deletions(-) diff --git a/clear_contest.py b/clear_contest.py index 102aa28..5fc5996 100755 --- a/clear_contest.py +++ b/clear_contest.py @@ -19,7 +19,6 @@ if confirm != "YES": print("Aborting") sys.exit(1) - from contest.models import QSO, ShadowCall, Reference, User print("{0} QSOs deleted".format(*QSO.objects.all().delete())) diff --git a/contest/admin.py b/contest/admin.py index 6236378..3638a67 100644 --- a/contest/admin.py +++ b/contest/admin.py @@ -1,9 +1,12 @@ from django.contrib import admin + from .models import Frequency, Band, Reference, QSO, User, Contest, ShadowCall, EntryCategory class UserAdmin(admin.ModelAdmin): list_display = ('username', 'dncall', 'qrv2m', 'qrv70cm', 'extra2m70cm') + + admin.site.register(User, UserAdmin) admin.site.register(QSO) admin.site.register(Band) diff --git a/contest/cbrparser.py b/contest/cbrparser.py index 887c6f2..05030df 100644 --- a/contest/cbrparser.py +++ b/contest/cbrparser.py @@ -1,16 +1,16 @@ +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 @@ -20,8 +20,9 @@ def parseCBR(raw): 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$") - + qsore = re.compile(r"^(?P144|432)\s+(?P[A-Z]{2})\s+(?P\d{4}-\d{2}-\d{2} \d{4}) " + r"(?P[A-Z0-9/-]+)\s+(?P\d{2,3})\s+(?P[A-Z0-9-]+)\s+" + r"(?P[A-Z0-9/-]+)\s+(?P\d{2,3})\s+(?P[A-Z0-9-]+)\s+0$") qsoNo = 1 info = { @@ -59,14 +60,17 @@ def parseCBR(raw): 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") + 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) + 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) + 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) @@ -79,8 +83,10 @@ def parseCBR(raw): 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"] @@ -88,6 +94,7 @@ class CBRForm(forms.Form): return parsedData + def checkCBRConsistency(contest, user, info): errors = [] qsos = [] @@ -95,11 +102,11 @@ def checkCBRConsistency(contest, user, info): 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"])) + 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, @@ -119,6 +126,7 @@ def checkCBRConsistency(contest, user, info): return qsos, errors + @login_required def uploadCBR(request): if not request.user.ref: @@ -149,7 +157,8 @@ def uploadCBR(request): 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.") + messages.warnnig(request, "CBR file was parsed, but no QSOs could be saved, " + "as all cointained errors.") return HttpResponseRedirect(reverse("contest:uploadCBR")) else: @@ -157,4 +166,6 @@ def uploadCBR(request): else: deadline = True - return render(request, "contest/uploadCBR.html", {"deadline": deadline, 'form': form, 'verifyData': verifyData, 'verifyErrors': verifyErrors, 'save': save, 'saved': saved}) + return render(request, "contest/uploadCBR.html", + {"deadline": deadline, 'form': form, 'verifyData': verifyData, 'verifyErrors': verifyErrors, + 'save': save, 'saved': saved}) diff --git a/contest/forms.py b/contest/forms.py index 3dcf8d8..c2a0798 100644 --- a/contest/forms.py +++ b/contest/forms.py @@ -1,14 +1,14 @@ -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 @@ -17,11 +17,15 @@ class CustomUserCreationForm(UserCreationForm): 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) + 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") @@ -49,8 +53,10 @@ class UpdateRefForm(forms.Form): 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) @@ -68,10 +74,11 @@ class UpdateCategoryForm(forms.Form): 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", "otherNo", "remarks"] fields = ["ownNo", "band", "call", "reportTX", "reportRX", "refStr", "remarks"] def __init__(self, user, *args, **kwargs): @@ -80,16 +87,15 @@ class QSOForm(forms.ModelForm): 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.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.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() @@ -141,12 +147,14 @@ class QSOForm(forms.ModelForm): 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", "otherNo", "refStr", "remarks"] fields = ["time", "ownNo", "band", "call", "reportTX", "reportRX", "refStr", "remarks"] + class ShadowCallAddForm(forms.ModelForm): class Meta: diff --git a/contest/models.py b/contest/models.py index 404772e..a612032 100644 --- a/contest/models.py +++ b/contest/models.py @@ -2,13 +2,14 @@ from __future__ import unicode_literals import datetime -from django.db import models from django.contrib.auth.models import AbstractUser from django.core.validators import RegexValidator, MinValueValidator, MaxValueValidator +from django.db import models from django.db.models import Q, signals -from .validators import CallUsernameValidator from .signals import checkForShadowCall +from .validators import CallUsernameValidator + class Contest(models.Model): name = models.CharField(max_length=20) @@ -34,6 +35,7 @@ class Reference(models.Model): def __str__(self): return self.name + class EntryCategory(models.Model): name = models.CharField(max_length=64, unique=True) description = models.TextField(blank=True) @@ -41,9 +43,10 @@ class EntryCategory(models.Model): def __str__(self): return self.name + class ShadowCall(models.Model): username = models.CharField(max_length=20, unique=True, db_index=True, validators=[CallUsernameValidator()]) - ref = models.ForeignKey(Reference, models.SET_NULL,null=True, blank=True) + ref = models.ForeignKey(Reference, models.SET_NULL, null=True, blank=True) location = models.CharField(max_length=128, default="", blank=True) opName = models.CharField(max_length=128, default="", blank=True) @@ -52,6 +55,7 @@ class ShadowCall(models.Model): def __str__(self): return self.username + class User(AbstractUser): ref = models.ForeignKey(Reference, models.SET_NULL, null=True, blank=True) cat = models.ForeignKey(EntryCategory, models.SET_NULL, null=True, blank=True) @@ -67,17 +71,18 @@ class User(AbstractUser): # extra profile stuff so DL7BST can sleep well without his doodles editedProfile = models.BooleanField(default=False) dncall = models.CharField(max_length=16, default='', blank=True, - verbose_name="DN-Call", - help_text="If you have a DN call that you will offer to SWLs please enter it here") + verbose_name="DN-Call", + help_text="If you have a DN call that you will offer to SWLs please enter it here") qrv2m = models.BooleanField(default=False, verbose_name="QRV on 2m", help_text="Will you be QRV on 2m during the contest?") qrv70cm = models.BooleanField(default=False, - verbose_name="QRV on 70cm", - help_text="Will you be QRV on 70cm during the contest?") + verbose_name="QRV on 70cm", + help_text="Will you be QRV on 70cm during the contest?") extra2m70cm = models.BooleanField(default=False, - verbose_name="Additional 2m/70cm TRX", - help_text="Will you bring an additional 2m/70cm TRX to lend to other participants?") + verbose_name="Additional 2m/70cm TRX", + help_text="Will you bring an additional 2m/70cm TRX to lend to " + "other participants?") def __init__(self, *args, **kwargs): super(User, self).__init__(*args, **kwargs) @@ -124,8 +129,11 @@ class User(AbstractUser): "qsoCount": qsos.count(), "refCount": len(refs) } + + signals.post_save.connect(checkForShadowCall, sender=User) + class Band(models.Model): name = models.CharField(max_length=10) contest = models.ForeignKey(Contest) @@ -133,6 +141,7 @@ class Band(models.Model): def __str__(self): return self.name + class Frequency(models.Model): # qrg # band @@ -145,6 +154,7 @@ class Frequency(models.Model): def __str__(self): return "Channel %s: %s MHz" % (self.channel, self.qrg) + class QSO(models.Model): MAX_NO_VALUE = 1000000 reportValidator = RegexValidator("[1-5][1-9]") @@ -174,7 +184,7 @@ class QSO(models.Model): cfmdQSO = models.ForeignKey("QSO", models.SET_NULL, null=True, blank=True, default=None) - CFMD_SEC = 5*60 + CFMD_SEC = 5 * 60 def checkQSOData(self): """ Match strdata to log rows. Only call, if you intent to save this object if we return True! """ @@ -213,10 +223,10 @@ class QSO(models.Model): # check if this still checks out q = self.cfmdQSO if abs((self.time - q.time).total_seconds()) <= self.CFMD_SEC and \ - self.ref and self.owner.ref and self.callRef and q.callRef and \ - q.owner == self.callRef and q.callRef == self.owner and \ - self.ref == q.owner.ref and self.owner.ref == q.ref and \ - self.band == q.band: + self.ref and self.owner.ref and self.callRef and q.callRef and \ + q.owner == self.callRef and q.callRef == self.owner and \ + self.ref == q.owner.ref and self.owner.ref == q.ref and \ + self.band == q.band: # checks out pass else: @@ -228,7 +238,8 @@ class QSO(models.Model): if self.ref and self.callRef and self.callRef.ref and not self.cfmdQSO: # look for a matching line q = QSO.objects.filter( - (Q(time__lte=self.time + datetime.timedelta(seconds=self.CFMD_SEC)) & Q(time__gte=self.time - datetime.timedelta(seconds=self.CFMD_SEC))), + (Q(time__lte=self.time + datetime.timedelta(seconds=self.CFMD_SEC)) & + Q(time__gte=self.time - datetime.timedelta(seconds=self.CFMD_SEC))), owner=self.callRef, callRef=self.owner, owner__ref=self.ref, @@ -250,4 +261,6 @@ class QSO(models.Model): super(QSO, self).save(*args, **kwargs) def __str__(self): - return "QSO no %s at %s on band %s from %s with %s@%s %s/%s" % (self.ownNo, self.time.strftime("%H:%M"), self.band, self.owner.username, self.call, self.refStr, self.reportTX, self.reportRX) + return "QSO no %s at %s on band %s from %s with %s@%s %s/%s" % (self.ownNo, self.time.strftime("%H:%M"), + self.band, self.owner.username, self.call, + self.refStr, self.reportTX, self.reportRX) diff --git a/contest/validators.py b/contest/validators.py index c157429..fecd7ed 100644 --- a/contest/validators.py +++ b/contest/validators.py @@ -8,7 +8,6 @@ import re @deconstructible class CallUsernameValidator(validators.RegexValidator): - #regex = r'^[\w.@+-]+$' regex = r'^(?:[A-Z]+/)?[A-Z]{1,2}[0-9][A-Z]{1,4}(?:-[0-9])??$' message = _( 'Enter a valid Callsign as Username, ALL UPPERCASE, if needed with -1 / -2,' @@ -18,7 +17,6 @@ class CallUsernameValidator(validators.RegexValidator): @deconstructible class CallLogValidator(validators.RegexValidator): - #regex = r'^[\w.@+-]+$' regex = r'^(?:[A-Z]+/)?[A-Z]{1,2}[0-9][A-Z]{1,4}(?:-[0-9])?(?:/[A-Z]{1,3})?$' message = _( 'Enter a valid callsign, ALL UPPERCASE, if needed with -1 / -2,' diff --git a/contest/views.py b/contest/views.py index 462e25f..f16751f 100644 --- a/contest/views.py +++ b/contest/views.py @@ -1,8 +1,8 @@ -from django.shortcuts import render, get_object_or_404 +import datetime +from django.shortcuts import render, get_object_or_404 from django.contrib.auth.decorators import login_required from django.contrib.admin.views.decorators import staff_member_required -#from django.db.models import Q from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm from django.http import HttpResponseRedirect from django.contrib import messages @@ -11,12 +11,10 @@ from django.contrib.auth import login as auth_login from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.utils import timezone - - -import datetime - from .models import User, Contest, Frequency, Reference, QSO, ShadowCall -from .forms import UpdateRefForm, QSOForm, QSOFormWithTime, CustomUserCreationForm, ShadowCallAddForm, UpdateCategoryForm +from .forms import UpdateRefForm, QSOForm, QSOFormWithTime, CustomUserCreationForm, ShadowCallAddForm, \ + UpdateCategoryForm + def index(request): if request.user.is_authenticated(): @@ -24,6 +22,7 @@ def index(request): return render(request, "index.html", {"loginForm": AuthenticationForm()}) + @login_required def contestIndex(request): qsoform = QSOForm(request.user) @@ -32,6 +31,7 @@ def contestIndex(request): return render(request, 'contest/index.html', {"qsoform": qsoform, "contest": contest, "qrgs": qrgs}) + @login_required def log(request): if not request.user.ref: @@ -68,9 +68,9 @@ def log(request): form = QSOForm(request.user, initial=data) form.helper.form_tag = False - return render(request, 'contest/log.html', {'form': form, 'qsos': qsos}) + @login_required def logEdit(request, qsoid): if not request.user.ref: @@ -91,6 +91,7 @@ def logEdit(request, qsoid): return render(request, 'contest/logEdit.html', {'form': form, "qso": qso}) + def logDelete(request, qsoid): if not request.user.ref: return HttpResponseRedirect(reverse("contest:index")) @@ -109,7 +110,6 @@ def logDelete(request, qsoid): return render(request, 'contest/logDelete.html', {"qso": qso}) - @staff_member_required def registerRefs(request): allUser = User.objects.all() @@ -127,7 +127,9 @@ def registerRefs(request): else: shadowForm = ShadowCallAddForm() - return render(request, 'contest/registerRefs.html', {'alluser': allUser, "qsos": qsos, "shadowForm": shadowForm, "shadows": shadows}) + return render(request, 'contest/registerRefs.html', + {'alluser': allUser, "qsos": qsos, "shadowForm": shadowForm, "shadows": shadows}) + def getPage(paginator, pageNo): try: @@ -153,6 +155,7 @@ def recheckAllQSOs(request): return render(request, "contest/checkAllQSOs.html", {}) + @staff_member_required def viewUserQSOs(request, uid, page=1): user = get_object_or_404(User, id=uid) @@ -162,7 +165,9 @@ def viewUserQSOs(request, uid, page=1): userRefs = set(map(lambda _x: _x["refStr"], user.qso_set.filter(ref__isnull=False).values("ref", "refStr"))) - return render(request, "contest/viewUserQSOs.html", {'owner': user, 'qsos': qsos, 'qsoPage': qsoPage, 'userRefs': userRefs}) + return render(request, "contest/viewUserQSOs.html", + {'owner': user, 'qsos': qsos, 'qsoPage': qsoPage, 'userRefs': userRefs}) + @staff_member_required def updateRef(request, shadow, uid): @@ -208,6 +213,7 @@ def updateRef(request, shadow, uid): return render(request, 'contest/updateRef.html', {'userobj': user, 'form': form, "shadow": shadow}) + @staff_member_required def viewAllQSOs(request, page=1): qsos = QSO.objects.all().order_by("-time") @@ -216,12 +222,14 @@ def viewAllQSOs(request, page=1): return render(request, 'contest/viewAllQSOs.html', {'qsoPage': qsoPage}) + def overview(request): # FIXME: Hardcoded for cqtu... everywhere c = Contest.objects.get(id=1) qrgs = Frequency.objects.filter(band__contest=c).order_by("channel") return render(request, 'contest/overview.html', {'contest': c, 'qrgs': qrgs}) + def register(request): form = None if request.method == 'POST': @@ -237,6 +245,7 @@ def register(request): return render(request, 'registration/register.html', {"form": form}) + @login_required def profile(request): pwForm = None diff --git a/cqtu/settings.py b/cqtu/settings.py index 35d3b9a..9573812 100644 --- a/cqtu/settings.py +++ b/cqtu/settings.py @@ -107,12 +107,6 @@ AUTH_PASSWORD_VALIDATORS = [ 'min_length': 4, }, }, - #{ - # 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', - #}, - #{ - # 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', - #}, ] diff --git a/cqtu/urls.py b/cqtu/urls.py index 66d856c..a954be5 100644 --- a/cqtu/urls.py +++ b/cqtu/urls.py @@ -15,16 +15,11 @@ Including another URLconf """ from django.conf.urls import url, include from django.contrib import admin - from django.contrib.auth import views as auth_views -#from django.views.generic.edit import CreateView -#from django.contrib.auth.forms import UserCreationForm -#from contest.forms import CustomUserCreationForm from contest.views import index, register, profile - urlpatterns = [ url('^$', index, name="index"), url('^cqtufm2019/', include('contest.urls', namespace='contest')), @@ -35,9 +30,4 @@ urlpatterns = [ url(r'^register/$', register, name='register'), url(r'^profile/$', profile, name='profile'), url(r'^api/', include('api.urls')), - #url(r'^register/$', CreateView.as_view( - # template_name='registration/register.html', - # form_class=CustomUserCreationForm, - # success_url='/', - #), name='register'), ]