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 from django.db.models import Q, signals from .validators import CallUsernameValidator from .signals import checkForShadowCall 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) deadline = models.DateTimeField() def __str__(self): return self.name class Reference(models.Model): name = models.CharField(max_length=20, unique=True, db_index=True) description = models.TextField() def __str__(self): return self.name class EntryCategory(models.Model): name = models.CharField(max_length=64, unique=True) description = models.TextField(blank=True) 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) 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) 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) 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) def __init__(self, *args, **kwargs): super(User, self).__init__(*args, **kwargs) self._meta.get_field("username").validators = [CallUsernameValidator()] def getQSOCount(self): return self.qso_set.count() def getCfmdQSOCount(self): return self.qso_set.filter(~Q(cfmdQSO=None)).count() def getCfmdRefCount(self): return len(set(map(lambda _x: _x["refStr"], self.qso_set.filter(ref__isnull=False).values("ref", "refStr")))) signals.post_save.connect(checkForShadowCall, sender=User) class Band(models.Model): name = models.CharField(max_length=10) contest = models.ForeignKey(Contest) 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) note = models.CharField(max_length=50, blank=True) def __str__(self): return "Channel %s: %s MHz" % (self.channel, self.qrg) class QSO(models.Model): 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-S') 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 = 300 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: # Old reference exists? if self.ref and self.ref.name != self.refStr: self.ref = None changed = True if not self.ref: # find matching ref try: self.ref = Reference.objects.get(name=self.refStr) 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 (self.time - q.time).total_seconds() <= self.CFMD_SEC and \ self.ref and self.owner.ref 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 = 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__gte=self.time + datetime.timedelta(0, self.CFMD_SEC)) | Q(time__gte=self.time - datetime.timedelta(0, self.CFMD_SEC))), 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 from %s %s with %s@%s %s/%s" % (self.ownNo, self.time.strftime("%H:%M"), self.owner.username, self.call, self.refStr, self.reportTX, self.reportRX)