Skip to content

Commit

Permalink
Merge 67558b2 into b71dac9
Browse files Browse the repository at this point in the history
  • Loading branch information
jnation3406 committed Jun 12, 2019
2 parents b71dac9 + 67558b2 commit 510ffb5
Show file tree
Hide file tree
Showing 24 changed files with 76 additions and 88 deletions.
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ RUN apt-get update \
&& apt-get install -y curl \
&& curl -sL https://deb.nodesource.com/setup_10.x | bash - \
&& apt-get update \
&& apt-get install -y nodejs
&& apt-get install -y nodejs \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

COPY package.json package-lock.json vue.config.js /
COPY static /static
Expand Down
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@
[![Coverage Status](https://coveralls.io/repos/github/LCOGT/observation-portal/badge.svg?branch=master)](https://coveralls.io/github/LCOGT/observation-portal?branch=master)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/510995ede421411f8a08d0cdb588cc75)](https://www.codacy.com/app/LCOGT/observation-portal?utm_source=github.com&utm_medium=referral&utm_content=LCOGT/observation-portal&utm_campaign=Badge_Grade)

_An Astronomical Observation Web Portal and Backend_
## An Astronomical Observation Web Portal and Backend

The portal manages observation requests and observations in the context of a larger system. Other parts of this system include:
- an information store containing the configuration of resources in the system
- an information store containing resource availability information
- an information store containing periods of maintenance or other downtimes for resources in the system
- a scheduler responsible for scheduling observations on resources and a mechanism by which to report back observation states
- an information store containing the configuration of resources in the system
- an information store containing resource availability information
- an information store containing periods of maintenance or other downtimes for resources in the system
- a scheduler responsible for scheduling observations on resources and a mechanism by which to report back observation states

## Prerequisites
The portal can be run as a standalone application with reduced functionality. The basic requirements are:

* Python >= 3.6
* PostgreSQL 11
- Python >= 3.6
- PostgreSQL 11

## Set up a virtual environment
From the base of this project:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from observation_portal.proposals.models import Proposal, Semester, Membership, ScienceCollaborationAllocation

import logging
import sys

logger = logging.getLogger()

Expand All @@ -33,7 +32,7 @@ def handle(self, *args, **options):
end=datetime(2100, 1, 1, tzinfo=timezone.utc))
try:
user = User.objects.create_superuser(user_str, 'fake_email@lco.global', 'password')
except IntegrityError as ie:
except IntegrityError:
user = User.objects.get(username=user_str)
logging.warning(f"user {user_str} already exists")
Profile.objects.get_or_create(user=user)
Expand All @@ -43,7 +42,7 @@ def handle(self, *args, **options):
public=False, non_science=True, direct_submission=True,
sca=sca)
Membership.objects.create(proposal=proposal, user=user, role=Membership.PI)
except IntegrityError as ie:
except IntegrityError:
logging.warning(f'proposal {proposal_str} already exists')

# Need to set the api token to some expected value
Expand Down
1 change: 0 additions & 1 deletion observation_portal/blocks/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import logging

from observation_portal.observations.models import Observation
from observation_portal.requestgroups.models import RequestGroup
from observation_portal.blocks.filters import PondBlockFilter
from observation_portal.blocks.conversion import (convert_pond_blocks_to_observations,
convert_observations_to_pond_blocks)
Expand Down
1 change: 0 additions & 1 deletion observation_portal/common/test_data/configdb.json
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,6 @@
"horizon": 15.0,
"ha_limit_pos": 4.6,
"ha_limit_neg": -4.6,
"ha_limit_neg": -4.6,
"slew_rate": 0.0,
"minimum_slew_overhead": 2.0,
"maximum_slew_overhead": 2.0,
Expand Down
2 changes: 1 addition & 1 deletion observation_portal/common/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def create_simple_many_requestgroup(user, proposal, n_requests, state='PENDING')
operator = 'SINGLE' if n_requests == 1 else 'MANY'
rg = mixer.blend(RequestGroup, state=state, submitter=user, proposal=proposal, operator=operator,
observation_type=RequestGroup.NORMAL)
for i in range(n_requests):
for _ in range(n_requests):
request = mixer.blend(Request, request_group=rg, state=state)
mixer.blend(Window, request=request)
mixer.blend(Location, request=request)
Expand Down
4 changes: 2 additions & 2 deletions observation_portal/common/test_telescope_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,14 +364,14 @@ def test_states_no_enclosure_interlock(self):
def test_states_end_time_after_start(self):
telescope_states = TelescopeStates(self.start, self.end).get()

for tk, events in telescope_states.items():
for _, events in telescope_states.items():
for event in events:
self.assertTrue(event['start'] <= event['end'])

def test_states_no_duplicate_consecutive_states(self):
telescope_states = TelescopeStates(self.start, self.end).get()

for tk, events in telescope_states.items():
for _, events in telescope_states.items():
previous_event = None
for event in events:
if previous_event:
Expand Down
4 changes: 2 additions & 2 deletions observation_portal/observations/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ class Observation(models.Model):
help_text='Current State of this Observation'
)

@classmethod
def cancel(self, observations):
@staticmethod
def cancel(observations):
now = timezone.now()

_, deleted_observations = observations.filter(start__gte=now + timedelta(hours=72)).delete()
Expand Down
17 changes: 9 additions & 8 deletions observation_portal/observations/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from dateutil.parser import parse
from django.urls import reverse
from django.core import cache
from dateutil.parser import parse as datetime_parser
from datetime import timedelta
from io import StringIO
from django.core.management import call_command
Expand Down Expand Up @@ -351,7 +350,8 @@ def setUp(self):
configuration.instrument_type = '1M0-SCICAM-SBIG'
configuration.save()

def _generate_observation_data(self, request_id, configuration_id_list, guide_camera_name='xx03'):
@staticmethod
def _generate_observation_data(request_id, configuration_id_list, guide_camera_name='xx03'):
observation = {
"request": request_id,
"site": "tst",
Expand Down Expand Up @@ -916,7 +916,7 @@ def test_last_schedule_date_is_7_days_out_if_no_cached_value(self):

response = self.client.get(reverse('api:last_scheduled'))
last_schedule = response.json()['last_schedule_time']
self.assertAlmostEqual(datetime_parser(last_schedule), timezone.now() - timedelta(days=7),
self.assertAlmostEqual(parse(last_schedule), timezone.now() - timedelta(days=7),
delta=timedelta(minutes=1))

def test_last_schedule_date_is_updated_when_single_observation_is_submitted(self):
Expand All @@ -929,12 +929,12 @@ def test_last_schedule_date_is_updated_when_single_observation_is_submitted(self

response = self.client.get(reverse('api:last_scheduled') + "?site=tst")
last_schedule = response.json()['last_schedule_time']
self.assertAlmostEqual(datetime_parser(last_schedule), timezone.now(), delta=timedelta(minutes=1))
self.assertAlmostEqual(parse(last_schedule), timezone.now(), delta=timedelta(minutes=1))

# Verify that the last scheduled time for a different site isn't updated
response = self.client.get(reverse('api:last_scheduled') + "?site=non")
last_schedule = response.json()['last_schedule_time']
self.assertAlmostEqual(datetime_parser(last_schedule), timezone.now() - timedelta(days=7),
self.assertAlmostEqual(parse(last_schedule), timezone.now() - timedelta(days=7),
delta=timedelta(minutes=1))

def test_last_schedule_date_is_updated_when_multiple_observations_are_submitted(self):
Expand All @@ -948,15 +948,15 @@ def test_last_schedule_date_is_updated_when_multiple_observations_are_submitted(

response = self.client.get(reverse('api:last_scheduled'))
last_schedule = response.json()['last_schedule_time']
self.assertAlmostEqual(datetime_parser(last_schedule), timezone.now(), delta=timedelta(minutes=1))
self.assertAlmostEqual(parse(last_schedule), timezone.now(), delta=timedelta(minutes=1))

def test_last_schedule_date_is_not_updated_when_observation_is_mixed(self):
mixer.blend(Observation, request=self.requestgroup.requests.first())
last_schedule_cached = self.locmem_cache.get('observation_portal_last_schedule_time_tst')
self.assertIsNone(last_schedule_cached)
response = self.client.get(reverse('api:last_scheduled'))
last_schedule = response.json()['last_schedule_time']
self.assertAlmostEqual(datetime_parser(last_schedule), timezone.now() - timedelta(days=7),
self.assertAlmostEqual(parse(last_schedule), timezone.now() - timedelta(days=7),
delta=timedelta(minutes=1))


Expand All @@ -967,7 +967,8 @@ def setUp(self):
proposal=self.proposal, std_allocation=100, rr_allocation=100,
tc_allocation=100, ipp_time_available=100)

def _create_observation_and_config_status(self, requestgroup, start, end, config_state='PENDING'):
@staticmethod
def _create_observation_and_config_status(requestgroup, start, end, config_state='PENDING'):
observation = mixer.blend(Observation, request=requestgroup.requests.first(),
site='tst', enclosure='domb', telescope='1m0a',
start=start, end=end, state='PENDING')
Expand Down
10 changes: 5 additions & 5 deletions observation_portal/observations/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ def get_queryset(self):
@action(detail=False, methods=['post'])
def cancel(self, request):
"""
Filters a set of observations based on the parameters provided, and then either deletes them if they are
scheduled >72 hours in the future, cancels them if they are in the future, or aborts them if they are currently
in progress.
:param request:
:return:
Filters a set of observations based on the parameters provided, and then either deletes them if they are
scheduled >72 hours in the future, cancels them if they are in the future, or aborts them if they are currently
in progress.
:param request:
:return:
"""
cancel_serializer = CancelObservationsSerializer(data=request.data)
if cancel_serializer.is_valid():
Expand Down
4 changes: 1 addition & 3 deletions observation_portal/proposals/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@
from django.contrib.auth.models import User
from django.db.utils import IntegrityError
from django.urls import reverse
from django.conf import settings
from django.utils import timezone
from mixer.backend.django import mixer
from unittest.mock import patch
from requests import HTTPError
import datetime
from django_dramatiq.test import DramatiqTestCase

from observation_portal.proposals.models import ProposalInvite, Proposal, Membership, ProposalNotification, TimeAllocation, Semester
from observation_portal.requestgroups.models import RequestGroup, Configuration, InstrumentConfig, AcquisitionConfig, GuidingConfig, Target
from observation_portal.requestgroups.models import RequestGroup, Configuration, InstrumentConfig
from observation_portal.accounts.models import Profile
from observation_portal.common.test_helpers import create_simple_requestgroup
from observation_portal.requestgroups.signals import handlers # DO NOT DELETE, needed to active signals
Expand Down
3 changes: 2 additions & 1 deletion observation_portal/requestgroups/contention.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ def _binned_durations_by_proposal_and_ra(self):
ra_bins[ra][proposal_id] += conf.duration
return ra_bins

def _anonymize(self, data):
@staticmethod
def _anonymize(data):
for index, ra in enumerate(data):
data[index] = {'All Proposals': sum(ra.values())}
return data
Expand Down
1 change: 0 additions & 1 deletion observation_portal/requestgroups/duration_utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import itertools
from django.utils.translation import ugettext as _
from math import ceil, floor
from django.utils import timezone
Expand Down
2 changes: 1 addition & 1 deletion observation_portal/requestgroups/filters.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import django_filters
from observation_portal.requestgroups.models import RequestGroup, Request, Location
from observation_portal.requestgroups.models import RequestGroup, Request


class RequestGroupFilter(django_filters.FilterSet):
Expand Down
2 changes: 1 addition & 1 deletion observation_portal/requestgroups/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ def validate(self, data):
guiding_config['optional'] = False

if data['type'] in ['LAMP_FLAT', 'ARC', 'AUTO_FOCUS', 'NRES_BIAS', 'NRES_DARK', 'BIAS', 'DARK', 'SCRIPT']:
# These types of observations should only ever be set to guiding mode OFF, but the acquisition modes for
# These types of observations should only ever be set to guiding mode OFF, but the acquisition modes for
# spectrographs won't necessarily have that mode. Force OFF here.
data['acquisition_config']['mode'] = AcquisitionConfig.OFF
else:
Expand Down
2 changes: 1 addition & 1 deletion observation_portal/requestgroups/test/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,5 +400,5 @@ def test_get_duration_from_non_existent_camera(self):
self.instrument_config_expose.save()

with self.assertRaises(ConfigDBException) as context:
duration = self.configuration_expose.duration
_ = self.configuration_expose.duration
self.assertTrue('not found in configdb' in context.exception)
5 changes: 1 addition & 4 deletions observation_portal/requestgroups/test/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,12 @@
from django.contrib.auth.models import User
from django.core import cache
from dateutil.parser import parse as datetime_parser
from datetime import timedelta
from rest_framework.test import APITestCase
from mixer.backend.django import mixer
from mixer.main import mixer as basic_mixer
from django.utils import timezone
from datetime import datetime, timedelta
import copy
import random
from urllib import parse
from unittest import skip
from unittest.mock import patch

Expand Down Expand Up @@ -2572,7 +2569,7 @@ def test_last_change_date_is_updated_when_request_is_submitted(self):
self.assertAlmostEqual(datetime_parser(last_change), timezone.now(), delta=timedelta(minutes=1))

def test_last_change_date_is_not_updated_when_request_is_mixed(self):
requestgroup = create_simple_requestgroup(
create_simple_requestgroup(
user=self.user, proposal=self.proposal, instrument_type='1M0-SCICAM-SBIG', window=self.window
)
last_change_cached = self.locmem_cache.get('observation_portal_last_change_time')
Expand Down
1 change: 0 additions & 1 deletion observation_portal/sciapplications/test_admin.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from django.test import TestCase
from django.core import mail
from django.urls import reverse
from django.contrib.auth.models import User
Expand Down
5 changes: 1 addition & 4 deletions observation_portal/userrequests/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,8 @@
from observation_portal.userrequests.filters import UserRequestFilter
from observation_portal.userrequests.conversion import (validate_userrequest, convert_userrequests_to_requestgroups,
convert_requestgroups_to_userrequests, expand_cadence,
convert_userrequest_to_requestgroup,
convert_requestgroup_to_userrequest)
from observation_portal.requestgroups.cadence import expand_cadence_request
convert_userrequest_to_requestgroup)
from observation_portal.requestgroups.serializers import RequestGroupSerializer
from observation_portal.requestgroups.serializers import CadenceRequestSerializer
from observation_portal.requestgroups.duration_utils import (
get_max_ipp_for_requestgroup
)
Expand Down
2 changes: 1 addition & 1 deletion static/js/components/instrumentconfig.vue
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ export default {
}
}
},
rotatorModeOptions: function(newValue, oldValue) {
rotatorModeOptions: function(newValue) {
if (this.instrumentHasRotatorModes) {
this.instrumentconfig.rotator_mode = this.available_instruments[this.selectedinstrument].modes.rotator.default;
} else {
Expand Down
32 changes: 16 additions & 16 deletions static/js/components/pressure.vue
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,21 @@
}
return maxPressure;
},
dataAvailable: function() {
for (let bin in this.rawData) {
for (let proposal in this.rawData[bin]) {
if (this.rawData[bin][proposal] > 0) {
return true;
}
}
}
return false;
}
},
created: function() {
this.fetchData();
},
methods: {
toSiteNightData: function() {
let nights = [];
let siteSpacing = 0.6;
Expand All @@ -160,21 +175,6 @@
this.maxY = Math.ceil(height);
return nights;
},
dataAvailable: function() {
for (let bin in this.rawData) {
for (let proposal in this.rawData[bin]) {
if (this.rawData[bin][proposal] > 0) {
return true;
}
}
}
return false;
}
},
created: function() {
this.fetchData();
},
methods: {
fetchData: function() {
this.isLoading = true;
this.rawData = [];
Expand All @@ -187,7 +187,7 @@
that.rawData = data.pressure_data;
that.rawSiteData = data.site_nights;
that.maxY = that.maxPressureInGraph;
that.siteNights = that.toSiteNightData;
that.siteNights = that.toSiteNightData();
that.data.datasets = that.toChartData;
}).done(function() {
that.loadingDataFailed = false;
Expand Down
1 change: 0 additions & 1 deletion static/js/request_row.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import $ from 'jquery';
import moment from 'moment';
import {datetimeFormat} from './utils.js';
import tooltip from 'bootstrap';

import { getThumbnail, getLatestFrame, downloadAll } from './archive.js';

Expand Down

0 comments on commit 510ffb5

Please sign in to comment.