Skip to content
Snippets Groups Projects
models.py 8.74 KiB
Newer Older
Brian Moe's avatar
Brian Moe committed
from django.db import models
from django.core.urlresolvers import reverse

from model_utils.managers import InheritanceManager

Brian Moe's avatar
Brian Moe committed
import datetime
import thread
import string
Brian Moe's avatar
Brian Moe committed
import os
Brian Moe's avatar
Brian Moe committed

# XXX ER2.utils.  utils is in project directory.  ugh.
from utils import posixToGpsTime

from django.conf import settings
import pytz, time

SERVER_TZ = pytz.timezone(settings.TIME_ZONE)

# Let's say we start here on schema versions
#
# 1.0 -> 1.1   changed EventLog.comment from CharField(length=200) -> TextField
#
schema_version = "1.1"
Brian Moe's avatar
Brian Moe committed
class User(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField()
    principal = models.CharField(max_length=100)
    dn = models.CharField(max_length=100)
    unixid = models.CharField(max_length=25)

    class Meta:
        ordering = ["name"]

    def __unicode__(self):
        return self.name


class Group(models.Model):
    name = models.CharField(max_length=20)
    managers = models.ManyToManyField(User)
    def __unicode__(self):
        return self.name

class Label(models.Model):
    name = models.CharField(max_length=20, unique=True)
    # XXX really, does this belong here? probably not.
    defaultColor = models.CharField(max_length=20, unique=False, default="black")
    def __unicode__(self):
        return self.name
Brian Moe's avatar
Brian Moe committed

class Event(models.Model):

    objects = InheritanceManager() # Queries can return subclasses, if available.

Brian Moe's avatar
Brian Moe committed
    ANALYSIS_TYPE_CHOICES = (
        ("LM",  "LowMass"),
        ("HM",  "HighMass"),
        ("GRB", "GRB"),
        ("RD",  "Ringdown"),
        ("OM",  "Omega"),
        ("Q",   "Q"),
        ("X",   "X"),
        ("CWB", "CWB"),
Brian Moe's avatar
Brian Moe committed
        ("MBTA", "MBTAOnline"),
        ("HWINJ", "HardwareInjection"),
Brian Moe's avatar
Brian Moe committed
    )
    DEFAULT_EVENT_NEIGHBORHOOD = (5,5)

Brian Moe's avatar
Brian Moe committed
    submitter = models.ForeignKey(User)
    created = models.DateTimeField(auto_now_add=True)
    group = models.ForeignKey(Group)
    uid = models.CharField(max_length=20, default="")  # XXX deprecated.  should be removed.
Brian Moe's avatar
Brian Moe committed
    analysisType = models.CharField(max_length=20, choices=ANALYSIS_TYPE_CHOICES)
Brian Moe's avatar
Brian Moe committed
    instruments = models.CharField(max_length=20, default="")
    nevents = models.PositiveIntegerField(null=True)
Brian Moe's avatar
Brian Moe committed
    far = models.FloatField(null=True)
Brian Moe's avatar
Brian Moe committed
    likelihood = models.FloatField(null=True)

    # NOT from coinc_event, but so, so common.
    #   Note that the semantics for this is different depending
    #   on search type, so in some sense, querying on this may
    #   be considered, umm, wrong?  But it is a starting point.
    gpstime = models.PositiveIntegerField(null=True)
    labels = models.ManyToManyField(Label, through="Labelling")

    class Meta:
        ordering = ["-id"]

        if self.group.name == "Test":
            return "T%04d" % self.id
        elif self.analysisType == "HWINJ":
            return "H%04d" % self.id
        elif self.analysisType == "GRB":
            return "E%04d" % self.id
Brian Moe's avatar
Brian Moe committed
    def weburl(self):
        return "https://ldas-jobs.phys.uwm.edu/gracedb/data/%s" % self.graceid()
Brian Moe's avatar
Brian Moe committed

    def clusterurl(self):
        #return "pcdev1.phys.uwm.edu:/archive/gracedb/data/%s" % self.graceid()
        return "file://pcdev1.phys.uwm.edu/archive/gracedb/data/%s" % self.graceid()
    def datadir(self, general=False):
        # Move to this.  Not the (more) ad hoc crap that's floating around.
        if general:
            subdir = "general"
        else:
            subdir = "private"
        return os.path.join(settings.GRACEDB_DATA_DIR, self.graceid(), subdir)
    def ligoApproved(self):
        return self.approval_set.filter(approvingCollaboration='L').count()

    def virgoApproved(self):
        return self.approval_set.filter(approvingCollaboration='V').count()
Brian Moe's avatar
Brian Moe committed

    def reportingLatency(self):
        if self.gpstime:
            dt = self.created
            if not dt.tzinfo:
                dt = SERVER_TZ.localize(dt)
            posix_time = time.mktime(dt.timetuple())
            gps_time = int(posixToGpsTime(posix_time))
            return gps_time - self.gpstime

    def neighbors(self, neighborhood=None):
        if not self.gpstime:
            return []
        if self.group.name == 'Test':
            nearby = Event.objects.filter(group__name='Test')
        else:
            nearby = Event.objects.exclude(group__name='Test')

        delta, delta2 = neighborhood or self.DEFAULT_EVENT_NEIGHBORHOOD

Brian Moe's avatar
Brian Moe committed
        nearby = nearby.filter(gpstime__range=(self.gpstime-delta, self.gpstime+delta2))
        nearby = nearby.exclude(id=self.id)
        nearby = nearby.order_by('gpstime')
        return nearby

    @classmethod
    def getTypeLabel(cls, code):
        for key, label in cls.ANALYSIS_TYPE_CHOICES:
            if (key == code) or (code == label):
                return label
        raise KeyError("Unknown analysis type code: %s" % code)

Brian Moe's avatar
Brian Moe committed
    @classmethod
    def getByGraceid(cls, id):
        try:
            e = cls.objects.filter(id=int(id[1:])).select_subclasses()[0]
        except IndexError:
            raise cls.DoesNotExist("Event matching query does not exist")
        if (id[0] == "T") and (e.group.name == "Test"):
            return e
        if (id[0] == "H") and (e.analysisType == "HWINJ"):
        if (id[0] == "E") and (e.analysisType == "GRB"):
            return e
        if (id[0] == "G"):
            return e
        raise cls.DoesNotExist("Event matching query does not exist")
    def __unicode__(self):
        return self.graceid()

Brian Moe's avatar
Brian Moe committed
class EventLog(models.Model):
    class Meta:
        ordering = ["-created"]
    event = models.ForeignKey(Event, null=False)
    created = models.DateTimeField(auto_now_add=True)
    issuer = models.ForeignKey(User)
    filename = models.CharField(max_length=100, default="")
    comment = models.TextField(null=False)
Brian Moe's avatar
Brian Moe committed

    def fileurl(self):
        if self.filename:
            return reverse('file', args=[self.event.graceid(), self.filename])
            #return os.path.join(self.event.weburl(), 'private', self.filename)
Brian Moe's avatar
Brian Moe committed
        else:
            return None

    def hasImage(self):
        # XXX hacky
        return self.filename and self.filename[-3:].lower() in ['png','gif','jpg']

class Labelling(models.Model):
    event = models.ForeignKey(Event)
    label = models.ForeignKey(Label)
    creator = models.ForeignKey(User)
    created = models.DateTimeField(auto_now_add=True)

# XXX Deprecated?  Is this used *anywhere*?
# Appears to only be used in models.py.  Here and Event class as approval_set
Brian Moe's avatar
Brian Moe committed
class Approval(models.Model):
    COLLABORATION_CHOICES = ( ('L','LIGO'), ('V','Virgo'), )
    approver = models.ForeignKey(User)
    created = models.DateTimeField(auto_now_add=True)
    approvedEvent = models.ForeignKey(Event, null=False)
    approvingCollaboration = models.CharField(max_length=1, choices=COLLABORATION_CHOICES)

## Analysis Specific Attributes.

Brian Moe's avatar
Brian Moe committed
class CoincInspiralEvent(Event):
    ifos             = models.CharField(max_length=20, default="")
    end_time         = models.PositiveIntegerField(null=True)
    end_time_ns      = models.PositiveIntegerField(null=True)
    mass             = models.FloatField(null=True)
    mchirp           = models.FloatField(null=True)
    minimum_duration = models.FloatField(null=True)
    snr              = models.FloatField(null=True)
    false_alarm_rate = models.FloatField(null=True)
    combined_far     = models.FloatField(null=True)


Brian Moe's avatar
Brian Moe committed
class MultiBurstEvent(Event):
    ifos             = models.CharField(max_length=20, default="")
    start_time       = models.PositiveIntegerField(null=True)
    start_time_ns    = models.PositiveIntegerField(null=True)
    duration         = models.FloatField(null=True)
    peak_time        = models.PositiveIntegerField(null=True)
    peak_time_ns     = models.PositiveIntegerField(null=True)
    central_freq     = models.FloatField(null=True)
    bandwidth        = models.FloatField(null=True)
    amplitude        = models.FloatField(null=True)
    snr              = models.FloatField(null=True)
    confidence       = models.FloatField(null=True)
    false_alarm_rate = models.FloatField(null=True)
    ligo_axis_ra     = models.FloatField(null=True)
    ligo_axis_dec    = models.FloatField(null=True)
    ligo_angle       = models.FloatField(null=True)
    ligo_angle_sig   = models.FloatField(null=True)

## Slots (user-defined event attributes)

class Slot(models.Model):
    """Slot Model"""
    # Does the slot need to have a submitter column?
Brian Moe's avatar
Brian Moe committed
    class Meta:
        unique_together = (('event', 'name'))
    event = models.ForeignKey(Event)
    name  = models.CharField(max_length=100)
    value = models.CharField(max_length=100)

    # In case the slot value is not a filename, this will just return None.
    def fileurl(self):
        if self.value:
            return reverse('file', args=[self.event.graceid(), self.value])
        else:
            return None