diff --git a/gracedb/admin.py b/gracedb/admin.py index d75cf54b3fffaf155b72b9197271261eef4595a5..4dbf01499602dbfa0b1c8f1ecea8ebf41b52b5c6 100644 --- a/gracedb/admin.py +++ b/gracedb/admin.py @@ -1,5 +1,5 @@ -from models import Event, EventLog, Group +from models import Event, EventLog, EMBBEventLog, EMGroup, Group from models import Label, Labelling, Tag from django.contrib import admin @@ -18,6 +18,11 @@ class EventLogAdmin(admin.ModelAdmin): list_display = [ 'event', 'issuer', 'filename', 'comment' ] search_fields = [ 'event__id', 'issuer__name', 'filename', 'comment'] +#class EMBBEventLogAdmin(admin.ModelAdmin): +# list_display = [ 'event' ] +# search_fields = [ 'event__id'] +# + class LabellingAdmin(admin.ModelAdmin): list_display = [ 'event', 'label', 'creator' ] search_fields = [ 'event__id', 'label__name', 'creator__name' ] @@ -28,6 +33,8 @@ class TagAdmin(admin.ModelAdmin): admin.site.register(Event, EventAdmin) admin.site.register(EventLog, EventLogAdmin) +admin.site.register(EMBBEventLog) +admin.site.register(EMGroup) admin.site.register(Group) admin.site.register(Label, LabelAdmin) admin.site.register(Labelling, LabellingAdmin) diff --git a/gracedb/api.py b/gracedb/api.py index ce613ea18991f77fa279c69cd4ee8f5a2a468c44..8e1d5612e944712bcadb5e3014e7f6250ac7ff56 100644 --- a/gracedb/api.py +++ b/gracedb/api.py @@ -7,6 +7,7 @@ from django.conf import settings from django.utils.http import urlquote from django.utils import dateformat from django.utils.functional import wraps +from django.db import IntegrityError import json @@ -14,9 +15,12 @@ from django.contrib.auth.models import User, Permission from django.contrib.auth.models import Group as AuthGroup from django.contrib.contenttypes.models import ContentType from gracedb.models import Event, Group, Search, Pipeline, EventLog, Tag +from gracedb.models import EMGroup, EMBBEventLog, EMSPECTRUM from view_logic import create_label, get_performance_info from view_logic import _createEventFromForm +from view_logic import create_eel from view_utils import fix_old_creation_request + from translator import handle_uploaded_data from forms import CreateEventForm from permission_utils import user_has_perm, filter_events_for_user @@ -522,6 +526,7 @@ def eventToDict(event, columns=None, request=None): rv['links'] = { "neighbors" : reverse("neighbors", args=[graceid], request=request), "log" : reverse("eventlog-list", args=[graceid], request=request), + "embb" : reverse("embbeventlog-list", args=[graceid], request=request), "files" : reverse("files", args=[graceid], request=request), "filemeta" : reverse("filemeta", args=[graceid], request=request), "labels" : reverse("labels", args=[graceid], request=request), @@ -1082,6 +1087,118 @@ class EventLogDetail(APIView): def get(self, request, event, eventlog): return Response(eventLogToDict(eventlog, request=request)) + +#================================================================== +# EMBBEventLog (EEL) + +# EEL serializer. +def embbEventLogToDict(eel, request=None): + uri = None + if request: + uri = reverse("embbeventlog-detail", + args=[eel.event.graceid(), eel.N], + request=request) + return { + "self" : uri, + "created" : eel.created, + "submitter" : eel.submitter.username, + "group" : eel.group.name, + "instrument" : eel.instrument, + "footprintID" : eel.footprintID, + "waveband" : eel.waveband, + "ra" : eel.ra, + "dec" : eel.dec, + "raWidth" : eel.raWidth, + "decWidth" : eel.decWidth, + "gpstime" : eel.gpstime, + "duration" : eel.duration, + "eel_status" : eel.get_eel_status_display(), + "obs_status" : eel.get_obs_status_display(), + "comment" : eel.comment, + "extra_info_dict" : eel.extra_info_dict, + } + +class EMBBEventLogList(APIView): + """EMBB Event Log List Resource + + POST param 'message' + """ + authentication_classes = (LigoAuthentication,) + permission_classes = (IsAuthenticated,) + + def get(self, request, graceid): + try: + event = Event.getByGraceid(graceid) + except Event.DoesNotExist: + # XXX Real error message. + return Response("Event does not exist.", + status=status.HTTP_404_NOT_FOUND) + + eel_set = event.embbeventlog_set.order_by("created","N") + count = eel_set.count() + + eel = [ embbEventLogToDict(eel, request) + for eel in eel_set.iterator() ] + + rv = { + 'start': 0, + 'numRows' : count, + 'links' : { + 'self' : request.build_absolute_uri(), + 'first' : request.build_absolute_uri(), + 'last' : request.build_absolute_uri(), + }, + 'embblog' : eel, + } + return Response(rv) + + def post(self, request, graceid): + try: + event = Event.getByGraceid(graceid) + except Event.DoesNotExist: + return Response("Event Not Found", + status=status.HTTP_404_NOT_FOUND) + + # Now create the EEL + try: + eel = create_eel(request.DATA, event, request.user) + except ValueError, e: + return Response("str(e)", status=status.HTTP_400_BAD_REQUEST) + except IntegrityError, e: + return Response("Failed to save EMBB entry: %s" % str(e), + status=status.HTTP_503_SERVICE_UNAVAILABLE) + except Exception, e: + return Response("Problem creating EEL: %s" % str(e), + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + rv = embbEventLogToDict(eel, request=request) + response = Response(rv, status=status.HTTP_201_CREATED) + response['Location'] = rv['self'] + + # Issue alert. + description = "New EMBB log entry." + issueAlertForUpdate(event, description, doxmpp=True) + + return response + +class EMBBEventLogDetail(APIView): + authentication_classes = (LigoAuthentication,) + permission_classes = (IsAuthenticated,) + + def get(self, request, graceid, n): + try: + event = Event.getByGraceid(graceid) + except Event.DoesNotExist: + return Response("Event Not Found", + status=status.HTTP_404_NOT_FOUND) + try: + rv = event.embbeventlog_set.filter(N=n)[0] + except: + return Response("Log Message Not Found", + status=status.HTTP_404_NOT_FOUND) + + return Response(embbEventLogToDict(rv, request=request)) + #================================================================== # Tags @@ -1467,6 +1584,8 @@ class GracedbRoot(APIView): vo_detail = vo_detail.replace("G1200", "{graceid}") log = reverse("eventlog-list", args=["G1200"], request=request) log = log.replace("G1200", "{graceid}") + embb = reverse("embbeventlog-list", args=["G1200"], request=request) + embb = embb.replace("G1200", "{graceid}") files = reverse("files", args=["G1200", "filename"], request=request) files = files.replace("G1200", "{graceid}") @@ -1495,6 +1614,7 @@ class GracedbRoot(APIView): "event-detail-template" : detail, "event-vo-detail-template" : vo_detail, "event-log-template" : log, + "embb-event-log-template" : embb, "event-label-template" : labels, "files-template" : files, "filemeta-template" : filemeta, @@ -1528,6 +1648,10 @@ class GracedbRoot(APIView): ("HWINJ", "HardwareInjection"), ) ), + "em-groups" : [g.name for g in EMGroup.objects.all()], + "wavebands" : dict(EMSPECTRUM), + "eel-statuses" : dict(EMBBEventLog.EEL_STATUS_CHOICES), + "obs-statuses" : dict(EMBBEventLog.OBS_STATUS_CHOICES), }) ################################################################## diff --git a/gracedb/fixtures/initial_emgroups.json b/gracedb/fixtures/initial_emgroups.json new file mode 100644 index 0000000000000000000000000000000000000000..5854b1d36f7a1da766045e85a52f870c82ab7fd5 --- /dev/null +++ b/gracedb/fixtures/initial_emgroups.json @@ -0,0 +1,163 @@ +[ + { + "pk": 1, + "model": "gracedb.emgroup", + "fields": { + "name": "Apertif-EVN" + } + }, + { + "pk": 2, + "model": "gracedb.emgroup", + "fields": { + "name": "ARI LJMU" + } + }, + { + "pk": 3, + "model": "gracedb.emgroup", + "fields": { + "name": "ATLAS" + } + }, + { + "pk": 4, + "model": "gracedb.emgroup", + "fields": { + "name": "CRTS" + } + }, + { + "pk": 5, + "model": "gracedb.emgroup", + "fields": { + "name": "FIGARO" + } + }, + { + "pk": 6, + "model": "gracedb.emgroup", + "fields": { + "name": "FRBSG" + } + }, + { + "pk": 7, + "model": "gracedb.emgroup", + "fields": { + "name": "GOTO" + } + }, + { + "pk": 8, + "model": "gracedb.emgroup", + "fields": { + "name": "GWU LSC Group" + } + }, + { + "pk": 9, + "model": "gracedb.emgroup", + "fields": { + "name": "H.E.S.S." + } + }, + { + "pk": 10, + "model": "gracedb.emgroup", + "fields": { + "name": "HXMT" + } + }, + { + "pk": 11, + "model": "gracedb.emgroup", + "fields": { + "name": "INAF" + } + }, + { + "pk": 12, + "model": "gracedb.emgroup", + "fields": { + "name": "IUCAA" + } + }, + { + "pk": 13, + "model": "gracedb.emgroup", + "fields": { + "name": "LCOGT" + } + }, + { + "pk": 14, + "model": "gracedb.emgroup", + "fields": { + "name": "LV-EM Followup" + } + }, + { + "pk": 15, + "model": "gracedb.emgroup", + "fields": { + "name": "MAGIC" + } + }, + { + "pk": 16, + "model": "gracedb.emgroup", + "fields": { + "name": "Pan-STARRS" + } + }, + { + "pk": 17, + "model": "gracedb.emgroup", + "fields": { + "name": "PIRATE" + } + }, + { + "pk": 18, + "model": "gracedb.emgroup", + "fields": { + "name": "RoboPol" + } + }, + { + "pk": 19, + "model": "gracedb.emgroup", + "fields": { + "name": "Swift" + } + }, + { + "pk": 20, + "model": "gracedb.emgroup", + "fields": { + "name": "TOROS" + } + }, + { + "pk": 21, + "model": "gracedb.emgroup", + "fields": { + "name": "VERITAS" + } + }, + { + "pk": 22, + "model": "gracedb.emgroup", + "fields": { + "name": "ZTF" + } + }, + { + "pk": 23, + "model": "gracedb.emgroup", + "fields": { + "name": "Test" + } + } +] \ No newline at end of file diff --git a/gracedb/management/commands/ingest_email.py b/gracedb/management/commands/ingest_email.py new file mode 100644 index 0000000000000000000000000000000000000000..ab3f449ed1ca513e3fd38963ce5273dddc8f5274 --- /dev/null +++ b/gracedb/management/commands/ingest_email.py @@ -0,0 +1,303 @@ +from django.core.management.base import BaseCommand +from gracedb.models import Event, EMBBEventLog +from gracedb.models import EMGroup +from ligoauth.models import AlternateEmail +from django.conf import settings +from django.contrib.auth.models import User +import json +import re +import smtplib +from email.mime.text import MIMEText +from email import message_from_string +from binascii import a2b_qp, a2b_base64 +wierdchars = re.compile(u'[\U00010000-\U0010ffff]') + +USER_NOT_FOUND_MESSAGE = """ + + +No GraceDB user was found matching email: %s + +To proceed, the following actions are recommended: + +For LVC users: Please re-send your EEL using your @LIGO.org mail + forwarding address or a LIGO alternate mail address (i.e., an + address from which you can send message to LIGO mailing lists). + +For non-LVC users: If you have not already done so, please log in + to the GraceDB web interface at + + https://gracedb.ligo.org + + This will have the effect of caching your email address, and + then you can try re-sending your EEL message. We apologize for + the inconvenience. + + Also, please use the email address with which you registered + at gw-astronomy.org. If you need to use an alternate email + address, we can add it to the system manually. Just send a + message to uwm-help@ligo.org. + + +""" + +def sendResponse(to, subject, message): + print message + msg = MIMEText(message) + # Allow the 'to' argument to contain either a list (for multiple recipients) + # or a string (for a single recipient) + if isinstance(to, list): + msg['To'] = ','.join(to) + to_list = to + else: + msg['To'] = to + to_list = [to] + # Remove any addresses to ignore + to_list = list(set(to_list) - set(settings.EMBB_IGNORE_ADDRESSES)) + if not len(to_list): + return None + from_address = settings.EMBB_MAIL_ADDRESS + msg['From'] = from_address + msg['Subject'] = subject + s = smtplib.SMTP(settings.EMBB_SMTP_SERVER) + s.sendmail(from_address, to_list, msg.as_string()) + s.quit() + return None + +# +# Given a string and an encoding, return a unicode string with the +# 6, 9, 66, and 99 characters replaced. +# +def get_unicode_and_fix_quotes(s, encoding): + rv = u'' + for char in s: + if encoding: + uchar = unicode(char, encoding) + else: + uchar = unicode(char) + if ord(uchar) > 127: + # Fix 6 and 9 + if uchar == u'\u2018' or uchar == u'\u2019': + uchar = u"'" + + # Fix 66 and 99 + if uchar == u'\u201c' or uchar == u'\u201d': + uchar = u'"' + rv += uchar + return rv + +class Command(BaseCommand): + help = "I am the email ingester!" + + def handle(self, *args, **options): + self.transcript = 'Started email ingester\n' + + # The file is understood to contain the raw contents of the email. + filename = args[0] + try: + f = open(filename, 'r') + data = f.read() + f.close() + self.transcript += 'Got email with %d characters incl headers\n' % len(data) + except Exception, e: + self.transcript += 'Could not fetch email file\n' + str(e) + return sendResponse(settings.EMBB_MAIL_ADMINS, 'embb submission', self.transcript) + + # Try to convert to email object. + email_obj = message_from_string(data) + + # Parse the email and find out who it's from. + from_string = email_obj['from'] + try: + # XXX Hacky way to get the stuff between the '<' and the '>' + from_address = from_string.split('<')[1].split('>')[0] + except: + try: + from_address = email_obj._unixfrom.split()[1] + except Exception, e: + self.transcript += 'Problem parsing out sender address\n' + str(e) + return sendResponse(settings.EMBB_MAIL_ADMINS, 'embb submission failure', self.transcript) + + # find the submitter + # Look up the sender's address. + user = None + try: + user = User.objects.get(email=from_address) + except: + pass + + try: + alt_email = AlternateEmail.objects.get(email=from_address) + user = alt_email.user + self.transcript += 'Found submitter %s\n' % user.username + except: + pass + + if not user: + self.transcript += USER_NOT_FOUND_MESSAGE % from_address + return sendResponse(from_address, 'gracedb user not found', self.transcript) + + # Get the subject of the email. Use it in the reply + subject = email_obj.get('Subject', '') + reply_subject = 'Re: ' + subject + + # Now we want to get the contents of the email. + # Get the payload and encoding. + encoding = None + if email_obj.is_multipart(): + # Let's look for a plain text part. If not, throw an error. + msg = None + for part in email_obj.get_payload(): + if part.get_content_type() == 'text/plain': + content_transfer_encoding = part.get('Content-Transfer-Encoding', None) + msg = part.get_payload() + try: + encoding = part.get_content_charset() + except: + pass + if not msg: + self.transcript += 'We cannot parse your email because it is not plain text.\n' + self.transcript += 'Please send plain text emails instead of just HTML.\n' + return sendResponse(from_address, reply_subject, self.transcript) + else: + # not multipart. + msg = email_obj.get_payload() + content_transfer_encoding = email_obj.get('Content-Transfer-Encoding', None) + try: + encoding = email_obj.get_content_charset() + except: + pass + + if content_transfer_encoding: + if content_transfer_encoding == 'quoted-printable': + msg = a2b_qp(msg) + elif content_transfer_encoding == 'base64': + msg = a2b_base64(msg) + else: + self.transcript += 'Your message uses an unsupported content transfer encoding.\n' + self.transcript += 'Please use quoted-printable or base64.\n' + return sendResponse(from_address, reply_subject, self.transcript) + + # Get a unicode string and fix any quotation marks. + msg = get_unicode_and_fix_quotes(msg, encoding) + + # Get the body of the message and convert to lines. + lines = msg.split('\n') + + comment = '' + dict = {} + p = re.compile('[A-Za-z-]+:') + inkey = 0 + key = '' + + for line in lines: + if len(line) > 0: + if inkey and line[0].isspace(): # initial space implies continuation + dict[key] += line + continue + m = p.match(line) + if m: + key = line[m.start():m.end()-1] + val = line[m.end():].strip() + if dict.has_key(key): # same key again just makes a new line in val + dict[key] += '\n' + val + else: + dict[key] = val + inkey = 1 + else: + comment += line + inkey = 0 + + self.transcript += 'Found %d keys in email\n' % len(dict.keys()) + +# if not dict.has_key('JSON'): +# self.transcript += 'Error: no JSON key' +# return sendResponse(from_address, dict['SUBJECT'], self.transcript) + + def getpop(dict, key, default): + if dict.has_key(key): + return dict.pop(key) + else: + return default + + +# look for the JSON field at the end of the mail + extra_dict = {} + if dict.has_key('JSON'): + try: + extra_dict = json.loads(dict['JSON']) + self.transcript += 'Found %d keys in JSON\n' % len(extra_dict.keys()) + except Exception, e: + self.transcript += 'Error: Cannot parse JSON: %s\n' % dict['JSON'] + self.transcript += str(e) + return sendResponse(from_address, reply_subject, self.transcript) + +# look for PARAM fields of the form +# PARAM: apple=34.2 + if dict.has_key('PARAM'): + lines = dict['PARAM'].split('\n') + for line in lines: + tok = line.split('=') + if len(tok) == 2: + key = tok[0].strip() + val = tok[1].strip() + extra_dict[key] = val + +# gotta get the Graceid! + graceid = getpop(extra_dict, 'graceid', None) # try to get the graceid from the extra_dict + if not graceid and dict.has_key('SUBJECT'): + tok = dict['SUBJECT'].split(':') # look for a second colon in the SUBJECT line + graceid = tok[0].strip() + + if not graceid: + self.transcript += 'Cannot locate GraceID in SUBJECT, JSON, or PARAM data' + return sendResponse(from_address, reply_subject, self.transcript) + + try: + event = Event.getByGraceid(graceid) + self.transcript += 'Found Graceid %s\n' % graceid + except Exception, e: + self.transcript += 'Error: Cannot find Graceid %s\n' % graceid + self.transcript += str(e) + return sendResponse(from_address, reply_subject, self.transcript) + + # create a log entry + eel = EMBBEventLog(event=event) + eel.event = event + eel.submitter = user + + # Assign a group name + group_name = getpop(extra_dict, 'group', None) + try: + group = EMGroup.objects.get(name=group_name) + eel.group = group + self.transcript += 'Found EMGroup %s\n' % group_name + except Exception, e: + self.transcript += 'Error: Cannot find EMGroup =%s=\n' % group_name + self.transcript += str(e) + return sendResponse(from_address, reply_subject, self.transcript) + + eel.eel_status = getpop(extra_dict, 'eel_status', 'FO') + eel.obs_status = getpop(extra_dict, 'obs_status', 'TE') + eel.footprintID = getpop(extra_dict, 'footprintID', '') + eel.waveband = getpop(extra_dict, 'waveband', 'em.opt') + eel.ra = getpop(extra_dict, 'ra', 0.0) + eel.dec = getpop(extra_dict, 'dec', 0.0) + eel.raWidth = getpop(extra_dict, 'raWidth', 0.0) + eel.decWidth = getpop(extra_dict, 'decWidth', 0.0) + eel.gpstime = getpop(extra_dict, 'gpstime', 0) + eel.duration = getpop(extra_dict, 'duration', 0) + eel.extra_info_dict = json.dumps(extra_dict) + self.transcript += 'Extra_info_dict is %s\n' % eel.extra_info_dict + +# eel.comment = 'hello' # wierdchars.sub(u'', comment) + eel.comment = comment + + try: + eel.save() + except Exception as e: + self.transcript += 'Error: Could not save EEL\n' + self.transcript += str(e) + return sendResponse(from_address, reply_subject, self.transcript) + + self.transcript += 'EEL is successfully saved!' + return sendResponse(from_address, reply_subject, self.transcript) diff --git a/gracedb/migrations/0019_auto__add_emgroup__add_embbeventlog__add_unique_embbeventlog_event_N.py b/gracedb/migrations/0019_auto__add_emgroup__add_embbeventlog__add_unique_embbeventlog_event_N.py new file mode 100644 index 0000000000000000000000000000000000000000..5670de6d9b98dba6f21918a690dd34fc0948a63e --- /dev/null +++ b/gracedb/migrations/0019_auto__add_emgroup__add_embbeventlog__add_unique_embbeventlog_event_N.py @@ -0,0 +1,291 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'EMGroup' + db.create_table(u'gracedb_emgroup', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=20)), + )) + db.send_create_signal(u'gracedb', ['EMGroup']) + + # Adding model 'EMBBEventLog' + db.create_table(u'gracedb_embbeventlog', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('N', self.gf('django.db.models.fields.IntegerField')()), + ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), + ('event', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['gracedb.Event'])), + ('submitter', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), + ('group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['gracedb.EMGroup'])), + ('instrument', self.gf('django.db.models.fields.CharField')(max_length=200, blank=True)), + ('footprintID', self.gf('django.db.models.fields.CharField')(max_length=200, blank=True)), + ('waveband', self.gf('django.db.models.fields.CharField')(max_length=25)), + ('ra', self.gf('django.db.models.fields.FloatField')(null=True)), + ('dec', self.gf('django.db.models.fields.FloatField')(null=True)), + ('raWidth', self.gf('django.db.models.fields.FloatField')(null=True)), + ('decWidth', self.gf('django.db.models.fields.FloatField')(null=True)), + ('gpstime', self.gf('django.db.models.fields.PositiveIntegerField')(null=True)), + ('duration', self.gf('django.db.models.fields.PositiveIntegerField')(null=True)), + ('eel_status', self.gf('django.db.models.fields.CharField')(max_length=2)), + ('obs_status', self.gf('django.db.models.fields.CharField')(max_length=2)), + ('comment', self.gf('django.db.models.fields.TextField')(blank=True)), + ('extra_info_dict', self.gf('django.db.models.fields.TextField')(blank=True)), + )) + db.send_create_signal(u'gracedb', ['EMBBEventLog']) + + # Adding unique constraint on 'EMBBEventLog', fields ['event', 'N'] + db.create_unique(u'gracedb_embbeventlog', ['event_id', 'N']) + + + def backwards(self, orm): + # Removing unique constraint on 'EMBBEventLog', fields ['event', 'N'] + db.delete_unique(u'gracedb_embbeventlog', ['event_id', 'N']) + + # Deleting model 'EMGroup' + db.delete_table(u'gracedb_emgroup') + + # Deleting model 'EMBBEventLog' + db.delete_table(u'gracedb_embbeventlog') + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'gracedb.approval': { + 'Meta': {'object_name': 'Approval'}, + 'approvedEvent': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Event']"}), + 'approver': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}), + 'approvingCollaboration': ('django.db.models.fields.CharField', [], {'max_length': '1'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'gracedb.coincinspiralevent': { + 'Meta': {'ordering': "['-id']", 'object_name': 'CoincInspiralEvent', '_ormbases': [u'gracedb.Event']}, + 'combined_far': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'end_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}), + 'end_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}), + u'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}), + 'false_alarm_rate': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'ifos': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}), + 'mass': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'mchirp': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'minimum_duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'}) + }, + u'gracedb.embbeventlog': { + 'Meta': {'ordering': "['-created', '-N']", 'unique_together': "(('event', 'N'),)", 'object_name': 'EMBBEventLog'}, + 'N': ('django.db.models.fields.IntegerField', [], {}), + 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'dec': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'decWidth': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'duration': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}), + 'eel_status': ('django.db.models.fields.CharField', [], {'max_length': '2'}), + 'event': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Event']"}), + 'extra_info_dict': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'footprintID': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), + 'gpstime': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.EMGroup']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'instrument': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), + 'obs_status': ('django.db.models.fields.CharField', [], {'max_length': '2'}), + 'ra': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'raWidth': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}), + 'waveband': ('django.db.models.fields.CharField', [], {'max_length': '25'}) + }, + u'gracedb.emgroup': { + 'Meta': {'object_name': 'EMGroup'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}) + }, + u'gracedb.event': { + 'Meta': {'ordering': "['-id']", 'object_name': 'Event'}, + 'analysisType': ('django.db.models.fields.CharField', [], {'max_length': '20'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'far': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'gpstime': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'instruments': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}), + 'labels': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['gracedb.Label']", 'through': u"orm['gracedb.Labelling']", 'symmetrical': 'False'}), + 'likelihood': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'nevents': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}), + 'submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}), + 'uid': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}) + }, + u'gracedb.eventlog': { + 'Meta': {'ordering': "['-created', '-N']", 'unique_together': "(('event', 'N'),)", 'object_name': 'EventLog'}, + 'N': ('django.db.models.fields.IntegerField', [], {}), + 'comment': ('django.db.models.fields.TextField', [], {}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'event': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Event']"}), + 'file_version': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'filename': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'issuer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) + }, + u'gracedb.grbevent': { + 'Meta': {'ordering': "['-id']", 'object_name': 'GrbEvent', '_ormbases': [u'gracedb.Event']}, + 'author_ivorn': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), + 'author_shortname': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), + 'coord_system': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), + 'dec': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'error_radius': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + u'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}), + 'how_description': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), + 'how_reference_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}), + 'ivorn': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), + 'observatory_location_id': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), + 'ra': ('django.db.models.fields.FloatField', [], {'null': 'True'}) + }, + u'gracedb.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '20'}) + }, + u'gracedb.label': { + 'Meta': {'object_name': 'Label'}, + 'defaultColor': ('django.db.models.fields.CharField', [], {'default': "'black'", 'max_length': '20'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}) + }, + u'gracedb.labelling': { + 'Meta': {'object_name': 'Labelling'}, + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}), + 'event': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Event']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Label']"}) + }, + u'gracedb.multiburstevent': { + 'Meta': {'ordering': "['-id']", 'object_name': 'MultiBurstEvent', '_ormbases': [u'gracedb.Event']}, + 'amplitude': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'bandwidth': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'central_freq': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'confidence': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + u'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}), + 'false_alarm_rate': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'ifos': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}), + 'ligo_angle': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'ligo_angle_sig': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'ligo_axis_dec': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'ligo_axis_ra': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'peak_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}), + 'peak_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}), + 'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'start_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}), + 'start_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}) + }, + u'gracedb.singleinspiral': { + 'Gamma0': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'Gamma1': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'Gamma2': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'Gamma3': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'Gamma4': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'Gamma5': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'Gamma6': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'Gamma7': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'Gamma8': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'Gamma9': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'Meta': {'object_name': 'SingleInspiral'}, + 'alpha': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'alpha1': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'alpha2': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'alpha3': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'alpha4': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'alpha5': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'alpha6': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'amplitude': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'bank_chisq': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'bank_chisq_dof': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'beta': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'channel': ('django.db.models.fields.CharField', [], {'max_length': '20'}), + 'chi': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'chisq': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'chisq_dof': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'coa_phase': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'cont_chisq': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'cont_chisq_dof': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'eff_distance': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'end_time': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'end_time_gmst': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'end_time_ns': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'eta': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'event': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Event']"}), + 'event_duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'f_final': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ifo': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True'}), + 'impulse_time': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'impulse_time_ns': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'kappa': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'mass1': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'mass2': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'mchirp': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'mtotal': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'psi0': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'psi3': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'rsqveto_duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'search': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True'}), + 'sigmasq': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'tau0': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'tau2': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'tau3': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'tau4': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'tau5': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'template_duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'ttotal': ('django.db.models.fields.FloatField', [], {'null': 'True'}) + }, + u'gracedb.tag': { + 'Meta': {'object_name': 'Tag'}, + 'displayName': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), + 'eventlogs': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['gracedb.EventLog']", 'symmetrical': 'False'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['gracedb'] \ No newline at end of file diff --git a/gracedb/migrations/0020_populate_emgroups.py b/gracedb/migrations/0020_populate_emgroups.py new file mode 100644 index 0000000000000000000000000000000000000000..32638e9e03072f401df437acc2172fcb77a42224 --- /dev/null +++ b/gracedb/migrations/0020_populate_emgroups.py @@ -0,0 +1,252 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models +from django.core.management import call_command + +class Migration(DataMigration): + + def forwards(self, orm): + call_command("loaddata", "initial_emgroups.json") + + def backwards(self, orm): + for emgroup in orm.EMGroup.objects.all(): + emgroup.delete() + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'gracedb.approval': { + 'Meta': {'object_name': 'Approval'}, + 'approvedEvent': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Event']"}), + 'approver': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}), + 'approvingCollaboration': ('django.db.models.fields.CharField', [], {'max_length': '1'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'gracedb.coincinspiralevent': { + 'Meta': {'ordering': "['-id']", 'object_name': 'CoincInspiralEvent', '_ormbases': [u'gracedb.Event']}, + 'combined_far': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'end_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}), + 'end_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}), + u'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}), + 'false_alarm_rate': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'ifos': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}), + 'mass': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'mchirp': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'minimum_duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'}) + }, + u'gracedb.embbeventlog': { + 'Meta': {'ordering': "['-created', '-N']", 'unique_together': "(('event', 'N'),)", 'object_name': 'EMBBEventLog'}, + 'N': ('django.db.models.fields.IntegerField', [], {}), + 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'dec': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'decWidth': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'duration': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}), + 'eel_status': ('django.db.models.fields.CharField', [], {'max_length': '2'}), + 'event': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Event']"}), + 'extra_info_dict': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'footprintID': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), + 'gpstime': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.EMGroup']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'instrument': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), + 'obs_status': ('django.db.models.fields.CharField', [], {'max_length': '2'}), + 'ra': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'raWidth': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}), + 'waveband': ('django.db.models.fields.CharField', [], {'max_length': '25'}) + }, + u'gracedb.emgroup': { + 'Meta': {'object_name': 'EMGroup'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}) + }, + u'gracedb.event': { + 'Meta': {'ordering': "['-id']", 'object_name': 'Event'}, + 'analysisType': ('django.db.models.fields.CharField', [], {'max_length': '20'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'far': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'gpstime': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'instruments': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}), + 'labels': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['gracedb.Label']", 'through': u"orm['gracedb.Labelling']", 'symmetrical': 'False'}), + 'likelihood': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'nevents': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}), + 'submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}), + 'uid': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}) + }, + u'gracedb.eventlog': { + 'Meta': {'ordering': "['-created', '-N']", 'unique_together': "(('event', 'N'),)", 'object_name': 'EventLog'}, + 'N': ('django.db.models.fields.IntegerField', [], {}), + 'comment': ('django.db.models.fields.TextField', [], {}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'event': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Event']"}), + 'file_version': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'filename': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'issuer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) + }, + u'gracedb.grbevent': { + 'Meta': {'ordering': "['-id']", 'object_name': 'GrbEvent', '_ormbases': [u'gracedb.Event']}, + 'author_ivorn': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), + 'author_shortname': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), + 'coord_system': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), + 'dec': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'error_radius': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + u'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}), + 'how_description': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), + 'how_reference_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}), + 'ivorn': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), + 'observatory_location_id': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), + 'ra': ('django.db.models.fields.FloatField', [], {'null': 'True'}) + }, + u'gracedb.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '20'}) + }, + u'gracedb.label': { + 'Meta': {'object_name': 'Label'}, + 'defaultColor': ('django.db.models.fields.CharField', [], {'default': "'black'", 'max_length': '20'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}) + }, + u'gracedb.labelling': { + 'Meta': {'object_name': 'Labelling'}, + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}), + 'event': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Event']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Label']"}) + }, + u'gracedb.multiburstevent': { + 'Meta': {'ordering': "['-id']", 'object_name': 'MultiBurstEvent', '_ormbases': [u'gracedb.Event']}, + 'amplitude': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'bandwidth': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'central_freq': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'confidence': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + u'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}), + 'false_alarm_rate': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'ifos': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}), + 'ligo_angle': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'ligo_angle_sig': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'ligo_axis_dec': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'ligo_axis_ra': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'peak_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}), + 'peak_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}), + 'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'start_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}), + 'start_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}) + }, + u'gracedb.singleinspiral': { + 'Gamma0': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'Gamma1': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'Gamma2': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'Gamma3': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'Gamma4': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'Gamma5': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'Gamma6': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'Gamma7': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'Gamma8': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'Gamma9': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'Meta': {'object_name': 'SingleInspiral'}, + 'alpha': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'alpha1': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'alpha2': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'alpha3': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'alpha4': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'alpha5': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'alpha6': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'amplitude': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'bank_chisq': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'bank_chisq_dof': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'beta': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'channel': ('django.db.models.fields.CharField', [], {'max_length': '20'}), + 'chi': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'chisq': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'chisq_dof': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'coa_phase': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'cont_chisq': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'cont_chisq_dof': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'eff_distance': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'end_time': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'end_time_gmst': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'end_time_ns': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'eta': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'event': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Event']"}), + 'event_duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'f_final': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ifo': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True'}), + 'impulse_time': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'impulse_time_ns': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'kappa': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'mass1': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'mass2': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'mchirp': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'mtotal': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'psi0': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'psi3': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'rsqveto_duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'search': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True'}), + 'sigmasq': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'tau0': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'tau2': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'tau3': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'tau4': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'tau5': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'template_duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'ttotal': ('django.db.models.fields.FloatField', [], {'null': 'True'}) + }, + u'gracedb.tag': { + 'Meta': {'object_name': 'Tag'}, + 'displayName': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), + 'eventlogs': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['gracedb.EventLog']", 'symmetrical': 'False'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['gracedb'] + symmetrical = True diff --git a/gracedb/models.py b/gracedb/models.py index 3ed021891b35fd9b621517fc24ce5ecb654d0220..14c1d225fa3965500301796fa796f7a66220d4ab 100644 --- a/gracedb/models.py +++ b/gracedb/models.py @@ -4,13 +4,12 @@ from django.core.urlresolvers import reverse from model_utils.managers import InheritanceManager from django.contrib.auth.models import User as DjangoUser -from django.contrib.auth.models import Group, Permission +from django.contrib.auth.models import Group from django.contrib.contenttypes.models import ContentType from guardian.models import GroupObjectPermission import os -import logging import glue import glue.ligolw @@ -21,8 +20,6 @@ from glue.lal import LIGOTimeGPS import json -log = logging.getLogger('gracedb.models') - # XXX ER2.utils. utils is in project directory. ugh. from utils import posixToGpsTime @@ -50,7 +47,6 @@ schema_version = "1.1" #def __unicode__(self): #return self.name - class Group(models.Model): name = models.CharField(max_length=20) def __unicode__(self): @@ -147,8 +143,6 @@ class Event(models.Model): def weburl(self): # XXX Not good. But then, it never was. - #return "https://ldas-jobs.phys.uwm.edu/gracedb/data/%s" % self.graceid() - #return "https://gracedb.ligo.org/gracedb-files/%s" % self.graceid() return reverse('file_list', args=[self.graceid()]) # XXX This should be considered deprecated. (Branson, July 22, 2014.) @@ -327,6 +321,164 @@ class EventLog(models.Model): # in the views that use it and give an informative error message. raise Exception("Too many attempts to save log message. Something is wrong.") +class EMGroup(models.Model): + name = models.CharField(max_length=20, unique=True) + + # XXX what else? Possibly the liasons. These can be populated + # automatically from the gw-astronomy COManage-provisioned LDAP. + # Let's leave this out for now. The submitter will be stored in + # the EMBB log record, and that should be enough for audit/blame + # purposes. + #liasons = models.ManyToManyField(DjangoUser) + + # XXX Characteristics needed to produce pointings? + + def __unicode__(self): + return self.name + +EMSPECTRUM = ( +('em.gamma', 'Gamma rays part of the spectrum'), +('em.gamma.soft', 'Soft gamma ray (120 - 500 keV)'), +('em.gamma.hard', 'Hard gamma ray (>500 keV)'), +('em.X-ray', 'X-ray part of the spectrum'), +('em.X-ray.soft', 'Soft X-ray (0.12 - 2 keV)'), +('em.X-ray.medium', 'Medium X-ray (2 - 12 keV)'), +('em.X-ray.hard', 'Hard X-ray (12 - 120 keV)'), +('em.UV', 'Ultraviolet part of the spectrum'), +('em.UV.10-50nm', 'Ultraviolet between 10 and 50 nm'), +('em.UV.50-100nm', 'Ultraviolet between 50 and 100 nm'), +('em.UV.100-200nm', 'Ultraviolet between 100 and 200 nm'), +('em.UV.200-300nm', 'Ultraviolet between 200 and 300 nm'), +('em.UV.FUV', 'Far-Infrared, 30-100 microns'), +('em.opt', 'Optical part of the spectrum'), +('em.opt.U', 'Optical band between 300 and 400 nm'), +('em.opt.B', 'Optical band between 400 and 500 nm'), +('em.opt.V', 'Optical band between 500 and 600 nm'), +('em.opt.R', 'Optical band between 600 and 750 nm'), +('em.opt.I', 'Optical band between 750 and 1000 nm'), +('em.IR', 'Infrared part of the spectrum'), +('em.IR.NIR', 'Near-Infrared, 1-5 microns'), +('em.IR.J', 'Infrared between 1.0 and 1.5 micron'), +('em.IR.H', 'Infrared between 1.5 and 2 micron'), +('em.IR.K', 'Infrared between 2 and 3 micron'), +('em.IR.MIR', 'Medium-Infrared, 5-30 microns'), +('em.IR.3-4um', 'Infrared between 3 and 4 micron'), +('em.IR.4-8um', 'Infrared between 4 and 8 micron'), +('em.IR.8-15um', 'Infrared between 8 and 15 micron'), +('em.IR.15-30um', 'Infrared between 15 and 30 micron'), +('em.IR.30-60um', 'Infrared between 30 and 60 micron'), +('em.IR.60-100um', 'Infrared between 60 and 100 micron'), +('em.IR.FIR', 'Far-Infrared, 30-100 microns'), +('em.mm', 'Millimetric part of the spectrum'), +('em.mm.1500-3000GHz', 'Millimetric between 1500 and 3000 GHz'), +('em.mm.750-1500GHz', 'Millimetric between 750 and 1500 GHz'), +('em.mm.400-750GHz', 'Millimetric between 400 and 750 GHz'), +('em.mm.200-400GHz', 'Millimetric between 200 and 400 GHz'), +('em.mm.100-200GHz', 'Millimetric between 100 and 200 GHz'), +('em.mm.50-100GHz', 'Millimetric between 50 and 100 GHz'), +('em.mm.30-50GHz', 'Millimetric between 30 and 50 GHz'), +('em.radio', 'Radio part of the spectrum'), +('em.radio.12-30GHz', 'Radio between 12 and 30 GHz'), +('em.radio.6-12GHz', 'Radio between 6 and 12 GHz'), +('em.radio.3-6GHz', 'Radio between 3 and 6 GHz'), +('em.radio.1500-3000MHz','Radio between 1500 and 3000 MHz'), +('em.radio.750-1500MHz','Radio between 750 and 1500 MHz'), +('em.radio.400-750MHz', 'Radio between 400 and 750 MHz'), +('em.radio.200-400MHz', 'Radio between 200 and 400 MHz'), +('em.radio.100-200MHz', 'Radio between 100 and 200 MHz'), +('em.radio.20-100MHz', 'Radio between 20 and 100 MHz'), +) + +class EMBBEventLog(models.Model): + """EMBB EventLog: A multi-purpose annotation for EM followup. + + A rectangle on the sky, equatorially aligned, + that has or will be imaged that is related to an event""" + + class Meta: + ordering = ['-created', '-N'] + unique_together = ("event","N") + + # A counter for Eels associated with a given event. This is + # important for addressibility. + N = models.IntegerField(null=False) + + # The time at which this Eel was created. Important for event auditing. + created = models.DateTimeField(auto_now_add=True) + + # The gracedb event that this Eel relates to + event = models.ForeignKey(Event) + + # The responsible author of this communication + submitter = models.ForeignKey(DjangoUser) # from a table of people + + # The MOU group responsible + group = models.ForeignKey(EMGroup) # from a table of facilities + + # The instrument used or intended for the imaging implied by this footprint + instrument = models.CharField(max_length=200, blank=True) + + # Facility-local identifier for this footprint + footprintID= models.CharField(max_length=200, blank=True) + # Now the global ID is a concatenation: facilityName#footprintID + + # the EM waveband used for the imaging as below + waveband = models.CharField(max_length=25, choices=EMSPECTRUM) + + # The center of the rectangular foorprint, right ascension and declination + # in J2000 in decimal degrees + ra = models.FloatField(null=True) + dec = models.FloatField(null=True) + + # The width and height (RA range and Dec range) in decimal degrees + raWidth = models.FloatField(null=True) + decWidth = models.FloatField(null=True) + + # The GPS time of the middle of of the imaging time + gpstime = models.PositiveIntegerField(null=True) + + # The duration of the imaging in seconds + duration = models.PositiveIntegerField(null=True) + + # Event Log status + EEL_STATUS_CHOICES = (('FO','FOOTPRINT'), ('SO','SOURCE'), ('CO','COMMENT'), ('CI','CIRCULAR')) + eel_status = models.CharField(max_length=2, choices=EEL_STATUS_CHOICES) + + # Observation status. If OBSERVATION, then there is a good chance of good image + OBS_STATUS_CHOICES = (('NA', 'NOT APPLICABLE'), ('OB','OBSERVATION'), ('TE','TEST'), ('PR','PREDICTION')) + obs_status = models.CharField(max_length=2, choices=OBS_STATUS_CHOICES) + + # This field is natural language for human + comment = models.TextField(blank=True) + + # This field is formal struct by a syntax TBD + # for example {"phot.mag.limit": 22.3} + extra_info_dict = models.TextField(blank=True) + + # We overload the 'save' method to avoid race conditions, since the Eels are numbered. + def save(self, *args, **kwargs): + success = False + attempts = 0 + while (not success and attempts < 5): + attempts = attempts + 1 + if self.event.embbeventlog_set.count(): + self.N = int(self.event.embbeventlog_set.aggregate(models.Max('N'))['N__max']) + 1 + else: + self.N = 1 + try: + super(EMBBEventLog, self).save(*args, **kwargs) + success = True + except IntegrityError: + # IntegrityError means an attempt to insert a duplicate + # key or to violate a foreignkey constraint. + # We are under race conditions. Let's try again. + pass + + if not success: + # XXX Should this be a custom exception? That way we could catch it + # in the views that use it and give an informative error message. + raise Exception("Too many attempts to save EMBB entry. Something is wrong.") + class Labelling(models.Model): event = models.ForeignKey(Event) label = models.ForeignKey(Label) @@ -458,14 +610,14 @@ class SingleInspiral(models.Model): field_names = cls.field_names() created_events = [] - log.debug("Single/create from table/fields: " + str(field_names)) + #log.debug("Single/create from table/fields: " + str(field_names)) for row in table: e = cls(event=event) - log.debug("Single/creating event") + #log.debug("Single/creating event") for column in field_names: value = getattr(row, column) - log.debug("Setting column '%s' with value '%s'" % (column, value)) + #log.debug("Setting column '%s' with value '%s'" % (column, value)) setattr(e, column, value) e.save() created_events.append(e) diff --git a/gracedb/urls.py b/gracedb/urls.py index 7a883f37f311756cd9a9f663fe65022d64537d5f..397699b595c888908bbe033d6f589985f36223ca 100644 --- a/gracedb/urls.py +++ b/gracedb/urls.py @@ -20,8 +20,11 @@ urlpatterns = patterns('gracedb.views', url (r'^(?P<graceid>[GEHT]\d+)/files/$', 'file_list', name="file_list"), url (r'^(?P<graceid>[GEHT]\d+)/files/(?P<filename>.*)$', download, name="file"), url (r'^(?P<graceid>[GEHT]\d+)/log/(?P<num>([\d]*|preview))$', 'logentry', name="logentry"), + url (r'^(?P<graceid>[GEHT]\d+)/embblog/(?P<num>([\d]*|preview))$', 'embblogentry', name="embblogentry"), url (r'^(?P<graceid>[GEHT]\d+)/log/(?P<num>\d+)/tag/(?P<tagname>\w+)$', 'taglogentry', name="taglogentry"), +# RDW Aug 2014 +#(r'^admin/', include(admin.site.urls)), # (r'^view/(?P<uid>[\w\d]+)', 'view'), # (r'^edit/(?P<uid>[\w\d]+)', 'edit'), diff --git a/gracedb/urls_rest.py b/gracedb/urls_rest.py index 9a25d66b24079a7376eda9dd18b2b56f74ef928d..db2d38a5e1f70728974a5b2b683134a50b71bf74 100644 --- a/gracedb/urls_rest.py +++ b/gracedb/urls_rest.py @@ -7,6 +7,7 @@ from django.conf.urls import patterns, url from gracedb.api import GracedbRoot from gracedb.api import EventList, EventDetail, EventVODetail from gracedb.api import EventLogList, EventLogDetail +from gracedb.api import EMBBEventLogList, EMBBEventLogDetail from gracedb.api import TagList # from gracedb.api import TagDetail from gracedb.api import EventTagList, EventTagDetail @@ -39,6 +40,13 @@ urlpatterns = patterns('gracedb.api', url (r'events/(?P<graceid>[GEHT]\d+)/log/(?P<n>\d+)$', EventLogDetail.as_view(), name='eventlog-detail'), + # EMBB Event Log Resources + # events/{graceid}/logs/[{logid}] + url (r'events/(?P<graceid>[GEHT]\d+)/embb/$', + EMBBEventLogList.as_view(), name='embbeventlog-list'), + url (r'events/(?P<graceid>[GEHT]\d+)/embb/(?P<n>\d+)$', + EMBBEventLogDetail.as_view(), name='embbeventlog-detail'), + # Tag Resources url (r'^tag/$', TagList.as_view(), name='tag-list'), diff --git a/gracedb/view_logic.py b/gracedb/view_logic.py index b7657b84a1ba3a07d522a9598a2674c90584c110..5e6c1ff236ccdb6d490d657a1d351c676efb94b1 100644 --- a/gracedb/view_logic.py +++ b/gracedb/view_logic.py @@ -6,6 +6,7 @@ from models import Pipeline, Search from models import CoincInspiralEvent from models import MultiBurstEvent from models import GrbEvent +from models import EMBBEventLog, EMGroup from alert import issueAlert, issueAlertForLabel, issueAlertForUpdate from translator import handle_uploaded_data @@ -315,3 +316,55 @@ def get_lvem_perm_status(request, event): return (True, False) else: return (False, False) + +# +# Create an EMBB event log message +# +def create_eel(d, event, user): + # create a log entry + eel = EMBBEventLog(event=event) + eel.event = event + eel.submitter = user + # Assign a group name + try: + eel.group = EMGroup.objects.get(name=d.get('group')) + except: + raise ValueError('Please specify an EM followup MOU group') + + # Assign an instrument name + eel.instrument = d.get('instrument', '') + + # Assign a group-specific footprint ID (if provided) + eel.footprintID = d.get('footprintID', '') + + # Assign the EM spectrum string + try: + eel.waveband = d.get('waveband') + except: + raise ValueError('Please specify a waveband') + + # Assign RA and Dec, plus widths + eel.ra = d.get('ra', None) + eel.dec = d.get('dec', None) + eel.raWidth = d.get('raWidth', None) + eel.decWidth = d.get('decWidth', None) + + # Assign gpstime and duration. + eel.gpstime = d.get('gpstime', None) + eel.duration = d.get('duration', None) + + # Assign EEL status and observation status. + try: + eel.eel_status = d.get('eel_status') + except: + raise ValueError('Please specify an EEL status.') + try: + eel.obs_status = d.get('obs_status') + except: + raise ValueError('Please specify an observation status.') + + eel.extra_info_dict = d.get('extra_info_dict', '') + eel.comment = d.get('comment', '') + eel.save() + return eel + diff --git a/gracedb/views.py b/gracedb/views.py index 78d016b373cb14f2b9b31755b75d79511ea88013..f7130228d48c6cb421159b2a9dd961c2e6c1bd6d 100644 --- a/gracedb/views.py +++ b/gracedb/views.py @@ -11,6 +11,7 @@ from django.shortcuts import render_to_response from django.views.generic.list import ListView from models import Event, Group, EventLog, Label, Tag, Pipeline, Search +from models import EMGroup from forms import CreateEventForm, EventSearchForm, SimpleSearchForm from django.contrib.auth.models import User, Permission @@ -22,6 +23,7 @@ from guardian.models import GroupObjectPermission from view_logic import _createEventFromForm from view_logic import get_performance_info from view_logic import get_lvem_perm_status +from view_logic import create_eel from view_utils import assembleLigoLw, get_file from view_utils import flexigridResponse, jqgridResponse @@ -277,9 +279,11 @@ def view(request, event): context['nearby'] = [(e.gpstime - event.gpstime, e) for e in event.neighbors()] # context['skyalert_authorized'] = skyalert_authorized(request) + context['groups'] = [g.name for g in EMGroup.objects.all()] context['blessed_tags'] = settings.BLESSED_TAGS context['single_inspiral_events'] = list(event.singleinspiral_set.all()) context['neighbor_delta'] = "[%+d,%+d]" % (-5,5) + context['SKYMAP_VIEWER_SERVICE_URL'] = settings.SKYMAP_VIEWER_SERVICE_URL # XXX This is something of a hack. In the future, we will want to show the # executive user a list of groups and a two column list of radio buttons, showing @@ -763,6 +767,35 @@ def modify_permissions(request, event): # Finished. Redirect back to the event. return HttpResponseRedirect(reverse("view", args=[event.graceid()])) +# A view to create embb log entries +def embblogentry(request, graceid, num=None): + try: + event = Event.getByGraceid(graceid) + except Event.DoesNotExist: + raise Http404 + if request.method == "POST": + try: + eel = create_eel(request.POST, event, request.user) + except ValueError, e: + return HttpResponseBadRequest(str(e)) + except Exception, e: + return HttpResponseServerError(str(e)) + else: + try: + eel = event.eventlog_set.filter(N=num)[0] + except Exception: + raise Http404 + + if not request.is_ajax(): + return HttpResponseRedirect(reverse(view, args=[graceid])) + + rv = {} + rv['comment'] = eel.comment + rv['submitter'] = eel.issuer.username + rv['created'] = eel.created.isoformat() + + return HttpResponse(json.dumps(rv), content_type="application/json") + #------------------------------------------------------------------------------------------ # Old Stuff #------------------------------------------------------------------------------------------ @@ -918,3 +951,4 @@ def modify_permissions(request, event): # return HttpResponseRedirect(reverse(view, args=[graceid])) # + diff --git a/ligoauth/management/commands/refresh_users_from_ldap.py b/ligoauth/management/commands/refresh_users_from_ldap.py index 7d30d33ba4d51f7c335183099ce321a6f0268308..3fb0226c7688151d610b523a0760a9a810195f40 100644 --- a/ligoauth/management/commands/refresh_users_from_ldap.py +++ b/ligoauth/management/commands/refresh_users_from_ldap.py @@ -1,7 +1,7 @@ from django.core.management.base import NoArgsCommand -from ligoauth.models import LigoLdapUser, X509Cert +from ligoauth.models import LigoLdapUser, X509Cert, AlternateEmail from django.contrib.auth.models import User, Group @@ -15,7 +15,9 @@ retrieveAttributes = ["krbPrincipalName", "givenName", "sn", "mail", - "isMemberOf"] + "isMemberOf", + "mailAlternateAddress", + "mailForwardingAddress"] class Command(NoArgsCommand): help = "Update ligoauth.models.LigoUser and django.contrib.auth.models.User from LIGO LDAP" @@ -39,6 +41,12 @@ class Command(NoArgsCommand): memberships = ldap_result.get('isMemberOf',[]) is_active = "Communities:LSCVirgoLIGOGroupMembers" in memberships principal = ldap_result['krbPrincipalName'][0] + #mailForwardingAddress = ldap_result.get('mailForwardingAddress', None) + try: + mailForwardingAddress = unicode(ldap_result['mailForwardingAddress'][0]) + except: + mailForwardingAddress = None + mailAlternateAddresses = ldap_result.get('mailAlternateAddress', []) # Update/Create LigoLdapUser entry # This is breaking. XXX Do we need to pass in default values for the underlying User object? @@ -93,3 +101,19 @@ class Command(NoArgsCommand): u = User.objects.get(username = user.username) print "Adding %s to %s" % (user.username,g.name) g.user_set.add(u) + + # Finally, deail with alternate emails. + if mailForwardingAddress: + try: + AlternateEmail.objects.get_or_create(user=user, + email=mailForwardingAddress) + except: + pass + + if len(mailAlternateAddresses) > 0: + for email in mailAlternateAddresses: + try: + AlternateEmail.objects.get_or_create(user=user, + email=email) + except: + pass diff --git a/ligoauth/middleware/auth.py b/ligoauth/middleware/auth.py index b0f3963a64b32759131e826571c6feab982801a6..f30c74ccb0ceae7f921b84db6acd2533cfa4a541 100644 --- a/ligoauth/middleware/auth.py +++ b/ligoauth/middleware/auth.py @@ -73,6 +73,7 @@ class LigoAuthMiddleware: # An authenticated LIGO user will have one of these set. remote_user = request.META.get('REMOTE_USER') + message = remote_user dn = cert_dn_from_request(request) # Apache should be configured so that the *only* thing that can @@ -90,7 +91,7 @@ class LigoAuthMiddleware: return HttpResponseForbidden("{ 'error': '%s' }" % str(e)) if not (user and user.is_authenticated()): - # XXX THIS SHOULD NEVER HAPPEN + message += "THIS SHOULD NEVER HAPPEN" pass # Add shib user to groups. This operation is idempotent, but may @@ -135,7 +136,7 @@ class LigoAuthMiddleware: return HttpResponseForbidden("{ 'error': '%s' }" % message) return render_to_response( 'forbidden.html', - {}, + {'error': message}, context_instance=RequestContext(request)) class RemoteUserBackend(DefaultRemoteUserBackend): diff --git a/ligoauth/migrations/0012_auto__add_alternateemail.py b/ligoauth/migrations/0012_auto__add_alternateemail.py new file mode 100644 index 0000000000000000000000000000000000000000..d42a7cbd1bf3fb84e8a9121c16aee7bb3897b611 --- /dev/null +++ b/ligoauth/migrations/0012_auto__add_alternateemail.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'AlternateEmail' + db.create_table(u'ligoauth_alternateemail', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), + ('email', self.gf('django.db.models.fields.EmailField')(max_length=254)), + )) + db.send_create_signal(u'ligoauth', ['AlternateEmail']) + + + def backwards(self, orm): + # Deleting model 'AlternateEmail' + db.delete_table(u'ligoauth_alternateemail') + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'ligoauth.alternateemail': { + 'Meta': {'object_name': 'AlternateEmail'}, + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '254'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) + }, + u'ligoauth.ligoldapuser': { + 'Meta': {'object_name': 'LigoLdapUser', '_ormbases': [u'auth.User']}, + 'ldap_dn': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), + u'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}) + }, + u'ligoauth.localuser': { + 'Meta': {'object_name': 'LocalUser', '_ormbases': [u'auth.User']}, + u'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}) + }, + u'ligoauth.x509cert': { + 'Meta': {'object_name': 'X509Cert'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'subject': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False'}) + } + } + + complete_apps = ['ligoauth'] \ No newline at end of file diff --git a/ligoauth/models.py b/ligoauth/models.py index eb8efc716f4322b376457194716f308b40a7a6b0..b64d6dc9f7c4cb991f90cc7e7a1286ea36139f5f 100644 --- a/ligoauth/models.py +++ b/ligoauth/models.py @@ -65,6 +65,9 @@ class X509Cert(models.Model): subject = models.CharField(max_length=200) users = models.ManyToManyField(User) +class AlternateEmail(models.Model): + user = models.ForeignKey(User) + email = models.EmailField(max_length=254) def shibid_to_user(shibid): try: diff --git a/manage.py b/manage.py index 6124f342fe5b699d4d3b418fb426564cd0a455a7..c632a8a38923012b836655195baeadd5c2ffd930 100755 --- a/manage.py +++ b/manage.py @@ -7,4 +7,3 @@ if __name__ == "__main__": from django.core.management import execute_from_command_line execute_from_command_line(sys.argv) - diff --git a/settings/__init__.py b/settings/__init__.py index 2c418284b3e0406651300b8c7ccef3d4b196409e..327421861d827656acbba8b37530cb0c54a7f144 100644 --- a/settings/__init__.py +++ b/settings/__init__.py @@ -32,6 +32,9 @@ configs = { '/home/fzhang/gracedb/gracedb': 'fan', '/home/fzhang/gracedb/gracedb/gracedb': 'fan', + '/home/roywilliams/gracedbdev': 'roy', + '/home/roywilliams/gracedbdev/gracedb': 'roy', + "/home/gracedb/gracestage" : "stage", } diff --git a/settings/branson.py b/settings/branson.py index 167583f5aad8aab3950a386f18833e3db5b48375..7ef2d84c0ae44b45d0ad437ad5178a4e68e8400d 100644 --- a/settings/branson.py +++ b/settings/branson.py @@ -32,6 +32,11 @@ ALERT_TEST_EMAIL_TO = [ "Branson Stephens <branson@gravity.phys.uwm.edu>", ] +EMBB_MAIL_ADDRESS = 'branson@moe.phys.uwm.edu' +EMBB_SMTP_SERVER = 'localhost' +EMBB_MAIL_ADMINS = ['branson@gravity.phys.uwm.edu',] +EMBB_IGNORE_ADDRESSES = ['Mailer-Daemon@moe.phys.uwm.edu',] + # Don't sent out non-test XMPP alerts on dev box! XMPP_ALERT_CHANNELS = [ 'test_omega', diff --git a/settings/default.py b/settings/default.py index 77bdbac0b9b94eff0c5c1c7042862fc3e9ebaab0..26eabc87d8a48ab76d29be320725a07d92dec45e 100644 --- a/settings/default.py +++ b/settings/default.py @@ -25,6 +25,11 @@ ALERT_TEST_EMAIL_FROM = "GraCEDb TEST <gracedb@archie.phys.uwm.edu>" ALERT_TEST_EMAIL_TO = [ ] +EMBB_MAIL_ADDRESS = 'embb@gracedb.ligo.org' +EMBB_SMTP_SERVER = 'localhost' +EMBB_MAIL_ADMINS = ['branson@gravity.phys.uwm.edu','roy.williams@ligo.org',] +MBB_IGNORE_ADDRESSES = ['Mailer-Daemon@gracedb.phys.uwm.edu',] + XMPP_ALERT_CHANNELS = [ 'burst_omega', 'test_omega', @@ -40,6 +45,8 @@ XMPP_ALERT_CHANNELS = [ 'external_grb', ] +SKYMAP_VIEWER_SERVICE_URL = "https://embb-dev.ligo.caltech.edu/cgi-bin/skymapViewer" + BLESSED_TAGS = [ 'analyst_comments', 'psd', diff --git a/settings/roy.py b/settings/roy.py new file mode 100644 index 0000000000000000000000000000000000000000..dc7120dfb7e682a257da25d3dce29b1fae8815ae --- /dev/null +++ b/settings/roy.py @@ -0,0 +1,189 @@ +# XXX I know import * is ugly, but I want the stuff from logSettings to be +# in this namespace. +# from logSettings import * + +CONFIG_NAME = "EMBB development" + +DEBUG = True +TEMPLATE_DEBUG = DEBUG +# LOG_ROOT = '/home/jkanner/logs' +# LOGGING={} + +DATABASES = { + 'default' : { + 'NAME' : 'gracedb', + 'ENGINE' : 'django.db.backends.mysql', + 'USER' : 'jkanner', + 'PASSWORD' : 'batman', + 'OPTIONS' : { + 'init_command' : 'SET storage_engine=MyISAM', + } + } +} + +ROOT_URLCONF = 'urls' + +#MEDIA_URL = "/gracedb-static/" + +SKYMAP_VIEWER_MEDIA_URL = "/skymap-viewer/" + +GRACEDB_DATA_DIR = "/home/roywilliams/gracedbData" +MPLCONFIGDIR = "/home/jkanner/mplconfig" + +ALERT_EMAIL_FROM = "Dev Alert <root@embb-dev.ligo.caltech.edu>" +ALERT_EMAIL_TO = [ + "Roy Williams <roy@caltech.edu>", + ] +ALERT_EMAIL_BCC = ["roy@caltech.edu"] + +ALERT_TEST_EMAIL_FROM = "Dev Test Alert <root@embb-dev.ligo.caltech.edu>" +ALERT_TEST_EMAIL_TO = [ + "Roy Williams <roy@caltech.edu>", + ] + + +EMBB_MAIL_ADDRESS = 'embb@embb-dev.ligo.caltech.edu' +EMBB_SMTP_SERVER = 'acrux.ligo.caltech.edu' +EMBB_MAIL_ADMINS = ['roy.williams@ligo.org',] + +BLESSED_TAGS = [ + 'analyst_comments', + 'psd', + 'data_quality', + 'sky_loc', + 'background', + 'ext_coinc', + 'strain', + 'tfplots', + 'sig_info', + 'audio', + ] + + +# Don't sent out non-test XMPP alerts on dev box! +XMPP_ALERT_CHANNELS = [ + 'test_omega', + 'test_mbtaonline', + 'test_cwb', + 'test_lowmass', + ] + +# SkyAlert +SKYALERT_IVORN_PATTERN = "ivo://ligo.org/gracedb#%s-dev" + +# Latency histograms. Where they go and max latency to bin. +LATENCY_REPORT_DEST_DIR = "/home/jkanner/data/latency" +LATENCY_REPORT_WEB_PAGE_FILE_PATH = LATENCY_REPORT_DEST_DIR + "/latency.inc" + +# Uptime reporting +UPTIME_REPORT_DIR = "/home/jkanner/data/uptime" + + +SITE_ID = 4 + +TEMPLATE_DIRS = ( + # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. + "/home/roywilliams/gracedbdev/templates", +) + +MIDDLEWARE_CLASSES = [ + 'middleware.accept.AcceptMiddleware', + 'middleware.cli.CliExceptionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', +# 'django.contrib.auth.middleware.AuthenticationMiddleware', +# 'ligodjangoauth.LigoShibbolethMiddleware', + 'ligoauth.middleware.auth.LigoAuthMiddleware', + 'maintenancemode.middleware.MaintenanceModeMiddleware', +# 'debug_toolbar.middleware.DebugToolbarMiddleware', +] + +AUTHENTICATION_BACKENDS = ( +# 'gracedb.middleware.auth.LigoAuthBackend', + 'ligoauth.middleware.auth.LigoX509Backend', + 'ligoauth.middleware.auth.LigoShibBackend', +# 'ligoauth.middleware.auth.RemoteUserBackend', +# 'ligodjangoauth.LigoShibbolethAuthBackend', +# 'django.contrib.auth.backends.ModelBackend', +) + + +TEMPLATE_CONTEXT_PROCESSORS = ( + #"django.core.context_processors.auth", + # replaced by... + "django.contrib.auth.context_processors.auth", + "django.core.context_processors.debug", + "django.core.context_processors.i18n", + "django.core.context_processors.media", + "django.core.context_processors.static", + "django.core.context_processors.request", + "gracedb.middleware.auth.LigoAuthContext", + 'middleware.debug.LigoDebugContext', +) + + +INSTALLED_APPS = ( + 'django.contrib.auth', + 'django.contrib.admin', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', + 'django.contrib.staticfiles', + 'gracedb', + 'userprofile', + 'ligoauth', + 'rest_framework', + 'south', +# 'debug_toolbar', +) + +INTERNAL_IPS = ( + '129.89.61.55', +) + + +LOGGING = {} +x = """ HACH HACK HACK +LOG_ROOT = '/home/jkanner/logs' +LOG_FILE_SIZE = 1024*1024 # 1 MB +LOG_FILE_BAK_CT = 3 + +LOGGING = { + 'version': 1, + 'disable_existing_loggers' : True, + 'formatters': { + 'simple': { + 'format': '%(asctime)s: %(message)s', + 'datefmt': '%Y-%m-%dT%H:%M:%S', + }, + }, + 'handlers': { + 'null': { + 'level':'DEBUG', + 'class':'django.utils.log.NullHandler', + }, + 'performance_file': { + 'class': 'logging.handlers.RotatingFileHandler', + 'formatter': 'simple', + 'filename': '%s/gracedb_performance.log' % LOG_ROOT, + 'maxBytes': LOG_FILE_SIZE, + 'backupCount': LOG_FILE_BAK_CT, + }, + }, + 'loggers': { + 'django': { + 'handlers': ['null'], + 'propagate': True, + 'level': 'INFO', + }, + 'middleware': { + 'handlers': ['performance_file'], + 'propagate': True, + 'level': 'INFO', + }, + }, +} +""" diff --git a/templates/forbidden.html b/templates/forbidden.html index e383758b9eccf0009b85c5f6af6520062175d58b..64e1485fcff1967531049642c60eac2a8fa038d0 100644 --- a/templates/forbidden.html +++ b/templates/forbidden.html @@ -6,6 +6,6 @@ {% block heading %}Forbidden{{ object.graceid }}{% endblock %} {% block content %} - <p>The item you requested is not for you.</p> {{ message|safe }} + <p>You are {{ error }} and the item you requested is not for you.</p> {{ message|safe }} {% endblock %} diff --git a/templates/gracedb/event_detail.html b/templates/gracedb/event_detail.html index 06f51c5c3f1312705b08084debc26af32cd2ee3e..9b71370d82b66e732d5ffca6b4a164d28a3f36e2 100644 --- a/templates/gracedb/event_detail.html +++ b/templates/gracedb/event_detail.html @@ -30,6 +30,10 @@ // Generate an array, the elements of which contain the inner html for each thematic digest //------------------------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------------------------------ + + function divFunction() { + } + function getDigests() { var digests = new Array(); {% for tag in object.getAvailableTags %} @@ -93,6 +97,10 @@ dc += ' <td>{{log.comment|sanitize|escapejs}} \n'; {% if log.fileurl %} dc += ' <a href="{{log.fileurl}}">{{log.filename}}</a> \n'; + // If this is a skymap.json file, we want a button. + {% if log.filename == "skymap.json" %} + dc += ' <button type="button" data-dojo-type="dijit/form/Button" id="sV_button" class="modButtonClass">View in skymapViewer!</button> \n'; + {% endif %} {% endif %} dc += ' </td> \n'; dc += ' </tr> \n'; @@ -491,6 +499,28 @@ new Tooltip({ connectId: tag.button_id, label: "delete this tag"}); } + + // Handle the post to skymapViewer button. + var sV_button = dom.byId("sV_button"); + if (sV_button) { + on(sV_button, "click", function() { + // construct skymap.json url. + var skymap_json_url = "{% url "file" object.graceid "skymap.json" %}" + // fetch JSON content. + dojo.xhrGet({ + url: skymap_json_url, + load: function(result) { + // Find the form and set its value to the appropriate JSON + sV_form = dom.byId("sV_form"); + // Shove the skymap.json contents into the value for the second form field. + sV_form.elements[1].value = result; + // Submit the form, which takes the user to the skymapViewer server. + sV_form.submit(); + } + }); + }); + } + }); </script> @@ -505,7 +535,7 @@ <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/CollapsibleToolbar.css" /> <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/Blockquote.css" /> <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/Smiley.css" /> - <style> +<style> @import "https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dijit/themes/tundra/tundra.css"; a:hover .dojoxEnlarge { display: block !important; @@ -617,6 +647,17 @@ {% endblock %} </div> +{# Enable post to skymap viewer #} +<div id="sV_form_div" style="display: none"> + +<form action="{{ SKYMAP_VIEWER_SERVICE_URL }}" id="sV_form" method="post"> + <input type="hidden" name="skymapid" value="{{ object.graceid }}"> + <!-- <input type="hidden" name="json" value="{{ skymap_json }}"> --> + <input type="hidden" name="json" value="blaargh"> + <input type="submit" value="View in skymapViewer!"> +</form> +</div> + <!-- Neighbors Support refresh_neighbors() function --> <script type="text/javascript"> var refresh_neighbors= function() { alert("NOT SET YET"); }; @@ -644,21 +685,237 @@ }); require(["dijit/InlineEditBox", "dijit/form/NumberSpinner", "dijit/form/TextBox"]); -</script> + </script> + + {% include "gracedb/neighbors_frag.html" %} +</p> + + <!-- Single Inspiral Data --> + + + <div id="bulletin_board" data-dojo-type="dijit/TitlePane" + data-dojo-props="title: 'EM Follow-up Bulletin Board', open: false"> + + <div data-dojo-type="dijit/form/Form" id="myForm" + data-dojo-id="myForm" encType="multipart/form-data" action="" + method=""> + <script> + require(["dojo/parser", "dijit/form/Form", "dijit/form/Button", "dijit/form/ValidationTextBox", "dijit/form/DateTextBox"]); + </script> + + <script type="dojo/on" data-dojo-event="reset"> + return confirm('Press OK to reset widget values'); + </script> + + <script type="dojo/on" data-dojo-event="submit"> + if(this.validate()){ + return confirm('Form is valid, press OK to submit'); + }else{ + alert('Form contains invalid data. Please correct first'); + return false; + } + return true; + </script> + + <h2>Make a report here</h2> + +<form method="POST" action="{% url "embblogentry" object.graceid "" %}"> +<table> + +<tr><td><a href=# onclick="alert('Define here what subclass of report this is:\n FOOTPRINT: There is or could be image data taken of the sky, with a specific instrument, at a specific time, in a specific waveband, as followup of a specific LVC alert.\n SOURCE: There is a candidate source at the given location, that could be associated with the given LVC alert.\n COMMENT: The primary purpose of this report is a human-written comment concerning the given LVC alert.'); return false;"> +What type of report is this?</a></td> <td><select name="eel_status"> +<option value="FO">FOOTPRINT</option> +<option value="SO">SOURCE</option> +<option value="CO">COMMENT</option> +</select></td></tr> + +<tr><td><a href=# onclick="alert('There is also Observation Status which is:\n OBSERVATION: has been done.\n PREDICTION: this report represents intent or future observation.\n TEST: This EEL is part of a test and has no astrophysical significance.\n NOT APPLICABLE: It is neither observation nor prediction.'); return false;"> +Observation Status</a></td> <td><select name="obs_status"> +<option value="OB">OBSERVATION</option> +<option value="PR">PREDICTION</option> +<option value="TE">TEST</option> +<option value="NA">NOT APPLICABLE</option> +</select></td></tr> + +<tr><td><a href=# onclick="alert('Group with which the LSC has signed a trust agreement, hereby providing data +under its trust (required).'); return false;"> +Which MOU Group provides this report?</a></td> <td><select name="group"> +{% for g in groups %} +<option value="{{ g }}">{{ g }}</option> +{% endfor %} </select> </td> </tr> + + + +<tr><td><a href=# onclick="alert('An ID from the data owner to identify this footprint (optional).');return false;"> +Your reference</a></td> <td><input type="text" name="footprintID"/></td></tr> + +<tr><td><a href=# onclick="alert('A natural language report.');return false;"> +Report as text</a></td> <td colspan=2><textarea name="comment" rows="8" cols="50"></textarea></td></tr> + +<tr><td><a href=# onclick="alert('The name of the photon detector whose observational metadata is being recorded here (optional).');return false;"> +Instrument</a></td> <td><input type="text" name="instrument"> </td> </tr> + +<tr><td><a href=# onclick="alert('Specify waveband of the observation. Vocabulary is taken from the UCD vocabulary, a Virtual Observatory standard. Waveband can be either wide (eg Radio) or narrow (eg Radio 200-400 MHz).');return false;"> +Waveband</a></td> <td colspan=2><select name="waveband"> +<option value="em.gamma">Gamma rays part of the spectrum</option> +<option value="em.gamma.soft">Soft gamma ray (120 - 500 keV)</option> +<option value="em.gamma.hard">Hard gamma ray (>500 keV)</option> +<option value="em.X-ray">X-ray part of the spectrum</option> +<option value="em.X-ray.soft">Soft X-ray (0.12 - 2 keV)</option> +<option value="em.X-ray.medium">Medium X-ray (2 - 12 keV)</option> +<option value="em.X-ray.hard">Hard X-ray (12 - 120 keV)</option> +<option value="em.UV">Ultraviolet part of the spectrum</option> +<option value="em.UV.10-50nm">Ultraviolet between 10 and 50 nm</option> +<option value="em.UV.50-100nm">Ultraviolet between 50 and 100 nm</option> +<option value="em.UV.100-200nm">Ultraviolet between 100 and 200 nm</option> +<option value="em.UV.200-300nm">Ultraviolet between 200 and 300 nm</option> +<option value="em.UV.FUV">Far-Infrared, 30-100 microns</option> +<option value="em.opt">Optical part of the spectrum</option> +<option value="em.opt.U">Optical band between 300 and 400 nm</option> +<option value="em.opt.B">Optical band between 400 and 500 nm</option> +<option value="em.opt.V">Optical band between 500 and 600 nm</option> +<option value="em.opt.R">Optical band between 600 and 750 nm</option> +<option value="em.opt.I">Optical band between 750 and 1000 nm</option> +<option value="em.IR">Infrared part of the spectrum</option> +<option value="em.IR.NIR">Near-Infrared, 1-5 microns</option> +<option value="em.IR.J">Infrared between 1.0 and 1.5 micron</option> +<option value="em.IR.H">Infrared between 1.5 and 2 micron</option> +<option value="em.IR.K">Infrared between 2 and 3 micron</option> +<option value="em.IR.MIR">Medium-Infrared, 5-30 microns</option> +<option value="em.IR.3-4um">Infrared between 3 and 4 micron</option> +<option value="em.IR.4-8um">Infrared between 4 and 8 micron</option> +<option value="em.IR.8-15um">Infrared between 8 and 15 micron</option> +<option value="em.IR.15-30um">Infrared between 15 and 30 micron</option> +<option value="em.IR.30-60um">Infrared between 30 and 60 micron</option> +<option value="em.IR.60-100um">Infrared between 60 and 100 micron</option> +<option value="em.IR.FIR">Far-Infrared, 30-100 microns</option> +<option value="em.mm">Millimetric part of the spectrum</option> +<option value="em.mm.1500-3000GHz">Millimetric between 1500 and 3000 GHz</option> +<option value="em.mm.750-1500GHz">Millimetric between 750 and 1500 GHz</option> +<option value="em.mm.400-750GHz">Millimetric between 400 and 750 GHz</option> +<option value="em.mm.200-400GHz">Millimetric between 200 and 400 GHz</option> +<option value="em.mm.100-200GHz">Millimetric between 100 and 200 GHz</option> +<option value="em.mm.50-100GHz">Millimetric between 50 and 100 GHz</option> +<option value="em.mm.30-50GHz">Millimetric between 30 and 50 GHz</option> +<option value="em.radio">Radio part of the spectrum</option> +<option value="em.radio.12-30GHz">Radio between 12 and 30 GHz</option> +<option value="em.radio.6-12GHz">Radio between 6 and 12 GHz</option> +<option value="em.radio.3-6GHz">Radio between 3 and 6 GHz</option> +<option value="em.radio.1500-3000MHz">Radio between 1500 and 3000 MHz</option> +<option value="em.radio.750-1500MHz">Radio between 750 and 1500 MHz</option> +<option value="em.radio.400-750MHz">Radio between 400 and 750 MHz</option> +<option value="em.radio.200-400MHz">Radio between 200 and 400 MHz</option> +<option value="em.radio.100-200MHz">Radio between 100 and 200 MHz</option> +<option value="em.radio.20-100MHz">Radio between 20 and 100 MHz</option> + </select></td></tr> +<tr><td><a href=# onclick="alert('RA and Dec specify a center point of a rectangle that is aligned equatorially. They must be in decimal degrees 0<=RA<=360 -90<=Dec<=90, in the J2000 frame.');return false;"> +RA (decimal degrees)</a></td> <td><input type="text" name="ra" value="0.0"/></td></tr> + +<tr><td><a href=# onclick="alert('RA and Dec specify a center point of a rectangle that is aligned equatorially. They must be in decimal degrees 0<=RA<=360 -90<=Dec<=90, in the J2000 frame.');return false;"> +Dec (decimal degrees)</a></td> <td><input type="text" name="dec" value="0.0"/></td></tr> + +<tr><td><a href=# onclick="alert('RAWidth and DecWidth specify the size of a a rectangle that is aligned equatorially. Thus the edge of the box is distant from the center by half of the width.');return false;"> +RAwidth (decimal degrees)</a></td> <td><input type="text" name="raWidth" value="0.0"/></td></tr> + +<tr><td><a href=# onclick="alert('RAWidth and DecWidth specify the size of a a rectangle that is aligned equatorially. Thus the edge of the box is distant from the center by half of the width.');return false;"> +Decwidth (decimal degrees)</a></td> <td><input type="text" name="decWidth" value="0.0"/></td></tr> + +<script language="javascript" type="text/javascript" src="https://losc.ligo.org/s/js/gpstimeutil.js"></script> +<script language="javascript" type="text/javascript" src="https://losc.ligo.org/s/js/sexg.js"></script> + + +<tr><td><a href=# onclick="alert('The time at the center of a time interval during which the observation was taken. Change either the GPS time or the UTC time and the other will change to match.');return false;"> +GPStime or UTC</a></td> +<td><input onKeyUp="return TIMEcopy(2,1);" id="TIMEgps1" name="gpstime" maxlength="12" size="20" value="1000000000"/></td> +<td><input onKeyUp="return TIMEcopy(1,1);" id="TIMEiso1" name="TIMEiso1" maxlength="25" size="25" value="2011-09-14T01:46:25"/></td> +<td><span id="TIMEerr1" STYLE="font: 12px Arial; color:red">OK</span><br/></td> +</tr> + + +<tr><td><a href=# onclick="alert('Duration is the number of seconds in the time interval during which the observation was taken.');return false;"> +Duration (seconds)</a></td> <td><input type="text" name="duration" value="10"/></td></tr> + +<tr><td><a href=# onclick="alert('This section allows for machine-readable data to be included in a flexible way. any key can be any value in a JSON dictionary. There is a useful validator at http://jsonlint.com.');return false;"> +JSON data</a></td> <td colspan=2><textarea name="extra_info_dict" cols="50">{"phot.mag.limit": 22.3}</textarea></td></tr> +</table> +<input type="submit" value="Submit"/> +</form> + + + </div> -{% include "gracedb/neighbors_frag.html" %} + <!-- Bulletin Board Log --> + <div data-dojo-type="dijit/TitlePane" + {% if object.embbeventlog_set.count > 0 %} + data-dojo-props="title: 'Bulletin Board Log', open: true"> + {% else %} + data-dojo-props="title: 'Bulletin Board Log', open: false"> + {% endif %} + <table id="bb_2" class="event" border="1"> + <thead> + <tr> + <th>Date</th> + <th>Submitter</th> + <th>MOU Group</th> + <th>Instrument</th> + <th>Footprint ID</th> + <th>Waveband</th> + <th>RA</th> + <th>DEC</th> + <th>RAwidth</th> + <th>Decwidth</th> + <th>GPS Time</th> + <th>Duration</th> + <th>EEL Type</th> + <th>Obs status</th> + <th>Comment</th> + <th>JSON Data</th> + </tr> + </thead> + + <tbody id="bb_log" > + {% for eel in object.embbeventlog_set.iterator %} +<tr> +<td>{{ eel.created }}</td> +<td>{{ eel.submitter }}</td> +<td>{{ eel.group }}</td> +<td>{{ eel.instrument }}</td> +<td>{{ eel.footprintID }}</td> +<td>{{ eel.waveband }}</td> +<td>{{ eel.ra }}</td> +<td>{{ eel.dec }}</td> +<td>{{ eel.raWidth }}</td> +<td>{{ eel.decWidth }}</td> +<td>{{ eel.gpstime }}</td> +<td>{{ eel.duration }}</td> +<td>{{ eel.eel_status }}</td> +<td>{{ eel.obs_status }}</td> +<td>{{ eel.comment }}</td> +<td>{{ eel.extra_info_dict }}</td> +</tr> + {% endfor %} + </tbody> + + </table> + </div> +</div> + + +<p> </p> <div id="annotations" class="content-area"> <div id="new_log_entry_form"> <noscript> <h3>Create a new log entry</h3> <form id="newlog" action="{% url "logentry" object.graceid "" %}" method="POST"> - <textarea id="newlogtext" name="comment" style="width:300px;display:block"></textarea> - <input type="submit" value="Submit"/> + <p> + <textarea id="newlogtext" name="comment" style="width:300px;display:block"></textarea> + <input type="submit" value="Submit"/> + </p> </form> </noscript> - </div> + </div> <h3 id="logmessagetitle">Event Log Messages</h3> <div id="previewer"></div> @@ -666,3 +923,5 @@ </div> {% endblock %} + +<!-- Single Inspiral Data --> diff --git a/wsgi/roy.wsgi b/wsgi/roy.wsgi new file mode 100644 index 0000000000000000000000000000000000000000..bfd45820e40be8b24bc1734642173d6128093762 --- /dev/null +++ b/wsgi/roy.wsgi @@ -0,0 +1,27 @@ +import os +import sys + +os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' +os.environ['MPLCONFIGDIR']='/tmp/' + +# Sandbox libs here, if required. +# +sys.path.append('/home/jkanner/djangoenv/lib/python2.7/site-packages') +sys.path.append('/home/jkanner/djangoenv/lib/python2.7') +sys.path.append('/home/roywilliams/gracedbdev') +sys.path.append('/home/roywilliams') + +# Scott's Shib app uses loggers. +import logging +logging.basicConfig() + +os.environ['MPLCONFIGDIR']='/tmp/' + +#logging.basicConfig(level=logging.DEBUG, +# format='%(asctime)s %(levelname)s %(message)s', +# filename='/tmp/myapp.log', +# filemode='w') + +import django.core.handlers.wsgi +application = django.core.handlers.wsgi.WSGIHandler() +