Skip to content
Snippets Groups Projects
Commit b80fa92f authored by Jorrit Schaap's avatar Jorrit Schaap
Browse files

Merge branch 'TMSS-2831' into 'master'

Resolve TMSS-2831

Closes TMSS-2831

See merge request !1210
parents 2c40e22c 4f82e71f
No related branches found
No related tags found
1 merge request!1210Resolve TMSS-2831
...@@ -335,125 +335,6 @@ class SubTasksCreationFromTaskBluePrint(unittest.TestCase): ...@@ -335,125 +335,6 @@ class SubTasksCreationFromTaskBluePrint(unittest.TestCase):
self.assertEqual(obs_subtask.output_dataproducts.first(), ingest_subtask.get_transformed_input_dataproduct(ingest_subtask.output_dataproducts.first())) self.assertEqual(obs_subtask.output_dataproducts.first(), ingest_subtask.get_transformed_input_dataproduct(ingest_subtask.output_dataproducts.first()))
class SubTasksCreationFromTaskBluePrintCalibrator(unittest.TestCase):
def test_create_sequence_of_subtask_from_task_blueprint_calibrator_failure(self):
"""
Create multiple subtasks from a task blueprint when task is a calibrator
Check that exception should occur due too missing related target observation
"""
task_blueprint = create_task_blueprint_object_for_testing(task_template_name="calibrator observation")
with self.assertRaises(SubtaskCreationException):
create_observation_control_subtask_from_task_blueprint(task_blueprint)
@unittest.skip("JS 2020-09-08: Cannot reproduce SubtaskCreationException. How is this test supposed to work??")
def test_create_sequence_of_subtask_from_task_blueprint_calibrator(self):
"""
Create multiple subtasks from a task blueprint when task is a calibrator and is related to task blueprint
of a target observation
Check that exception should occur due too missing pointing setting in target observation,
the calibrator default is AutoSelect=True
Check NO exception, when AutoSelect=False
"""
cal_task_blueprint = create_task_blueprint_object_for_testing(task_template_name="calibrator observation")
target_task_blueprint = create_task_blueprint_object_for_testing()
create_scheduling_relation_task_blueprint_for_testing(cal_task_blueprint, target_task_blueprint)
with self.assertRaises(SubtaskCreationException):
create_observation_control_subtask_from_task_blueprint(cal_task_blueprint)
cal_task_blueprint.specifications_doc['autoselect'] = False
cal_task_blueprint.specifications_doc['pointing']['angle1'] = 1.111
cal_task_blueprint.specifications_doc['pointing']['angle2'] = 2.222
subtask = create_observation_control_subtask_from_task_blueprint(cal_task_blueprint)
self.assertEqual("defined", str(subtask.state))
self.assertEqual("observation control", str(subtask.specifications_template.name))
self.assertEqual("observation", str(subtask.specifications_template.type))
self.assertEqual('J2000', subtask.specifications_doc['stations']['analog_pointing']['direction_type'])
self.assertEqual(1.111, subtask.specifications_doc['stations']['analog_pointing']['angle1'])
self.assertEqual(2.222, subtask.specifications_doc['stations']['analog_pointing']['angle2'])
def test_create_combined_subtask_from_task_blueprints(self):
"""
Create subtasks from a target task blueprint and a separate calibrator task blueprint.
"""
cal_task_blueprint = create_task_blueprint_object_for_testing(task_template_name="calibrator observation")
target_task_blueprint = create_task_blueprint_object_for_testing()
create_scheduling_relation_task_blueprint_for_testing(cal_task_blueprint, target_task_blueprint, placement='parallel')
subtask_1 = create_observation_control_subtask_from_task_blueprint(target_task_blueprint)
num_pointings_target = len(subtask_1.specifications_doc['stations']['digital_pointings'])
# assert target subtask still in defining state
self.assertEqual("defining", str(subtask_1.state))
self.assertTrue(subtask_1.primary)
subtask_2 = create_observation_control_subtask_from_task_blueprint(cal_task_blueprint)
# assert the same subtask is returned
self.assertEqual(subtask_1, subtask_2)
# assert the calibrator obs was added as an additional beam
num_pointings_calibrator = len(subtask_2.specifications_doc['stations']['digital_pointings'])
self.assertEqual(num_pointings_target + 1, num_pointings_calibrator)
# assert the subtask is now in defined state
self.assertEqual("defined", str(subtask_2.state))
self.assertTrue(subtask_2.primary)
# assert the subtask references both tasks
self.assertEqual(subtask_1.task_blueprints.count(), 2)
self.assertIn(target_task_blueprint, subtask_1.task_blueprints.all())
self.assertIn(cal_task_blueprint, subtask_1.task_blueprints.all())
# assert we have subtask outputs for both tasks
self.assertEqual(subtask_1.outputs.count(), 2)
self.assertEqual(subtask_1.outputs.filter(task_blueprint=target_task_blueprint).count(), 1)
self.assertEqual(subtask_1.outputs.filter(task_blueprint=cal_task_blueprint).count(), 1)
def test_create_combined_subtask_from_task_blueprints_fails_if_calibrator_handled_before_target(self):
"""
Create subtasks from a target task blueprint and a separate calibrator task blueprint.
Handling calibrator before target task should raise Exception.
"""
cal_task_blueprint = create_task_blueprint_object_for_testing(task_template_name="calibrator observation")
target_task_blueprint = create_task_blueprint_object_for_testing()
create_scheduling_relation_task_blueprint_for_testing(cal_task_blueprint, target_task_blueprint, placement='parallel')
with self.assertRaises(SubtaskCreationException) as cm:
create_observation_control_subtask_from_task_blueprint(cal_task_blueprint)
create_observation_control_subtask_from_task_blueprint(target_task_blueprint)
self.assertIn("cannot be added to the target subtask, because it does not exist", str(cm.exception))
def test_create_combined_subtask_from_task_blueprints_fails_if_calibrator_does_not_fit(self):
"""
Create subtasks from a target task blueprint and a separate calibrator task blueprint.
And exception is raised when the combined number of subbands exceeds 488.
"""
cal_task_blueprint = create_task_blueprint_object_for_testing(task_template_name="calibrator observation")
target_task_blueprint = create_task_blueprint_object_for_testing()
create_scheduling_relation_task_blueprint_for_testing(cal_task_blueprint, target_task_blueprint, placement='parallel')
target_task_blueprint.specifications_doc['station_configuration']['SAPs'] = [{'name': 'target1', 'subbands': list(range(0, 150)),
'digital_pointing': {'angle1': 0.1, 'angle2': 0.1,
'direction_type': 'J2000',
'target': 'target1'}},
{'name': 'target2', 'subbands': list(range(150, 300)),
'digital_pointing': {'angle1': 0.2, 'angle2': 0.2,
'direction_type': 'J2000',
'target': 'target2'}}]
target_task_blueprint.save()
with self.assertRaises(SubtaskCreationException) as cm:
create_observation_control_subtask_from_task_blueprint(target_task_blueprint)
create_observation_control_subtask_from_task_blueprint(cal_task_blueprint)
self.assertIn("results in 600 total subbands, but only 488 are possible", str(cm.exception))
class SubTasksCreationFromTaskBluePrintCalibrator(unittest.TestCase): class SubTasksCreationFromTaskBluePrintCalibrator(unittest.TestCase):
def test_create_sequence_of_subtask_from_task_blueprint_calibrator_failure(self): def test_create_sequence_of_subtask_from_task_blueprint_calibrator_failure(self):
......
...@@ -49,10 +49,7 @@ import { ...@@ -49,10 +49,7 @@ import {
fetchUserPermissions, fetchUserPermissions,
} from "./data/week.view.data"; } from "./data/week.view.data";
import useWebSocket from 'react-use-websocket'; import useWeekViewWebSocket from "../../utils/websocket.js";
import _ from 'lodash';
import ScheduleService from "../../services/schedule.service";
import ReservationService from "../../services/reservation.service";
function getTimelineHeaders(headerSettings) { function getTimelineHeaders(headerSettings) {
return <TimelineHeaders className="sticky"> return <TimelineHeaders className="sticky">
...@@ -260,138 +257,12 @@ export default function WeekView() { ...@@ -260,138 +257,12 @@ export default function WeekView() {
setWeekString( newStart, newEnd); setWeekString( newStart, newEnd);
} }
// websocket handling
/**
* Function to call wnen websocket is connected
*/
function onConnect() {
try {
console.log("WS Opened");
const userDets = localStorage.getItem("user");
if (userDets) {
sendMessage(JSON.stringify({ "token": JSON.parse(userDets).websocket_token }));
console.log("Auth token submitted");
}
} catch (err) {
console.log('err', err)
}
}
/** // websocket handling
* Function to call when websocket is disconnected
*/
function onDisconnect() {
console.log("WS Closed")
}
/** // websocket hook that opens and allows interaction via the wss connection
* Handles the message received through websocket // todo: there is probably a better way to access the component state from the hook?
* @param {String} data - String of JSON data useWeekViewWebSocket(data, setData, summaryItem, setSummaryItem, setShowSummary);
*/
function handleData(event) {
const jsonData = JSON.parse(event?.data) || {};
switch (jsonData.object_type) {
case 'scheduling_unit_blueprint': {
switch (jsonData.action) {
case 'delete': {
const schedulingUnits = data.schedulingUnits
_.remove(schedulingUnits, function (su) { return su.id === jsonData.object_details.id });
setData(prevData => ({
...prevData,
schedulingUnits: schedulingUnits
}))
if (summaryItem?.id === jsonData.object_details.id) {
setShowSummary(false);
}
break;
}
case 'update': {
setData(prevData => ({
...prevData,
schedulingUnits: prevData.schedulingUnits.map(
unit => unit.id === jsonData.object_details.id ? { ...unit, ...jsonData.object_details } : unit
)
}));
if (summaryItem?.id === jsonData.object_details.id) {
// Note: we could also update details directly so we don't need to fetch again.
// However, we would have to send full deltas via websocket instead of the subset that is
// relevant for the timeline (like id, timestamps, and status).
// e.g.
//
// setSummarySettings(prevState => ({
// ...prevState,
// schedulingUnitItem: {...prevState.schedulingUnitItem, ...jsonData.object_details}
// }));
//
// Instead, trigger a full refresh of the details panel:
setSummaryItem({ id: jsonData.object_details.id, type: "SCHEDULE" });
}
break;
}
case 'create': {
// The websocket message only contains a subset of the details we need, so fetch the full set
ScheduleService.getTimelineSlimBlueprints(undefined, undefined, jsonData.object_details.id) // todo: check time
.then((response) => {
setData(prevData => ({
...prevData,
schedulingUnits: prevData.schedulingUnits.concat(response)
}));
});
break;
}
default: { break; }
}
break;
}
case 'reservation': {
switch (jsonData.action) {
case 'delete': {
const reservations = data.reservations
_.remove(reservations, function (res) { return res.id === jsonData.object_details.id });
setData(prevData => ({
...prevData,
reservations: reservations
}))
if (summaryItem?.id === jsonData.object_details.id) {
setShowSummary(false);
}
break;
}
case 'update': {
setData(prevData => ({
...prevData,
reservations: prevData.reservations.map(
res => res.id === jsonData.object_details.id ? { ...res, ...jsonData.object_details } : res
),
}));
if (summaryItem?.id === jsonData.object_details.id) {
// Trigger a full refresh of the details panel
setSummaryItem({ id: jsonData.object_details.id, type: "RESERVATION" });
}
break;
}
case 'create': {
const shouldFetchReservations = getStore(UIConstants.STORE_KEY_TIMELINE).reservationsToggle;
if (shouldFetchReservations) {
// The websocket message only contains a subset of the details we need, so fetch the full set
ReservationService.getTimelineReservations(undefined, undefined, jsonData.object_details.id) // todo: check time
.then((response) => {
setData(prevData => ({
...prevData,
reservations: prevData.reservations.concat(response)
}));
});
}
break;
}
default: { break; }
}
break;
}
default: { break; }
}
}
/** /**
...@@ -406,23 +277,6 @@ export default function WeekView() { ...@@ -406,23 +277,6 @@ export default function WeekView() {
setShowLegendbar(newState); setShowLegendbar(newState);
} }
// websocket hook that opens and allows interaction via the wss connection
const {
sendMessage,
sendJsonMessage,
lastMessage,
lastJsonMessage,
readyState,
getWebSocket,
} = useWebSocket(process.env.REACT_APP_WEBSOCKET_URL, {
onOpen: () => onConnect(),
onClose: () => onDisconnect(),
onMessage: (event) => handleData(event),
onError: (event) => { console.error(event); },
shouldReconnect: (closeEvent) => true,
});
return <div> return <div>
<div className={(isLoading ? "fix-element" : "hide-element")}> <div className={(isLoading ? "fix-element" : "hide-element")}>
<ProgressBar className={isLoading ? "" : "hide-element"} mode="indeterminate" <ProgressBar className={isLoading ? "" : "hide-element"} mode="indeterminate"
......
import _ from 'lodash';
import ScheduleService from "../services/schedule.service";
import ReservationService from "../services/reservation.service";
import useWebSocket from 'react-use-websocket';
import { getStore } from "../services/store.helper";
import UIConstants from "../utils/ui.constants";
function useWeekViewWebSocket(data, setData, summaryItem, setSummaryItem, setShowSummary) {
/**
* Function to call when websocket is connected
*/
function onConnect() {
try {
console.log("WS Opened");
const userDets = localStorage.getItem("user");
if (userDets) {
sendMessage(JSON.stringify({ "token": JSON.parse(userDets).websocket_token }));
console.log("Auth token submitted");
}
} catch (err) {
console.log('err', err);
}
}
/**
* Function to call when websocket is disconnected
*/
function onDisconnect() {
console.log("WS Closed");
}
function fetchBlueprintAndAddToTimeline(id) {
// The websocket message only contains a subset of the details we need, so fetch the full set
ScheduleService.getTimelineSlimBlueprints(undefined, undefined, id) // todo: check time
.then((response) => {
setData(prevData => ({
...prevData,
schedulingUnits: prevData.schedulingUnits.concat(response)
}));
})
.catch(e => console.error("Couldn't retrieve scheduling unit details for id: ", id, e));
}
function fetchReservationAndAddToTimeline(id) {
const shouldFetchReservations = getStore(UIConstants.STORE_KEY_TIMELINE).reservationsToggle;
if (shouldFetchReservations) {
ReservationService.getTimelineReservations(undefined, undefined, id) // todo: check time
.then((response) => {
setData(prevData => ({
...prevData,
reservations: prevData.reservations.concat(response)
}));
})
.catch(e => console.error("Couldn't retrieve reservation details for id: ", id, e));
}
}
/**
* Handles the message received through websocket
* @param {String} data - String of JSON data
*/
function handleData(event) {
const jsonData = JSON.parse(event?.data) || {};
switch (jsonData.object_type) {
case 'scheduling_unit_blueprint': {
switch (jsonData.action) {
case 'delete': {
const schedulingUnits = data.schedulingUnits
_.remove(schedulingUnits, function (su) { return su.id === jsonData.object_details.id });
setData(prevData => ({
...prevData,
schedulingUnits: schedulingUnits
}))
if (summaryItem?.id === jsonData.object_details.id) {
setShowSummary(false);
}
break;
}
case 'update': {
if (data.schedulingUnits.some(unit => unit.id === jsonData.object_details.id)) {
// usually we already have most details, so only update the relevant ones.
setData(prevData => ({
...prevData,
schedulingUnits: prevData.schedulingUnits.map(
unit => unit.id === jsonData.object_details.id ? { ...unit, ...jsonData.object_details } : unit
)
}));
} else {
// ...but sometimes we don't have the details yet, e.g. because it at least used to be
// outside the timelines time range, and we need to fetch all details so we don't miss
// anything that e.g. got moved into scope.
fetchBlueprintAndAddToTimeline(jsonData.object_details.id);
}
if (summaryItem?.id === jsonData.object_details.id) {
// Note: we could also update details directly so we don't need to fetch again.
// However, we would have to send full deltas via websocket instead of the subset that is
// relevant for the timeline (like id, timestamps, and status).
// e.g.
//
// setSummarySettings(prevState => ({
// ...prevState,
// schedulingUnitItem: {...prevState.schedulingUnitItem, ...jsonData.object_details}
// }));
//
// Instead, trigger a full refresh of the details panel:
setSummaryItem({ id: jsonData.object_details.id, type: "SCHEDULE" });
}
break;
}
case 'create': {
// The websocket message only contains a subset of the details we need, so fetch the full set
fetchBlueprintAndAddToTimeline(jsonData.object_details.id);
break;
}
default: { break; }
}
break;
}
case 'reservation': {
switch (jsonData.action) {
case 'delete': {
const reservations = data.reservations
_.remove(reservations, function (res) { return res.id === jsonData.object_details.id });
setData(prevData => ({
...prevData,
reservations: reservations
}))
if (summaryItem?.id === jsonData.object_details.id) {
setShowSummary(false);
}
break;
}
case 'update': {
if (data.reservations.some(res => res.id === jsonData.object_details.id)) {
// usually we already have most details, so only update the relevant ones.
setData(prevData => ({
...prevData,
reservations: prevData.reservations.map(
res => res.id === jsonData.object_details.id ? { ...res, ...jsonData.object_details } : res
),
}));
} else {
// ...but sometimes we don't have the details yet, e.g. because it at least used to be
// outside the timelines time range, and we need to fetch all details so we don't miss
// anything that e.g. got moved into scope.
fetchReservationAndAddToTimeline(jsonData.object_details.id);
}
if (summaryItem?.id === jsonData.object_details.id) {
// Trigger a full refresh of the details panel
setSummaryItem({ id: jsonData.object_details.id, type: "RESERVATION" });
}
break;
}
case 'create': {
// The websocket message only contains a subset of the details we need, so fetch the full set
fetchReservationAndAddToTimeline(jsonData.object_details.id);
break;
}
default: { break; }
}
break;
}
default: { break; }
}
}
// websocket hook that opens and allows interaction via the wss connection
const {
sendMessage
} = useWebSocket(process.env.REACT_APP_WEBSOCKET_URL, {
onOpen: () => onConnect(),
onClose: () => onDisconnect(),
onMessage: (event) => handleData(event),
onError: (event) => { console.error(event); },
shouldReconnect: (closeEvent) => true,
});
}
export default useWeekViewWebSocket;
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment