Skip to content

Commit

Permalink
Merge pull request #591 from LCOGT/feature/data_summary
Browse files Browse the repository at this point in the history
Feature/data summary
  • Loading branch information
jchate6 authored Jan 4, 2022
2 parents f37f0f9 + f8a92e7 commit 67d14ff
Show file tree
Hide file tree
Showing 21 changed files with 802 additions and 226 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ Portal for scheduling observations of NEOs (and other Solar System objects) usin

## History

### 3.11.0
* Update Summary pages for enhanced usability and utility
* Add body publication status
* Add form for updating period from LC plot page

### 3.10.3
* Minor bug fixes and error handling.

Expand Down
26 changes: 25 additions & 1 deletion neoexchange/core/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from astrometrics.sources_subs import fetch_sfu, fetch_filter_list
from .models import Body, Proposal, Block, StaticSource, ORIGINS
from .models import Body, Proposal, Block, StaticSource, ORIGINS, STATUS_CHOICES, PHYSICAL_PARAMETER_QUALITIES
from astrometrics.time_subs import tomorrow

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -69,6 +69,8 @@
('4', '4'),
('5', '5'))

LC_QUALITIES = tuple((qual, PHYSICAL_PARAMETER_QUALITIES['P'][qual]) for qual in PHYSICAL_PARAMETER_QUALITIES['P'])


class SiteSelectWidget(forms.Select):
"""
Expand Down Expand Up @@ -441,3 +443,25 @@ class AddTargetForm(forms.Form):
origin = forms.ChoiceField(choices=ORIGINS, widget=forms.HiddenInput())
target_name = forms.CharField(label="Enter target to add...", max_length=30, required=True, widget=forms.TextInput(attrs={'size': '20'}),
error_messages={'required': _(u'Target name is required')})


class AddPeriodForm(forms.Form):
period = forms.FloatField(label="Period", initial=None, required=True, widget=forms.DateTimeInput(attrs={'style': 'width: 75px;'}))
error = forms.FloatField(label="Error", initial=0.0, required=False, widget=forms.DateTimeInput(attrs={'style': 'width: 75px;'}))
quality = forms.ChoiceField(required=False, choices=LC_QUALITIES)
notes = forms.CharField(label="Notes", required=False, widget=forms.DateTimeInput(attrs={'style': 'width: 275px;'}))
preferred = forms.BooleanField(initial=False, required=False)

def clean(self):
cleaned_data = super(AddPeriodForm, self).clean()
period = self.cleaned_data.get('period', None)
error = self.cleaned_data.get('error', None)
if period and period <= 0:
raise forms.ValidationError("Please enter a positive number for Period.")
if error and error < 0:
raise forms.ValidationError("Please enter a positive number, zero, or leave Error blank.")


class UpdateAnalysisStatusForm(forms.Form):
update_body = forms.ChoiceField(required=False, choices=[])
status = forms.ChoiceField(required=False, choices=STATUS_CHOICES)
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Generated by Django 3.1.13 on 2021-12-22 19:51

import datetime
from django.db import migrations, models


class Migration(migrations.Migration):

replaces = [('core', '0061_auto_20211208_2330'), ('core', '0062_auto_20211214_2332'), ('core', '0063_auto_20211214_2345'), ('core', '0064_auto_20211218_0114')]

dependencies = [
('core', '0060_auto_20210820_1933'),
]

operations = [
migrations.AddField(
model_name='body',
name='as_updated',
field=models.DateTimeField(blank=True, db_index=True, null=True),
),
migrations.AlterField(
model_name='dataproduct',
name='created',
field=models.DateTimeField(default=datetime.datetime.utcnow, verbose_name='Datetime of the products creation or most recent update.'),
),
migrations.AlterField(
model_name='dataproduct',
name='filetype',
field=models.PositiveSmallIntegerField(choices=[(0, 'JPEG'), (1, 'Spectroscopy Guider GIF'), (22, 'Thumbnail Frame GIF'), (2, 'FITS Image'), (3, 'FITS Spectra'), (4, 'ALCDEF Lightcurve file'), (23, 'Periodogram output file'), (24, 'Model Lightcurve output file'), (25, 'Parameter file used to create DAMIT lightcurve models'), (26, 'Shape Model output file'), (5, 'MP4'), (6, 'CSV'), (7, 'PNG astrometric ref stars'), (8, 'PNG photometric ref stars'), (9, 'Block light curve PNG (phased)'), (10, 'Block light curve PNG (unphased)'), (11, 'Combined light curve PNG (phased)'), (12, 'Combined light curve PNG (unphased)'), (13, 'Period finder periodogram PNG'), (14, 'Period finder data window PNG'), (15, 'FWHM condition PNG'), (16, 'Zero point PNG'), (20, 'Planetary Data System (PDS) XML'), (99, 'Other')], verbose_name='Type of file to be stored.'),
),
migrations.AlterField(
model_name='dataproduct',
name='product',
field=models.FileField(blank=True, upload_to='products/', verbose_name='Filefield for actual data product.'),
),
migrations.AlterField(
model_name='dataproduct',
name='update',
field=models.BooleanField(default=True, verbose_name='Flag for allowing automated updates. Set to False for robust storage.'),
),
migrations.AddField(
model_name='body',
name='analysis_status',
field=models.IntegerField(choices=[(0, 'No Analysis Done'), (1, 'Light Curve Analysis Done'), (2, 'Spectroscopic Analysis Done'), (3, 'LC & Spec Analysis Done'), (10, 'More Observations Needed'), (20, 'Published'), (99, 'No usable Data')], db_index=True, default=0, verbose_name='Current Analysis Status'),
),
]
18 changes: 18 additions & 0 deletions neoexchange/core/migrations/0062_auto_20220102_1930.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.1.13 on 2022-01-02 19:30

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('core', '0061_auto_20211208_2330_squashed_0064_auto_20211218_0114'),
]

operations = [
migrations.AlterField(
model_name='physicalparameters',
name='quality',
field=models.IntegerField(blank=True, null=True, verbose_name='Physical Parameter Quality Designation'),
),
]
4 changes: 4 additions & 0 deletions neoexchange/core/models/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from django.db.models import Sum
from django.utils.translation import gettext_lazy as _
from django.utils.functional import cached_property
from django.contrib.contenttypes.fields import GenericRelation
from requests.compat import urljoin
from numpy import frombuffer

Expand All @@ -27,6 +28,7 @@
from core.models.body import Body
from core.models.frame import Frame
from core.models.proposal import Proposal
from core.models.dataproducts import DataProduct

TELESCOPE_CHOICES = (
('1m0', '1-meter'),
Expand Down Expand Up @@ -62,6 +64,7 @@ class SuperBlock(models.Model):
jitter = models.FloatField('Acceptable deviation before or after strict period (hours)', null=True, blank=True)
timeused = models.FloatField('Time used (seconds)', null=True, blank=True)
active = models.BooleanField(default=False)
dataproduct = GenericRelation(DataProduct, related_query_name='sblock')

@cached_property
def get_blocks(self):
Expand Down Expand Up @@ -197,6 +200,7 @@ class Block(models.Model):
active = models.BooleanField(default=False)
reported = models.BooleanField(default=False)
when_reported = models.DateTimeField(null=True, blank=True)
dataproduct = GenericRelation(DataProduct, related_query_name='block')

def current_name(self):
name = ''
Expand Down
48 changes: 46 additions & 2 deletions neoexchange/core/models/body.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"""
from datetime import datetime, timedelta
from math import degrees
import re
import logging

from astropy.time import Time
Expand Down Expand Up @@ -114,6 +115,28 @@
('/a', 'Reciprocal of semimajor axis')
)

STATUS_CHOICES = (
(0, 'No Analysis Done'),
(1, 'Light Curve Analysis Done'),
(2, 'Spectroscopic Analysis Done'),
(3, 'LC & Spec Analysis Done'),
(10, 'More Observations Needed'),
(20, 'Published'),
(99, 'No usable Data')
)

PHYSICAL_PARAMETER_QUALITIES = {"P": {-99: 'Debunked (0)',
0: 'Probably Completely Wrong (1-)',
2: 'Possibly Completely Wrong (1)',
3: 'Possibly Just Noise (1+)',
5: 'Not well established (2-)',
6: 'Within 30% accurate (2)',
7: 'Possible Ambiguity (2+)',
9: 'Unique (3-)',
10: 'Unambiguous (3)',
}
}

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -149,6 +172,8 @@ class Body(models.Model):
updated = models.BooleanField('Has this object been updated?', default=False)
ingest = models.DateTimeField(default=datetime.utcnow, db_index=True)
update_time = models.DateTimeField(blank=True, null=True, db_index=True)
analysis_status = models.IntegerField('Current Analysis Status', choices=STATUS_CHOICES, db_index=True, default=0)
as_updated = models.DateTimeField(blank=True, null=True, db_index=True)

def _compute_period(self):
period = None
Expand Down Expand Up @@ -177,7 +202,7 @@ def _compute_one_over_a(self):

period = property(_compute_period)
recip_a = property(_compute_one_over_a)
one_over_a = property(_compute_one_over_a)
one_over_a = property(_compute_one_over_a)

def characterization_target(self):
# If we change the definition of Characterization Target,
Expand Down Expand Up @@ -590,12 +615,31 @@ class PhysicalParameters(models.Model):
value2 = models.FloatField('2nd component of Physical Parameter', blank=True, null=True)
error2 = models.FloatField('Error for 2nd component of Physical Parameter', blank=True, null=True)
units = models.CharField('Physical Parameter Units', blank=True, null=True, max_length=30)
quality = models.CharField('Physical Parameter Quality Designation', blank=True, null=True, max_length=10)
quality = models.IntegerField('Physical Parameter Quality Designation', blank=True, null=True)
preferred = models.BooleanField('Is this the preferred value for this type of parameter?', default=False)
reference = models.TextField('Reference for this value', blank=True, null=True)
notes = models.TextField('Notes on this value', blank=True, null=True)
update_time = models.DateTimeField(blank=True, null=True, db_index=True)

def quality_parser(self):
"""Retrieve quality description for given Parameter type
Return Quality description or quality int.
"""
quality_dict = PHYSICAL_PARAMETER_QUALITIES.get(self.parameter_type, {})
quality_text_or_value = quality_dict.get(self.quality, self.quality)
return quality_text_or_value

def quality_short(self):
"""If it exists, return shortened parenthetical quality tag.
Else, return quality int. as string.
"""
quality_string = self.quality_parser()
if not isinstance(quality_string, str):
short_string = re.search(r'\(.*?\)', quality_string).group()
if short_string:
return short_string
return f"({quality_string})"

class Meta:
verbose_name = _('Physical Parameter')
verbose_name_plural = _('Physical Parameters')
Expand Down
9 changes: 2 additions & 7 deletions neoexchange/core/models/dataproducts.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@
from django.dispatch import receiver
from django.core.files.storage import default_storage

from core.models.body import Body
from core.models.blocks import Block, SuperBlock


class CoreQuerySet(models.QuerySet):
def block(self):
Expand All @@ -45,11 +42,9 @@ def fullbody(self, *args, **kwargs):
else:
block = ContentType.objects.get(app_label='core', model='block')
sblock = ContentType.objects.get(app_label='core', model='superblock')
blockslist = Block.objects.filter(body=bodyid).values_list('id', flat=True)
sblockslist = SuperBlock.objects.filter(body=bodyid).values_list('id', flat=True)
query1 = Q(content_type=block, object_id__in=blockslist)
query1 = Q(content_type=block, block__body__pk=bodyid)
query2 = Q(content_type=body, object_id=bodyid)
query3 = Q(content_type=sblock, object_id__in=sblockslist)
query3 = Q(content_type=sblock, sblock__body__pk=bodyid)
return self.filter(query1 | query2 | query3)


Expand Down
1 change: 1 addition & 0 deletions neoexchange/core/models/proposal.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def __str__(self):
title = self.title[0:10]
return "%s %s" % (self.code, title)


class ProposalPermission(models.Model):
"""
Linking a user to proposals in NEOx to control their access
Expand Down
1 change: 1 addition & 0 deletions neoexchange/core/models/spectra.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
('O', 'Other')
)


class SpectralInfo(models.Model):
body = models.ForeignKey(Body, on_delete=models.CASCADE)
taxonomic_class = models.CharField('Taxonomic Class', blank=True, null=True, max_length=6)
Expand Down
27 changes: 27 additions & 0 deletions neoexchange/core/static/core/css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -1182,3 +1182,30 @@ ul.candidate-controls li:hover {background-color:#eee; }
background: white;
color: silver;
}

.collapsible {
background-color: #777;
color: white;
cursor: pointer;
border: none;
text-align: left;
outline: none;
box-shadow: 2px 2px 10px #777;
}

.active, .collapsible:hover {
background-color: #555;
color: white;
}

.content {
margin-bottom: 0px;
margin-top: 2px;
box-shadow: 2px 2px 10px #777;
}

.long-row {
display: block;
white-space: nowrap;
overflow-x: auto;
}
2 changes: 1 addition & 1 deletion neoexchange/core/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@
<a href="{% url 'blocklist' %}" title="Blocks scheduled for NEO follow-up" class="navitem">Blocks</a>
</li>
<li>
<a href="{% url 'block-summary' %}" title="Block Summary for NEO follow-up" class="navitem">Efficiency</a>
<a href="{% url 'lc_data_summary' %}" title="Data Summary Tables" class="navitem">Data</a>
</li>

</ul>
Expand Down
2 changes: 1 addition & 1 deletion neoexchange/core/templates/core/block_list.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ <h1>Observing Blocks</h1>
<span class="sortable" id="obs_details">Obs. Details</span>
</th>
<th>
<span class="sortable" id="block_active">Cadence?</span>
<span class="sortable" id="block_cadence">Cadence?</span>
</th>
<th>
<span class="sortable" id="block_active">Active?</span>
Expand Down
Loading

0 comments on commit 67d14ff

Please sign in to comment.