2017-01-18 02:25:12 +01:00
from __future__ import unicode_literals
2017-01-26 19:14:58 +01:00
import datetime
2017-01-18 02:25:12 +01:00
from django . db import models
from django . contrib . auth . models import AbstractUser
2017-01-21 01:52:19 +01:00
from django . core . validators import RegexValidator
2017-01-28 17:06:05 +01:00
from django . db . models import Q , signals
2017-01-18 02:25:12 +01:00
2017-01-19 18:42:11 +01:00
from . validators import CallUsernameValidator
2017-01-28 17:06:05 +01:00
from . signals import checkForShadowCall
2017-01-19 18:42:11 +01:00
class Contest ( models . Model ) :
2020-01-26 11:36:47 +01:00
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 )
deadline = models . DateTimeField ( )
2017-01-19 18:42:11 +01:00
2020-01-26 11:36:47 +01:00
qsoStartTime = models . DateTimeField ( )
qsoEndTime = models . DateTimeField ( )
2017-01-28 18:38:32 +01:00
2020-01-26 11:36:47 +01:00
def __str__ ( self ) :
return self . name
2017-01-19 18:42:11 +01:00
2020-02-02 02:10:33 +01:00
@classmethod
def get_current_contest ( cls ) :
return cls . objects . get ( id = 1 )
2017-01-18 02:25:12 +01:00
class Reference ( models . Model ) :
2020-01-26 11:36:47 +01:00
name = models . CharField ( max_length = 20 , unique = True , db_index = True )
description = models . TextField ( )
2017-01-18 02:25:12 +01:00
2020-01-26 11:36:47 +01:00
def __str__ ( self ) :
return self . name
2017-01-19 18:42:11 +01:00
2017-01-26 20:36:55 +01:00
class EntryCategory ( models . Model ) :
2020-01-26 11:36:47 +01:00
name = models . CharField ( max_length = 64 , unique = True )
description = models . TextField ( blank = True )
2017-01-26 20:36:55 +01:00
2020-01-26 11:36:47 +01:00
def __str__ ( self ) :
return self . name
2017-01-26 20:36:55 +01:00
2017-01-25 01:53:22 +01:00
class ShadowCall ( models . Model ) :
2020-01-26 11:36:47 +01:00
username = models . CharField ( max_length = 20 , unique = True , db_index = True , validators = [ CallUsernameValidator ( ) ] )
ref = models . ForeignKey ( Reference , models . SET_NULL , null = True , blank = True )
2017-01-25 01:53:22 +01:00
2020-01-26 11:36:47 +01:00
location = models . CharField ( max_length = 128 , default = " " , blank = True )
opName = models . CharField ( max_length = 128 , default = " " , blank = True )
regTime = models . DateTimeField ( null = True , default = None )
2017-01-28 05:05:21 +01:00
2020-01-26 11:36:47 +01:00
def __str__ ( self ) :
return self . username
2017-01-25 01:53:22 +01:00
2017-01-18 02:25:12 +01:00
class User ( AbstractUser ) :
2020-01-26 11:36:47 +01:00
ref = models . ForeignKey ( Reference , models . SET_NULL , null = True , blank = True )
cat = models . ForeignKey ( EntryCategory , 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 )
2020-02-01 16:53:27 +01:00
regTime = models . DateTimeField ( null = True , default = None , blank = True )
2020-01-26 11:36:47 +01:00
# because of cbr parsing bug, we sometimes have users who only have 70cm qsos
# we ignore the band for them when checking QSOs
ignoreBand = models . BooleanField ( default = False )
# 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 " )
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? " )
extra2m70cm = models . BooleanField ( default = False ,
2019-01-24 23:26:31 +01:00
verbose_name = " Additional 2m/70cm TRX " ,
help_text = " Will you bring an additional 2m/70cm TRX to lend to other participants? " )
2019-01-23 00:50:51 +01:00
2020-01-26 11:36:47 +01:00
def __init__ ( self , * args , * * kwargs ) :
super ( User , self ) . __init__ ( * args , * * kwargs )
self . _meta . get_field ( " username " ) . validators = [ CallUsernameValidator ( ) ]
2017-01-18 02:25:12 +01:00
2020-01-26 11:36:47 +01:00
def getQSOCount ( self ) :
return self . qso_set . count ( )
2017-01-26 19:14:58 +01:00
2020-01-26 11:36:47 +01:00
def getCfmdQSOCount ( self ) :
return self . qso_set . filter ( ~ Q ( cfmdQSO = None ) ) . count ( )
2017-01-26 19:14:58 +01:00
2020-01-26 11:36:47 +01:00
def getCfmdRefCount ( self ) :
return len ( set ( map ( lambda _x : _x [ " refStr " ] , self . qso_set . filter ( ref__isnull = False ) . values ( " ref " , " refStr " ) ) ) )
2017-01-28 18:39:00 +01:00
2020-01-26 11:36:47 +01:00
def calcClaimedPoints ( self ) :
return self . calcPoints ( cfmd = False )
2017-01-28 18:39:00 +01:00
2020-01-26 11:36:47 +01:00
def calcCfmdPoints ( self ) :
return self . calcPoints ( cfmd = True )
2017-01-28 18:39:00 +01:00
2020-01-26 11:36:47 +01:00
def calcPoints ( self , cfmd ) :
contest = Contest . objects . get ( id = 1 )
2017-01-28 18:39:00 +01:00
2020-01-26 11:36:47 +01:00
result = { " refCount " : 0 , " qsoCount " : 0 }
for band in contest . band_set . all ( ) :
result [ band . name ] = self . calcBandPoints ( band , cfmd )
result [ " refCount " ] + = result [ band . name ] [ " refCount " ]
result [ " qsoCount " ] + = result [ band . name ] [ " qsoCount " ]
2017-01-28 18:39:00 +01:00
2020-01-26 11:36:47 +01:00
result [ " points " ] = result [ " qsoCount " ] * result [ " refCount " ]
2017-01-28 18:39:00 +01:00
2020-01-26 11:36:47 +01:00
return result
2017-01-28 18:39:00 +01:00
2020-01-26 11:36:47 +01:00
def calcBandPoints ( self , band , cfmd = False ) :
contest = band . contest
qsos = self . qso_set . filter ( band = band , time__gte = contest . qsoStartTime , time__lte = contest . qsoEndTime )
if cfmd :
qsos = qsos . filter ( cfmdQSO__isnull = False )
refs = set ( map ( lambda _x : _x [ " refStr " ] , qsos . values ( " refStr " ) ) )
2017-01-28 18:39:00 +01:00
2020-01-26 11:36:47 +01:00
return {
" band " : band ,
" refs " : refs ,
" qsoCount " : qsos . count ( ) ,
" refCount " : len ( refs )
}
2017-01-28 17:06:05 +01:00
signals . post_save . connect ( checkForShadowCall , sender = User )
2017-01-26 19:14:58 +01:00
2017-01-18 02:25:12 +01:00
class Band ( models . Model ) :
2020-01-26 11:36:47 +01:00
name = models . CharField ( max_length = 10 )
contest = models . ForeignKey ( Contest )
2017-01-18 02:25:12 +01:00
2020-01-26 11:36:47 +01:00
def __str__ ( self ) :
return self . name
2017-01-18 02:25:12 +01:00
class Frequency ( models . Model ) :
2020-01-26 11:36:47 +01:00
# qrg
# band
channel = models . CharField ( max_length = 3 )
qrg = models . DecimalField ( max_digits = 7 , decimal_places = 3 )
band = models . ForeignKey ( Band )
2017-01-18 02:25:12 +01:00
2020-01-26 11:36:47 +01:00
note = models . CharField ( max_length = 50 , blank = True )
2017-01-19 18:42:11 +01:00
2020-01-26 11:36:47 +01:00
def __str__ ( self ) :
return " Channel %s : %s MHz " % ( self . channel , self . qrg )
2017-01-18 02:25:12 +01:00
2017-01-19 18:42:11 +01:00
class QSO ( models . Model ) :
2020-01-26 11:36:47 +01:00
reportValidator = RegexValidator ( " [1-5][1-9] " )
class Meta :
index_together = [
[ " owner " , " call " ] ,
]
owner = models . ForeignKey ( User , 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 )
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 )
refStr = models . CharField ( max_length = 20 , verbose_name = " EXC " )
ref = models . ForeignKey ( Reference , 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 )
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! """
# find reference
changed = False
if self . refStr :
refName = self . refStr . replace ( " - " , " " )
if refName == " GX " :
refName = " DX "
# Old reference exists?
if self . ref and self . ref . name != refName :
self . ref = None
changed = True
if not self . ref :
# find matching ref
try :
self . ref = Reference . objects . get ( name = refName )
changed = True
except Reference . DoesNotExist :
pass
# find call
if not self . callRef or self . callRef . username != self . call :
try :
self . callRef = User . objects . get ( username = self . call )
changed = True
except User . DoesNotExist :
if self . callRef :
changed = True
self . callRef = None
# find matching qso
if self . cfmdQSO :
# 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 :
# checks out
pass
else :
changed = True
self . cfmdQSO . cfmdQSO = None
self . cfmdQSO . save ( checkQSO = False )
self . cfmdQSO = None
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 ) ) ) ,
owner = self . callRef ,
callRef = self . owner ,
owner__ref = self . ref ,
ref = self . owner . ref ,
band = self . band )
if q . count ( ) == 1 :
changed = True
q [ 0 ] . cfmdQSO = self
q [ 0 ] . save ( checkQSO = False )
self . cfmdQSO = q [ 0 ]
return changed
def save ( self , checkQSO = True , * args , * * kwargs ) :
if checkQSO :
self . checkQSOData ( )
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 )