@ -1,19 +1,21 @@
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 )
shortName = models . CharField ( max_length = 20 , unique = True )
callQrg = models . ForeignKey ( " Frequency " , models . SET_NULL , null = True , blank = True )
contestNo = models . IntegerField ( validators = [ MinValueValidator ( 1 ) ] ,
help_text = " Running number of contest (for vanity reasons) " )
rulesetLink = models . TextField ( help_text = " URL to the ruleset pdf for this contest " )
callQrg = models . ForeignKey ( " Frequency " , on_delete = models . SET_NULL , null = True , blank = True )
deadline = models . DateTimeField ( )
qsoStartTime = models . DateTimeField ( )
@ -24,7 +26,12 @@ class Contest(models.Model):
@classmethod
def get_current_contest ( cls ) :
return cls . objects . get ( id = 1 )
# Currently the contest with the latest deadline is the active one
# This definitely has potential for improvement, but it's better than a hardcoded contest
contests = cls . objects . order_by ( " -deadline " )
if len ( contests ) > 0 :
return contests [ 0 ]
return None
class Reference ( models . Model ) :
@ -34,6 +41,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 +49,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 +61,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 +77,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,27 +135,32 @@ 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 )
contest = models . ForeignKey ( Contest , on_delete = models . CASCADE )
def __str__ ( self ) :
return self . name
class Frequency ( models . Model ) :
# qrg
# band
channel = models . CharField ( max_length = 3 )
qrg = models . DecimalField ( max_digits = 7 , decimal_places = 3 )
band = models . ForeignKey ( Band )
band = models . ForeignKey ( Band , on_delete = models . CASCADE )
note = models . CharField ( max_length = 50 , blank = True )
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] " )
@ -154,11 +170,11 @@ class QSO(models.Model):
[ " owner " , " call " ] ,
]
owner = models . ForeignKey ( User , db_index= True )
owner = models . ForeignKey ( User , on_delete= models . CASCADE , db_index= True )
time = models . DateTimeField ( blank = True )
call = models . CharField ( max_length = 20 , db_index = True )
callRef = models . ForeignKey ( User , models. SET_NULL , related_name = ' qsoref ' , null = True , blank = True , default = None )
band = models . ForeignKey ( Band )
callRef = models . ForeignKey ( User , on_delete= models. SET_NULL , related_name = ' qsoref ' , null = True , blank = True , default = None )
band = models . ForeignKey ( Band , on_delete = models . CASCADE )
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 ] )
@ -168,13 +184,13 @@ class QSO(models.Model):
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 )
ref = models . ForeignKey ( Reference , on_delete= models. SET_NULL , null = True , blank = True )
remarks = models . CharField ( max_length = 50 , blank = True , default = None )
cfmdQSO = models . ForeignKey ( " QSO " , models. SET_NULL , null = True , blank = True , default = None )
cfmdQSO = models . ForeignKey ( " QSO " , on_delete= 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 +229,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 +244,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 +267,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 )