diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index b7cedc3c91918494814c46a19eb98f2165d6ff87..7ffafe1839926cac13ad3b6ad1e9a66d2907cde2 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -211,8 +211,6 @@ dockerize_TMSS:
   needs:
     - job: build_SCU
       artifacts: true
-    - job: integration_test_SCU
-      artifacts: false     
 
 #
 # INTEGRATION TEST STAGE
@@ -317,6 +315,8 @@ deploy-tmss-ua:
     - ssh lofarsys@tmss-ua.control.lofar "docker-compose -f docker-compose-ua.yml up -d"
   needs:
     - dockerize_TMSS
+    - job: integration_test_SCU
+      artifacts: false
   when: manual
   only:
     - "master"
@@ -330,9 +330,9 @@ deploy-tmss-dockerhub:
     - docker logout
   needs:
     - dockerize_TMSS
+    - job: integration_test_SCU
+      artifacts: false
   when: manual
-  only:
-    - "master"
 
 deploy-MCU_MAC-test:
   stage: deploy
diff --git a/Docker/lofar-ci/Dockerfile_ci_scu b/Docker/lofar-ci/Dockerfile_ci_scu
index ef581a80eaf0eb70ca3d2b58334baab6ef53de27..a8bca2fa7e8674609edeb39799f23f88ca3a3184 100644
--- a/Docker/lofar-ci/Dockerfile_ci_scu
+++ b/Docker/lofar-ci/Dockerfile_ci_scu
@@ -30,7 +30,7 @@ RUN pip3 install cython kombu lxml requests pygcn xmljson mysql-connector-python
 RUN pip3 install django-material django-viewflow
 
 # Note: nodejs now comes with npm, do not install the npm package separately, since that will be taken from the epel repo and is conflicting.
-RUN echo "Installing Nodejs packages..." && \
+RUN echo "Installing Nodejs packages...." && \
     curl -sL https://rpm.nodesource.com/setup_14.x | bash - && \
     yum install -y nodejs && \
     npm -v && \
diff --git a/LCS/PyCommon/json_utils.py b/LCS/PyCommon/json_utils.py
index 963e397174ee5943fa038d869af8c78edcaae33e..6a40f670614a047e0107b7c21059e1dce1fb2d99 100644
--- a/LCS/PyCommon/json_utils.py
+++ b/LCS/PyCommon/json_utils.py
@@ -16,6 +16,8 @@
 # with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
 
 import json
+import time
+
 import jsonschema
 from copy import deepcopy
 import requests
@@ -159,19 +161,31 @@ def get_referenced_subschema(ref_url, cache: dict=None, max_cache_age: timedelta
     '''fetch the schema given by the ref_url, and get the sub-schema given by the #/ path in the ref_url'''
     # deduct referred schema name and version from ref-value
     head, anchor, tail = ref_url.partition('#')
+
+    def _fech_url_and_update_cache_entry_if_needed():
+        # try to fetch the url a few time (jsonschema.org is down quite often, but only for a brief moment)
+        for attempt_nr in range(5):
+            try:
+                response = requests.get(ref_url)
+                if response.status_code == 200:
+                    referenced_schema = json.loads(response.text)
+                    if isinstance(cache, dict):
+                        cache[head] = referenced_schema, datetime.utcnow()
+                    return referenced_schema
+            except requests.exceptions.RequestException as e:
+                time.sleep(2) # retry after a little sleep
+        raise Exception("Could not get: %s" % (ref_url,))
+
     if isinstance(cache, dict) and head in cache:
         # use cached value
         referenced_schema, last_update_timestamp = cache[head]
 
         # refresh cache if outdated
         if datetime.utcnow() - last_update_timestamp > max_cache_age:
-            referenced_schema = json.loads(requests.get(ref_url).text)
-            cache[head] = referenced_schema, datetime.utcnow()
+            referenced_schema = _fech_url_and_update_cache_entry_if_needed()
     else:
         # fetch url, and store in cache
-        referenced_schema = json.loads(requests.get(ref_url).text)
-        if isinstance(cache, dict):
-            cache[head] = referenced_schema, datetime.utcnow()
+        referenced_schema = _fech_url_and_update_cache_entry_if_needed()
 
     # extract sub-schema
     tail = tail.strip('/')
@@ -222,13 +236,12 @@ def get_refs(schema) -> set:
     return refs
 
 
-def validate_json_against_its_schema(json_object: dict):
+def validate_json_against_its_schema(json_object: dict, cache: dict=None, max_cache_age: timedelta=DEFAULT_MAX_SCHEMA_CACHE_AGE):
     '''validate the give json object against its own schema (the URI/URL that its propery $schema points to)'''
     schema_url = json_object['$schema']
-    response = requests.get(schema_url, headers={"Accept":"application/json"})
-    if response.status_code == 200:
-        return validate_json_against_schema(json_object, response.text)
-    raise jsonschema.exceptions.ValidationError("Could not get schema from '%s'\n%s" % (schema_url, str(response.text)))
+    schema_object = get_referenced_subschema(schema_url, cache=cache, max_cache_age=max_cache_age)
+    return validate_json_against_schema(json_object, schema_object)
+
 
 def validate_json_against_schema(json_string: str, schema: str):
     '''validate the given json_string against the given schema.
@@ -260,13 +273,13 @@ def validate_json_against_schema(json_string: str, schema: str):
         raise jsonschema.exceptions.ValidationError(str(e))
 
 
-def get_default_json_object_for_schema(schema: str) -> dict:
+def get_default_json_object_for_schema(schema: str, cache: dict=None, max_cache_age: timedelta=DEFAULT_MAX_SCHEMA_CACHE_AGE) -> dict:
     """
     TMSS wrapper for TMSS 'add_defaults_to_json_object_for_schema'
     :param schema:
     :return: json_object with default values of the schema
     """
-    data = add_defaults_to_json_object_for_schema({}, schema)
+    data = add_defaults_to_json_object_for_schema({}, schema, cache=cache, max_cache_age=max_cache_age)
     if '$id' in schema:
         data['$schema'] = schema['$id']
     return data
diff --git a/LCS/PyCommon/math.py b/LCS/PyCommon/math.py
index b461a4202bbd588b5184647ce0cb7210317444ec..3dd0b351b9ab6a84ed89cf3581be91aa4b0740e2 100644
--- a/LCS/PyCommon/math.py
+++ b/LCS/PyCommon/math.py
@@ -1,7 +1,10 @@
-from fractions import gcd
+try:
+    from math import gcd
+except ImportError:
+    from fractions import gcd
 
 __all__ = ["lcm"]
 
 def lcm(a, b):
     """ Return the Least Common Multiple of a and b. """
-    return abs(a * b) / gcd(a, b) if a and b else 0
+    return int(abs(a * b) / gcd(a, b) if a and b else 0)
diff --git a/LTA/LTACommon/CMakeLists.txt b/LTA/LTACommon/CMakeLists.txt
index 590e81909a57a76557c620d7509923ac90238782..fed7330d6f64b4791f34c11cc9d06d317a011080 100644
--- a/LTA/LTACommon/CMakeLists.txt
+++ b/LTA/LTACommon/CMakeLists.txt
@@ -1,5 +1,5 @@
 lofar_package(LTACommon 1.0)
 
-set(etc_files LTA-SIP.xsd)
-lofar_add_sysconf_files(${etc_files} DESTINATION lta)
+set(share_files LTA-SIP.xsd)
+lofar_add_data_files(${share_files} DESTINATION lta)
 
diff --git a/LTA/LTAIngest/LTAIngestServer/LTAIngestAdminServer/lib/ingesttmssadapter.py b/LTA/LTAIngest/LTAIngestServer/LTAIngestAdminServer/lib/ingesttmssadapter.py
index 7fd829007bf08bc58122d8ba8b1ad33e8f62c1ff..0e6210ef221fac0d7b2564f0845f0cd41aa3f7bf 100644
--- a/LTA/LTAIngest/LTAIngestServer/LTAIngestAdminServer/lib/ingesttmssadapter.py
+++ b/LTA/LTAIngest/LTAIngestServer/LTAIngestAdminServer/lib/ingesttmssadapter.py
@@ -27,14 +27,14 @@ from lofar.lta.ingest.server.config import MAX_NR_OF_RETRIES
 from lofar.sas.tmss.client.tmss_http_rest_client import TMSSsession
 from lofar.messaging.messagebus import ToBus, DEFAULT_BROKER, DEFAULT_BUSNAME, UsingToBusMixin
 from lofar.messaging.messages import CommandMessage, EventMessage
-from lofar.sas.tmss.client.tmssbuslistener import TMSSBusListener, TMSSEventMessageHandler, TMSS_ALL_EVENTS_FILTER
+from lofar.sas.tmss.client.tmssbuslistener import TMSSBusListener, TMSSEventMessageHandler, TMSS_SUBTASK_STATUS_EVENT_PREFIX
 from lofar.common.datetimeutils import totalSeconds
 from lofar.common.dbcredentials import DBCredentials
 from lofar.common.util import waitForInterrupt
 
 from threading import Thread
 import time
-from datetime import datetime
+from datetime import datetime, timedelta
 from typing import Union
 
 import logging
@@ -62,7 +62,25 @@ class IngestEventMessageHandlerForIngestTMSSAdapter(UsingToBusMixin, IngestEvent
 
     def onJobStarted(self, job_dict):
         if self.is_tmss_job(job_dict):
-            self.tmss_client.set_subtask_status(job_dict['export_id'], 'started')
+            subtask_id = job_dict['export_id']
+            subtask = self.tmss_client.get_subtask(subtask_id)
+
+            if subtask['state_value'] == 'started':
+                pass # the ingest subtask was already started
+            else:
+                # wait until subtask was fully queued (or in error/cancelled)
+                start_wait_timestamp = datetime.utcnow()
+                while subtask['state_value'] not in ('queued', 'cancelled', 'error'):
+                    if datetime.utcnow() - start_wait_timestamp > timedelta(seconds=60):
+                        raise TimeoutError("Timeout while waiting for ingest subtask id=%s to get status queued/cancelled/error. Current status is %s" % (subtask_id, subtask['state_value']))
+                    time.sleep(1)
+                    subtask = self.tmss_client.get_subtask(subtask_id)
+
+                if subtask['state_value'] == 'queued':
+                    # the ingest subtask was fully queued, and this is the first ingest transfer job that started
+                    # so, set the ingest subtask to starting->started
+                    self.tmss_client.set_subtask_status(subtask_id, 'starting')
+                    self.tmss_client.set_subtask_status(subtask_id, 'started')
 
     def onJobFailed(self, job_dict):
         if self.is_tmss_job(job_dict):
@@ -150,13 +168,15 @@ class TMSSEventMessageHandlerForIngestTMSSAdapter(UsingToBusMixin, TMSSEventMess
                 self.tmss_client.set_subtask_status(subtask['id'], 'queueing')
 
                 # gather all relevant and needed info...
-                task_blueprint = self.tmss_client.get_url_as_json_object(subtask['task_blueprint'])
+                task_blueprint = self.tmss_client.get_url_as_json_object(subtask['task_blueprints'][0])
                 task_draft = self.tmss_client.get_url_as_json_object(task_blueprint['draft'])
                 scheduling_unit_draft = self.tmss_client.get_url_as_json_object(task_draft['scheduling_unit_draft'])
                 scheduling_set = self.tmss_client.get_url_as_json_object(scheduling_unit_draft['scheduling_set'])
                 project = self.tmss_client.get_url_as_json_object(scheduling_set['project'])
 
                 # create an ingest xml job for each input dataproduct
+                # store the jobs in a list, and submit them in one go to the queue
+                jobs = []
                 for input_dp in input_dataproducts:
                     dp_global_identifier = self.tmss_client.get_url_as_json_object(input_dp['global_identifier'])
                     producer = self.tmss_client.get_url_as_json_object(input_dp['producer'])
@@ -169,7 +189,10 @@ class TMSSEventMessageHandlerForIngestTMSSAdapter(UsingToBusMixin, TMSSEventMess
                                        location=subtask['cluster_name']+':'+os.path.join(input_dp['directory'], input_dp['filename']),
                                        tmss_ingest_subtask_id=subtask['id'],
                                        tmss_input_dataproduct_id=input_dp['id'])
+                    jobs.append(job)
 
+                # submit all jobs to the  in one go to ingest-incoming-job-queue
+                for job in jobs:
                     msg = CommandMessage(content=job, subject=DEFAULT_INGEST_INCOMING_JOB_SUBJECT)
                     logger.info('submitting job %s to exchange %s with subject %s at broker %s',
                                 parseJobXml(job)['JobId'], self._tobus.exchange, msg.subject, self._tobus.broker)
diff --git a/LTA/LTAIngest/LTAIngestServer/LTAIngestTransferServer/lib/sip.py b/LTA/LTAIngest/LTAIngestServer/LTAIngestTransferServer/lib/sip.py
index 7c42d02ca0d8e9ca5522d16c1afe055d40aca316..440792e4546b6e7703dea0046dd35eb0ca1ef2d6 100755
--- a/LTA/LTAIngest/LTAIngestServer/LTAIngestTransferServer/lib/sip.py
+++ b/LTA/LTAIngest/LTAIngestServer/LTAIngestTransferServer/lib/sip.py
@@ -19,7 +19,7 @@ def validateSIPAgainstSchema(sip, log_prefix=''):
         start  = time.time()
 
         lofarrootdir = os.environ.get('LOFARROOT', '/opt/lofar')
-        sip_xsd_path = os.path.join(lofarrootdir, 'etc', 'lta', 'LTA-SIP.xsd')
+        sip_xsd_path = os.path.join(lofarrootdir, 'share', 'lta', 'LTA-SIP.xsd')
 
         if not os.path.exists(sip_xsd_path):
             logger.error('Could not find LTA-SIP.xsd at %s', sip_xsd_path)
diff --git a/LTA/sip/lib/siplib.py b/LTA/sip/lib/siplib.py
index e81b00ed5576eaf33f567f4a9394e609d9e284c5..71b7c184c5004408cb40ca7e3ec4b69b7e4da9c4 100644
--- a/LTA/sip/lib/siplib.py
+++ b/LTA/sip/lib/siplib.py
@@ -1488,11 +1488,16 @@ class Sip(object):
             raise Exception("This SIP does not describe a correlated dataproduct. No subarray pointing available.")
 
     # this will also validate the document so far
-    def get_prettyxml(self):
+    def get_prettyxml(self, schema_location:str = None):
         try:
             dom = self.__sip.toDOM()
             dom.documentElement.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
-            dom.documentElement.setAttribute('xsi:schemaLocation', "http://www.astron.nl/SIP-Lofar LTA-SIP-2.7.0.xsd")
+            if schema_location is None:
+                # this is/was the default schema location, even though we never hosted the xsd at the astron server
+                # That makes xmllint fail to validate (because the schema obviously can't be found)
+                schema_location = "http://www.astron.nl/SIP-Lofar LTA-SIP-2.7.2.xsd"
+            dom.documentElement.setAttribute('xsi:schemaLocation', schema_location)
+            dom.documentElement.setAttribute('xmlns:sip', schema_location.split(' ')[0])
             return dom.toprettyxml()
         except pyxb.ValidationError as err:
             logger.error(err.details())
diff --git a/LTA/sip/lib/validator.py b/LTA/sip/lib/validator.py
index 508c2beee330b5c7a32226031eb9d95beef2c2d3..e0de12d44e2a71d200607a2d44264f082e619105 100644
--- a/LTA/sip/lib/validator.py
+++ b/LTA/sip/lib/validator.py
@@ -8,7 +8,7 @@ logger = logging.getLogger(__name__)
 d = os.path.dirname(os.path.realpath(__file__))
 XSDPATH = d+"/LTA-SIP.xsd"
 
-DEFAULT_SIP_XSD_PATH = os.path.join(os.environ.get('LOFARROOT', '/opt/lofar'), 'etc', 'lta', 'LTA-SIP.xsd')
+DEFAULT_SIP_XSD_PATH = os.path.join(os.environ.get('LOFARROOT', '/opt/lofar'), 'share', 'lta', 'LTA-SIP.xsd')
 
 def validate(xmlpath, xsdpath=DEFAULT_SIP_XSD_PATH):
     '''validates given xml file against given xsd file'''
diff --git a/MAC/APL/MainCU/src/MACScheduler/MACScheduler.cc b/MAC/APL/MainCU/src/MACScheduler/MACScheduler.cc
index 070672448124bd3697786a99704e1dd4ca1c3ec9..d1a1564f304e17f25a3d742aa955a1fdfdd9ad48 100644
--- a/MAC/APL/MainCU/src/MACScheduler/MACScheduler.cc
+++ b/MAC/APL/MainCU/src/MACScheduler/MACScheduler.cc
@@ -577,6 +577,9 @@ GCFEvent::TResult MACScheduler::active_state(GCFEvent& event, GCFPortInterface&
             tm.setTreeState(theObs->second, tsc.get("queued"));
 #endif
 		} else {
+		    // TODO: set to queueing state the moment MAC knows about this upoming obs.
+		    // I've tried that but it's realy hard to keep the MAC internal bookkeeping and TMSS in sync.
+            itsTMSSconnection->setSubtaskState(theObs->second, "queueing");
     		itsTMSSconnection->setSubtaskState(theObs->second, "queued");
         }
 		break;
@@ -598,6 +601,7 @@ GCFEvent::TResult MACScheduler::active_state(GCFEvent& event, GCFPortInterface&
 #endif
         }
         else {
+    		itsTMSSconnection->setSubtaskState(theObs->second, "starting");
     		itsTMSSconnection->setSubtaskState(theObs->second, "started");
         }
 		break;
@@ -922,8 +926,6 @@ void MACScheduler::_updatePlannedList()
 		OLiter	prepIter = itsPreparedObs.find(subtask_id);
 		if ((prepIter == itsPreparedObs.end()) || (prepIter->second.prepReady == false) ||
 												  (prepIter->second.modTime != modTime)) {
-            itsTMSSconnection->setSubtaskState(subtask_id, "queueing");
-
 			// create a ParameterFile for this Observation
             string parsetText = itsTMSSconnection->getParsetAsText(subtask_id);
             if(prepIter == itsPreparedObs.end()) {
diff --git a/MAC/APL/MainCU/src/MACScheduler/TMSSBridge.cc b/MAC/APL/MainCU/src/MACScheduler/TMSSBridge.cc
index 5a9bc3f4bea1cadc352584deeb3ff09fba52e036..2fd54053b9b58da8c84a19bf981eb8c7b6e27d50 100644
--- a/MAC/APL/MainCU/src/MACScheduler/TMSSBridge.cc
+++ b/MAC/APL/MainCU/src/MACScheduler/TMSSBridge.cc
@@ -236,6 +236,7 @@ bool TMSSBridge::httpQuery(const string& target, string &result, const string& q
     curl_global_cleanup();
 
     LOG_INFO_STR(string("[") << query_method << "] code=" << httpCode << " " << url);
+
     if (httpCode == 200) {
         return true;
     }
diff --git a/SAS/ResourceAssignment/TaskPrescheduler/lib/cobaltblocksize.py b/SAS/ResourceAssignment/TaskPrescheduler/lib/cobaltblocksize.py
index ac14727d9a2c2645de608bf7454bd9bf60e30175..52c3ae1853232578adeb76089b96260d020ba94b 100644
--- a/SAS/ResourceAssignment/TaskPrescheduler/lib/cobaltblocksize.py
+++ b/SAS/ResourceAssignment/TaskPrescheduler/lib/cobaltblocksize.py
@@ -96,7 +96,7 @@ class BlockConstraints(object):
         NR_PPF_TAPS = 16
         MAX_THREADS_PER_BLOCK = 1024
         CORRELATOR_BLOCKSIZE = 16
-        BEAMFORMER_NR_DELAYCOMPENSATION_CHANNELS = 64
+        BEAMFORMER_NR_DELAYCOMPENSATION_CHANNELS = 256
         BEAMFORMER_DELAYCOMPENSATION_BLOCKSIZE = 16
 
         # Process correlator settings
diff --git a/SAS/ResourceAssignment/TaskPrescheduler/test/t_cobaltblocksize.py b/SAS/ResourceAssignment/TaskPrescheduler/test/t_cobaltblocksize.py
index 8eaec011e3fd642723377b9ace171db8a687dfd1..dee024550e87002b001d5488e4d657bc246120d1 100644
--- a/SAS/ResourceAssignment/TaskPrescheduler/test/t_cobaltblocksize.py
+++ b/SAS/ResourceAssignment/TaskPrescheduler/test/t_cobaltblocksize.py
@@ -99,5 +99,20 @@ class TestBlockSize(unittest.TestCase):
 
             self.assertAlmostEquals(c._samples2time(bs.integrationSamples), integrationTime, delta = integrationTime * 0.05)
 
+    @unit_test
+    def testCoherentStokesBlocksize(self):
+        """ Test the coherent stokes block size against reference output, based on cases that used to fail in production.
+            If the output of these calculations change, make sure the described configurations do actually work in COBALT! """
+
+        coh = StokesSettings()
+        coh.nrChannelsPerSubband = 16
+        coh.timeIntegrationFactor = 1
+
+        c = BlockConstraints(coherentStokesSettings=[coh])
+        bs = BlockSize(c)
+
+        self.assertEqual(bs.blockSize, 196608)
+
+
 if __name__ == "__main__":
     unittest.main(verbosity=2)
diff --git a/SAS/TMSS/backend/services/scheduling/lib/constraints/template_constraints_v1.py b/SAS/TMSS/backend/services/scheduling/lib/constraints/template_constraints_v1.py
index 342b727554e0c3a5ca3212ab4008f8ecd116e752..18cf8bc94ede9b0f46bd2871f5c01e30f6b05f13 100644
--- a/SAS/TMSS/backend/services/scheduling/lib/constraints/template_constraints_v1.py
+++ b/SAS/TMSS/backend/services/scheduling/lib/constraints/template_constraints_v1.py
@@ -62,7 +62,7 @@ def can_run_within_timewindow(scheduling_unit: models.SchedulingUnitBlueprint, l
 def can_run_after(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound: datetime) -> bool:
     '''Check if the given scheduling_unit can run somewhere after the given lowerbound timestamp depending on the sub's constrains-template/doc.'''
     constraints = scheduling_unit.scheduling_constraints_doc
-    if 'before' in constraints['time']:
+    if 'time' in constraints and 'before' in constraints['time']:
         before = parser.parse(constraints['time']['before'], ignoretz=True)
         return before > lower_bound
 
@@ -83,7 +83,7 @@ def can_run_within_timewindow_with_daily_constraints(scheduling_unit: models.Sch
     Checks whether it is possible to run the scheduling unit /somewhere/ in the given time window, considering the duration of the involved observation.
     :return: True if there is at least one possibility to place the scheduling unit in a way that all daily constraints are met over the runtime of the observation, else False.
     """
-    main_observation_task_name = get_target_observation_task_name_from_requirements_doc(scheduling_unit)
+    main_observation_task_name = get_longest_observation_task_name_from_requirements_doc(scheduling_unit)
     duration = timedelta(seconds=scheduling_unit.requirements_doc['tasks'][main_observation_task_name]['specifications_doc']['duration'])
     window_lower_bound = lower_bound
     while window_lower_bound + duration < upper_bound:
@@ -100,8 +100,12 @@ def can_run_anywhere_within_timewindow_with_daily_constraints(scheduling_unit: m
     Checks whether it is possible to place the scheduling unit arbitrarily in the given time window, i.e. the daily constraints must be met over the full time window.
     :return: True if all daily constraints are met over the entire time window, else False.
     """
-    main_observation_task_name = get_target_observation_task_name_from_requirements_doc(scheduling_unit)
+    main_observation_task_name = get_longest_observation_task_name_from_requirements_doc(scheduling_unit)
     constraints = scheduling_unit.scheduling_constraints_doc
+
+    if not "daily" in constraints:
+        return True
+
     if constraints['daily']['require_day'] or constraints['daily']['require_night'] or constraints['daily']['avoid_twilight']:
 
         if (upper_bound - lower_bound).days >= 1:
@@ -157,11 +161,11 @@ def can_run_within_timewindow_with_time_constraints(scheduling_unit: models.Sche
     :return: True if there is at least one possibility to place the scheduling unit in a way that all time
              constraints are met over the runtime of the observation, else False.
     """
-    main_observation_task_name = get_target_observation_task_name_from_requirements_doc(scheduling_unit)
+    main_observation_task_name = get_longest_observation_task_name_from_requirements_doc(scheduling_unit)
     constraints = scheduling_unit.scheduling_constraints_doc
 
     # Check the 'at' constraint and then only check can_run_anywhere for the single possible time window
-    if 'at' in constraints['time']:
+    if 'time' in constraints and 'at' in constraints['time']:
         at = parser.parse(constraints['time']['at'], ignoretz=True)
         if (at >= lower_bound and at + scheduling_unit.duration <= upper_bound):    # todo: suggestion: use scheduling_unit.requirements_doc['tasks']['Observation']['specifications_doc']['duration']
             return can_run_anywhere_within_timewindow_with_time_constraints(scheduling_unit, lower_bound=at,
@@ -191,6 +195,9 @@ def can_run_anywhere_within_timewindow_with_time_constraints(scheduling_unit: mo
     can_run_not_between = True
     constraints = scheduling_unit.scheduling_constraints_doc
 
+    if not "time" in constraints:
+        return True
+
     # given time window needs to end before constraint
     if 'before' in constraints['time']:
         before = parser.parse(constraints['time']['before'], ignoretz=True)
@@ -251,9 +258,6 @@ def can_run_anywhere_within_timewindow_with_sky_constraints(scheduling_unit: mod
     Checks whether it is possible to place the scheduling unit arbitrarily in the given time window, i.e. the sky constraints must be met over the full time window.
     :return: True if all sky constraints are met over the entire time window, else False.
     """
-    # TODO: remove this shortcut after demo
-    return True
-
     constraints = scheduling_unit.scheduling_constraints_doc
     if not "sky" in constraints:
         return True
@@ -355,8 +359,6 @@ def can_run_anywhere_within_timewindow_with_sky_constraints(scheduling_unit: mod
                                 average_transit_time = _reference_date + sum([date - _reference_date for date in sap_datetime_list], timedelta()) / len(sap_datetime_list)
                                 transit_times.get(station, []).append(average_transit_time)
 
-                        logger.warning('##### %s' % transit_times)
-
                         for station, times in transit_times.items():
                             for i in range(len(timestamps)):
                                 offset = (timestamps[i] - times[i]).total_seconds()
@@ -385,21 +387,37 @@ def get_target_observation_task_name_from_requirements_doc(scheduling_unit: mode
     raise TMSSException("Cannot find target observation in scheduling_unit requirements_doc")
 
 
+def get_longest_observation_task_name_from_requirements_doc(scheduling_unit: models.SchedulingUnitBlueprint) -> str:
+    longest_observation_task_name = None
+    longest_observation_duration = 0
+    for task_name, task in scheduling_unit.requirements_doc['tasks'].items():
+        if 'observation' in task.get('specifications_template', ''):
+            if 'duration' in task.get('specifications_doc', {}):
+                duration = task['specifications_doc']['duration']
+                if duration > longest_observation_duration:
+                    longest_observation_duration = duration
+                    longest_observation_task_name = task_name
+    if longest_observation_task_name is not None:
+        return longest_observation_task_name
+    raise TMSSException("Cannot find a longest observation in scheduling_unit requirements_doc")
+
+
 def get_earliest_possible_start_time(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound: datetime) -> datetime:
     constraints = scheduling_unit.scheduling_constraints_doc
 
-    main_observation_task_name = get_target_observation_task_name_from_requirements_doc(scheduling_unit)
+    # TODO: for estimating the earliest_possible_start_time, we need the full duration of the scheduling unit, not just the longest one.
+    main_observation_task_name = get_longest_observation_task_name_from_requirements_doc(scheduling_unit)
     duration = timedelta(seconds=scheduling_unit.requirements_doc['tasks'][main_observation_task_name]['specifications_doc']['duration'])
     try:
-        if 'at' in constraints['time']:
+        if 'time' in constraints and 'at' in constraints['time']:
             at = parser.parse(constraints['time']['at'], ignoretz=True)
             return max(lower_bound, at)
 
-        if 'after' in constraints['time']:
+        if 'time' in constraints and 'after' in constraints['time']:
             after = parser.parse(constraints['time']['after'], ignoretz=True)
             return max(lower_bound, after)
 
-        if constraints['daily']['require_day'] or constraints['daily']['require_night'] or constraints['daily']['avoid_twilight']:
+        if 'daily' in constraints and (constraints['daily']['require_day'] or constraints['daily']['require_night'] or constraints['daily']['avoid_twilight']):
             station_groups = scheduling_unit.requirements_doc['tasks'][main_observation_task_name]['specifications_doc']["station_groups"]
             stations = list(set(sum([group['stations'] for group in station_groups], [])))  # flatten all station_groups to single list
             all_sun_events = timestamps_and_stations_to_sun_rise_and_set(timestamps=(lower_bound,lower_bound+timedelta(days=1)), stations=tuple(stations))
@@ -467,7 +485,7 @@ def compute_scores(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound:
 
     # for now (as a proof of concept and sort of example), just return 1's. Return 1000 (placeholder value, change later) if the 'at' constraint is in, so it gets prioritised.
     scores = {'daily': 1.0,
-              'time': 1000.0 if ('at' in constraints['time'] and constraints['time']['at'] is not None) else 1.0,
+              'time': 1000.0 if ('time' in constraints and 'at' in constraints['time'] and constraints['time']['at'] is not None) else 1.0,
               'sky': 1.0}
 
     # add "common" scores which do not depend on constraints, such as project rank and creation date
diff --git a/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py b/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py
index 5ff4971b7f719615583eaf50ad3aaf5b86d27f92..5769fa88fbda113e9cb37bc1b3ed98007abcbbc8 100644
--- a/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py
+++ b/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py
@@ -33,7 +33,7 @@ from datetime import datetime, timedelta, time
 
 from lofar.sas.tmss.tmss.tmssapp import models
 from lofar.sas.tmss.tmss.tmssapp.tasks import schedule_independent_subtasks_in_scheduling_unit_blueprint, unschedule_subtasks_in_scheduling_unit_blueprint
-from lofar.sas.tmss.tmss.tmssapp.subtasks import update_subtasks_start_times_for_scheduling_unit, clear_defined_subtasks_start_stop_times_for_scheduling_unit
+from lofar.sas.tmss.tmss.tmssapp.subtasks import update_subtasks_start_times_for_scheduling_unit, clear_defined_subtasks_start_stop_times_for_scheduling_unit, cancel_subtask
 from lofar.sas.tmss.client.tmssbuslistener import *
 from lofar.common.datetimeutils import round_to_second_precision
 from threading import Thread, Event
@@ -68,7 +68,14 @@ def find_best_next_schedulable_unit(scheduling_units:[models.SchedulingUnitBluep
     filtered_scheduling_units = filter_scheduling_units_using_constraints(scheduling_units, lower_bound_start_time, upper_bound_stop_time)
 
     if filtered_scheduling_units:
-        best_scored_scheduling_unit = get_best_scored_scheduling_unit_scored_by_constraints(filtered_scheduling_units, lower_bound_start_time, upper_bound_stop_time)
+        triggered_scheduling_units = [scheduling_unit for scheduling_unit in filtered_scheduling_units if scheduling_unit.is_triggered]
+        if triggered_scheduling_units:
+            highest_priority_triggered_scheduling_unit = max(triggered_scheduling_units, key=lambda su: su.project.trigger_priority)
+            best_scored_scheduling_unit = ScoredSchedulingUnit(scheduling_unit=highest_priority_triggered_scheduling_unit,
+                                                               start_time=get_earliest_possible_start_time(highest_priority_triggered_scheduling_unit, lower_bound_start_time),
+                                                               scores={}, weighted_score=None)  # we don't care about scores in case of a trigger
+        else:
+            best_scored_scheduling_unit = get_best_scored_scheduling_unit_scored_by_constraints(filtered_scheduling_units, lower_bound_start_time, upper_bound_stop_time)
         return best_scored_scheduling_unit
 
     # no filtered scheduling units found...
@@ -92,10 +99,14 @@ def schedule_next_scheduling_unit() -> models.SchedulingUnitBlueprint:
     lower_bound_start_time = get_min_earliest_possible_start_time(schedulable_units, datetime.utcnow()+DEFAULT_NEXT_STARTTIME_GAP)
 
     # estimate the upper_bound_stop_time, which may give us a small timewindow before any next scheduled unit, or a default window of a day
-    try:
-        upper_bound_stop_time = max(su.start_time for su in get_scheduled_scheduling_units(lower=lower_bound_start_time, upper=lower_bound_start_time + timedelta(days=1)))
-    except ValueError:
+    if any([su.is_triggered for su in schedulable_units]):
+        # ignore what's scheduled if we have triggers
         upper_bound_stop_time = lower_bound_start_time + timedelta(days=1)
+    else:
+        try:
+            upper_bound_stop_time = max(su.start_time for su in get_scheduled_scheduling_units(lower=lower_bound_start_time, upper=lower_bound_start_time + timedelta(days=1)))
+        except ValueError:
+            upper_bound_stop_time = lower_bound_start_time + timedelta(days=1)
 
     # no need to irritate user in log files with subsecond scheduling precision
     lower_bound_start_time = round_to_second_precision(lower_bound_start_time)
@@ -115,15 +126,18 @@ def schedule_next_scheduling_unit() -> models.SchedulingUnitBlueprint:
                 # make start_time "look nice" for us humans
                 best_start_time = round_to_second_precision(best_start_time)
 
-                logger.info("schedule_next_scheduling_unit: found best candidate id=%s '%s' weighted_score=%s start_time=%s",
-                            best_scheduling_unit.id, best_scheduling_unit.name, best_scheduling_unit_score, best_start_time)
+                logger.info("schedule_next_scheduling_unit: found best candidate id=%s '%s' weighted_score=%s start_time=%s is_triggered=%s",
+                            best_scheduling_unit.id, best_scheduling_unit.name, best_scheduling_unit_score, best_start_time, best_scheduling_unit.is_triggered)
+
+                if best_scheduling_unit.is_triggered:
+                    cancel_running_observation_if_needed_and_possible(best_scored_scheduling_unit)
 
                 if unschededule_blocking_scheduled_units_if_needed_and_possible(best_scored_scheduling_unit):
                     # no (old) scheduled scheduling_units in the way, so schedule our candidate!
                     scheduled_scheduling_unit = schedule_independent_subtasks_in_scheduling_unit_blueprint(best_scheduling_unit, start_time=best_start_time)
 
-                    logger.info("schedule_next_scheduling_unit: scheduled best candidate id=%s '%s' score=%s start_time=%s",
-                                best_scheduling_unit.id, best_scheduling_unit.name, best_scheduling_unit_score, best_start_time)
+                    logger.info("schedule_next_scheduling_unit: scheduled best candidate id=%s '%s' score=%s start_time=%s is_triggered=%s",
+                                best_scheduling_unit.id, best_scheduling_unit.name, best_scheduling_unit_score, best_start_time, best_scheduling_unit.is_triggered)
                     return scheduled_scheduling_unit
 
         except SubtaskSchedulingException as e:
@@ -253,7 +267,7 @@ class TMSSDynamicSchedulingMessageHandler(TMSSEventMessageHandler):
             # This way we are sure that the latest units are always taken into account while scheduling, but we do not waste cpu cylces.
             self._do_schedule_event.set()
 
-    def onSchedulingUnitDraftConstraintsUpdated(self, id: int, scheduling_constraints_doc: dict):
+    def onSchedulingUnitDraftConstraintsUpdated(self, id: int, scheduling_constraints_doc: dict):  # todo: Does this now have to be onSchedulingUnitBlueprintConstraintsUpdated (since we now have those on the blueprint as well)?
         affected_scheduling_units = models.SchedulingUnitBlueprint.objects.filter(draft__id=id).all()
         for scheduling_unit in affected_scheduling_units:
             if scheduling_unit.status == 'scheduled':
@@ -312,6 +326,15 @@ def get_scheduled_scheduling_units(lower:datetime=None, upper:datetime=None) ->
     return list(models.SchedulingUnitBlueprint.objects.filter(id__in=scheduled_subtasks.values('task_blueprints__scheduling_unit_blueprint_id').distinct()).all())
 
 
+def get_running_observation_subtasks(stopping_after:datetime=None) -> [models.Subtask]:
+    '''get a list of all starting/started subtasks, optionally filter for those finishing after the provided time'''
+    running_obs_subtasks = models.Subtask.objects.filter(state__value__in=[models.SubtaskState.Choices.STARTING.value, models.SubtaskState.Choices.STARTED.value],
+                                                         specifications_template__type__value=models.SubtaskType.Choices.OBSERVATION.value)
+    if stopping_after is not None:
+        running_obs_subtasks = running_obs_subtasks.filter(stop_time__gte=stopping_after)
+    return list(running_obs_subtasks.all())
+
+
 def unschededule_blocking_scheduled_units_if_needed_and_possible(candidate: ScoredSchedulingUnit) -> bool:
     '''check if there are any already scheduled units in the way, and unschedule them if allowed. Return True if nothing is blocking anymore.'''
     # check any previously scheduled units, and unschedule if needed/allowed
@@ -322,7 +345,16 @@ def unschededule_blocking_scheduled_units_if_needed_and_possible(candidate: Scor
     for scheduled_scheduling_unit in scheduled_scheduling_units:
         scheduled_score = compute_scores(scheduled_scheduling_unit, candidate.start_time, candidate.start_time + candidate.scheduling_unit.duration)
 
-        if candidate.weighted_score > scheduled_score.weighted_score:
+        # in case of a triggered candidate with higher trigger priority than the scheduled unit, we don't care about
+        # scores, but only check trigger priority.
+        if candidate.scheduling_unit.is_triggered:
+            if (not scheduled_scheduling_unit.is_triggered) or candidate.scheduling_unit.project.trigger_priority > scheduled_scheduling_unit.project.trigger_priority:
+                logger.info("unscheduling id=%s '%s' because it is in the way and has a lower trigger_priority=%s than the best candidate id=%s '%s' trigger_priority=%s start_time=%s",
+                    scheduled_scheduling_unit.id, scheduled_scheduling_unit.name, scheduled_scheduling_unit.project.trigger_priority,
+                    candidate.scheduling_unit.id, candidate.scheduling_unit.name, candidate.scheduling_unit.project.trigger_priority, candidate.scheduling_unit.start_time)
+                unschedule_subtasks_in_scheduling_unit_blueprint(scheduled_scheduling_unit)
+
+        elif candidate.weighted_score > scheduled_score.weighted_score:
             # ToDo: also check if the scheduled_scheduling_unit is manually/dynamically scheduled
             logger.info("unscheduling id=%s '%s' because it is in the way and has a lower score than the best candidate id=%s '%s' score=%s start_time=%s",
                 scheduled_scheduling_unit.id, scheduled_scheduling_unit.name,
@@ -345,4 +377,28 @@ def unschededule_blocking_scheduled_units_if_needed_and_possible(candidate: Scor
     return True
 
 
+def cancel_running_observation_if_needed_and_possible(candidate: ScoredSchedulingUnit) -> bool:
+    '''check if there are starting/started observation subtasks that block the candidate.
+     Only triggered scheduling_units can cancel running observations and only if they belong to a project with a higher
+     trigger_priority than the projects of the subtasks to cancel'''
+
+    # todo: is it sufficient to cancel the subtasks, or do we cancel the whole scheduling unit?
+    if candidate.scheduling_unit.is_triggered:
+        running_obs_subtasks = get_running_observation_subtasks(candidate.start_time)
+        for obs in running_obs_subtasks:
+            if obs.project is None:
+                logger.warning('cannot cancel running subtask pk=%s for triggered scheduling_unit pk=%s because it does belong to a project and hence has unknown priority' %
+                               (obs.pk, candidate.scheduling_unit.name))
+                continue
+            if candidate.scheduling_unit.project.trigger_priority > obs.project.trigger_priority:
+                logger.info('cancelling observation subtask pk=%s trigger_priority=%s because it blocks the triggered scheduling_unit pk=%s trigger_priority=%s' %
+                            (obs.pk, obs.project.trigger_priority, candidate.scheduling_unit.pk, candidate.scheduling_unit.project.trigger_priority))
+                # todo: check if cancellation is really necessary or the trigger can be scheduled afterwards
+                #  I guess we could just do can_run_after(candidate, obs.stop_time) here for that?
+                #  We could also only do this, of there is a 'before' constraint on each trigger.
+                #  -> Clarify and implemented with TMSS-704.
+                cancel_subtask(obs)
+            else:
+                logger.info('NOT cancelling subtask pk=%s trigger_priority=%s for triggered scheduling_unit pk=%s trigger_priority=%s because its priority is too low' %
+                            (obs.pk, obs.project.trigger_priority, candidate.scheduling_unit.pk, candidate.scheduling_unit.project.trigger_priority))
 
diff --git a/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py b/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py
index 59e644b4a882c4add00baa7b495fb95f41a524df..5c4dde3fd2756219d9d95731c36677f988ea439f 100755
--- a/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py
+++ b/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py
@@ -26,9 +26,6 @@ from astropy.coordinates import Angle
 import logging
 logger = logging.getLogger(__name__)
 
-#TODO: remove after demo
-exit(3)
-
 from lofar.common.test_utils import skip_integration_tests
 if skip_integration_tests():
     exit(3)
@@ -113,15 +110,17 @@ class TestDynamicScheduling(TestCase):  # Note: we use django.test.TestCase inst
                             for input in subtask.inputs.all():
                                 input.delete()
                             subtask.delete()
-                        task_blueprint.draft.delete()
+                        draft = task_blueprint.draft
                         task_blueprint.delete()
+                        draft.delete()
                     scheduling_unit_blueprint.delete()
                 scheduling_unit_draft.delete()
 
     @staticmethod
     def create_simple_observation_scheduling_unit(name:str=None, scheduling_set=None,
                                                   obs_duration:int=60,
-                                                  constraints=None):
+                                                  constraints=None,
+                                                  is_triggered=False):
         constraints_template = models.SchedulingConstraintsTemplate.objects.get(name="constraints")
         constraints = add_defaults_to_json_object_for_schema(constraints or {}, constraints_template.schema)
 
@@ -138,7 +137,8 @@ class TestDynamicScheduling(TestCase):  # Note: we use django.test.TestCase inst
                                                          requirements_doc=scheduling_unit_spec,
                                                          observation_strategy_template=strategy_template,
                                                          scheduling_constraints_doc=constraints,
-                                                         scheduling_constraints_template=constraints_template)
+                                                         scheduling_constraints_template=constraints_template,
+                                                         is_triggered=is_triggered)
 
     def test_simple_observation_with_at_constraint(self):
         """
@@ -348,6 +348,34 @@ class TestDynamicScheduling(TestCase):  # Note: we use django.test.TestCase inst
         self.assertGreaterEqual(scheduling_unit_blueprint_high.start_time - scheduling_unit_blueprint_manual.stop_time, DEFAULT_INTER_OBSERVATION_GAP)
 
 
+    def test_can_schedule_all_observing_strategy_templates_with_default_constraints(self):
+        ''''''
+        constraints_template = models.SchedulingConstraintsTemplate.objects.get(name="constraints")
+        constraints = get_default_json_object_for_schema(constraints_template.schema)
+
+        for strategy_template in models.SchedulingUnitObservingStrategyTemplate.objects.all():
+            scheduling_unit_spec = add_defaults_to_json_object_for_schema(strategy_template.template,
+                                                                          strategy_template.scheduling_unit_template.schema)
+
+            draft = models.SchedulingUnitDraft.objects.create(name=strategy_template.name,
+                                                              scheduling_set=self.scheduling_set_high,
+                                                              requirements_template=strategy_template.scheduling_unit_template,
+                                                              requirements_doc=scheduling_unit_spec,
+                                                              observation_strategy_template=strategy_template,
+                                                              scheduling_constraints_doc=constraints,
+                                                              scheduling_constraints_template=constraints_template)
+            blueprint = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(draft)
+
+            # call the method-under-test: do_dynamic_schedule
+            # this test only checks if each strategy_template *can* be scheduled by the dynamic scheduler without any exceptions/errors. The defaults should just work.
+            scheduled_scheduling_unit = do_dynamic_schedule()
+            self.assertEqual(blueprint.id, scheduled_scheduling_unit.id)
+            self.assertEqual("scheduled", scheduled_scheduling_unit.status)
+
+            # wipe all entries in tmss-db/radb, and try next strategy_template
+            self.setUp()
+
+
 class TestDailyConstraints(TestCase):
     '''
     Tests for the constraint checkers used in dynamic scheduling
@@ -807,7 +835,7 @@ class TestSkyConstraints(unittest.TestCase):
         self.distance_mock = self.distance_patcher.start()
         self.distance_mock.return_value = self.distance_data
         self.addCleanup(self.distance_patcher.stop)
-        
+
         self.target_rise_and_set_data = {"CS002": [{"rise": datetime(2020, 1, 1, 8, 0, 0), "set": datetime(2020, 1, 1, 12, 30, 0), "always_above_horizon": False, "always_below_horizon": False},
                                                    {"rise": datetime(2020, 1, 1, 8, 0, 0), "set": datetime(2020, 1, 1, 12, 30, 0), "always_above_horizon": False, "always_below_horizon": False}]}
         self.target_rise_and_set_data_always_above = {"CS002": [{"rise": None, "set": None, "always_above_horizon": True, "always_below_horizon": False}]}
@@ -863,14 +891,14 @@ class TestSkyConstraints(unittest.TestCase):
         # case 1: transits at 14h, obs middle is at 13h, so we have an offset of -3600 seconds
 
         # big window
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'transit_offset': {'from': -43200, 'to': 43200}}  # todo: use blueprint contraints after TMSS-697 was merged
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['sky'] = {'transit_offset': {'from': -43200, 'to': 43200}}
         self.scheduling_unit_blueprint.save()
         timestamp = datetime(2020, 1, 1, 12, 0, 0)
         returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration))
         self.assertTrue(returned_value)
 
         # narrow window
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'transit_offset': {'from': -3601, 'to': -3599}}  # todo: use blueprint contraints after TMSS-697 was merged
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['sky'] = {'transit_offset': {'from': -3601, 'to': -3599}}
         self.scheduling_unit_blueprint.save()
         timestamp = datetime(2020, 1, 1, 12, 0, 0)
         returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration))
@@ -880,7 +908,7 @@ class TestSkyConstraints(unittest.TestCase):
 
         # window spans past 12h, so reference transit is not nearest transit to obs time
         self.target_transit_mock.return_value = self.target_transit_data_previous
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'transit_offset': {'from': -43300, 'to': -43100}}  # todo: use blueprint contraints after TMSS-697 was merged
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['sky'] = {'transit_offset': {'from': -43300, 'to': -43100}}
         self.scheduling_unit_blueprint.save()
         timestamp = datetime(2020, 1, 1, 1, 0, 0)
         returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration))
@@ -891,14 +919,14 @@ class TestSkyConstraints(unittest.TestCase):
         # transits at 14h, obs middle is at 13h, so we have an offset of -3600 seconds
 
         # window after
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'transit_offset': {'from': -3599, 'to': 43200}}  # todo: use blueprint contraints after TMSS-697 was merged
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['sky'] = {'transit_offset': {'from': -3599, 'to': 43200}}
         self.scheduling_unit_blueprint.save()
         timestamp = datetime(2020, 1, 1, 12, 0, 0)
         returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration))
         self.assertFalse(returned_value)
 
         # window before
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'transit_offset': {'from': -43200, 'to': -3601}}  # todo: use blueprint contraints after TMSS-697 was merged
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['sky'] = {'transit_offset': {'from': -43200, 'to': -3601}}
         self.scheduling_unit_blueprint.save()
         timestamp = datetime(2020, 1, 1, 12, 0, 0)
         returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration))
@@ -910,7 +938,7 @@ class TestSkyConstraints(unittest.TestCase):
         # obs middle is 13h, so we have an offset of -7200 seconds
 
         self.target_transit_mock.side_effect = self.target_transit_data_saps
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'transit_offset': {'from': -7201, 'to': -7199}}  # todo: use blueprint contraints after TMSS-697 was merged
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['sky'] = {'transit_offset': {'from': -7201, 'to': -7199}}
         self.scheduling_unit_blueprint.requirements_doc['tasks']['Observation']['specifications_doc']['antenna_set'] = 'LBA_INNER'
         self.scheduling_unit_blueprint.requirements_doc['tasks']['Observation']['specifications_doc']['SAPs'] = \
             [{'name': 'CygA', 'target': 'CygA', 'subbands': [0, 1], 'digital_pointing': {'angle1': 5.233660650313663, 'angle2': 0.7109404782526458, 'direction_type': 'J2000'}},
@@ -1745,6 +1773,467 @@ class TestReservedStations(unittest.TestCase):
         self.assertTrue(can_run_within_station_reservations(self.scheduling_unit_blueprint))
 
 
+class TestTriggers(TestCase):
+    """
+    Tests for scheduling behavior of triggered observations
+    """
+    def setUp(self):
+
+        # wipe all radb entries (via cascading deletes) in between tests, so the tests don't influence each other
+        with PostgresDatabaseConnection(tmss_test_env.ra_test_environment.radb_test_instance.dbcreds) as radb:
+            radb.executeQuery('DELETE FROM resource_allocation.specification;')
+            radb.executeQuery('TRUNCATE resource_allocation.resource_usage;')
+            radb.commit()
+
+        # wipe all scheduling_unit_drafts in between tests, so the tests don't influence each other
+        for scheduling_set in models.SchedulingSet.objects.all():
+            for scheduling_unit_draft in scheduling_set.scheduling_unit_drafts.all():
+                for scheduling_unit_blueprint in scheduling_unit_draft.scheduling_unit_blueprints.all():
+                    for task_blueprint in scheduling_unit_blueprint.task_blueprints.all():
+                        for subtask in task_blueprint.subtasks.all():
+                            try:
+                                if subtask.state.value == models.SubtaskState.Choices.SCHEDULED.value:
+                                    unschedule_subtask(subtask)
+                            except Exception as e:
+                                logger.exception(e)
+                            for output in subtask.outputs.all():
+                                for dataproduct in output.dataproducts.all():
+                                    dataproduct.delete()
+                                for consumer in output.consumers.all():
+                                    consumer.delete()
+                                output.delete()
+                            for input in subtask.inputs.all():
+                                input.delete()
+                            subtask.delete()
+                        task_blueprint.delete()
+                    scheduling_unit_blueprint.delete()
+                scheduling_unit_draft.delete()
+
+        # create a scheduling set in a project that allows triggers
+        self.scheduling_set = models.SchedulingSet.objects.create(**SchedulingSet_test_data())
+        self.scheduling_set.project.can_trigger = True
+        self.scheduling_set.project.save()
+
+        # create a second scheduling set in a project that allows triggers and has higher trigger_priority
+        self.scheduling_set_high_trigger_priority = models.SchedulingSet.objects.create(**SchedulingSet_test_data())
+        self.scheduling_set_high_trigger_priority.project.can_trigger = True
+        self.scheduling_set_high_trigger_priority.project.trigger_priority = self.scheduling_set_high_trigger_priority.project.trigger_priority + 1
+        self.scheduling_set_high_trigger_priority.project.save()
+
+        self.rarpc_patcher = mock.patch('lofar.sas.resourceassignment.resourceassignmentservice.rpc.RADBRPC')
+        self.addCleanup(self.rarpc_patcher.stop)
+        self.rarpc_mock = self.rarpc_patcher.start()
+        self.rarpc_mock.getTasks.return_value = []
+
+    def test_simple_triggered_scheduling_unit_gets_scheduled(self):
+
+        scheduling_unit_draft = TestDynamicScheduling.create_simple_observation_scheduling_unit(
+                                    "triggered scheduling unit for %s" % self._testMethodName,
+                                    scheduling_set=self.scheduling_set,
+                                    is_triggered=True)
+        triggered_scheduling_unit_blueprint = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft)
+
+        scheduled_scheduling_unit = do_dynamic_schedule()
+
+        # Assert the scheduling_unit has been scheduled
+        self.assertIsNotNone(scheduled_scheduling_unit)
+        self.assertEqual(scheduled_scheduling_unit.id, triggered_scheduling_unit_blueprint.id)
+        self.assertEqual(scheduled_scheduling_unit.status, 'scheduled')
+
+    def test_triggered_scheduling_unit_with_at_constraint_gets_scheduled_at_correct_time(self):
+
+        scheduling_unit_draft = TestDynamicScheduling.create_simple_observation_scheduling_unit(
+                                    'scheduling_unit for at %s' % self._testMethodName,
+                                    scheduling_set=self.scheduling_set)
+        # Clear constraints
+        scheduling_unit_draft.scheduling_constraints_doc['sky'] = {}
+        scheduling_unit_draft.scheduling_constraints_doc['time']["between"] = []
+        scheduling_unit_draft.scheduling_constraints_doc['time']["not_between"] = []
+        scheduling_unit_draft.scheduling_constraints_doc['time'].pop('at', None)
+        scheduling_unit_draft.scheduling_constraints_doc['time'].pop("before", None)
+        scheduling_unit_draft.scheduling_constraints_doc['time'].pop('after', None)
+        # Set at constraint
+        at = round_to_second_precision(datetime.utcnow() + timedelta(minutes=17))
+        scheduling_unit_draft.scheduling_constraints_doc['time']['at'] = at.isoformat()
+        scheduling_unit_draft.save()
+        triggered_scheduling_unit_blueprint = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft)
+
+        scheduled_scheduling_unit = do_dynamic_schedule()
+
+        # Assert the scheduling_unit has been scheduled and assert is has been scheduled at "at" timestamp
+        self.assertIsNotNone(scheduled_scheduling_unit)
+        self.assertEqual(scheduled_scheduling_unit.id, triggered_scheduling_unit_blueprint.id)
+        self.assertEqual(scheduled_scheduling_unit.status, 'scheduled')
+        self.assertEqual(scheduled_scheduling_unit.start_time, at)
+
+    def test_triggered_scheduling_unit_has_priority_over_regular_observation(self):
+
+        scheduling_unit_draft = TestDynamicScheduling.create_simple_observation_scheduling_unit(
+                                    "scheduling unit for %s" % self._testMethodName,
+                                    scheduling_set=self.scheduling_set,
+                                    is_triggered=False)
+        regular_scheduling_unit_blueprint = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft)
+
+        scheduling_unit_draft = TestDynamicScheduling.create_simple_observation_scheduling_unit(
+                                    "triggered scheduling unit for %s" % self._testMethodName,
+                                    scheduling_set=self.scheduling_set,
+                                    is_triggered=True)
+        triggered_scheduling_unit_blueprint = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft)
+
+        scheduled_scheduling_unit = do_dynamic_schedule()
+
+        # Assert the triggered scheduling_unit has been scheduled
+        self.assertIsNotNone(scheduled_scheduling_unit)
+        self.assertEqual(scheduled_scheduling_unit.id, triggered_scheduling_unit_blueprint.id)
+        self.assertEqual(regular_scheduling_unit_blueprint.status, 'schedulable')
+        self.assertEqual(triggered_scheduling_unit_blueprint.status, 'scheduled')
+
+    def test_triggered_scheduling_unit_unschedules_regular_observation(self):
+
+        # create a regular scheduling_unit
+        scheduling_unit_draft = TestDynamicScheduling.create_simple_observation_scheduling_unit(
+                                    "scheduling unit for %s" % self._testMethodName,
+                                    scheduling_set=self.scheduling_set,
+                                    is_triggered=False)
+        regular_scheduling_unit_blueprint = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft)
+        regular_scheduling_unit_blueprint.scheduling_constraints_doc = {}
+        regular_scheduling_unit_blueprint.save()
+
+        scheduled_scheduling_unit = do_dynamic_schedule()
+
+        # Assert the scheduling_unit has been scheduled
+        self.assertIsNotNone(scheduled_scheduling_unit)
+        self.assertEqual(scheduled_scheduling_unit.id, regular_scheduling_unit_blueprint.id)
+        self.assertEqual(regular_scheduling_unit_blueprint.status, 'scheduled')
+
+        # add a triggered scheduling_unit
+        scheduling_unit_draft = TestDynamicScheduling.create_simple_observation_scheduling_unit(
+                                    "triggered scheduling unit for %s" % self._testMethodName,
+                                    scheduling_set=self.scheduling_set,
+                                    is_triggered=True)
+        triggered_scheduling_unit_blueprint = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft)
+        triggered_scheduling_unit_blueprint.scheduling_constraints_doc = {}
+        triggered_scheduling_unit_blueprint.save()
+
+        scheduled_scheduling_unit = do_dynamic_schedule()
+
+        # Assert now the new triggered scheduling_unit has been scheduled, and the regular one has been unscheduled
+        self.assertIsNotNone(scheduled_scheduling_unit)
+        self.assertEqual(scheduled_scheduling_unit.id, triggered_scheduling_unit_blueprint.id)
+        self.assertEqual(regular_scheduling_unit_blueprint.status, 'schedulable')
+        self.assertEqual(triggered_scheduling_unit_blueprint.status, 'scheduled')
+
+    @mock.patch("lofar.sas.tmss.services.scheduling.dynamic_scheduling.cancel_subtask")
+    def test_triggered_scheduling_unit_cancels_regular_observation(self, cancel_mock):
+
+        # create a regular scheduling_unit
+        scheduling_unit_draft = TestDynamicScheduling.create_simple_observation_scheduling_unit(
+                                    "scheduling unit for %s" % self._testMethodName,
+                                    scheduling_set=self.scheduling_set,
+                                    is_triggered=False)
+        regular_scheduling_unit_blueprint = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft)
+        regular_scheduling_unit_blueprint.scheduling_constraints_doc = {}
+        regular_scheduling_unit_blueprint.save()
+
+        scheduled_scheduling_unit = do_dynamic_schedule()
+
+        # Assert the scheduling_unit has been scheduled
+        self.assertIsNotNone(scheduled_scheduling_unit)
+        self.assertEqual(scheduled_scheduling_unit.id, regular_scheduling_unit_blueprint.id)
+        self.assertEqual(regular_scheduling_unit_blueprint.status, 'scheduled')
+
+        # put obs to started state
+        subtask = scheduled_scheduling_unit.task_blueprints.first().subtasks.first()
+        subtask.state = models.SubtaskState.objects.get(value='starting')
+        subtask.save()
+        subtask.state = models.SubtaskState.objects.get(value='started')
+        subtask.save()
+
+        # assert obs it detected as running
+        running_subtasks = get_running_observation_subtasks()
+        self.assertIn(subtask, running_subtasks)
+
+        # also assert cut-off date is considered
+        running_subtasks = get_running_observation_subtasks(subtask.stop_time + timedelta(minutes=5))
+        self.assertNotIn(subtask, running_subtasks)
+
+        # add a triggered scheduling_unit with higher priority
+        scheduling_unit_draft = TestDynamicScheduling.create_simple_observation_scheduling_unit(
+                                    "triggered scheduling unit for %s" % self._testMethodName,
+                                    scheduling_set=self.scheduling_set_high_trigger_priority,
+                                    is_triggered=True)
+        triggered_scheduling_unit_blueprint = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft)
+        triggered_scheduling_unit_blueprint.scheduling_constraints_doc = {}
+        triggered_scheduling_unit_blueprint.save()
+
+        # wipe all radb entries (via cascading deletes) so that we don't get a conflict later because we omöy mock
+        # the cancellation.
+        # todo: TMSS-704: if we don't do this, the triggered SU goes in error state (conflict due to the cancel being
+        #  mocked?) - confirm that's ok.
+        with PostgresDatabaseConnection(tmss_test_env.ra_test_environment.radb_test_instance.dbcreds) as radb:
+            radb.executeQuery('DELETE FROM resource_allocation.specification;')
+            radb.executeQuery('TRUNCATE resource_allocation.resource_usage;')
+            radb.commit()
+
+        scheduled_scheduling_unit = do_dynamic_schedule()
+
+        # assert the subtask has been cancelled
+        cancel_mock.assert_called_with(subtask)
+
+        # Note that we cannot check that the regular_scheduling_unit_blueprint or the subtask has been cancelled since
+        # we only mocked the cancellation.
+
+        # Assert now the new triggered scheduling_unit has been scheduled
+        # todo: TMSS-704: We should only cancel if the trigger cannot run afterwards due to constraints.
+        #  Add such constraints once the scheduler considers that, since that will break this test.
+        self.assertIsNotNone(scheduled_scheduling_unit)
+        self.assertEqual(scheduled_scheduling_unit.id, triggered_scheduling_unit_blueprint.id)
+        self.assertEqual(triggered_scheduling_unit_blueprint.status, 'scheduled')
+
+    @mock.patch("lofar.sas.tmss.services.scheduling.dynamic_scheduling.cancel_subtask")
+    def test_triggered_scheduling_unit_does_not_cancel_regular_observation_with_same_trigger_priority(self, cancel_mock):
+
+        # create a regular scheduling_unit
+        scheduling_unit_draft = TestDynamicScheduling.create_simple_observation_scheduling_unit(
+                                    "scheduling unit for %s" % self._testMethodName,
+                                    scheduling_set=self.scheduling_set,
+                                    is_triggered=False)
+        regular_scheduling_unit_blueprint = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft)
+        regular_scheduling_unit_blueprint.scheduling_constraints_doc = {}
+        regular_scheduling_unit_blueprint.save()
+
+        scheduled_scheduling_unit = do_dynamic_schedule()
+
+        # Assert the scheduling_unit has been scheduled
+        self.assertIsNotNone(scheduled_scheduling_unit)
+        self.assertEqual(scheduled_scheduling_unit.id, regular_scheduling_unit_blueprint.id)
+        self.assertEqual(regular_scheduling_unit_blueprint.status, 'scheduled')
+
+        # put obs to started state
+        subtask = scheduled_scheduling_unit.task_blueprints.first().subtasks.first()
+        subtask.state = models.SubtaskState.objects.get(value='starting')
+        subtask.save()
+        subtask.state = models.SubtaskState.objects.get(value='started')
+        subtask.save()
+
+        # assert obs it detected as running
+        running_subtasks = get_running_observation_subtasks()
+        self.assertIn(subtask, running_subtasks)
+
+        # also assert cut-off date is considered
+        running_subtasks = get_running_observation_subtasks(subtask.stop_time + timedelta(minutes=5))
+        self.assertNotIn(subtask, running_subtasks)
+
+        # add a triggered scheduling_unit with same trigger priority
+        scheduling_unit_draft = TestDynamicScheduling.create_simple_observation_scheduling_unit(
+                                    "triggered scheduling unit for %s" % self._testMethodName,
+                                    scheduling_set=self.scheduling_set,
+                                    is_triggered=True)
+        triggered_scheduling_unit_blueprint = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft)
+        triggered_scheduling_unit_blueprint.scheduling_constraints_doc = {}
+        triggered_scheduling_unit_blueprint.save()
+
+        scheduled_scheduling_unit = do_dynamic_schedule()
+
+        # assert that the subtask has NOT been cancelled and is still in state 'started'
+        cancel_mock.assert_not_called()
+        self.assertEqual(subtask.state.value, 'started')
+
+        # Assert that the new triggered scheduling_unit has NOT been scheduled, and the regular one is still observing
+        self.assertIsNone(scheduled_scheduling_unit)
+        self.assertEqual(regular_scheduling_unit_blueprint.status, 'observing')
+        #self.assertEqual(triggered_scheduling_unit_blueprint.status, 'schedulable')  # todo: TMSS-704: Make this pass. Currently goes to error state
+
+    @mock.patch("lofar.sas.tmss.services.scheduling.dynamic_scheduling.cancel_subtask")
+    def test_triggered_scheduling_unit_does_not_cancel_regular_observation_if_it_cannot_run_anyway(self, cancel_mock):
+
+        # create a regular scheduling_unit
+        scheduling_unit_draft = TestDynamicScheduling.create_simple_observation_scheduling_unit(
+                                    "scheduling unit for %s" % self._testMethodName,
+                                    scheduling_set=self.scheduling_set,
+                                    is_triggered=False)
+        regular_scheduling_unit_blueprint = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft)
+        regular_scheduling_unit_blueprint.scheduling_constraints_doc = {}
+        regular_scheduling_unit_blueprint.save()
+
+        scheduled_scheduling_unit = do_dynamic_schedule()
+
+        # Assert the scheduling_unit has been scheduled
+        self.assertIsNotNone(scheduled_scheduling_unit)
+        self.assertEqual(scheduled_scheduling_unit.id, regular_scheduling_unit_blueprint.id)
+        self.assertEqual(regular_scheduling_unit_blueprint.status, 'scheduled')
+
+        # put obs to started state
+        subtask = scheduled_scheduling_unit.task_blueprints.first().subtasks.first()
+        subtask.state = models.SubtaskState.objects.get(value='starting')
+        subtask.save()
+        subtask.state = models.SubtaskState.objects.get(value='started')
+        subtask.save()
+
+        # assert obs it detected as running
+        running_subtasks = get_running_observation_subtasks()
+        self.assertIn(subtask, running_subtasks)
+
+        # also assert cut-off date is considered
+        running_subtasks = get_running_observation_subtasks(subtask.stop_time + timedelta(minutes=5))
+        self.assertNotIn(subtask, running_subtasks)
+
+        # add a triggered scheduling_unit with higher priority, but a between constraint that can never be met
+        scheduling_unit_draft = TestDynamicScheduling.create_simple_observation_scheduling_unit(
+                                    "triggered scheduling unit for %s" % self._testMethodName,
+                                    scheduling_set=self.scheduling_set_high_trigger_priority,
+                                    is_triggered=True)
+        triggered_scheduling_unit_blueprint = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft)
+        triggered_scheduling_unit_blueprint.scheduling_constraints_doc = {'time': {'between': [{"from": datetime.utcnow().isoformat(), "to": (datetime.utcnow()+timedelta(minutes=10)).isoformat()},]}}
+        triggered_scheduling_unit_blueprint.save()
+
+        scheduled_scheduling_unit = do_dynamic_schedule()
+
+        # assert that the subtask has NOT been cancelled and is still in state 'started'
+        #cancel_mock.assert_not_called()
+        self.assertEqual(subtask.state.value, 'started')
+
+        # Assert that the new triggered scheduling_unit has NOT been scheduled, and the regular one is still observing
+        self.assertIsNone(scheduled_scheduling_unit)
+        self.assertEqual(regular_scheduling_unit_blueprint.status, 'observing')
+        #self.assertEqual(triggered_scheduling_unit_blueprint.status, 'schedulable')  # todo: TMSS-704: Make this pass. Currently goes to error state.
+
+    @mock.patch("lofar.sas.tmss.services.scheduling.dynamic_scheduling.cancel_subtask")
+    def test_triggered_scheduling_unit_gets_scheduled_in_correct_trigger_priority_order(self, cancel_mock):
+
+        # create three regular scheduling_units, two with high trigger priority, one with lower
+        scheduling_unit_draft_high1 = TestDynamicScheduling.create_simple_observation_scheduling_unit(
+                                    "scheduling unit for %s" % self._testMethodName,
+                                    scheduling_set=self.scheduling_set_high_trigger_priority,
+                                    is_triggered=False)
+        regular_scheduling_unit_blueprint_high1 = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft_high1)
+        regular_scheduling_unit_blueprint_high1.scheduling_constraints_doc = {}
+        regular_scheduling_unit_blueprint_high1.save()
+
+        scheduling_unit_draft_high2 = TestDynamicScheduling.create_simple_observation_scheduling_unit(
+                                    "scheduling unit for %s" % self._testMethodName,
+                                    scheduling_set=self.scheduling_set_high_trigger_priority,
+                                    is_triggered=False)
+        regular_scheduling_unit_blueprint_high2 = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft_high2)
+        regular_scheduling_unit_blueprint_high2.scheduling_constraints_doc = {}
+        regular_scheduling_unit_blueprint_high2.save()
+
+        scheduling_unit_draft_low = TestDynamicScheduling.create_simple_observation_scheduling_unit(
+                                    "scheduling unit for %s" % self._testMethodName,
+                                    scheduling_set=self.scheduling_set,
+                                    is_triggered=False)
+        regular_scheduling_unit_blueprint_low = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft_low)
+        regular_scheduling_unit_blueprint_low.scheduling_constraints_doc = {}
+        regular_scheduling_unit_blueprint_low.save()
+
+
+        scheduled_scheduling_unit = do_dynamic_schedule()
+
+        # Assert the scheduling_unit has been scheduled
+        self.assertIsNotNone(scheduled_scheduling_unit)
+        self.assertEqual(scheduled_scheduling_unit.id, regular_scheduling_unit_blueprint_high1.id)
+        self.assertEqual(regular_scheduling_unit_blueprint_high1.status, 'scheduled')
+
+        # put first obs to started state
+        subtask = scheduled_scheduling_unit.task_blueprints.first().subtasks.first()
+        subtask.state = models.SubtaskState.objects.get(value='starting')
+        subtask.save()
+        subtask.state = models.SubtaskState.objects.get(value='started')
+        subtask.save()
+
+        # add a triggered scheduling_unit with same priority
+        scheduling_unit_draft_trigger = TestDynamicScheduling.create_simple_observation_scheduling_unit(
+                                    "triggered scheduling unit for %s" % self._testMethodName,
+                                    scheduling_set=self.scheduling_set_high_trigger_priority,
+                                    is_triggered=True)
+        triggered_scheduling_unit_blueprint = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft_trigger)
+        triggered_scheduling_unit_blueprint.scheduling_constraints_doc = {}
+        triggered_scheduling_unit_blueprint.save()
+
+        scheduled_scheduling_unit = do_dynamic_schedule()
+
+        # assert that the subtask has NOT been cancelled and is still in state 'started', and its SU is observing
+        cancel_mock.assert_not_called()
+        self.assertEqual(subtask.state.value, 'started')
+        self.assertEqual(regular_scheduling_unit_blueprint_high1.status, 'observing')
+
+        # Assert that the new triggered scheduling_unit has been scheduled, and starts in between the same and lower
+        # priority units
+        self.assertIsNotNone(scheduled_scheduling_unit)
+        #self.assertEqual(triggered_scheduling_unit_blueprint.status, 'scheduled')  # todo: TMSS-704: Make this pass. Currently goes to error state
+        self.assertEqual(regular_scheduling_unit_blueprint_high2.status, 'scheduled')
+        self.assertEqual(regular_scheduling_unit_blueprint_low.status, 'schedulable')  # todo: why high2 gets scheduled, but this is only schedulable?
+        self.assertGreater(regular_scheduling_unit_blueprint_low.start_time, triggered_scheduling_unit_blueprint.stop_time)
+        #self.assertGreater(triggered_scheduling_unit_blueprint.start_time, regular_scheduling_unit_blueprint_high2.stop_time)  # todo: TMSS-704: Make this pass. Currently starts after high1, but unexpectedly before high2
+        self.assertGreater(regular_scheduling_unit_blueprint_high2.start_time, regular_scheduling_unit_blueprint_high1.stop_time)
+
+    @mock.patch("lofar.sas.tmss.services.scheduling.dynamic_scheduling.cancel_subtask")
+    def test_triggered_scheduling_unit_goes_to_unschedulable_if_it_cannot_cancel_and_does_not_fit(self, cancel_mock):
+
+        # create three regular scheduling_units, two with high trigger priority, one with lower
+        scheduling_unit_draft_high1 = TestDynamicScheduling.create_simple_observation_scheduling_unit(
+                                    "scheduling unit for %s" % self._testMethodName,
+                                    scheduling_set=self.scheduling_set_high_trigger_priority,
+                                    is_triggered=False)
+        regular_scheduling_unit_blueprint_high1 = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft_high1)
+        regular_scheduling_unit_blueprint_high1.scheduling_constraints_doc = {}
+        regular_scheduling_unit_blueprint_high1.save()
+
+        scheduling_unit_draft_high2 = TestDynamicScheduling.create_simple_observation_scheduling_unit(
+                                    "scheduling unit for %s" % self._testMethodName,
+                                    scheduling_set=self.scheduling_set_high_trigger_priority,
+                                    is_triggered=False)
+        regular_scheduling_unit_blueprint_high2 = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft_high2)
+        regular_scheduling_unit_blueprint_high2.scheduling_constraints_doc = {}
+        regular_scheduling_unit_blueprint_high2.save()
+
+        scheduling_unit_draft_low = TestDynamicScheduling.create_simple_observation_scheduling_unit(
+                                    "scheduling unit for %s" % self._testMethodName,
+                                    scheduling_set=self.scheduling_set,
+                                    is_triggered=False)
+        regular_scheduling_unit_blueprint_low = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft_low)
+        regular_scheduling_unit_blueprint_low.scheduling_constraints_doc = {}
+        regular_scheduling_unit_blueprint_low.save()
+
+
+        scheduled_scheduling_unit = do_dynamic_schedule()
+
+        # Assert the scheduling_unit has been scheduled
+        self.assertIsNotNone(scheduled_scheduling_unit)
+        self.assertEqual(scheduled_scheduling_unit.id, regular_scheduling_unit_blueprint_high1.id)
+        self.assertEqual(regular_scheduling_unit_blueprint_high1.status, 'scheduled')
+
+        # put first obs to started state
+        subtask = scheduled_scheduling_unit.task_blueprints.first().subtasks.first()
+        subtask.state = models.SubtaskState.objects.get(value='starting')
+        subtask.save()
+        subtask.state = models.SubtaskState.objects.get(value='started')
+        subtask.save()
+
+        # add a triggered scheduling_unit with same trigger priority, and a between constraint that can only be met
+        # when the regular obs would be cancelled (which is not allowed because it requires higher trigger priority).
+        scheduling_unit_draft_trigger = TestDynamicScheduling.create_simple_observation_scheduling_unit(
+                                    "triggered scheduling unit for %s" % self._testMethodName,
+                                    scheduling_set=self.scheduling_set_high_trigger_priority,
+                                    is_triggered=True)
+        triggered_scheduling_unit_blueprint = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft_trigger)
+        triggered_scheduling_unit_blueprint.scheduling_constraints_doc = {'time': {'between': [{"from": datetime.utcnow().isoformat(), "to": (datetime.utcnow()+timedelta(hours=3)).isoformat()},]}}
+        triggered_scheduling_unit_blueprint.save()
+
+        scheduled_scheduling_unit = do_dynamic_schedule()
+
+        # assert that the subtask has NOT been cancelled and is still in state 'started', and its SU is observing
+        cancel_mock.assert_not_called()
+        self.assertEqual(subtask.state.value, 'started')
+        self.assertEqual(regular_scheduling_unit_blueprint_high1.status, 'observing')
+
+        # Assert that the new triggered scheduling_unit has NOT been scheduled and regular observations remain scheduled
+        self.assertIsNotNone(scheduled_scheduling_unit)
+        #self.assertEqual(triggered_scheduling_unit_blueprint.status, 'unschedulable')  # todo: TMSS-704: Make this pass. Currently goes to error state
+        self.assertEqual(regular_scheduling_unit_blueprint_high2.status, 'scheduled')
+        self.assertEqual(regular_scheduling_unit_blueprint_low.status, 'schedulable')  # todo: why high2 gets scheduled, but this is only schedulable?
+
+
 logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', level=logging.INFO)
 
 if __name__ == '__main__':
diff --git a/SAS/TMSS/backend/src/tmss/CMakeLists.txt b/SAS/TMSS/backend/src/tmss/CMakeLists.txt
index 3e7754777f2f6d34a58352c9d78765303dd9cfa4..eff8df62cd8cfc0c40da5a5e6405453f6ea53509 100644
--- a/SAS/TMSS/backend/src/tmss/CMakeLists.txt
+++ b/SAS/TMSS/backend/src/tmss/CMakeLists.txt
@@ -7,6 +7,7 @@ set(_py_files
     urls.py
     wsgi.py
     exceptions.py
+    authentication_backends.py
     )
 
 python_install(${_py_files}
diff --git a/SAS/TMSS/backend/src/tmss/authentication_backends.py b/SAS/TMSS/backend/src/tmss/authentication_backends.py
new file mode 100644
index 0000000000000000000000000000000000000000..4eba2a64b9e20c8b9c148ece41838dbacc2e1f78
--- /dev/null
+++ b/SAS/TMSS/backend/src/tmss/authentication_backends.py
@@ -0,0 +1,73 @@
+from mozilla_django_oidc.auth import OIDCAuthenticationBackend
+import logging
+from lofar.sas.tmss.tmss.tmssapp.models import ProjectRole
+from django.contrib.auth.models import Group
+
+logger = logging.getLogger(__name__)
+
+class TMSSOIDCAuthenticationBackend(OIDCAuthenticationBackend):
+    """
+    A custom OIDCAuthenticationBackend, that allows us to perform extra actions when a user gets authenticated,
+    most importantly we can assign the user's system and project roles according to the claims that we get from the
+    identity provider.
+    """
+
+    def _set_user_project_roles_from_claims(self, user, claims):
+        project_roles = []
+        project_role_prefix = 'urn:mace:astron.nl:science:group:lofar:project:'
+        project_role_map = {'contact': 'contact_author'}
+        for entitlement in claims.get('eduperson_entitlement', []):
+            try:
+                if entitlement.startswith(project_role_prefix):
+                    project_entitlement = entitlement.replace(project_role_prefix, '')
+                    if 'role' in project_entitlement:
+                        project_name, role_name = project_entitlement.split(':role=')
+                        if role_name in project_role_map:
+                            role_name = project_role_map[role_name]
+                        if ProjectRole.objects.filter(value=role_name).count() > 0:
+                            project_roles.append({'project': project_name, 'role': role_name})
+                        else:
+                            logger.error('could not handle entitlement=%s because no project role exists that matches the entitlement role=%s' % (entitlement, role_name))
+                    else:
+                        # we only care about explicit roles, 'general' membership of a project is not relevant to TMSS
+                        pass
+            except Exception as e:
+                logger.error('could not handle entitlement=%s because of exception=%s' % (entitlement, e))
+        user.project_roles = project_roles
+        logger.info("### assigned project_roles=%s to user=%s" % (project_roles, user))
+        user.save()
+
+    def _set_user_system_roles_from_claims(self, user, claims):
+        groups = []
+        system_role_prefix = 'urn:mace:astron.nl:science:group:lofar:role='
+        system_role_map = {'expert scientist': Group.objects.get(name='Scientist (Expert)')}   # usually, we can use the entitlement name as group name, but some groups have weird names and need to be translated
+        for entitlement in claims.get('eduperson_entitlement', []):
+            try:
+                if entitlement.startswith(system_role_prefix):
+                    role_name = entitlement.replace(system_role_prefix, '')
+                    if role_name in system_role_map:
+                        groups.append(system_role_map[role_name])
+                    else:
+                        role_name = role_name.replace('_', ' ')
+                        if Group.objects.filter(name__iexact=role_name).count() > 0:
+                            groups.append(Group.objects.filter(name__iexact=role_name).first())
+                        else:
+                            logger.error('could not handle entitlement=%s because no system role / group exists that matches the entitlement role=%s' % (entitlement, role_name))
+            except Exception as e:
+                logger.error('could not handle entitlement=%s because of exception=%s' % (entitlement, e))
+        logger.info("### assigned groups=%s to user=%s" % (groups, user))
+        user.groups.set(groups)
+        user.save()
+
+    def create_user(self, claims):
+        user = super(TMSSOIDCAuthenticationBackend, self).create_user(claims)
+        logger.info('### create user=%s claims=%s' % (user, claims))
+        self._set_user_project_roles_from_claims(user, claims)
+        self._set_user_system_roles_from_claims(user, claims)
+        return user
+
+    def update_user(self, user, claims):
+        logger.info('### update user=%s claims=%s' % (user, claims))
+        self._set_user_project_roles_from_claims(user, claims)
+        self._set_user_system_roles_from_claims(user, claims)
+        return user
\ No newline at end of file
diff --git a/SAS/TMSS/backend/src/tmss/settings.py b/SAS/TMSS/backend/src/tmss/settings.py
index 528ae22e586a4dc02e89d87ba8f232e64584db28..5ac185475abc07af34fba570389d8c7e9ad4a044 100644
--- a/SAS/TMSS/backend/src/tmss/settings.py
+++ b/SAS/TMSS/backend/src/tmss/settings.py
@@ -222,11 +222,12 @@ REST_FRAMEWORK = {
 # AUTHENTICATION: simple LDAP, or OpenID, or both
 
 AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',)
+AUTH_USER_MODEL = 'tmssapp.TMSSUser'
 
 if "TMSS_LDAPCREDENTIALS" in os.environ.keys():
     # plain LDAP
     import ldap
-
+    logger.info('Authenticating against LDAP is enabled')
     REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'].append('rest_framework.authentication.BasicAuthentication')
     REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'].append('rest_framework.authentication.SessionAuthentication')
     REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'].append('rest_framework.authentication.TokenAuthentication')
@@ -254,7 +255,7 @@ if "TMSS_LDAPCREDENTIALS" in os.environ.keys():
 
 
 if "OIDC_RP_CLIENT_ID" in os.environ.keys():
-
+    logger.info('Authenticating against Keycloak is enabled')
     INSTALLED_APPS.append('mozilla_django_oidc')  # Load after auth
     REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'].append('mozilla_django_oidc.contrib.drf.OIDCAuthentication')
     REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'].append('rest_framework.authentication.SessionAuthentication')
@@ -263,17 +264,21 @@ if "OIDC_RP_CLIENT_ID" in os.environ.keys():
 
     # OPEN-ID CONNECT
     OIDC_DRF_AUTH_BACKEND = 'mozilla_django_oidc.auth.OIDCAuthenticationBackend'
-    # For talking to Mozilla Identity Provider:
-    OIDC_RP_SCOPES = "openid email profile"  # todo: groups are not a standard scope, how to handle those?
-    OIDC_RP_CLIENT_ID = os.environ.get('OIDC_RP_CLIENT_ID', '2')  # Secret, do not put real credentials on Git
-    OIDC_RP_CLIENT_SECRET = os.environ.get('OIDC_RP_CLIENT_SECRET',
-                                           'secret')  # Secret, do not put real credentials on Git
-    OIDC_ENDPOINT_HOST = os.environ.get('OIDC_ENDPOINT_HOST', 'localhost')
-    OIDC_OP_AUTHORIZATION_ENDPOINT = os.environ.get('OIDC_OP_AUTHORIZATION_ENDPOINT', "http://localhost:8088/openid/authorize/")
-    OIDC_OP_TOKEN_ENDPOINT = os.environ.get('OIDC_OP_TOKEN_ENDPOINT', "http://localhost:8088/openid/token/")
-    OIDC_OP_USER_ENDPOINT = os.environ.get('OIDC_OP_USER_ENDPOINT', "http://localhost:8088/openid/userinfo/")
-
-    AUTHENTICATION_BACKENDS += ('mozilla_django_oidc.auth.OIDCAuthenticationBackend',)
+
+    # Defaults are for using the Astron dev OIDC (Keycloak) deployment for SDC
+    # !! Set OIDC_RP_CLIENT_ID and OIDC_RP_CLIENT_SECRET in environment (todo: use dbcredentials to configure as we have for LDAP?)
+    OIDC_RP_SCOPES = "openid email profile eduperson_entitlement"
+    OIDC_RP_CLIENT_ID = os.environ.get('OIDC_RP_CLIENT_ID', 'secret')  # Secret, do not put real credentials on Git
+    OIDC_RP_CLIENT_SECRET = os.environ.get('OIDC_RP_CLIENT_SECRET', 'secret')  # Secret, do not put real credentials on Git
+    OIDC_RP_SIGN_ALGO = os.environ.get('OIDC_RP_SIGN_ALGO', 'RS256')
+    OIDC_OP_JWKS_ENDPOINT = os.environ.get('OIDC_OP_JWKS_ENDPOINT', 'https://sdc-dev.astron.nl/auth/realms/master/protocol/openid-connect/certs')
+
+    OIDC_ENDPOINT_HOST = os.environ.get('OIDC_ENDPOINT_HOST', 'https://sdc-dev.astron.nl')
+    OIDC_OP_AUTHORIZATION_ENDPOINT = os.environ.get('OIDC_OP_AUTHORIZATION_ENDPOINT', "https://sdc-dev.astron.nl/auth/realms/master/protocol/openid-connect/auth")
+    OIDC_OP_TOKEN_ENDPOINT = os.environ.get('OIDC_OP_TOKEN_ENDPOINT', "https://sdc-dev.astron.nl/auth/realms/master/protocol/openid-connect/token")
+    OIDC_OP_USER_ENDPOINT = os.environ.get('OIDC_OP_USER_ENDPOINT', "https://sdc-dev.astron.nl/auth/realms/master/protocol/openid-connect/userinfo")
+
+    AUTHENTICATION_BACKENDS += ('lofar.sas.tmss.tmss.authentication_backends.TMSSOIDCAuthenticationBackend',)
     MIDDLEWARE.append('mozilla_django_oidc.middleware.SessionRefresh')
 
 if len(AUTHENTICATION_BACKENDS) == 1:
@@ -282,10 +287,9 @@ if len(AUTHENTICATION_BACKENDS) == 1:
 
 LOGIN_REDIRECT_URL = "/api/"
 LOGIN_REDIRECT_URL_FAILURE = "/api/"
-LOGOUT_REDIRECT_URL = "/api/"
+LOGOUT_REDIRECT_URL = os.environ.get('TMSS_LOGOUT_REDIRECT_URL', "https://sdc-dev.astron.nl/auth/realms/master/account/#/")  # so the user can log out of OpenID provider too
 LOGOUT_REDIRECT_URL_FAILURE = "/api/"
 
-
 # Password validation
 # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators
 
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/admin.py b/SAS/TMSS/backend/src/tmss/tmssapp/admin.py
index 8c38f3f3dad51e4585f3984282c2a4bec5349c1e..a4e0d5b36c7528edfbcbe9a8404b04a5ef661d5c 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/admin.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/admin.py
@@ -1,3 +1,7 @@
 from django.contrib import admin
 
-# Register your models here.
+from django.contrib import admin
+from django.contrib.auth.admin import UserAdmin
+from .models import TMSSUser
+
+admin.site.register(TMSSUser, UserAdmin)
\ No newline at end of file
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0001_initial.py b/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0001_initial.py
index 0110ee6adaa831b503f1b8b42ea28f0ed7a6d0d2..bc33eae30f700b063b914923a9912c6f5aa98f75 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0001_initial.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0001_initial.py
@@ -1,13 +1,19 @@
-# Generated by Django 3.0.9 on 2021-04-28 21:14
+<<<<<<< HEAD
+# Generated by Django 3.0.9 on 2021-05-04 15:32
+=======
+# Generated by Django 3.0.9 on 2021-05-18 08:28
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
 
 from django.conf import settings
+import django.contrib.auth.models
+import django.contrib.auth.validators
 import django.contrib.postgres.fields
 import django.contrib.postgres.fields.jsonb
 import django.contrib.postgres.indexes
 from django.db import migrations, models
 import django.db.models.deletion
+import django.utils.timezone
 import lofar.sas.tmss.tmss.tmssapp.models.common
-import lofar.sas.tmss.tmss.tmssapp.models.specification
 
 
 class Migration(migrations.Migration):
@@ -15,10 +21,39 @@ class Migration(migrations.Migration):
     initial = True
 
     dependencies = [
-        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('auth', '0011_update_proxy_permissions'),
     ]
 
     operations = [
+        migrations.CreateModel(
+            name='TMSSUser',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('password', models.CharField(max_length=128, verbose_name='password')),
+                ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
+                ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
+                ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
+                ('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
+                ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
+                ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
+                ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
+                ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
+                ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
+<<<<<<< HEAD
+                ('project_roles', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True, help_text='A list of structures that contain a project name and project role')),
+=======
+                ('project_roles', django.contrib.postgres.fields.jsonb.JSONField(blank=True, help_text='A list of structures that contain a project name and project role', null=True)),
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+            ],
+            options={
+                'verbose_name': 'user',
+                'verbose_name_plural': 'users',
+                'abstract': False,
+            },
+            managers=[
+                ('objects', django.contrib.auth.models.UserManager()),
+            ],
+        ),
         migrations.CreateModel(
             name='AntennaSet',
             fields=[
@@ -621,6 +656,7 @@ class Migration(migrations.Migration):
                 ('piggyback_allowed_aartfaac', models.BooleanField(help_text='Piggyback key for AARTFAAC.', null=True)),
                 ('priority_rank', models.FloatField(default=0.0, help_text='Priority of this scheduling unit w.r.t. other scheduling units within the same queue and project.')),
                 ('scheduling_constraints_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Scheduling Constraints for this run.', null=True)),
+                ('is_triggered', models.BooleanField(default=False, help_text='boolean (default FALSE), which indicates whether this observation was triggered (responsive telescope)')),
             ],
             options={
                 'abstract': False,
@@ -643,6 +679,7 @@ class Migration(migrations.Migration):
                 ('piggyback_allowed_tbb', models.BooleanField(help_text='Piggyback key for TBB.', null=True)),
                 ('piggyback_allowed_aartfaac', models.BooleanField(help_text='Piggyback key for AARTFAAC.', null=True)),
                 ('priority_rank', models.FloatField(default=0.0, help_text='Priority of this scheduling unit w.r.t. other scheduling units within the same queue and project.')),
+                ('is_triggered', models.BooleanField(default=False, help_text='boolean (default FALSE), which indicates whether this observation was triggered (responsive telescope)')),
             ],
             options={
                 'abstract': False,
@@ -725,7 +762,7 @@ class Migration(migrations.Migration):
             options={
                 'abstract': False,
             },
-            bases=(models.Model, lofar.sas.tmss.tmss.tmssapp.models.common.TemplateSchemaMixin),
+            bases=(models.Model, lofar.sas.tmss.tmss.tmssapp.models.common.ProjectPropertyMixin, lofar.sas.tmss.tmss.tmssapp.models.common.TemplateSchemaMixin),
         ),
         migrations.CreateModel(
             name='SubtaskAllowedStateTransitions',
@@ -844,13 +881,7 @@ class Migration(migrations.Migration):
             name='TaskConnectorType',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
-                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
-                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
             ],
-            options={
-                'abstract': False,
-            },
         ),
         migrations.CreateModel(
             name='TaskDraft',
@@ -864,7 +895,7 @@ class Migration(migrations.Migration):
                 ('specifications_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Specifications for this task.')),
                 ('output_pinned', models.BooleanField(default=False, help_text='True if the output of this task is pinned to disk, that is, forbidden to be removed.')),
             ],
-            bases=(models.Model, lofar.sas.tmss.tmss.tmssapp.models.specification.ProjectPropertyMixin, lofar.sas.tmss.tmss.tmssapp.models.common.TemplateSchemaMixin),
+            bases=(models.Model, lofar.sas.tmss.tmss.tmssapp.models.common.ProjectPropertyMixin, lofar.sas.tmss.tmss.tmssapp.models.common.TemplateSchemaMixin),
         ),
         migrations.CreateModel(
             name='TaskRelationBlueprint',
@@ -1072,7 +1103,7 @@ class Migration(migrations.Migration):
         migrations.AddField(
             model_name='taskconnectortype',
             name='task_template',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='output_connector_types', to='tmssapp.TaskTemplate'),
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='connector_types', to='tmssapp.TaskTemplate'),
         ),
         migrations.AddField(
             model_name='taskblueprint',
@@ -1510,6 +1541,16 @@ class Migration(migrations.Migration):
             name='station_type',
             field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tmssapp.StationType'),
         ),
+        migrations.AddField(
+            model_name='tmssuser',
+            name='groups',
+            field=models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups'),
+        ),
+        migrations.AddField(
+            model_name='tmssuser',
+            name='user_permissions',
+            field=models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions'),
+        ),
         migrations.AddConstraint(
             model_name='tasktemplate',
             constraint=models.UniqueConstraint(fields=('name', 'version'), name='tasktemplate_unique_name_version'),
@@ -1534,9 +1575,9 @@ class Migration(migrations.Migration):
             model_name='taskdraft',
             constraint=models.UniqueConstraint(fields=('name', 'scheduling_unit_draft'), name='TaskDraft_unique_name_in_scheduling_unit'),
         ),
-        migrations.AddIndex(
+        migrations.AddConstraint(
             model_name='taskconnectortype',
-            index=django.contrib.postgres.indexes.GinIndex(fields=['tags'], name='tmssapp_tas_tags_19ff09_gin'),
+            constraint=models.UniqueConstraint(fields=('role', 'datatype', 'dataformat', 'task_template', 'iotype'), name='task_connector_type_unique_combination'),
         ),
         migrations.AddConstraint(
             model_name='taskblueprint',
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/models/common.py b/SAS/TMSS/backend/src/tmss/tmssapp/models/common.py
index 4eeeb68e1a42963aeabbd1111c7dcd509f0eb781..b36141b60469b4271d08d33dc2ee41780adcd335 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/models/common.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/models/common.py
@@ -14,6 +14,12 @@ from django.urls import reverse as reverse_url
 import json
 import jsonschema
 from datetime import timedelta
+from django.utils.functional import cached_property
+from lofar.sas.tmss.tmss.exceptions import TMSSException
+
+#
+# Mixins
+#
 
 class RefreshFromDbInvalidatesCachedPropertiesMixin():
     """Helper Mixin class which invalidates all 'cached_property' attributes on a model upon refreshing from the db"""
@@ -22,11 +28,30 @@ class RefreshFromDbInvalidatesCachedPropertiesMixin():
         return super().refresh_from_db(*args, **kwargs)
 
     def invalidate_cached_properties(self):
-        from django.utils.functional import cached_property
         for key, value in self.__class__.__dict__.items():
             if isinstance(value, cached_property):
                 self.__dict__.pop(key, None)
 
+
+class ProjectPropertyMixin(RefreshFromDbInvalidatesCachedPropertiesMixin):
+    @cached_property
+    def project(self): # -> Project:
+        '''return the related project of this task
+        '''
+        if not hasattr(self, 'path_to_project'):
+            return TMSSException("Please define a 'path_to_project' attribute on the %s object for the ProjectPropertyMixin to function." % type(self))
+        obj = self
+        for attr in self.path_to_project.split('__'):
+            obj = getattr(obj, attr)
+            if attr == 'project':
+                return obj
+            if obj and not isinstance(obj, Model):  # ManyToMany fields
+                obj = obj.first()
+            if obj is None:
+                logger.warning("The element '%s' in the path_to_project of the %s object returned None for pk=%s" % (attr, type(self), self.pk))
+                return None
+
+
 # abstract models
 
 class BasicCommon(Model):
@@ -203,7 +228,7 @@ class TemplateSchemaMixin():
 
                 # add defaults for missing properies, and validate on the fly
                 # use the class's _schema_cache
-                document = add_defaults_to_json_object_for_schema(document, template.schema, self._schema_cache)
+                document = add_defaults_to_json_object_for_schema(document, template.schema, cache=self._schema_cache, max_cache_age=self._MAX_SCHEMA_CACHE_AGE)
 
             # update the model instance with the updated and validated document
             setattr(self, document_attr, document)
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/models/permissions.py b/SAS/TMSS/backend/src/tmss/tmssapp/models/permissions.py
index 8cdbf52b01eb27e1c3e8467cbba34e4494c37b3d..a0fecb1c75b998531bf323d48a7a6b79298efe81 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/models/permissions.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/models/permissions.py
@@ -10,6 +10,18 @@ from django.db.models import ManyToManyField
 from enum import Enum
 
 from rest_framework.permissions import DjangoModelPermissions
+from django.contrib.auth.models import AbstractUser
+from django.contrib.postgres.fields import JSONField
+
+class TMSSUser(AbstractUser):
+    """
+    A custom user model that allows for additional information on the user like project roles.
+    """
+    # todo: The project roles field feels very free-form at the moment.
+    #  Maybe this can be modeled better somehow, with references to the ProjectRole table?
+    #  Otherwise, we should probably come up with a schema to makes sure things are consistent.
+    #  Also, I'd suggest to simply map project name to a list of roles instead of the structure that is used here.
+    project_roles = JSONField(null=True, blank=True, help_text='A list of structures that contain a project name and project role')  # e.g. [{'project': 'high', 'role': 'PI'}, {'project': 'high', 'role': 'Friend of Project'}]
 
 
 #
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/models/scheduling.py b/SAS/TMSS/backend/src/tmss/tmssapp/models/scheduling.py
index 3fa4cc2134aa7b636f5a8809f0483fc749c2c229..c4d5601db0a305f858711c382ec089293911dd38 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/models/scheduling.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/models/scheduling.py
@@ -11,8 +11,8 @@ from datetime import datetime, timedelta
 from django.db.models import Model, ForeignKey, OneToOneField, CharField, DateTimeField, BooleanField, IntegerField, BigIntegerField, \
     ManyToManyField, CASCADE, SET_NULL, PROTECT, QuerySet, BigAutoField, UniqueConstraint
 from django.contrib.postgres.fields import ArrayField, JSONField
-from django.contrib.auth.models import User
-from .common import AbstractChoice, BasicCommon, Template, NamedCommon, TemplateSchemaMixin
+from .permissions import TMSSUser as User
+from .common import AbstractChoice, BasicCommon, Template, NamedCommon, TemplateSchemaMixin, ProjectPropertyMixin
 from enum import Enum
 from django.db.models.expressions import RawSQL
 from django.core.exceptions import ValidationError
@@ -138,7 +138,7 @@ class SIPidentifier(Model):
 #
 # Instance Objects
 #
-class Subtask(BasicCommon, TemplateSchemaMixin):
+class Subtask(BasicCommon, ProjectPropertyMixin, TemplateSchemaMixin):
     """
     Represents a low-level task, which is an atomic unit of execution, such as running an observation, running
     inspection plots on the observed data, etc. Each task has a specific configuration, will have resources allocated
@@ -156,6 +156,7 @@ class Subtask(BasicCommon, TemplateSchemaMixin):
     created_or_updated_by_user = ForeignKey(User, null=True, editable=False, on_delete=PROTECT, help_text='The user who created / updated the subtask.')
     raw_feedback = CharField(null=True, max_length=1048576, help_text='The raw feedback for this Subtask')
     global_identifier = OneToOneField('SIPidentifier', null=False, editable=False, on_delete=PROTECT, help_text='The global unique identifier for LTA SIP.')
+    path_to_project = 'task_blueprints__scheduling_unit_blueprint__draft__scheduling_set__project'
 
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py
index ada071a865bdf4f336164fa504e62eb9f7083a87..37b312c4633e4ed20addff9110bb3265b73bb1df 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py
@@ -10,32 +10,16 @@ from django.contrib.postgres.fields import JSONField
 from enum import Enum
 from django.db.models.expressions import RawSQL
 from django.db.models.deletion import ProtectedError
-from .common import AbstractChoice, BasicCommon, Template, NamedCommon, TemplateSchemaMixin, NamedCommonPK, RefreshFromDbInvalidatesCachedPropertiesMixin
+from .common import AbstractChoice, BasicCommon, Template, NamedCommon, TemplateSchemaMixin, NamedCommonPK, RefreshFromDbInvalidatesCachedPropertiesMixin, ProjectPropertyMixin
 from lofar.common.json_utils import validate_json_against_schema, validate_json_against_its_schema, add_defaults_to_json_object_for_schema
 from lofar.sas.tmss.tmss.exceptions import *
 from django.core.exceptions import ValidationError
 import datetime
 from collections import Counter
 from django.utils.functional import cached_property
+from pprint import pformat
 from lofar.sas.tmss.tmss.exceptions import TMSSException
-
-#
-# Mixins
-#
-
-class ProjectPropertyMixin(RefreshFromDbInvalidatesCachedPropertiesMixin):
-    @cached_property
-    def project(self): # -> Project:
-        '''return the related project of this task
-        '''
-        if not hasattr(self, 'path_to_project'):
-            return TMSSException("Please define a 'path_to_project' attribute on the object for the ProjectPropertyMixin to function.")
-        obj = self
-        for attr in self.path_to_project.split('__'):
-            obj = getattr(obj, attr)
-            if attr == 'project':
-                return obj
-
+from lofar.sas.tmss.tmss.exceptions import BlueprintCreationException, TMSSException
 
 #
 # I/O
@@ -177,7 +161,7 @@ class Setting(BasicCommon):
     value = BooleanField(null=False)
 
 
-class TaskConnectorType(BasicCommon):
+class TaskConnectorType(Model):
     ''' Describes the data type & format combinations a Task can accept or produce. The "role" is used to distinguish
         inputs (or outputs) that have the same data type & format, but are used in different ways by the task. For
         example, a calibration pipeline accepts measurement sets only, but distinghuishes between CALIBRATOR and
@@ -185,9 +169,12 @@ class TaskConnectorType(BasicCommon):
     role = ForeignKey('Role', null=False, on_delete=PROTECT)
     datatype = ForeignKey('Datatype', null=False, on_delete=PROTECT)
     dataformat = ForeignKey('Dataformat', null=False, on_delete=PROTECT)
-    task_template = ForeignKey("TaskTemplate", related_name='output_connector_types', null=False, on_delete=CASCADE)
+    task_template = ForeignKey("TaskTemplate", related_name='connector_types', null=False, on_delete=CASCADE)
     iotype = ForeignKey('IOType', null=False, on_delete=PROTECT, help_text="Is this connector an input or output")
 
+    class Meta:
+        constraints = [UniqueConstraint(fields=['role', 'datatype', 'dataformat', 'task_template', 'iotype'], name='task_connector_type_unique_combination')]
+
 
 #
 # Templates
@@ -219,7 +206,13 @@ class SchedulingUnitObservingStrategyTemplate(NamedCommon):
 
     def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
         if self.template and self.scheduling_unit_template_id and self.scheduling_unit_template.schema:
-            validate_json_against_schema(self.template, self.scheduling_unit_template.schema)
+            try:
+                validate_json_against_schema(self.template, self.scheduling_unit_template.schema)
+            except Exception as e:
+                # log the error for debugging and re-raise
+                logger.error("Error while validating SchedulingUnitObservingStrategyTemplate name='%s' id='%s' error: %s\ntemplate:\n%s",
+                             self.name, self.id, e, pformat(self.template))
+                raise
 
         super().save(force_insert, force_update, using, update_fields)
 
@@ -407,6 +400,7 @@ class SchedulingUnitDraft(NamedCommon, TemplateSchemaMixin):
     piggyback_allowed_aartfaac = BooleanField(help_text='Piggyback key for AARTFAAC.', null=True)
     priority_rank = FloatField(null=False, default=0.0, help_text='Priority of this scheduling unit w.r.t. other scheduling units within the same queue and project.')
     priority_queue = ForeignKey('PriorityQueueType', null=False, on_delete=PROTECT, default="A", help_text='Priority queue of this scheduling unit. Queues provide a strict ordering between scheduling units.')
+    is_triggered = BooleanField(default=False, null=False, help_text='boolean (default FALSE), which indicates whether this observation was triggered (responsive telescope)')
 
     def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
         if self.requirements_doc is not None and self.requirements_template_id and self.requirements_template.schema is not None:
@@ -496,6 +490,7 @@ class SchedulingUnitBlueprint(RefreshFromDbInvalidatesCachedPropertiesMixin, Tem
     priority_queue = ForeignKey('PriorityQueueType', null=False, on_delete=PROTECT, default="A", help_text='Priority queue of this scheduling unit. Queues provide a strict ordering between scheduling units.')
     scheduling_constraints_doc = JSONField(help_text='Scheduling Constraints for this run.', null=True)
     scheduling_constraints_template = ForeignKey('SchedulingConstraintsTemplate', on_delete=CASCADE, null=True, help_text='Schema used for scheduling_constraints_doc.')
+    is_triggered = BooleanField(default=False, null=False, help_text='boolean (default FALSE), which indicates whether this observation was triggered (responsive telescope)')
 
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
@@ -514,7 +509,7 @@ class SchedulingUnitBlueprint(RefreshFromDbInvalidatesCachedPropertiesMixin, Tem
         if self._state.adding:
             # On creation, propagate the following scheduling_unit_draft attributes as default for the new scheduling_unit_blueprint
             for copy_field in ['ingest_permission_required', 'piggyback_allowed_tbb', 'piggyback_allowed_aartfaac',
-                               'scheduling_constraints_doc', 'scheduling_constraints_template']:
+                               'scheduling_constraints_doc', 'scheduling_constraints_template', 'is_triggered']:
                 if hasattr(self, 'draft'):
                     setattr(self, copy_field, getattr(self.draft, copy_field))
         else:
@@ -529,6 +524,20 @@ class SchedulingUnitBlueprint(RefreshFromDbInvalidatesCachedPropertiesMixin, Tem
         self.__original_scheduling_constraints_doc = self.scheduling_constraints_doc
         self.__original_scheduling_constraints_template_id = self.scheduling_constraints_template_id
 
+        if self._state.adding and self.is_triggered:
+            if self.project.can_trigger:
+                from lofar.sas.tmss.services.scheduling.constraints import can_run_after
+                start_time = datetime.datetime.utcnow() + datetime.timedelta(minutes=3)
+                if self.scheduling_constraints_template is None or can_run_after(self, start_time):
+                    logger.info('Triggered obs name=%s can run after start_time=%s. The scheduler will pick this up and cancel ongoing observations if necessary.' % (self.name, start_time))
+                else:
+                    logger.info('Triggered obs name=%s cannot run after start_time=%s. Adding it for book-keeping, but it will be unschedulable.' % (self.name, start_time))
+                    # todo: set to unschedulable? This is a derived state and we do not have subtasks at this point. We could check this in 'status' of course, but this check is quite costly...
+            else:
+                msg = 'Triggered obs name=%s is rejected because its project name=%s does not allow triggering.' % (self.name, self.project.name)
+                logger.warning(msg)
+                raise BlueprintCreationException(msg)
+
         super().save(force_insert, force_update, using, update_fields)
 
     @cached_property
@@ -750,20 +759,6 @@ class SchedulingUnitBlueprint(RefreshFromDbInvalidatesCachedPropertiesMixin, Tem
         return fields_found
 
 
-class ProjectPropertyMixin():
-    @cached_property
-    def project(self) -> Project:
-        '''return the related project of this task
-        '''
-        if not hasattr(self, 'path_to_project'):
-            return TMSSException("Please define a 'path_to_project' attribute on the object for the ProjectPropertyMixin to function.")
-        obj = self
-        for attr in self.path_to_project.split('__'):
-            obj = getattr(obj, attr)
-            if attr == 'project':
-                return obj
-
-
 class TaskDraft(NamedCommon, ProjectPropertyMixin, TemplateSchemaMixin):
     specifications_doc = JSONField(help_text='Specifications for this task.')
     copies = ForeignKey('TaskDraft', related_name="copied_from", on_delete=SET_NULL, null=True, help_text='Source reference, if we are a copy (NULLable).')
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/populate.py b/SAS/TMSS/backend/src/tmss/tmssapp/populate.py
index 8913d4251adf474562462cc7579c0663cc528f32..978bc94eb675cf02ec3c272f339a353e5d880e2c 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/populate.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/populate.py
@@ -28,7 +28,9 @@ from lofar.sas.tmss.tmss.tmssapp.models.permissions import *
 from lofar.sas.tmss.tmss.tmssapp.conversions import timestamps_and_stations_to_sun_rise_and_set, get_all_stations
 from lofar.common import isTestEnvironment, isDevelopmentEnvironment
 from concurrent.futures import ThreadPoolExecutor
-from django.contrib.auth.models import User, Group, Permission
+from django.contrib.auth.models import Group, Permission
+from django.contrib.auth import get_user_model
+User = get_user_model()
 from django.contrib.contenttypes.models import ContentType
 from django.db.utils import IntegrityError
 
@@ -456,16 +458,28 @@ def populate_connectors():
                                          task_template=TaskTemplate.objects.get(name='preprocessing pipeline'),
                                          iotype=IOType.objects.get(value=iotype_value))
 
-    # ingest and cleanup
+    # Ingest and Cleanup can/should accept all kinds of data.
+    # So we could loop over all combinations of Datatype, Dataformat and Role and create an input connector for each.
+    # This would result however in "unrealistic"/non-existing types like: TIME_SERIES-MEASUREMENTSET, or VISIBILITIES-BEAMFORMED, etc, which do not make any sense.
+    # So, instead, lets loop over all exising output connectors, and accept those as input.
     for task_template_name in ('ingest', 'cleanup'):
-        for datatype_value in (Datatype.Choices.VISIBILITIES.value, Datatype.Choices.TIME_SERIES.value):
-            for dataformat_value in [choice.value for choice in Dataformat.Choices]:
-                for role_value in [choice.value for choice in Role.Choices]:
-                    TaskConnectorType.objects.create(role=Role.objects.get(value=role_value),
-                                                     datatype=Datatype.objects.get(value=datatype_value),
-                                                     dataformat=Dataformat.objects.get(value=dataformat_value),
-                                                     task_template=TaskTemplate.objects.get(name=task_template_name),
+        task_template = TaskTemplate.objects.get(name=task_template_name)
+
+        # loop over all existing output types
+        # but filter out the possibly exsisting 'any' roles, so we can add it later without creating duplicates
+        any_role = Role.objects.get(value=Role.Choices.ANY.value)
+        for output_connector_type in TaskConnectorType.objects.filter(iotype=IOType.objects.get(value=IOType.Choices.OUTPUT.value)).exclude(role=any_role).all():
+            # always create two input connectors for the specific output_connector_type.role and the any_role
+            for role in [output_connector_type.role, any_role]:
+                try:
+                    TaskConnectorType.objects.create(role=role,
+                                                     datatype=output_connector_type.datatype,
+                                                     dataformat=output_connector_type.dataformat,
+                                                     task_template=task_template,
                                                      iotype=IOType.objects.get(value=IOType.Choices.INPUT.value))
+                except IntegrityError:
+                    # we just tried to create a duplicate... It's ok to silently continue...
+                    pass
 
 
 def populate_permissions():
@@ -670,8 +684,8 @@ def assign_system_permissions():
     tmss_admin_group.permissions.add(perm)
 
     # User model permissions
-    ct = ContentType.objects.get(model='user')
-    perm = Permission.objects.get(codename='add_user')
+    ct = ContentType.objects.get(model='tmssuser')
+    perm = Permission.objects.get(codename='add_tmssuser')
     to_observer_group.permissions.add(perm)
     sdco_support_group.permissions.add(perm)
     tmss_maintainer_group.permissions.add(perm)
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/LoTSS-observation-scheduling-unit-observation-strategy.json b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/LoTSS-observation-scheduling-unit-observation-strategy.json
index ce930faebf11e9d798bfa64809f06f067e4aeefe..674c49680e4caa76246e00893a8ed0f946c729f9 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/LoTSS-observation-scheduling-unit-observation-strategy.json
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/LoTSS-observation-scheduling-unit-observation-strategy.json
@@ -331,8 +331,8 @@
               447
             ],
             "digital_pointing":{
-              "angle1":0.24,
-              "angle2":0.25,
+              "angle1": 0.6624317181687094,
+              "angle2": 1.5579526427549426,
               "direction_type":"J2000"
             }
           },
@@ -584,8 +584,8 @@
               447
             ],
             "digital_pointing":{
-              "angle1":0.27,
-              "angle2":0.28,
+              "angle1": 0.6624317181687094,
+              "angle2": 1.5579526427549426,
               "direction_type":"J2000"
             }
           }
@@ -593,8 +593,8 @@
         "filter":"HBA_110_190",
         "duration":28800,
         "tile_beam":{
-          "angle1":0.42,
-          "angle2":0.43,
+          "angle1": 0.6624317181687094,
+          "angle2": 1.5579526427549426,
           "direction_type":"J2000"
         },
         "correlator":{
@@ -737,8 +737,8 @@
         "name":"calibrator1",
         "duration":600,
         "pointing":{
-          "angle1":0,
-          "angle2":0,
+          "angle1": 0.6624317181687094,
+          "angle2": 1.5579526427549426,
           "direction_type":"J2000"
         },
         "autoselect":false
@@ -754,8 +754,8 @@
         "name":"calibrator2",
         "duration":600,
         "pointing":{
-          "angle1":0,
-          "angle2":0,
+          "angle1": 0.6624317181687094,
+          "angle2": 1.5579526427549426,
           "direction_type":"J2000"
         },
         "autoselect":false
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/UC1-scheduling-unit-observation-strategy.json b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/UC1-scheduling-unit-observation-strategy.json
index 9a7a4fe7b836db4579a9111af512f2d31b6e4a9c..c8cf099bb1f48f17fef7067087e7a7de7cb27271 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/UC1-scheduling-unit-observation-strategy.json
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/UC1-scheduling-unit-observation-strategy.json
@@ -8,8 +8,8 @@
         "autoselect": false,
         "pointing": {
           "direction_type": "J2000",
-          "angle1": 0,
-          "angle2": 0
+          "angle1": 0.6624317181687094,
+          "angle2": 1.5579526427549426
         },
         "name": "calibrator1"
       },
@@ -78,16 +78,16 @@
         ],
         "tile_beam": {
           "direction_type": "J2000",
-          "angle1": 0.42,
-          "angle2": 0.43
+          "angle1": 0.6624317181687094,
+          "angle2": 1.5579526427549426
         },
         "SAPs": [
           {
             "name": "target1",
             "digital_pointing": {
               "direction_type": "J2000",
-              "angle1": 0.24,
-              "angle2": 0.25
+              "angle1": 0.6624317181687094,
+              "angle2": 1.5579526427549426
             },
             "subbands": [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243]
           },
@@ -95,8 +95,8 @@
             "name": "target2",
             "digital_pointing": {
               "direction_type": "J2000",
-              "angle1": 0.27,
-              "angle2": 0.28
+              "angle1": 0.6624317181687094,
+              "angle2": 1.5579526427549426
             },
             "subbands": [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243]
           }
@@ -158,8 +158,8 @@
         "autoselect": false,
         "pointing": {
           "direction_type": "J2000",
-          "angle1": 0,
-          "angle2": 0
+          "angle1": 0.6624317181687094,
+          "angle2": 1.5579526427549426
         },
         "name": "calibrator2"
       },
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/common_schema_template-pointing-1.json b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/common_schema_template-pointing-1.json
index 75e850155bd192c799fc8e659516ac23c9ee2f2d..daaf144d92f22def7252cd2c259dcce965cebb26 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/common_schema_template-pointing-1.json
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/common_schema_template-pointing-1.json
@@ -35,13 +35,13 @@
           "type": "number",
           "title": "Angle 1",
           "description": "First angle (e.g. RA)",
-          "default": 0
+          "default": 0.6624317181687094
         },
         "angle2": {
           "type": "number",
           "title": "Angle 2",
           "description": "Second angle (e.g. DEC)",
-          "default": 0
+          "default": 1.5579526427549426
         }
       },
       "required": [
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/common_schema_template-stations-1.json b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/common_schema_template-stations-1.json
index e3afa001749c54992e3de0cc6938a24ac4ed2867..2fb3614642699975018bf09db55d6c2ce5595dab 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/common_schema_template-stations-1.json
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/common_schema_template-stations-1.json
@@ -80,13 +80,17 @@
       "type":"integer",
       "title":"Maximum number of stations to omit",
       "description":"Maximum number of stations that can be omitted from a group (due to maintenance for example)",
-      "minimum":0
+      "minimum":0,
+      "default": 0
     },
     "station_group":{
       "type":"object",
       "title": "Station group",
       "description": "A set of predefined list of stations, and a constraint on how many stations are allowed to be missing (due to maintenance for example)",
-      "default":{},
+      "default":{
+        "stations": ["CS002", "CS003", "CS004", "CS005", "CS006", "CS007"],
+        "max_nr_missing": 1
+      },
       "anyOf": [
         {
           "title":"Superterp",
@@ -95,17 +99,18 @@
           "properties":{
             "stations":{
               "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/station_list",
-              "enum": [["CS002", "CS003", "CS004", "CS005", "CS006", "CS007"]],
-              "default": ["CS002", "CS003", "CS004", "CS005", "CS006", "CS007"],
-              "uniqueItems": false
+              "enum": [["CS002", "CS003", "CS004", "CS005", "CS006", "CS007"]]
             },
             "max_nr_missing":{
-              "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/max_number_of_missing_stations",
-              "default": 0
+              "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/max_number_of_missing_stations"
             }
           },
           "required": ["stations", "max_nr_missing"],
-          "additionalProperties": false
+          "additionalProperties": false,
+          "default":{
+            "stations": ["CS002", "CS003", "CS004", "CS005", "CS006", "CS007"],
+            "max_nr_missing": 0
+          }
         },
         {
           "title":"Core",
@@ -114,17 +119,18 @@
           "properties":{
             "stations":{
               "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/station_list",
-              "enum": [["CS001", "CS002", "CS003", "CS004", "CS005", "CS006", "CS007", "CS011", "CS013", "CS017", "CS021", "CS024", "CS026", "CS028", "CS030", "CS031", "CS032", "CS103", "CS201", "CS301", "CS302", "CS401", "CS501"]],
-              "default": ["CS001", "CS002", "CS003", "CS004", "CS005", "CS006", "CS007", "CS011", "CS013", "CS017", "CS021", "CS024", "CS026", "CS028", "CS030", "CS031", "CS032", "CS103", "CS201", "CS301", "CS302", "CS401", "CS501"],
-              "uniqueItems": false
+              "enum": [["CS001", "CS002", "CS003", "CS004", "CS005", "CS006", "CS007", "CS011", "CS013", "CS017", "CS021", "CS024", "CS026", "CS028", "CS030", "CS031", "CS032", "CS103", "CS201", "CS301", "CS302", "CS401", "CS501"]]
             },
             "max_nr_missing":{
-              "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/max_number_of_missing_stations",
-              "default": 4
+              "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/max_number_of_missing_stations"
             }
           },
           "required": ["stations", "max_nr_missing"],
-          "additionalProperties": false
+          "additionalProperties": false,
+          "default":{
+            "stations": ["CS001", "CS002", "CS003", "CS004", "CS005", "CS006", "CS007", "CS011", "CS013", "CS017", "CS021", "CS024", "CS026", "CS028", "CS030", "CS031", "CS032", "CS103", "CS201", "CS301", "CS302", "CS401", "CS501"],
+            "max_nr_missing": 4
+          }
         },
         {
           "title":"Remote",
@@ -133,17 +139,18 @@
           "properties":{
             "stations":{
               "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/station_list",
-              "enum": [["RS106", "RS205", "RS208", "RS210", "RS305", "RS306", "RS307", "RS310", "RS406", "RS407", "RS409", "RS503", "RS508", "RS509"]],
-              "default": ["RS106", "RS205", "RS208", "RS210", "RS305", "RS306", "RS307", "RS310", "RS406", "RS407", "RS409", "RS503", "RS508", "RS509"],
-              "uniqueItems": false
+              "enum": [["RS106", "RS205", "RS208", "RS210", "RS305", "RS306", "RS307", "RS310", "RS406", "RS407", "RS409", "RS503", "RS508", "RS509"]]
             },
             "max_nr_missing":{
-              "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/max_number_of_missing_stations",
-              "default": 4
+              "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/max_number_of_missing_stations"
             }
           },
           "required": ["stations", "max_nr_missing"],
-          "additionalProperties": false
+          "additionalProperties": false,
+          "default": {
+              "stations": ["RS106", "RS205", "RS208", "RS210", "RS305", "RS306", "RS307", "RS310", "RS406", "RS407", "RS409", "RS503", "RS508", "RS509"],
+              "max_nr_missing": 4
+          }
         },
         {
           "title":"Dutch",
@@ -152,17 +159,18 @@
           "properties":{
             "stations":{
               "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/station_list",
-              "enum": [["CS001", "CS002", "CS003", "CS004", "CS005", "CS006", "CS007", "CS011", "CS013", "CS017", "CS021", "CS024", "CS026", "CS028", "CS030", "CS031", "CS032", "CS103", "CS201", "CS301", "CS302", "CS401", "CS501", "RS106", "RS205", "RS208", "RS210", "RS305", "RS306", "RS307", "RS310", "RS406", "RS407", "RS409", "RS503", "RS508", "RS509"]],
-              "default": ["CS001", "CS002", "CS003", "CS004", "CS005", "CS006", "CS007", "CS011", "CS013", "CS017", "CS021", "CS024", "CS026", "CS028", "CS030", "CS031", "CS032", "CS103", "CS201", "CS301", "CS302", "CS401", "CS501", "RS106", "RS205", "RS208", "RS210", "RS305", "RS306", "RS307", "RS310", "RS406", "RS407", "RS409", "RS503", "RS508", "RS509"],
-              "uniqueItems": false
+              "enum": [["CS001", "CS002", "CS003", "CS004", "CS005", "CS006", "CS007", "CS011", "CS013", "CS017", "CS021", "CS024", "CS026", "CS028", "CS030", "CS031", "CS032", "CS103", "CS201", "CS301", "CS302", "CS401", "CS501", "RS106", "RS205", "RS208", "RS210", "RS305", "RS306", "RS307", "RS310", "RS406", "RS407", "RS409", "RS503", "RS508", "RS509"]]
             },
             "max_nr_missing":{
-              "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/max_number_of_missing_stations",
-              "default": 4
+              "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/max_number_of_missing_stations"
             }
           },
           "required": ["stations", "max_nr_missing"],
-          "additionalProperties": false
+          "additionalProperties": false,
+          "default": {
+            "stations": ["CS001", "CS002", "CS003", "CS004", "CS005", "CS006", "CS007", "CS011", "CS013", "CS017", "CS021", "CS024", "CS026", "CS028", "CS030", "CS031", "CS032", "CS103", "CS201", "CS301", "CS302", "CS401", "CS501", "RS106", "RS205", "RS208", "RS210", "RS305", "RS306", "RS307", "RS310", "RS406", "RS407", "RS409", "RS503", "RS508", "RS509"],
+            "max_nr_missing": 4
+          }
         },
         {
           "title":"International",
@@ -171,17 +179,18 @@
           "properties":{
             "stations":{
               "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/station_list",
-              "enum": [["DE601", "DE602", "DE603", "DE604", "DE605", "DE609", "FR606", "SE607", "UK608", "PL610", "PL611", "PL612", "IE613", "LV614"]],
-              "default": ["DE601", "DE602", "DE603", "DE604", "DE605", "DE609", "FR606", "SE607", "UK608", "PL610", "PL611", "PL612", "IE613", "LV614"],
-              "uniqueItems": false
+              "enum": [["DE601", "DE602", "DE603", "DE604", "DE605", "DE609", "FR606", "SE607", "UK608", "PL610", "PL611", "PL612", "IE613", "LV614"]]
             },
             "max_nr_missing":{
-              "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/max_number_of_missing_stations",
-              "default": 2
+              "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/max_number_of_missing_stations"
             }
           },
           "required": ["stations", "max_nr_missing"],
-          "additionalProperties": false
+          "additionalProperties": false,
+          "default": {
+              "stations": ["DE601", "DE602", "DE603", "DE604", "DE605", "DE609", "FR606", "SE607", "UK608", "PL610", "PL611", "PL612", "IE613", "LV614"],
+              "max_nr_missing": 2
+          }
         },
         {
           "title":"International required",
@@ -190,17 +199,18 @@
           "properties":{
             "stations":{
               "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/station_list",
-              "enum": [["DE601", "DE605"]],
-              "default": ["DE601", "DE605"],
-              "uniqueItems": false
+              "enum": [["DE601", "DE605"]]
             },
             "max_nr_missing":{
-              "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/max_number_of_missing_stations",
-              "default": 1
+              "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/max_number_of_missing_stations"
             }
           },
           "required": ["stations", "max_nr_missing"],
-          "additionalProperties": false
+          "additionalProperties": false,
+          "default": {
+              "stations": ["DE601", "DE605"],
+              "max_nr_missing": 1
+          }
         },
         {
           "title":"All",
@@ -209,17 +219,18 @@
           "properties":{
             "stations":{
               "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/station_list",
-              "enum": [["CS001", "CS002", "CS003", "CS004", "CS005", "CS006", "CS007", "CS011", "CS013", "CS017", "CS021", "CS024", "CS026", "CS028", "CS030", "CS031", "CS032", "CS301", "CS302", "CS401", "CS501", "RS106", "RS205", "RS208", "RS210", "RS305", "RS306", "RS307", "RS310", "RS406", "RS407", "RS409", "RS503", "RS508", "RS509", "DE601", "DE602", "DE603", "DE604", "DE605", "DE609", "FR606", "SE607", "UK608", "PL610", "PL611", "PL612", "IE613", "LV614"]],
-              "default": ["CS001", "CS002", "CS003", "CS004", "CS005", "CS006", "CS007", "CS011", "CS013", "CS017", "CS021", "CS024", "CS026", "CS028", "CS030", "CS031", "CS032", "CS301", "CS302", "CS401", "CS501", "RS106", "RS205", "RS208", "RS210", "RS305", "RS306", "RS307", "RS310", "RS406", "RS407", "RS409", "RS503", "RS508", "RS509", "DE601", "DE602", "DE603", "DE604", "DE605", "DE609", "FR606", "SE607", "UK608", "PL610", "PL611", "PL612", "IE613", "LV614"],
-              "uniqueItems": false
+              "enum": [["CS001", "CS002", "CS003", "CS004", "CS005", "CS006", "CS007", "CS011", "CS013", "CS017", "CS021", "CS024", "CS026", "CS028", "CS030", "CS031", "CS032", "CS301", "CS302", "CS401", "CS501", "RS106", "RS205", "RS208", "RS210", "RS305", "RS306", "RS307", "RS310", "RS406", "RS407", "RS409", "RS503", "RS508", "RS509", "DE601", "DE602", "DE603", "DE604", "DE605", "DE609", "FR606", "SE607", "UK608", "PL610", "PL611", "PL612", "IE613", "LV614"]]
             },
             "max_nr_missing":{
-              "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/max_number_of_missing_stations",
-              "default": 6
+              "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/max_number_of_missing_stations"
             }
           },
           "required": ["stations", "max_nr_missing"],
-          "additionalProperties": false
+          "additionalProperties": false,
+          "default": {
+            "stations": ["CS001", "CS002", "CS003", "CS004", "CS005", "CS006", "CS007", "CS011", "CS013", "CS017", "CS021", "CS024", "CS026", "CS028", "CS030", "CS031", "CS032", "CS301", "CS302", "CS401", "CS501", "RS106", "RS205", "RS208", "RS210", "RS305", "RS306", "RS307", "RS310", "RS406", "RS407", "RS409", "RS503", "RS508", "RS509", "DE601", "DE602", "DE603", "DE604", "DE605", "DE609", "FR606", "SE607", "UK608", "PL610", "PL611", "PL612", "IE613", "LV614"],
+            "max_nr_missing": 6
+          }
         },
         {
           "title":"Custom",
@@ -227,20 +238,18 @@
           "type": "object",
           "properties":{
             "stations":{
-              "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/station_list",
-              "default": ["CS001"],
-              "minItems": 1,
-              "additionalItems": false,
-              "additionalProperties": false,
-              "uniqueItems": true
+              "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/station_list"
             },
             "max_nr_missing":{
-              "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/max_number_of_missing_stations",
-              "default": 0
+              "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/max_number_of_missing_stations"
             }
           },
           "required": ["stations", "max_nr_missing"],
-          "additionalProperties": false
+          "additionalProperties": false,
+          "default": {
+            "stations": ["CS001"],
+            "max_nr_missing": 0
+          }
         }
         ]
     },
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/short-observation-pipeline-ingest-scheduling-unit-observation-strategy.json b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/short-observation-pipeline-ingest-scheduling-unit-observation-strategy.json
index 6ae834740335d9474e7351d58c3739b1bf154a2f..768804b59e502d0d94257c219b212e232cb7e6b4 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/short-observation-pipeline-ingest-scheduling-unit-observation-strategy.json
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/short-observation-pipeline-ingest-scheduling-unit-observation-strategy.json
@@ -25,12 +25,13 @@
         "antenna_set": "HBA_DUAL_INNER",
         "filter": "HBA_110_190",
         "station_groups": [ {
-            "stations": ["CS002", "CS003", "CS004", "CS005", "CS006", "CS007"]
+            "stations": ["CS002", "CS003", "CS004", "CS005", "CS006", "CS007"],
+            "max_nr_missing": 1
         }],
         "tile_beam": {
           "direction_type": "J2000",
-          "angle1": 5.233660650313663,
-          "angle2": 0.7109404782526458
+          "angle1": 0.6624317181687094,
+          "angle2": 1.5579526427549426
         },
         "SAPs": [
           {
@@ -38,8 +39,8 @@
             "target": "CygA",
             "digital_pointing": {
               "direction_type": "J2000",
-              "angle1": 5.233660650313663,
-              "angle2": 0.7109404782526458
+              "angle1": 0.6624317181687094,
+              "angle2": 1.5579526427549426
             },
             "subbands": [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243]
           }
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/simple-beamforming-observation-scheduling-unit-observation-strategy.json b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/simple-beamforming-observation-scheduling-unit-observation-strategy.json
index 4d56ae8273810ae352ab54fbab2a37c2d2913399..bc6925c79cf44060f7962678a406b58c1609123a 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/simple-beamforming-observation-scheduling-unit-observation-strategy.json
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/simple-beamforming-observation-scheduling-unit-observation-strategy.json
@@ -13,8 +13,8 @@
             "target": "CygA",
             "digital_pointing": {
               "direction_type": "J2000",
-              "angle1": 5.233660650313663,
-              "angle2": 0.7109404782526458
+              "angle1": 0.6624317181687094,
+              "angle2": 1.5579526427549426
             },
             "subbands": [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243]
           }
@@ -26,8 +26,8 @@
         ],
         "tile_beam": {
           "direction_type": "J2000",
-          "angle1": 5.233660650313663,
-          "angle2": 0.7109404782526458
+          "angle1": 0.6624317181687094,
+          "angle2": 1.5579526427549426
         },
         "beamformers": [
           {
@@ -38,8 +38,8 @@
                 "tabs": [{
                   "pointing": {
                     "direction_type": "J2000",
-                    "angle1": 0,
-                    "angle2": 0
+                    "angle1": 0.6624317181687094,
+                    "angle2": 1.5579526427549426
                   },
                   "relative": true
                 }],
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/simple-observation-scheduling-unit-observation-strategy.json b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/simple-observation-scheduling-unit-observation-strategy.json
index 4ea17e719fad83f17b9746f474f1761f9682a48f..f598d9956f417f935d7af687ecd0d8ddd17d2a1b 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/simple-observation-scheduling-unit-observation-strategy.json
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/simple-observation-scheduling-unit-observation-strategy.json
@@ -29,8 +29,8 @@
         }],
         "tile_beam": {
           "direction_type": "J2000",
-          "angle1": 5.233660650313663,
-          "angle2": 0.7109404782526458
+          "angle1": 0.6624317181687094,
+          "angle2": 1.5579526427549426
         },
         "SAPs": [
           {
@@ -38,8 +38,8 @@
             "target": "CygA",
             "digital_pointing": {
               "direction_type": "J2000",
-              "angle1": 5.233660650313663,
-              "angle2": 0.7109404782526458
+              "angle1": 0.6624317181687094,
+              "angle2": 1.5579526427549426
             },
             "subbands": [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243]
           }
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py
index a29fcbcfb975811d317d98a9adee1000b42d7ac5..6180292a3916534ac9279819325e88b54a2710dd 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py
@@ -7,7 +7,7 @@ from .. import models
 from .scheduling import SubtaskSerializer
 from .common import FloatDurationField, RelationalHyperlinkedModelSerializer, AbstractTemplateSerializer, DynamicRelationalHyperlinkedModelSerializer
 from .widgets import JSONEditorField
-from django.contrib.auth.models import User
+from ..models import TMSSUser as User
 
 # This is required for keeping a user reference as ForeignKey in other models
 # (I think so that the HyperlinkedModelSerializer can generate a URI)
@@ -143,6 +143,12 @@ class TaskConnectorTypeSerializer(DynamicRelationalHyperlinkedModelSerializer):
         fields = '__all__'
 
 
+class TaskConnectorTypeModelSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = models.TaskConnectorType
+        fields = '__all__'
+
+
 class CycleSerializer(DynamicRelationalHyperlinkedModelSerializer):
     duration = FloatDurationField(read_only=True, help_text="Duration of the cycle [seconds]")
 
@@ -303,6 +309,7 @@ class TaskDraftSerializer(DynamicRelationalHyperlinkedModelSerializer):
     relative_stop_time = FloatDurationField(read_only=True)
     specifications_doc = JSONEditorField(schema_source='specifications_template.schema')
     task_type = serializers.StringRelatedField(source='specifications_template.type', label='task_type', read_only=True, help_text='The task type as defined in the specifications template.')
+    connector_types = TaskConnectorTypeModelSerializer(source='specifications_template.connector_types', label='connector_types', many=True, read_only=True, help_text='The connector types which define what kind of data this task consumes/produces.')
 
     class Meta:
         model = models.TaskDraft
@@ -322,6 +329,7 @@ class TaskBlueprintSerializer(DynamicRelationalHyperlinkedModelSerializer):
     relative_stop_time = FloatDurationField(read_only=True)
     specifications_doc = JSONEditorField(schema_source='specifications_template.schema')
     task_type = serializers.StringRelatedField(source='specifications_template.type', label='task_type', read_only=True, help_text='The task type as defined in the specifications template.')
+    connector_types = TaskConnectorTypeModelSerializer(source='specifications_template.connector_types', label='connector_types', many=True, read_only=True, help_text='The connector types which define what kind of data this task consumes/produces.')
 
     class Meta:
         model = models.TaskBlueprint
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py b/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py
index e99dd864d74c15acb51854aa8f145c3a96bf9ea7..116fdbe86a22fa90e330fe30b53776c34ab343c2 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py
@@ -33,6 +33,9 @@ from lofar.sas.tmss.tmss.tmssapp.conversions import antennafields_for_antennaset
 from lofar.sas.tmss.tmss.exceptions import TMSSException
 from django.db import transaction
 
+# cache for json schema's
+_schema_cache = {}
+
 # ==== various create* methods to convert/create a TaskBlueprint into one or more Subtasks ====
 
 def check_prerequities_for_subtask_creation(task_blueprint: TaskBlueprint) -> bool:
@@ -155,7 +158,7 @@ def create_observation_subtask_specifications_from_observation_task_blueprint(ta
 
     # start with an observation subtask specification with all the defaults and the right structure according to the schema
     subtask_template = SubtaskTemplate.objects.get(name='observation control')
-    subtask_spec = get_default_json_object_for_schema(subtask_template.schema)
+    subtask_spec = get_default_json_object_for_schema(subtask_template.schema, cache=_schema_cache)
 
     # wipe the default pointings, these should come from the task_spec
     subtask_spec['stations'].pop('analog_pointing', None)
@@ -534,7 +537,7 @@ def create_qafile_subtask_from_observation_subtask(observation_subtask: Subtask)
 
     # step 1: create subtask in defining state, with filled-in subtask_template
     qafile_subtask_template = SubtaskTemplate.objects.get(name="QA file conversion")
-    qafile_subtask_spec = add_defaults_to_json_object_for_schema({}, qafile_subtask_template.schema)
+    qafile_subtask_spec = add_defaults_to_json_object_for_schema({}, qafile_subtask_template.schema, cache=_schema_cache)
     qafile_subtask_spec['nr_of_subbands'] = obs_task_qafile_spec.get("nr_of_subbands")
     qafile_subtask_spec['nr_of_timestamps'] = obs_task_qafile_spec.get("nr_of_timestamps")
     validate_json_against_schema(qafile_subtask_spec, qafile_subtask_template.schema)
@@ -551,7 +554,7 @@ def create_qafile_subtask_from_observation_subtask(observation_subtask: Subtask)
 
     # step 2: create and link subtask input/output
     selection_template = TaskRelationSelectionTemplate.objects.get(name="all")
-    selection_doc = get_default_json_object_for_schema(selection_template.schema)
+    selection_doc = get_default_json_object_for_schema(selection_template.schema, cache=_schema_cache)
 
     for obs_out in observation_subtask.outputs.all():
         qafile_subtask_input = SubtaskInput.objects.create(subtask=qafile_subtask,
@@ -615,7 +618,7 @@ def create_qaplots_subtask_from_qafile_subtask(qafile_subtask: Subtask) -> Subta
 
     # step 1: create subtask in defining state, with filled-in subtask_template
     qaplots_subtask_template = SubtaskTemplate.objects.get(name="QA plots")
-    qaplots_subtask_spec_doc = add_defaults_to_json_object_for_schema({}, qaplots_subtask_template.schema)
+    qaplots_subtask_spec_doc = add_defaults_to_json_object_for_schema({}, qaplots_subtask_template.schema, cache=_schema_cache)
     qaplots_subtask_spec_doc['autocorrelation'] = obs_task_qaplots_spec.get("autocorrelation")
     qaplots_subtask_spec_doc['crosscorrelation'] = obs_task_qaplots_spec.get("crosscorrelation")
     validate_json_against_schema(qaplots_subtask_spec_doc, qaplots_subtask_template.schema)
@@ -632,7 +635,7 @@ def create_qaplots_subtask_from_qafile_subtask(qafile_subtask: Subtask) -> Subta
 
     # step 2: create and link subtask input/output
     selection_template = TaskRelationSelectionTemplate.objects.get(name="all")
-    selection_doc = get_default_json_object_for_schema(selection_template.schema)
+    selection_doc = get_default_json_object_for_schema(selection_template.schema, cache=_schema_cache)
     qaplots_subtask_input = SubtaskInput.objects.create(subtask=qaplots_subtask,
                                                         producer=qafile_subtask.outputs.first(),
                                                         selection_doc=selection_doc,
@@ -667,8 +670,8 @@ def create_pipeline_subtask_from_task_blueprint(task_blueprint: TaskBlueprint, s
 
     # step 1: create subtask in defining state, with filled-in subtask_template
     subtask_template = SubtaskTemplate.objects.get(name=subtask_template_name)
-    default_subtask_specs = get_default_json_object_for_schema(subtask_template.schema)
-    task_specs_with_defaults = add_defaults_to_json_object_for_schema(task_blueprint.specifications_doc, task_blueprint.specifications_template.schema)
+    default_subtask_specs = get_default_json_object_for_schema(subtask_template.schema, cache=_schema_cache)
+    task_specs_with_defaults = add_defaults_to_json_object_for_schema(task_blueprint.specifications_doc, task_blueprint.specifications_template.schema, cache=_schema_cache)
     subtask_specs = generate_subtask_specs_from_task_spec_func(task_specs_with_defaults, default_subtask_specs)
 
     cluster_name = task_blueprint.specifications_doc.get("storage_cluster", "CEP4")
@@ -723,7 +726,7 @@ def create_ingest_subtask_from_task_blueprint(task_blueprint: TaskBlueprint) ->
 
     # step 1: create subtask in defining state, with filled-in subtask_template
     subtask_template = SubtaskTemplate.objects.get(name='ingest control')
-    default_subtask_specs = get_default_json_object_for_schema(subtask_template.schema)
+    default_subtask_specs = get_default_json_object_for_schema(subtask_template.schema, cache=_schema_cache)
     subtask_specs = default_subtask_specs  # todo: translate specs from task to subtask once we have non-empty templates
     cluster_name = task_blueprint.specifications_doc.get("storage_cluster", "CEP4")
     subtask_data = {"start_time": None,
@@ -766,7 +769,7 @@ def create_cleanup_subtask_from_task_blueprint(task_blueprint: TaskBlueprint) ->
 
     # step 1: create subtask in defining state, with filled-in subtask_template
     subtask_template = SubtaskTemplate.objects.get(name='cleanup')
-    subtask_specs = get_default_json_object_for_schema(subtask_template.schema)
+    subtask_specs = get_default_json_object_for_schema(subtask_template.schema, cache=_schema_cache)
     cluster_name = task_blueprint.specifications_doc.get("storage_cluster", "CEP4")
     subtask_data = {"start_time": None,
                     "stop_time": None,
@@ -1170,9 +1173,9 @@ def schedule_qafile_subtask(qafile_subtask: Subtask):
                                                                 dataformat=Dataformat.objects.get(value=Dataformat.Choices.QA_HDF5.value),
                                                                 datatype=Datatype.objects.get(value=Datatype.Choices.QUALITY.value),   # todo: is this correct?
                                                                 producer=qafile_subtask.outputs.first(),
-                                                                specifications_doc=get_default_json_object_for_schema(DataproductSpecificationsTemplate.objects.get(name="empty").schema),
+                                                                specifications_doc=get_default_json_object_for_schema(DataproductSpecificationsTemplate.objects.get(name="empty").schema, cache=_schema_cache),
                                                                 specifications_template=DataproductSpecificationsTemplate.objects.get(name="empty"),
-                                                                feedback_doc=get_default_json_object_for_schema(DataproductFeedbackTemplate.objects.get(name="empty").schema),
+                                                                feedback_doc=get_default_json_object_for_schema(DataproductFeedbackTemplate.objects.get(name="empty").schema, cache=_schema_cache),
                                                                 feedback_template=DataproductFeedbackTemplate.objects.get(name="empty"),
                                                                 sap=None  # todo: do we need to point to a SAP here? Of which dataproduct then?
                                                                 )
@@ -1223,9 +1226,9 @@ def schedule_qaplots_subtask(qaplots_subtask: Subtask):
                                                              dataformat=Dataformat.objects.get(value=Dataformat.Choices.QA_PLOTS.value),
                                                              datatype=Datatype.objects.get(value=Datatype.Choices.QUALITY.value),   # todo: is this correct?
                                                              producer=qaplots_subtask.outputs.first(),
-                                                             specifications_doc=get_default_json_object_for_schema(DataproductSpecificationsTemplate.objects.get(name="empty").schema),
+                                                             specifications_doc=get_default_json_object_for_schema(DataproductSpecificationsTemplate.objects.get(name="empty").schema, cache=_schema_cache),
                                                              specifications_template=DataproductSpecificationsTemplate.objects.get(name="empty"),
-                                                             feedback_doc=get_default_json_object_for_schema(DataproductFeedbackTemplate.objects.get(name="empty").schema),
+                                                             feedback_doc=get_default_json_object_for_schema(DataproductFeedbackTemplate.objects.get(name="empty").schema, cache=_schema_cache),
                                                              feedback_template=DataproductFeedbackTemplate.objects.get(name="empty"),
                                                              sap=None  # todo: do we need to point to a SAP here? Of which dataproduct then?
                                                              )
@@ -1336,7 +1339,7 @@ def schedule_observation_subtask(observation_subtask: Subtask):
     specifications_doc = observation_subtask.specifications_doc
     dataproduct_specifications_template = DataproductSpecificationsTemplate.objects.get(name="SAP")
     dataproduct_feedback_template = DataproductFeedbackTemplate.objects.get(name="empty")
-    dataproduct_feedback_doc = get_default_json_object_for_schema(dataproduct_feedback_template.schema)
+    dataproduct_feedback_doc = get_default_json_object_for_schema(dataproduct_feedback_template.schema, cache=_schema_cache)
 
 
     # select correct output for each pointing based on name
@@ -1433,7 +1436,7 @@ def schedule_observation_subtask(observation_subtask: Subtask):
                                          producer=observation_subtask.outputs.first(),  # todo: select correct output. I tried "subtask_output_dict[sap['name']]" but tests fail because the sap's name is not in the task blueprint. Maybe it's just test setup and this should work?
                                          specifications_doc={"sap": specifications_doc['stations']['digital_pointings'][sap_nr]["name"], "coherent": coherent, "identifiers": {"pipeline_index": pipeline_nr, "tab_index": tab_nr, "stokes_index": stokes_nr, "part_index": part_nr}},
                                          specifications_template=dataproduct_specifications_template_timeseries,
-                                         feedback_doc=get_default_json_object_for_schema(dataproduct_feedback_template.schema),
+                                         feedback_doc=get_default_json_object_for_schema(dataproduct_feedback_template.schema, cache=_schema_cache),
                                          feedback_template=dataproduct_feedback_template,
                                          size=0,
                                          expected_size=1024*1024*1024*tab_nr,
@@ -1503,7 +1506,7 @@ def _create_preprocessing_output_dataproducts_and_transforms(pipeline_subtask: S
                                 producer=pipeline_subtask_output,
                                 specifications_doc=input_dp.specifications_doc,
                                 specifications_template=dataproduct_specifications_template,
-                                feedback_doc=get_default_json_object_for_schema(dataproduct_feedback_template.schema),
+                                feedback_doc=get_default_json_object_for_schema(dataproduct_feedback_template.schema, cache=_schema_cache),
                                 feedback_template=dataproduct_feedback_template,
                                 sap=input_dp.sap,
                                 global_identifier=None) for input_dp in input_dataproducts]
@@ -1537,7 +1540,7 @@ def _create_pulsar_pipeline_output_dataproducts_and_transforms(pipeline_subtask:
                                 producer=pipeline_subtask_output,
                                 specifications_doc=input_dp.specifications_doc,
                                 specifications_template=dataproduct_specifications_template,
-                                feedback_doc=get_default_json_object_for_schema(dataproduct_feedback_template.schema),
+                                feedback_doc=get_default_json_object_for_schema(dataproduct_feedback_template.schema, cache=_schema_cache),
                                 feedback_template=dataproduct_feedback_template,
                                 sap=input_dp.sap,
                                 global_identifier=None) for input_dp in input_dataproducts]
@@ -1575,7 +1578,7 @@ def _create_pulsar_pipeline_output_dataproducts_and_transforms(pipeline_subtask:
                                 producer=pipeline_subtask_output,
                                 specifications_doc={ "coherent": is_coherent, "identifiers": { "obsid": obsid } },
                                 specifications_template=dataproduct_specifications_template,
-                                feedback_doc=get_default_json_object_for_schema(dataproduct_feedback_template.schema),
+                                feedback_doc=get_default_json_object_for_schema(dataproduct_feedback_template.schema, cache=_schema_cache),
                                 feedback_template=dataproduct_feedback_template,
                                 sap=None, # TODO: Can we say anything here, as summaries cover all SAPs
                                 global_identifier=None) for (obsid, is_coherent) in summaries}
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/tasks.py b/SAS/TMSS/backend/src/tmss/tmssapp/tasks.py
index a43d4d81c28c4cc5138f02645d1c9a0adbb066a2..d5c0e15c50188167ca595d9a18fc0a20d44ee680 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/tasks.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/tasks.py
@@ -1,7 +1,7 @@
 from lofar.sas.tmss.tmss.exceptions import *
 from lofar.sas.tmss.tmss.tmssapp import models
 from lofar.sas.tmss.tmss.tmssapp.subtasks import unschedule_subtasks_in_task_blueprint, cancel_subtask
-from lofar.sas.tmss.tmss.tmssapp.models.specification import TaskBlueprint, SchedulingUnitBlueprint, IOType, TaskTemplate, TaskType, TaskRelationSelectionTemplate
+from lofar.sas.tmss.tmss.tmssapp.models.specification import TaskBlueprint, SchedulingUnitBlueprint, SchedulingUnitDraft, IOType, TaskTemplate, TaskType, TaskRelationSelectionTemplate
 from lofar.sas.tmss.tmss.tmssapp.subtasks import create_and_schedule_subtasks_from_task_blueprint, create_subtasks_from_task_blueprint, schedule_independent_subtasks_in_task_blueprint, update_subtasks_start_times_for_scheduling_unit
 from lofar.common.datetimeutils import round_to_minute_precision
 from functools import cmp_to_key
@@ -15,6 +15,9 @@ from django.db import transaction
 
 logger = logging.getLogger(__name__)
 
+# cache for json schema's
+_schema_cache = {}
+
 def create_scheduling_unit_blueprint_from_scheduling_unit_draft(scheduling_unit_draft: models.SchedulingUnitDraft) -> models.SchedulingUnitBlueprint:
     """
     Create a SchedulingUnitBlueprint from the SchedulingUnitDraft
@@ -55,7 +58,7 @@ def copy_scheduling_unit_draft(scheduling_unit_draft: models.SchedulingUnitDraft
     task_drafts_copy = []
     scheduling_unit_draft_copy.save()
     for td in task_drafts:
-        task_drafts_copy.append(copy_task_draft(td))
+        task_drafts_copy.append(copy_task_draft(td, scheduling_unit_draft_copy.copy_reason))
     scheduling_unit_draft_copy.task_drafts.set(task_drafts_copy)
     scheduling_unit_draft_copy.save()
 
@@ -146,12 +149,14 @@ def create_task_drafts_from_scheduling_unit_draft(scheduling_unit_draft: models.
     if len(scheduling_unit_draft.requirements_doc.get("tasks", {})) == 0:
         raise BlueprintCreationException("create_task_drafts_from_scheduling_unit_draft: scheduling_unit_draft.id=%s has no tasks defined in its requirements_doc" % (scheduling_unit_draft.pk,))
 
+    schema_cache = {}
+
     for task_name, task_definition in scheduling_unit_draft.requirements_doc["tasks"].items():
         task_template_name = task_definition["specifications_template"]
         task_template = models.TaskTemplate.objects.get(name=task_template_name)
 
         task_specifications_doc = task_definition["specifications_doc"]
-        task_specifications_doc = add_defaults_to_json_object_for_schema(task_specifications_doc, task_template.schema)
+        task_specifications_doc = add_defaults_to_json_object_for_schema(task_specifications_doc, task_template.schema, cache=_schema_cache)
 
         try:
             logger.debug("creating task draft... task_name='%s', task_template_name='%s'", task_template_name, task_template_name)
@@ -464,7 +469,7 @@ def create_cleanuptask_for_scheduling_unit_blueprint(scheduling_unit_blueprint:
     with transaction.atomic():
         # create a cleanup task draft and blueprint....
         cleanup_template = models.TaskTemplate.objects.get(name="cleanup")
-        cleanup_spec_doc = get_default_json_object_for_schema(cleanup_template.schema)
+        cleanup_spec_doc = get_default_json_object_for_schema(cleanup_template.schema, cache=_schema_cache)
 
         cleanup_task_draft = models.TaskDraft.objects.create(
             name="Cleanup",
@@ -487,7 +492,7 @@ def create_cleanuptask_for_scheduling_unit_blueprint(scheduling_unit_blueprint:
 
         # ... and connect the outputs of the producing tasks to the cleanup, so the cleanup task knows what to remove.
         selection_template = TaskRelationSelectionTemplate.objects.get(name="all")
-        selection_doc = get_default_json_object_for_schema(selection_template.schema)
+        selection_doc = get_default_json_object_for_schema(selection_template.schema, cache=_schema_cache)
 
         for producer_task_blueprint in scheduling_unit_blueprint.task_blueprints.exclude(specifications_template__type=TaskType.Choices.CLEANUP).exclude(specifications_template__type=TaskType.Choices.INGEST).all():
             for connector_type in producer_task_blueprint.specifications_template.output_connector_types.filter(iotype__value=IOType.Choices.OUTPUT.value).all():
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/views.py b/SAS/TMSS/backend/src/tmss/tmssapp/views.py
index c043399964b788b809194e49c1c0b6872e57fdfe..f6b7cfaedf56319b831a9880228f87c0259d9d35 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/views.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/views.py
@@ -12,6 +12,7 @@ from rest_framework.authtoken.models import Token
 from rest_framework.permissions import AllowAny
 from rest_framework.decorators import authentication_classes, permission_classes
 from django.apps import apps
+import re
 
 from rest_framework.decorators import api_view
 from datetime import datetime
@@ -57,6 +58,15 @@ def task_specify_observation(request, pk=None):
     task = get_object_or_404(models.TaskDraft, pk=pk)
     return HttpResponse("response", content_type='text/plain')
 
+
+def authentication_state(request):
+    if not request.user.is_authenticated:
+        return JsonResponse({'is_authenticated': False})
+
+    return JsonResponse({'is_authenticated': True,
+                         'username': request.user.username})
+
+
 # Allow everybody to GET our publicly available template-json-schema's
 @permission_classes([AllowAny])
 @authentication_classes([AllowAny])
@@ -78,6 +88,32 @@ def get_template_json_schema(request, template:str, name:str, version:str):
     return response
 
 
+# Allow everybody to GET our publicly available LTA SIP XSD (XML Schema Definition for the LTA SIP)
+@permission_classes([AllowAny])
+@authentication_classes([AllowAny])
+@swagger_auto_schema(#method='GET',
+                     responses={200: 'Get the LTA SIP XSD',
+                                404: 'not available'},
+                     operation_description="Get the LTA SIP XSD.")
+#@api_view(['GET'])   # todo: !! decorating this as api_view somehow breaks json ref resolution !! fix this and double url issue in urls.py, then use decorator here to include in Swagger
+def get_lta_sip_xsd(request):
+
+    lta_sip_xsd_path = os.path.join(os.environ["LOFARROOT"], "share", "lta", "LTA-SIP.xsd")
+    with open(lta_sip_xsd_path, 'rt') as file:
+        xsd = file.read()
+
+        # hacky way of setting the namespace to this url
+        # can/should be done with proper xml dom setAttribute on document node.
+        # but this string manipulation is faster, and works just as well.
+        # the namespace should point to the absolute url of this request, without the document name.
+        abs_uri = "%s://%s/%s" % (request.scheme, request.get_host().rstrip('/'), request.get_full_path().lstrip('/'))
+        abs_uri = abs_uri[:abs_uri.rfind('/')]
+        for attr in ('targetNamespace', 'xmlns'):
+            xsd = xsd.replace('''%s="http://www.astron.nl/SIP-Lofar"'''%attr, '''%s="%s"'''%(attr,abs_uri))
+
+        return HttpResponse(content=xsd, content_type='application/xml')
+
+
 # Allow everybody to GET our publicly available station group lookups
 @permission_classes([AllowAny])
 @authentication_classes([AllowAny])
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/permissions.py b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/permissions.py
index 291e602d5832032000e0db6a09771e2238e69d78..c7cffaffd00ae8fb981b2acf3d5ca7c825dc5ba4 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/permissions.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/permissions.py
@@ -23,25 +23,16 @@ import urllib.parse
 
 def get_project_roles_for_user(user):
 
-        # todo: this set of project/role pairs needs to be provided by the OIDC federation and will probably enter TMSS
-        #  as a property on request.user. Create this for the requesting user in the following format:
-        # project_roles = ({'project': 'high', 'role': 'PI'},  # demo data
-        #                        {'project': 'low', 'role': 'Friend of Project'},  # demo data
-        #                        {'project': 'test_user_is_pi', 'role': 'PI'},  # for unittests
-        #                        {'project': 'test_user_is_contact', 'role': 'Contact Author'})  # for unittests
-        project_roles = ()
-
         # todo: stupid hack to make test pass, because we so far have failed mocking this function out successfully.
         #  Should not hit production!
         try:
             if user == models.User.objects.get(username='paulus'):
                 return ({'project': 'test_user_is_shared_support', 'role': 'shared_support_user'},
                         {'project': 'test_user_is_contact', 'role': 'contact_author'})
-                        #{'project': 'high', 'role': 'shared_support_user'})
         except:
             pass
 
-        return project_roles
+        return tuple(user.project_roles)
 
 
 def get_project_roles_with_permission(permission_name, method='GET'):
@@ -55,6 +46,7 @@ def get_project_roles_with_permission(permission_name, method='GET'):
         logger.error("This action was configured to enforce project permissions, but no project permission with name '%s' has been defined." % permission_name)
         return []
 
+
 class IsProjectMember(drf_permissions.DjangoObjectPermissions):
     """
     Object-level permission to only allow users of the related project to access it.
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/scheduling.py b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/scheduling.py
index 28ad1dbc6a6174dcb31ee2db8d88e0f954228001..df92de0ce14cdcd67cc4913818ee26ea7a140127 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/scheduling.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/scheduling.py
@@ -395,9 +395,24 @@ class DataproductViewSet(LOFARViewSet):
                          operation_description="Get the Submission Information Package (SIP) for this dataproduct")
     @action(methods=['get'], detail=True, url_name="sip")
     def sip(self, request, pk=None):
-        dataproduct = get_object_or_404(models.Dataproduct, pk=pk)
         from lofar.sas.tmss.tmss.tmssapp.adapters.sip import generate_sip_for_dataproduct
-        return HttpResponse(generate_sip_for_dataproduct(dataproduct).get_prettyxml(), content_type='application/xml')
+        from lofar.sas.tmss.tmss.tmssapp import views
+        from django.urls import reverse
+
+        # get the dataproduct...
+        dataproduct = get_object_or_404(models.Dataproduct, pk=pk)
+
+        # construct the schema loction for the sip
+        lta_sip_xsd_path = reverse(views.get_lta_sip_xsd)
+        lta_sip_xsd_uri = "%s://%s/%s" % (request.scheme, request.get_host().rstrip('/'), lta_sip_xsd_path.lstrip('/'))
+        # the schema_location should point to a weird 2 part url, the path -space- document.
+        schema_location = lta_sip_xsd_uri[:lta_sip_xsd_uri.rfind('/')] + ' ' + lta_sip_xsd_uri[lta_sip_xsd_uri.rfind('/')+1:]
+
+        # generate the sip
+        sip = generate_sip_for_dataproduct(dataproduct).get_prettyxml(schema_location=schema_location)
+
+        # and return it
+        return HttpResponse(sip, content_type='application/xml')
 
     @swagger_auto_schema(responses={200: 'The SIP graph for this dataproduct',
                                     403: 'forbidden'},
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py
index e2f0b0663b136e84bf3bba8f21200dfac82de836..870eea602378bc18aec5e146700c9d104f37af41 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py
@@ -5,7 +5,8 @@ This file contains the viewsets (based on the elsewhere defined data models and
 from django.shortcuts import get_object_or_404, get_list_or_404, render
 
 from django.http import JsonResponse
-from django.contrib.auth.models import User
+from django.contrib.auth import get_user_model
+User = get_user_model()
 from django_filters import rest_framework as filters
 import django_property_filter as property_filters
 from rest_framework.viewsets import ReadOnlyModelViewSet
@@ -560,25 +561,33 @@ class TaskDraftCopyViewSet(LOFARCopyViewSet):
     serializer_class = serializers.TaskDraftSerializer
 
     @swagger_auto_schema(responses={201: 'The new Task Draft',
-                                    403: 'forbidden'},
-                         operation_description="Copy a Task Draft to a new Task Draft")
+                                    400: 'bad request',
+                                    403: 'forbidden',
+                                    404: 'not found'},
+                         operation_description="Copy this Task Draft to a new Task Draft")
     def create(self, request, *args, **kwargs):
-        if 'task_draft_id' in kwargs:
-            task_draft = get_object_or_404(models.TaskDraft, pk=kwargs["task_draft_id"])
+        if 'id' in kwargs:
+            task_draft = get_object_or_404(models.TaskDraft, pk=kwargs["id"])
 
             body_unicode = request.body.decode('utf-8')
             body_data = json.loads(body_unicode)
 
             copy_reason = body_data.get('copy_reason', None)
+            if copy_reason == None:
+                content = {'Error': 'copy_reason is missing'}
+                return Response(content, status=status.HTTP_400_BAD_REQUEST)
 
             try:
                 copy_reason_obj = models.CopyReason.objects.get(value=copy_reason)
             except ObjectDoesNotExist:
                 logger.info("CopyReason matching query does not exist.")
-                #if a non valid copy_reason is specified, set copy_reason to None
-                copy_reason = None
+                #if a non valid copy_reason is specified, raise a 400 error
+                choices = [c.value for c in models.CopyReason.Choices]
+                content = {'Error': 'The provided copy_reason is not defined in the system. Possible choices are: %s' % choices}
+                return Response(content, status=status.HTTP_400_BAD_REQUEST)
 
-            task_draft_copy = copy_task_draft(task_draft,copy_reason)
+            # TODO: Update copy_task_draft() accordingly if needed.
+            task_draft_copy = copy_task_draft(task_draft,copy_reason_obj)
 
 
             # url path magic to construct the new task_draft_path url
@@ -592,7 +601,7 @@ class TaskDraftCopyViewSet(LOFARCopyViewSet):
                             status=status.HTTP_201_CREATED,
                             headers={'Location': task_draft_copy_path})
         else:
-            content = {'Error': 'scheduling_unit_draft_id is missing'}
+            content = {'Error': 'SchedulingUnitDraft id is missing'}
             return Response(content, status=status.HTTP_404_NOT_FOUND)
 
 
@@ -600,25 +609,32 @@ class SchedulingUnitDraftCopyViewSet(LOFARCopyViewSet):
     queryset = models.SchedulingUnitDraft.objects.all()
     serializer_class = serializers.SchedulingUnitDraftCopySerializer
 
-    @swagger_auto_schema(responses={201: 'The new scheduling_unit_draft',
-                                    403: 'forbidden'},
-                         operation_description="Copy a Scheduling Unit Draft to a new Scheduling Unit Draft")
+    @swagger_auto_schema(responses={201: 'The new SchedulingUnitDraft',
+                                    400: 'bad request',
+                                    403: 'forbidden',
+                                    404: 'not found'},
+                         operation_description="Copy a SchedulingUnitDraft to a new SchedulingUnitDraft")
     def create(self, request, *args, **kwargs):
-        if 'scheduling_unit_draft_id' in kwargs:
-            scheduling_unit_draft = get_object_or_404(models.SchedulingUnitDraft, pk=kwargs['scheduling_unit_draft_id'])
+        if 'id' in kwargs:
+            scheduling_unit_draft = get_object_or_404(models.SchedulingUnitDraft, pk=kwargs['id'])
             scheduling_set = scheduling_unit_draft.scheduling_set
 
             body_unicode = request.body.decode('utf-8')
             body_data = json.loads(body_unicode)
 
             copy_reason = body_data.get('copy_reason', None)
+            if copy_reason == None:
+                content = {'Error': 'copy_reason is missing'}
+                return Response(content, status=status.HTTP_400_BAD_REQUEST)
 
             try:
                 copy_reason_obj = models.CopyReason.objects.get(value=copy_reason)
             except ObjectDoesNotExist:
                 logger.info("CopyReason matching query does not exist.")
-                #if a non valid copy_reason is specified, set copy_reason to None
-                copy_reason = None
+                # if a non valid copy_reason is specified, raise a 400 error
+                choices = [c.value for c in models.CopyReason.Choices]
+                content = {'Error': 'The provided copy_reason is not defined in the system. Possible choices are: %s' % choices}
+                return Response(content, status=status.HTTP_400_BAD_REQUEST)
 
             scheduling_set_id = body_data.get('scheduling_set_id', None)
             logger.info(scheduling_set_id)
@@ -628,6 +644,7 @@ class SchedulingUnitDraftCopyViewSet(LOFARCopyViewSet):
                 except ObjectDoesNotExist:
                     logger.info("scheduling Set does not exist.")
 
+            # TODO: Change parameter from copy_reason to copy_reason_obj and update copy_scheduling_unit_draft() accordingly.
             scheduling_unit_draft_copy = copy_scheduling_unit_draft(scheduling_unit_draft,scheduling_set,copy_reason)
             # url path magic to construct the new scheduling_unit_draft_path url
             scheduling_unit_draft_path = request._request.path
@@ -639,7 +656,7 @@ class SchedulingUnitDraftCopyViewSet(LOFARCopyViewSet):
                             status=status.HTTP_201_CREATED,
                             headers={'Location': scheduling_unit_draft_copy_path})
         else:
-            content = {'Error': 'scheduling_unit_draft_id is missing'}
+            content = {'Error': 'SchedulingUnitDraft id is missing'}
             return Response(content, status=status.HTTP_404_NOT_FOUND)
 
 
@@ -654,29 +671,36 @@ class SchedulingUnitDraftCopyFromSchedulingSetViewSet(LOFARCopyViewSet):
         else:
             return models.SchedulingUnitDraft.objects.all()
 
-    @swagger_auto_schema(responses={201: "The TaskDrafts copied from the TaskDrafts in this Scheduling Unit Set",
-                                    403: 'forbidden'},
-                         operation_description="Create a copy of all the TaskDrafts in this Scheduling Unit Set.")
+    @swagger_auto_schema(responses={201: "The SchedulingUnitSet which will also contain the created new drafts",
+                                    400: 'bad request',
+                                    403: 'forbidden',
+                                    404: 'not found'},
+                         operation_description="Copy the SchedulingUnitDrafts in this SchedulingUnitSet to new SchedulingUnitDrafts")
     def create(self, request, *args, **kwargs):
-        if 'scheduling_set_id' in kwargs:
-            scheduling_set = get_object_or_404(models.SchedulingSet, pk=kwargs['scheduling_set_id'])
+        if 'id' in kwargs:
+            scheduling_set = get_object_or_404(models.SchedulingSet, pk=kwargs['id'])
             scheduling_unit_drafts = scheduling_set.scheduling_unit_drafts.all()
 
             body_unicode = request.body.decode('utf-8')
             body_data = json.loads(body_unicode)
 
-
             copy_reason = body_data.get('copy_reason', None)
+            if copy_reason == None:
+                content = {'Error': 'copy_reason is missing'}
+                return Response(content, status=status.HTTP_400_BAD_REQUEST)
 
             try:
                 copy_reason_obj = models.CopyReason.objects.get(value=copy_reason)
             except ObjectDoesNotExist:
                 logger.info("CopyReason matching query does not exist.")
-                #if a non valid copy_reason is specified, set copy_reason to None
-                copy_reason = None
+                # if a non valid copy_reason is specified, raise a 400 error
+                choices = [c.value for c in models.CopyReason.Choices]
+                content = {'Error': 'The provided copy_reason is not defined in the system. Possible choices are: %s' % choices}
+                return Response(content, status=status.HTTP_400_BAD_REQUEST)
 
             scheduling_unit_draft_copy_path=[]
             for scheduling_unit_draft in scheduling_unit_drafts:
+                # TODO: Change parameter from copy_reason to copy_reason_obj and update copy_scheduling_unit_draft() accordingly.
                 scheduling_unit_draft_copy = copy_scheduling_unit_draft(scheduling_unit_draft,scheduling_set,copy_reason)
                 # url path magic to construct the new scheduling_unit_draft url
                 copy_scheduling_unit_draft_path = request._request.path
@@ -686,35 +710,42 @@ class SchedulingUnitDraftCopyFromSchedulingSetViewSet(LOFARCopyViewSet):
             # just return as a response the serialized scheduling_set (with references to the created copy_scheduling_unit_draft(s)
             return Response(serializers.SchedulingSetSerializer(scheduling_set, context={'request':request}).data,status=status.HTTP_201_CREATED)
         else:
-            content = {'Error': 'scheduling_set_id is missing'}
+            content = {'Error': 'SchedulingSet id is missing'}
             return Response(content, status=status.HTTP_404_NOT_FOUND)
 
 class SchedulingUnitBlueprintCopyToSchedulingUnitDraftViewSet(LOFARCopyViewSet):
     queryset = models.SchedulingUnitBlueprint.objects.all()
     serializer_class = serializers.SchedulingUnitBlueprintCopyToSchedulingUnitDraftSerializer
 
-    @swagger_auto_schema(responses={201: "The copy of the SchedulingUnitDraft",
-                                    403: 'forbidden'},
-                         operation_description="Create a SchedulingUnitDraft from the SchedulingUnitBlueprint")
+    @swagger_auto_schema(responses={201: "The new SchedulingUnitDraft copied from this SchedulingUnitBlueprint",
+                                    400: 'bad request',
+                                    403: 'forbidden',
+                                    404: 'not found'},
+                         operation_description="Copy the SchedulingUnitBlueprint to a new SchedulingUnitDraft")
     def create(self, request, *args, **kwargs):
 
-        if 'scheduling_unit_blueprint_id' in kwargs:
-            scheduling_unit_blueprint = get_object_or_404(models.SchedulingUnitBlueprint, pk=kwargs['scheduling_unit_blueprint_id'])
+        if 'id' in kwargs:
+            scheduling_unit_blueprint = get_object_or_404(models.SchedulingUnitBlueprint, pk=kwargs['id'])
 
             body_unicode = request.body.decode('utf-8')
             body_data = json.loads(body_unicode)
 
-
             copy_reason = body_data.get('copy_reason', None)
+            if copy_reason == None:
+                content = {'Error': 'copy_reason is missing'}
+                return Response(content, status=status.HTTP_400_BAD_REQUEST)
 
             try:
                 copy_reason_obj = models.CopyReason.objects.get(value=copy_reason)
             except ObjectDoesNotExist:
                 logger.info("CopyReason matching query does not exist.")
-                #if a non valid copy_reason is specified, set copy_reason to None
-                copy_reason = None
+                # if a non valid copy_reason is specified, raise a 400 error
+                choices = [c.value for c in models.CopyReason.Choices]
+                content = {'Error': 'The provided copy_reason is not defined in the system. Possible choices are: %s' % choices}
+                return Response(content, status=status.HTTP_400_BAD_REQUEST)
 
-            scheduling_unit_draft = create_scheduling_unit_draft_from_scheduling_unit_blueprint(scheduling_unit_blueprint,copy_reason)
+            # TODO: Update create_scheduling_unit_draft_from_scheduling_unit_blueprint() accordingly if needed.
+            scheduling_unit_draft = create_scheduling_unit_draft_from_scheduling_unit_blueprint(scheduling_unit_blueprint,copy_reason_obj)
 
             # return a response with the new serialized scheduling_unit_blueprint (with references to the created task_blueprint(s) and (scheduled) subtasks)
             return Response(serializers.SchedulingUnitDraftSerializer(scheduling_unit_draft, context={'request':request}).data,
@@ -728,12 +759,33 @@ class TaskBlueprintCopyToTaskDraftViewSet(LOFARCopyViewSet):
     queryset = models.SchedulingUnitBlueprint.objects.all()
     serializer_class = serializers.SchedulingUnitBlueprintCopyToSchedulingUnitDraftSerializer
 
-    @swagger_auto_schema(responses={201: "The TaskDraft created from this TaskBlueprint",
-                                    403: 'forbidden'},
-                         operation_description="Copy this TaskBlueprint to a new TaskDraft.")
+    @swagger_auto_schema(responses={201: "The new TaskDraft created from this TaskBlueprint",
+                                    400: 'bad request',
+                                    403: 'forbidden',
+                                    404: 'not found'},
+                         operation_description="Copy this TaskBlueprint into a new TaskDraft.")
     def create(self, request, *args, **kwargs):
-        if 'task_blueprint_id' in kwargs:
-            task_blueprint = get_object_or_404(models.TaskBlueprint, pk=kwargs['task_blueprint_id'])
+        if 'id' in kwargs:
+            task_blueprint = get_object_or_404(models.TaskBlueprint, pk=kwargs['id'])
+
+            body_unicode = request.body.decode('utf-8')
+            body_data = json.loads(body_unicode)
+
+            copy_reason = body_data.get('copy_reason', None)
+            if copy_reason == None:
+                content = {'Error': 'copy_reason is missing'}
+                return Response(content, status=status.HTTP_400_BAD_REQUEST)
+
+            try:
+                copy_reason_obj = models.CopyReason.objects.get(value=copy_reason)
+            except ObjectDoesNotExist:
+                logger.info("CopyReason matching query does not exist.")
+                # if a non valid copy_reason is specified, raise a 400 error
+                choices = [c.value for c in models.CopyReason.Choices]
+                content = {'Error': 'The provided copy_reason is not defined in the system. Possible choices are: %s' % choices}
+                return Response(content, status=status.HTTP_400_BAD_REQUEST)
+
+            # TODO: Add copy_reason_obj and update the copy_task_blueprint_to_task_draft() accordingly.
             task_draft = copy_task_blueprint_to_task_draft(task_blueprint)
 
             # return a response with the new serialized scheduling_unit_blueprint (with references to the created task_blueprint(s) and (scheduled) subtasks)
@@ -741,7 +793,7 @@ class TaskBlueprintCopyToTaskDraftViewSet(LOFARCopyViewSet):
                         status=status.HTTP_201_CREATED)
 
         else:
-            content = {'Error': 'task_blueprint_id is missing'}
+            content = {'Error': 'TaskBlueprint id is missing'}
             return Response(content, status=status.HTTP_404_NOT_FOUND)
 
 
@@ -895,7 +947,9 @@ class TaskDraftViewSet(LOFARViewSet):
 
     # prefetch nested references in reverse models to avoid duplicate lookup queries
     queryset = queryset.prefetch_related('first_scheduling_relation__placement') \
-                       .prefetch_related('second_scheduling_relation__placement')
+                       .prefetch_related('second_scheduling_relation__placement') \
+                       .prefetch_related('specifications_template__type') \
+                       .prefetch_related('specifications_template__connector_types')
 
     # select all references to other models to avoid even more duplicate queries
     queryset = queryset.select_related('copies') \
@@ -1019,7 +1073,9 @@ class TaskBlueprintViewSet(LOFARViewSet):
     # prefetch nested references in reverse models to avoid duplicate lookup queries
     queryset = queryset.prefetch_related('first_scheduling_relation__placement') \
                        .prefetch_related('second_scheduling_relation__placement') \
-                       .prefetch_related('subtasks__specifications_template')
+                       .prefetch_related('subtasks__specifications_template') \
+                       .prefetch_related('specifications_template__connector_types')
+
 
     # use select_related for forward related references
     queryset = queryset.select_related('draft', 'specifications_template', 'specifications_template__type', 'scheduling_unit_blueprint')
diff --git a/SAS/TMSS/backend/src/tmss/urls.py b/SAS/TMSS/backend/src/tmss/urls.py
index c077e51431b29da1484c0653421d54c27a7a5f91..d34176d054977cce5a480ac3ac64df7db9ea1687 100644
--- a/SAS/TMSS/backend/src/tmss/urls.py
+++ b/SAS/TMSS/backend/src/tmss/urls.py
@@ -58,6 +58,7 @@ swagger_schema_view = get_schema_view(
 urlpatterns = [
     path('admin/', admin.site.urls),
     path('logout/', LogoutView.as_view(), name='logout'),
+    path('authentication_state/', views.authentication_state, name='authentication_state'),
     path('token-auth/', obtain_auth_token, name='obtain-token-auth'),
     path('token-deauth/', views.revoke_token_deauth, name='revoke-token-deauth'),
     path('docs/', include_docs_urls(title='TMSS API')),
@@ -67,6 +68,7 @@ urlpatterns = [
     #re_path('schemas/<str:template>/<str:name>/<str:version>', views.get_template_json_schema, name='get_template_json_schema'),  # !! use of regex here breaks reverse url lookup
     path('schemas/<str:template>/<str:name>/<str:version>', views.get_template_json_schema, name='get_template_json_schema'),   # !! two urls for same view break Swagger, one url break json ref resolution !!
     path('schemas/<str:template>/<str:name>/<str:version>/', views.get_template_json_schema, name='get_template_json_schema'),  # !! two urls for same view break Swagger, one url break json ref resolution !!
+    path('xsd/LTA-SIP.xsd', views.get_lta_sip_xsd, name='get_lta_sip_xsd'),
     #re_path('station_groups/<str:template_name>/<str:template_version>/<str:station_group>/?', views.get_stations_in_group, name='get_stations_in_group'), # !! use of regex here somehow breaks functionality (because parameters?) -> index page
     path('station_groups/<str:template_name>/<str:template_version>/<str:station_group>', views.get_stations_in_group, name='get_stations_in_group'),
     path('station_groups/<str:template_name>/<str:template_version>/<str:station_group>/', views.get_stations_in_group, name='get_stations_in_group'),
@@ -182,11 +184,11 @@ router.register(r'task_blueprint/(?P<task_blueprint_id>\d+)/task_relation_bluepr
 router.register(r'task_blueprint/(?P<task_blueprint_id>\d+)/subtask', viewsets.SubtaskNestedViewSet)
 
 # copy
-router.register(r'task_draft/(?P<task_draft_id>\d+)/copy', viewsets.TaskDraftCopyViewSet)
-router.register(r'task_blueprint/(?P<task_blueprint_id>\d+)/copy_to_task_draft', viewsets.TaskBlueprintCopyToTaskDraftViewSet)
-router.register(r'scheduling_set/(?P<scheduling_set_id>\d+)/copy_scheduling_unit_drafts', viewsets.SchedulingUnitDraftCopyFromSchedulingSetViewSet)
-router.register(r'scheduling_unit_draft/(?P<scheduling_unit_draft_id>\d+)/copy', viewsets.SchedulingUnitDraftCopyViewSet)
-router.register(r'scheduling_unit_blueprint/(?P<scheduling_unit_blueprint_id>\d+)/copy_to_scheduling_unit_draft', viewsets.SchedulingUnitBlueprintCopyToSchedulingUnitDraftViewSet)
+router.register(r'task_draft/(?P<id>\d+)/copy', viewsets.TaskDraftCopyViewSet)
+router.register(r'task_blueprint/(?P<id>\d+)/copy_to_draft', viewsets.TaskBlueprintCopyToTaskDraftViewSet)
+router.register(r'scheduling_set/(?P<id>\d+)/copy_drafts', viewsets.SchedulingUnitDraftCopyFromSchedulingSetViewSet)
+router.register(r'scheduling_unit_draft/(?P<id>\d+)/copy', viewsets.SchedulingUnitDraftCopyViewSet)
+router.register(r'scheduling_unit_blueprint/(?P<id>\d+)/copy_to_draft', viewsets.SchedulingUnitBlueprintCopyToSchedulingUnitDraftViewSet)
 
 
 # SCHEDULING
diff --git a/SAS/TMSS/backend/test/t_permissions.py b/SAS/TMSS/backend/test/t_permissions.py
index 35e49ca24e617bad635c6edc37d7142e8d7af004..e3a429cb36416b0bdd02992bc78b599090cd78de 100755
--- a/SAS/TMSS/backend/test/t_permissions.py
+++ b/SAS/TMSS/backend/test/t_permissions.py
@@ -51,8 +51,6 @@ from lofar.sas.tmss.test.tmss_test_data_rest import TMSSRESTTestDataCreator
 
 from django.test import TestCase
 
-from django.contrib.auth.models import User, Group, Permission
-
 class ProjectPermissionTestCase(TestCase):
     # This tests that the project permissions are enforced in light of the project roles that are externally provided
     # for the user through the user admin. This test does not rely on the project permissions as defined in the system,
diff --git a/SAS/TMSS/backend/test/t_permissions_system_roles.py b/SAS/TMSS/backend/test/t_permissions_system_roles.py
index 74c3d6c24088a38cf214a7038f22ccd9152241ff..980c36d4486b75d423e51ccdfa4f4e4cae08985d 100755
--- a/SAS/TMSS/backend/test/t_permissions_system_roles.py
+++ b/SAS/TMSS/backend/test/t_permissions_system_roles.py
@@ -66,7 +66,9 @@ test_data_creator = TMSSRESTTestDataCreator(BASE_URL, AUTH)
 
 from lofar.sas.tmss.tmss.tmssapp.viewsets.permissions import TMSSPermissions
 from lofar.sas.tmss.tmss.tmssapp.viewsets.scheduling import SubtaskViewSet
-from django.contrib.auth.models import User, Group
+from django.contrib.auth.models import Group
+from django.contrib.auth import get_user_model
+User = get_user_model()
 from lofar.sas.tmss.test.test_utils import set_subtask_state_following_allowed_transitions, Subtask
 
 class SystemPermissionTestCase(unittest.TestCase):
@@ -589,7 +591,7 @@ class SystemPermissionTestCase(unittest.TestCase):
 
         # Try to task_log subtask and assert Paulus can do it within the TO observer group permissions.
         response = GET_and_assert_equal_expected_code(self, BASE_URL + '/subtask/%s/task_log/' % self.obs_subtask_id,
-                                                      200,
+                                                      302,
                                                       auth=self.test_data_creator.auth)
 
 
diff --git a/SAS/TMSS/backend/test/t_tmssapp_authorization_REST_API.py b/SAS/TMSS/backend/test/t_tmssapp_authorization_REST_API.py
index c3425fff49aeb72e46426cd464a54163044b4296..662f75b7ca52765aa3ad88a320886601c2614485 100755
--- a/SAS/TMSS/backend/test/t_tmssapp_authorization_REST_API.py
+++ b/SAS/TMSS/backend/test/t_tmssapp_authorization_REST_API.py
@@ -46,6 +46,8 @@ from lofar.sas.tmss.client.tmss_http_rest_client import TMSSsession
 
 from lofar.common.test_utils import integration_test
 
+import urllib
+
 # todo: figure out why csrftoken is missing when using the TMSSRESTTestDataCreator while it is present when...
 # todo: ...running tmss manually and referring to that by overriding BASE_URL here:
 # BASE_URL = 'http://localhost:8008/api'
@@ -89,20 +91,31 @@ class OIDCSession(unittest.TestCase):
             session.verify = False
             r = session.get(BASE_URL + '/task_draft/?format=api')
             self.assertEqual(r.status_code, 401)
-            self.assertTrue("Authentication credentials were not provided" in r.content.decode('utf8'))
+            self.assertIn("Authentication credentials were not provided", r.content.decode('utf8'))
 
     @integration_test
     def test_failure_using_wrong_credentials(self):
         with self.assertRaises(ValueError) as err:
-            with TMSSsession(AUTH.username, 'wrong', BASE_URL.replace('/api', '')) as session:
+            with TMSSsession('nouser', 'wrongpassword', urllib.parse.urlparse(BASE_URL).hostname, urllib.parse.urlparse(BASE_URL).port):
                 pass
 
-        self.assertTrue('The username and/or password you specified are not correct' in str(err.exception))
+        self.assertIn('The username and/or password you specified are not correct', str(err.exception))
 
     @integration_test
     def test_success_using_correct_credentials(self):
-        with TMSSsession(AUTH.username, AUTH.password, BASE_URL.replace('/api', '')).session as session:
-            r = session.get(BASE_URL + '/task_draft/?format=api')
+        '''
+        Note: This test expects the test env AUTH credentials to work against the OpenID provider in use.
+        If that is not the case, set working credentials via OIDC_USERNAME and OIDC_PASSWORD env vars.
+        todo: fix this test for Keycloak. We can login and get redirected back to TMSS as expected,
+          but unexpectedly get a 401 then, i.e. the session is not authenticated even though it should be.
+          The callback looks the same as when the credentials are posted with the browser, and there we end up with
+          an authenticated session. (Not sure if this is somehow cookie-related or so, but Keycloak seems happy...)
+        '''
+        username = os.environ.get('OIDC_USERNAME', AUTH.username)
+        password = os.environ.get('OIDC_PASSWORD', AUTH.password)
+        with TMSSsession(username, password, urllib.parse.urlparse(BASE_URL).hostname, urllib.parse.urlparse(BASE_URL).port) as tmss_session:
+            r = tmss_session.session.get(BASE_URL + '/task_draft/')
+            logger.info(r.content.decode('utf8'))
             self.assertEqual(r.status_code, 200)
             self.assertTrue("Task Draft List" in r.content.decode('utf8'))
 
diff --git a/SAS/TMSS/backend/test/t_tmssapp_specification_REST_API.py b/SAS/TMSS/backend/test/t_tmssapp_specification_REST_API.py
index d7515c0afdd7169c391097f628cff0248a99bf1c..5f691e52387be626bf96b368c183d3a36e09fc18 100755
--- a/SAS/TMSS/backend/test/t_tmssapp_specification_REST_API.py
+++ b/SAS/TMSS/backend/test/t_tmssapp_specification_REST_API.py
@@ -43,7 +43,9 @@ from lofar.sas.tmss.test.tmss_test_environment_unittest_setup import *
 from lofar.sas.tmss.test.tmss_test_data_django_models import *
 from lofar.sas.tmss.tmss.tmssapp import models
 from lofar.sas.tmss.test.test_utils import assertUrlList
-from django.contrib.auth.models import User, Group, Permission
+from django.contrib.auth.models import Group, Permission
+from django.contrib.auth import get_user_model
+User = get_user_model()
 
 # import and setup test data creator
 from lofar.sas.tmss.test.tmss_test_data_rest import TMSSRESTTestDataCreator
@@ -644,8 +646,9 @@ class TaskConnectorTestCase(unittest.TestCase):
         PUT_and_assert_expected_response(self, BASE_URL + '/task_connector_type/9876789876/', test_data_creator.TaskConnectorType(task_template_url=self.task_template_url), 404, {})
 
     def test_task_connector_PUT(self):
-        tc_test_data1 = test_data_creator.TaskConnectorType(role="correlator", task_template_url=self.task_template_url)
-        tc_test_data2 = test_data_creator.TaskConnectorType(role="beamformer", task_template_url=self.task_template_url)
+        task_template_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskTemplate(), '/task_template/')
+        tc_test_data1 = test_data_creator.TaskConnectorType(role="correlator", task_template_url=task_template_url)
+        tc_test_data2 = test_data_creator.TaskConnectorType(role="beamformer", task_template_url=task_template_url)
 
         # POST new item, verify
         r_dict = POST_and_assert_expected_response(self, BASE_URL + '/task_connector_type/', tc_test_data1, 201, tc_test_data1)
@@ -1734,8 +1737,9 @@ class TaskRelationDraftTestCase(unittest.TestCase):
         cls.producer_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskDraft(), '/task_draft/')
         cls.consumer_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskDraft(), '/task_draft/')
         cls.template_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskRelationSelectionTemplate(), '/task_relation_selection_template/')
-        cls.input_role_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskConnectorType(), '/task_connector_type/')
-        cls.output_role_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskConnectorType(), '/task_connector_type/')
+        cls.task_template_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskTemplate(), '/task_template/')
+        cls.input_role_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskConnectorType(iotype="input", task_template_url=cls.task_template_url), '/task_connector_type/')
+        cls.output_role_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskConnectorType(iotype="output", task_template_url=cls.task_template_url), '/task_connector_type/')
 
     def test_task_relation_draft_list_apiformat(self):
         r = requests.get(BASE_URL + '/task_relation_draft/?format=api', auth=AUTH)
@@ -1746,7 +1750,7 @@ class TaskRelationDraftTestCase(unittest.TestCase):
         GET_and_assert_equal_expected_code(self, BASE_URL + '/task_relation_draft/1234321/', 404)
 
     def test_task_relation_draft_POST_and_GET(self):
-        trd_test_data = test_data_creator.TaskRelationDraft(producer_url=self.producer_url, consumer_url=self.consumer_url, template_url=self.template_url, input_role_url=self.input_role_url)
+        trd_test_data = test_data_creator.TaskRelationDraft(producer_url=self.producer_url, consumer_url=self.consumer_url, template_url=self.template_url)
 
         # POST and GET a new item and assert correctness
         r_dict = POST_and_assert_expected_response(self, BASE_URL + '/task_relation_draft/', trd_test_data, 201, trd_test_data)
@@ -1758,7 +1762,9 @@ class TaskRelationDraftTestCase(unittest.TestCase):
         PUT_and_assert_expected_response(self, BASE_URL + '/task_relation_draft/9876789876/', trd_test_data, 404, {})
 
     def test_task_relation_draft_PUT(self):
-        trd_test_data1 = test_data_creator.TaskRelationDraft(producer_url=self.producer_url, consumer_url=self.consumer_url, template_url=self.template_url, input_role_url=self.input_role_url)
+        task_template_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskTemplate(), '/task_template/')
+        input_role_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskConnectorType(iotype="input", task_template_url=task_template_url), '/task_connector_type/')
+        trd_test_data1 = test_data_creator.TaskRelationDraft(producer_url=self.producer_url, consumer_url=self.consumer_url, template_url=self.template_url, input_role_url=input_role_url)
 
         # POST new item, verify
         r_dict = POST_and_assert_expected_response(self, BASE_URL + '/task_relation_draft/', trd_test_data1, 201, trd_test_data1)
@@ -1766,7 +1772,10 @@ class TaskRelationDraftTestCase(unittest.TestCase):
         GET_OK_and_assert_equal_expected_response(self, url, trd_test_data1)
 
         # PUT new values, verify
-        trd_test_data2 = test_data_creator.TaskRelationDraft(producer_url=self.producer_url, consumer_url=test_data_creator.post_data_and_get_url(test_data_creator.TaskDraft(),'/task_draft/'), template_url=self.template_url, input_role_url=self.input_role_url)
+        task_template_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskTemplate(), '/task_template/')
+        input_role_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskConnectorType(iotype="input", task_template_url=task_template_url), '/task_connector_type/')
+        new_consumer_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskDraft(), '/task_draft/')
+        trd_test_data2 = test_data_creator.TaskRelationDraft(producer_url=self.producer_url, consumer_url=new_consumer_url, template_url=self.template_url, input_role_url=input_role_url)
         PUT_and_assert_expected_response(self, url, trd_test_data2, 200, trd_test_data2)
         GET_OK_and_assert_equal_expected_response(self, url, trd_test_data2)
 
@@ -1848,7 +1857,8 @@ class TaskRelationDraftTestCase(unittest.TestCase):
         GET_and_assert_equal_expected_code(self, url, 404)
 
     def test_task_relation_draft_CASCADE_behavior_on_input_deleted(self):
-        input_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskConnectorType(), '/task_connector_type/')
+        task_template_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskTemplate(), '/task_template/')
+        input_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskConnectorType(task_template_url=task_template_url), '/task_connector_type/')
         trd_test_data = test_data_creator.TaskRelationDraft(input_role_url=input_url, producer_url=self.producer_url, consumer_url=self.consumer_url, template_url=self.template_url)
 
         # POST new item
@@ -1865,7 +1875,8 @@ class TaskRelationDraftTestCase(unittest.TestCase):
         GET_and_assert_equal_expected_code(self, url, 404)
 
     def test_task_relation_draft_CASCADE_behavior_on_output_deleted(self):
-        output_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskConnectorType(), '/task_connector_type/')
+        task_template_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskTemplate(), '/task_template/')
+        output_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskConnectorType(task_template_url=task_template_url), '/task_connector_type/')
         trd_test_data = test_data_creator.TaskRelationDraft(output_role_url=output_url, producer_url=self.producer_url, consumer_url=self.consumer_url, template_url=self.template_url, input_role_url=self.input_role_url)
 
         # POST new item with dependency
@@ -2320,8 +2331,9 @@ class TaskRelationBlueprintTestCase(unittest.TestCase):
         cls.producer_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskBlueprint(), '/task_blueprint/')
         cls.consumer_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskBlueprint(), '/task_blueprint/')
         cls.template_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskRelationSelectionTemplate(), '/task_relation_selection_template/')
-        cls.input_role_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskConnectorType(), '/task_connector_type/')
-        cls.output_role_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskConnectorType(), '/task_connector_type/')
+        cls.task_template_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskTemplate(), '/task_template/')
+        cls.input_role_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskConnectorType(iotype="input", task_template_url=cls.task_template_url), '/task_connector_type/')
+        cls.output_role_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskConnectorType(iotype="output", task_template_url=cls.task_template_url), '/task_connector_type/')
 
     def test_task_relation_blueprint_list_apiformat(self):
         r = requests.get(BASE_URL + '/task_relation_blueprint/?format=api', auth=AUTH)
@@ -2332,7 +2344,7 @@ class TaskRelationBlueprintTestCase(unittest.TestCase):
         GET_and_assert_equal_expected_code(self, BASE_URL + '/task_relation_blueprint/1234321/', 404)
 
     def test_task_relation_blueprint_POST_and_GET(self):
-        trb_test_data = test_data_creator.TaskRelationBlueprint(draft_url=self.draft_url, template_url=self.template_url, input_role_url=self.input_role_url, consumer_url=self.consumer_url, producer_url=self.producer_url)
+        trb_test_data = test_data_creator.TaskRelationBlueprint(draft_url=self.draft_url, producer_url=self.producer_url, consumer_url=self.consumer_url, template_url=self.template_url, input_role_url=self.input_role_url, output_role_url=self.output_role_url)
 
         # POST and GET a new item and assert correctness
         r_dict = POST_and_assert_expected_response(self, BASE_URL + '/task_relation_blueprint/', trb_test_data, 201, trb_test_data)
@@ -2344,20 +2356,25 @@ class TaskRelationBlueprintTestCase(unittest.TestCase):
         PUT_and_assert_expected_response(self, BASE_URL + '/task_relation_blueprint/9876789876/', trb_test_data, 404, {})
 
     def test_task_relation_blueprint_PUT(self):
-        trb_test_data1 = test_data_creator.TaskRelationBlueprint(draft_url=self.draft_url, template_url=self.template_url, input_role_url=self.input_role_url, consumer_url=self.consumer_url, producer_url=self.producer_url)
+        task_template_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskTemplate(), '/task_template/')
+        input_role_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskConnectorType(iotype="input", task_template_url=task_template_url), '/task_connector_type/')
+        trd_test_data1 = test_data_creator.TaskRelationBlueprint(draft_url=self.draft_url, producer_url=self.producer_url, consumer_url=self.consumer_url, template_url=self.template_url, input_role_url=input_role_url)
 
         # POST new item, verify
-        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/task_relation_blueprint/', trb_test_data1, 201, trb_test_data1)
+        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/task_relation_blueprint/', trd_test_data1, 201, trd_test_data1)
         url = r_dict['url']
-        GET_OK_and_assert_equal_expected_response(self, url, trb_test_data1)
+        GET_OK_and_assert_equal_expected_response(self, url, trd_test_data1)
 
         # PUT new values, verify
-        trb_test_data2 = test_data_creator.TaskRelationBlueprint(draft_url=self.draft_url, template_url=self.template_url, input_role_url=self.input_role_url, consumer_url=test_data_creator.post_data_and_get_url(test_data_creator.TaskBlueprint(),'/task_blueprint/'), producer_url=self.producer_url)
-        PUT_and_assert_expected_response(self, url, trb_test_data2, 200, trb_test_data2)
-        GET_OK_and_assert_equal_expected_response(self, url, trb_test_data2)
+        task_template_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskTemplate(), '/task_template/')
+        input_role_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskConnectorType(iotype="input", task_template_url=task_template_url), '/task_connector_type/')
+        new_consumer_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskBlueprint(), '/task_blueprint/')
+        trd_test_data2 = test_data_creator.TaskRelationBlueprint(draft_url=self.draft_url, producer_url=self.producer_url, consumer_url=new_consumer_url, template_url=self.template_url, input_role_url=input_role_url)
+        PUT_and_assert_expected_response(self, url, trd_test_data2, 200, trd_test_data2)
+        GET_OK_and_assert_equal_expected_response(self, url, trd_test_data2)
 
     def test_task_relation_blueprint_PATCH(self):
-        trb_test_data = test_data_creator.TaskRelationBlueprint(draft_url=self.draft_url, template_url=self.template_url, input_role_url=self.input_role_url, consumer_url=self.consumer_url, producer_url=self.producer_url)
+        trb_test_data = test_data_creator.TaskRelationBlueprint(draft_url=self.draft_url, producer_url=self.producer_url, consumer_url=self.consumer_url, template_url=self.template_url)
 
         # POST new item, verify
         r_dict = POST_and_assert_expected_response(self, BASE_URL + '/task_relation_blueprint/', trb_test_data, 201, trb_test_data)
@@ -2373,7 +2390,7 @@ class TaskRelationBlueprintTestCase(unittest.TestCase):
         GET_OK_and_assert_equal_expected_response(self, url, expected_data)
 
     def test_task_relation_blueprint_DELETE(self):
-        trb_test_data = test_data_creator.TaskRelationBlueprint(draft_url=self.draft_url, template_url=self.template_url, input_role_url=self.input_role_url, consumer_url=self.consumer_url, producer_url=self.producer_url)
+        trb_test_data = test_data_creator.TaskRelationBlueprint(draft_url=self.draft_url, producer_url=self.producer_url, consumer_url=self.consumer_url, template_url=self.template_url)
 
         # POST new item, verify
         r_dict = POST_and_assert_expected_response(self, BASE_URL + '/task_relation_blueprint/', trb_test_data, 201, trb_test_data)
@@ -2451,7 +2468,7 @@ class TaskRelationBlueprintTestCase(unittest.TestCase):
 
     def test_task_relation_blueprint_CASCADE_behavior_on_task_relation_selection_template_deleted(self):
         template_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskRelationSelectionTemplate(), '/task_relation_selection_template/')
-        trb_test_data = test_data_creator.TaskRelationBlueprint(draft_url=self.draft_url, template_url=template_url, input_role_url=self.input_role_url, consumer_url=self.consumer_url, producer_url=self.producer_url)
+        trb_test_data = test_data_creator.TaskRelationBlueprint(draft_url=self.draft_url, template_url=template_url, producer_url=self.producer_url, consumer_url=self.consumer_url)
 
         # POST new item
         url = POST_and_assert_expected_response(self, BASE_URL + '/task_relation_blueprint/',  trb_test_data, 201, trb_test_data)['url']
@@ -2500,8 +2517,9 @@ class TaskRelationBlueprintTestCase(unittest.TestCase):
         GET_and_assert_equal_expected_code(self, url, 404)
 
     def test_task_relation_blueprint_CASCADE_behavior_on_input_deleted(self):
-        input_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskConnectorType(), '/task_connector_type/')
-        trb_test_data = test_data_creator.TaskRelationBlueprint(draft_url=self.draft_url, template_url=self.template_url, input_role_url=input_url, consumer_url=self.consumer_url, producer_url=self.producer_url)
+        task_template_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskTemplate(), '/task_template/')
+        input_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskConnectorType(task_template_url=task_template_url), '/task_connector_type/')
+        trb_test_data = test_data_creator.TaskRelationBlueprint(draft_url=self.draft_url, input_role_url=input_url, producer_url=self.producer_url, consumer_url=self.consumer_url, template_url=self.template_url)
 
         # POST new item
         url = POST_and_assert_expected_response(self, BASE_URL + '/task_relation_blueprint/',
@@ -2517,8 +2535,9 @@ class TaskRelationBlueprintTestCase(unittest.TestCase):
         GET_and_assert_equal_expected_code(self, url, 404)
 
     def test_task_relation_blueprint_CASCADE_behavior_on_output_deleted(self):
-        output_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskConnectorType(), '/task_connector_type/')
-        trb_test_data = test_data_creator.TaskRelationBlueprint(draft_url=self.draft_url, template_url=self.template_url, input_role_url=self.input_role_url, output_role_url=output_url, consumer_url=self.consumer_url, producer_url=self.producer_url)
+        task_template_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskTemplate(), '/task_template/')
+        output_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskConnectorType(task_template_url=task_template_url), '/task_connector_type/')
+        trb_test_data = test_data_creator.TaskRelationBlueprint(draft_url=self.draft_url, output_role_url=output_url, producer_url=self.producer_url, consumer_url=self.consumer_url, template_url=self.template_url, input_role_url=self.input_role_url)
 
         # POST new item with dependency
         url = POST_and_assert_expected_response(self, BASE_URL + '/task_relation_blueprint/',
diff --git a/SAS/TMSS/backend/test/t_tmssapp_specification_django_API.py b/SAS/TMSS/backend/test/t_tmssapp_specification_django_API.py
index f1218237f3ff7b8ee8a7d70e24c3486d081cf500..3639b0fbe6efa756c15c156a925248310e62d834 100755
--- a/SAS/TMSS/backend/test/t_tmssapp_specification_django_API.py
+++ b/SAS/TMSS/backend/test/t_tmssapp_specification_django_API.py
@@ -44,8 +44,7 @@ from lofar.sas.tmss.test.tmss_test_data_django_models import *
 from django.db.utils import IntegrityError
 from django.core.exceptions import ValidationError
 from django.db.models.deletion import ProtectedError
-from lofar.sas.tmss.tmss.exceptions import SchemaValidationException
-from lofar.sas.tmss.tmss.exceptions import TMSSException
+from lofar.sas.tmss.tmss.exceptions import SchemaValidationException, BlueprintCreationException, TMSSException
 
 class GeneratorTemplateTest(unittest.TestCase):
     def test_GeneratorTemplate_gets_created_with_correct_creation_timestamp(self):
@@ -850,239 +849,264 @@ class SchedulingUnitBlueprintTest(unittest.TestCase):
         scheduling_unit_blueprint.scheduling_constraints_doc = {'foo': 'matic'}
         scheduling_unit_blueprint.save()
 
+    def test_SchedulingUnitBlueprint_gets_created_with_correct_is_triggered_flag(self):
 
-# class TaskBlueprintTest(unittest.TestCase):
-#     @classmethod
-#     def setUpClass(cls) -> None:
-#         cls.task_draft = models.TaskDraft.objects.create(**TaskDraft_test_data())
-#         cls.scheduling_unit_blueprint = models.SchedulingUnitBlueprint.objects.create(**SchedulingUnitBlueprint_test_data())
-#
-#     def test_TaskBlueprint_gets_created_with_correct_creation_timestamp(self):
-#
-#         # setup
-#         before = datetime.utcnow()
-#         entry = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
-#
-#         after = datetime.utcnow()
-#
-#         # assert
-#         self.assertLess(before, entry.created_at)
-#         self.assertGreater(after, entry.created_at)
-#
-#     def test_TaskBlueprint_update_timestamp_gets_changed_correctly(self):
-#
-#         # setup
-#         entry = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
-#         before = datetime.utcnow()
-#         entry.save()
-#         after = datetime.utcnow()
-#
-#         # assert
-#         self.assertLess(before, entry.updated_at)
-#         self.assertGreater(after, entry.updated_at)
-#
-#     def test_TaskBlueprint_prevents_missing_template(self):
-#
-#         # setup
-#         test_data = dict(TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
-#         test_data['specifications_template'] = None
-#
-#         # assert
-#         with self.assertRaises(IntegrityError):
-#             models.TaskBlueprint.objects.create(**test_data)
-#
-#     def test_TaskBlueprint_prevents_missing_draft(self):
-#
-#         # setup
-#         test_data = dict(TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
-#         test_data['draft'] = None
-#
-#         # assert
-#         with self.assertRaises(IntegrityError):
-#             models.TaskBlueprint.objects.create(**test_data)
-#
-#     def test_TaskBlueprint_prevents_draft_deletion(self):
-#         # setup
-#         test_data = dict(TaskBlueprint_test_data())
-#         blueprint = models.TaskBlueprint.objects.create(**test_data)
-#         draft = blueprint.draft
-#         with self.assertRaises(ProtectedError):
-#             draft.delete()
-#
-#     def test_TaskBlueprint_prevents_missing_scheduling_unit_blueprint(self):
-#
-#         # setup
-#         test_data = dict(TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
-#         test_data['scheduling_unit_blueprint'] = None
-#
-#         # assert
-#         with self.assertRaises(IntegrityError):
-#             models.TaskBlueprint.objects.create(**test_data)
-#
-#     def test_TaskBlueprint_predecessors_and_successors_none(self):
-#         task_blueprint_1: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
-#         task_blueprint_2: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
-#
-#         self.assertEqual(set(), set(task_blueprint_1.predecessors.all()))
-#         self.assertEqual(set(), set(task_blueprint_2.predecessors.all()))
-#         self.assertEqual(set(), set(task_blueprint_1.successors.all()))
-#         self.assertEqual(set(), set(task_blueprint_2.successors.all()))
-#
-#     def test_TaskBlueprint_predecessors_and_successors_simple(self):
-#         task_blueprint_1: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
-#         task_blueprint_2: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
-#
-#         models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_1, consumer=task_blueprint_2))
-#
-#         self.assertEqual(task_blueprint_1, task_blueprint_2.predecessors.all()[0])
-#         self.assertEqual(task_blueprint_2, task_blueprint_1.successors.all()[0])
-#
-#     def test_TaskBlueprint_predecessors_and_successors_complex(self):
-#         task_blueprint_1: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4())))
-#         task_blueprint_2: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint))
-#         task_blueprint_3: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint))
-#         task_blueprint_4: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint))
-#         task_blueprint_5: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint))
-#         task_blueprint_6: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint))
-#
-#         # ST1 ---> ST3 ---> ST4
-#         #      |        |
-#         # ST2 -          -> ST5 ---> ST6
-#
-#         models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_1, consumer=task_blueprint_3))
-#         trb1 = models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_2, consumer=task_blueprint_3))
-#         models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_3, consumer=task_blueprint_4))
-#         models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_3, consumer=task_blueprint_5))
-#         models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_5, consumer=task_blueprint_6))
-#
-#         self.assertEqual(set((task_blueprint_1, task_blueprint_2)), set(task_blueprint_3.predecessors.all()))
-#         self.assertEqual(set((task_blueprint_4, task_blueprint_5)), set(task_blueprint_3.successors.all()))
-#         self.assertEqual(set((task_blueprint_3,)), set(task_blueprint_4.predecessors.all()))
-#         self.assertEqual(set((task_blueprint_3,)), set(task_blueprint_5.predecessors.all()))
-#         self.assertEqual(set((task_blueprint_3,)), set(task_blueprint_1.successors.all()))
-#         self.assertEqual(set((task_blueprint_3,)), set(task_blueprint_2.successors.all()))
-#         self.assertEqual(set(), set(task_blueprint_1.predecessors.all()))
-#         self.assertEqual(set(), set(task_blueprint_2.predecessors.all()))
-#         self.assertEqual(set(), set(task_blueprint_4.successors.all()))
-#         self.assertEqual(set((task_blueprint_6,)), set(task_blueprint_5.successors.all()))
-#
-#
-# class TaskRelationBlueprintTest(unittest.TestCase):
-#     @classmethod
-#     def setUpClass(cls) -> None:
-#         cls.producer = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data())
-#         cls.consumer = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data())
-#
-#     def test_TaskRelationBlueprint_gets_created_with_correct_creation_timestamp(self):
-#         # setup
-#         before = datetime.utcnow()
-#         entry = models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
-#
-#         after = datetime.utcnow()
-#
-#         # assert
-#         self.assertLess(before, entry.created_at)
-#         self.assertGreater(after, entry.created_at)
-#
-#     def test_TaskRelationBlueprint_update_timestamp_gets_changed_correctly(self):
-#         # setup
-#         entry = models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
-#         before = datetime.utcnow()
-#         entry.save()
-#         after = datetime.utcnow()
-#
-#         # assert
-#         self.assertLess(before, entry.updated_at)
-#         self.assertGreater(after, entry.updated_at)
-#
-#     def test_TaskRelationBlueprint_prevents_missing_selection_template(self):
-#         # setup
-#         test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
-#         test_data['selection_template'] = None
-#
-#         # assert
-#         with self.assertRaises(IntegrityError):
-#             models.TaskRelationBlueprint.objects.create(**test_data)
-#
-#     def test_TaskRelationBlueprint_prevents_missing_draft(self):
-#         # setup
-#         test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
-#         test_data['draft'] = None
-#
-#         # assert
-#         with self.assertRaises(IntegrityError):
-#             models.TaskRelationBlueprint.objects.create(**test_data)
-#
-#     def test_TaskRelationBlueprint_prevents_missing_producer(self):
-#         # setup
-#         test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
-#         test_data['producer'] = None
-#
-#         # assert
-#         with self.assertRaises(IntegrityError):
-#             models.TaskRelationBlueprint.objects.create(**test_data)
-#
-#     def test_TaskRelationBlueprint_prevents_missing_consumer(self):
-#         # setup
-#         test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
-#         test_data['consumer'] = None
-#
-#         # assert
-#         with self.assertRaises(IntegrityError):
-#             models.TaskRelationBlueprint.objects.create(**test_data)
-#
-#     def test_TaskRelationBlueprint_prevents_missing_input(self):
-#         # setup
-#         test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
-#         test_data['input_role'] = None
-#
-#         # assert
-#         with self.assertRaises(IntegrityError):
-#             models.TaskRelationBlueprint.objects.create(**test_data)
-#
-#     def test_TaskRelationBlueprint_prevents_missing_output(self):
-#         # setup
-#         test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
-#         test_data['output_role'] = None
-#
-#         # assert
-#         with self.assertRaises(IntegrityError):
-#             models.TaskRelationBlueprint.objects.create(**test_data)
-#
-#
-#
-#
-# class TestStationTimeLine(unittest.TestCase):
-#     """
-#     Actually this simple testcase should be in a separate module (t_tmssapp_calculations_django_API.py)
-#     but I was just lazy and spare some overhead and I just 'piggyback' with this module
-#     """
-#
-#     def test_StationTimeline_raises_Error_on_duplicate_station_timeline(self):
-#         """
-#         Test if adding a duplicate station-timestamp combination leads to an Error and so data is not inserted
-#         """
-#         import datetime
-#
-#         test_data = {"station_name": "CS001",
-#                      "timestamp": datetime.date(2021, 4, 1),
-#                      "sunrise_start": datetime.datetime(year=2021, month=4, day=1, hour=6, minute=1, second=0),
-#                      "sunrise_end": datetime.datetime(year=2021, month=4, day=1, hour=7, minute=2, second=0),
-#                      "sunset_start": datetime.datetime(year=2021, month=4, day=1, hour=20, minute=31, second=0),
-#                      "sunset_end": datetime.datetime(year=2021, month=4, day=1, hour=21, minute=33, second=0) }
-#
-#         models.StationTimeline.objects.create(**test_data)
-#         with self.assertRaises(IntegrityError) as context:
-#             models.StationTimeline.objects.create(**test_data)
-#             self.assertIn('unique_station_time_line', str(context.exception))
-#
-#         self.assertEqual(len(models.StationTimeline.objects.filter(timestamp=datetime.date(2021, 4, 1))), 1)
-#         self.assertEqual(len(models.StationTimeline.objects.all()), 1)
-#         # Add a non-duplicate
-#         test_data["station_name"] = "CS002"
-#         models.StationTimeline.objects.create(**test_data)
-#         self.assertEqual(len(models.StationTimeline.objects.filter(timestamp=datetime.date(2021, 4, 1))), 2)
-#         self.assertEqual(len(models.StationTimeline.objects.all()), 2)
+        # setup
+        project = models.Project.objects.create(**Project_test_data(can_trigger=True))
+        scheduling_set = models.SchedulingSet.objects.create(**SchedulingSet_test_data(project=project))
+        constraints_template = models.SchedulingConstraintsTemplate.objects.get(name="constraints")
+        scheduling_unit_draft = models.SchedulingUnitDraft.objects.create(**SchedulingUnitDraft_test_data(scheduling_set=scheduling_set, is_triggered=True, scheduling_constraints_template=constraints_template))
+        scheduling_unit_blueprint = models.SchedulingUnitBlueprint.objects.create(**SchedulingUnitBlueprint_test_data(draft=scheduling_unit_draft))
+
+        # assert
+        self.assertEqual(scheduling_unit_blueprint.is_triggered, scheduling_unit_blueprint.is_triggered)
+
+    def test_SchedulingUnitBlueprint_prevents_triggers_if_project_does_not_allow_triggers(self):
+
+        # setup
+        project = models.Project.objects.create(**Project_test_data(can_trigger=False))
+        scheduling_set = models.SchedulingSet.objects.create(**SchedulingSet_test_data(project=project))
+        scheduling_unit_draft = models.SchedulingUnitDraft.objects.create(**SchedulingUnitDraft_test_data(scheduling_set=scheduling_set, is_triggered=True))
+
+        # assert
+        with self.assertRaises(BlueprintCreationException) as context:
+            models.SchedulingUnitBlueprint.objects.create(**SchedulingUnitBlueprint_test_data(draft=scheduling_unit_draft))
+
+        self.assertIn('does not allow triggering', str(context.exception))
+
+
+class TaskBlueprintTest(unittest.TestCase):
+    @classmethod
+    def setUpClass(cls) -> None:
+        cls.task_draft = models.TaskDraft.objects.create(**TaskDraft_test_data())
+        cls.scheduling_unit_blueprint = models.SchedulingUnitBlueprint.objects.create(**SchedulingUnitBlueprint_test_data())
+
+    def test_TaskBlueprint_gets_created_with_correct_creation_timestamp(self):
+
+        # setup
+        before = datetime.utcnow()
+        entry = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
+
+        after = datetime.utcnow()
+
+        # assert
+        self.assertLess(before, entry.created_at)
+        self.assertGreater(after, entry.created_at)
+
+    def test_TaskBlueprint_update_timestamp_gets_changed_correctly(self):
+
+        # setup
+        entry = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
+        before = datetime.utcnow()
+        entry.save()
+        after = datetime.utcnow()
+
+        # assert
+        self.assertLess(before, entry.updated_at)
+        self.assertGreater(after, entry.updated_at)
+
+    def test_TaskBlueprint_prevents_missing_template(self):
+
+        # setup
+        test_data = dict(TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
+        test_data['specifications_template'] = None
+
+        # assert
+        with self.assertRaises(IntegrityError):
+            models.TaskBlueprint.objects.create(**test_data)
+
+    def test_TaskBlueprint_prevents_missing_draft(self):
+
+        # setup
+        test_data = dict(TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
+        test_data['draft'] = None
+
+        # assert
+        with self.assertRaises(IntegrityError):
+            models.TaskBlueprint.objects.create(**test_data)
+
+    def test_TaskBlueprint_prevents_draft_deletion(self):
+        # setup
+        test_data = dict(TaskBlueprint_test_data())
+        blueprint = models.TaskBlueprint.objects.create(**test_data)
+        draft = blueprint.draft
+        with self.assertRaises(ProtectedError):
+            draft.delete()
+
+    def test_TaskBlueprint_prevents_missing_scheduling_unit_blueprint(self):
+
+        # setup
+        test_data = dict(TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
+        test_data['scheduling_unit_blueprint'] = None
+
+        # assert
+        with self.assertRaises(IntegrityError):
+            models.TaskBlueprint.objects.create(**test_data)
+
+    def test_TaskBlueprint_predecessors_and_successors_none(self):
+        task_blueprint_1: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
+        task_blueprint_2: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
+
+        self.assertEqual(set(), set(task_blueprint_1.predecessors.all()))
+        self.assertEqual(set(), set(task_blueprint_2.predecessors.all()))
+        self.assertEqual(set(), set(task_blueprint_1.successors.all()))
+        self.assertEqual(set(), set(task_blueprint_2.successors.all()))
+
+    def test_TaskBlueprint_predecessors_and_successors_simple(self):
+        task_blueprint_1: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
+        task_blueprint_2: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
+
+        models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_1, consumer=task_blueprint_2))
+
+        self.assertEqual(task_blueprint_1, task_blueprint_2.predecessors.all()[0])
+        self.assertEqual(task_blueprint_2, task_blueprint_1.successors.all()[0])
+
+    def test_TaskBlueprint_predecessors_and_successors_complex(self):
+        task_blueprint_1: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4())))
+        task_blueprint_2: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint))
+        task_blueprint_3: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint))
+        task_blueprint_4: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint))
+        task_blueprint_5: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint))
+        task_blueprint_6: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint))
+
+        # ST1 ---> ST3 ---> ST4
+        #      |        |
+        # ST2 -          -> ST5 ---> ST6
+
+        models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_1, consumer=task_blueprint_3))
+        trb1 = models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_2, consumer=task_blueprint_3))
+        models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_3, consumer=task_blueprint_4))
+        models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_3, consumer=task_blueprint_5))
+        models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_5, consumer=task_blueprint_6))
+
+        self.assertEqual(set((task_blueprint_1, task_blueprint_2)), set(task_blueprint_3.predecessors.all()))
+        self.assertEqual(set((task_blueprint_4, task_blueprint_5)), set(task_blueprint_3.successors.all()))
+        self.assertEqual(set((task_blueprint_3,)), set(task_blueprint_4.predecessors.all()))
+        self.assertEqual(set((task_blueprint_3,)), set(task_blueprint_5.predecessors.all()))
+        self.assertEqual(set((task_blueprint_3,)), set(task_blueprint_1.successors.all()))
+        self.assertEqual(set((task_blueprint_3,)), set(task_blueprint_2.successors.all()))
+        self.assertEqual(set(), set(task_blueprint_1.predecessors.all()))
+        self.assertEqual(set(), set(task_blueprint_2.predecessors.all()))
+        self.assertEqual(set(), set(task_blueprint_4.successors.all()))
+        self.assertEqual(set((task_blueprint_6,)), set(task_blueprint_5.successors.all()))
+
+
+class TaskRelationBlueprintTest(unittest.TestCase):
+    @classmethod
+    def setUpClass(cls) -> None:
+        cls.producer = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data())
+        cls.consumer = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data())
+
+    def test_TaskRelationBlueprint_gets_created_with_correct_creation_timestamp(self):
+        # setup
+        before = datetime.utcnow()
+        entry = models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
+
+        after = datetime.utcnow()
+
+        # assert
+        self.assertLess(before, entry.created_at)
+        self.assertGreater(after, entry.created_at)
+
+    def test_TaskRelationBlueprint_update_timestamp_gets_changed_correctly(self):
+        # setup
+        entry = models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
+        before = datetime.utcnow()
+        entry.save()
+        after = datetime.utcnow()
+
+        # assert
+        self.assertLess(before, entry.updated_at)
+        self.assertGreater(after, entry.updated_at)
+
+    def test_TaskRelationBlueprint_prevents_missing_selection_template(self):
+        # setup
+        test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
+        test_data['selection_template'] = None
+
+        # assert
+        with self.assertRaises(IntegrityError):
+            models.TaskRelationBlueprint.objects.create(**test_data)
+
+    def test_TaskRelationBlueprint_prevents_missing_draft(self):
+        # setup
+        test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
+        test_data['draft'] = None
+
+        # assert
+        with self.assertRaises(IntegrityError):
+            models.TaskRelationBlueprint.objects.create(**test_data)
+
+    def test_TaskRelationBlueprint_prevents_missing_producer(self):
+        # setup
+        test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
+        test_data['producer'] = None
+
+        # assert
+        with self.assertRaises(IntegrityError):
+            models.TaskRelationBlueprint.objects.create(**test_data)
+
+    def test_TaskRelationBlueprint_prevents_missing_consumer(self):
+        # setup
+        test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
+        test_data['consumer'] = None
+
+        # assert
+        with self.assertRaises(IntegrityError):
+            models.TaskRelationBlueprint.objects.create(**test_data)
+
+    def test_TaskRelationBlueprint_prevents_missing_input(self):
+        # setup
+        test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
+        test_data['input_role'] = None
+
+        # assert
+        with self.assertRaises(IntegrityError):
+            models.TaskRelationBlueprint.objects.create(**test_data)
+
+    def test_TaskRelationBlueprint_prevents_missing_output(self):
+        # setup
+        test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
+        test_data['output_role'] = None
+
+        # assert
+        with self.assertRaises(IntegrityError):
+            models.TaskRelationBlueprint.objects.create(**test_data)
+
+
+
+
+class TestStationTimeLine(unittest.TestCase):
+    """
+    Actually this simple testcase should be in a separate module (t_tmssapp_calculations_django_API.py)
+    but I was just lazy and spare some overhead and I just 'piggyback' with this module
+    """
+
+    def test_StationTimeline_raises_Error_on_duplicate_station_timeline(self):
+        """
+        Test if adding a duplicate station-timestamp combination leads to an Error and so data is not inserted
+        """
+        import datetime
+
+        test_data = {"station_name": "CS001",
+                     "timestamp": datetime.date(2021, 4, 1),
+                     "sunrise_start": datetime.datetime(year=2021, month=4, day=1, hour=6, minute=1, second=0),
+                     "sunrise_end": datetime.datetime(year=2021, month=4, day=1, hour=7, minute=2, second=0),
+                     "sunset_start": datetime.datetime(year=2021, month=4, day=1, hour=20, minute=31, second=0),
+                     "sunset_end": datetime.datetime(year=2021, month=4, day=1, hour=21, minute=33, second=0) }
+
+        models.StationTimeline.objects.create(**test_data)
+        with self.assertRaises(IntegrityError) as context:
+            models.StationTimeline.objects.create(**test_data)
+            self.assertIn('unique_station_time_line', str(context.exception))
+
+        self.assertEqual(len(models.StationTimeline.objects.filter(timestamp=datetime.date(2021, 4, 1))), 1)
+        self.assertEqual(len(models.StationTimeline.objects.all()), 1)
+        # Add a non-duplicate
+        test_data["station_name"] = "CS002"
+        models.StationTimeline.objects.create(**test_data)
+        self.assertEqual(len(models.StationTimeline.objects.filter(timestamp=datetime.date(2021, 4, 1))), 2)
+        self.assertEqual(len(models.StationTimeline.objects.all()), 2)
 
 
 if __name__ == "__main__":
diff --git a/SAS/TMSS/backend/test/test_environment.py b/SAS/TMSS/backend/test/test_environment.py
index 2cf2d6c51f8f246101f405113a20d2437bbdc8f2..183ded21b4019bb174094f90e0465faf1dd9c701 100644
--- a/SAS/TMSS/backend/test/test_environment.py
+++ b/SAS/TMSS/backend/test/test_environment.py
@@ -321,7 +321,8 @@ class TMSSTestEnvironment:
         # now that the ldap and django server are running, and the django set has been done,
         # we can announce our test user as superuser, so the test user can do anythin via the API.
         # (there are also other tests, using other (on the fly created) users with restricted permissions, which is fine but not part of this generic setup.
-        from django.contrib.auth.models import User
+        from django.contrib.auth import get_user_model
+        User = get_user_model()
         user, _ = User.objects.get_or_create(username=self.ldap_server.dbcreds.user)
         user.is_superuser = True
         user.save()
@@ -631,7 +632,7 @@ def main_test_environment():
 
 def create_scheduling_unit_blueprint_simulator(scheduling_unit_blueprint_id: int, stop_event: threading.Event,
                                                handle_observations: bool = True, handle_pipelines: bool = True,
-                                               handle_QA: bool = True, handle_ingest: bool = True,
+                                               handle_QA: bool = True, handle_ingest: bool = True, handle_cleanup: bool = True,
                                                auto_grant_ingest_permission: bool = True,
                                                delay: float=1, duration: float=5,
                                                create_output_dataproducts: bool=False,
@@ -653,7 +654,7 @@ def create_scheduling_unit_blueprint_simulator(scheduling_unit_blueprint_id: int
     class SimulationEventHandler(TMSSEventMessageHandler):
         def __init__(self, scheduling_unit_blueprint_id: int, stop_event: threading.Event,
                      handle_observations: bool = True, handle_pipelines: bool = True,
-                     handle_QA: bool = True, handle_ingest: bool = True,
+                     handle_QA: bool = True, handle_ingest: bool = True, handle_cleanup: bool = True,
                      delay: float = 1, duration: float = 10,
                      create_output_dataproducts: bool=False) -> None:
             super().__init__(log_event_messages=False)
@@ -663,13 +664,14 @@ def create_scheduling_unit_blueprint_simulator(scheduling_unit_blueprint_id: int
             self.handle_pipelines = handle_pipelines
             self.handle_QA = handle_QA
             self.handle_ingest = handle_ingest
+            self.handle_cleanup = handle_cleanup
             self.auto_grant_ingest_permission = auto_grant_ingest_permission
             self.delay = delay
             self.duration = duration
             self.create_output_dataproducts = create_output_dataproducts
 
         def need_to_handle(self, subtask: models.Subtask) -> bool:
-            if self.scheduling_unit_blueprint_id in [tb.scheduling_unit_blueprint.id for tb in subtask.task_blueprints.all()]:
+            if self.scheduling_unit_blueprint_id not in [tb.scheduling_unit_blueprint.id for tb in subtask.task_blueprints.all()]:
                 return False
 
             if subtask.specifications_template.type.value == models.SubtaskType.Choices.OBSERVATION.value and not self.handle_observations:
@@ -685,6 +687,9 @@ def create_scheduling_unit_blueprint_simulator(scheduling_unit_blueprint_id: int
             if subtask.specifications_template.type.value == models.SubtaskType.Choices.INGEST.value and not self.handle_ingest:
                 return False
 
+            if subtask.specifications_template.type.value == models.SubtaskType.Choices.CLEANUP.value and not self.handle_cleanup:
+                return False
+
             return True
 
         def start_handling(self):
@@ -694,21 +699,21 @@ def create_scheduling_unit_blueprint_simulator(scheduling_unit_blueprint_id: int
 
             logger.info("starting to simulate a run for scheduling_unit id=%s ...", self.scheduling_unit_blueprint_id)
 
-            super().start_handling()
-
             try:
                 # exit if already finished
                 scheduling_unit = models.SchedulingUnitBlueprint.objects.get(id=self.scheduling_unit_blueprint_id)
-                if scheduling_unit.status in ["finished", "error"]:
+                if scheduling_unit.status in ["finished", "error", "cancelled"]:
                     logger.info("scheduling_unit id=%s name='%s' has status=%s -> not simulating", scheduling_unit.id, scheduling_unit.name, scheduling_unit.status)
                     self.stop_event.set()
                     return
             except models.SchedulingUnitBlueprint.DoesNotExist:
                 pass
 
+            super().start_handling()
+
             # trick: trigger any already scheduled subtasks, cascading in events simulating the run
-            subtasks = models.Subtask.objects.filter(task_blueprints__scheduling_unit_blueprint_id=self.scheduling_unit_blueprint_id)
-            for subtask in subtasks.filter(state__value=models.SubtaskState.Choices.SCHEDULED.value):
+            scheduled_subtasks = models.Subtask.objects.filter(task_blueprints__scheduling_unit_blueprint_id=self.scheduling_unit_blueprint_id).filter(state__value=models.SubtaskState.Choices.SCHEDULED.value).all()
+            for subtask in scheduled_subtasks:
                 self.onSubTaskStatusChanged(subtask.id, "scheduled")
 
             # schedule the defined subtasks, cascading in events simulating the run
@@ -760,8 +765,7 @@ def create_scheduling_unit_blueprint_simulator(scheduling_unit_blueprint_id: int
             if not self.need_to_handle(subtask):
                 return
 
-            logger.info("subtask id=%s type='%s' now has status='%s'", id, subtask.specifications_template.type.value,
-                        status)
+            logger.info("subtask id=%s type='%s' has status='%s'", id, subtask.specifications_template.type.value, status)
 
             next_state = None
             if status == models.SubtaskState.Choices.SCHEDULED.value:
@@ -856,7 +860,7 @@ def create_scheduling_unit_blueprint_simulator(scheduling_unit_blueprint_id: int
     return BusListenerJanitor(TMSSBusListener(SimulationEventHandler, handler_kwargs={'scheduling_unit_blueprint_id': scheduling_unit_blueprint_id,
                                                                                       'stop_event': stop_event,
                                                                                       'handle_observations': handle_observations, 'handle_pipelines': handle_pipelines,
-                                                                                      'handle_QA': handle_QA, 'handle_ingest': handle_ingest,
+                                                                                      'handle_QA': handle_QA, 'handle_ingest': handle_ingest, 'handle_cleanup': handle_cleanup,
                                                                                       'create_output_dataproducts': create_output_dataproducts,
                                                                                       'delay': delay, 'duration': duration},
                                                                        exchange=exchange, broker=broker))
@@ -883,12 +887,14 @@ def main_scheduling_unit_blueprint_simulator():
     group.add_option('-p', '--pipeline', dest='pipeline', action='store_true', help='simulate events for pipeline subtasks')
     group.add_option('-Q', '--QA', dest='QA', action='store_true', help='simulate events for QA subtasks')
     group.add_option('-i', '--ingest', dest='ingest', action='store_true', help='simulate events for ingest subtasks')
+    group.add_option('-c', '--cleanup', dest='cleanup', action='store_true', help='simulate events for cleanup subtasks')
 
     group = OptionGroup(parser, 'Simulation parameters')
     parser.add_option_group(group)
     group.add_option('-e', '--event_delay', dest='event_delay', type='float', default=1.0, help='wait <event_delay> seconds between simulating events to mimic real-world behaviour, default: %default')
     group.add_option('-d', '--duration', dest='duration', type='float', default=60.0, help='wait <duration> seconds while "observing"/"processing" between started and finishing state to mimic real-world behaviour, default: %default')
     group.add_option('-g', '--grant_ingest_permission', dest='grant_ingest_permission', action='store_true', help='automatically grant ingest permission for ingest subtasks if needed')
+    group.add_option('-f', '--create_output_dataproducts', dest='create_output_dataproducts', action='store_true', help='create small fake output dataproduct files for the observation and pipeline subtask(s)')
 
     group = OptionGroup(parser, 'Messaging options')
     parser.add_option_group(group)
@@ -906,11 +912,12 @@ def main_scheduling_unit_blueprint_simulator():
 
     scheduling_unit_blueprint_id = int(args[0])
 
-    if not (options.observation or options.pipeline or options.QA or options.ingest):
+    if not (options.observation or options.pipeline or options.QA or options.ingest or options.cleanup):
         options.observation = True
         options.pipeline = True
         options.QA = True
         options.ingest = True
+        options.cleanup = True
 
     from lofar.sas.tmss.tmss import setup_and_check_tmss_django_database_connection_and_exit_on_error
     setup_and_check_tmss_django_database_connection_and_exit_on_error(options.dbcredentials)
@@ -919,8 +926,9 @@ def main_scheduling_unit_blueprint_simulator():
     with create_scheduling_unit_blueprint_simulator(scheduling_unit_blueprint_id, stop_event=stop_event,
                                                     delay=options.event_delay, duration=options.duration,
                                                     handle_observations=bool(options.observation), handle_pipelines=bool(options.pipeline),
-                                                    handle_QA=bool(options.QA), handle_ingest=bool(options.ingest),
+                                                    handle_QA=bool(options.QA), handle_ingest=bool(options.ingest), handle_cleanup=bool(options.cleanup),
                                                     auto_grant_ingest_permission=bool(options.grant_ingest_permission),
+                                                    create_output_dataproducts=bool(options.create_output_dataproducts),
                                                     exchange=options.exchange, broker=options.broker):
         print("Press Ctrl-C to exit")
         try:
diff --git a/SAS/TMSS/backend/test/tmss_test_data_django_models.py b/SAS/TMSS/backend/test/tmss_test_data_django_models.py
index 538d5d7480920cc1ca4c924914f675e91e4c8892..6c0810dc0715624cc4195b59d693c7e3e613f573 100644
--- a/SAS/TMSS/backend/test/tmss_test_data_django_models.py
+++ b/SAS/TMSS/backend/test/tmss_test_data_django_models.py
@@ -108,8 +108,7 @@ def TaskConnectorType_test_data() -> dict:
             "datatype": models.Datatype.objects.get(value='instrument model'),
             "dataformat": models.Dataformat.objects.get(value='Beamformed'),
             "task_template": models.TaskTemplate.objects.create(**TaskTemplate_test_data()),
-            "iotype": models.IOType.objects.get(value=models.IOType.Choices.OUTPUT.value),
-            "tags": []}
+            "iotype": models.IOType.objects.get(value=models.IOType.Choices.OUTPUT.value)}
 
 def Cycle_test_data() -> dict:
     return {"name": 'my_cycle' + str(uuid.uuid4()),
@@ -118,7 +117,7 @@ def Cycle_test_data() -> dict:
             "start": datetime.utcnow().isoformat(),
             "stop": datetime.utcnow().isoformat()}
 
-def Project_test_data(name: str=None, priority_rank: int = 1, auto_pin=False) -> dict:
+def Project_test_data(name: str=None, priority_rank: int = 1, auto_pin=False, can_trigger=False) -> dict:
     if name is None:
         name = 'my_project_' + str(uuid.uuid4())
 
@@ -129,7 +128,7 @@ def Project_test_data(name: str=None, priority_rank: int = 1, auto_pin=False) ->
                "auto_ingest": False,
                "priority_rank": priority_rank,
                "trigger_priority": 1000,
-               "can_trigger": False,
+               "can_trigger": can_trigger,
                "private_data": True,
                "expert": True,
                "filler": False,
@@ -193,7 +192,8 @@ def SchedulingUnitDraft_test_data(name="my_scheduling_unit_draft", scheduling_se
                                   template: models.SchedulingUnitTemplate=None, requirements_doc: dict=None,
                                   observation_strategy_template: models.SchedulingUnitObservingStrategyTemplate=None,
                                   scheduling_constraints_doc: dict=None,
-                                  scheduling_constraints_template: models.SchedulingConstraintsTemplate=None) -> dict:
+                                  scheduling_constraints_template: models.SchedulingConstraintsTemplate=None,
+                                  is_triggered=False) -> dict:
     if scheduling_set is None:
         scheduling_set = models.SchedulingSet.objects.create(**SchedulingSet_test_data())
 
@@ -220,7 +220,8 @@ def SchedulingUnitDraft_test_data(name="my_scheduling_unit_draft", scheduling_se
             "requirements_template": template,
             "observation_strategy_template": observation_strategy_template,
             "scheduling_constraints_template": scheduling_constraints_template,
-            "scheduling_constraints_doc": scheduling_constraints_doc}
+            "scheduling_constraints_doc": scheduling_constraints_doc,
+            "is_triggered": is_triggered}
 
 def TaskDraft_test_data(name: str=None, specifications_template: models.TaskTemplate=None, specifications_doc: dict=None, scheduling_unit_draft: models.SchedulingUnitDraft=None, output_pinned=False) -> dict:
     if name is None:
diff --git a/SAS/TMSS/backend/test/tmss_test_data_rest.py b/SAS/TMSS/backend/test/tmss_test_data_rest.py
index 4b74a99f08e150ac3dd61c17157696fb048bf5c9..7fbd564e75315243b58a3f8cf5b8061fb398cbe7 100644
--- a/SAS/TMSS/backend/test/tmss_test_data_rest.py
+++ b/SAS/TMSS/backend/test/tmss_test_data_rest.py
@@ -229,8 +229,7 @@ class TMSSRESTTestDataCreator():
                 "datatype": self.django_api_url + '/datatype/image',
                 "dataformat": self.django_api_url + '/dataformat/Beamformed',
                 "task_template": task_template_url,
-                "iotype": self.django_api_url + '/iotype/%s'%iotype,
-                "tags": []}
+                "iotype": self.django_api_url + '/iotype/%s'%iotype}
 
 
     def DefaultTemplates(self, name="defaulttemplate"):
@@ -432,11 +431,19 @@ class TMSSRESTTestDataCreator():
             selection_doc = self.get_response_as_json_object(template_url+'/default')
 
         if input_role_url is None:
-            input_role_url = self.post_data_and_get_url(self.TaskConnectorType(iotype="input"), '/task_connector_type/')
-    
+            try:
+                input_role_url = self.post_data_and_get_url(self.TaskConnectorType(iotype="input"), '/task_connector_type/')
+            except:
+                # duplicate connector created... pick first random input connector
+                input_role_url = next(c for c in self.get_response_as_json_object(self.django_api_url+'/task_connector_type')['results'] if c['iotype_value']=='input')['url']
+
         if output_role_url is None:
-            output_role_url = self.post_data_and_get_url(self.TaskConnectorType(iotype="output"), '/task_connector_type/')
-    
+            try:
+                output_role_url = self.post_data_and_get_url(self.TaskConnectorType(iotype="output"), '/task_connector_type/')
+            except:
+                # duplicate connector created... pick first random output connector
+                output_role_url = next(c for c in self.get_response_as_json_object(self.django_api_url+'/task_connector_type')['results'] if c['iotype_value']=='output')['url']
+
         return {"tags": [],
                 "selection_doc": selection_doc,
                 "producer": producer_url,
@@ -530,11 +537,19 @@ class TMSSRESTTestDataCreator():
             selection_doc = self.get_response_as_json_object(template_url+'/default')
 
         if input_role_url is None:
-            input_role_url = self.post_data_and_get_url(self.TaskConnectorType(iotype="input"), '/task_connector_type/')
-    
+            try:
+                input_role_url = self.post_data_and_get_url(self.TaskConnectorType(iotype="input"), '/task_connector_type/')
+            except:
+                # duplicate connector created... pick first random input connector
+                input_role_url = next(c for c in self.get_response_as_json_object(self.django_api_url+'/task_connector_type')['results'] if c['iotype_value']=='input')['url']
+
         if output_role_url is None:
-            output_role_url = self.post_data_and_get_url(self.TaskConnectorType(iotype="output"), '/task_connector_type/')
-    
+            try:
+                output_role_url = self.post_data_and_get_url(self.TaskConnectorType(iotype="output"), '/task_connector_type/')
+            except:
+                # duplicate connector created... pick first random output connector
+                output_role_url = next(c for c in self.get_response_as_json_object(self.django_api_url+'/task_connector_type')['results'] if c['iotype_value']=='output')['url']
+
         # test data
         return {"tags": [],
                 "selection_doc": selection_doc,
diff --git a/SAS/TMSS/backend/test/tmss_test_environment_unittest_setup.py b/SAS/TMSS/backend/test/tmss_test_environment_unittest_setup.py
index 2c3dd34f8f81bd2a256eaa7ffe5164408eb8de34..acbc4384ad8402735abf504ee44d2ba66e662fa8 100644
--- a/SAS/TMSS/backend/test/tmss_test_environment_unittest_setup.py
+++ b/SAS/TMSS/backend/test/tmss_test_environment_unittest_setup.py
@@ -65,7 +65,7 @@ def _call_API_and_assert_expected_response(test_instance, url, call, data, expec
     elif call == 'POST':
         response = requests.post(url, json=data, auth=auth)
     elif call == 'GET':
-        response = requests.get(url, auth=auth)
+        response = requests.get(url, auth=auth, allow_redirects=False)
     elif call == 'PATCH':
         response = requests.patch(url, json=data, auth=auth)
     elif call == 'DELETE':
diff --git a/SAS/TMSS/client/lib/populate.py b/SAS/TMSS/client/lib/populate.py
index 952b0e27de20aa016f8d2de0c7622a59a964e657..db1898204cf2a70e0b1960207f90fbbe713acd06 100644
--- a/SAS/TMSS/client/lib/populate.py
+++ b/SAS/TMSS/client/lib/populate.py
@@ -100,8 +100,12 @@ def populate_schemas(schema_dir: str=None, templates_filename: str=None):
 
             # helper functions for uploading
             def upload_template(template: dict):
-                logger.info("Uploading template with name='%s' version='%s'", template['name'], template['version'])
-                client.post_template(template_path=template.pop('template'), **template)
+                try:
+                    logger.info("Uploading template with name='%s' version='%s'", template['name'], template['version'])
+                    client.post_template(template_path=template.pop('template'), **template)
+                except Exception as e:
+                    logger.error("Error while uploading template with name='%s' version='%s': %s",
+                                 template['name'], template['version'], e)
 
             # helper functions for uploading
             def upload_template_if_needed_with_dependents_first(id: str):
diff --git a/SAS/TMSS/client/lib/tmss_http_rest_client.py b/SAS/TMSS/client/lib/tmss_http_rest_client.py
index d128bc0937f651fc8dce325166463e9a1546d801..9cd158c0cfe0373aec10ed196c57652768662650 100644
--- a/SAS/TMSS/client/lib/tmss_http_rest_client.py
+++ b/SAS/TMSS/client/lib/tmss_http_rest_client.py
@@ -11,6 +11,7 @@ from datetime import datetime, timedelta
 from lofar.common.datetimeutils import formatDatetime
 from lofar.common.dbcredentials import DBCredentials
 
+import html
 
 # usage example:
 #
@@ -103,14 +104,35 @@ class TMSSsession(object):
         if self.authentication_method == self.OPENID:
             # get authentication page of OIDC through TMSS redirect
             response = self.session.get(self.api_url.replace('/api', '/oidc/authenticate/'), allow_redirects=True)
-            csrftoken = self.session.cookies['csrftoken']
-
-            # post user credentials to login page, also pass csrf token
-            data = {'username': self.username, 'password': self.password, 'csrfmiddlewaretoken': csrftoken}
-            response = self.session.post(url=response.url, data=data, allow_redirects=True)
+            for resp in response.history:
+                logger.info("via %s: %s" % (resp.status_code, resp.url))
+            logger.info("got %s: %s" % (response.status_code, response.url))
+            # todo: remove this ugly fix once we have a production url:
+            if response.status_code == 400 and "127.0.0.1" in response.url:
+                response = self.session.get(response.url.replace('127.0.0.1', 'localhost'))
+                logger.info("fix %s: %s" % (response.status_code, response.url))
+            # post user credentials to login page, also pass csrf token if present
+            if 'csrftoken' in self.session.cookies:
+                # Mozilla OIDC provider
+                csrftoken = self.session.cookies['csrftoken']
+                data = {'username': self.username, 'password': self.password, 'csrfmiddlewaretoken': csrftoken}
+                response = self.session.post(url=response.url, data=data, allow_redirects=True)
+            else:
+                # Keycloak
+                content = response.content.decode('utf-8')
+                if 'action' not in content:
+                    raise Exception('Could not determine login form action from server response: %s' % content)
+                action = content.split('action="')[1].split('"')[0]
+                data = {'username': self.username, 'password': self.password, 'credentialId': ''}
+                response = self.session.post(url=html.unescape(action), data=data, allow_redirects=True)
+
+            for resp in response.history:
+                logger.info("via %s: %s" % (resp.status_code, resp.url))
+            logger.info("got %s: %s" % (response.status_code, response.url))
 
             # raise when sth went wrong
-            if "The username and/or password you specified are not correct" in response.content.decode('utf8'):
+            if "The username and/or password you specified are not correct" in response.content.decode('utf8') \
+                    or "Invalid username or password" in response.content.decode('utf8'):
                 raise ValueError("The username and/or password you specified are not correct")
             if response.status_code != 200:
                 raise ConnectionError(response.content.decode('utf8'))
diff --git a/SAS/TMSS/frontend/tmss_webapp/package.json b/SAS/TMSS/frontend/tmss_webapp/package.json
index a265239926a84e93f1817f57f9db1988760ab4a0..ee21b00607ac5dfa07ad57918d6e50dd3345a825 100644
--- a/SAS/TMSS/frontend/tmss_webapp/package.json
+++ b/SAS/TMSS/frontend/tmss_webapp/package.json
@@ -15,15 +15,17 @@
     "ag-grid-react": "^24.1.1",
     "axios": "^0.21.1",
     "bootstrap": "^4.5.0",
+    "chart.js": "^3.2.1",
     "cleave.js": "^1.6.0",
     "flatpickr": "^4.6.3",
     "font-awesome": "^4.7.0",
     "history": "^5.0.0",
+    "html2canvas": "^1.0.0-rc.7",
     "interactjs": "^1.9.22",
     "jspdf": "^2.3.0",
     "jspdf-autotable": "^3.5.13",
     "katex": "^0.12.0",
-    "lodash": "^4.17.19",
+    "lodash": "^4.17.21",
     "match-sorter": "^4.1.0",
     "moment": "^2.27.0",
     "node-sass": "^4.12.0",
@@ -38,22 +40,25 @@
     "react-bootstrap": "^1.0.1",
     "react-bootstrap-datetimepicker": "0.0.22",
     "react-calendar-timeline": "^0.27.0",
+    "react-chartjs-2": "^3.0.3",
     "react-dom": "^16.13.1",
     "react-flatpickr": "^3.10.7",
     "react-frame-component": "^4.1.2",
     "react-json-to-table": "^0.1.7",
-    "react-json-view": "^1.19.1",
+    "react-json-view": "^1.21.3",
     "react-loader-spinner": "^3.1.14",
     "react-router-dom": "^5.2.0",
     "react-scripts": "^3.4.2",
     "react-split-pane": "^0.1.92",
     "react-table": "^7.2.1",
     "react-table-plugins": "^1.3.1",
+    "react-to-print": "^2.12.4",
     "react-transition-group": "^2.5.1",
     "react-websocket": "^2.1.0",
     "reactstrap": "^8.5.1",
     "shortcut-buttons-flatpickr": "^0.3.1",
     "styled-components": "^5.1.1",
+    "suneditor": "^2.36.5",
     "suneditor-react": "^2.14.10",
     "typescript": "^3.9.5",
     "yup": "^0.29.1"
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/App.js b/SAS/TMSS/frontend/tmss_webapp/src/App.js
index 3d4e9e0ac8152ae0a427918bf569f5668d1e0142..42ebe3118071b3a80a8252941150b880c9715bcf 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/App.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/App.js
@@ -61,8 +61,9 @@ class App extends Component {
         {label: 'Project', icon: 'fab fa-fw fa-wpexplorer', to:'/project',section: 'project'},
         {label: 'Scheduling Units', icon: 'pi pi-fw pi-calendar', to:'/schedulingunit',section: 'schedulingunit'},
         {label: 'Tasks', icon: 'pi pi-fw pi-check-square', to:'/task'},
+        {label: 'Workflow', icon: 'pi pi-sitemap', to:'/workflow',section: 'workflow'},
         {label: 'Timeline', icon: 'pi pi-fw pi-clock', to:'/su/timelineview',section: 'su/timelineview'},
-        {label: 'Workflow', icon: 'fa fa-sitemap', to:'/workflow',section: 'workflow'},
+        {label: 'Reports', icon: 'pi pi-fw pi-chart-bar', to:'/reports',section: 'reports'},
     ];
     }
 
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/authenticate/login.js b/SAS/TMSS/frontend/tmss_webapp/src/authenticate/login.js
index fd5b4b041eccc5f155117b6e072092358b35657e..dc664ca93ed31e31f1a3240ecad5698848b5ce62 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/authenticate/login.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/authenticate/login.js
@@ -20,6 +20,7 @@ export class Login extends Component {
         };
         this.login = this.login.bind(this);
         this.setCredentials = this.setCredentials.bind(this);
+        this.formSubmit = this.formSubmit.bind(this);
     }
 
     /**
@@ -45,6 +46,16 @@ export class Login extends Component {
         this.setState(state);
     }
 
+    /**
+     * Function to call login function on Enter key press.
+     * @param {React.KeyboardEvent} event 
+     */
+    formSubmit(event) {
+        if (event.key === "Enter" && this.state.username && this.state.password) {
+            this.login();
+        }
+    }
+
     /**
      * Login function called on click of 'Login' button. 
      * If authenticated, callback parent component function.
@@ -88,7 +99,8 @@ export class Login extends Component {
                                                 <div className="form-field">
                                                     <span className="p-float-label">
                                                         <InputText id="" className={`${this.state.errors.username?"input-error ":""} form-control`} 
-                                                                    value={this.state.username} onChange={(e) => this.setCredentials('username', e.target.value)} />
+                                                                    value={this.state.username} onChange={(e) => this.setCredentials('username', e.target.value)}
+                                                                    onKeyUp={this.formSubmit} />
                                                         <label htmlFor="username"><i className="fa fa-user"></i>Enter Username</label>
                                                     </span>
                                                     <label className={this.state.errors.username?"error":""}>
@@ -98,7 +110,8 @@ export class Login extends Component {
                                                 <div className="form-field">
                                                     <span className="p-float-label">
                                                         <InputText id="password" className={`${this.state.errors.password?"input-error ":""} form-control`} 
-                                                                type="password" value={this.state.password} onChange={(e) => this.setCredentials('password', e.target.value )} />
+                                                                type="password" value={this.state.password} onChange={(e) => this.setCredentials('password', e.target.value )} 
+                                                                onKeyUp={this.formSubmit} />
                                                         <label htmlFor="password"><i className="fa fa-key"></i>Enter Password</label>
                                                     </span>
                                                     <label className={this.state.errors.password?"error":""}>
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/JSONEditor/JEditor.js b/SAS/TMSS/frontend/tmss_webapp/src/components/JSONEditor/JEditor.js
index df3e6659276fc8b78c97288612fb751a5366d73d..a408ef43f60f5f51dff7bdcccff261d65a8d4647 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/components/JSONEditor/JEditor.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/components/JSONEditor/JEditor.js
@@ -17,6 +17,7 @@ function Jeditor(props) {
     // console.log("In JEditor", props.schema);
     const editorRef = useRef(null);
     let pointingProps = useRef(null);
+    // let durationProps = useRef(null);
     let editor = null;
 
     /**
@@ -113,6 +114,7 @@ function Jeditor(props) {
             props.defintionFormatter(schema);
         }
         pointingProps = [];
+        // durationProps = [];
         // Customize the pointing property to capture angle1 and angle2 to specified format
         for (const definitionKey in schema.definitions) {
             if (definitionKey === 'pointing') {
@@ -133,6 +135,7 @@ function Jeditor(props) {
                 }
             }
         }
+        
     // Customize datatype of certain properties like subbands, duration, etc.,
         getCustomProperties(schema.properties);
         getCustomProperties(schema.definitions);
@@ -193,7 +196,8 @@ function Jeditor(props) {
             } 
             return errors;
         });
-        schema.format = "grid"
+        schema.format = "grid";
+        console.log(schema);
         const editorOptions = {
             form_name_root: "specification",
             schema: schema,
@@ -307,6 +311,7 @@ function Jeditor(props) {
                 newProperty.validationType = propertyKey === 'subbands'?'subband_list':'subband_list_optional';
                 properties[propertyKey] = newProperty;
             }   else if (propertyKey.toLowerCase() === 'duration') {
+            // }   else if (propertyValue['$id'] === '#duration') {
                 let newProperty = {
                     "type": "string",
                     "format": "time",
@@ -333,8 +338,8 @@ function Jeditor(props) {
                         }
                     }
                 };
-
                 properties[propertyKey] = newProperty;
+                // durationProps.push(propertyKey);
             }   else if (propertyValue instanceof Object) {
                 // by default previously, all field will have format as Grid, but this will fail for calendar, so added property called skipFormat
                 if (propertyKey !== 'properties' && propertyKey !== 'default' && !propertyValue.skipFormat) {
@@ -383,6 +388,7 @@ function Jeditor(props) {
                     updateInput(inputValue);
                 }
             }  else if (inputKey.toLowerCase() === 'duration') {
+            // }  else if (durationProps.indexOf(inputKey)) {
                 editorInput[inputKey] = getTimeInput(inputValue);
             }
         }
@@ -407,6 +413,7 @@ function Jeditor(props) {
                         (outputKey === 'list' && typeof(outputValue) === 'string')) {
                 editorOutput[outputKey] = getSubbandOutput(outputValue);
             } else if (outputKey.toLowerCase() === 'duration') {
+            // }  else if (durationProps.indexOf(outputKey)) {
                 const splitOutput = outputValue.split(':');
                 editorOutput[outputKey] = ((splitOutput[0] * 3600) + (splitOutput[1] * 60)  + parseInt(splitOutput[2]));
             }
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/Timeline/CalendarTimeline.js b/SAS/TMSS/frontend/tmss_webapp/src/components/Timeline/CalendarTimeline.js
index 0e9235c53d76d9286682e5c15412b3c86fd8d41c..f57e42b6e93d4139a7f82a11de4f387f37db0fe0 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/components/Timeline/CalendarTimeline.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/components/Timeline/CalendarTimeline.js
@@ -744,7 +744,7 @@ export class CalendarTimeline extends Component {
         }
         let itemDivStyle = { background: backgroundColor,
                                 color: item.color,
-                                borderColor: "#5a5a5a",
+                                borderColor: item.color,
                                 borderRadius: 3,
                                 border: "none",
                                 zIndex: item.type==="SUNTIME"?79:80
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/ViewTable.js b/SAS/TMSS/frontend/tmss_webapp/src/components/ViewTable.js
index d0108eef8d31fd90ba6c635fcf83a2854c79cfdf..c19f4fea15c48c7c1d96c9bc9e0a3b39996e35b2 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/components/ViewTable.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/components/ViewTable.js
@@ -289,7 +289,7 @@ function CalendarColumnFilter({
     <div className="table-filter" onClick={e => { e.stopPropagation() }}>
       <Calendar value={filterValue} appendTo={document.body} dateFormat="yy-mm-dd" onChange={(e) => {
         const value = moment(e.value).format('YYYY-MM-DD')
-        setValue(value); 
+        setValue(value);
         setFilter(e.value);
       }} showIcon></Calendar>
       {value && <i onClick={() => { setFilter(undefined); setValue('') }} className="tb-cal-reset fa fa-times" />}
@@ -599,7 +599,51 @@ const IndeterminateCheckbox = React.forwardRef(
 )
 
 // Our table component
-function Table({ columns, data, defaultheader, optionalheader, tablename, defaultSortColumn, defaultpagesize, columnOrders, showAction }) {
+function Table(props) {
+  let { columns, data, defaultheader, optionalheader, tablename, defaultSortColumn, defaultpagesize, columnOrders, showAction, toggleBySorting, onColumnToggle, lsKeySortColumn
+    , descendingColumn, ignoreSorting } = props;
+    ignoreSorting = ignoreSorting ||[];
+    ignoreSorting = [...ignoreSorting,'action']
+  descendingColumn = descendingColumn || [];
+  const checkDefaultSortColumnEmpty = () => {
+    return !defaultSortColumn || !defaultSortColumn[0] || Object.keys(defaultSortColumn[0]).length === 0;
+  }
+  const checkDescendingColumnExists = (value) => {
+    return descendingColumn.includes(value);
+  }
+  const checkToIgnoreSorting = (value) => {
+    return ignoreSorting.includes(value);
+  }
+  const getFirstVisibleColumn = (selectedColumn, tempAllColumns) => {
+    let selected = {};
+    let tempColumn = {};
+    let totalColumns = undefined;
+
+    if (tempAllColumns && tempAllColumns.length > 0) {
+      totalColumns = tempAllColumns;
+    }
+
+    if (totalColumns) {
+      for (let i = 0; i < totalColumns.length; i++) {
+        tempColumn = { ...totalColumns[i] };
+        if (tempColumn.Header && typeof tempColumn.Header === "string") {
+          if (tempColumn.Header.toLowerCase() === selectedColumn.Header.toLowerCase()) {
+            tempColumn.isVisible = selectedColumn.isVisible;
+          }
+          if (!checkToIgnoreSorting(tempColumn.Header.toLowerCase()) && tempColumn.isVisible) {
+            selected = tempColumn;
+            break;
+          }
+        }
+      }
+    }
+    return selected;
+  }
+
+  if (checkDefaultSortColumnEmpty()) {
+    let tempVisibleColumn = getFirstVisibleColumn({ Header: '' }, columns);
+    defaultSortColumn = [{ id: tempVisibleColumn.Header, desc: checkDescendingColumnExists(tempVisibleColumn.Header.toLowerCase()) }];
+  }
 
   const filterTypes = React.useMemo(
     () => ({
@@ -629,7 +673,7 @@ function Table({ columns, data, defaultheader, optionalheader, tablename, defaul
     }),
     []
   )
-
+  let tblinstance;
   const {
     getTableProps,
     getTableBodyProps,
@@ -650,7 +694,7 @@ function Table({ columns, data, defaultheader, optionalheader, tablename, defaul
     selectedFlatRows,
     setColumnOrder,
     exportData,
-  } = useTable(
+  } = tblinstance = useTable(
     {
       columns,
       data,
@@ -671,10 +715,13 @@ function Table({ columns, data, defaultheader, optionalheader, tablename, defaul
     useColumnOrder,
     useExportData
   );
+
+
+
   React.useEffect(() => {
     setHiddenColumns(
-    //  columns.filter(column => !column.isVisible).map(column => column.accessor)
-    columns.filter(column => !column.isVisible).map(column => column.id)
+      //  columns.filter(column => !column.isVisible).map(column => column.accessor)
+      columns.filter(column => !column.isVisible).map(column => column.id)
     );
     // console.log('columns List', visibleColumns.map((d) => d.id));
     if (columnOrders && columnOrders.length) {
@@ -684,7 +731,7 @@ function Table({ columns, data, defaultheader, optionalheader, tablename, defaul
         setColumnOrder(['Select', ...columnOrders]);
       }
     }
-    
+
   }, [setHiddenColumns, columns]);
 
   let op = useRef(null);
@@ -693,6 +740,7 @@ function Table({ columns, data, defaultheader, optionalheader, tablename, defaul
   const [currentrows, setcurrentRows] = React.useState(defaultpagesize);
   const [custompagevalue, setcustompagevalue] = React.useState();
 
+
   const onPagination = (e) => {
     gotoPage(e.page);
     setcurrentPage(e.first);
@@ -722,16 +770,48 @@ function Table({ columns, data, defaultheader, optionalheader, tablename, defaul
     setcustompagevalue();
   };
 
+
+
+  const onColumnToggleViewTable = (selectedColumn, sortedColumn) => {
+    let visibleColumn = {};
+    let viewColumn = {};
+    if (selectedColumn.Header === sortedColumn.Header && !selectedColumn.isVisible) {
+      visibleColumn = getFirstVisibleColumn(selectedColumn, allColumns);     
+      viewColumn = { Header: visibleColumn.Header, desc: checkDescendingColumnExists(visibleColumn.Header.toLowerCase())};
+      let tempdefaultSortColumn = [{ id: viewColumn.Header, desc: viewColumn.desc }];
+      if (lsKeySortColumn && lsKeySortColumn.trim().length > 0) {
+        localStorage.setItem(lsKeySortColumn, JSON.stringify(tempdefaultSortColumn));
+      }
+    }
+    visibleColumn.Header = visibleColumn.Header || "";
+    return viewColumn;
+  }
+
   const onToggleChange = (e) => {
     let lsToggleColumns = [];
+    let selectedColumn = null;
+    let sortedColumn = {};
     allColumns.forEach(acolumn => {
       let jsonobj = {};
       let visible = (acolumn.Header === e.target.id) ? ((acolumn.isVisible) ? false : true) : acolumn.isVisible
       jsonobj['Header'] = acolumn.Header;
       jsonobj['isVisible'] = visible;
       lsToggleColumns.push(jsonobj)
-    })
-    localStorage.setItem(tablename, JSON.stringify(lsToggleColumns))
+      selectedColumn = (acolumn.Header === e.target.id) ? jsonobj : selectedColumn;
+      if (acolumn.isSorted) {
+        sortedColumn['Header'] = acolumn.Header;
+        sortedColumn['isVisible'] = visible;
+      }
+    });
+    localStorage.setItem(tablename, JSON.stringify(lsToggleColumns));
+
+    if (onColumnToggleViewTable) {
+      let columnTobeSorted = onColumnToggleViewTable(selectedColumn, sortedColumn); //onColumnToggle(selectedColumn, sortedColumn);
+      columnTobeSorted.Header = columnTobeSorted.Header || "";
+      if (columnTobeSorted.Header.trim().length > 0) {
+        tblinstance.toggleSortBy(columnTobeSorted.Header, columnTobeSorted.desc);
+      }
+    }
   }
 
   filteredData = _.map(rows, 'values');
@@ -748,6 +828,10 @@ function Table({ columns, data, defaultheader, optionalheader, tablename, defaul
     parentCBonSelection(selectedRows)
   }
 
+  const onSortBy = () => {
+    sessionStorage.setItem("sortedData", tbldata);
+  }
+
   return (
     <>
       <div style={{ display: 'flex', justifyContent: 'space-between' }}>
@@ -773,7 +857,7 @@ function Table({ columns, data, defaultheader, optionalheader, tablename, defaul
                           <div key={column.id} style={{ 'display': column.id !== 'actionpath' ? 'block' : 'none' }}>
                             <input type="checkbox" {...column.getToggleHiddenProps()}
                               id={(defaultheader[column.id]) ? defaultheader[column.id] : (optionalheader[column.id] ? optionalheader[column.id] : column.id)}
-                              onClick={onToggleChange}
+                              onClick={(e) => onToggleChange(e)}
                             /> {
                               (defaultheader[column.id]) ? defaultheader[column.id] : (optionalheader[column.id] ? optionalheader[column.id] : column.id)}
                           </div>
@@ -827,8 +911,8 @@ function Table({ columns, data, defaultheader, optionalheader, tablename, defaul
             {headerGroups.map(headerGroup => (
               <tr {...headerGroup.getHeaderGroupProps()}>
                 {headerGroup.headers.map(column => (
-                  <th>
-                    <div {...column.getHeaderProps(column.getSortByToggleProps())}>
+                  <th onClick={() => toggleBySorting({ 'id': column.id, desc: (column.isSortedDesc != undefined ? !column.isSortedDesc : false) })}>
+                    <div {...column.getHeaderProps(column.getSortByToggleProps())} >
                       {column.Header !== 'actionpath' && column.render('Header')}
                       {column.Header !== 'Action' ?
                         column.isSorted ? (column.isSortedDesc ? <i className="pi pi-sort-down" aria-hidden="true"></i> : <i className="pi pi-sort-up" aria-hidden="true"></i>) : ""
@@ -910,7 +994,8 @@ filterGreaterThan.autoRemove = val => typeof val !== 'number'
 function ViewTable(props) {
   const history = useHistory();
   // Data to show in table
-  tbldata = props.data;
+  console.log("sessionStorage", JSON.parse(sessionStorage.getItem("sortedData")));
+  tbldata = JSON.parse(sessionStorage.getItem("sortedData")) || props.data;
   showCSV = (props.showCSV) ? props.showCSV : false;
 
   parentCallbackFunction = props.filterCallback;
@@ -967,7 +1052,7 @@ function ViewTable(props) {
     });
   }
 
-  if (props.showaction === 'true') {
+  if (props.showaction) {
     columns.push({
       Header: 'Action',
       id: 'Action',
@@ -975,7 +1060,8 @@ function ViewTable(props) {
       Cell: props => <button className='p-link' onClick={navigateTo(props)} ><i className="fa fa-eye" style={{ cursor: 'pointer' }}></i></button>,
       disableFilters: true,
       disableSortBy: true,
-      isVisible: true//defaultdataheader.includes(props.keyaccessor),
+      //isVisible: defaultdataheader.includes(props.keyaccessor),
+      isVisible: true
     })
   }
 
@@ -989,7 +1075,7 @@ function ViewTable(props) {
           }
         })
       } else {
-        window.open(cellProps.cell.row.values['actionpath'] , '_blank');
+        window.open(cellProps.cell.row.values['actionpath'], '_blank');
       }
     }
     // Object.entries(props.paths[0]).map(([key,value]) =>{})
@@ -1032,17 +1118,11 @@ function ViewTable(props) {
 
   let togglecolumns = localStorage.getItem(tablename);
   if (togglecolumns) {
-        togglecolumns = JSON.parse(togglecolumns);
-        columns.forEach(column => {
-            let tcolumn = _.find(togglecolumns, {Header: column.Header});
-            column['isVisible'] = (tcolumn)? tcolumn.isVisible: column.isVisible;
-        });
-        /*columns.forEach(column => {
-            togglecolumns.filter(tcol => {
-            column.isVisible = (tcol.Header === column.Header) ? tcol.isVisible : column.isVisible;
-            return tcol;
-        });
-      });*/
+    togglecolumns = JSON.parse(togglecolumns);
+    columns.forEach(column => {
+      let tcolumn = _.find(togglecolumns, { Header: column.Header });
+      column['isVisible'] = (tcolumn) ? tcolumn.isVisible : column.isVisible;
+    });
   }
 
   function updatedCellvalue(key, value, properties) {
@@ -1063,13 +1143,13 @@ function ViewTable(props) {
         return retval;
       } else if (typeof value == "boolean") {
         return value.toString();
-      }else if (typeof value == "string") {
+      } else if (typeof value == "string") {
         const format = properties ? properties.format : 'YYYY-MM-DD HH:mm:ss';
         const dateval = moment(value, moment.ISO_8601).format(format);
         if (dateval !== 'Invalid date') {
           return dateval;
         }
-     }
+      }
     } catch (err) {
       console.error('Error', err)
     }
@@ -1079,7 +1159,11 @@ function ViewTable(props) {
   return (
     <div>
       <Table columns={columns} data={tbldata} defaultheader={defaultheader[0]} optionalheader={optionalheader[0]} showAction={props.showaction}
-        defaultSortColumn={defaultSortColumn} tablename={tablename} defaultpagesize={defaultpagesize} columnOrders={props.columnOrders} />
+        defaultSortColumn={defaultSortColumn} tablename={tablename} defaultpagesize={defaultpagesize} columnOrders={props.columnOrders} toggleBySorting={(sortData) => props.toggleBySorting(sortData)}
+        onColumnToggle={props.onColumnToggle}
+        lsKeySortColumn={props.lsKeySortColumn}
+        descendingColumn={props.descendingColumn}
+        ignoreSorting={props.ignoreSorting} />
     </div>
   )
 }
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/_overrides.scss b/SAS/TMSS/frontend/tmss_webapp/src/layout/_overrides.scss
index b417a3392a75736c5173c70da62a1e264d15075b..6eed93d5df517bd98cf4513613d1e8e83d6ac7b2 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/layout/_overrides.scss
+++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/_overrides.scss
@@ -245,6 +245,14 @@ In Excel View the for Accordion  background color override
     border-color: transparent !important;
 }
 
+.p-tabview-title {
+    display: inline !important;
+}
+
+.layout-sidebar {
+    overflow-y: unset;
+}
+
 /**
     Override the Primereact MultiSelect Dropdown
     Begin
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/components/PageHeader.js b/SAS/TMSS/frontend/tmss_webapp/src/layout/components/PageHeader.js
index 05648df30a821b8b89b2696f52df68ee7a7e5f16..754950eec81f46a31893569fe22af7256193d1c4 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/layout/components/PageHeader.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/components/PageHeader.js
@@ -2,7 +2,12 @@ import React, { useEffect, useState } from 'react';
 import { routes } from '../../routes';
 import {matchPath, Link} from 'react-router-dom';
 
+<<<<<<< HEAD
+export default ({ title, subTitle, actions, ...props}) => {    
+=======
 export default ({ title, subTitle, actions, ...props}) => {
+    debugger;
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
     const [page, setPage] = useState({});
 
     useEffect(() => {
@@ -40,7 +45,7 @@ export default ({ title, subTitle, actions, ...props}) => {
             </div>
             <div className="page-action-menu">
                 {(actions || []).map((action, index) =>{
-                    if (action.type === 'button') {
+                    if(action.type === 'button') {
                         return (
                             <button className="p-link" key={index} title={action.title || ''}>
                                 <i className={`fa ${action.disabled?'fa-disabled':''} ${action.icon}`}  
@@ -48,6 +53,15 @@ export default ({ title, subTitle, actions, ...props}) => {
                                     onClick={(e) => action.disabled?'':onButtonClick(e, action)} />
                             </button>
                         );
+                    } else if(action.type === 'element'){
+                        return(
+                            <div className={action.classes} dangerouslySetInnerHTML={{ __html: action.element }}/>
+                        )
+                    }   else if (action.type === 'ext_link') {
+                        return (
+                            <a href={action.props.pathname} title={action.title || ''} 
+                                target={action.target?action.target:"_blank"}>{action.label}</a>
+                        );
                     }   else {
                         return (
                             <Link key={index} className={action.classname} to={action.disabled?{}:{ ...action.props }} 
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_layout.scss b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_layout.scss
index 162da9617a52b68cd91028960fdabd32e09cc305..6b3aa8ba4bfce76b3347d3d09e2e2667017a540d 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_layout.scss
+++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_layout.scss
@@ -20,4 +20,5 @@
 @import "./reservation";
 @import "./animation";
 @import "./workflow";
+@import "./_report";
 // @import "./splitpane";
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_report.scss b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_report.scss
new file mode 100644
index 0000000000000000000000000000000000000000..f464c5864521c483657fc38bd3cc76c4d53d4f47
--- /dev/null
+++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_report.scss
@@ -0,0 +1,69 @@
+<<<<<<< HEAD
+.report-div, .report-div a, 
+.report-toolbar label, .report-toolbar input,
+.report-toolbar button {
+    font-size: 12px;
+    margin-bottom: 5px;
+}
+
+.report-toolbar .p-autocomplete-token {
+    margin-bottom: 2px !important;
+=======
+.report-div, .report-div a {
+    font-size: 12px;
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+}
+
+.report-div label {
+    font-size: 12px;
+    color: black;
+}
+
+<<<<<<< HEAD
+.print-btn {
+    margin-top: 10px;
+    cursor: pointer;
+    // color: rgb(4, 140, 252);
+}
+
+.report-toolbar .p-autocomplete-multiple-container {
+    padding: 0px 1px 0px 1px !important;
+}
+
+.report-toolbar .p-autocomplete-token-icon,
+.report-toolbar .p-autocomplete-token-label {
+    font-size: 12px !important;
+}
+
+.report-table {
+    width: 98%;
+    table-layout: fixed;
+    font-size: 10px;
+    word-break: break-all;
+=======
+.report-table {
+    width: 100%;
+    table-layout: fixed;
+    font-size: 10px;
+    word-break: break-word;
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+    border: 1px solid;
+}
+
+.report-table thead th, .report-table tbody td {
+    padding: 5px;
+    border: 1px solid;
+}
+
+.report-calendar span,.report-calendar span input {
+    width: 100%;
+<<<<<<< HEAD
+}
+
+.report-download-bar {
+    float: right;
+    margin-left: 5px;
+    font-size: 20px;
+=======
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+}
\ No newline at end of file
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_timeline.scss b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_timeline.scss
index a8754c015f28a1a839243790de09f507e734ff24..2b0360d86c6e0c6965febe1fa9464b0efa90a39c 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_timeline.scss
+++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_timeline.scss
@@ -29,6 +29,7 @@
 .timeline-view-toolbar .p-radiobutton {
     margin-top: -18px;
     margin-right: 3px;
+    height: 25px;
 }
 
 .timeline-toolbar-margin-top-0 {
@@ -424,6 +425,11 @@ body .p-multiselect-panel .p-multiselect-header .p-multiselect-filter-container
     padding: 0.520em;
     // padding-right: 6em;  //Ramesh: Not sure why is it required. As the search text content in the multiselect component is not visible, removing it.
 }
+
+.timeline-view-toolbar .p-multiselect .p-multiselect-label {
+    padding: 0em .2em 0em .2em
+}
+
 .alignTimeLineHeader {
     display: flex;
     justify-content: space-between;
@@ -438,7 +444,7 @@ body .p-multiselect-panel .p-multiselect-header .p-multiselect-filter-container
   //  top: -3px;
 }
 .toggle-btn {
-    height: 20px;
+    height: 25px;
     font-size: 12px !important;
     bottom: 8px !important;
 }
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_workflow.scss b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_workflow.scss
index e9172f64cf59d4f24fdcaf09752ebcbe494786c3..c1935e5355f79b0a0281046b1ebf1bf3d0976772 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_workflow.scss
+++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_workflow.scss
@@ -39,6 +39,13 @@
       }
     }
   }
+
+  .help-desk-link{
+    display: inline-block;
+    margin-right: 7px;
+    text-decoration: underline;
+    font-weight: bolder;
+  }
 }
 
 .step-header-1 {
@@ -89,4 +96,4 @@
 
 .btn-bar {
   padding: 10px;
-}
\ No newline at end of file
+}
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/list.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/list.js
index ac81b969c4f2c2a45696aaaba4424c2b3a29294d..4bef0c706c1d83ac6095b70b11bdc4febb6c6a07 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/list.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/list.js
@@ -8,9 +8,14 @@ import UnitConversion from '../../utils/unit.converter';
 import AppLoader from '../../layout/components/AppLoader';
 import PageHeader from '../../layout/components/PageHeader';
 import UIConstants from '../../utils/ui.constants';
+import UtilService from '../../services/util.service';
+/* eslint-disable no-unused-expressions */
 
-class CycleList extends Component{
-	 constructor(props){
+class CycleList extends Component {
+    lsTableName = "cycle_list";
+    lsKeySortColumn='cycleSortData';
+    descendingColumn=['start date']; // Values should be lower case
+    constructor(props) {
         super(props)
         this.state = {
             cyclelist: [],
@@ -21,83 +26,92 @@ class CycleList extends Component{
         }
         this.projectCategory = ['regular', 'user_shared_support'];
         this.periodCategory = ['long_term'];
-        this.defaultcolumns = [ {   id:"Cycle Code",
-                                    start: {
-                                        name: "Start Date",
-                                        filter: "date",
-                                        format: UIConstants.CALENDAR_DEFAULTDATE_FORMAT
-                                    },
-                                    stop: {
-                                        name: "End Date",
-                                        filter: "date",
-                                        format: UIConstants.CALENDAR_DEFAULTDATE_FORMAT
-                                    },
-                                    duration:{
-                                        name: "Duration (Days)",
-                                        filter: "range",
-                                        format: UIConstants.CALENDAR_TIME_FORMAT
-                                    },
-                                    totalProjects:{ 
-                                        name:'No.of Projects',
-                                        filter:"range"
-                                    },
-                                    observingTime:{
-                                        name: "Lofar Observing Time (Hrs)",
-                                        filter:"range"
-                                    },
-                                    processingTime:{ 
-                                        name:"Lofar Processing Time (Hrs)",
-                                        filter:"range"
-                                    },
-                                    ltaResources: {
-                                        name:"Lofar LTA Resources(TB)",
-                                        filter:"range"
-                                    },
-                                    support:{
-                                        name:"Lofar Support (Hrs)",
-                                        filter:"range"
-                                    },
-                                    longterm : {
-                                        name:"Long Term Projects",
-                                        filter:"range"
-                                    }} ];
-        this.optionalcolumns = [{   regularProjects:{                              
-                                        name: "No.of Regular Projects",
-                                        filter:"range"
-                                    },   
-                                    observingTimeDDT:{
-                                        name: "Lofar Observing Time Commissioning (Hrs)",
-                                        filter:"range"
-                                    },
-                                    observingTimePrioA:{
-                                        name:"Lofar Observing Time Prio A (Hrs)",
-                                        filter:"range"
-                                    },
-                                    observingTimePrioB:{
-                                        name:"Lofar Observing Time Prio B (Hrs)",
-                                        filter:"range"
-                                    },
-                                    actionpath: "actionpath" }];
+        this.defaultcolumns = [{
+            id: "Cycle Code",
+            start: {
+                name: "Start Date",
+                filter: "date",
+                format: UIConstants.CALENDAR_DEFAULTDATE_FORMAT
+            },
+            stop: {
+                name: "End Date",
+                filter: "date",
+                format: UIConstants.CALENDAR_DEFAULTDATE_FORMAT
+            },
+            duration: {
+                name: "Duration (Days)",
+                filter: "range",
+                format: UIConstants.CALENDAR_TIME_FORMAT
+            },
+            totalProjects: {
+                name: 'No.of Projects',
+                filter: "range"
+            },
+            observingTime: {
+                name: "Lofar Observing Time (Hrs)",
+                filter: "range"
+            },
+            processingTime: {
+                name: "Lofar Processing Time (Hrs)",
+                filter: "range"
+            },
+            ltaResources: {
+                name: "Lofar LTA Resources(TB)",
+                filter: "range"
+            },
+            support: {
+                name: "Lofar Support (Hrs)",
+                filter: "range"
+            },
+            longterm: {
+                name: "Long Term Projects",
+                filter: "range"
+            }
+        }];
+        this.optionalcolumns = [{
+            regularProjects: {
+                name: "No.of Regular Projects",
+                filter: "range"
+            },
+            observingTimeDDT: {
+                name: "Lofar Observing Time Commissioning (Hrs)",
+                filter: "range"
+            },
+            observingTimePrioA: {
+                name: "Lofar Observing Time Prio A (Hrs)",
+                filter: "range"
+            },
+            observingTimePrioB: {
+                name: "Lofar Observing Time Prio B (Hrs)",
+                filter: "range"
+            },
+            actionpath: "actionpath"
+        }];
 
-        this.columnclassname = [{   "Cycle Code":"filter-input-75",
-                                    "Duration (Days)" : "filter-input-50",
-                                    "No.of Projects" : "filter-input-50",
-                                    "Lofar Observing Time (Hrs)" : "filter-input-75",
-                                    "Lofar Processing Time (Hrs)" : "filter-input-75",
-                                    "Lofar LTA Resources(TB)" : "filter-input-75",
-                                    "Lofar Support (Hrs)" : "filter-input-50",
-                                    "Long Term Projects" : "filter-input-50",
-                                    "No.of Regular Projects" : "filter-input-50",
-                                    "Lofar Observing Time Commissioning (Hrs)" : "filter-input-75",
-                                    "Lofar Observing Time Prio A (Hrs)" : "filter-input-75",
-                                    "Lofar Observing Time Prio B (Hrs)" : "filter-input-75" }];
-                                     
-        this.defaultSortColumn = [{id: "Cycle Code", desc: false}];                          
+        this.columnclassname = [{
+            "Cycle Code": "filter-input-75",
+            "Duration (Days)": "filter-input-50",
+            "No.of Projects": "filter-input-50",
+            "Lofar Observing Time (Hrs)": "filter-input-75",
+            "Lofar Processing Time (Hrs)": "filter-input-75",
+            "Lofar LTA Resources(TB)": "filter-input-75",
+            "Lofar Support (Hrs)": "filter-input-50",
+            "Long Term Projects": "filter-input-50",
+            "No.of Regular Projects": "filter-input-50",
+            "Lofar Observing Time Commissioning (Hrs)": "filter-input-75",
+            "Lofar Observing Time Prio A (Hrs)": "filter-input-75",
+            "Lofar Observing Time Prio B (Hrs)": "filter-input-75"
+        }];
+
+        this.setToggleBySorting();
+        //this.defaultSortColumn = [{ id: "Start Date", desc: true }];
+        this.toggleBySorting = this.toggleBySorting.bind(this);
+        this.setToggleBySorting = this.setToggleBySorting.bind(this);
     }
     getUnitConvertedQuotaValue(cycle, cycleQuota, resourceName) {
-        const quota = _.find(cycleQuota, {'cycle_id': cycle.name, 'resource_type_id': resourceName});
+        const quota = _.find(cycleQuota, { 'cycle_id': cycle.name, 'resource_type_id': resourceName });
         const unitQuantity = this.state.resources.find(i => i.name === resourceName).quantity_value;
-        return UnitConversion.getUIResourceUnit(unitQuantity, quota?quota.value:0);
+        return UnitConversion.getUIResourceUnit(unitQuantity, quota ? quota.value : 0);
     }
     getCycles(cycles = [], cycleQuota) {
         const promises = [];
@@ -110,7 +124,7 @@ class CycleList extends Component{
                 const longterm = projects.filter(project => this.periodCategory.includes(project.period_category_value));
                 cycle.duration = UnitConversion.getUIResourceUnit('days', cycle.duration);
                 cycle.totalProjects = cycle.projects ? cycle.projects.length : 0;
-                cycle.id = cycle.name ;
+                cycle.id = cycle.name;
                 cycle.regularProjects = regularProjects.length;
                 cycle.longterm = longterm.length;
                 cycle.observingTime = this.getUnitConvertedQuotaValue(cycle, cycleQuota, 'LOFAR Observing Time');
@@ -120,18 +134,17 @@ class CycleList extends Component{
                 cycle.observingTimeDDT = this.getUnitConvertedQuotaValue(cycle, cycleQuota, 'LOFAR Observing Time Commissioning');
                 cycle.observingTimePrioA = this.getUnitConvertedQuotaValue(cycle, cycleQuota, 'LOFAR Observing Time prio A');
                 cycle.observingTimePrioB = this.getUnitConvertedQuotaValue(cycle, cycleQuota, 'LOFAR Observing Time prio B');
-                
                 cycle['actionpath'] = `/cycle/view/${cycle.id}`;
                 return cycle;
             });
             this.setState({
-                cyclelist : results,
+                cyclelist: results,
                 isLoading: false
             });
         });
     }
 
-    componentDidMount(){ 
+    componentDidMount() {
         const promises = [CycleService.getAllCycleQuotas(), CycleService.getResources()]
         Promise.all(promises).then(responses => {
             const cycleQuota = responses[0];
@@ -139,13 +152,34 @@ class CycleList extends Component{
             CycleService.getAllCycles().then(cyclelist => {
                 this.getCycles(cyclelist, cycleQuota)
             });
-        });  
+        });
+        this.setToggleBySorting();
+    }
+
+    setToggleBySorting() {   
+        let sortData = UtilService.localStore({ type: 'get', key: this.lsKeySortColumn });        
+        if(sortData){
+            if(Object.prototype.toString.call(sortData) === '[object Array]'){
+                this.defaultSortColumn = sortData;
+            }
+            else{
+                this.defaultSortColumn = [{...sortData}];
+            }
+        }else{
+            this.defaultSortColumn =  [{ id: "Start Date", desc: true }];
+        }
+        this.defaultSortColumn = this.defaultSortColumn || [];
+        UtilService.localStore({ type: 'set', key: 'cycleSortData', value: this.defaultSortColumn });
+    }
+
+    toggleBySorting(sortData) { 
+        UtilService.localStore({ type: 'set', key: this.lsKeySortColumn, value: [{...sortData}] });
     }
-	
-	render(){
+
+    render() {
         return (
             <>
-           { /*<div className="p-grid">
+                { /*<div className="p-grid">
                     <div className="p-col-10 p-lg-10 p-md-10">
                         <h2>Cycle - List </h2>
                     </div>
@@ -162,7 +196,7 @@ class CycleList extends Component{
                     showaction - {true/false} -> to show the action column
                     paths - specify the path for navigation - Table will set "id" value for each row in action button
                 */}
-                <PageHeader location={this.props.location} title={'Cycle - List'} actions={[{icon:'fa-plus-square',title:'Click to Add Cycle', props:{ pathname: '/cycle/create'}}]}/>
+                <PageHeader location={this.props.location} title={'Cycle - List'} actions={[{ icon: 'fa-plus-square', title: 'Click to Add Cycle', props: { pathname: '/cycle/create' } }]} />
                 {/*
                     * Call View table to show table data, the parameters are,
                     data - Pass API data
@@ -170,27 +204,26 @@ class CycleList extends Component{
                     showaction - {true/false} -> to show the action column
                     paths - specify the path for navigation - Table will set "id" value for each row in action button
                 */}
-                 
-                {this.state.isLoading? <AppLoader /> : (this.state.cyclelist && this.state.cyclelist.length) ?
-                
-                    <ViewTable 
-                        data={this.state.cyclelist} 
-                        defaultcolumns={this.defaultcolumns} 
+
+                {this.state.isLoading ? <AppLoader /> : (this.state.cyclelist && this.state.cyclelist.length) ?
+
+                    <ViewTable
+                        data={this.state.cyclelist}
+                        defaultcolumns={this.defaultcolumns}
                         optionalcolumns={this.optionalcolumns}
-                        columnclassname = {this.columnclassname}
-                        defaultSortColumn= {this.defaultSortColumn}
-                        showaction="true"
+                        columnclassname={this.columnclassname}
+                        defaultSortColumn={this.defaultSortColumn}
+                        showaction={true}
                         paths={this.state.paths}
-                        tablename="cycle_list"
-                 />  : <></>
-                 } 
-                
-
-                
+                        tablename={this.lsTableName}
+                        toggleBySorting={(sortData) => this.toggleBySorting(sortData)}
+                        lsKeySortColumn={this.lsKeySortColumn}
+                        descendingColumn={this.descendingColumn}
+                    /> : <></>
+                }
             </>
         )
     }
 }
 
-export default CycleList
-
+export default CycleList
\ No newline at end of file
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/view.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/view.js
index 47c90d8bcbca18c07900b81a15d3ed7512a5099a..2d3dec85738a927061d5b05396e020cf6112b423 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/view.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/view.js
@@ -110,10 +110,10 @@ export class CycleView extends Component {
                                 <span className="col-lg-4 col-md-4 col-sm-12">{this.state.cycle.description}</span>
                             </div>
                             <div className="p-grid">
-                                <label className="col-lg-2 col-md-2 col-sm-12">Created At</label>
-                                <span className="col-lg-4 col-md-4 col-sm-12">{moment.utc(this.state.cycle.created_at).format(UIConstants.CALENDAR_DATETIME_FORMAT)}</span>
-                                <label className="col-lg-2 col-md-2 col-sm-12">Updated At</label>
-                                <span className="col-lg-4 col-md-4 col-sm-12">{moment.utc(this.state.cycle.updated_at).format(UIConstants.CALENDAR_DATETIME_FORMAT)}</span>
+                                <label className="col-lg-2 col-md-2 col-sm-12">Start Time</label>
+                                <span className="col-lg-4 col-md-4 col-sm-12">{moment.utc(this.state.cycle.start).format(UIConstants.CALENDAR_DATETIME_FORMAT)}</span>
+                                <label className="col-lg-2 col-md-2 col-sm-12">End Time</label>
+                                <span className="col-lg-4 col-md-4 col-sm-12">{moment.utc(this.state.cycle.stop).format(UIConstants.CALENDAR_DATETIME_FORMAT)}</span>
                             </div>
                             
                             {/* <div className="p-grid">
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/list.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/list.js
index 3a36213f6769764122071412e4b2ad7b19c69467..207d7e40d38d251e48050f0abe0fc1ae95dec6f0 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/list.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/list.js
@@ -1,114 +1,122 @@
-import React, {Component} from 'react';
+import React, { Component } from 'react';
 import ProjectService from '../../services/project.service';
 import ViewTable from '../../components/ViewTable';
 import AppLoader from '../../layout/components/AppLoader';
 import PageHeader from '../../layout/components/PageHeader';
 import CycleService from '../../services/cycle.service';
+import UtilService from '../../services/util.service';
+/* eslint-disable no-unused-expressions */
 
-export class ProjectList extends Component{
-    constructor(props){
+export class ProjectList extends Component {
+    lsTableName = 'project_list';
+    lsKeySortColumn = "projectSortData";
+    defaultSortColumn= [{ id: "Name / Project Code", desc: false }];
+    constructor(props) {
         super(props)
         this.state = {
             projectlist: [],
-            defaultcolumns: [ {
-                name:"Name / Project Code",
-                status:{
-                    name:"Status",
-                    filter:"select"
+            defaultcolumns: [{
+                name: "Name / Project Code",
+                status: {
+                    name: "Status",
+                    filter: "select"
                 },
-                project_category_value:{
-                    name:"Category of Project",
-                    filter:"select"
+                project_category_value: {
+                    name: "Category of Project",
+                    filter: "select"
                 },
-                description:"Description",
-                archive_location_label:{
-                    name:"LTA Storage Location",
-                    filter:"select"
+                description: "Description",
+                archive_location_label: {
+                    name: "LTA Storage Location",
+                    filter: "select"
                 },
-                archive_subdirectory:"LTA Storage Path",
-             }],
-            optionalcolumns:  [{
-                priority_rank:{
-                    name:"Project Priority", 
-                    filter:"range"
+                archive_subdirectory: "LTA Storage Path",
+            }],
+            optionalcolumns: [{
+                priority_rank: {
+                    name: "Project Priority",
+                    filter: "range"
                 },
-                trigger_priority:{
-                    name:"Trigger Priority",
-                    filter:"range"
+                trigger_priority: {
+                    name: "Trigger Priority",
+                    filter: "range"
                 },
-                period_category_value:{
-                    name:"Category of Period",
-                    filter:"select"
+                period_category_value: {
+                    name: "Category of Period",
+                    filter: "select"
                 },
-                cycles_ids:{
-                    name:"Cycles",
-                    filter:"select"
+                cycles_ids: {
+                    name: "Cycles",
+                    filter: "select"
                 },
-                can_trigger:{
-                    name:"Trigger Allowed",
-                    filter:"switch"
+                can_trigger: {
+                    name: "Trigger Allowed",
+                    filter: "switch"
                 },
-                LOFAR_Observing_Time:{
-                    name:"Observing time (Hrs)",
-                    filter:"range"
+                LOFAR_Observing_Time: {
+                    name: "Observing time (Hrs)",
+                    filter: "range"
                 },
-                LOFAR_Observing_Time_prio_A:{
-                    name:"Observing time prio A (Hrs)",
-                    filter:"range"
+                LOFAR_Observing_Time_prio_A: {
+                    name: "Observing time prio A (Hrs)",
+                    filter: "range"
                 },
-                LOFAR_Observing_Time_prio_B:{
-                    name:"Observing time prio B (Hrs)",
-                    filter:"range"
+                LOFAR_Observing_Time_prio_B: {
+                    name: "Observing time prio B (Hrs)",
+                    filter: "range"
                 },
-                CEP_Processing_Time:{
-                    name:"Processing time (Hrs)",
-                    filter:"range"
+                CEP_Processing_Time: {
+                    name: "Processing time (Hrs)",
+                    filter: "range"
                 },
-                LTA_Storage:{
-                    name:"LTA storage (TB)",
-                    filter:"range"
+                LTA_Storage: {
+                    name: "LTA storage (TB)",
+                    filter: "range"
                 },
-                Number_of_triggers:{
-                    name:"Number of Triggers",
-                    filter:"range"
+                Number_of_triggers: {
+                    name: "Number of Triggers",
+                    filter: "range"
                 },
-                auto_pin:{
-                    name:"Prevent automatic deletion after ingest",
-                    filter:"switch"
+                auto_pin: {
+                    name: "Prevent automatic deletion after ingest",
+                    filter: "switch"
                 },
-                actionpath:"actionpath"
-               
+                actionpath: "actionpath"
+
             }],
             columnclassname: [{
-                "Observing time (Hrs)":"filter-input-50",
-                "Observing time prio A (Hrs)":"filter-input-50",
-                "Observing time prio B (Hrs)":"filter-input-50",
-                "Processing time (Hrs)":"filter-input-50",
-                "LTA storage (TB)":"filter-input-50",
-                "Status":"filter-input-50",
-                "Trigger Allowed":"filter-input-50",
-                "Number of Triggers":"filter-input-50",
-                "Project Priority":"filter-input-50",
-                "Trigger Priority":"filter-input-50",
-                "Category of Period":"filter-input-50",
-                "Cycles":"filter-input-100",
-                "LTA Storage Location":"filter-input-100",
-                "LTA Storage Path":"filter-input-100"
+                "Observing time (Hrs)": "filter-input-50",
+                "Observing time prio A (Hrs)": "filter-input-50",
+                "Observing time prio B (Hrs)": "filter-input-50",
+                "Processing time (Hrs)": "filter-input-50",
+                "LTA storage (TB)": "filter-input-50",
+                "Status": "filter-input-50",
+                "Trigger Allowed": "filter-input-50",
+                "Number of Triggers": "filter-input-50",
+                "Project Priority": "filter-input-50",
+                "Trigger Priority": "filter-input-50",
+                "Category of Period": "filter-input-50",
+                "Cycles": "filter-input-100",
+                "LTA Storage Location": "filter-input-100",
+                "LTA Storage Path": "filter-input-100"
             }],
-            defaultSortColumn: [{id: "Name / Project Code", desc: false}],
+            defaultSortColumn: [{ id: "Name / Project Code", desc: false }],
             isprocessed: false,
             isLoading: true
         }
         this.getPopulatedProjectList = this.getPopulatedProjectList.bind(this);
+        this.toggleBySorting = this.toggleBySorting.bind(this);
+        this.setToggleBySorting = this.setToggleBySorting.bind(this);
+        this.setToggleBySorting();
     }
 
     getPopulatedProjectList(cycleId) {
-        Promise.all([ProjectService.getFileSystem(), ProjectService.getCluster()]).then(async(response) => {
+        Promise.all([ProjectService.getFileSystem(), ProjectService.getCluster()]).then(async (response) => {
             const options = {};
             response[0].map(fileSystem => {
-                const cluster =  response[1].filter(clusterObj => { return (clusterObj.id === fileSystem.cluster_id && clusterObj.archive_site);});
+                const cluster = response[1].filter(clusterObj => { return (clusterObj.id === fileSystem.cluster_id && clusterObj.archive_site); });
                 if (cluster.length) {
-                    fileSystem.label =`${cluster[0].name} - ${fileSystem.name}`
+                    fileSystem.label = `${cluster[0].name} - ${fileSystem.name}`
                     options[fileSystem.url] = fileSystem;
                 }
                 return fileSystem;
@@ -116,7 +124,7 @@ export class ProjectList extends Component{
             let projects = [];
             if (cycleId) {
                 projects = await CycleService.getProjectsByCycle(cycleId);
-            }   else {
+            } else {
                 projects = await ProjectService.getProjectList();
             }
             projects = await ProjectService.getUpdatedProjectQuota(projects);
@@ -133,16 +141,37 @@ export class ProjectList extends Component{
         });
     }
 
-    componentDidMount(){  
+    componentDidMount() {      
         // Show Project for the Cycle, This request will be coming from Cycle View. Otherwise it is consider as normal Project List.
         let cycle = this.props.cycle;
         this.getPopulatedProjectList(cycle);
+        this.setToggleBySorting();
+    }
+
+    setToggleBySorting() {
+        let sortData = UtilService.localStore({ type: 'get', key: this.lsKeySortColumn });
+        if(sortData){
+            if(Object.prototype.toString.call(sortData) === '[object Array]'){
+                this.defaultSortColumn = sortData;
+            }
+            else{
+                this.defaultSortColumn = [{...sortData}];
+            }
+        }else{
+            this.defaultSortColumn =  [{ id: "Name / Project Code", desc: false }];
+        }
+        this.defaultSortColumn = this.defaultSortColumn || [];
+        UtilService.localStore({ type: 'set', key: this.lsKeySortColumn, value: [...this.defaultSortColumn] })        
     }
-   
-    render(){
-        return(
+
+    toggleBySorting(sortData) {
+        UtilService.localStore({ type: 'set', key: this.lsKeySortColumn, value: sortData });
+    }
+
+    render() {
+        return (
             <>
-               {/*<div className="p-grid">
+                {/*<div className="p-grid">
                     <div className="p-col-10 p-lg-10 p-md-10">
                         <h2>Project - List </h2>
                     </div>
@@ -152,27 +181,29 @@ export class ProjectList extends Component{
                         </Link>
                     </div>
                 </div> */}
-              { (this.props.cycle) ? 
-                <>
-                </>
-                :
-                <PageHeader location={this.props.location} title={'Project - List'} 
-                actions={[{icon: 'fa-plus-square',title:'Click to Add Project', props:{pathname: '/project/create' }}]}
-                />
-               }
-                {this.state.isLoading? <AppLoader /> : (this.state.isprocessed && this.state.projectlist.length>0) ?
-                    <ViewTable 
-                        data={this.state.projectlist} 
-                        defaultcolumns={this.state.defaultcolumns} 
+                { (this.props.cycle) ?
+                    <>
+                    </>
+                    :
+                    <PageHeader location={this.props.location} title={'Project - List'}
+                        actions={[{ icon: 'fa-plus-square', title: 'Click to Add Project', props: { pathname: '/project/create' } }]}
+                    />
+                }
+                {this.state.isLoading ? <AppLoader /> : (this.state.isprocessed && this.state.projectlist.length > 0) ?
+                    <ViewTable
+                        data={this.state.projectlist}
+                        defaultcolumns={this.state.defaultcolumns}
                         optionalcolumns={this.state.optionalcolumns}
                         columnclassname={this.state.columnclassname}
-                        defaultSortColumn={this.state.defaultSortColumn}
-                        showaction="true"
+                        defaultSortColumn={this.defaultSortColumn}
+                        showaction={true}
                         paths={this.state.paths}
                         keyaccessor="name"
                         unittest={this.state.unittest}
-                        tablename="project_list"
-                    />
+                        tablename={this.lsTableName}
+                        toggleBySorting={(sortData) => this.toggleBySorting(sortData)}
+                        lsKeySortColumn={this.lsKeySortColumn}
+                    />                   
                     : <div>No project found </div>
                 }
             </>
@@ -180,10 +211,11 @@ export class ProjectList extends Component{
     }
 
     // Set table data for Unit test
-    unittestDataProvider(){
-        if(this.props.testdata){
+    unittestDataProvider() {
+        if (this.props.testdata) {
             this.setState({
-                projectlist: [{can_trigger: true,
+                projectlist: [{
+                    can_trigger: true,
                     created_at: "2020-07-27T01:29:57.348499",
                     cycles: ["http://localhost:3000/api/cycle/Cycle%204/"],
                     cycles_ids: ["Cycle 4"],
@@ -194,16 +226,16 @@ export class ProjectList extends Component{
                     observing_time: "155852.10",
                     priority_rank: 10,
                     private_data: true,
-                    project_quota:   ["http://localhost:3000/api/project_quota/6/", "http://localhost:3000/api/project_quota/7/"],
-                    project_quota_ids:   [6, 7],
+                    project_quota: ["http://localhost:3000/api/project_quota/6/", "http://localhost:3000/api/project_quota/7/"],
+                    project_quota_ids: [6, 7],
                     tags: ["Lofar TMSS Project"],
                     trigger_priority: 20,
                     triggers_allowed: "56",
                     updated_at: "2020-07-27T01:29:57.348522",
                     url: "http://localhost:3000/api/project/Lofar-TMSS-Commissioning/"
-                    }],
-                    isprocessed: true,
-                    unittest: true,
+                }],
+                isprocessed: true,
+                unittest: true,
             })
         }
     }
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/index.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..d504a309c5b0936aed90ece2a9cda1aba996a396
--- /dev/null
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/index.js
@@ -0,0 +1,52 @@
+import React, { Component } from 'react';
+import { TabView,TabPanel } from 'primereact/tabview';
+
+import PageHeader from '../../layout/components/PageHeader';
+
+<<<<<<< HEAD
+import ProjectReportMain from "./project.report.main";
+=======
+import ProjectReport from "./project.report";
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+
+class ReportHome extends Component {
+
+    constructor(props) {
+        super(props);
+        this.state = {
+            reportIndex: 1
+        }
+        this.close = this.close.bind(this);
+    }
+
+    close() {
+        this.props.history.goBack();
+    }
+
+    render() {
+        return (
+            <React.Fragment>
+            <PageHeader location={this.props.location} title={'Reports'} actions={[{icon:'fa-window-close', title:'Click to Close Report',
+                   type: 'button',  actOn: 'click', props:{ callback: this.close }}]}/>
+            <TabView activeIndex={this.state.reportIndex} onTabChange={(e) => this.setState({reportIndex: e.index})}>
+                <TabPanel header="Cycle">
+                    <h1>Cycle Report...</h1>
+                </TabPanel>
+                <TabPanel header="Project">
+<<<<<<< HEAD
+                    <ProjectReportMain ></ProjectReportMain>
+=======
+                    <ProjectReport ></ProjectReport>
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+                </TabPanel>
+                <TabPanel header="Scheduling Unit">
+                    <h1>Scheduling Unit Report...</h1>
+                </TabPanel>
+            </TabView>
+            
+            </React.Fragment>
+        );
+    }
+}
+
+export default ReportHome;
\ No newline at end of file
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/project.report.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/project.report.js
new file mode 100644
index 0000000000000000000000000000000000000000..f98215a8a4735a3ef27cb4642f021d6f8dad19c1
--- /dev/null
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/project.report.js
@@ -0,0 +1,519 @@
+import React, { Component } from 'react';
+import { Link } from 'react-router-dom';
+import moment from 'moment';
+import _ from 'lodash';
+import { Bar } from 'react-chartjs-2';
+<<<<<<< HEAD
+
+import ReportService from '../../services/report.service';
+import UnitConverter from '../../utils/unit.converter';
+
+/**
+ * Component to get report data and display them for a Project.
+ */
+=======
+import jsPDF from 'jspdf';
+import html2canvas from 'html2canvas';
+
+import ProjectService from '../../services/project.service';
+import ReportService from '../../services/report.service';
+import UnitConverter from '../../utils/unit.converter';
+import { Dropdown } from 'primereact/dropdown';
+import { Calendar } from 'primereact/calendar';
+import { Button } from 'primereact/button';
+import CycleService from '../../services/cycle.service';
+
+const SU_DETAILS_COLUMNS = [{name: "su_name", headerTitle: "SU Name & Link in TMSS", propertyname: "name"},
+                            {name: "su_status", headerTitle: "SU Status Failed / Success ", propertyname: "status"},
+                            {name: "su_execDate", headerTitle: "SU Execution Date", propertyname: "exec_date"},
+                            {name: "observTime", headerTitle: "Time Observed (hr)", propertyname: "observingTime"},
+                            {name: "observTimeInc", headerTitle: "Time Observed Incremental (hr)", propertyname: "observingTimeInc"},
+                            {name: "observTimeLeft", headerTitle: "Time left for Observing (hr)", propertyname: "observingTimeLeft"},
+                            {name: "observTimeIncPercent", headerTitle: "Completed Observing Time(%)", propertyname: "observingTimeIncPercent"},
+                            {name: "processTime", headerTitle: "Time Processed (hr)", propertyname: "processTime"},
+                            {name: "processTimeInc", headerTitle: "Time Processed Incremental (hr)", propertyname: "processTimeInc"},
+                            {name: "processTimeLeft", headerTitle: "Time left for Processing (hr)", propertyname: "processTimeLeft"},
+                            {name: "processTimeIncPercent", headerTitle: "Completed Processing Time(%)", propertyname: "processTimeIncPercent"},
+                            {name: "ingestDate", headerTitle: "LTA Ingest Date", propertyname: "ingestDate"},
+                            {name: "ingestDataSize", headerTitle: "Ingested Data Size(TB)", propertyname: "ingestDataSize"},
+                            {name: "ingestDataIncPercent", headerTitle: "Used LTA Allocation (Incremental) (%)", propertyname: "ingestDataIncPercent"},
+                            {name: "observationSASId", headerTitle: "SAS ID (Observations)", propertyname: "observationSASId"},
+                            {name: "pipelinseSASId", headerTitle: "SAS ID (Pipelines)", propertyname: "pipelinseSASId"},
+                            ]
+
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+class ProjectReport extends Component {
+    
+    constructor(props) {
+        super(props);
+        this.state = {
+            project: null,
+            reportData: null,
+            projectResources: {},
+            suStatsList: [],
+            resourceUtilization: []
+        };
+<<<<<<< HEAD
+    }
+
+    componentDidMount() {
+        if (this.props.project) {
+            this.loadProjectReport(this.props.project);
+        }
+    }
+
+    componentDidUpdate() {
+        if (this.state.project !== this.props.project) {
+            this.loadProjectReport(this.props.project);
+        }
+    }
+
+    /**
+     * Get project report data and format or calculate values as required for the report.
+     * @param {Object} project 
+     */
+    async loadProjectReport(project) {
+        const resourceList = this.props.resourceList;
+        const projectReport = await ReportService.getProjectReport(project.name);
+        let projectResources = {};
+        if (projectReport.error) {
+            console.log(projectReport.error);
+        }   else {
+            // Get all project resources and conver the values to display values
+            for (let quota of projectReport.quota) {
+                const resource = _.find(resourceList, ["name", quota.resource_type_id]);
+                const conversionFactor = UnitConverter.resourceUnitMap[resource.quantity_value]?UnitConverter.resourceUnitMap[resource.quantity_value].conversionFactor:1;
+                quota.convertedValue =  (quota.value / conversionFactor).toFixed(2);
+                projectResources[quota.resource_type_id] = quota;
+            }
+        }
+        let suStatsList = [], resourceUtilization = [];
+        // Calculate SUB resource values from project resource valies.
+        // If it is received from report API, this needs to be modified.
+        if (projectReport["SUBs"]) {
+            const projectObservingTime = projectResources["LOFAR Observing Time"]?projectResources["LOFAR Observing Time"].value:0;
+            const projectProcessTime = projectResources["CEP Processing Time"]?projectResources["CEP Processing Time"].value:0;
+            const projectLTAStorage = projectResources["LTA Storage"]?projectResources["LTA Storage"].value:0;
+            const timeFactor = UnitConverter.resourceUnitMap["time"].conversionFactor;
+            const dataSizeFactor = UnitConverter.resourceUnitMap["bytes"].conversionFactor;
+            let totalSUBObsTime = 0, totalProcessTime = 0, totalLTAStorage = 0;
+            for (const subStatus of _.keys(projectReport["SUBs"])) {
+                let subs = projectReport["SUBs"][subStatus];
+                for (const sub of subs) {
+                    let reportSub = _.cloneDeep(sub);
+                    reportSub.status = subStatus;
+                    suStatsList.push(reportSub);
+                }
+            }
+            suStatsList = _.orderBy(suStatsList, ['id']);
+            for (const reportSub of suStatsList) {
+                if (reportSub.duration) {
+                    reportSub.observingTime = (reportSub.duration/timeFactor).toFixed(2);
+                    totalSUBObsTime += reportSub.duration;
+                    reportSub.observingTimeInc = (totalSUBObsTime / timeFactor).toFixed(2);
+                    reportSub.observingTimeLeft = ((projectObservingTime - totalSUBObsTime)/timeFactor).toFixed(2);
+                    reportSub.observingTimeIncPercent = (totalSUBObsTime/projectObservingTime*100).toFixed(2);
+                }
+                // For testing set duration as processDuration
+                reportSub.processDuration = reportSub.duration;    
+                if (reportSub.processDuration) {
+                    reportSub.processTime = (reportSub.processDuration/timeFactor).toFixed(2);
+                    totalProcessTime += reportSub.processDuration;
+                    reportSub.processTimeInc = (totalProcessTime / timeFactor).toFixed(2);
+                    reportSub.processTimeLeft = ((projectProcessTime - totalProcessTime)/timeFactor).toFixed(2);
+                    reportSub.processTimeIncPercent = (totalProcessTime / projectProcessTime *100).toFixed(2);
+                }
+                // For testing set dummy value for LTA dataproducts
+                reportSub["LTA dataproducts"] = {size__sum: 10737418240};
+                if (reportSub["LTA dataproducts"]) {
+                    reportSub.ingestDataSize = ((reportSub["LTA dataproducts"]["size__sum"] || 0)/dataSizeFactor).toFixed(2);
+                    totalLTAStorage += reportSub["LTA dataproducts"]["size__sum"];
+                    reportSub.ingestDataIncPercent = (totalLTAStorage / projectLTAStorage * 100).toFixed(2);
+                }
+                delete reportSub["LTA dataproducts"];
+            }
+            let observTimeUtilization = {type: 'Observing (hrs)', value: parseFloat((totalSUBObsTime/timeFactor).toFixed(2))};
+            resourceUtilization.push(observTimeUtilization);
+            let processTimeUtilization = {type: 'CEP Processing (hrs)', value: parseFloat((totalProcessTime/timeFactor).toFixed(2))};
+            resourceUtilization.push(processTimeUtilization);
+            let ltaStorageUtilization = {type: 'LTA Storage (TB)', value: parseFloat((totalLTAStorage/dataSizeFactor).toFixed(2))};
+            resourceUtilization.push (ltaStorageUtilization);
+        }
+        if (this.props.passReportData) {
+            this.props.passReportData(project.name, {reportData: projectReport, projectResources: projectResources, 
+                suStatsList: suStatsList, resourceUtilization: resourceUtilization});
+        }
+        this.setState({project: project, reportData: projectReport, projectResources: projectResources, 
+                        suStatsList: suStatsList, resourceUtilization: resourceUtilization});
+        
+    }
+
+    /**
+     * Renders the SU table header component
+     * @returns Component
+     */
+=======
+        this.selectProject = this.selectProject.bind(this);
+        this.loadProjectReport = this.loadProjectReport.bind(this);
+    }
+
+    componentDidMount() {
+        ProjectService.getResources()
+        .then(resourceList => {this.resourceList = resourceList});
+        CycleService.getAllCycles()
+        .then(cycles => {this.cycles = cycles});
+        ProjectService.getProjects()
+        .then(projects => { this.setState({projects: projects}) });
+    }
+
+    selectProject(projectName) {
+        const project = _.find(this.state.projects, ['name', projectName]);
+        let reportPeriod = [];
+        if (project.cycles_ids.length > 0) {
+            let projectCycleDates = [];
+            for (const cycleId of project.cycles_ids) {
+                const cycle = _.find(this.cycles, ['name', cycleId]);
+                projectCycleDates.push(moment(cycle.start));
+                projectCycleDates.push(moment(cycle.stop));
+            }
+            reportPeriod = [_.min(projectCycleDates).toDate(), _.max(projectCycleDates).toDate()];
+        }
+        this.setState({projectName: projectName, reportPeriod: reportPeriod})
+    }
+
+    async loadProjectReport() {
+        const projectName = this.state.projectName;
+        if (projectName) {
+            const resourceList = this.resourceList;
+            const project = _.find(this.state.projects, ['name', projectName]);
+            const projectReport = await ReportService.getProjectReport(projectName);
+            let projectResources = {};
+            if (projectReport.error) {
+                console.log(projectReport.error);
+            }   else {
+                for (let quota of projectReport.quota) {
+                    const resource = _.find(resourceList, ["name", quota.resource_type_id]);
+                    const conversionFactor = UnitConverter.resourceUnitMap[resource.quantity_value]?UnitConverter.resourceUnitMap[resource.quantity_value].conversionFactor:1;
+                    quota.convertedValue =  (quota.value / conversionFactor).toFixed(2);
+                    projectResources[quota.resource_type_id] = quota;
+                }
+            }
+            let suStatsList = [], resourceUtilization = [];
+            if (projectReport["SUBs"]) {
+                const projectObservingTime = projectResources["LOFAR Observing Time"]?projectResources["LOFAR Observing Time"].value:0;
+                const projectProcessTime = projectResources["CEP Processing Time"]?projectResources["CEP Processing Time"].value:0;
+                const projectLTAStorage = projectResources["LTA Storage"]?projectResources["LTA Storage"].value:0;
+                const timeFactor = UnitConverter.resourceUnitMap["time"].conversionFactor;
+                const dataSizeFactor = UnitConverter.resourceUnitMap["bytes"].conversionFactor;
+                let totalSUBObsTime = 0, totalProcessTime = 0, totalLTAStorage = 0;
+                for (const subStatus of _.keys(projectReport["SUBs"])) {
+                    let subs = projectReport["SUBs"][subStatus];
+                    for (const sub of subs) {
+                        let reportSub = _.cloneDeep(sub);
+                        reportSub.status = subStatus;
+                        suStatsList.push(reportSub);
+                    }
+                }
+                suStatsList = _.orderBy(suStatsList, ['id']);
+                for (const reportSub of suStatsList) {
+                    if (reportSub.duration) {
+                        reportSub.observingTime = (reportSub.duration/timeFactor).toFixed(2);
+                        totalSUBObsTime += reportSub.duration;
+                        reportSub.observingTimeInc = (totalSUBObsTime / timeFactor).toFixed(2);
+                        reportSub.observingTimeLeft = ((projectObservingTime - totalSUBObsTime)/timeFactor).toFixed(2);
+                        reportSub.observingTimeIncPercent = (totalSUBObsTime/projectObservingTime*100).toFixed(2);
+                    }
+                    // For testing set duration as processDuration
+                    reportSub.processDuration = reportSub.duration;    
+                    if (reportSub.processDuration) {
+                        reportSub.processTime = (reportSub.processDuration/timeFactor).toFixed(2);
+                        totalProcessTime += reportSub.processDuration;
+                        reportSub.processTimeInc = (totalProcessTime / timeFactor).toFixed(2);
+                        reportSub.processTimeLeft = ((projectProcessTime - totalProcessTime)/timeFactor).toFixed(2);
+                        reportSub.processTimeIncPercent = (totalProcessTime / projectProcessTime *100).toFixed(2);
+                    }
+                    // For testing set dummy value for LTA dataproducts
+                    reportSub["LTA dataproducts"] = {size__sum: 10737418240};
+                    if (reportSub["LTA dataproducts"]) {
+                        reportSub.ingestDataSize = (reportSub["LTA dataproducts"]["size__sum"]/dataSizeFactor).toFixed(2);
+                        totalLTAStorage += reportSub["LTA dataproducts"]["size__sum"];
+                        reportSub.ingestDataIncPercent = (totalLTAStorage / projectLTAStorage * 100).toFixed(2);
+                    }
+                }
+                let observTimeUtilization = {type: 'Observing (hrs)', value: parseFloat((totalSUBObsTime/timeFactor).toFixed(2))};
+                resourceUtilization.push(observTimeUtilization);
+                let processTimeUtilization = {type: 'CEP Processing (hrs)', value: parseFloat((totalProcessTime/timeFactor).toFixed(2))};
+                resourceUtilization.push(processTimeUtilization);
+                let ltaStorageUtilization = {type: 'LTA Storage (TB)', value: parseFloat((totalLTAStorage/dataSizeFactor).toFixed(2))};
+                resourceUtilization.push (ltaStorageUtilization);
+            }
+            this.setState({project: project, reportData: projectReport, projectResources: projectResources, 
+                            suStatsList: suStatsList, resourceUtilization: resourceUtilization});
+        }
+    }
+
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+    renderSUTableHeader() {
+        return (
+            <thead><tr>
+            {
+<<<<<<< HEAD
+                this.props.SU_DETAILS_COLUMNS.map((item, index) =>  {
+=======
+                SU_DETAILS_COLUMNS.map((item, index) =>  {
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+                    return <th key={("th_"+index)}>{item.headerTitle}</th>
+                })
+            }
+            </tr></thead>
+        );
+    }
+
+<<<<<<< HEAD
+    /**
+     * Renders SUB details rows
+     * @returns Component
+     */
+=======
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+    renderSURows() {
+        const suStatsList = this.state.suStatsList;
+        return (
+            <>
+             {
+                 suStatsList.map((rowData, rowIndex) => {
+                     return (
+                         <tr key={("tr_"+rowIndex)} style={{border: "1px solid"}}>
+<<<<<<< HEAD
+                            { this.props.SU_DETAILS_COLUMNS.map((column, colIndex) => {
+                                 return (
+                                    <td key={(rowIndex+"_td_"+colIndex)}>
+                                        {column.propertyName === "name"?(
+                                        <Link to={`/schedulingunit/view/blueprint/${rowData['id']}`} target="_blank">{rowData[column.propertyName]}</Link>
+                                        ):rowData[column.propertyName]}
+=======
+                            { SU_DETAILS_COLUMNS.map((column, colIndex) => {
+                                 return (
+                                    <td key={(rowIndex+"_td_"+colIndex)}>
+                                        {column.propertyname === "name"?(
+                                        <Link to={`/schedulingunit/view/blueprint/${rowData['id']}`} target="_blank">{rowData[column.propertyname]}</Link>
+                                        ):rowData[column.propertyname]}
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+                                    </td>
+                                 )
+                              })
+                            }
+                         </tr>
+                     )
+                 })
+             }
+            </>
+        );
+    }
+
+<<<<<<< HEAD
+    render() {
+        const reportData = this.state.reportData;
+        const project = this.props.project;
+        let barData = {}, barOptions = {};
+        const resourceUtilization = this.state.resourceUtilization;
+
+        // Resource Utilization bar chart data set with options
+=======
+    downloadPDF() {
+        // let pdf = new jsPDF();
+        // pdf.html(document.getElementById("report-div").innerHTML)
+        // .then(doc => {pdf.save("project.pdf")});
+        const input = document.getElementById('report-div');
+        html2canvas(input)
+        .then((canvas) => {
+            const imgData = canvas.toDataURL('image/png');
+            const pdf = new jsPDF();
+            pdf.addImage(imgData, 'JPEG', 0, 0);
+            // pdf.output('dataurlnewwindow');
+            pdf.save("project.pdf");
+        });
+    }
+
+    render() {
+        const reportData = this.state.reportData;
+        const project = this.state.project;
+        let barData = {}, barOptions = {};
+        const resourceUtilization = this.state.resourceUtilization;
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+        if (resourceUtilization.length > 0) {
+            barData = {
+                labels: _.map(resourceUtilization, 'type'),
+                datasets: [
+                    {   label: 'Utilization', 
+                        data: _.map(resourceUtilization, 'value'),
+                        backgroundColor: [
+                            '#44a3ce',
+                            '#44a3ce',
+                            '#44a3ce'
+<<<<<<< HEAD
+                        ]
+=======
+                        ],
+                        // borderColor: [
+                        //     'rgba(54, 162, 235, 1)',
+                        //     'rgba(54, 162, 235, 1)',
+                        //     'rgba(54, 162, 235, 1)'
+                        // ],
+                        // borderWidth: 1
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+                    }
+                ]
+            };
+
+            barOptions = {
+                indexAxis: 'y',
+                // Elements options apply to all of the options unless overridden in a dataset
+<<<<<<< HEAD
+                // In this case, we are setting the border of each horizontal bar to be 1px wide
+=======
+                // In this case, we are setting the border of each horizontal bar to be 2px wide
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+                elements: {
+                  bar: {
+                    borderWidth: 1,
+                  },
+                },
+                scales: {
+                    y: {
+                        max: 100
+                    }
+                },
+                responsive: true,
+                plugins: {
+                  legend: {
+                    position: 'right',
+                  },
+                  title: {
+                    display: true,
+                    text: 'Resource Utilization',
+                  }
+                }
+            };
+        }
+        return(
+            <React.Fragment>
+<<<<<<< HEAD
+                {reportData &&
+                <>
+                <div className="report-div" id={`${this.props.project.name}-report-div`}>
+                    <div id={`${this.props.project.name}-project-details`}>
+                    <h2 style={{textAlign: "center", marginBottom:"25px"}}>Report statistics for project {this.props.project.name}</h2>
+=======
+                <div className="report-toolbar p-grid" style={{marginTop: "10px", paddingBottom: "10px", borderBottom: "1px solid lightgrey"}}>
+                    <label htmlFor="project" className="col-lg-1 col-md-2 col-sm-12">Project </label>
+                    {/* <div className="col-lg-3 col-md-3 col-sm-12"> */}
+                        <Dropdown inputId="project" optionLabel="name" optionValue="name"
+                                className="col-lg-2 col-md-3 col-sm-12"
+                                tooltip="Select Project" tooltipOptions={this.tooltipOptions}
+                                value={this.state.projectName} 
+                                options={this.state.projects} 
+                                onChange={(e) => {this.selectProject(e.value)}} 
+                                placeholder="Select Project" />
+                    {/* </div> */}
+                    <label htmlFor="period" className="col-lg-2 col-md-2 col-sm-12" style={{paddingRight: "0px"}}>For Period </label>
+                    <div className="col-lg-3 col-md-3 col-sm-12 report-calendar">
+                        <Calendar value={this.state.reportPeriod} selectionMode="range" dateFormat="yy-mm-dd"
+                                onChange={e => this.setState({reportPeriod: e.value})}></Calendar>
+                    </div>
+                    <div className="col-lg-1 col-md-1 col-sm-12">
+                        <Button label="Generate" className="p-button-primary" icon="pi pi-check" 
+                                onClick={this.loadProjectReport} disabled={!this.state.projectName || !this.state.reportPeriod} />
+                        
+                    </div>
+                    {/* <i className="fa fa-download" onClick={(e) => this.downloadPDF()}></i> */}
+                </div>
+                {reportData &&
+                <>
+                <div className="report-div" id="report-div">
+                    <h2 style={{textAlign: "center", marginBottom:"25px"}}>Report statistics for project {this.props.project}</h2>
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+                    <div className="p-grid">
+                        <div className="col-lg-3 col-md-4 col-sm-12">
+                            <label>Project Documentation</label>
+                        </div>
+                        <div className="col-lg-9 col-md-8 col-sm-12">
+                            <a href="https://support.astron.nl/jira" target="_blank">Link to Jira Ticket</a>
+                        </div>
+                        <div className="col-lg-3 col-md-4 col-sm-12">
+                            <label>Project statistics over the period</label>
+                        </div>
+                        <div className="col-lg-8 col-md-9 col-sm-12">
+<<<<<<< HEAD
+                            <span>{this.props.reportPeriod?`${moment(this.props.reportPeriod[0]).format("MMM DD YYYY")} - ${moment(this.props.reportPeriod[1]).format("MMM DD YYYY")}`:"-"}</span>
+=======
+                            <span>{this.state.reportPeriod?`${moment(this.state.reportPeriod[0]).format("MMM DD YYYY")} - ${moment(this.state.reportPeriod[1]).format("MMM DD YYYY")}`:"-"}</span>
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+                        </div>
+                        <div className="col-lg-3 col-md-4 col-sm-12">
+                            <label>Contact Project Friend</label>
+                        </div>
+                        <div className="col-lg-8 col-md-9 col-sm-12">
+                            <span>{reportData.friends?reportData.friends.join(","):"-"}</span>
+                        </div>
+                        <div className="col-lg-3 col-md-4 col-sm-12">
+                            <label>Awarded Observing Time(hours)</label>
+                        </div>
+                        <div className="col-lg-8 col-md-9 col-sm-12">
+                            <span>{this.state.projectResources["LOFAR Observing Time"]?this.state.projectResources["LOFAR Observing Time"].convertedValue:"-"}</span>
+                        </div>
+                        <div className="col-lg-3 col-md-4 col-sm-12">
+                            <label>Awarded Processing Time(hours)</label>
+                        </div>
+                        <div className="col-lg-8 col-md-9 col-sm-12">
+                            <span>{this.state.projectResources["CEP Processing Time"]?this.state.projectResources["CEP Processing Time"].convertedValue:"-"}</span>
+                        </div>
+                        <div className="col-lg-3 col-md-4 col-sm-12">
+                            <label>Awarded LTA Storage(TB)</label>
+                        </div>
+                        <div className="col-lg-8 col-md-9 col-sm-12">
+                            <span>{this.state.projectResources["LTA Storage"]?this.state.projectResources["LTA Storage"].convertedValue:"-"}</span>
+                        </div>
+                        <div className="col-lg-3 col-md-4 col-sm-12">
+                            <label>Release Date</label>
+                        </div>
+                        <div className="col-lg-8 col-md-9 col-sm-12">
+                            <span>{project.releaseDate?project.releaseDate:"-"}</span>
+                        </div>
+                    </div>
+<<<<<<< HEAD
+                    {resourceUtilization.length > 0 &&
+                    <div className="resource-utilization" id={`${this.props.project.name}-resource-utilization`}
+                        style={{paddingTop: "10px", paddingBottom: "10px"}}>
+                        <Bar data={barData} options={barOptions} width="50%" height="10"/>
+                    </div>
+                    }
+                    </div>
+                    <div className="su-details" id={`${this.props.project.name}-su-details`}>
+=======
+
+                    {resourceUtilization.length > 0 &&
+                    <div className="resource-utilization" style={{paddingTop: "10px", paddingBottom: "10px"}}>
+                        <Bar data={barData} options={barOptions} width="50%" height="10"/>
+                    </div>
+                    }
+                    <div className="su_details">
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+                        <label>Scheduling Units of the project</label>
+                        <table className="report-table">
+                            {this.renderSUTableHeader()}
+                            <tbody>
+                                {this.renderSURows()}
+                            </tbody>
+                        </table>
+                    </div>
+                </div>
+                </>
+                }
+            </React.Fragment>
+        );
+    }
+
+}
+
+export default ProjectReport;
\ No newline at end of file
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/project.report.main.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/project.report.main.js
new file mode 100644
index 0000000000000000000000000000000000000000..f116dea986cb44b3fcd0c180c8f506f0a525f082
--- /dev/null
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/project.report.main.js
@@ -0,0 +1,310 @@
+import React, { Component } from 'react';
+import { Link } from 'react-router-dom';
+import moment from 'moment';
+import _ from 'lodash';
+import Papa from "papaparse";
+import jsPDF from 'jspdf';
+import html2canvas from 'html2canvas';
+import ReactToPrint from "react-to-print";
+
+import ProjectService from '../../services/project.service';
+import { AutoComplete } from 'primereact/autocomplete';
+import { Calendar } from 'primereact/calendar';
+import { Button } from 'primereact/button';
+import CycleService from '../../services/cycle.service';
+import ProjectReport from './project.report';
+
+// Constants for SU details table column property name to be used for identifying the properties in the 
+// report data and title for displaying in reports and while exporting them.
+const SU_DETAILS_COLUMNS = [{name: "su_name", headerTitle: "SU Name & Link in TMSS", propertyName: "name"},
+                            {name: "su_status", headerTitle: "SU Status Failed / Success ", propertyName: "status"},
+                            {name: "su_execDate", headerTitle: "SU Execution Date", propertyName: "exec_date"},
+                            {name: "observTime", headerTitle: "Time Observed (hr)", propertyName: "observingTime"},
+                            {name: "observTimeInc", headerTitle: "Time Observed Incremental (hr)", propertyName: "observingTimeInc"},
+                            {name: "observTimeLeft", headerTitle: "Time left for Observing (hr)", propertyName: "observingTimeLeft"},
+                            {name: "observTimeIncPercent", headerTitle: "Completed Observing Time(%)", propertyName: "observingTimeIncPercent"},
+                            {name: "processTime", headerTitle: "Time Processed (hr)", propertyName: "processTime"},
+                            {name: "processTimeInc", headerTitle: "Time Processed Incremental (hr)", propertyName: "processTimeInc"},
+                            {name: "processTimeLeft", headerTitle: "Time left for Processing (hr)", propertyName: "processTimeLeft"},
+                            {name: "processTimeIncPercent", headerTitle: "Completed Processing Time(%)", propertyName: "processTimeIncPercent"},
+                            {name: "ingestDate", headerTitle: "LTA Ingest Date", propertyName: "ingestDate"},
+                            {name: "ingestDataSize", headerTitle: "Ingested Data Size(TB)", propertyName: "ingestDataSize"},
+                            {name: "ingestDataIncPercent", headerTitle: "Used LTA Allocation (Incremental) (%)", propertyName: "ingestDataIncPercent"},
+                            {name: "observationSASId", headerTitle: "SAS ID (Observations)", propertyName: "observationSASId"},
+                            {name: "pipelinseSASId", headerTitle: "SAS ID (Pipelines)", propertyName: "pipelinseSASId"},
+                            ]
+
+/**
+ * Main component for Project Report
+ */
+class ProjectReportMain extends Component {
+    
+    constructor(props) {
+        super(props);
+        this.state = {
+            selectedProjects: [],   // Used by the Autocomplete field
+            reportProjects: [],     // Used to generate the report for selected project
+            reportPeriod: []        // Period of reporting
+        };
+        this.searchProjects = this.searchProjects.bind(this);
+        this.selectProjects = this.selectProjects.bind(this);
+        this.setReportProjects = this.setReportProjects.bind(this);
+        this.renderProjectReports = this.renderProjectReports.bind(this);
+        this.setReportData = this.setReportData.bind(this);
+        this.downloadCSV = this.downloadCSV.bind(this);
+        this.downloadPDF = this.downloadPDF.bind(this);
+        this.clearAll = this.clearAll.bind(this);
+    }
+
+    componentDidMount() {
+        ProjectService.getResources()
+        .then(resourceList => {this.resourceList = resourceList});
+        CycleService.getAllCycles()
+        .then(cycles => {this.cycles = cycles});
+        ProjectService.getProjects()
+        .then(projects => { this.setState({projects: projects}) });
+    }
+
+    /**
+     * Function passed to AutoComplete component to search and list
+     * @param {*} event 
+     */
+    searchProjects(event) {
+        setTimeout(() => {
+            let suggestedProjects;
+            if (!event.query.trim().length) {
+                suggestedProjects = [...this.state.projects];
+            }
+            else {
+                suggestedProjects = this.state.projects.filter((project) => {
+                    // For matching the start of the project with the search text
+                    return project.name.toLowerCase().startsWith(event.query.toLowerCase());
+                    // For values containing the search text
+                    // return project.name.toLowerCase().indexOf(event.query.toLowerCase())>=0;
+                });
+            }
+            this.setState({ suggestedProjects });
+        }, 250);
+    }
+
+    /**
+     * Function to set the selected projects from the AutoComplete component
+     * @param {Array} projects 
+     */
+    selectProjects(projects) {
+        let reportPeriod = [];
+        if (projects.length) {
+            let projectCycleDates = [];
+            // Get all cycle dates of the project associated with
+            for (const project of projects) {
+                if (project.cycles_ids.length > 0) {
+                    for (const cycleId of project.cycles_ids) {
+                        const cycle = _.find(this.cycles, ['name', cycleId]);
+                        projectCycleDates.push(moment(cycle.start));
+                        projectCycleDates.push(moment(cycle.stop));
+                    }
+                }
+            }
+            // Get the minimum and maximum date from the cycle dates and set as the report period
+            let minProjectCycleDate = _.min(projectCycleDates);
+            let maxProjectCycleDate = _.max(projectCycleDates);
+            if (minProjectCycleDate && maxProjectCycleDate) {
+                reportPeriod = [minProjectCycleDate.toDate(), maxProjectCycleDate.toDate()];
+            }
+        }
+        this.setState({selectedProjects: projects, reportPeriod: reportPeriod});
+    }
+
+    /**
+     * Function to generate the PDF of the report.
+     */
+    async downloadPDF() {
+        let reportDivs = document.getElementsByClassName('report-div');
+        // Get the div heights of each project report
+        let divHeights = _.map(reportDivs, 'clientHeight');
+        // Create a PDF document with landscape orientation, 1st report div width and the max height of all report divs in pixels
+        const pdf = new jsPDF('l', 'px',  [ (reportDivs[0].clientWidth+50), (_.max(divHeights)+50)]);
+        let pageIndex = 0;
+        // Draw each project report in canvas and create image of the canvas before saving the pdf
+        for (const project of this.state.reportProjects) {
+            // Create page for each project
+            if (pageIndex > 0) {
+                pdf.addPage();     
+            }
+            const projectCanvas = await html2canvas(document.getElementById(`${project.name}-report-div`));
+            pdf.addImage(projectCanvas.toDataURL('image/jpeg'), 'JPEG', 50, 50);
+            pageIndex++;
+        }
+        // To open the generated PDF in new window
+        // pdf.output('dataurlnewwindow');
+        const reportPeriod = `${moment(this.state.reportPeriod[0]).format("DDMMMYYYY")}-${moment(this.state.reportPeriod[1]).format("DDMMMYYYY")}`;
+        pdf.save(`Project_${reportPeriod}.pdf`);
+    }
+
+    /**
+     * Function to download the report data in CSV format
+     */
+    downloadCSV() {
+        let csvConfig = {};
+        let csvList= [];            //Data list to export
+        let colHeaders = [];        //Column header to export. Both no of headers and data values should be same.
+        let isColHeaderSet = false; //Flag to identify if colHeader is added
+        // For every project of the selected project get report data
+        for(const projectName of _.keys(this.projectsReportData)) {
+            // Get the whole report data set from ProjectReport component
+            const projectReport = this.projectsReportData[projectName];
+            // Get Project Data
+            const reportData = projectReport.reportData;
+            // Get Project Resources Data
+            const projectResources = projectReport.projectResources;
+            // Add project and SU data to the csv list
+            projectReport.suStatsList.map((data, index) => {
+                let csvData = {};
+                const colKeys = _.keys(data);
+                /* Add Project Details to the first row of the project SU data */
+                if (!isColHeaderSet) {
+                    colHeaders.push("Project");
+                    colHeaders.push("Contact Project Friend");
+                    colHeaders.push("Awarded Observing Time(hours)");
+                    colHeaders.push("Awarded Processing Time(hours)");
+                    colHeaders.push("Awarded LTA Storage(TB)");
+                }
+                if (index === 0) {
+                    csvData["Project"] = projectName;
+                    csvData["Contact Project Friend"] = reportData.friends?reportData.friends.join(","):"-"
+                    csvData["Awarded Observing Time(hours)"] = projectResources["LOFAR Observing Time"]?projectResources["LOFAR Observing Time"].convertedValue:"-";
+                    csvData["Awarded Processing Time(hours)"] = projectResources["CEP Processing Time"]?projectResources["CEP Processing Time"].convertedValue:"-";
+                    csvData["Awarded LTA Storage(TB)"] = projectResources["LTA Storage"]?projectResources["LTA Storage"].convertedValue:"-";
+                }
+                // For every column of the data, replace the column name with the column title
+                for (const colKey of colKeys) {
+                    let colHeader = _.find(SU_DETAILS_COLUMNS, ["propertyName", colKey]);
+                    colHeader = colHeader?colHeader.headerTitle:_.upperFirst(colKey);
+                    if (!isColHeaderSet) {
+                        colHeaders.push(colHeader);
+                    }
+                    csvData[colHeader] = data[colKey];
+                }
+                csvList.push(csvData);
+                isColHeaderSet = true;
+            });
+            
+        }
+        // Pass column headers to CSV parser
+        if (colHeaders.length > 0) {
+            csvConfig.columns = colHeaders;
+        }
+        // Parse the CSV list and header opject to create CSV string
+        const csvString = Papa.unparse(csvList, csvConfig);
+        // Create a CSV BLOB from the csvString
+        const blob = new Blob([csvString], { type: "text/csv" });
+
+        // Add dummy link to click and download the created CSV
+        var a = document.createElement("a");
+        document.body.appendChild(a);
+        a.style = "display: none";
+        var url = window.URL.createObjectURL(blob);
+        a.href = url;
+        const reportPeriod = `${moment(this.state.reportPeriod[0]).format("DDMMMYYYY")}-${moment(this.state.reportPeriod[1]).format("DDMMMYYYY")}`;
+        a.download = `Project_${reportPeriod}.csv`;
+        a.click();
+        window.URL.revokeObjectURL(url);
+    }
+
+    /**
+     * Function called to set report project from the selected projects.
+     */
+    setReportProjects() {
+        this.setState({ reportProjects: _.cloneDeep(this.state.selectedProjects)});
+    }
+
+    /**
+     * Callback function passed to the ProjectReport component to get the report data to use in CSV export.
+     * @param {String} projectName 
+     * @param {Object} projectReportData - All required data from the ProjectReport component
+     */
+    setReportData(projectName, projectReportData) {
+        let projectsReportData = this.projectsReportData || {};
+        projectsReportData[projectName] = projectReportData;
+        this.projectsReportData = projectsReportData;
+    }
+
+    /**
+     * Clears the report filters and report divs
+     */
+    clearAll(){
+        this.projectsReportData = {};
+        this.setState({selectedProjects:[], reportPeriod:[], reportProjects:[]});
+    }
+
+    /**
+     * To render multiple project reports from a list of projects.
+     * @returns Component
+     */
+    renderProjectReports() {
+        return (
+            <>
+                {
+                    this.state.reportProjects.map((project, index) => {
+                        return <ProjectReport project={project} resourceList={this.resourceList} 
+                                    reportPeriod={this.state.reportPeriod} SU_DETAILS_COLUMNS={SU_DETAILS_COLUMNS} 
+                                    passReportData={this.setReportData} />
+                    })
+                }
+            </>
+        );
+    }
+
+    render() {
+        return(
+            <React.Fragment>
+                <div className="report-toolbar p-grid" style={{marginTop: "10px", paddingBottom: "10px", borderBottom: "1px solid lightgrey"}}>
+                    <label htmlFor="project" className="col-lg-1 col-md-2 col-sm-12">Project </label>
+                    <AutoComplete className="col-lg-3 col-md-3 col-sm-12" multiple value={this.state.selectedProjects} suggestions={this.state.suggestedProjects}
+                        completeMethod={this.searchProjects} field="name" onChange={(e) => this.selectProjects(e.value)} />
+                    <label htmlFor="period" className="col-lg-2 col-md-2 col-sm-12" style={{paddingRight: "0px"}}>For Period </label>
+                    <div className="col-lg-3 col-md-3 col-sm-12 report-calendar">
+                        <Calendar value={this.state.reportPeriod} selectionMode="range" dateFormat="yy-mm-dd"
+                                onChange={e => this.setState({reportPeriod: e.value})}></Calendar>
+                    </div>
+                    <div className="col-lg-2 col-md-1 col-sm-12">
+                        <Button label="" className="p-button-primary" icon="pi pi-check" tooltip="Generate Report"
+                                onClick={this.setReportProjects} disabled={this.state.selectedProjects.length===0 || this.state.reportPeriod.length!==2} />
+                        <Button label="" className="p-button-primary" icon="fas fa-sync-alt" tooltip="Clear All"
+                                onClick={this.clearAll} style={{marginLeft:"10px"}} />
+                    </div>
+                </div>
+                {this.state.reportProjects.length>0 &&
+                <>
+                    <div ref={(el) => (this.reportDownloadBarRef = el)} >   
+                        <Link to={{}} className="report-download-bar" style={{color: "#148048"}} title="Download Report Data in CSV format" 
+                                onClick={this.downloadCSV}>
+                                <i className="fas fa-file-csv"></i>
+                        </Link> 
+                        <Link to={{}} className="report-download-bar" style={{color: "#f20f00"}} title="Download Report as PDF" 
+                                onClick={(e) => { // Scroll to the bottom of the report and back to the top before exporting
+                                                    this.reportEndRef.scrollIntoView({behavior: "smooth", block: "end"});
+                                                    this.reportDownloadBarRef.scrollIntoView({behavior: "smooth", block: "end"});
+                                                    setTimeout(this.downloadPDF, 500)}}>
+                                <i className="fas fa-file-pdf"></i>
+                        </Link> 
+                        <ReactToPrint
+                            trigger={() => <i className="pi pi-print report-download-bar print-btn" title="Print Report"></i>}
+                            content={() => this.reportPrintRef}
+                        />
+                    </div>
+                    <div ref={(el) => (this.reportPrintRef = el)}>
+                        { this.renderProjectReports()}
+                    </div>
+                    {/* Dummy reference div to scroll down before exporting to PDF so that 
+                        all components will be rendered properly before exporting */}
+                    <div ref={(el) => {this.reportEndRef = el}}></div>
+                </>
+                }
+            </React.Fragment>
+        );
+    }
+
+}
+
+export default ProjectReportMain;
\ No newline at end of file
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Reservation/reservation.list.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Reservation/reservation.list.js
index 22ddbc9addc2b443f597267cffd26ede7e25dfcc..f1ff8ea2a5d5e9c98e7c6991f93159cc4e087594 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Reservation/reservation.list.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Reservation/reservation.list.js
@@ -16,8 +16,10 @@ import UnitService from '../../utils/unit.converter';
 import UIConstants from '../../utils/ui.constants';
 import ReservationService from '../../services/reservation.service'; 
 import CycleService from '../../services/cycle.service';
+import UtilService from '../../services/util.service';
 
 export class ReservationList extends Component{
+    lsKeySortColumn = "ReservationListSortData";
     constructor(props){
         super(props);
         this.state = {
@@ -120,6 +122,7 @@ export class ReservationList extends Component{
     }
     
     async componentDidMount() {
+        this. setToggleBySorting();
         const promises = [  ReservationService.getReservations(),
             CycleService.getAllCycles(),
         ];
@@ -163,7 +166,25 @@ export class ReservationList extends Component{
             });
         });
     }
- 
+
+    toggleBySorting=(sortData) => {       
+        UtilService.localStore({ type: 'set', key: this.lsKeySortColumn, value: sortData });
+    }
+
+    setToggleBySorting() {      
+        let sortData = UtilService.localStore({ type: 'get', key: this.lsKeySortColumn });
+        if(sortData){
+            if(Object.prototype.toString.call(sortData) === '[object Array]'){
+                this.defaultSortColumn = sortData;
+            }
+            else{
+                this.defaultSortColumn = [{...sortData}];
+            }
+        }
+        this.defaultSortColumn = this.defaultSortColumn || [];
+        UtilService.localStore({ type: 'set', key: this.lsKeySortColumn, value: [...this.defaultSortColumn] });       
+    }
+	
     mergeResourceWithReservation ( reservation, params) {
         if( params ){
             Object.keys(params).map((key, i) => (
@@ -474,13 +495,15 @@ export class ReservationList extends Component{
                         defaultcolumns={this.state.defaultcolumns} 
                         optionalcolumns={this.state.optionalcolumns}
                         columnclassname={this.state.columnclassname}
-                        defaultSortColumn={this.state.defaultSortColumn}
+                        defaultSortColumn={this.defaultSortColumn}
                         showaction="true"
                         paths={this.state.paths}
                         tablename="reservation_list"
                         showCSV= {true}
                         allowRowSelection={true}
                         onRowSelection = {this.onRowSelection}
+                        toggleBySorting={(sortData) => this.toggleBySorting(sortData)}
+                        lsKeySortColumn={this.lsKeySortColumn}
                     />
                 </>
                 : <div>No Reservation found </div>
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/Scheduling.task.relation.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/Scheduling.task.relation.js
index 63646ee9251522943bd675e28e236f0290030c7b..e55b8f27712b840bcb19afa812abfa09d5ee2768 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/Scheduling.task.relation.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/Scheduling.task.relation.js
@@ -1,178 +1,305 @@
 import React, { useState, useEffect } from 'react';
 import { Dialog } from 'primereact/dialog';
-import {Checkbox} from 'primereact/checkbox';
+import { Checkbox } from 'primereact/checkbox';
 import { Button } from 'primereact/button';
 import _ from 'lodash';
 import UtilService from '../../services/util.service';
 import { CustomDialog } from '../../layout/components/CustomDialog';
+import { DataTable } from 'primereact/datatable';
+import { Column } from 'primereact/column';
 /* eslint-disable no-unused-expressions */
 
-export default (props) => {   
+export default (props) => {
     const [ingestRelation, setInjestRelation] = useState(_.cloneDeep(props.ingestGroup));
     const [isToggle, setToggle] = useState(true);
-    const [stateConfrimDialog, setConfirmDialog] = useState({dialog:{},dialogVisible:false});
+    const [stateConfrimDialog, setConfirmDialog] = useState({ dialog: {}, dialogVisible: false });
     const [addTaskRelationDraft, setAddTaskRelationDraft] = useState([]);
     const [taskRelationDraft, setTaskRelationDraft] = useState([]);
-    const [allTasksRel, setAllTasksRel] = useState([]); 
+    const [allTasksRel, setAllTasksRel] = useState([]);
     const isAllTaskChecked = (groupName) => !ingestRelation[groupName].filter(task => !task.canIngest).length;
-    const actionToggle = (taskData)=>{
-        return taskData.find( (task)=> task.action=='add' || task.action=='delete' )
+    const actionToggle = (taskData) => {
+        return taskData.find((task) => task.action == 'add' || task.action == 'delete')
     }
-    const closeDialog = () => { 
-        stateConfrimDialog.dialogVisible=false
-        setConfirmDialog({...stateConfrimDialog});
+    const closeDialog = () => {
+        stateConfrimDialog.dialogVisible = false
+        setConfirmDialog({ ...stateConfrimDialog });
         return false;
     }
-    const taskActionMode = (task,isGroup) => {
-        let tempTask = UtilService.findObject(taskRelationDraft,task,'id','id');
-        let tempTaskPosition = UtilService.findObjectIndex(taskRelationDraft,task,'id','id');
-        let taskAction = addOrDeleteAction(tempTask,task,isGroup);
-        taskRelationDraft[tempTaskPosition]=taskAction;
+    const taskActionMode = (task, isGroup) => {
+        let tempTask = UtilService.findObject(taskRelationDraft, task, 'id', 'id');
+        let tempTaskPosition = UtilService.findObjectIndex(taskRelationDraft, task, 'id', 'id');
+        let taskAction = addOrDeleteAction(tempTask, task, isGroup);
+        taskRelationDraft[tempTaskPosition] = taskAction;
         setTaskRelationDraft([...taskRelationDraft]);
     }
-    const confirmTasks = (modeObj,task,isGroup) => { 
+    const confirmTasks = (modeObj, task, isGroup) => {
+        debugger;
         const relationGroup = { ...ingestRelation };
-        if(modeObj.mode=='single'){
-            let tasCanIngest =  !relationGroup[modeObj.group][modeObj.pos].canIngest;
+        if (modeObj.mode == 'single') {
+            let tasCanIngest = !relationGroup[modeObj.group][modeObj.pos].canIngest;
             relationGroup[modeObj.group][modeObj.pos].canIngest = tasCanIngest;
-            setInjestRelation({...relationGroup}); 
-            task.canIngest=tasCanIngest; 
-            taskActionMode(task,isGroup);
+            setInjestRelation({ ...relationGroup });
+            task.canIngest = tasCanIngest;
+            taskActionMode(task, isGroup);
         }
         else {
-            if(modeObj.isSelected){
+            if (modeObj.isSelected) {
                 modeObj.findRelGroup.map(task => task.canIngest = false);
             }
-            else{
+            else {
                 modeObj.findRelGroup.map(task => task.canIngest = true);
             }
-            modeObj.findRelGroup.forEach(task =>  taskActionMode(task,isGroup));
+            modeObj.findRelGroup.forEach(task => taskActionMode(task, isGroup));
             setInjestRelation(relationGroup);
         }
-        if ( !actionToggle(taskRelationDraft) ) {
-            setToggle( true );
+        if (!actionToggle(taskRelationDraft)) {
+            setToggle(true);
         }
-        stateConfrimDialog.dialogVisible=false
-        setConfirmDialog({...stateConfrimDialog});
+        stateConfrimDialog.dialogVisible = false
+        setConfirmDialog({ ...stateConfrimDialog });
     }
-    const confirmDeleteTasks = (modeObj,task,isGroup)=> { 
-            let tempTask;
-            (!isGroup)? tempTask = UtilService.findObject(taskRelationDraft,task,'id','id'):'';
-            let displayDetails =  (isGroup) ? `${ (modeObj.isSelected)? 'remove ' : 'add '}  ${modeObj.group}`  : `${(tempTask.action || tempTask.canIngest) ?  'remove ' : 'add '} ${tempTask.name}`; 
-            let dialog = {};
-            dialog.type = "confirmation"; 
-            dialog.header=`Confirmation Task(s)`;
-            dialog.detail = `Do you want to ${displayDetails} Task?`;
-            dialog.content = ''//task;
-            dialog.actions = [{id: 'yes', title: 'Yes', callback: ()=> confirmTasks(modeObj,task,isGroup)},
-            {id: 'no', title: 'No', callback: closeDialog}];
-            dialog.onSubmit =  ()=> confirmTasks(modeObj,task,isGroup);
-            dialog.width = '55vw';
-            dialog.showIcon = false;
-            stateConfrimDialog.dialog=dialog;
-            stateConfrimDialog.dialogVisible=true;
-            setConfirmDialog({...stateConfrimDialog});        
+    const [observationTasks, setObservationTasks] = useState([]);
+    const [pipelineTasks, setPipelineTasks] = useState([]);
+
+    // const pipelineTasks = [];
+    const search = (myArray, key, value) => {
+        for (var i = 0; i < myArray.length; i++) {
+            if (myArray[i][key] === value) {
+                return myArray[i];
+            }
+        }
+    }
+    const upsertObservationTask = (data, displayMessage) => {
+        let tempobservationTasks = [...observationTasks];
+        //  const observationTasks = [];
+        // const pipelineTasks = [];
+        data = data || [];
+        let selectedItem = undefined;
+        for (let i = 0; i < data.length; i++) {
+            selectedItem = search(tempobservationTasks, 'id', data[i].id);
+            if (!selectedItem) {
+                tempobservationTasks.push({ ...data[i], displayMessage: displayMessage });
+            } else {
+                selectedItem.displayMessage = displayMessage;
+            }
+        }
+        setObservationTasks(tempobservationTasks);
+    }
+
+    const upsertPipeLineTasks = (data, displayMessage) => {
+        let temppipelineTasks = [...pipelineTasks];
+        //  const observationTasks = [];
+        // const pipelineTasks = [];
+        data = data || [];
+        let selectedItem = undefined;
+        for (let i = 0; i < data.length; i++) {
+            selectedItem = search(temppipelineTasks, 'id', data[i].id);
+            if (!selectedItem) {
+                temppipelineTasks.push({ ...data[i], displayMessage: displayMessage });
+            } else {
+                selectedItem.displayMessage = displayMessage;
+            }
+        }
+        setPipelineTasks(temppipelineTasks);
+    }
+    const confirmDeleteTasks = (modeObj, task, isGroup) => {
+        debugger
+        let tempTask;
+        (!isGroup) ? tempTask = UtilService.findObject(taskRelationDraft, task, 'id', 'id') : '';
+        let displayDetails = (isGroup) ? `${(modeObj.isSelected) ? 'remove ' : 'add '}` : `${(tempTask.action || tempTask.canIngest) ? 'remove ' : 'add '}`;
+        let values = [];
+        if (modeObj.mode == 'single')
+            values.push(tempTask);
+        else {
+            values = modeObj.findRelGroup;
+        }
+
+        let callObservation = false;
+        if (modeObj.group == 'observation') {
+            callObservation = true;
+        }
+        if (callObservation) {
+            upsertObservationTask(values, displayDetails);
+        }
+        else {
+            upsertPipeLineTasks(values, displayDetails);
+        }
+
+        confirmTasks(modeObj, task, isGroup);
+
+        // let tempTask;
+        // (!isGroup) ? tempTask = UtilService.findObject(taskRelationDraft, task, 'id', 'id') : '';
+        // let displayDetails = (isGroup) ? `${(modeObj.isSelected) ? 'remove ' : 'add '}  ${modeObj.group}` : `${(tempTask.action || tempTask.canIngest) ? 'remove ' : 'add '} ${tempTask.name}`;
+        // let dialog = {};
+        // dialog.type = "confirmation";
+        // dialog.header = `Confirmation Task(s)`;
+        // dialog.detail = `Do you want to ${displayDetails} Task?`;
+        // dialog.content = ''//task;
+        // dialog.actions = [{ id: 'yes', title: 'Yes', callback: () => confirmTasks(modeObj, task, isGroup) },
+        // { id: 'no', title: 'No', callback: closeDialog }];
+        // dialog.onSubmit = () => confirmTasks(modeObj, task, isGroup);
+        // dialog.width = '55vw';
+        // dialog.showIcon = false;
+        // stateConfrimDialog.dialog = dialog;
+        // stateConfrimDialog.dialogVisible = true;
+        // setConfirmDialog({ ...stateConfrimDialog });
     }
-    const addOrDeleteAction = (tempTask,task,isGroup) => {  
-        let tcanIngest = task.canIngest ;
+    const addOrDeleteAction = (tempTask, task, isGroup) => {
+        let tcanIngest = task.canIngest;
         let tpCanIngest = tempTask.canIngest;
         // common for single or group check/un-check
-        if((tpCanIngest && !tcanIngest && isGroup) || (tpCanIngest && !tcanIngest && !isGroup) )
-        { 
-            tempTask.action='delete';
+        if ((tpCanIngest && !tcanIngest && isGroup) || (tpCanIngest && !tcanIngest && !isGroup)) {
+            tempTask.action = 'delete';
         }
-        else if((!tpCanIngest && tcanIngest && !isGroup) || (!tpCanIngest && tcanIngest && isGroup))
-        {
-            tempTask.action='add';
+        else if ((!tpCanIngest && tcanIngest && !isGroup) || (!tpCanIngest && tcanIngest && isGroup)) {
+            tempTask.action = 'add';
         }
         else {
-            tempTask.action='';
+            tempTask.action = '';
         }
         return tempTask;
     }
-    const processTasks = (modeObj,taskRelationDraft,task,isGroup) => {         
-        confirmDeleteTasks(modeObj,task,isGroup);        
+    const processTasks = (modeObj, taskRelationDraft, task, isGroup) => {
+        confirmDeleteTasks(modeObj, task, isGroup);
     }
-    const toggleCheckItem = (group,task, index) => {
-        setToggle(false);  
-        processTasks({mode:'single',group,pos:index},taskRelationDraft,task,false,'');        
+    const toggleCheckItem = (group, task, index) => {
+        setToggle(false);
+        processTasks({ mode: 'single', group, pos: index }, taskRelationDraft, task, false, '');
     };
     const toggleGroup = (group) => {
-        setToggle(false); 
+        setToggle(false);
         const relationGroup = { ...ingestRelation };
         const findRelGroup = relationGroup[group];
-        let isSelected=false;
+        let isSelected = false;
         if (isAllTaskChecked(group)) {
             isSelected = true
-        } 
+        }
         else {
             isSelected = false
         }
-        processTasks({mode:'group',group,isSelected,findRelGroup,relationGroup},taskRelationDraft,null,true)       
+        processTasks({ mode: 'group', group, isSelected, findRelGroup, relationGroup }, taskRelationDraft, null, true)
     };
+
+    const getSUDialogContent = () => {
+        return <>
+            {(observationTasks.length > 0 || pipelineTasks.length > 0) &&
+                <div style={{ marginTop: '1em' }}>
+                    <b>Do you want to perform the following action(s)?</b>
+
+                </div>
+            }
+            {observationTasks.length > 0 &&
+                <div style={{ marginTop: '1em' }}>
+                    <b>Observation Task(s) - Data Products to Ingest </b>
+                    <DataTable value={observationTasks} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}>
+                        <Column field="name" header="Task Name"></Column>
+                        <Column field="displayMessage" header="Action"></Column>
+                    </DataTable>
+                </div>
+            }
+            {pipelineTasks.length > 0 &&
+                <div style={{ marginTop: '1em' }}>
+                    <b>Pipeline Task(s) - Data Products to Ingest </b>
+                    <DataTable value={pipelineTasks} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}>
+                        <Column field="name" header="Task Name"></Column>
+                        <Column field="displayMessage" header="Action"></Column>
+                    </DataTable>
+                </div>
+            }
+        </>
+    }
+
     const submitToIngest = () => {
-        setToggle(true);      
-        props.submitTRDToIngest({'ingest':ingestRelation.ingest[0],'taskRelationDraft':taskRelationDraft});//addTaskRelation    
+
+
+        let dialog = {};
+        dialog.type = "confirmation";
+        dialog.header = `Confirmation Task(s)`;
+        dialog.content = getSUDialogContent//task;
+        dialog.actions = [{
+            id: 'yes', title: 'Yes', callback: () => {
+                closeDialog();
+                props.submitTRDToIngest({ 'ingest': ingestRelation.ingest[0], 'taskRelationDraft': taskRelationDraft });
+                setToggle(true);
+            }
+        },
+        { id: 'no', title: 'No', callback: closeDialog }];
+        dialog.onSubmit = () => {
+            closeDialog();
+            props.submitTRDToIngest({ 'ingest': ingestRelation.ingest[0], 'taskRelationDraft': taskRelationDraft });
+            setToggle(true);
+        }
+        dialog.width = '55vw';
+        dialog.showIcon = false;
+        stateConfrimDialog.dialog = dialog;
+        stateConfrimDialog.dialogVisible = true;
+        setConfirmDialog({ ...stateConfrimDialog });
+
+        // props.submitTRDToIngest({ 'ingest': ingestRelation.ingest[0], 'taskRelationDraft': taskRelationDraft });//addTaskRelation    
     };
     useEffect(() => {
         setInjestRelation(_.cloneDeep(props.ingestGroup));
-        const ingestGroup = props.ingestGroup,tempIngestData=[];
-        Object.keys(ingestGroup).sort().map(group =>{
-            if(group !== 'ingest'){
-                ingestRelation[group].map((task, index)=>{ 
+        const ingestGroup = props.ingestGroup, tempIngestData = [];
+        Object.keys(ingestGroup).sort().map(group => {
+            if (group !== 'ingest') {
+                ingestRelation[group].map((task, index) => {
                     tempIngestData.push(task);
                 })
             }
-        }); 
+        });
         setAllTasksRel(_.cloneDeep(tempIngestData));
-        setTaskRelationDraft(_.cloneDeep(tempIngestData));         
+        setTaskRelationDraft(_.cloneDeep(tempIngestData));
     }, [props.ingestGroup]);
 
+    const footer = (
+        <div >
+            <Button label="Save" className="p-button-primary p-mr-2" icon="pi pi-check" disabled={isToggle} onClick={submitToIngest} data-testid="save-btn" />
+            <Button label="Cancel" className="p-button-danger mr-0" icon="pi pi-times" onClick={props.toggle} />
+        </div>
+    );
+
     return (
         <>
-        <Dialog header="Data Products To Ingest"
-            visible={props.showTaskRelationDialog} maximizable maximized={false} position="center" style={{ width: '50vw' }}
-            onHide={props.toggle} >
-            <div style={{width:'100%'}}>
-                <div class="p-fluid">
-                <div class="p-grid p-field" style={{paddingLeft:'15px'}}><h3>From Task</h3></div>                          
-                    {Object.keys(ingestRelation).sort().map(group => (
-                    <>
-                        {group !== 'ingest' && (
+            <Dialog header="Data Products To Ingest"
+                footer={footer}
+                visible={props.showTaskRelationDialog} maximizable maximized={false} position="center" style={{ width: '50vw' }}
+                onHide={props.toggle} >
+                <div style={{ width: '100%' }}>
+                    <div class="p-fluid">
+                        <div class="p-grid p-field" style={{ paddingLeft: '15px' }}><h3>From Task</h3></div>
+                        {Object.keys(ingestRelation).sort().map(group => (
                             <>
-                            <div className="p-col-12">
-                                    <Checkbox inputId={group} value={group} onChange={() => toggleGroup(group)} checked={isAllTaskChecked(group)}></Checkbox>
-                                    <label htmlFor={group} className="p-checkbox-label capitalize">{group}</label>
-                                </div>
-                               <div className="pl-4">
-                                    {ingestRelation[group].map((task, index) => 
-                                    (
-                                        <div className="p-col-12 pl-3">
-                                            <Checkbox inputId={task.name} onL onChange={() => toggleCheckItem(group,task, index)} checked={task.canIngest}></Checkbox>
-                                            <label htmlFor={task.name} className="p-checkbox-label">{task.name}</label>
+                                {group !== 'ingest' && (
+                                    <>
+                                        <div className="p-col-12">
+                                            <Checkbox inputId={group} value={group} onChange={() => toggleGroup(group)} checked={isAllTaskChecked(group)}></Checkbox>
+                                            <label htmlFor={group} className="p-checkbox-label capitalize">{group}</label>
+                                        </div>
+                                        <div className="pl-4">
+                                            {ingestRelation[group].map((task, index) =>
+                                            (
+                                                <div className="p-col-12 pl-3">
+                                                    <Checkbox inputId={task.name} onL onChange={() => toggleCheckItem(group, task, index)} checked={task.canIngest}></Checkbox>
+                                                    <label htmlFor={task.name} className="p-checkbox-label">{task.name}</label>
+                                                </div>
+                                            ))}
                                         </div>
-                                    ))}
-                                </div>
+                                    </>
+
+                                )}
                             </>
-                            
-                        )}
-                    </>
-                    ))}           
-                  
-                    
+                        ))}
+
+
+                    </div>
                 </div>
-                <div className="p-grid p-justify-end act-btn-grp" >
-                            <Button label="Save" className="p-button-primary p-mr-2" icon="pi pi-check" disabled={isToggle} onClick={submitToIngest}  data-testid="save-btn" />
-                            <Button label="Cancel" className="p-button-danger" icon="pi pi-times" onClick={props.toggle} />                   
-                </div>   
-            </div>  
-                                  
-            
-        </Dialog>
-         <CustomDialog type="confirmation" visible={stateConfrimDialog.dialogVisible}
-         header={stateConfrimDialog.dialog.header} message={stateConfrimDialog.dialog.detail} actions={stateConfrimDialog.dialog.actions}
-         content={stateConfrimDialog.dialog.content} width={stateConfrimDialog.dialog.width} showIcon={stateConfrimDialog.dialog.showIcon}
-         onClose={closeDialog} onCancel={closeDialog} onSubmit={confirmTasks}/>
-         </>
+
+
+            </Dialog>
+            <CustomDialog type="confirmation" visible={stateConfrimDialog.dialogVisible}
+                header={stateConfrimDialog.dialog.header} message={stateConfrimDialog.dialog.detail} actions={stateConfrimDialog.dialog.actions}
+                content={stateConfrimDialog.dialog.content} width={stateConfrimDialog.dialog.width} showIcon={stateConfrimDialog.dialog.showIcon}
+                onClose={closeDialog} onCancel={closeDialog} onSubmit={confirmTasks} />
+        </>
     )
 };
\ No newline at end of file
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/SchedulingUnitList.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/SchedulingUnitList.js
index 2e173e9085a13141c43365b63a7dab3778440cef..39d1dbb8eb60bfe5f12b37a449afe6ef382f62ee 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/SchedulingUnitList.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/SchedulingUnitList.js
@@ -13,10 +13,21 @@ import { DataTable } from 'primereact/datatable';
 import { Column } from 'primereact/column';
 import { CustomDialog } from '../../layout/components/CustomDialog';
 import { appGrowl } from '../../layout/components/AppGrowl';
+import UtilService from '../../services/util.service';
 
 class SchedulingUnitList extends Component{
+    lsKeySortColumn = "SchedulingUnitListSortData";
+<<<<<<< HEAD
+    defaultSortColumn= [{id: "Name", desc: false}]; 
+    SU_NOT_STARTED_STATUSES = ['defined', 'schedulable', 'scheduled'];
+    SU_ACTIVE_STATUSES = ['started', 'observing', 'observed', 'processing', 'processed', 'ingesting'];
+    SU_END_STATUSES = ['finished', 'error', 'cancelled'];
+=======
+    defaultSortColumn= [{id: "Name", desc: false}];    
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
     constructor(props){
        super(props);
+       this. setToggleBySorting();
        this.defaultcolumns = {
         status: {
             name: "Status",
@@ -151,6 +162,10 @@ class SchedulingUnitList extends Component{
         this.onRowSelection = this.onRowSelection.bind(this);
         this.reloadData = this.reloadData.bind(this);
         this.addTargetColumns = this.addTargetColumns.bind(this);
+        this.confirmCancelSchedulingUnit = this.confirmCancelSchedulingUnit.bind(this);
+        this.cancelSchedulingUnit = this.cancelSchedulingUnit.bind(this);
+        this.getSUCancelConfirmContent = this.getSUCancelConfirmContent.bind(this);
+        this.getSUCancelStatusContent = this.getSUCancelStatusContent.bind(this);
     }
 
     /**
@@ -406,6 +421,28 @@ class SchedulingUnitList extends Component{
 
     componentDidMount(){ 
        this.getSchedulingUnitList();
+       this. setToggleBySorting();
+    }
+
+    setToggleBySorting() {      
+        let sortData = UtilService.localStore({ type: 'get', key: this.lsKeySortColumn });
+        if(sortData){
+            if(Object.prototype.toString.call(sortData) === '[object Array]'){
+                this.defaultSortColumn = sortData;
+            }
+            else{
+                this.defaultSortColumn = [{...sortData}];
+            }
+        }
+        else {
+            this.defaultSortColumn = [{id: "Name", desc: false}];         
+        }
+        this.defaultSortColumn = this.defaultSortColumn || [];
+        UtilService.localStore({ type: 'set', key: this.lsKeySortColumn, value: [...this.defaultSortColumn] })       
+    }
+
+    toggleBySorting=(sortData) =>{
+        UtilService.localStore({ type: 'set', key: this.lsKeySortColumn, value: sortData });
     }
 
     /**
@@ -561,6 +598,125 @@ class SchedulingUnitList extends Component{
         this.componentDidMount();
     }
 
+    /**
+     * Prepare Scheduling Unit(s) details to show on confirmation dialog before cancelling
+     */
+     getSUCancelConfirmContent() {
+        let selectedSUs = [], ignoredSUs = [];
+        for (const obj of this.selectedRows) {
+            if (obj.type  === "Blueprint" && this.SU_END_STATUSES.indexOf(obj.status) < 0) {
+                selectedSUs.push({
+                    suId: obj.id, suName: obj.name,
+                    suType: obj.type, status: obj.status
+                });
+            }   else {
+                ignoredSUs.push({
+                    suId: obj.id, suName: obj.name,
+                    suType: obj.type, status: obj.status
+                });
+            }
+        }
+        return <>
+            <div style={{marginTop: '1em'}}>
+                <b>Scheduling Unit(s) that can be cancelled</b>
+                <DataTable value={selectedSUs} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}>
+                    <Column field="suId" header="Scheduling Unit Id"></Column>
+                    <Column field="suName" header="Scheduling Unit Name"></Column>
+                    <Column field="suType" header="Type"></Column>
+                    <Column field="status" header="Status"></Column>
+                </DataTable>
+            </div>
+            {ignoredSUs.length > 0 &&
+            <div style={{marginTop: '1em'}}>
+                <b>Scheduling Unit(s) that will be ignored</b>
+                <DataTable value={ignoredSUs} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}>
+                    <Column field="suId" header="Scheduling Unit Id"></Column>
+                    <Column field="suName" header="Scheduling Unit Name"></Column>
+                    <Column field="suType" header="Type"></Column>
+                    <Column field="status" header="Status"></Column>
+                </DataTable>
+            </div>
+            }
+        </>
+    }
+
+    /**
+     * Prepare Scheduling Unit(s) details to show status of cancellationn
+     */
+    getSUCancelStatusContent() {
+        let cancelledSchedulingUnits = this.state.cancelledSchedulingUnits;
+        return <>
+            <DataTable value={cancelledSchedulingUnits} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}>
+                <Column field="suId" header="Scheduling Unit Id"></Column>
+                <Column field="suName" header="Scheduling Unit Name"></Column>
+                <Column field="type" header="Type"></Column>
+                <Column field="status" header="Status"></Column>
+            </DataTable>
+        </>
+    }
+
+    /**
+     * Function to get confirmation before cancelling all selected scheduling unit blueprints if the status is 
+     * not one of the end statuses. 
+     * If no selected scheduling unit is cancellable, show info to select a cancellable scheduling unit.
+     * 
+     */
+    confirmCancelSchedulingUnit() {
+        let selectedBlueprints = this.selectedRows.filter(schedulingUnit => {
+                                        return schedulingUnit.type === 'Blueprint' && 
+                                        this.SU_END_STATUSES.indexOf(schedulingUnit.status)<0});
+        if (selectedBlueprints.length === 0) {
+            appGrowl.show({ severity: 'info', summary: 'Select Row', 
+                            detail: 'Select atleast one cancellable Scheduling Unit Blueprint to cancel.' });
+        } else {
+            let dialog = this.state.dialog;
+            dialog.type = "confirmation";
+            dialog.header = "Confirm to Cancel Scheduling Unit(s)";
+            dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.cancelSchedulingUnit, className:(this.props.project)?"dialog-btn": "" },
+                                { id: 'no', title: 'No', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": "" }];
+            dialog.detail = "Cancelling the scheduling unit means it will no longer be executed / will be aborted. This action cannot be undone. Already finished/cancelled scheduling unit(s) will be ignored. Do you want to proceed?";
+            dialog.content = this.getSUCancelConfirmContent;
+            dialog.submit = this.cancelSchedulingUnit;
+            dialog.width = '55vw';
+            dialog.showIcon = false;
+            this.setState({ dialog: dialog, dialogVisible: true });
+        }
+    }
+    
+    /**
+     * Function to cancel all selected Scheduling Unit blueprints if its status is not one of the end statuses
+     * and update their status on successful cancellation.
+     */
+    async cancelSchedulingUnit() {
+        let schedulingUnits = this.state.scheduleunit;
+        let selectedBlueprints = this.selectedRows.filter(su => {return su.type === 'Blueprint'});
+        let cancelledSchedulingUnits = []
+        for (const selectedSU of selectedBlueprints) {
+            if (this.SU_END_STATUSES.indexOf(selectedSU.status) < 0) {
+                const cancelledSU = await ScheduleService.cancelSchedulingUnit(selectedSU.id);
+                let schedulingUnit = _.find(schedulingUnits, {'id': selectedSU.id, type: 'Blueprint'});
+                if (cancelledSU) {
+                    schedulingUnit.status = cancelledSU.status;
+                }
+                cancelledSchedulingUnits.push({
+                    suId: schedulingUnit.id, suName: schedulingUnit.name, type: schedulingUnit.type,
+                    status: schedulingUnit.status.toLowerCase()==='cancelled'?'Cancelled': 'Error Occured'
+                });
+            }
+        }
+        let dialog = this.state.dialog;
+        dialog.type = "confirmation";
+        dialog.header = "Cancel Scheduling Unit(s) Status";
+        dialog.actions = [{ id: 'no', title: 'Ok', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": "" }];
+        dialog.detail = ""
+        dialog.content = this.getSUCancelStatusContent;
+        dialog.submit = this.closeDialog;
+        dialog.width = '55vw';
+        dialog.showIcon = false;
+        this.selectedRows = [];
+        this.setState({ scheduleunit: schedulingUnits, cancelledSchedulingUnits: cancelledSchedulingUnits, dialog: dialog, dialogVisible: true });
+    }
+    
     /**
      * Callback function to close the dialog prompted.
      */
@@ -590,9 +746,14 @@ class SchedulingUnitList extends Component{
                     <div >
                         <span className="p-float-label">
                             {this.state.scheduleunit && this.state.scheduleunit.length > 0 &&
+                            <>
+                                <a href="#" onClick={this.confirmCancelSchedulingUnit}  title="Cancel selected Scheduling Unit(s)">
+                                    <i class="fa fa-ban" aria-hidden="true" ></i>
+                                </a>
                                 <a href="#" onClick={this.checkAndDeleteSchedulingUnit}  title="Delete selected Scheduling Unit(s)">
                                     <i class="fa fa-trash" aria-hidden="true" ></i>
                                 </a>
+                            </>
                             }
                         </span>
                     </div>                           
@@ -605,7 +766,7 @@ class SchedulingUnitList extends Component{
                         optionalcolumns={this.state.optionalcolumns}
                         columnclassname={this.state.columnclassname}
                         columnOrders={this.state.columnOrders}
-                        defaultSortColumn={this.state.defaultSortColumn}
+                        defaultSortColumn={this.defaultSortColumn}
                         showaction="true"
                         keyaccessor="id"
                         paths={this.state.paths}
@@ -613,6 +774,8 @@ class SchedulingUnitList extends Component{
                         tablename="scheduleunit_list"
                         allowRowSelection={this.props.allowRowSelection}
                         onRowSelection = {this.onRowSelection}
+                        toggleBySorting={(sortData) => this.toggleBySorting(sortData)}
+                        lsKeySortColumn={this.lsKeySortColumn}                        
                     />
                     :<div>No Scheduling Unit found</div>
                  }  
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js
index c03fe6d1a4da8d6bd9b32253d9e058161de5f479..ac4312f5cf39e8a8398717630147417777483aa3 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js
@@ -14,7 +14,7 @@ import moment from 'moment';
 import _, { initial } from 'lodash';
 import SchedulingConstraint from './Scheduling.Constraints';
 import { Dialog } from 'primereact/dialog';
-import TaskStatusLogs from '../Task/state_logs'; 
+import TaskStatusLogs from '../Task/state_logs';
 import Stations from './Stations';
 import { Redirect } from 'react-router-dom';
 import { CustomDialog } from '../../layout/components/CustomDialog';
@@ -26,12 +26,23 @@ import TaskService from '../../services/task.service';
 import UIConstants from '../../utils/ui.constants';
 import UtilService from '../../services/util.service';
 
-class ViewSchedulingUnit extends Component{
-    constructor(props){
-        super(props)
+class ViewSchedulingUnit extends Component {
+    lsKeySortColumn = 'SortDataViewSchedulingUnit';
+    defaultSortColumn = [];
+    ignoreSorting = ['status logs'];
+<<<<<<< HEAD
+    SU_NOT_STARTED_STATUSES = ['defined', 'schedulable', 'scheduled'];
+    SU_ACTIVE_STATUSES = ['started', 'observing', 'observed', 'processing', 'processed', 'ingesting'];
+    SU_END_STATUSES = ['finished', 'error', 'cancelled'];
+    TASK_END_STATUSES = ['finished', 'error', 'cancelled'];
+=======
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+    constructor(props) {
+        super(props);
+        this.setToggleBySorting();
         this.state = {
             scheduleunit: null,
-            schedule_unit_task: [],
+            schedulingUnitTasks: [],
             isLoading: true,
             showStatusLogs: false,
             showTaskRelationDialog: false,
@@ -42,107 +53,107 @@ class ViewSchedulingUnit extends Component{
             missingStationFieldsErrors: [],
             columnOrders: [
                 "Status Logs",
-                 "Status",
-                 "Type",
-                 "ID",
-                 "Control ID",
-                 "Name",
-                 "Description",
-                 "Start Time",
-                 "End Time",
-                 "Duration (HH:mm:ss)",
-                 "Relative Start Time (HH:mm:ss)",
-                 "Relative End Time (HH:mm:ss)",
-                 "#Dataproducts",
-                 "size",
-                 "dataSizeOnDisk",
-                 "subtaskContent",
-                 "tags",
-                 "blueprint_draft",
-                 "url",
-                 "Cancelled",
-                 "Created at",
-                 "Updated at"
-             ],
-            defaultcolumns: [ {
+                "Status",
+                "Type",
+                "ID",
+                "Control ID",
+                "Name",
+                "Description",
+                "Start Time",
+                "End Time",
+                "Duration (HH:mm:ss)",
+                "Relative Start Time (HH:mm:ss)",
+                "Relative End Time (HH:mm:ss)",
+                "#Dataproducts",
+                "size",
+                "dataSizeOnDisk",
+                "subtaskContent",
+                "tags",
+                "blueprint_draft",
+                "url",
+                "Cancelled",
+                "Created at",
+                "Updated at"
+            ],
+            defaultcolumns: [{
                 status_logs: "Status Logs",
-                status:{
-                name:"Status",
-                filter: "select"
+                status: {
+                    name: "Status",
+                    filter: "select"
                 },
-                tasktype:{
-                    name:"Type",
-                    filter:"select"
+                tasktype: {
+                    name: "Type",
+                    filter: "select"
                 },
                 id: "ID",
                 subTaskID: 'Control ID',
-                name:"Name",
-                description:"Description",
-                start_time:{
-                    name:"Start Time",
+                name: "Name",
+                description: "Description",
+                start_time: {
+                    name: "Start Time",
                     filter: "date",
-                    format:UIConstants.CALENDAR_DATETIME_FORMAT
+                    format: UIConstants.CALENDAR_DATETIME_FORMAT
                 },
-                stop_time:{
-                    name:"End Time",
+                stop_time: {
+                    name: "End Time",
                     filter: "date",
-                    format:UIConstants.CALENDAR_DATETIME_FORMAT
+                    format: UIConstants.CALENDAR_DATETIME_FORMAT
                 },
-                duration:{
-                    name:"Duration (HH:mm:ss)",
-                    format:UIConstants.CALENDAR_TIME_FORMAT
+                duration: {
+                    name: "Duration (HH:mm:ss)",
+                    format: UIConstants.CALENDAR_TIME_FORMAT
                 },
-                relative_start_time:"Relative Start Time (HH:mm:ss)",
-                relative_stop_time:"Relative End Time (HH:mm:ss)",
+                relative_start_time: "Relative Start Time (HH:mm:ss)",
+                relative_stop_time: "Relative End Time (HH:mm:ss)",
                 noOfOutputProducts: "#Dataproducts",
-                do_cancel:{
+                do_cancel: {
                     name: "Cancelled",
                     filter: "switch"
                 },
             }],
-            optionalcolumns:  [{
+            optionalcolumns: [{
                 size: "Data size",
                 dataSizeOnDisk: "Data size on Disk",
                 subtaskContent: "Subtask Content",
-                tags:"Tags",
-                blueprint_draft:"BluePrint / Task Draft link",
-                url:"API URL",
-                created_at:{
+                tags: "Tags",
+                blueprint_draft: "BluePrint / Task Draft link",
+                url: "API URL",
+                created_at: {
                     name: "Created at",
-                    filter:"date",
-                    format:UIConstants.CALENDAR_DATETIME_FORMAT
+                    filter: "date",
+                    format: UIConstants.CALENDAR_DATETIME_FORMAT
                 },
-                updated_at:{
+                updated_at: {
                     name: "Updated at",
                     filter: "date",
-                    format:UIConstants.CALENDAR_DATETIME_FORMAT
+                    format: UIConstants.CALENDAR_DATETIME_FORMAT
                 },
-                actionpath:"actionpath"
+                actionpath: "actionpath"
             }],
             columnclassname: [{
                 "Status Logs": "filter-input-0",
-                "Type":"filter-input-75",
-                "ID":"filter-input-50",
-                "Control ID":"filter-input-75",
-                "Cancelled":"filter-input-50",
-                "Duration (HH:mm:ss)":"filter-input-75",
-                "Template ID":"filter-input-50",
+                "Type": "filter-input-75",
+                "ID": "filter-input-50",
+                "Control ID": "filter-input-75",
+                "Cancelled": "filter-input-50",
+                "Duration (HH:mm:ss)": "filter-input-75",
+                "Template ID": "filter-input-50",
                 // "BluePrint / Task Draft link": "filter-input-100",
                 "Relative Start Time (HH:mm:ss)": "filter-input-75",
                 "Relative End Time (HH:mm:ss)": "filter-input-75",
-                "Status":"filter-input-100",
-                "#Dataproducts":"filter-input-75",
-                "Data size":"filter-input-50",
-                "Data size on Disk":"filter-input-50",
-                "Subtask Content":"filter-input-75",
-                "BluePrint / Task Draft link":"filter-input-50",
+                "Status": "filter-input-100",
+                "#Dataproducts": "filter-input-75",
+                "Data size": "filter-input-50",
+                "Data size on Disk": "filter-input-50",
+                "Subtask Content": "filter-input-75",
+                "BluePrint / Task Draft link": "filter-input-50",
             }],
             stationGroup: [],
-            dialog: {header: 'Confirm', detail: 'Do you want to create a Scheduling Unit Blueprint?'},
+            dialog: { header: 'Confirm', detail: 'Do you want to create a Scheduling Unit Blueprint?' },
             dialogVisible: false,
             actions: [],
-            dataformat: ["MeasurementSet"],
-            taskStatus:[]
+            dataformat: ['MeasurementSet'],
+            taskStatus: []
         }
         this.actions = [];
         this.stations = [];
@@ -156,72 +167,106 @@ class ViewSchedulingUnit extends Component{
         this.dialogContent = "";
 
         this.confirmDeleteTasks = this.confirmDeleteTasks.bind(this);
+        this.confirmCancelTasks = this.confirmCancelTasks.bind(this);
         this.onRowSelection = this.onRowSelection.bind(this);
         this.deleteTasks = this.deleteTasks.bind(this);
         this.deleteSchedulingUnit = this.deleteSchedulingUnit.bind(this);
-        this.getTaskDialogContent = this.getTaskDialogContent.bind(this);
+        this.getTaskDeleteDialogContent = this.getTaskDeleteDialogContent.bind(this);
+        this.getTaskCancelConfirmContent = this.getTaskCancelConfirmContent.bind(this);
+        this.getTaskCancelStatusContent = this.getTaskCancelStatusContent.bind(this);
         this.getSUDialogContent = this.getSUDialogContent.bind(this);
         this.checkAndCreateBlueprint = this.checkAndCreateBlueprint.bind(this);
         this.createBlueprintTree = this.createBlueprintTree.bind(this);
         this.closeDialog = this.closeDialog.bind(this);
         this.showTaskRelationDialog = this.showTaskRelationDialog.bind(this);
         this.showDeleteSUConfirmation = this.showDeleteSUConfirmation.bind(this);
+        this.showCancelSUConfirmation = this.showCancelSUConfirmation.bind(this);
+        this.cancelSchedulingUnit = this.cancelSchedulingUnit.bind(this);
+        this.cancelTasks = this.cancelTasks.bind(this);
     }
 
     componentDidUpdate(prevProps, prevState) {
         if (this.state.scheduleunit && this.props.match.params &&
             (this.state.scheduleunitId !== this.props.match.params.id ||
-            this.state.scheduleunitType !== this.props.match.params.type)) {
+                this.state.scheduleunitType !== this.props.match.params.type)) {
             this.getSchedulingUnitDetails(this.props.match.params.type, this.props.match.params.id);
-       }
+        }
     }
-        
+
     showTaskRelationDialog() {
-        this.setState({ showTaskRelationDialog: !this.state.showTaskRelationDialog});
+        this.setState({ showTaskRelationDialog: !this.state.showTaskRelationDialog });
     }
 
-    async componentDidMount(){ 
+    async componentDidMount() {
+        this.setToggleBySorting();
         let schedule_id = this.props.match.params.id;
-        let schedule_type = this.props.match.params.type; 
+        let schedule_type = this.props.match.params.type;
         if (schedule_type && schedule_id) {
             this.stations = await ScheduleService.getStationGroup();
-            this.setState({stationOptions: this.stations});
+            this.setState({ stationOptions: this.stations });
             this.subtaskTemplates = await TaskService.getSubtaskTemplates();
             this.getSchedulingUnitDetails(schedule_type, schedule_id);
-		}
+        }
+<<<<<<< HEAD
+    }
+
+    toggleBySorting = (sortData) => {
+        UtilService.localStore({ type: 'set', key: this.lsKeySortColumn, value: sortData });
+    }
+
+=======
+    }
+
+    toggleBySorting = (sortData) => {
+        UtilService.localStore({ type: 'set', key: this.lsKeySortColumn, value: sortData });
     }
 
-    subtaskComponent = (task)=> {
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+    setToggleBySorting() {
+        let sortData = UtilService.localStore({ type: 'get', key: this.lsKeySortColumn });
+        if (sortData) {
+            if (Object.prototype.toString.call(sortData) === '[object Array]') {
+                this.defaultSortColumn = sortData;
+            }
+            else {
+                this.defaultSortColumn = [{ ...sortData }];
+            }
+        }
+        this.defaultSortColumn = this.defaultSortColumn || [];
+        UtilService.localStore({ type: 'set', key: this.lsKeySortColumn, value: [...this.defaultSortColumn] });
+    }
+
+    subtaskComponent = (task) => {
         return (
-            <button className="p-link" onClick={(e) => {this.setState({showStatusLogs: true, task: task})}}>
+            <button className="p-link" onClick={(e) => { this.setState({ showStatusLogs: true, task: task }) }}>
                 <i className="fa fa-history"></i>
             </button>
         );
     };
 
-    getSchedulingUnitDetails(schedule_type, schedule_id) { 
+    getSchedulingUnitDetails(schedule_type, schedule_id) {
         ScheduleService.getSchedulingUnitExtended(schedule_type, schedule_id)
-            .then(async(schedulingUnit) =>{
+            .then(async (schedulingUnit) => {
                 if (schedulingUnit) {
                     ScheduleService.getSchedulingConstraintTemplate(schedulingUnit.scheduling_constraints_template_id)
                         .then((template) => {
-                        this.setState({constraintTemplate: template})
-                    });
+                            this.setState({ constraintTemplate: template })
+                        });
                     if (schedulingUnit.draft_id) {
                         await ScheduleService.getSchedulingUnitDraftById(schedulingUnit.draft_id).then((response) => {
                             schedulingUnit['observation_strategy_template_id'] = response.observation_strategy_template_id;
                         });
                     }
-                    let tasks = schedulingUnit.task_drafts?(await this.getFormattedTaskDrafts(schedulingUnit)):this.getFormattedTaskBlueprints(schedulingUnit);
-                    let ingestGroup = tasks.map(task => ({name: task.name, canIngest: task.canIngest, type_value: task.type_value, id: task.id }));
+                    let tasks = schedulingUnit.task_drafts ? (await this.getFormattedTaskDrafts(schedulingUnit)) : this.getFormattedTaskBlueprints(schedulingUnit);
+                    let ingestGroup = tasks.map(task => ({ name: task.name, canIngest: task.canIngest, type_value: task.type_value, id: task.id }));
                     ingestGroup = _.groupBy(_.filter(ingestGroup, 'type_value'), 'type_value');
                     await Promise.all(tasks.map(async task => {
-                        task.status_logs = task.tasktype === "Blueprint"?this.subtaskComponent(task):"";
+                        task.status_logs = task.tasktype === "Blueprint" ? this.subtaskComponent(task) : "";
                         //Displaying SubTask ID of the 'control' Task
-                        const subTaskIds = task.subTasks?task.subTasks.filter(sTask => sTask.subTaskTemplate.name.indexOf('control') >= 0):[];
+                        const subTaskIds = task.subTasks ? task.subTasks.filter(sTask => sTask.subTaskTemplate.name.indexOf('control') >= 0) : [];
                         const promise = [];
                         subTaskIds.map(subTask => promise.push(ScheduleService.getSubtaskOutputDataproduct(subTask.id)));
-                        const dataProducts = promise.length > 0? await Promise.all(promise):[];
+                        const dataProducts = promise.length > 0 ? await Promise.all(promise) : [];
                         task.dataProducts = [];
                         task.size = 0;
                         task.dataSizeOnDisk = 0;
@@ -230,111 +275,130 @@ class ViewSchedulingUnit extends Component{
                         // task.start_time = moment(task.start_time).format(UIConstants.CALENDAR_DATETIME_FORMAT);
                         // task.created_at =  moment(task.created_at).format(UIConstants.CALENDAR_DATETIME_FORMAT);
                         // task.updated_at =  moment(task.updated_at).format(UIConstants.CALENDAR_DATETIME_FORMAT);
-                        task.canSelect = task.tasktype.toLowerCase() === 'blueprint' ? true:(task.tasktype.toLowerCase() === 'draft' && task.blueprint_draft.length === 0)?true:false;
+                        task.canSelect = task.tasktype.toLowerCase() === 'blueprint' ? true : (task.tasktype.toLowerCase() === 'draft' && task.blueprint_draft.length === 0) ? true : false;
                         if (dataProducts.length && dataProducts[0].length) {
                             task.dataProducts = dataProducts[0];
                             task.noOfOutputProducts = dataProducts[0].length;
                             task.size = _.sumBy(dataProducts[0], 'size');
-                            task.dataSizeOnDisk = _.sumBy(dataProducts[0], function(product) { return product.deletedSince?0:product.size});
+                            task.dataSizeOnDisk = _.sumBy(dataProducts[0], function (product) { return product.deletedSince ? 0 : product.size });
                             task.size = UnitConverter.getUIResourceUnit('bytes', (task.size));
                             task.dataSizeOnDisk = UnitConverter.getUIResourceUnit('bytes', (task.dataSizeOnDisk));
                         }
-                        task.subTaskID = subTaskIds.length ? subTaskIds[0].id : ''; 
+                        task.subTaskID = subTaskIds.length ? subTaskIds[0].id : '';
                         return task;
                     }));
-                   
-                    const targetObservation = _.find(tasks, (task)=> {return task.template.type_value==='observation' && task.tasktype.toLowerCase()===schedule_type && task.specifications_doc.station_groups});
+
+                    const targetObservation = _.find(tasks, (task) => { return task.template.type_value === 'observation' && task.tasktype.toLowerCase() === schedule_type && task.specifications_doc.station_groups });
                     this.setState({
                         scheduleunitId: schedule_id,
-                        scheduleunit : schedulingUnit,
+                        scheduleunit: schedulingUnit,
                         scheduleunitType: schedule_type,
-                        schedule_unit_task : tasks,
+<<<<<<< HEAD
+                        schedulingUnitTasks: tasks,
+=======
+                        schedule_unit_task: tasks,
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
                         isLoading: false,
-                        stationGroup: targetObservation?targetObservation.specifications_doc.station_groups:[],
+                        stationGroup: targetObservation ? targetObservation.specifications_doc.station_groups : [],
                         redirect: null,
                         dialogVisible: false,
-                        ingestGroup});
+                        ingestGroup
+                    });
                     this.selectedRows = [];
                     // Add Action menu
                     this.getActionMenu(schedule_type);
-                }   else {
+                } else {
                     this.setState({
                         isLoading: false,
                         redirect: "/not-found"
                     });
                 }
             });
-        }
+    }
 
     /**
      * Get action menus for page header
      */
     getActionMenu(schedule_type) {
-        this.actions =[];
+        this.actions = [];
         let canDelete = (this.state.scheduleunit &&
-                            (!this.state.scheduleunit.scheduling_unit_blueprints_ids || this.state.scheduleunit.scheduling_unit_blueprints_ids.length === 0));
-        this.actions.push({icon: 'fa fa-trash',title:!canDelete? 'Cannot delete Draft when Blueprint exists':'Scheduling Unit',  
-                        type: 'button',  disabled: !canDelete, actOn: 'click', props:{ callback: this.showDeleteSUConfirmation}});
-        
-        this.actions.push({icon: 'fa-window-close',title:'Click to Close Scheduling Unit View', link: this.props.history.goBack} );
+            (!this.state.scheduleunit.scheduling_unit_blueprints_ids || this.state.scheduleunit.scheduling_unit_blueprints_ids.length === 0));
+        this.actions.push({
+            icon: 'fa fa-trash', title: !canDelete ? 'Cannot delete Draft when Blueprint exists' : 'Scheduling Unit',
+            type: 'button', disabled: !canDelete, actOn: 'click', props: { callback: this.showDeleteSUConfirmation }
+        });
+
+        this.actions.push({ icon: 'fa-window-close', title: 'Click to Close Scheduling Unit View', link: this.props.history.goBack });
         if (this.props.match.params.type === 'draft') {
-            this.actions.unshift({icon:'fa-file-import', title: 'Data Products To Ingest', type:'button',
-            actOn:'click', props : { callback: this.showTaskRelationDialog}
+            this.actions.unshift({
+                icon: 'fa-file-import', title: 'Data Products To Ingest', type: 'button',
+                actOn: 'click', props: { callback: this.showTaskRelationDialog }
             });
-            this.actions.unshift({icon: 'fa-edit', title: 'Click to edit',  props : { pathname:`/schedulingunit/edit/${ this.props.match.params.id}`}
+            this.actions.unshift({
+                icon: 'fa-edit', title: 'Click to edit', props: { pathname: `/schedulingunit/edit/${this.props.match.params.id}` }
             });
-            this.actions.unshift({icon:'fa-stamp', title: 'Create Blueprint', type:'button',
-            actOn:'click', props : { callback: this.checkAndCreateBlueprint},
+            this.actions.unshift({
+                icon: 'fa-stamp', title: 'Create Blueprint', type: 'button',
+                actOn: 'click', props: { callback: this.checkAndCreateBlueprint },
             });
         } else {
-            this.actions.unshift({icon: 'fa-sitemap',title :'View Workflow',props :{pathname:`/schedulingunit/${this.props.match.params.id}/workflow`}});
-            this.actions.unshift({icon: 'fa-lock', title: 'Cannot edit blueprint'});
+<<<<<<< HEAD
+            this.actions.unshift({
+                icon: 'fa-ban', type: 'button', actOn: 'click', 
+                title: this.SU_END_STATUSES.indexOf(this.state.scheduleunit.status.toLowerCase())>=0?'Cannot Cancel Scheduling Unit':'Cancel Scheduling Unit', 
+                disabled:this.SU_END_STATUSES.indexOf(this.state.scheduleunit.status.toLowerCase())>=0, 
+                props: { callback: this.showCancelSUConfirmation }
+            });
+=======
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+            this.actions.unshift({ icon: 'fa-sitemap', title: 'View Workflow', props: { pathname: `/schedulingunit/${this.props.match.params.id}/workflow` } });
+            this.actions.unshift({ icon: 'fa-lock', title: 'Cannot edit blueprint' });
         }
-        this.setState({actions: this.actions});
+        this.setState({ actions: this.actions });
     }
-    
+
     /**
      * Formatting the task_drafts and task_blueprints in draft view to pass to the ViewTable component
      * @param {Object} schedulingUnit - scheduling_unit_draft object from extended API call loaded with tasks(draft & blueprint) along with their template and subtasks
      */
     async getFormattedTaskDrafts(schedulingUnit) {
-        let scheduletasklist=[];
+        let scheduletasklist = [];
         // Common keys for Task and Blueprint
-        let commonkeys = ['id','created_at','description','name','tags','updated_at','url','do_cancel','relative_start_time','relative_stop_time','start_time','stop_time','duration','status'];
-        for(const task of schedulingUnit.task_drafts){
+        let commonkeys = ['id', 'created_at', 'description', 'name', 'tags', 'updated_at', 'url', 'do_cancel', 'relative_start_time', 'relative_stop_time', 'start_time', 'stop_time', 'duration', 'status'];
+        for (const task of schedulingUnit.task_drafts) {
             let scheduletask = {};
             scheduletask['tasktype'] = 'Draft';
-            scheduletask['actionpath'] = '/task/view/draft/'+task['id'];
+            scheduletask['actionpath'] = '/task/view/draft/' + task['id'];
             scheduletask['blueprint_draft'] = _.map(task['task_blueprints'], 'url');
             scheduletask['status'] = task['status'];
             //fetch task draft details
-            for(const key of commonkeys){
+            for (const key of commonkeys) {
                 scheduletask[key] = task[key];
             }
             scheduletask['specifications_doc'] = task['specifications_doc'];
-            scheduletask.duration = moment.utc((scheduletask.duration || 0)*1000).format(UIConstants.CALENDAR_TIME_FORMAT); 
-            scheduletask.relative_start_time = moment.utc(scheduletask.relative_start_time*1000).format(UIConstants.CALENDAR_TIME_FORMAT); 
-            scheduletask.relative_stop_time = moment.utc(scheduletask.relative_stop_time*1000).format(UIConstants.CALENDAR_TIME_FORMAT); 
+            scheduletask.duration = moment.utc((scheduletask.duration || 0) * 1000).format(UIConstants.CALENDAR_TIME_FORMAT);
+            scheduletask.relative_start_time = moment.utc(scheduletask.relative_start_time * 1000).format(UIConstants.CALENDAR_TIME_FORMAT);
+            scheduletask.relative_stop_time = moment.utc(scheduletask.relative_stop_time * 1000).format(UIConstants.CALENDAR_TIME_FORMAT);
             scheduletask.template = task.specifications_template;
             scheduletask.type_value = task.specifications_template.type_value;
             scheduletask.produced_by = task.produced_by;
             scheduletask.produced_by_ids = task.produced_by_ids;
-            
-            for(const blueprint of task['task_blueprints']){
+
+            for (const blueprint of task['task_blueprints']) {
                 let taskblueprint = {};
                 taskblueprint['tasktype'] = 'Blueprint';
-                taskblueprint['actionpath'] = '/task/view/blueprint/'+blueprint['id'];
+                taskblueprint['actionpath'] = '/task/view/blueprint/' + blueprint['id'];
                 taskblueprint['blueprint_draft'] = blueprint['draft'];
                 taskblueprint['status'] = blueprint['status'];
-                
-                for(const key of commonkeys){
+
+                for (const key of commonkeys) {
                     taskblueprint[key] = blueprint[key];
                 }
                 taskblueprint['created_at'] = moment(blueprint['created_at'], moment.ISO_8601).format(UIConstants.CALENDAR_DATETIME_FORMAT);
                 taskblueprint['updated_at'] = moment(blueprint['updated_at'], moment.ISO_8601).format(UIConstants.CALENDAR_DATETIME_FORMAT);
-                taskblueprint.duration = moment.utc((taskblueprint.duration || 0)*1000).format(UIConstants.CALENDAR_TIME_FORMAT); 
-                taskblueprint.relative_start_time = moment.utc(taskblueprint.relative_start_time*1000).format(UIConstants.CALENDAR_TIME_FORMAT); 
-                taskblueprint.relative_stop_time = moment.utc(taskblueprint.relative_stop_time*1000).format(UIConstants.CALENDAR_TIME_FORMAT); 
+                taskblueprint.duration = moment.utc((taskblueprint.duration || 0) * 1000).format(UIConstants.CALENDAR_TIME_FORMAT);
+                taskblueprint.relative_start_time = moment.utc(taskblueprint.relative_start_time * 1000).format(UIConstants.CALENDAR_TIME_FORMAT);
+                taskblueprint.relative_stop_time = moment.utc(taskblueprint.relative_stop_time * 1000).format(UIConstants.CALENDAR_TIME_FORMAT);
                 taskblueprint.template = scheduletask.template;
                 taskblueprint.subTasks = blueprint.subtasks;
                 for (const subtask of taskblueprint.subTasks) {
@@ -346,14 +410,14 @@ class ViewSchedulingUnit extends Component{
             //Add Task Draft details to array
             scheduletasklist.push(scheduletask);
         }
-        //Ingest Task Relation         
+        //Ingest Task Relation
         const ingestTask = scheduletasklist.find(task => task.type_value === 'ingest' && task.tasktype.toLowerCase() === 'draft');
         if (ingestTask) {
             for (const producer_id of ingestTask.produced_by_ids) {
                 const taskRelation = await ScheduleService.getTaskRelation(producer_id);
                 let producerTask = scheduletasklist.find(task => task.id === taskRelation.producer_id && task.tasktype.toLowerCase() === 'draft');
                 if(producerTask!=undefined){
-                    producerTask.canIngest = true;  
+                    producerTask.canIngest = true;
                 }
             }
         }
@@ -366,13 +430,13 @@ class ViewSchedulingUnit extends Component{
      */
     getFormattedTaskBlueprints(schedulingUnit) {
         let taskBlueprintsList = [];
-        for(const taskBlueprint of schedulingUnit.task_blueprints) {
+        for (const taskBlueprint of schedulingUnit.task_blueprints) {
             taskBlueprint['tasktype'] = 'Blueprint';
-            taskBlueprint['actionpath'] = '/task/view/blueprint/'+taskBlueprint['id'];
+            taskBlueprint['actionpath'] = '/task/view/blueprint/' + taskBlueprint['id'];
             taskBlueprint['blueprint_draft'] = taskBlueprint['draft'];
             taskBlueprint['relative_start_time'] = 0;
             taskBlueprint['relative_stop_time'] = 0;
-            taskBlueprint.duration = moment.utc((taskBlueprint.duration || 0)*1000).format(UIConstants.CALENDAR_TIME_FORMAT);
+            taskBlueprint.duration = moment.utc((taskBlueprint.duration || 0) * 1000).format(UIConstants.CALENDAR_TIME_FORMAT);
             taskBlueprint.template = taskBlueprint.specifications_template;
             for (const subtask of taskBlueprint.subtasks) {
                 subtask.subTaskTemplate = _.find(this.subtaskTemplates, ['id', subtask.specifications_template_id]);
@@ -383,15 +447,15 @@ class ViewSchedulingUnit extends Component{
         return taskBlueprintsList;
     }
 
-    getScheduleUnitTasks(type, scheduleunit){
-        if(type === 'draft')
+    getScheduleUnitTasks(type, scheduleunit) {
+        if (type === 'draft')
             return ScheduleService.getTasksBySchedulingUnit(scheduleunit.id, true, true, true);
         else
             return ScheduleService.getTaskBPWithSubtaskTemplateOfSU(scheduleunit);
     }
-    
-    getScheduleUnit(type, id){
-        if(type === 'draft')
+
+    getScheduleUnit(type, id) {
+        if (type === 'draft')
             return ScheduleService.getSchedulingUnitDraftById(id)
         else
             return ScheduleService.getSchedulingUnitBlueprintById(id)
@@ -407,14 +471,14 @@ class ViewSchedulingUnit extends Component{
             dialog.onSubmit = this.createBlueprintTree;
             dialog.content = null;
             dialog.width = null;
-            if (this.state.scheduleunit.scheduling_unit_blueprints.length>0) {
+            if (this.state.scheduleunit.scheduling_unit_blueprints.length > 0) {
                 dialog.detail = "Blueprint(s) already exist for this Scheduling Unit. Do you want to create another one?";
-            }   else {
-                dialog.detail ="Do you want to create a Scheduling Unit Blueprint?";
+            } else {
+                dialog.detail = "Do you want to create a Scheduling Unit Blueprint?";
             }
-            dialog.actions = [{id: 'yes', title: 'Yes', callback: this.createBlueprintTree},
-                                {id: 'no', title: 'No', callback: this.closeDialog}];
-            this.setState({dialogVisible: true, dialog: dialog});
+            dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.createBlueprintTree },
+            { id: 'no', title: 'No', callback: this.closeDialog }];
+            this.setState({ dialogVisible: true, dialog: dialog });
         }
     }
 
@@ -422,15 +486,15 @@ class ViewSchedulingUnit extends Component{
      * Funtion called to create blueprint on confirmation.
      */
     createBlueprintTree() {
-        this.setState({dialogVisible: false, showSpinner: true});
+        this.setState({ dialogVisible: false, showSpinner: true });
         ScheduleService.createSchedulingUnitBlueprintTree(this.state.scheduleunit.id)
             .then(blueprint => {
                 if (blueprint) {
-                    appGrowl.show({severity: 'success', summary: 'Success', detail: 'Blueprint created successfully!'});
-                    this.setState({showSpinner: false, redirect: `/schedulingunit/view/blueprint/${blueprint.id}`, isLoading: true});
-                }   else {
-                    appGrowl.show({severity: 'error', summary: 'Failed', detail: 'Unable to create blueprint!'});
-                    this.setState({showSpinner: false});
+                    appGrowl.show({ severity: 'success', summary: 'Success', detail: 'Blueprint created successfully!' });
+                    this.setState({ showSpinner: false, redirect: `/schedulingunit/view/blueprint/${blueprint.id}`, isLoading: true });
+                } else {
+                    appGrowl.show({ severity: 'error', summary: 'Failed', detail: 'Unable to create blueprint!' });
+                    this.setState({ showSpinner: false });
                 }
             });
     }
@@ -439,66 +503,119 @@ class ViewSchedulingUnit extends Component{
      * Callback function to close the dialog prompted.
      */
     closeDialog() {
-        this.setState({dialogVisible: false});
+<<<<<<< HEAD
+        this.setState({ dialogVisible: false, cancelledTasks: [] });
+=======
+        this.setState({ dialogVisible: false });
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
     }
-   
+
     onRowSelection(selectedRows) {
         this.selectedRows = selectedRows;
     }
-    
+
     /**
      * Confirmation dialog for delete task(s)
      */
     confirmDeleteTasks() {
-        if(this.selectedRows.length === 0) {
-            appGrowl.show({severity: 'info', summary: 'Select Row', detail: 'Select Task to delete.'});
-        }   else {
+        if (this.selectedRows.length === 0) {
+            appGrowl.show({ severity: 'info', summary: 'Select Row', detail: 'Select Task to delete.' });
+        } else {
             let dialog = this.state.dialog;
             dialog.type = "confirmation";
-            dialog.header= "Confirm to Delete Task(s)";
+            dialog.header = "Confirm to Delete Task(s)";
             dialog.detail = "Do you want to delete the selected Task(s)?";
+<<<<<<< HEAD
+            dialog.content = this.getTaskDeleteDialogContent;
+=======
             dialog.content = this.getTaskDialogContent;
-            dialog.actions = [{id: 'yes', title: 'Yes', callback: this.deleteTasks},
-            {id: 'no', title: 'No', callback: this.closeDialog}];
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+            dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.deleteTasks },
+            { id: 'no', title: 'No', callback: this.closeDialog }];
             dialog.onSubmit = this.deleteTasks;
             dialog.width = '55vw';
             dialog.showIcon = false;
-            this.setState({dialog: dialog, dialogVisible: true});
+            this.setState({ dialog: dialog, dialogVisible: true });
         }
     }
-    
+
     showDeleteSUConfirmation() {
         let dialog = this.state.dialog;
         dialog.type = "confirmation";
-        dialog.header= "Confirm to Delete Scheduling Unit";
+        dialog.header = "Confirm to Delete Scheduling Unit";
         dialog.detail = "Do you want to delete this Scheduling Unit?";
         dialog.content = this.getSUDialogContent;
-        dialog.actions = [{id: 'yes', title: 'Yes', callback: this.deleteSchedulingUnit},
-        {id: 'no', title: 'No', callback: this.closeDialog}];
+        dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.deleteSchedulingUnit },
+        { id: 'no', title: 'No', callback: this.closeDialog }];
         dialog.onSubmit = this.deleteSchedulingUnit;
         dialog.width = '55vw';
         dialog.showIcon = false;
-        this.setState({dialog: dialog, dialogVisible: true});
+        this.setState({ dialog: dialog, dialogVisible: true });
+<<<<<<< HEAD
     }
 
+     /**
+     * Show confirmation dialog before cancelling the scheduling unit.
+     */
+      showCancelSUConfirmation() {
+        let dialog = this.state.dialog;
+        dialog.type = "confirmation";
+        dialog.header = "Confirm to Cancel Scheduling Unit";
+        dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.cancelSchedulingUnit },
+                            { id: 'no', title: 'No', callback: this.closeDialog }];
+        if (this.SU_NOT_STARTED_STATUSES.indexOf(this.state.scheduleunit.status) >= 0) {
+            dialog.detail = "Cancelling this scheduling unit means it will no longer be executed. This action cannot be undone. Do you want to proceed?";
+        }   else if (this.SU_ACTIVE_STATUSES.indexOf(this.state.scheduleunit.status) >= 0) {
+            dialog.detail = "Cancelling this scheduling unit means it will be aborted. This action cannot be undone. Do you want to proceed?";
+        }
+        dialog.submit = this.cancelSchedulingUnit;
+        dialog.width = '40vw';
+        dialog.showIcon = true;
+        this.setState({ dialog: dialog, dialogVisible: true });
+    }
     /**
-     * Prepare Task(s) details to show on confirmation dialog
+     * Show confirmation dialog before cancelling the scheduling unit.
      */
-    getTaskDialogContent() {
+    showCancelSUConfirmation() {
+        let dialog = this.state.dialog;
+        dialog.type = "confirmation";
+        dialog.header = "Confirm to Cancel Scheduling Unit";
+        dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.cancelSchedulingUnit },
+                            { id: 'no', title: 'No', callback: this.closeDialog }];
+        if (this.SU_NOT_STARTED_STATUSES.indexOf(this.state.scheduleunit.status) >= 0) {
+            dialog.detail = "Cancelling this scheduling unit means it will no longer be executed. This action cannot be undone. Do you want to proceed?";
+        }   else if (this.SU_ACTIVE_STATUSES.indexOf(this.state.scheduleunit.status) >= 0) {
+            dialog.detail = "Cancelling this scheduling unit means it will be aborted. This action cannot be undone. Do you want to proceed?";
+        }
+        dialog.submit = this.cancelSchedulingUnit;
+        dialog.width = '40vw';
+        dialog.showIcon = true;
+        this.setState({ dialog: dialog, dialogVisible: true });
+=======
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+    }
+
+
+    /**
+     * Prepare Task(s) details to show on confirmation dialog before deleting
+     */
+    getTaskDeleteDialogContent() {
         let selectedTasks = [];
-        for(const obj of this.selectedRows) {
-            selectedTasks.push({id:obj.id, suId: this.state.scheduleunit.id, suName: this.state.scheduleunit.name, 
-                taskId: obj.id, controlId: obj.subTaskID, taskName: obj.name, status: obj.status});
-        }   
-        return  <>  
-                <DataTable value={selectedTasks} resizableColumns columnResizeMode="expand" className="card" style={{paddingLeft: '0em'}}>
-                    <Column field="suId" header="Scheduling Unit Id"></Column>
-                    <Column field="suName" header="Scheduling Unit Name"></Column>
-                    <Column field="taskId" header="Task Id"></Column>
-                    <Column field="controlId" header="Control Id"></Column>
-                    <Column field="taskName" header="Task Name"></Column>
-                    <Column field="status" header="Status"></Column>
-                </DataTable>
+        for (const obj of this.selectedRows) {
+            selectedTasks.push({
+                id: obj.id, suId: this.state.scheduleunit.id, suName: this.state.scheduleunit.name,
+                taskId: obj.id, controlId: obj.subTaskID, taskName: obj.name, status: obj.status
+            });
+        }
+        return <>
+            <DataTable value={selectedTasks} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}>
+                <Column field="suId" header="Scheduling Unit Id"></Column>
+                <Column field="suName" header="Scheduling Unit Name"></Column>
+                <Column field="taskId" header="Task Id"></Column>
+                <Column field="controlId" header="Control Id"></Column>
+                <Column field="taskName" header="Task Name"></Column>
+                <Column field="status" header="Status"></Column>
+            </DataTable>
         </>
     }
 
@@ -506,9 +623,9 @@ class ViewSchedulingUnit extends Component{
      * Prepare Scheduling Unit details to show on confirmation dialog
      */
     getSUDialogContent() {
-        let selectedTasks = [{suId: this.state.scheduleunit.id, suName: this.state.scheduleunit.name, suType: (this.state.scheduleunit.draft)?'Blueprint': 'Draft'}];
-        return  <>  
-             <DataTable value={selectedTasks} resizableColumns columnResizeMode="expand" className="card" style={{paddingLeft: '0em'}}>
+        let selectedTasks = [{ suId: this.state.scheduleunit.id, suName: this.state.scheduleunit.name, suType: (this.state.scheduleunit.draft) ? 'Blueprint' : 'Draft' }];
+        return <>
+            <DataTable value={selectedTasks} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}>
                 <Column field="suId" header="Scheduling Unit Id"></Column>
                 <Column field="suName" header="Scheduling Unit Name"></Column>
                 <Column field="suType" header="Type"></Column>
@@ -521,58 +638,92 @@ class ViewSchedulingUnit extends Component{
      */
     async deleteTasks() {
         let hasError = false;
-        for(const task of this.selectedRows) {
-            if(!await TaskService.deleteTask(task.tasktype, task.id)) {
+        for (const task of this.selectedRows) {
+            if (!await TaskService.deleteTask(task.tasktype, task.id)) {
                 hasError = true;
             }
         }
-        if(hasError){
-            appGrowl.show({severity: 'error', summary: 'error', detail: 'Error while deleting Task(s)'});
-            this.setState({dialogVisible: false});
-        }   else {
+        if (hasError) {
+            appGrowl.show({ severity: 'error', summary: 'error', detail: 'Error while deleting Task(s)' });
+            this.setState({ dialogVisible: false });
+        } else {
             this.selectedRows = [];
-            this.setState({dialogVisible: false});
+            this.setState({ dialogVisible: false });
             this.componentDidMount();
-            appGrowl.show({severity: 'success', summary: 'Success', detail: 'Task(s) deleted successfully'});
+            appGrowl.show({ severity: 'success', summary: 'Success', detail: 'Task(s) deleted successfully' });
         }
     }
 
-     /**
-     * Delete Scheduling Unit
-     */
+    /**
+    * Delete Scheduling Unit
+    */
     async deleteSchedulingUnit() {
         let hasError = false;
-        if(!await ScheduleService.deleteSchedulingUnit(this.state.scheduleunitType, this.state.scheduleunit.id)) {
+        if (!await ScheduleService.deleteSchedulingUnit(this.state.scheduleunitType, this.state.scheduleunit.id)) {
             hasError = true;
         }
-        if(hasError){
-            appGrowl.show({severity: 'error', summary: 'error', detail: 'Error while deleting scheduling Unit'});
-            this.setState({dialogVisible: false});
-        }   else {
+        if (hasError) {
+            appGrowl.show({ severity: 'error', summary: 'error', detail: 'Error while deleting scheduling Unit' });
+            this.setState({ dialogVisible: false });
+        } else {
             this.selectedRows = [];
-            appGrowl.show({severity: 'success', summary: 'Success', detail: 'Scheduling Unit is deleted successfully'});
-            this.setState({dialogVisible: false, redirect: '/schedulingunit'});
+            appGrowl.show({ severity: 'success', summary: 'Success', detail: 'Scheduling Unit is deleted successfully' });
+            this.setState({ dialogVisible: false, redirect: '/schedulingunit' });
         }
     }
-    async submitTRDToIngest(data) {       
+    async submitTRDToIngest(data) {
         if(data) {
             let consumer = data.ingest;
-            let taskRelationsToAdd = UtilService.filterByObject(data.taskRelationDraft,'add');
-            let taskRelationsToDelete = UtilService.filterByObject(data.taskRelationDraft,'delete');
-            const propPromises = [],
-                propConnectorPromises = [],
+            let taskRelationsToAdd = UtilService.filterByObject(data.taskRelationDraft, 'add');
+            let taskRelationsToDelete = UtilService.filterByObject(data.taskRelationDraft, 'delete');
+            const propPromises = [],               
                 taskRelAddDraftObj=[],
                 taskRelObj=ScheduleService.taskRelationDraftAPIReqObj();
             let createdTaskRes=[],deletedTaskRes=[],updateTaskRelObj=[];
             if(taskRelationsToAdd.length) {
-                const consumerData = await ScheduleService.getTaskDraft(consumer.id);              
-                const consConnData = await ScheduleService.getTaskConnectorType(consumerData.specifications_template_id,'input');               
+<<<<<<< HEAD
+                const consumerData = await ScheduleService.getTaskDraft(consumer.id);               
+                let tempRes = consumerData;
+                let consConnData = [];
+                if (tempRes) {
+                    let tempResults = tempRes.connector_types;
+                    tempResults = tempResults.filter(function(connector){
+                        return  connector.task_template == consumerData.specifications_template_id
+                    });
+                    consConnData = tempResults.filter((connector) =>
+                        connector.role == "any" &&
+                        connector.datatype == "visibilities" &&
+                        connector.iotype == 'input')
+                }
+=======
+                const consumerData = await ScheduleService.getTaskDraft(consumer.id);
+                const consConnData = await ScheduleService.getTaskConnectorType(consumerData.specifications_template_id,'input');
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
                 taskRelationsToAdd.map((task) => {
                     propPromises.push(ScheduleService.getTaskDraft(task.id))
                 });
-                const producerData = await Promise.all(propPromises);        
+                const producerData = await Promise.all(propPromises);
+<<<<<<< HEAD
+                if(producerData){                 
+                    let outputResult =[];                    
+                    producerData.forEach((pd, i)=> {                        
+                        outputResult = pd.connector_types.filter(function(connector){
+                            return  connector.task_template == pd.specifications_template_id
+                        });
+                        outputResult = pd.connector_types.filter((connector) =>
+                            // connector.role_value == "any" &&
+                            connector.datatype == "visibilities" &&
+                            connector.iotype == 'output')                      
+                    });               
+                    outputResult.forEach((pd, i) => {
+                        let tempTaskRelObj = _.cloneDeep(taskRelObj);
+                        tempTaskRelObj.consumer = `${taskRelObj.consumer}/${consumer.id}`;
+                        tempTaskRelObj.producer = `${taskRelObj.producer}/${producerData[i].id}`;
+                        tempTaskRelObj.input_role = `${taskRelObj.input_role}/${consConnData[i].id}`;
+                        tempTaskRelObj.output_role = `${taskRelObj.output_role}/${pd.id}`;                      
+=======
                 if(producerData){
-                    producerData.forEach((pd)=> {                       
+                    producerData.forEach((pd)=> {
                         propConnectorPromises.push(ScheduleService.getTaskConnectorType(pd.specifications_template_id,'output'))
                     });
                     const prodConnData = await Promise.all(propConnectorPromises);
@@ -582,27 +733,31 @@ class ViewSchedulingUnit extends Component{
                         tempTaskRelObj.producer=`${taskRelObj.producer}/${producerData[i].id}`;
                         tempTaskRelObj.input_role=`${taskRelObj.input_role}/${consConnData.task_template_id}`;
                         tempTaskRelObj.output_role=`${taskRelObj.output_role}/${pc.task_template_id}`;
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
                         taskRelAddDraftObj.push(tempTaskRelObj);
-                    });                                       
-                    if(taskRelAddDraftObj) {                     
+                    });
+                    if(taskRelAddDraftObj) {
                         createdTaskRes = await ScheduleService.createTaskRelationDraft(taskRelAddDraftObj,taskRelationsToAdd);//'dataFormat':this.state.dataformat
-                        updateTaskRelObj=[...updateTaskRelObj,...createdTaskRes];                       
+                        updateTaskRelObj=[...updateTaskRelObj,...createdTaskRes];
                     }
-                    
+<<<<<<< HEAD
+=======
+
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
                 }
             }
             if(taskRelationsToDelete.length) {
-                let taskRelDraftData = await ScheduleService.getAllTaskRelationDraft(); 
+                let taskRelDraftData = await ScheduleService.getAllTaskRelationDraft();
                 let getDelTRelDrafts = taskRelDraftData.filter(obj1 => {
-                    return obj1.consumer_id==consumer.id 
-                    && (taskRelationsToDelete.some(obj2 => obj1.producer_id == obj2.id)) 
-                }  ); 
-                deletedTaskRes = await ScheduleService.deleteTaskRelationDraft(getDelTRelDrafts,taskRelationsToDelete);               
-                updateTaskRelObj=[...updateTaskRelObj,...deletedTaskRes];                
+                    return obj1.consumer_id==consumer.id
+                    && (taskRelationsToDelete.some(obj2 => obj1.producer_id == obj2.id))
+                }  );
+                deletedTaskRes = await ScheduleService.deleteTaskRelationDraft(getDelTRelDrafts,taskRelationsToDelete);
+                updateTaskRelObj=[...updateTaskRelObj,...deletedTaskRes];
             }
             if(updateTaskRelObj.length) {
             // To refresh the Data Product to Ingest the pop up
-               this.getSchedulingUnitDetails(this.props.match.params.type, this.props.match.params.id);                    
+               this.getSchedulingUnitDetails(this.props.match.params.type, this.props.match.params.id);
                     this.setState({
                         confirmDialogVisible: false,
                         showSpinner: true
@@ -611,9 +766,9 @@ class ViewSchedulingUnit extends Component{
                     updateTaskRelObj.forEach((task)=>{
                         let tempTask = {};
                         tempTask['name'] = task.name;
-                        tempTask['action'] = task.action;                       
+                        tempTask['action'] = task.action;
                         taskStatus.push(tempTask);
-                    });                    
+                    });
                     this.setState({taskStatus:taskStatus});
                     this.showIcon = true;
                     this.dialogType = "success";
@@ -625,114 +780,255 @@ class ViewSchedulingUnit extends Component{
                     this.callBackFunction = this.cancelDialog;
                     this.dialogContent = this.getTasksDialogContent;
                     this.setState({isDirty : false, showSpinner: false, confirmDialogVisible: true});
-                
+<<<<<<< HEAD
+=======
+
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
             }
             else  {
                 this.setState({isDirty: false, showSpinner: false});
                 appGrowl.show({severity: 'error', summary: 'Warning', detail: 'No Tasks create/update '});
             }
-            
+
         }
-        
-    } 
+
+    }
     cancelDialog = () => {
         this.setState({
             confirmDialogVisible: false,
-            cancelTaskReplationDialog:false,
+            cancelTaskReplationDialog: false,
             isDirty: false
         });
     }
-    getTasksDialogContent = () => { 
+    getTasksDialogContent = () => {
         let taskStatus = this.state.taskStatus;
-       return  <> 
+       return  <>
                     {taskStatus.length > 0 &&
                        <div style={{marginTop: '1em'}}>
                            <b>Data Products to Ingest </b>
                            <DataTable value={taskStatus} resizableColumns columnResizeMode="expand" className="card" style={{paddingLeft: '0em'}}>
                                <Column field="name" header="Task Name"></Column>
-                               <Column field="action" header="Action"></Column>                              
+                               <Column field="action" header="Action"></Column>
                            </DataTable>
                        </div>
-                   } 
+                   }
                </>
-   }   
+   }
+<<<<<<< HEAD
+   
+
+    /**
+     * Function to cancel the scheduling unit and update its status and status of tasks if succeeeded.
+     */
+    async cancelSchedulingUnit() {
+        let schedulingUnit = this.state.scheduleunit;
+        let cancelledSU = await ScheduleService.cancelSchedulingUnit(schedulingUnit.id);
+        if (!cancelledSU) {
+            appGrowl.show({ severity: 'error', summary: 'error', detail: 'Error while cancelling Scheduling Unit' });
+            this.setState({ dialogVisible: false });
+        } else {
+            schedulingUnit.status = cancelledSU.status;
+            let actions = this.state.actions;
+            let cancelAction = _.find(actions, ['icon', 'fa-ban']);
+            cancelAction.disabled = true;
+            const cancelActionIndex = _.findIndex(actions, {'icon': 'fa-ban'});
+            actions.splice(cancelActionIndex, 1, cancelAction);
+            const cancelledSUTasks = cancelledSU.task_blueprints;
+            let suTasks = this.state.schedulingUnitTasks;
+            for (let suTask of suTasks) {
+                const cancelledSUTask = _.find(cancelledSUTasks, {'id': suTask.id});
+                suTask.status = cancelledSUTask.status;
+            }
+            appGrowl.show({ severity: 'success', summary: 'Success', detail: 'Scheduling Unit is cancelled successfully' });
+            this.setState({ dialogVisible: false, scheduleunit: schedulingUnit,
+                            schedulingUnitTasks:suTasks, actions: actions});
+        }
+    }
+
+    /**
+     * Prepare Task(s) details to show on confirmation dialog before cancelling
+     */
+    getTaskCancelConfirmContent() {
+        let selectedTasks = [];
+        for (const obj of this.selectedRows) {
+            if (this.TASK_END_STATUSES.indexOf(obj.status) < 0) {
+                selectedTasks.push({
+                    id: obj.id, suId: this.state.scheduleunit.id, suName: this.state.scheduleunit.name,
+                    taskId: obj.id, controlId: obj.subTaskID, taskName: obj.name, status: obj.status
+                });
+            }
+        }
+        return <>
+            <DataTable value={selectedTasks} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}>
+                <Column field="suId" header="Scheduling Unit Id"></Column>
+                <Column field="suName" header="Scheduling Unit Name"></Column>
+                <Column field="taskId" header="Task Id"></Column>
+                <Column field="controlId" header="Control Id"></Column>
+                <Column field="taskName" header="Task Name"></Column>
+                <Column field="status" header="Status"></Column>
+            </DataTable>
+        </>
+    }
+
+    /**
+     * Prepare Task(s) details to show status of Task cancellationn
+     */
+    getTaskCancelStatusContent() {
+        let cancelledTasks = this.state.cancelledTasks;
+        return <>
+            <DataTable value={cancelledTasks} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}>
+                <Column field="suId" header="Scheduling Unit Id"></Column>
+                <Column field="suName" header="Scheduling Unit Name"></Column>
+                <Column field="taskId" header="Task Id"></Column>
+                <Column field="controlId" header="Control Id"></Column>
+                <Column field="taskName" header="Task Name"></Column>
+                <Column field="status" header="Status"></Column>
+            </DataTable>
+        </>
+    }
+
+    /**
+     * Function to get confirmation before cancelling all selected task blueprints if the task status is 
+     * not one of the end statuses. If no selected task is cancellable, show info to select a cancellable task.
+     * 
+     */
+    confirmCancelTasks() {
+        let selectedBlueprints = this.selectedRows.filter(task => {
+                                        return task.tasktype === 'Blueprint' && 
+                                        this.TASK_END_STATUSES.indexOf(task.status)<0});
+        if (selectedBlueprints.length === 0) {
+            appGrowl.show({ severity: 'info', summary: 'Select Row', 
+                            detail: 'Select atleast one cancellable Task Blueprint to cancel.' });
+        } else {
+            let dialog = this.state.dialog;
+            dialog.type = "confirmation";
+            dialog.header = "Confirm to Cancel Task(s)";
+            dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.cancelTasks },
+                                { id: 'no', title: 'No', callback: this.closeDialog }];
+            dialog.detail = "Cancelling the task means it will no longer be executed / will be aborted. This action cannot be undone. Do you want to proceed?";
+            dialog.content = this.getTaskCancelConfirmContent;
+            dialog.submit = this.cancelTasks;
+            dialog.width = '55vw';
+            dialog.showIcon = false;
+            this.setState({ dialog: dialog, dialogVisible: true });
+        }
+    }
+    
+    /**
+     * Function to cancel all selected task blueprints if the task status is not one of the end statuses
+     * and update their status on successful cancellation.
+     */
+    async cancelTasks() {
+        let schedulingUnitTasks = this.state.schedulingUnitTasks;
+        let selectedBlueprints = this.selectedRows.filter(task => {return task.tasktype === 'Blueprint'});
+        let cancelledTasks = []
+        for (const selectedTask of selectedBlueprints) {
+            if (this.TASK_END_STATUSES.indexOf(selectedTask.status) < 0) {
+                const cancelledTask = await TaskService.cancelTask(selectedTask.id);
+                let task = _.find(schedulingUnitTasks, {'id': selectedTask.id, tasktype: 'Blueprint'});
+                if (cancelledTask) {
+                    task.status = cancelledTask.status;
+                }
+                cancelledTasks.push({
+                    id: task.id, suId: this.state.scheduleunit.id, suName: this.state.scheduleunit.name,
+                    taskId: task.id, controlId: task.subTaskID, taskName: task.name, 
+                    status: task.status.toLowerCase()==='cancelled'?'Cancelled': 'Error Occured'
+                });
+            }
+        }
+        let dialog = this.state.dialog;
+        dialog.type = "confirmation";
+        dialog.header = "Cancel Task(s) Status";
+        dialog.actions = [{ id: 'no', title: 'Ok', callback: this.closeDialog }];
+        dialog.detail = ""
+        dialog.content = this.getTaskCancelStatusContent;
+        dialog.submit = this.closeDialog;
+        dialog.width = '55vw';
+        dialog.showIcon = false;
+        this.selectedRows = [];
+        this.setState({ schedulingUnitTasks: schedulingUnitTasks, cancelledTasks: cancelledTasks, dialog: dialog, dialogVisible: true });
+    }
+
+    render() {
+=======
     render(){
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
         if (this.state.redirect) {
-            return <Redirect to={ {pathname: this.state.redirect} }></Redirect>
+            return <Redirect to={{ pathname: this.state.redirect }}></Redirect>
         }
-        return(
-		   <>   
-                <PageHeader location={this.props.location} title={'Scheduling Unit - Details'} 
-                            actions={this.state.actions}/>
-				{ this.state.isLoading ? <AppLoader/> :this.state.scheduleunit &&
-			    <>
-		            <div className="main-content">
-                        <div className="p-grid">
-                            <label  className="col-lg-2 col-md-2 col-sm-12">Name</label>
-                            <span className="p-col-lg-4 col-md-4 col-sm-12">{this.state.scheduleunit.name}</span>
-                            <label  className="col-lg-2 col-md-2 col-sm-12">Description</label>
-                            <span className="col-lg-4 col-md-4 col-sm-12">{this.state.scheduleunit.description}</span>
-                        </div>
-                        <div className="p-grid">
-                            <label className="col-lg-2 col-md-2 col-sm-12">Created At</label>
-                            <span className="col-lg-4 col-md-4 col-sm-12">{this.state.scheduleunit.created_at && moment(this.state.scheduleunit.created_at,moment.ISO_8601).format(UIConstants.CALENDAR_DATETIME_FORMAT)}</span>
-                            <label className="col-lg-2 col-md-2 col-sm-12">Updated At</label>
-                            <span className="col-lg-4 col-md-4 col-sm-12">{this.state.scheduleunit.created_at && moment(this.state.scheduleunit.updated_at,moment.ISO_8601).format(UIConstants.CALENDAR_DATETIME_FORMAT)}</span>
-                        </div>
-                        <div className="p-grid">
-                            <label className="col-lg-2 col-md-2 col-sm-12">Start Time</label>
-                            <span className="col-lg-4 col-md-4 col-sm-12">{this.state.scheduleunit.start_time && moment(this.state.scheduleunit.start_time).format(UIConstants.CALENDAR_DATETIME_FORMAT)}</span>
-                            <label className="col-lg-2 col-md-2 col-sm-12">End Time</label>
-                            <span className="col-lg-4 col-md-4 col-sm-12">{this.state.scheduleunit.stop_time && moment(this.state.scheduleunit.stop_time).format(UIConstants.CALENDAR_DATETIME_FORMAT)}</span>
-                        </div>
-                        <div className="p-grid">
-                            <label className="col-lg-2 col-md-2 col-sm-12" >Duration (HH:mm:ss)</label>
-                            <span className="col-lg-4 col-md-4 col-sm-12">{moment.utc((this.state.scheduleunit.duration?this.state.scheduleunit.duration:0)*1000).format(UIConstants.CALENDAR_TIME_FORMAT)}</span>
-                            <label className="col-lg-2 col-md-2 col-sm-12">Template ID</label>
-                            <span className="col-lg-4 col-md-4 col-sm-12">{this.state.scheduleunit.observation_strategy_template_id}</span>
-                        </div>
-                        <div className="p-grid">
-                            {this.state.scheduleunit.scheduling_set_object.project_id && 
-                                <>
-                                    <label className="col-lg-2 col-md-2 col-sm-12">Project</label>
-                                    <span className="col-lg-4 col-md-4 col-sm-12">
-                                        <Link to={`/project/view/${this.state.scheduleunit.scheduling_set_object.project_id}`}>{this.state.scheduleunit.scheduling_set_object.project_id}</Link>
-                                    </span>
-                                </>
+        return (
+            <>
+                <PageHeader location={this.props.location} title={'Scheduling Unit - Details'}
+                    actions={this.state.actions} />
+                { this.state.isLoading ? <AppLoader /> : this.state.scheduleunit &&
+                    <>
+                        <div className="main-content">
+                            <div className="p-grid">
+                                <label className="col-lg-2 col-md-2 col-sm-12">Name</label>
+                                <span className="p-col-lg-4 col-md-4 col-sm-12">{this.state.scheduleunit.name}</span>
+                                <label className="col-lg-2 col-md-2 col-sm-12">Description</label>
+                                <span className="col-lg-4 col-md-4 col-sm-12">{this.state.scheduleunit.description}</span>
+                            </div>
+                            <div className="p-grid">
+                                <label className="col-lg-2 col-md-2 col-sm-12">Created At</label>
+                                <span className="col-lg-4 col-md-4 col-sm-12">{this.state.scheduleunit.created_at && moment(this.state.scheduleunit.created_at, moment.ISO_8601).format(UIConstants.CALENDAR_DATETIME_FORMAT)}</span>
+                                <label className="col-lg-2 col-md-2 col-sm-12">Updated At</label>
+                                <span className="col-lg-4 col-md-4 col-sm-12">{this.state.scheduleunit.created_at && moment(this.state.scheduleunit.updated_at, moment.ISO_8601).format(UIConstants.CALENDAR_DATETIME_FORMAT)}</span>
+                            </div>
+                            <div className="p-grid">
+                                <label className="col-lg-2 col-md-2 col-sm-12">Start Time</label>
+                                <span className="col-lg-4 col-md-4 col-sm-12">{this.state.scheduleunit.start_time && moment(this.state.scheduleunit.start_time).format(UIConstants.CALENDAR_DATETIME_FORMAT)}</span>
+                                <label className="col-lg-2 col-md-2 col-sm-12">End Time</label>
+                                <span className="col-lg-4 col-md-4 col-sm-12">{this.state.scheduleunit.stop_time && moment(this.state.scheduleunit.stop_time).format(UIConstants.CALENDAR_DATETIME_FORMAT)}</span>
+                            </div>
+                            <div className="p-grid">
+                                <label className="col-lg-2 col-md-2 col-sm-12" >Duration (HH:mm:ss)</label>
+                                <span className="col-lg-4 col-md-4 col-sm-12">{moment.utc((this.state.scheduleunit.duration ? this.state.scheduleunit.duration : 0) * 1000).format(UIConstants.CALENDAR_TIME_FORMAT)}</span>
+                                <label className="col-lg-2 col-md-2 col-sm-12">Template ID</label>
+                                <span className="col-lg-4 col-md-4 col-sm-12">{this.state.scheduleunit.observation_strategy_template_id}</span>
+                            </div>
+                            <div className="p-grid">
+                                {this.state.scheduleunit.scheduling_set_object.project_id &&
+                                    <>
+                                        <label className="col-lg-2 col-md-2 col-sm-12">Project</label>
+                                        <span className="col-lg-4 col-md-4 col-sm-12">
+                                            <Link to={`/project/view/${this.state.scheduleunit.scheduling_set_object.project_id}`}>{this.state.scheduleunit.scheduling_set_object.project_id}</Link>
+                                        </span>
+                                    </>
                                 }
-                            <label  className="col-lg-2 col-md-2 col-sm-12">Scheduling set</label>
-                            <span className="col-lg-4 col-md-4 col-sm-12">{this.state.scheduleunit.scheduling_set_object.name}</span>
-                        </div>
-                        <div className="p-grid">
-                            <label  className="col-lg-2 col-md-2 col-sm-12">{this.props.match.params.type === 'blueprint' ? 'Draft' : 'Blueprints'}</label>
-                            <span className="col-lg-4 col-md-4 col-sm-12">
-                                <ul className="task-list">
-                                {(this.state.scheduleunit.blueprintList || []).map(blueprint => (
-                                    <li>
-                                        <Link to={{ pathname: `/schedulingunit/view/blueprint/${blueprint.id}`}}>{blueprint.name}</Link>
-                                    </li>))}
-                                {this.state.scheduleunit.draft_object && 
-                                    <li>
-                                        <Link to={{ pathname: `/schedulingunit/view/draft/${this.state.scheduleunit.draft_object.id}` }}>
-                                        {this.state.scheduleunit.draft_object.name}
-                                        </Link>
-                                    </li>}
-                                </ul>
-                            </span>
-                            {this.props.match.params.type === 'blueprint' &&
-                            <label className="col-lg-2 col-md-2 col-sm-12 ">Status</label> }
-                            {this.props.match.params.type === 'blueprint' &&
-                            <span className="col-lg-2 col-md-2 col-sm-12">{this.state.scheduleunit.status}</span>}
-                       </div> 
-                        <div className="p-grid">
-                            <label  className="col-lg-2 col-md-2 col-sm-12">Tags</label>
-                            <Chips className="p-col-4 chips-readonly" disabled value={this.state.scheduleunit.tags}></Chips>
+                                <label className="col-lg-2 col-md-2 col-sm-12">Scheduling set</label>
+                                <span className="col-lg-4 col-md-4 col-sm-12">{this.state.scheduleunit.scheduling_set_object.name}</span>
+                            </div>
+                            <div className="p-grid">
+                                <label className="col-lg-2 col-md-2 col-sm-12">{this.props.match.params.type === 'blueprint' ? 'Draft' : 'Blueprints'}</label>
+                                <span className="col-lg-4 col-md-4 col-sm-12">
+                                    <ul className="task-list">
+                                        {(this.state.scheduleunit.blueprintList || []).map(blueprint => (
+                                            <li>
+                                                <Link to={{ pathname: `/schedulingunit/view/blueprint/${blueprint.id}` }}>{blueprint.name}</Link>
+                                            </li>))}
+                                        {this.state.scheduleunit.draft_object &&
+                                            <li>
+                                                <Link to={{ pathname: `/schedulingunit/view/draft/${this.state.scheduleunit.draft_object.id}` }}>
+                                                    {this.state.scheduleunit.draft_object.name}
+                                                </Link>
+                                            </li>}
+                                    </ul>
+                                </span>
+                                {this.props.match.params.type === 'blueprint' &&
+                                    <label className="col-lg-2 col-md-2 col-sm-12 ">Status</label>}
+                                {this.props.match.params.type === 'blueprint' &&
+                                    <span className="col-lg-2 col-md-2 col-sm-12">{this.state.scheduleunit.status}</span>}
+                            </div>
+                            <div className="p-grid">
+                                <label className="col-lg-2 col-md-2 col-sm-12">Tags</label>
+                                <Chips className="p-col-4 chips-readonly" disabled value={this.state.scheduleunit.tags}></Chips>
+                            </div>
+
                         </div>
-                        
-                    </div>
-                </>
-			    }
-               
+                    </>
+                }
+
                 <div>
                     <h3>Tasks Details</h3>
                 </div>
@@ -746,97 +1042,118 @@ class ViewSchedulingUnit extends Component{
                     paths - specify the path for navigation - Table will set "id" value for each row in action button
                     
                 */}
-    
+
                 <div className="delete-option">
                     <div >
                         <span className="p-float-label">
+<<<<<<< HEAD
+                            {this.state.schedulingUnitTasks && this.state.schedulingUnitTasks.length > 0 &&
+                                <>
+                                    <a href="#" onClick={this.confirmCancelTasks} title="Cancel selected Task(s)">
+                                        <i class="fa fa-ban" aria-hidden="true" ></i>
+                                    </a>
+                                    <a href="#" onClick={this.confirmDeleteTasks} title="Delete selected Task(s)">
+                                        <i class="fa fa-trash" aria-hidden="true" ></i>
+                                    </a>
+                                </>
+=======
                             {this.state.schedule_unit_task && this.state.schedule_unit_task.length > 0 &&
-                                <a href="#" onClick={this.confirmDeleteTasks}  title="Delete selected Task(s)">
+                                <a href="#" onClick={this.confirmDeleteTasks} title="Delete selected Task(s)">
                                     <i class="fa fa-trash" aria-hidden="true" ></i>
                                 </a>
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
                             }
                         </span>
-                    </div>                           
+                    </div>
                 </div>
-                {this.state.isLoading ? <AppLoader/> : (this.state.schedule_unit_task.length>0 )?
-                    <ViewTable 
-                        data={this.state.schedule_unit_task} 
+<<<<<<< HEAD
+                {this.state.isLoading ? <AppLoader /> : (this.state.schedulingUnitTasks.length > 0) ?
+                    <ViewTable
+                        data={this.state.schedulingUnitTasks}
+=======
+                {this.state.isLoading ? <AppLoader /> : (this.state.schedule_unit_task.length > 0) ?
+                    <ViewTable
+                        data={this.state.schedule_unit_task}
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
                         defaultcolumns={this.state.defaultcolumns}
                         optionalcolumns={this.state.optionalcolumns}
                         columnclassname={this.state.columnclassname}
                         columnOrders={this.state.columnOrders}
-                        defaultSortColumn={this.state.defaultSortColumn}
                         showaction="true"
                         keyaccessor="id"
                         paths={this.state.paths}
                         unittest={this.state.unittest}
                         tablename="scheduleunit_task_list"
                         allowRowSelection={true}
-                        onRowSelection = {this.onRowSelection}
+                        onRowSelection={this.onRowSelection}
+                        ignoreSorting={this.ignoreSorting}
+                        lsKeySortColumn={this.lsKeySortColumn}
+                        toggleBySorting={(sortData) => this.toggleBySorting(sortData)}
+                        defaultSortColumn={this.defaultSortColumn}
                     />
-                    :<div>No Tasks found</div>
+                    : <div>No Tasks found</div>
                 }
-                 
-                {!this.state.isLoading  &&
+
+                {!this.state.isLoading &&
                     <>
-                    {(this.state.stationGroup && this.state.stationGroup.length > 0 )?
-                        <Stations
-                            stationGroup={this.state.stationGroup}
-                            targetObservation={this.state.targetObservation}
-                            view
-                        />
-                        :<>
-                        <div style={{marginTop: "10px"}}>
-                            <h3>Station Groups</h3>
-                        </div>
-                        <div>No Station Groups Specified</div>
-                        </>
-                    }
+                        {(this.state.stationGroup && this.state.stationGroup.length > 0) ?
+                            <Stations
+                                stationGroup={this.state.stationGroup}
+                                targetObservation={this.state.targetObservation}
+                                view
+                            />
+                            : <>
+                                <div style={{ marginTop: "10px" }}>
+                                    <h3>Station Groups</h3>
+                                </div>
+                                <div>No Station Groups Specified</div>
+                            </>
+                        }
 
-                    {this.state.scheduleunit && this.state.scheduleunit.scheduling_constraints_doc && 
-                        <SchedulingConstraint disable constraintTemplate={this.state.constraintTemplate} 
+                        {this.state.scheduleunit && this.state.scheduleunit.scheduling_constraints_doc &&
+                            <SchedulingConstraint disable constraintTemplate={this.state.constraintTemplate}
                                 initValue={this.state.scheduleunit.scheduling_constraints_doc} />}
                     </>
                 }
                 {this.state.showStatusLogs &&
-                    <Dialog header={`Status change logs - ${this.state.task?this.state.task.name:""}`} 
-                            visible={this.state.showStatusLogs} maximizable maximized={false} position="left" style={{ width: '50vw' }} 
-                            onHide={() => {this.setState({showStatusLogs: false})}}>
-                            <TaskStatusLogs taskId={this.state.task.id}></TaskStatusLogs>
+                    <Dialog header={`Status change logs - ${this.state.task ? this.state.task.name : ""}`}
+                        visible={this.state.showStatusLogs} maximizable maximized={false} position="left" style={{ width: '50vw' }}
+                        onHide={() => { this.setState({ showStatusLogs: false }) }}>
+                        <TaskStatusLogs taskId={this.state.task.id}></TaskStatusLogs>
                     </Dialog>
-                 }
+                }
                 {/* Dialog component to show messages and get confirmation */}
-                
+
                 <CustomDialog type="confirmation" visible={this.state.dialogVisible}
-                        header={this.state.dialog.header} message={this.state.dialog.detail} actions={this.state.dialog.actions}
-                        content={this.state.dialog.content} width={this.state.dialog.width} showIcon={this.state.dialog.showIcon}
-                        onClose={this.closeDialog} onCancel={this.closeDialog} onSubmit={this.state.dialog.onSubmit}/>
-                        
+                    header={this.state.dialog.header} message={this.state.dialog.detail} actions={this.state.dialog.actions}
+                    content={this.state.dialog.content} width={this.state.dialog.width} showIcon={this.state.dialog.showIcon}
+                    onClose={this.closeDialog} onCancel={this.closeDialog} onSubmit={this.state.dialog.onSubmit} />
+
                 {/* Show spinner during backend API call */}
                 <CustomPageSpinner visible={this.state.showSpinner} />
 
                 {/* To show Data Products To Ingest */}
                 {this.state.showTaskRelationDialog && (
-                      <Schedulingtaskrelation
-                      showTaskRelationDialog={this.state.showTaskRelationDialog}
-                      ingestGroup={this.state.ingestGroup}
-                      toggle={this.showTaskRelationDialog}
-                      submitTRDToIngest={ (trDraft)=> this.submitTRDToIngest(trDraft) }
-                      />
+                    <Schedulingtaskrelation
+                        showTaskRelationDialog={this.state.showTaskRelationDialog}
+                        ingestGroup={this.state.ingestGroup}
+                        toggle={this.showTaskRelationDialog}
+                        submitTRDToIngest={(trDraft) => this.submitTRDToIngest(trDraft)}
+                    />
                 )}
                 <CustomDialog type={this.dialogType} visible={this.state.confirmDialogVisible} width={this.dialogWidth} height={this.dialogHeight}
-                    header={this.dialogHeader} message={this.dialogMsg} 
-                    content={this.dialogContent} 
+                    header={this.dialogHeader} message={this.dialogMsg}
+                    content={this.dialogContent}
                     showIcon={this.showIcon} onClose={this.cancelDialog} onCancel={this.cancelDialog} onSubmit={this.callBackFunction}>
                 </CustomDialog>
 
                 <CustomDialog type={this.dialogType} visible={this.state.confirmTaskReplationDialog} width={this.dialogWidth} height={this.dialogHeight}
-                    header={this.dialogHeader} message={this.dialogMsg} 
-                    content={this.dialogContent} 
+                    header={this.dialogHeader} message={this.dialogMsg}
+                    content={this.dialogContent}
                     showIcon={this.showIcon} onClose={this.cancelDialog} onCancel={this.cancelDialog} onSubmit={this.submitTaskReplationDialog}>
                 </CustomDialog>
                 <CustomPageSpinner visible={this.state.showSpinner} />
-              </>
+            </>
         )
     }
 }
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js
index 7f552ba2d3c3bf60ebff7705b5f98edc3f38c999..f10d5e44b551c9d7e326c3f11e67ce9c38252d7a 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js
@@ -18,6 +18,7 @@ import ProjectService from '../../services/project.service';
 import ScheduleService from '../../services/schedule.service';
 import TaskService from '../../services/task.service';
 import UIConstants from '../../utils/ui.constants';
+import ParserUtility from '../../utils/parser.utility';
 import PageHeader from '../../layout/components/PageHeader';
 import SchedulingConstraint from './Scheduling.Constraints';
 import Stations from './Stations';
@@ -62,6 +63,7 @@ export class SchedulingUnitCreate extends Component {
         this.schedulingSets = [];                   // All scheduling sets to be filtered for project
         this.observStrategies = [];                 // All Observing strategy templates
         this.taskTemplates = [];                    // All task templates to be filtered based on tasks in selected strategy template
+        this.taskTemplateSchemas = {};
         this.tooltipOptions = UIConstants.tooltipOptions;
         this.constraintTemplates = [];              
         this.nameInput = React.createRef();         // Ref to Name field for auto focus
@@ -135,74 +137,43 @@ export class SchedulingUnitCreate extends Component {
      * It generates the JSON schema for JSON editor and defult vales for the parameters to be captured
      * @param {number} strategyId 
      */
-    async changeStrategy (strategyId) {
+     async changeStrategy (strategyId) {
         const observStrategy = _.find(this.observStrategies, {'id': strategyId});
         let station_group = [];
-        const tasks = observStrategy.template.tasks;    
+        const tasks = observStrategy.template.tasks;
+        const parameters = observStrategy.template.parameters;
         let paramsOutput = {};
         let schema = { type: 'object', additionalProperties: false, 
                         properties: {}, definitions:{}
-                     };
-                     
-            // TODo: This schema reference resolving code has to be moved to common file and needs to rework
-            for (const taskName of _.keys(tasks)) {
+                        };
+        const $strategyRefs = await $RefParser.resolve(observStrategy.template);
+        // TODo: This schema reference resolving code has to be moved to common file and needs to rework
+        for (const param of parameters) {
+            let taskPaths = param.refs[0].split("/");
+            const taskName = taskPaths[2];
+            taskPaths = taskPaths.slice(4, taskPaths.length);
             const task = tasks[taskName];
-            //Resolve task from the strategy template
-            const $taskRefs = await $RefParser.resolve(task);
-
-            // Identify the task specification template of every task in the strategy template
             const taskTemplate = _.find(this.taskTemplates, {'name': task['specifications_template']});
-            schema['$id'] = taskTemplate.schema['$id'];
-            schema['$schema'] = taskTemplate.schema['$schema'];
             if (taskTemplate.type_value==='observation' && task.specifications_doc.station_groups) {
                 station_group = task.specifications_doc.station_groups;
             }
-            let index = 0;
-            for (const param of observStrategy.template.parameters) {
-                if (param.refs[0].indexOf(`/tasks/${taskName}`) > 0) {
-                    // Resolve the identified template
-                    const $templateRefs = await $RefParser.resolve(taskTemplate);
-                    let property = { };
-                    let tempProperty = null;
-                    const taskPaths = param.refs[0].split("/");
-                    // Get the property type from the template and create new property in the schema for the parameters
-                    try {
-                        const parameterRef = param.refs[0];//.replace(`#/tasks/${taskName}/specifications_doc`, '#/schema/properties');
-                        tempProperty = $templateRefs.get(parameterRef);
-                    //    property = _.cloneDeep(taskTemplate.schema.properties[taskPaths[4]]);
-                       
-                    }   catch(error) {
-                        tempProperty = _.cloneDeep(taskTemplate.schema.properties[taskPaths[4]]);
-                        if (tempProperty['$ref']) {
-                            tempProperty = await UtilService.resolveSchema(tempProperty);
-                            if (tempProperty.definitions && tempProperty.definitions[taskPaths[4]]) {
-                                schema.definitions = {...schema.definitions, ...tempProperty.definitions};
-                                tempProperty = tempProperty.definitions[taskPaths[4]];
-                            }   else if (tempProperty.properties && tempProperty.properties[taskPaths[4]]) {
-                                tempProperty = tempProperty.properties[taskPaths[4]];
-                            }
-                        }
-                        if (tempProperty.type === 'array' && taskPaths.length>6) {
-                            tempProperty = tempProperty.items.properties[taskPaths[6]];
-                        }
-                        property = tempProperty;
-                    }
-                    property.title = param.name;
-                    property.default = $taskRefs.get(param.refs[0].replace(`#/tasks/${taskName}`, '#'));
-                    paramsOutput[`param_${index}`] = property.default;
-                    schema.properties[`param_${index}`] = property;
-                    // Set property defintions taken from the task template in new schema
-                    for (const definitionName in taskTemplate.schema.definitions) {
-                        schema.definitions[definitionName] = taskTemplate.schema.definitions[definitionName];
-                        
-                    }
-                }
-                index++;
-               
+            let taskTemplateSchema = this.taskTemplateSchemas[task['specifications_template']];
+            if (!taskTemplateSchema) {
+                taskTemplateSchema = _.find(this.taskTemplates, {'name': task['specifications_template']}).schema;
+                taskTemplateSchema = await UtilService.resolveSchema(_.cloneDeep(taskTemplateSchema));
+                this.taskTemplateSchemas[task['specifications_template']] = taskTemplateSchema;
+            }
+            schema.definitions = {...schema.definitions, ...taskTemplateSchema.definitions};
+            taskPaths.reverse();
+            const paramProp = await ParserUtility.getParamProperty($strategyRefs, taskPaths, taskTemplateSchema, param);
+            schema.properties[param.name] = _.cloneDeep(paramProp);
+            if (schema.properties[param.name]) {
+                schema.properties[param.name].title = param.name;
+                schema.properties[param.name].default = $strategyRefs.get(param.refs[0]);
+                paramsOutput[param.name] = schema.properties[param.name].default; 
             }
-            
         }
-        this.setState({observStrategy: observStrategy, paramsSchema: schema, paramsOutput: paramsOutput, stationGroup: station_group, isDirty: true});
+        this.setState({observStrategy: observStrategy, paramsSchema: _.cloneDeep(schema), paramsOutput: paramsOutput, stationGroup: station_group, isDirty: true});
         publish('edit-dirty', true);
 
         // Function called to clear the JSON Editor fields and reload with new schema
@@ -387,7 +358,7 @@ export class SchedulingUnitCreate extends Component {
         let observStrategy = _.cloneDeep(this.state.observStrategy);
         const $refs = await $RefParser.resolve(observStrategy.template);
         observStrategy.template.parameters.forEach(async(param, index) => {
-            $refs.set(observStrategy.template.parameters[index]['refs'][0], this.state.paramsOutput['param_' + index]);
+            $refs.set(observStrategy.template.parameters[index]['refs'][0], this.state.paramsOutput[param.name]);
         });
         for (const taskName in observStrategy.template.tasks) {
             let task = observStrategy.template.tasks[taskName];
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/edit.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/edit.js
index d28fc907e584d5c443998210e87a01b5a55321a1..a5741a57125fb6fb4fa82e7c729f5ceb80bb7c57 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/edit.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/edit.js
@@ -21,6 +21,7 @@ import ProjectService from '../../services/project.service';
 import ScheduleService from '../../services/schedule.service';
 import TaskService from '../../services/task.service';
 import UIConstants from '../../utils/ui.constants';
+import ParserUtility from '../../utils/parser.utility';
 import SchedulingConstraint from './Scheduling.Constraints';
 import UtilService from '../../services/util.service';
 
@@ -54,6 +55,7 @@ export class EditSchedulingUnit extends Component {
         this.schedulingSets = [];                   // All scheduling sets to be filtered for project
         this.observStrategies = [];                 // All Observing strategy templates
         this.taskTemplates = [];                    // All task templates to be filtered based on tasks in selected strategy template             
+        this.taskTemplateSchemas = {};
         this.schedulingSets = [];                   
         this.observStrategies = [];
         this.taskTemplates = [];                    
@@ -84,73 +86,50 @@ export class EditSchedulingUnit extends Component {
      * It generates the JSON schema for JSON editor and defult vales for the parameters to be captured
      * @param {number} strategyId 
      */
-    async changeStrategy (strategyId) {
-        let tasksToUpdate = {};
+     async changeStrategy (strategyId) {
         const observStrategy = _.find(this.observStrategies, {'id': strategyId});
-        const tasks = observStrategy.template.tasks;    
+        let station_group = [];
+        let tasksToUpdate = {};
+        const tasks = observStrategy.template.tasks;
+        const parameters = observStrategy.template.parameters;
         let paramsOutput = {};
         let schema = { type: 'object', additionalProperties: false, 
                         properties: {}, definitions:{}
-                     };
+                        };
+        const $strategyRefs = await $RefParser.resolve(observStrategy.template);
         // TODo: This schema reference resolving code has to be moved to common file and needs to rework
-        for (const taskName in tasks)  {
+        for (const param of parameters) {
+            let taskPaths = param.refs[0].split("/");
+            const taskName = taskPaths[2];
+            tasksToUpdate[taskName] = taskName;
+            taskPaths = taskPaths.slice(4, taskPaths.length);
             const task = tasks[taskName];
             const taskDraft = this.state.taskDrafts.find(taskD => taskD.name === taskName);
             if (taskDraft) {
                 task.specifications_doc = taskDraft.specifications_doc;
             }
-            //Resolve task from the strategy template
-            const $taskRefs = await $RefParser.resolve(task);
-
-            // Identify the task specification template of every task in the strategy template
             const taskTemplate = _.find(this.taskTemplates, {'name': task['specifications_template']});
-            schema['$id'] = taskTemplate.schema['$id'];
-            schema['$schema'] = taskTemplate.schema['$schema'];
-            let index = 0;
-            for (const param of observStrategy.template.parameters) {
-                if (param.refs[0].indexOf(`/tasks/${taskName}`) > 0) {
-                    tasksToUpdate[taskName] = taskName;
-                    // Resolve the identified template
-                    const $templateRefs = await $RefParser.resolve(taskTemplate);
-                    let property = { };
-                    let tempProperty = null;
-                    const taskPaths = param.refs[0].split("/");
-                    // Get the property type from the template and create new property in the schema for the parameters
-                    try {
-                        const parameterRef = param.refs[0];//.replace(`#/tasks/${taskName}/specifications_doc`, '#/schema/properties');
-                        tempProperty = $templateRefs.get(parameterRef);
-                    }   catch(error) {
-                        tempProperty = _.cloneDeep(taskTemplate.schema.properties[taskPaths[4]]);
-                        if (tempProperty['$ref']) {
-                            tempProperty = await UtilService.resolveSchema(tempProperty);
-                            if (tempProperty.definitions && tempProperty.definitions[taskPaths[4]]) {
-                                schema.definitions = {...schema.definitions, ...tempProperty.definitions};
-                                tempProperty = tempProperty.definitions[taskPaths[4]];
-                            }   else if (tempProperty.properties && tempProperty.properties[taskPaths[4]]) {
-                                tempProperty = tempProperty.properties[taskPaths[4]];
-                            }
-                        }
-                        if (tempProperty.type === 'array' && taskPaths.length>6) {
-                            tempProperty = tempProperty.items.properties[taskPaths[6]];
-                        }
-                        property = tempProperty;
-                    }
-                    property.title = param.name;
-                    property.default = $taskRefs.get(param.refs[0].replace(`#/tasks/${taskName}`, '#'));
-                    paramsOutput[`param_${index}`] = property.default;
-                    schema.properties[`param_${index}`] = property;
-                    // Set property defintions taken from the task template in new schema
-                    for (const definitionName in taskTemplate.schema.definitions) {
-                        schema.definitions[definitionName] = taskTemplate.schema.definitions[definitionName];
-                    }
-                }
-                index++;
-            }
             if (taskTemplate.type_value==='observation' && task.specifications_doc.station_groups) {
-                tasksToUpdate[taskName] = taskName;
+                station_group = task.specifications_doc.station_groups;
+            }
+            let taskTemplateSchema = this.taskTemplateSchemas[task['specifications_template']];
+            if (!taskTemplateSchema) {
+                taskTemplateSchema = _.find(this.taskTemplates, {'name': task['specifications_template']}).schema;
+                taskTemplateSchema = await UtilService.resolveSchema(_.cloneDeep(taskTemplateSchema));
+                this.taskTemplateSchemas[task['specifications_template']] = taskTemplateSchema;
+            }
+            schema.definitions = {...schema.definitions, ...taskTemplateSchema.definitions};
+            taskPaths.reverse();
+            const paramProp = await ParserUtility.getParamProperty($strategyRefs, taskPaths, taskTemplateSchema, param);
+            schema.properties[param.name] = _.cloneDeep(paramProp);
+            if (schema.properties[param.name]) {
+                schema.properties[param.name].title = param.name;
+                schema.properties[param.name].default = $strategyRefs.get(param.refs[0]);
+                paramsOutput[param.name] = schema.properties[param.name].default; 
             }
         }
         this.setState({observStrategy: observStrategy, paramsSchema: schema, paramsOutput: paramsOutput, tasksToUpdate: tasksToUpdate});
+        // this.setState({observStrategy: observStrategy, paramsSchema: _.cloneDeep(schema), paramsOutput: paramsOutput, stationGroup: station_group, isDirty: true});
 
         // Function called to clear the JSON Editor fields and reload with new schema
         if (this.state.editorFunction) {
@@ -354,7 +333,7 @@ export class EditSchedulingUnit extends Component {
             let observStrategy = _.cloneDeep(this.state.observStrategy);
             const $refs = await $RefParser.resolve(observStrategy.template);
             observStrategy.template.parameters.forEach(async(param, index) => {
-                $refs.set(observStrategy.template.parameters[index]['refs'][0], this.state.paramsOutput['param_' + index]);
+                $refs.set(observStrategy.template.parameters[index]['refs'][0], this.state.paramsOutput[param.name]);
             });
             const schUnit = { ...this.state.schedulingUnit };
             schUnit.scheduling_constraints_doc = constStrategy;
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.js
index ff028172ae69ebe0768d5451823edda91486d744..780bfffe4bee72fcf6feeb006d6bcd92189943f4 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.js
@@ -24,7 +24,8 @@ import AppLoader from '../../layout/components/AppLoader';
 import PageHeader from '../../layout/components/PageHeader';
 import { publish } from '../../App';
 import { CustomDialog } from '../../layout/components/CustomDialog';
-import SchedulingSet from './schedulingset.create';  
+import SchedulingSet from './schedulingset.create';
+import SchedulingConstraints from './Scheduling.Constraints';
 
 import ProjectService from '../../services/project.service';
 import ScheduleService from '../../services/schedule.service';
@@ -34,6 +35,7 @@ import UtilService from '../../services/util.service';
 import Validator from  '../../utils/validator';
 import UnitConverter from '../../utils/unit.converter'
 import UIConstants from '../../utils/ui.constants';
+import ParserUtility from '../../utils/parser.utility';
 
 import moment from 'moment';
 import _ from 'lodash';
@@ -155,6 +157,7 @@ export class SchedulingSetCreate extends Component {
         this.schedulingSets = [];                   // All scheduling sets to be filtered for project
         this.observStrategies = [];                 // All Observing strategy templates
         this.taskTemplates = [];                    // All task templates to be filtered based on tasks in selected strategy template
+        this.taskTemplateSchemas = [];
         this.constraintTemplates = [];
         this.agSUWithDefaultValue = {'id': 0, 'suname': '', 'sudesc': ''};
         this.emptyAGSU = {};
@@ -194,6 +197,7 @@ export class SchedulingSetCreate extends Component {
             project: {required: true, message: "Select project to get Scheduling Sets"},
             scheduling_set_id: {required: true, message: "Select the Scheduling Set"},
         };
+        this.setConstraintsEditorOutput = this.setConstraintsEditorOutput.bind(this);
     }
     
     async onTopGridReady (params) {
@@ -483,6 +487,48 @@ export class SchedulingSetCreate extends Component {
     }
 
     async getTaskSchema(observStrategy) {
+        let station_group = [];
+        let tasksToUpdate = {};
+        if(observStrategy) {
+            const tasks = observStrategy.template.tasks;
+            const parameters = observStrategy.template.parameters;
+            let paramsOutput = {};
+            let schema = { type: 'object', additionalProperties: false, 
+                            properties: {}, definitions:{}
+                            };
+            const $strategyRefs = await $RefParser.resolve(observStrategy.template);
+            // TODo: This schema reference resolving code has to be moved to common file and needs to rework
+            for (const param of parameters) {
+                let taskPaths = param.refs[0].split("/");
+                const taskName = taskPaths[2];
+                tasksToUpdate[taskName] = taskName;
+                taskPaths = taskPaths.slice(4, taskPaths.length);
+                const task = tasks[taskName];
+                const taskTemplate = _.find(this.taskTemplates, {'name': task['specifications_template']});
+                if (taskTemplate.type_value==='observation' && task.specifications_doc.station_groups) {
+                    station_group = task.specifications_doc.station_groups;
+                }
+                let taskTemplateSchema = this.taskTemplateSchemas[task['specifications_template']];
+                if (!taskTemplateSchema) {
+                    taskTemplateSchema = _.find(this.taskTemplates, {'name': task['specifications_template']}).schema;
+                    taskTemplateSchema = await UtilService.resolveSchema(_.cloneDeep(taskTemplateSchema));
+                    this.taskTemplateSchemas[task['specifications_template']] = taskTemplateSchema;
+                }
+                schema.definitions = {...schema.definitions, ...taskTemplateSchema.definitions};
+                taskPaths.reverse();
+                const paramProp = await ParserUtility.getParamProperty($strategyRefs, taskPaths, taskTemplateSchema, param);
+                schema.properties[param.name] = _.cloneDeep(paramProp);
+                if (schema.properties[param.name]) {
+                    schema.properties[param.name].title = param.name;
+                    schema.properties[param.name].default = $strategyRefs.get(param.refs[0]);
+                    paramsOutput[param.name] = schema.properties[param.name].default; 
+                }
+            }
+            await this.setState({observStrategy: observStrategy, paramsSchema: schema, paramsOutput: paramsOutput,defaultStationGroups: station_group, tasksToUpdate: tasksToUpdate});
+        }
+    }
+
+    /*async getTaskSchema(observStrategy) {
         let station_group = [];
         let tasksToUpdate = {};
         if(observStrategy) {
@@ -526,7 +572,11 @@ export class SchedulingSetCreate extends Component {
                                 tempProperty = await UtilService.resolveSchema(tempProperty);
                                 if (tempProperty.definitions && tempProperty.definitions[taskPaths[4]]) {
                                     schema.definitions = {...schema.definitions, ...tempProperty.definitions};
-                                    tempProperty = tempProperty.definitions[taskPaths[4]];
+                                    if (taskPaths.length>6) {
+                                        tempProperty = _.cloneDeep(tempProperty.definitions[taskPaths[4]]);
+                                    }   else {
+                                        tempProperty = {'$ref': `#/definitions/${taskPaths[4]}`};
+                                    }
                                 }   else if (tempProperty.properties && tempProperty.properties[taskPaths[4]]) {
                                     tempProperty = tempProperty.properties[taskPaths[4]];
                                 }
@@ -550,7 +600,7 @@ export class SchedulingSetCreate extends Component {
             }
             await this.setState({observStrategy: observStrategy, paramsSchema: schema, paramsOutput: paramsOutput,defaultStationGroups: station_group, tasksToUpdate: tasksToUpdate});
         }
-    }
+    }*/
 
     /**
      * Resolve JSON Schema
@@ -1553,9 +1603,11 @@ export class SchedulingSetCreate extends Component {
     async prepareObservStrategyFromExcelValue(suRow) {
         let colKeys =  Object.keys(suRow);
         let paramsOutput = {};
+        let parameters = _.map(this.state.observStrategy.template.parameters, 'name');
         for(const colKey of colKeys)  {
             let prefix = colKey.split("~");  
-            if(colKey.startsWith('param_') && prefix.length > 1) {
+            // if(colKey.startsWith('param_') && prefix.length > 1) {
+            if(prefix.length > 1 && parameters.indexOf(prefix[0])>=0 ) {
                 var res =  Object.keys(suRow).filter(v => v.startsWith(prefix[0]));
                 if(res && res.length > 1) {
                     let res = paramsOutput[prefix[0]];
@@ -1591,7 +1643,8 @@ export class SchedulingSetCreate extends Component {
         let observStrategy = _.cloneDeep(this.state.observStrategy);
         const $refs = await $RefParser.resolve(observStrategy.template);
         observStrategy.template.parameters.forEach(async(param, index) => {
-            $refs.set(observStrategy.template.parameters[index]['refs'][0], this.state.paramsOutput['param_' + index]);
+            // $refs.set(observStrategy.template.parameters[index]['refs'][0], this.state.paramsOutput['param_' + index]);
+            $refs.set(observStrategy.template.parameters[index]['refs'][0], this.state.paramsOutput[param.name]);
         });
         return observStrategy;
     }
@@ -1613,8 +1666,9 @@ export class SchedulingSetCreate extends Component {
             constraint = this.state.schedulingConstraintsDoc;
         }
         if(!constraint) {
-            let schedulingUnit = await ScheduleService.getSchedulingUnitDraftById(1);
-            constraint = (schedulingUnit)? schedulingUnit.scheduling_constraints_doc : {};
+            // let schedulingUnit = await ScheduleService.getSchedulingUnitDraftById(1);
+            // constraint = (schedulingUnit)? schedulingUnit.scheduling_constraints_doc : {};
+            constraint = this.defaultConstraintDoc;
         }
         //If No SU Constraint create default ( maintain default struc)
         constraint['scheduler'] = suRow.scheduler;
@@ -1623,11 +1677,11 @@ export class SchedulingSetCreate extends Component {
                 delete constraint.time.at;
             }
           
-            if (!this.isNotEmpty(suRow.timeafter)) {
+            if (!this.isNotEmpty(suRow.timeafter) && constraint.time) {
                 delete constraint.time.after;
             }
            
-            if (!this.isNotEmpty(suRow.timebefore)) {
+            if (!this.isNotEmpty(suRow.timebefore) && constraint.time) {
                 delete constraint.time.before;
             }
         }  
@@ -1806,9 +1860,9 @@ export class SchedulingSetCreate extends Component {
                 }
                 
                 this.dialogContent = this.getSchedulingDialogContent;
-                this.onCancel = this.reset;
+                this.onCancel = null;
                 this.onClose = this.reset;
-                this.callBackFunction = this.reset;
+                this.callBackFunction = null;
                 this.setState({isDirty : false, showSpinner: false, confirmDialogVisible: true, /*dialog: dialog,*/ isAGLoading: true, copyHeader: false, rowData: []});
                 publish('edit-dirty', false);
             }  else  {
@@ -1817,6 +1871,7 @@ export class SchedulingSetCreate extends Component {
                 this.growl.show({severity: 'error', summary: 'Warning', detail: 'No Scheduling Units create/update '});
             }
         } catch(err){
+            console.error(err);
             this.growl.show({severity: 'error', summary: 'Error Occured', detail: 'Unable to create/update Scheduling Units'});
             this.setState({showSpinner: false});
         }
@@ -1921,18 +1976,20 @@ export class SchedulingSetCreate extends Component {
      * Refresh the grid with updated data
      */
       async reset() {
-        let schedulingUnitList = await ScheduleService.getSchedulingBySet(this.state.selectedSchedulingSetId);
-        schedulingUnitList = _.filter(schedulingUnitList,{'observation_strategy_template_id': this.state.observStrategy.id}) ;
-        this.setState({
-            schedulingUnitList:  schedulingUnitList,
-            confirmDialogVisible: false,
-            isDirty: false
-        });
-        publish('edit-dirty', false);
-        this.isNewSet = false;
-        await this.prepareScheduleUnitListForGrid();
-        this.state.gridApi.setRowData(this.state.rowData);
-        this.state.gridApi.redrawRows();
+        if (this.state.confirmDialogVisible) {
+            let schedulingUnitList = await ScheduleService.getSchedulingBySet(this.state.selectedSchedulingSetId);
+            schedulingUnitList = _.filter(schedulingUnitList,{'observation_strategy_template_id': this.state.observStrategy.id}) ;
+            this.setState({
+                schedulingUnitList:  schedulingUnitList,
+                confirmDialogVisible: false,
+                isDirty: false
+            });
+            publish('edit-dirty', false);
+            this.isNewSet = false;
+            await this.prepareScheduleUnitListForGrid();
+            this.state.gridApi.setRowData(this.state.rowData);
+            this.state.gridApi.redrawRows();
+        }
     }
     
      /**
@@ -2302,6 +2359,14 @@ export class SchedulingSetCreate extends Component {
         }
     }
 
+    /**
+     * Callback function for JSON Editor to set the default constraint_doc value.
+     * @param {Object} jsonOutput - default constraint values from the editor
+     */
+    setConstraintsEditorOutput(jsonOutput) {
+        this.defaultConstraintDoc = jsonOutput;
+    }
+
     render() {
         if (this.state.redirect) {
             return <Redirect to={ {pathname: this.state.redirect} }></Redirect>
@@ -2313,7 +2378,13 @@ export class SchedulingSetCreate extends Component {
                 actions={[{icon: 'fa-window-close',title:'Close',  type: 'button',  actOn: 'click', props:{ callback: this.checkIsDirty }}]}
                 />
                 { this.state.isLoading ? <AppLoader /> :
-                <>                   
+                <>       
+                    {/* SchedulingConstraint editor to pass the scheduling_constraint schema and get the default 
+                        constraint_doc for new SUs if not constraint fields are edited */}
+                    <div style={{display: "none"}}>
+                        <SchedulingConstraints constraintTemplate={this.constraintTemplates[0]} disable 
+                            formatOutput={false} callback={this.setConstraintsEditorOutput} />
+                    </div>            
                     <div> 
                         <div className="p-fluid">
                             
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Simulator/index.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Simulator/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..5d06a46d932beb2503b5b0e63799d0571a9c8b08
--- /dev/null
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Simulator/index.js
@@ -0,0 +1,68 @@
+import React, { Component } from 'react';
+import { ProgressBar } from 'primereact/progressbar';
+
+import SimulatorService from "../../services/simulator.service";
+
+export class Simulator extends Component {
+
+    constructor(props) {
+        super(props);
+        this.state = {
+            status: "Simulating...",
+            updateStatus: []
+        }
+    }
+
+    componentDidMount() {
+        this.simulate(this.props.match.params.id);
+    }
+
+    async simulate(subId) {
+        const subStatus = await SimulatorService.simulateSUB(subId);
+        console.log(subStatus);
+        this.setState({status: "Simulation Completed", updateStatus: subStatus.taskStatus});
+    }
+
+    render() {
+        return (
+            <div>
+                {this.state.status}
+                <ProgressBar mode={this.state.status==="Simulating..."?"indeterminate":"determinate"} 
+                            value={this.state.status==="Simulating..."?0:100} style={{ height: '3px' }}/>
+                <h3 style={{marginTop: "10px"}}>Task Status Update Log</h3>
+                <div className="p-grid" style={{marginTop: "10px", border: "1px solid", backgroundColor: "lightgrey"}}>
+                    <div className="p-col-2" style={{border: "1px solid"}}>Task Id</div>
+                    <div className="p-col-2" style={{border: "1px solid"}}>Task Name</div>
+                    <div className="p-col-8" style={{border: "1px solid"}}>
+                        <div className="p-grid" style={{border: "1px solid"}}>
+                            <div className="p-col-4" style={{border: "1px solid"}}>Subtask Id</div>
+                            <div className="p-col-4" style={{border: "1px solid"}}>Subtask State</div>
+                            <div className="p-col-4" style={{border: "1px solid"}}>Update Status</div>
+                        </div>
+                    </div>
+                </div>
+                {this.state.updateStatus.length>0 && this.state.updateStatus.map((task, index) => (
+                    <React.Fragment key={index+10}>
+                        <div key={"task_" + index} className="p-grid" style={{border: "1px solid"}}>
+                            <div key={"task_id_" + index} className="p-col-2" style={{border: "1px solid"}}>{task.id}</div>
+                            <div key={"task_name_" + index} className="p-col-2" style={{border: "1px solid"}}>{task.name}</div>
+                            <div key={"subtasks_" + index} className="p-col-8" style={{border: "1px solid"}}>
+                            {task.subtaskStatus.length>0 && task.subtaskStatus.map((subtask, sindex) => (
+                                <React.Fragment key={index+"_"+sindex}>
+                                    <div key={"subtask_" + index + "_" + sindex} className="p-grid" style={{border: "1px solid"}}>
+                                        <div key={"subtask_id_" + index + "_" + sindex} className="p-col-4" style={{border: "1px solid"}}>{subtask.id}</div>
+                                        <div key={"subtask_state_" + index + "_" + sindex} className="p-col-4" style={{border: "1px solid"}}>{subtask.state}</div>
+                                        <div key={"subtask_status_" + index + "_" + sindex} className="p-col-4" style={{border: "1px solid"}}>{subtask.status}</div>
+                                    </div>
+                                </React.Fragment>
+                            ))}
+                            </div>
+                        </div>
+                    </React.Fragment>
+                ))}
+            </div>
+        );
+    }
+}
+
+export default Simulator;
\ No newline at end of file
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/list.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/list.js
index 5bdef19b88263dc4fab8d695fc6ae02f9d2f7f49..86d892395eb6b6eba304124932b7a8027d04a230 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/list.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/list.js
@@ -1,5 +1,5 @@
-import React, {Component} from 'react';
-import {Redirect} from 'react-router-dom'
+import React, { Component } from 'react';
+import { Redirect } from 'react-router-dom'
 import moment from 'moment';
 import { Dialog } from 'primereact/dialog';
 import { DataTable } from 'primereact/datatable';
@@ -15,8 +15,16 @@ import { appGrowl } from '../../layout/components/AppGrowl';
 import { CustomDialog } from '../../layout/components/CustomDialog';
 import ScheduleService from '../../services/schedule.service';
 import UnitConverter from '../../utils/unit.converter';
+import UtilService from '../../services/util.service';
 
 export class TaskList extends Component {
+    lsKeySortColumn = "TaskListSortData";
+    // The following values should be lower case
+    ignoreSorting = ['status logs'];
+<<<<<<< HEAD
+    TASK_END_STATUSES = ['finished', 'error', 'cancelled'];
+=======
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
     constructor(props) {
         super(props);
         this.state = {
@@ -27,118 +35,127 @@ export class TaskList extends Component {
             }],
             columnOrders: [
                 "Status Logs",
-                 "Status",
-                 "Type",
-                 "Scheduling Unit ID",
-                 "Scheduling Unit Name",
-                 "ID",
-                 "Control ID",
-                 "Name",
-                 "Description",
-                 "Start Time",
-                 "End Time",
-                 "Duration (HH:mm:ss)",
-                 "Relative Start Time (HH:mm:ss)",
-                 "Relative End Time (HH:mm:ss)",
-                 "#Dataproducts",
-                 "size",
-                 "dataSizeOnDisk",
-                 "subtaskContent",
-                 "tags",
-                 "blueprint_draft",
-                 "url",
-                 "Cancelled",
-                 "Created at",
-                 "Updated at"
-             ],
+                "Status",
+                "Type",
+                "Scheduling Unit ID",
+                "Scheduling Unit Name",
+                "ID",
+                "Control ID",
+                "Name",
+                "Description",
+                "Start Time",
+                "End Time",
+                "Duration (HH:mm:ss)",
+                "Relative Start Time (HH:mm:ss)",
+                "Relative End Time (HH:mm:ss)",
+                "#Dataproducts",
+                "size",
+                "dataSizeOnDisk",
+                "subtaskContent",
+                "tags",
+                "blueprint_draft",
+                "url",
+                "Cancelled",
+                "Created at",
+                "Updated at"
+            ],
             dialog: {},
-            defaultcolumns: [ {
+            defaultcolumns: [{
                 status_logs: "Status Logs",
-                status:{
-                    name:"Status",
+                status: {
+                    name: "Status",
                     filter: "select"
                 },
-                tasktype:{
-                    name:"Type",
-                    filter:"select"
+                tasktype: {
+                    name: "Type",
+                    filter: "select"
                 },
                 schedulingUnitId: "Scheduling Unit ID",
                 schedulingUnitName: "Scheduling Unit Name",
                 id: "ID",
                 subTaskID: 'Control ID',
-                name:"Name",
-                description:"Description",
-                start_time:{
-                    name:"Start Time",
+                name: "Name",
+                description: "Description",
+                start_time: {
+                    name: "Start Time",
                     filter: "date",
-                    format:UIConstants.CALENDAR_DATETIME_FORMAT
+                    format: UIConstants.CALENDAR_DATETIME_FORMAT
                 },
-                stop_time:{
-                    name:"End Time",
+                stop_time: {
+                    name: "End Time",
                     filter: "date",
-                    format:UIConstants.CALENDAR_DATETIME_FORMAT
+                    format: UIConstants.CALENDAR_DATETIME_FORMAT
                 },
-                duration:"Duration (HH:mm:ss)",
-                relative_start_time:"Relative Start Time (HH:mm:ss)",
-                relative_stop_time:"Relative End Time (HH:mm:ss)",
+                duration: "Duration (HH:mm:ss)",
+                relative_start_time: "Relative Start Time (HH:mm:ss)",
+                relative_stop_time: "Relative End Time (HH:mm:ss)",
                 noOfOutputProducts: "#Dataproducts",
-                do_cancel:{
+                do_cancel: {
                     name: "Cancelled",
                     filter: "switch"
                 },
             }],
-            optionalcolumns:  [{
+            optionalcolumns: [{
                 size: "Data size",
                 dataSizeOnDisk: "Data size on Disk",
                 subtaskContent: "Subtask Content",
-                tags:"Tags",
-                blueprint_draft:"BluePrint / Task Draft link",
-                url:"API URL",
-                created_at:{
+                tags: "Tags",
+                blueprint_draft: "BluePrint / Task Draft link",
+                url: "API URL",
+                created_at: {
                     name: "Created at",
                     filter: "date",
-                    format:UIConstants.CALENDAR_DATETIME_FORMAT
+                    format: UIConstants.CALENDAR_DATETIME_FORMAT
                 },
-                updated_at:{
+                updated_at: {
                     name: "Updated at",
                     filter: "date",
-                    format:UIConstants.CALENDAR_DATETIME_FORMAT
+                    format: UIConstants.CALENDAR_DATETIME_FORMAT
                 },
-                actionpath:"actionpath"
+                actionpath: "actionpath"
             }],
             columnclassname: [{
                 "Status Logs": "filter-input-0",
-                "Type":"filter-input-75",
+                "Type": "filter-input-75",
                 "Scheduling Unit ID": "filter-input-50",
                 "Scheduling Unit Name": "filter-input-100",
-                "ID":"filter-input-50",
-                "Control ID":"filter-input-75",
-                "Cancelled":"filter-input-50",
-                "Duration (HH:mm:ss)":"filter-input-75",
-                "Template ID":"filter-input-50",
+                "ID": "filter-input-50",
+                "Control ID": "filter-input-75",
+                "Cancelled": "filter-input-50",
+                "Duration (HH:mm:ss)": "filter-input-75",
+                "Template ID": "filter-input-50",
                 // "BluePrint / Task Draft link": "filter-input-100",
                 "Relative Start Time (HH:mm:ss)": "filter-input-75",
                 "Relative End Time (HH:mm:ss)": "filter-input-75",
-                "Status":"filter-input-100",
-                "#Dataproducts":"filter-input-75",
-                "Data size":"filter-input-50",
-                "Data size on Disk":"filter-input-50",
-                "Subtask Content":"filter-input-75",
-                "BluePrint / Task Draft link":"filter-input-50",
+                "Status": "filter-input-100",
+                "#Dataproducts": "filter-input-75",
+                "Data size": "filter-input-50",
+                "Data size on Disk": "filter-input-50",
+                "Subtask Content": "filter-input-75",
+                "BluePrint / Task Draft link": "filter-input-50",
+<<<<<<< HEAD
+            }],
+            actions: []
+=======
             }]
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
         };
         this.selectedRows = [];
         this.subtaskTemplates = [];
         this.confirmDeleteTasks = this.confirmDeleteTasks.bind(this);
+        this.confirmCancelTasks = this.confirmCancelTasks.bind(this);
         this.onRowSelection = this.onRowSelection.bind(this);
         this.deleteTasks = this.deleteTasks.bind(this);
+        this.cancelTasks = this.cancelTasks.bind(this);
         this.closeDialog = this.closeDialog.bind(this);
-        this.getTaskDialogContent = this.getTaskDialogContent.bind(this);
+        this.getTaskDeleteDialogContent = this.getTaskDeleteDialogContent.bind(this);
+        this.getTaskCancelConfirmContent = this.getTaskCancelConfirmContent.bind(this);
+        this.getTaskCancelStatusContent = this.getTaskCancelStatusContent.bind(this);
     }
 
-    subtaskComponent = (task)=> {
+    subtaskComponent = (task) => {
         return (
-            <button className="p-link" onClick={(e) => {this.setState({showStatusLogs: true, task: task})}}>
+            <button className="p-link" onClick={(e) => { this.setState({ showStatusLogs: true, task: task }) }}>
                 <i className="fa fa-history"></i>
             </button>
         );
@@ -149,23 +166,23 @@ export class TaskList extends Component {
      * Formatting the task_blueprints in blueprint view to pass to the ViewTable component
      * @param {Object} schedulingUnit - scheduling_unit_blueprint object from extended API call loaded with tasks(blueprint) along with their template and subtasks
      */
-     getFormattedTaskBlueprints(schedulingUnit) {
+    getFormattedTaskBlueprints(schedulingUnit) {
         let taskBlueprintsList = [];
-        for(const taskBlueprint of schedulingUnit.task_blueprints) {
+        for (const taskBlueprint of schedulingUnit.task_blueprints) {
             taskBlueprint['status_logs'] = this.subtaskComponent(taskBlueprint);
             taskBlueprint['tasktype'] = 'Blueprint';
-            taskBlueprint['actionpath'] = '/task/view/blueprint/'+taskBlueprint['id'];
+            taskBlueprint['actionpath'] = '/task/view/blueprint/' + taskBlueprint['id'];
             taskBlueprint['blueprint_draft'] = taskBlueprint['draft'];
             taskBlueprint['relative_start_time'] = 0;
             taskBlueprint['relative_stop_time'] = 0;
-            taskBlueprint.duration = moment.utc((taskBlueprint.duration || 0)*1000).format(UIConstants.CALENDAR_TIME_FORMAT);
-            taskBlueprint.template = taskBlueprint.specifications_template; 
+            taskBlueprint.duration = moment.utc((taskBlueprint.duration || 0) * 1000).format(UIConstants.CALENDAR_TIME_FORMAT);
+            taskBlueprint.template = taskBlueprint.specifications_template;
             taskBlueprint.schedulingUnitName = schedulingUnit.name;
             for (const subtask of taskBlueprint.subtasks) {
                 subtask.subTaskTemplate = _.find(this.subtaskTemplates, ['id', subtask.specifications_template_id]);
             }
             taskBlueprint.schedulingUnitId = taskBlueprint.scheduling_unit_blueprint_id;
-            taskBlueprint.subTasks = taskBlueprint.subtasks;           
+            taskBlueprint.subTasks = taskBlueprint.subtasks;
             taskBlueprintsList.push(taskBlueprint);
         }
         return taskBlueprintsList;
@@ -175,25 +192,25 @@ export class TaskList extends Component {
      * Formatting the task_drafts and task_blueprints in draft view to pass to the ViewTable component
      * @param {Object} schedulingUnit - scheduling_unit_draft object from extended API call loaded with tasks(draft & blueprint) along with their template and subtasks
      */
-     getFormattedTaskDrafts(schedulingUnit) {
-        let scheduletasklist=[];
+    getFormattedTaskDrafts(schedulingUnit) {
+        let scheduletasklist = [];
         // Common keys for Task and Blueprint
-        let commonkeys = ['id','created_at','description','name','tags','updated_at','url','do_cancel','relative_start_time','relative_stop_time','start_time','stop_time','duration','status'];
-        for(const task of schedulingUnit.task_drafts){
+        let commonkeys = ['id', 'created_at', 'description', 'name', 'tags', 'updated_at', 'url', 'do_cancel', 'relative_start_time', 'relative_stop_time', 'start_time', 'stop_time', 'duration', 'status'];
+        for (const task of schedulingUnit.task_drafts) {
             let scheduletask = {};
             scheduletask['tasktype'] = 'Draft';
-            scheduletask['actionpath'] = '/task/view/draft/'+task['id'];
+            scheduletask['actionpath'] = '/task/view/draft/' + task['id'];
             scheduletask['blueprint_draft'] = _.map(task['task_blueprints'], 'url');
             scheduletask['status'] = task['status'];
 
             //fetch task draft details
-            for(const key of commonkeys){
+            for (const key of commonkeys) {
                 scheduletask[key] = task[key];
             }
             scheduletask['specifications_doc'] = task['specifications_doc'];
-            scheduletask.duration = moment.utc((scheduletask.duration || 0)*1000).format(UIConstants.CALENDAR_TIME_FORMAT); 
-            scheduletask.relative_start_time = moment.utc(scheduletask.relative_start_time*1000).format(UIConstants.CALENDAR_TIME_FORMAT); 
-            scheduletask.relative_stop_time = moment.utc(scheduletask.relative_stop_time*1000).format(UIConstants.CALENDAR_TIME_FORMAT); 
+            scheduletask.duration = moment.utc((scheduletask.duration || 0) * 1000).format(UIConstants.CALENDAR_TIME_FORMAT);
+            scheduletask.relative_start_time = moment.utc(scheduletask.relative_start_time * 1000).format(UIConstants.CALENDAR_TIME_FORMAT);
+            scheduletask.relative_stop_time = moment.utc(scheduletask.relative_stop_time * 1000).format(UIConstants.CALENDAR_TIME_FORMAT);
             scheduletask.template = task.specifications_template;
             scheduletask.type_value = task.specifications_template.type_value;
             scheduletask.produced_by = task.produced_by;
@@ -208,92 +225,128 @@ export class TaskList extends Component {
 
     async formatDataProduct(tasks) {
         await Promise.all(tasks.map(async task => {
-            task.status_logs = task.tasktype === "Blueprint"?this.subtaskComponent(task):"";
+            task.status_logs = task.tasktype === "Blueprint" ? this.subtaskComponent(task) : "";
             //Displaying SubTask ID of the 'control' Task
-            const subTaskIds = task.subTasks?task.subTasks.filter(sTask => sTask.subTaskTemplate.name.indexOf('control') >= 0):[];
+            const subTaskIds = task.subTasks ? task.subTasks.filter(sTask => sTask.subTaskTemplate.name.indexOf('control') >= 0) : [];
             const promise = [];
             subTaskIds.map(subTask => promise.push(ScheduleService.getSubtaskOutputDataproduct(subTask.id)));
-            const dataProducts = promise.length > 0? await Promise.all(promise):[];
+            const dataProducts = promise.length > 0 ? await Promise.all(promise) : [];
             task.dataProducts = [];
             task.size = 0;
             task.dataSizeOnDisk = 0;
             task.noOfOutputProducts = 0;
-            task.canSelect = task.tasktype.toLowerCase() === 'blueprint' ? true:(task.tasktype.toLowerCase() === 'draft' && task.blueprint_draft.length === 0)?true:false;
+            task.canSelect = task.tasktype.toLowerCase() === 'blueprint' ? true : (task.tasktype.toLowerCase() === 'draft' && task.blueprint_draft.length === 0) ? true : false;
             if (dataProducts.length && dataProducts[0].length) {
                 task.dataProducts = dataProducts[0];
                 task.noOfOutputProducts = dataProducts[0].length;
                 task.size = _.sumBy(dataProducts[0], 'size');
-                task.dataSizeOnDisk = _.sumBy(dataProducts[0], function(product) { return product.deletedSince?0:product.size});
+                task.dataSizeOnDisk = _.sumBy(dataProducts[0], function (product) { return product.deletedSince ? 0 : product.size });
                 task.size = UnitConverter.getUIResourceUnit('bytes', (task.size));
                 task.dataSizeOnDisk = UnitConverter.getUIResourceUnit('bytes', (task.dataSizeOnDisk));
             }
-            task.subTaskID = subTaskIds.length ? subTaskIds[0].id : ''; 
+            task.subTaskID = subTaskIds.length ? subTaskIds[0].id : '';
             return task;
         }));
         return tasks;
     }
 
-
     async componentDidMount() {
+        this.setToggleBySorting();
         this.subtaskTemplates = await TaskService.getSubtaskTemplates()
         const promises = [
-            ScheduleService.getSchedulingUnitsExtended('draft'), 
+            ScheduleService.getSchedulingUnitsExtended('draft'),
             ScheduleService.getSchedulingUnitsExtended('blueprint')
         ];
         Promise.all(promises).then(async (responses) => {
             let allTasks = [];
             for (const schedulingUnit of responses[0]) {
-                let tasks = schedulingUnit.task_drafts?(await this.getFormattedTaskDrafts(schedulingUnit)):this.getFormattedTaskBlueprints(schedulingUnit);
-                let ingestGroup = tasks.map(task => ({name: task.name, canIngest: task.canIngest, type_value: task.type_value, id: task.id }));
+                let tasks = schedulingUnit.task_drafts ? (await this.getFormattedTaskDrafts(schedulingUnit)) : this.getFormattedTaskBlueprints(schedulingUnit);
+                let ingestGroup = tasks.map(task => ({ name: task.name, canIngest: task.canIngest, type_value: task.type_value, id: task.id }));
                 ingestGroup = _.groupBy(_.filter(ingestGroup, 'type_value'), 'type_value');
                 tasks = await this.formatDataProduct(tasks);
                 allTasks = [...allTasks, ...tasks];
             }
             for (const schedulingUnit of responses[1]) {
-                let tasks = schedulingUnit.task_drafts?(await this.getFormattedTaskDrafts(schedulingUnit)):this.getFormattedTaskBlueprints(schedulingUnit);
-                let ingestGroup = tasks.map(task => ({name: task.name, canIngest: task.canIngest, type_value: task.type_value, id: task.id }));
+                let tasks = schedulingUnit.task_drafts ? (await this.getFormattedTaskDrafts(schedulingUnit)) : this.getFormattedTaskBlueprints(schedulingUnit);
+                let ingestGroup = tasks.map(task => ({ name: task.name, canIngest: task.canIngest, type_value: task.type_value, id: task.id }));
                 ingestGroup = _.groupBy(_.filter(ingestGroup, 'type_value'), 'type_value');
                 tasks = await this.formatDataProduct(tasks);
                 allTasks = [...allTasks, ...tasks];
             }
-            this.setState({ tasks: allTasks,  isLoading: false });
+<<<<<<< HEAD
+            const actions = [{icon: 'fa fa-ban', title: 'Cancel Task(s)',
+                                type: 'button', actOn: 'click', props: { callback: this.confirmCancelTasks }},
+                             {icon: 'fa fa-trash', title: 'Delete Task(s)',
+                                type: 'button', actOn: 'click', props: { callback: this.confirmDeleteTasks }}
+                            ];
+            this.setState({ tasks: allTasks, isLoading: false, actions: actions });
+=======
+            this.setState({ tasks: allTasks, isLoading: false });
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
         });
     }
 
+    toggleBySorting = (sortData) => {
+        UtilService.localStore({ type: 'set', key: this.lsKeySortColumn, value: sortData });
+    }
+    setToggleBySorting() {     
+        let sortData = UtilService.localStore({ type: 'get', key: this.lsKeySortColumn });
+        if (sortData) {
+            if (Object.prototype.toString.call(sortData) === '[object Array]') {
+                this.defaultSortColumn = sortData;
+            }
+            else {
+                this.defaultSortColumn = [{ ...sortData }];
+            }
+        }
+        this.defaultSortColumn = this.defaultSortColumn || [];
+        UtilService.localStore({ type: 'set', key: this.lsKeySortColumn, value: [...this.defaultSortColumn] });
+    }
+
     /**
-     * Prepare Task(s) details to show on confirmation dialog
+     * Prepare Task(s) details to show on confirmation dialog before deleting
      */
-     getTaskDialogContent() {
+<<<<<<< HEAD
+    getTaskDeleteDialogContent() {
+=======
+    getTaskDialogContent() {
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
         let selectedTasks = [];
-        for(const obj of this.selectedRows) {
-            selectedTasks.push({id:obj.id, suId: obj.schedulingUnitId, suName: obj.schedulingUnitName, 
-                taskId: obj.id, controlId: obj.subTaskID, taskName: obj.name, status: obj.status});
-        }   
-        return  <>  
-                <DataTable value={selectedTasks} resizableColumns columnResizeMode="expand" className="card" style={{paddingLeft: '0em'}}>
-                    <Column field="suId" header="Scheduling Unit Id"></Column>
-                    <Column field="taskId" header="Task Id"></Column>
-                    <Column field="taskName" header="Task Name"></Column>
-                    <Column field="status" header="Status"></Column>
-                </DataTable>
+        for (const obj of this.selectedRows) {
+            selectedTasks.push({
+                id: obj.id, suId: obj.schedulingUnitId, suName: obj.schedulingUnitName,
+                taskId: obj.id, controlId: obj.subTaskID, taskName: obj.name, status: obj.status
+            });
+        }
+        return <>
+            <DataTable value={selectedTasks} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}>
+                <Column field="suId" header="Scheduling Unit Id"></Column>
+                <Column field="taskId" header="Task Id"></Column>
+                <Column field="taskName" header="Task Name"></Column>
+                <Column field="status" header="Status"></Column>
+            </DataTable>
         </>
     }
 
     confirmDeleteTasks() {
-        if(this.selectedRows.length === 0) {
-            appGrowl.show({severity: 'info', summary: 'Select Row', detail: 'Select Task to delete.'});
-        }   else {
+        if (this.selectedRows.length === 0) {
+            appGrowl.show({ severity: 'info', summary: 'Select Row', detail: 'Select Task to delete.' });
+        } else {
             let dialog = {};
             dialog.type = "confirmation";
-            dialog.header= "Confirm to Delete Task(s)";
+            dialog.header = "Confirm to Delete Task(s)";
             dialog.detail = "Do you want to delete the selected Task(s)?";
+<<<<<<< HEAD
+            dialog.content = this.getTaskDeleteDialogContent;
+=======
             dialog.content = this.getTaskDialogContent;
-            dialog.actions = [{id: 'yes', title: 'Yes', callback: this.deleteTasks},
-            {id: 'no', title: 'No', callback: this.closeDialog}];
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+            dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.deleteTasks },
+            { id: 'no', title: 'No', callback: this.closeDialog }];
             dialog.onSubmit = this.deleteTasks;
             dialog.width = '55vw';
             dialog.showIcon = false;
-            this.setState({dialog: dialog, dialogVisible: true});
+            this.setState({ dialog: dialog, dialogVisible: true });
         }
     }
 
@@ -302,27 +355,159 @@ export class TaskList extends Component {
      */
     async deleteTasks() {
         let hasError = false;
-        for(const task of this.selectedRows) {
-            if(!await TaskService.deleteTask(task.tasktype, task.id)) {
+        for (const task of this.selectedRows) {
+            if (!await TaskService.deleteTask(task.tasktype, task.id)) {
                 hasError = true;
             }
         }
-        if(hasError){
-            appGrowl.show({severity: 'error', summary: 'error', detail: 'Error while deleting Task(s)'});
-            this.setState({dialogVisible: false});
-        }   else {
+        if (hasError) {
+            appGrowl.show({ severity: 'error', summary: 'error', detail: 'Error while deleting Task(s)' });
+            this.setState({ dialogVisible: false });
+        } else {
             this.selectedRows = [];
-            this.setState({dialogVisible: false});
+            this.setState({ dialogVisible: false });
             this.componentDidMount();
-            appGrowl.show({severity: 'success', summary: 'Success', detail: 'Task(s) deleted successfully'});
+            appGrowl.show({ severity: 'success', summary: 'Success', detail: 'Task(s) deleted successfully' });
+<<<<<<< HEAD
+        }
+    }
+
+    /**
+     * Prepare Task(s) details to show on confirmation dialog before cancelling
+     */
+    getTaskCancelConfirmContent() {
+        let selectedTasks = [], ignoredTasks = [];
+        for (const obj of this.selectedRows) {
+            if (this.TASK_END_STATUSES.indexOf(obj.status) < 0) {
+                selectedTasks.push({
+                    id: obj.id, suId: obj.schedulingUnitId, suName: obj.schedulingUnitName,
+                    taskId: obj.id, controlId: obj.subTaskID, taskName: obj.name, status: obj.status
+                });
+            }   else {
+                ignoredTasks.push({
+                    id: obj.id, suId: obj.schedulingUnitId, suName: obj.schedulingUnitName,
+                    taskId: obj.id, controlId: obj.subTaskID, taskName: obj.name, status: obj.status
+                });
+            }
+=======
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
         }
+        return <>
+            <div style={{marginTop: '1em'}}>
+                <b>Task(s) that can be cancelled</b>
+                <DataTable value={selectedTasks} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}>
+                    <Column field="suId" header="Scheduling Unit Id"></Column>
+                    <Column field="suName" header="Scheduling Unit Name"></Column>
+                    <Column field="taskId" header="Task Id"></Column>
+                    <Column field="controlId" header="Control Id"></Column>
+                    <Column field="taskName" header="Task Name"></Column>
+                    <Column field="status" header="Status"></Column>
+                </DataTable>
+            </div>
+            {ignoredTasks.length > 0 &&
+            <div style={{marginTop: '1em'}}>
+                <b>Task(s) that will be ignored</b>
+                <DataTable value={ignoredTasks} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}>
+                    <Column field="suId" header="Scheduling Unit Id"></Column>
+                    <Column field="suName" header="Scheduling Unit Name"></Column>
+                    <Column field="taskId" header="Task Id"></Column>
+                    <Column field="controlId" header="Control Id"></Column>
+                    <Column field="taskName" header="Task Name"></Column>
+                    <Column field="status" header="Status"></Column>
+                </DataTable>
+            </div>
+            }
+        </>
+    }
+
+    /**
+     * Prepare Task(s) details to show status of Task cancellationn
+     */
+    getTaskCancelStatusContent() {
+        let cancelledTasks = this.state.cancelledTasks;
+        return <>
+            <DataTable value={cancelledTasks} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}>
+                <Column field="suId" header="Scheduling Unit Id"></Column>
+                <Column field="suName" header="Scheduling Unit Name"></Column>
+                <Column field="taskId" header="Task Id"></Column>
+                <Column field="controlId" header="Control Id"></Column>
+                <Column field="taskName" header="Task Name"></Column>
+                <Column field="status" header="Status"></Column>
+            </DataTable>
+        </>
+    }
+
+    /**
+     * Function to get confirmation before cancelling all selected task blueprints if the task status is 
+     * not one of the end statuses. If no selected task is cancellable, show info to select a cancellable task.
+     * 
+     */
+    confirmCancelTasks() {
+        let selectedBlueprints = this.selectedRows.filter(task => {
+                                        return task.tasktype === 'Blueprint' && 
+                                        this.TASK_END_STATUSES.indexOf(task.status)<0});
+        if (selectedBlueprints.length === 0) {
+            appGrowl.show({ severity: 'info', summary: 'Select Row', 
+                            detail: 'Select atleast one cancellable Task Blueprint to cancel.' });
+        } else {
+            let dialog = this.state.dialog;
+            dialog.type = "confirmation";
+            dialog.header = "Confirm to Cancel Task(s)";
+            dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.cancelTasks },
+                                { id: 'no', title: 'No', callback: this.closeDialog }];
+            dialog.detail = "Cancelling the task means, it will no longer be executed / will be aborted. This action cannot be undone. Already finished/cancelled task(s) will be ignored. Do you want to proceed?";
+            dialog.content = this.getTaskCancelConfirmContent;
+            dialog.submit = this.cancelTasks;
+            dialog.width = '55vw';
+            dialog.showIcon = false;
+            this.setState({ dialog: dialog, dialogVisible: true });
+        }
+    }
+    
+    /**
+     * Function to cancel all selected task blueprints if the task status is not one of the end statuses
+     * and update their status on successful cancellation.
+     */
+    async cancelTasks() {
+        let tasks = this.state.tasks;
+        let selectedBlueprints = this.selectedRows.filter(task => {return task.tasktype === 'Blueprint'});
+        let cancelledTasks = []
+        for (const selectedTask of selectedBlueprints) {
+            if (this.TASK_END_STATUSES.indexOf(selectedTask.status) < 0) {
+                const cancelledTask = await TaskService.cancelTask(selectedTask.id);
+                let task = _.find(tasks, {'id': selectedTask.id, tasktype: 'Blueprint'});
+                if (cancelledTask) {
+                    task.status = cancelledTask.status;
+                }
+                cancelledTasks.push({
+                    id: task.id, suId: task.schedulingUnitId, suName: task.schedulingUnitName,
+                    taskId: task.id, controlId: task.subTaskID, taskName: task.name, 
+                    status: task.status.toLowerCase()==='cancelled'?'Cancelled': 'Error Occured'
+                });
+            }
+        }
+        let dialog = this.state.dialog;
+        dialog.type = "confirmation";
+        dialog.header = "Cancel Task(s) Status";
+        dialog.actions = [{ id: 'no', title: 'Ok', callback: this.closeDialog }];
+        dialog.detail = ""
+        dialog.content = this.getTaskCancelStatusContent;
+        dialog.submit = this.closeDialog;
+        dialog.width = '55vw';
+        dialog.showIcon = false;
+        this.selectedRows = [];
+        this.setState({ tasks: tasks, cancelledTasks: cancelledTasks, dialog: dialog, dialogVisible: true });
     }
 
     /**
      * Callback function to close the dialog prompted.
      */
     closeDialog() {
-        this.setState({dialogVisible: false});
+<<<<<<< HEAD
+        this.setState({ dialogVisible: false, cancelledTasks: [] });
+=======
+        this.setState({ dialogVisible: false });
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
     }
 
     onRowSelection(selectedRows) {
@@ -332,53 +517,61 @@ export class TaskList extends Component {
 
     render() {
         if (this.state.redirect) {
-            return <Redirect to={ {pathname: this.state.redirect} }></Redirect>
+            return <Redirect to={{ pathname: this.state.redirect }}></Redirect>
         }
 
         return (
             <React.Fragment>
+<<<<<<< HEAD
+                <PageHeader location={this.props.location} title={'Task - List'} actions={this.state.actions}/>
+                {this.state.isLoading ? <AppLoader /> :
+                    <>
+=======
                 <PageHeader location={this.props.location} title={'Task - List'} />
-                {this.state.isLoading? <AppLoader /> :
-                     <>
-                     <div className="delete-option">
-                        <div >
-                            <span className="p-float-label">
-                                <a href="#" onClick={this.confirmDeleteTasks}  title="Delete selected Task(s)">
-                                    <i class="fa fa-trash" aria-hidden="true" ></i>
-                                </a>
-                            </span>
-                        </div>                           
-                    </div>
-                    <ViewTable 
-                        data={this.state.tasks} 
-                        defaultcolumns={this.state.defaultcolumns}
-                        optionalcolumns={this.state.optionalcolumns}
-                        columnclassname={this.state.columnclassname}
-                        columnOrders={this.state.columnOrders}
-                        defaultSortColumn={this.state.defaultSortColumn}
-                        showaction="true"
-                        keyaccessor="id"
-                        paths={this.state.paths}
-                        unittest={this.state.unittest}
-                        tablename="scheduleunit_task_list"
-                        allowRowSelection={true}
-                        onRowSelection = {this.onRowSelection}
-                    />
-                </>
+                {this.state.isLoading ? <AppLoader /> :
+                    <>
+                        <div className="delete-option">
+                            <div >
+                                <span className="p-float-label">
+                                    <a href="#" onClick={this.confirmDeleteTasks} title="Delete selected Task(s)">
+                                        <i class="fa fa-trash" aria-hidden="true" ></i>
+                                    </a>
+                                </span>
+                            </div>
+                        </div>
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+                        <ViewTable
+                            data={this.state.tasks}
+                            defaultcolumns={this.state.defaultcolumns}
+                            optionalcolumns={this.state.optionalcolumns}
+                            columnclassname={this.state.columnclassname}
+                            columnOrders={this.state.columnOrders}
+                            defaultSortColumn={this.defaultSortColumn}
+                            showaction="true"
+                            keyaccessor="id"
+                            paths={this.state.paths}
+                            unittest={this.state.unittest}
+                            tablename="scheduleunit_task_list"
+                            allowRowSelection={true}
+                            onRowSelection={this.onRowSelection}
+                            lsKeySortColumn={this.lsKeySortColumn}
+                            toggleBySorting={(sortData) => this.toggleBySorting(sortData)}
+                            ignoreSorting={this.ignoreSorting}
+                        />
+                    </>
                 }
                 {this.state.showStatusLogs &&
-                    <Dialog header={`Status change logs - ${this.state.task?this.state.task.name:""}`} 
-                            visible={this.state.showStatusLogs} maximizable maximized={false} position="left" style={{ width: '50vw' }} 
-                            onHide={() => {this.setState({showStatusLogs: false})}}>
-                            <TaskStatusLogs taskId={this.state.task.id}></TaskStatusLogs>
+                    <Dialog header={`Status change logs - ${this.state.task ? this.state.task.name : ""}`}
+                        visible={this.state.showStatusLogs} maximizable maximized={false} position="left" style={{ width: '50vw' }}
+                        onHide={() => { this.setState({ showStatusLogs: false }) }}>
+                        <TaskStatusLogs taskId={this.state.task.id}></TaskStatusLogs>
                     </Dialog>
                 }
                 <CustomDialog type="confirmation" visible={this.state.dialogVisible}
                     header={this.state.dialog.header} message={this.state.dialog.detail} actions={this.state.dialog.actions}
                     content={this.state.dialog.content} width={this.state.dialog.width} showIcon={this.state.dialog.showIcon}
-                    onClose={this.closeDialog} onCancel={this.closeDialog} onSubmit={this.state.dialog.onSubmit}/>
+                    onClose={this.closeDialog} onCancel={this.closeDialog} onSubmit={this.state.dialog.onSubmit} />
             </React.Fragment>
         );
     }
-} 
- 
\ No newline at end of file
+}
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/view.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/view.js
index bde2f9d803f8bb2cc98f7fa7bb4a3bbe2aa11a1b..6427470c7d4e01cde703a45ac8e644467c7cf764 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/view.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/view.js
@@ -17,28 +17,27 @@ import { Column } from 'primereact/column';
 
 export class TaskView extends Component {
    // DATE_FORMAT = 'YYYY-MMM-DD HH:mm:ss';
+    TASK_NOT_STARTED_STATUSES = ['defined', 'schedulable', 'scheduled'];
+    TASK_ACTIVE_STATUSES = ['started', 'observing', 'observed', 'processing', 'processed', 'ingesting'];
+    TASK_END_STATUSES = ['finished', 'error', 'cancelled'];
+    
     constructor(props) {
         super(props);
         this.state = {
             isLoading: true,
             confirmDialogVisible: false,
-            hasBlueprint: true
+            hasBlueprint: true,
+            dialog: {}
         };
-        this.showIcon = false;
-        this.dialogType = "confirmation";
-        this.dialogHeader = "";
-        this.dialogMsg = "";
-        this.dialogContent = "";
-        this.callBackFunction = "";
-        this.dialogWidth = '40vw';
-        this.onClose = this.close;
-        this.onCancel =this.close;
 
         this.setEditorFunction = this.setEditorFunction.bind(this);
         this.deleteTask = this.deleteTask.bind(this);
-        this.showConfirmation = this.showConfirmation.bind(this);
-        this.close = this.close.bind(this);
-        this.getDialogContent = this.getDialogContent.bind(this);
+        this.showDeleteConfirmation = this.showDeleteConfirmation.bind(this);
+        this.closeDialog = this.closeDialog.bind(this);
+        this.getTaskDeleteDialogContent = this.getTaskDeleteDialogContent.bind(this);
+        this.showCancelConfirmation = this.showCancelConfirmation.bind(this);
+        this.cancelTask = this.cancelTask.bind(this);
+        
         
         if (this.props.match.params.id) {
             this.state.taskId  = this.props.match.params.id;
@@ -127,23 +126,22 @@ export class TaskView extends Component {
     /**
      * Show confirmation dialog
      */
-    showConfirmation() {
-        this.dialogType = "confirmation";
-        this.dialogHeader = "Confirm to Delete Task";
-        this.showIcon = false;
-        this.dialogMsg = "Do you want to delete this Task?";
-        this.dialogWidth = '55vw';
-        this.dialogContent = this.getDialogContent;
-        this.callBackFunction = this.deleteTask;
-        this.onClose = this.close;
-        this.onCancel =this.close;
-        this.setState({confirmDialogVisible: true});
+    showDeleteConfirmation() {
+        let dialog = this.state.dialog;
+        dialog.type = "confirmation";
+        dialog.header = "Confirm to Delete Task";
+        dialog.showIcon = false;
+        dialog.detail = "Do you want to delete this Task?";
+        dialog.width = '55vw';
+        dialog.content = this.getTaskDeleteDialogContent;
+        dialog.onSubmit = this.deleteTask;
+        this.setState({dialog: dialog, confirmDialogVisible: true});
     }
 
     /**
      * Prepare Task details to show on confirmation dialog
      */
-    getDialogContent() {
+    getTaskDeleteDialogContent() {
         let selectedTasks = [{suId: this.state.schedulingUnit.id, suName: this.state.schedulingUnit.name, taskId: this.state.task.id, 
             controlId: this.state.task.subTaskID, taskName: this.state.task.name, status: this.state.task.status}];
         return  <> 
@@ -158,7 +156,7 @@ export class TaskView extends Component {
                 </>
     }
 
-    close() {
+    closeDialog() {
         this.setState({confirmDialogVisible: false});
     }
 
@@ -185,6 +183,43 @@ export class TaskView extends Component {
         }
     }
 
+    /**
+     * Show confirmation dialog before cancelling the task.
+     */
+     showCancelConfirmation() {
+        let dialog = this.state.dialog;
+        dialog.type = "confirmation";
+        dialog.header = "Confirm to Cancel Task";
+        dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.cancelTask },
+                            { id: 'no', title: 'No', callback: this.closeDialog }];
+        if (this.TASK_NOT_STARTED_STATUSES.indexOf(this.state.task.status) >= 0) {
+            dialog.detail = "Cancelling this task means it will no longer be executed. This action cannot be undone. Do you want to proceed?";
+        }   else if (this.TASK_ACTIVE_STATUSES.indexOf(this.state.task.status) >= 0) {
+            dialog.detail = "Cancelling this task means it will be aborted. This action cannot be undone. Do you want to proceed?";
+        }
+        dialog.submit = this.cancelTask;
+        dialog.width = '40vw';
+        dialog.showIcon = true;
+        this.setState({ dialog: dialog, confirmDialogVisible: true });
+    }
+
+    /**
+     * Function to cancel the task and update its status.
+     */
+    async cancelTask() {
+        let task = this.state.task;
+        let cancelledTask = await TaskService.cancelTask(task.id);
+        if (!cancelledTask) {
+            appGrowl.show({ severity: 'error', summary: 'error', detail: 'Error while cancelling Scheduling Unit' });
+            this.setState({ dialogVisible: false });
+        } else {
+            task.status = cancelledTask.status;
+            let actions = this.state.actions;
+            appGrowl.show({ severity: 'success', summary: 'Success', detail: 'Scheduling Unit is cancelled successfully' });
+            this.setState({ confirmDialogVisible: false, task: task, actions: actions});
+        }
+    }
+
     render() {
         if (this.state.redirect) {
             return <Redirect to={ {pathname: this.state.redirect} }></Redirect>
@@ -212,9 +247,16 @@ export class TaskView extends Component {
         }   else {
             actions = [{    icon: 'fa-lock',
                             title: 'Cannot edit blueprint'}];
+            if (this.state.task) {
+                actions.push({icon: 'fa-ban', type: 'button', actOn: 'click', 
+                                    title: this.TASK_END_STATUSES.indexOf(this.state.task.status.toLowerCase())>=0?'Cannot Cancel Task':'Cancel Task', 
+                                    disabled:this.TASK_END_STATUSES.indexOf(this.state.task.status.toLowerCase())>=0, 
+                                    props: { callback: this.showCancelConfirmation }
+                                });
+            }
         }
         actions.push({icon: 'fa fa-trash',title:this.state.hasBlueprint? 'Cannot delete Draft when Blueprint exists':'Delete Task',  
-                        type: 'button',  disabled: this.state.hasBlueprint, actOn: 'click', props:{ callback: this.showConfirmation}});
+                        type: 'button',  disabled: this.state.hasBlueprint, actOn: 'click', props:{ callback: this.showDeleteConfirmation}});
         actions.push({  icon: 'fa-window-close', link: this.props.history.goBack,
                         title:'Click to Close Task', props : { pathname:'/schedulingunit' }});
 
@@ -283,8 +325,10 @@ export class TaskView extends Component {
                             <span className="col-lg-4 col-md-4 col-sm-12">{this.state.task.end_time?moment(this.state.task.end_time,moment.ISO_8601).format(UIConstants.CALENDAR_DATETIME_FORMAT):""}</span>
                         </div>
                         <div className="p-grid">
-                            <label className="col-lg-2 col-md-2 col-sm-12">Tags</label>
-                            <Chips className="col-lg-4 col-md-4 col-sm-12 chips-readonly" disabled value={this.state.task.tags}></Chips>
+                            {/* <label className="col-lg-2 col-md-2 col-sm-12">Tags</label>
+                            <Chips className="col-lg-4 col-md-4 col-sm-12 chips-readonly" disabled value={this.state.task.tags}></Chips> */}
+                            <label className="col-lg-2 col-md-2 col-sm-12">Status</label>
+                            <span className="col-lg-4 col-md-4 col-sm-12">{this.state.task.status}</span>
                             {this.state.schedulingUnit &&
                             <>
                                 <label className="col-lg-2 col-md-2 col-sm-12">Scheduling Unit</label>
@@ -339,10 +383,10 @@ export class TaskView extends Component {
                         </div>
                     </React.Fragment>
                 }
-                 <CustomDialog type={this.dialogType} visible={this.state.confirmDialogVisible} width={this.dialogWidth}
-                    header={this.dialogHeader} message={this.dialogMsg} 
-                    content={this.dialogContent} onClose={this.onClose} onCancel={this.onCancel} onSubmit={this.callBackFunction}
-                    showIcon={this.showIcon} actions={this.actions}>
+                 <CustomDialog type="confirmation" visible={this.state.confirmDialogVisible} width={this.state.dialog.width}
+                    header={this.state.dialog.header} message={this.state.dialog.detail} 
+                    content={this.state.dialog.content} onClose={this.closeDialog} onCancel={this.closeDialog} onSubmit={this.callBackFunction}
+                    showIcon={this.state.dialog.showIcon} actions={this.state.dialog.actions}>
                 </CustomDialog>
             </React.Fragment>
         );
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/view.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/view.js
index 4d03a9eebe5993937c24710d778b32826936de4c..6c136afd207fc43f6659bd614223b774a5c10ad9 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/view.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/view.js
@@ -1,4 +1,4 @@
-import React, {Component} from 'react';
+import React, { Component } from 'react';
 import { Redirect } from 'react-router-dom/cjs/react-router-dom.min';
 import moment from 'moment';
 import _ from 'lodash';
@@ -32,33 +32,43 @@ import { Button } from 'primereact/button';
 
 
 // Color constant for SU status
-const SU_STATUS_COLORS = { "ERROR": "FF0000", "CANCELLED": "#00FF00", "DEFINED": "#00BCD4", 
-                            "SCHEDULABLE":"#0000FF", "SCHEDULED": "#abc", "OBSERVING": "#bcd",
-                            "OBSERVED": "#cde", "PROCESSING": "#cddc39", "PROCESSED": "#fed",
-                            "INGESTING": "#edc", "FINISHED": "#47d53d"};
+const SU_STATUS_COLORS = {
+    "ERROR": "FF0000", "CANCELLED": "#00FF00", "DEFINED": "#00BCD4",
+    "SCHEDULABLE": "#0000FF", "SCHEDULED": "#abc", "OBSERVING": "#bcd",
+    "OBSERVED": "#cde", "PROCESSING": "#cddc39", "PROCESSED": "#fed",
+    "INGESTING": "#edc", "FINISHED": "#47d53d"
+};
 
 // Color constant for Task status
-const TASK_STATUS_COLORS = { "ERROR": "FF0000", "CANCELLED": "#00FF00", "DEFINED": "#00BCD4", 
-                            "SCHEDULABLE":"#0000FF", "SCHEDULED": "#abc", "STARTED": "#bcd",
-                            "OBSERVED": "#cde", "FINISHED": "#47d53d"};
+const TASK_STATUS_COLORS = {
+    "ERROR": "FF0000", "CANCELLED": "#00FF00", "DEFINED": "#00BCD4",
+    "SCHEDULABLE": "#0000FF", "SCHEDULED": "#abc", "STARTED": "#bcd",
+    "OBSERVED": "#cde", "FINISHED": "#47d53d"
+};
 
-const RESERVATION_COLORS = {"true-true":{bgColor:"lightgrey", color:"#585859"}, "true-false":{bgColor:'#585859', color:"white"},
-                            "false-true":{bgColor:"#9b9999", color:"white"}, "false-false":{bgColor:"black", color:"white"}};
+const RESERVATION_COLORS = {
+    "true-true": { bgColor: "lightgrey", color: "#585859" }, "true-false": { bgColor: '#585859', color: "white" },
+    "false-true": { bgColor: "#9b9999", color: "white" }, "false-false": { bgColor: "black", color: "white" }
+};
 
 /**
  * Scheduling Unit timeline view component to view SU List and timeline
  */
 export class TimelineView extends Component {
 
+    lsKeySortColumn = 'SortDataTimelineView';
+    defaultSortColumn = [];
+
     constructor(props) {
         super(props);
+        this.setToggleBySorting();
         this.state = {
             isLoading: true,
             suBlueprints: [],       // Scheduling Unit Blueprints
             suDrafts: [],           // Scheduling Unit Drafts
             suBlueprintList: [],    // SU Blueprints filtered to view
-            group:[],               // Timeline group from scheduling unit draft name
-            items:[],               // Timeline items from scheduling unit blueprints grouped by scheduling unit draft
+            group: [],               // Timeline group from scheduling unit draft name
+            items: [],               // Timeline items from scheduling unit blueprints grouped by scheduling unit draft
             isSUDetsVisible: false,
             isTaskDetsVisible: false,
             canExtendSUList: true,
@@ -66,14 +76,16 @@ export class TimelineView extends Component {
             isSUListVisible: true,
             selectedItem: null,
             mouseOverItem: null,
-            suTaskList:[],
+            suTaskList: [],
             isSummaryLoading: false,
             stationGroup: [],
             selectedStationGroup: [], //Station Group(core,international,remote)
             reservationFilter: null,
             showSUs: true,
             showTasks: false,
-            groupByProject: false
+            groupByProject: false,
+            taskTypes: [],
+            selectedTaskTypes: ['observation']
         }
         this.STATUS_BEFORE_SCHEDULED = ['defining', 'defined', 'schedulable'];  // Statuses before scheduled to get station_group
         this.allStationsGroup = [];
@@ -81,10 +93,10 @@ export class TimelineView extends Component {
         this.reservations = [];
         this.reservationReasons = [];
         this.optionsMenu = React.createRef();
-        this.menuOptions = [ {label:'Add Reservation', icon: "fa fa-", command: () => {this.selectOptionMenu('Add Reservation')}}, 
-                            {label:'Reservation List', icon: "fa fa-", command: () => {this.selectOptionMenu('Reservation List')}},
-                           ];
-        
+        this.menuOptions = [{ label: 'Add Reservation', icon: "fa fa-", command: () => { this.selectOptionMenu('Add Reservation') } },
+        { label: 'Reservation List', icon: "fa fa-", command: () => { this.selectOptionMenu('Reservation List') } },
+        ];
+
         this.showOptionMenu = this.showOptionMenu.bind(this);
         this.selectOptionMenu = this.selectOptionMenu.bind(this);
         this.onItemClick = this.onItemClick.bind(this);
@@ -102,22 +114,25 @@ export class TimelineView extends Component {
         this.addNewData = this.addNewData.bind(this);
         this.updateExistingData = this.updateExistingData.bind(this);
         this.updateSchedulingUnit = this.updateSchedulingUnit.bind(this);
-         this.setSelectedStationGroup = this.setSelectedStationGroup.bind(this);
+        this.setSelectedStationGroup = this.setSelectedStationGroup.bind(this);
         this.getStationsByGroupName = this.getStationsByGroupName.bind(this);
     }
 
     async componentDidMount() {
+        this.setToggleBySorting();
         this.setState({ loader: true });
+        let taskTypes = []
+        TaskService.getTaskTypes().then(results => {taskTypes = results});
         // Fetch all details from server and prepare data to pass to timeline and table components
-        const promises = [  ProjectService.getProjectList(), 
-                            ScheduleService.getSchedulingUnitsExtended('blueprint'),
-                            ScheduleService.getSchedulingUnitDraft(),
-                            ScheduleService.getSchedulingSets(),
-                            UtilService.getUTC(),
-                            ScheduleService.getStations('All'),
-                            TaskService.getSubtaskTemplates(),
-                            ScheduleService.getMainGroupStations()];
-        Promise.all(promises).then(async(responses) => {
+        const promises = [ProjectService.getProjectList(),
+        ScheduleService.getSchedulingUnitsExtended('blueprint'),
+        ScheduleService.getSchedulingUnitDraft(),
+        ScheduleService.getSchedulingSets(),
+        UtilService.getUTC(),
+        ScheduleService.getStations('All'),
+        TaskService.getSubtaskTemplates(),
+        ScheduleService.getMainGroupStations()];
+        Promise.all(promises).then(async (responses) => {
             this.subtaskTemplates = responses[6];
             const projects = responses[0];
             const suBlueprints = _.sortBy(responses[1], 'name');
@@ -129,11 +144,11 @@ export class TimelineView extends Component {
             const defaultEndTime = currentUTC.clone().add(24, 'hours');         // Default end time, this should be updated if default view is changed.
             let suList = [];
             for (const suDraft of suDrafts) {
-                const suSet = suSets.find((suSet) => { return suDraft.scheduling_set_id===suSet.id});
-                const project = projects.find((project) => { return suSet.project_id===project.name});
+                const suSet = suSets.find((suSet) => { return suDraft.scheduling_set_id === suSet.id });
+                const project = projects.find((project) => { return suSet.project_id === project.name });
                 if (suDraft.scheduling_unit_blueprints.length > 0) {
                     for (const suBlueprintId of suDraft.scheduling_unit_blueprints_ids) {
-                        const suBlueprint = _.find(suBlueprints, {'id': suBlueprintId});
+                        const suBlueprint = _.find(suBlueprints, { 'id': suBlueprintId });
                         suBlueprint['actionpath'] = `/schedulingunit/view/blueprint/${suBlueprintId}`;
                         suBlueprint.suDraft = suDraft;
                         suBlueprint.project = project.name;
@@ -142,15 +157,17 @@ export class TimelineView extends Component {
                         suBlueprint.duration = UnitConverter.getSecsToHHmmss(suBlueprint.duration);
                         suBlueprint.tasks = suBlueprint.task_blueprints;
                         // Select only blueprints with start_time and stop_time in the default time limit
-                        if (suBlueprint.start_time && 
+                        if (suBlueprint.start_time &&
                             ((moment.utc(suBlueprint.start_time).isBetween(defaultStartTime, defaultEndTime) ||
-                             moment.utc(suBlueprint.stop_time).isBetween(defaultStartTime, defaultEndTime))	 
-                             || (moment.utc(suBlueprint.start_time).isSameOrBefore(defaultStartTime) && 
-                                 moment.utc(suBlueprint.stop_time).isSameOrAfter(defaultEndTime)))) {
+                                moment.utc(suBlueprint.stop_time).isBetween(defaultStartTime, defaultEndTime))
+                                || (moment.utc(suBlueprint.start_time).isSameOrBefore(defaultStartTime) &&
+                                    moment.utc(suBlueprint.stop_time).isSameOrAfter(defaultEndTime)))) {
                             items.push(this.getTimelineItem(suBlueprint));
-                            if (!_.find(group, {'id': suDraft.id})) {
-                                group.push({'id': this.state.groupByProject?suBlueprint.project:suDraft.id, 
-                                            title: this.state.groupByProject?suBlueprint.project:suDraft.name});
+                            if (!_.find(group, { 'id': suDraft.id })) {
+                                group.push({
+                                    'id': this.state.groupByProject ? suBlueprint.project : suDraft.id,
+                                    title: this.state.groupByProject ? suBlueprint.project : suDraft.name
+                                });
                             }
                             suList.push(suBlueprint);
                         }
@@ -159,7 +176,7 @@ export class TimelineView extends Component {
                                 const template = _.find(this.subtaskTemplates, ['id', subtask.specifications_template_id]);
                                 return (template && template.name.indexOf('control')) > 0;
                             });
-                            task.subTaskID = subTaskIds.length ? subTaskIds[0].id : ''; 
+                            task.subTaskID = subTaskIds.length ? subTaskIds[0].id : '';
                             if (task.specifications_template.type_value.toLowerCase() === "observation") {
                                 task.antenna_set = task.specifications_doc.antenna_set;
                                 task.band = task.specifications_doc.filter;
@@ -169,18 +186,18 @@ export class TimelineView extends Component {
                 }
             }
             for (const station of responses[5]['stations']) {
-                this.allStationsGroup.push({id: station, title: station});
+                this.allStationsGroup.push({ id: station, title: station });
             }
             // Fetch Reservations and keep ready to use in station view
             UtilService.getReservations().then(reservations => {
                 this.reservations = reservations;
             });
             UtilService.getReservationTemplates().then(templates => {
-                this.reservationTemplate = templates.length>0?templates[0]:null;
+                this.reservationTemplate = templates.length > 0 ? templates[0] : null;
                 if (this.reservationTemplate) {
                     let reasons = this.reservationTemplate.schema.properties.activity.properties.type.enum;
                     for (const reason of reasons) {
-                        this.reservationReasons.push({name: reason});
+                        this.reservationReasons.push({ name: reason });
                     }
                 }
             });
@@ -188,19 +205,43 @@ export class TimelineView extends Component {
             ScheduleService.getSchedulingConstraintTemplates()
                 .then(suConstraintTemplates => {
                     this.suConstraintTemplates = suConstraintTemplates;
+                });
+            this.setState({
+                suBlueprints: suBlueprints, suDrafts: suDrafts, group: group, suSets: suSets,
+<<<<<<< HEAD
+                loader: false, taskTypes: taskTypes,
+=======
+                loader: false,
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+                projects: projects, suBlueprintList: suList,
+                items: items, currentUTC: currentUTC, isLoading: false,
+                currentStartTime: defaultStartTime, currentEndTime: defaultEndTime
             });
-            this.setState({suBlueprints: suBlueprints, suDrafts: suDrafts, group: group, suSets: suSets,
-                            loader: false,
-                            projects: projects, suBlueprintList: suList,
-                            items: items, currentUTC: currentUTC, isLoading: false,
-                            currentStartTime: defaultStartTime, currentEndTime: defaultEndTime});
             this.mainStationGroups = responses[7];
             this.mainStationGroupOptions = Object.keys(responses[7]).map(value => ({ value }));
         });
     }
 
+    toggleBySorting = (sortData) => {
+        UtilService.localStore({ type: 'set', key: this.lsKeySortColumn, value: sortData });
+    }
+
+    setToggleBySorting() {       
+        let sortData = UtilService.localStore({ type: 'get', key: this.lsKeySortColumn });
+        if (sortData) {
+            if (Object.prototype.toString.call(sortData) === '[object Array]') {
+                this.defaultSortColumn = sortData;
+            }
+            else {
+                this.defaultSortColumn = [{ ...sortData }];
+            }
+        }
+        this.defaultSortColumn = this.defaultSortColumn || [];
+        UtilService.localStore({ type: 'set', key: this.lsKeySortColumn, value: [...this.defaultSortColumn] });
+    }
+
     setSelectedStationGroup(value) {
-        this.setState({ selectedStationGroup: value});
+        this.setState({ selectedStationGroup: value });
     }
 
     /**
@@ -211,26 +252,28 @@ export class TimelineView extends Component {
         let antennaSet = "";
         for (let task of suBlueprint.tasks) {
             if (task.specifications_template.type_value.toLowerCase() === "observation"
-                    && task.specifications_doc.antenna_set) {
+                && task.specifications_doc.antenna_set) {
                 antennaSet = task.specifications_doc.antenna_set;
             }
         }
-        let item = { id: suBlueprint.id, 
-            group: this.state.groupByProject?suBlueprint.project:suBlueprint.suDraft.id,
+        let item = {
+            id: suBlueprint.id,
+            group: this.state.groupByProject ? suBlueprint.project : suBlueprint.suDraft.id,
             //title: `${suBlueprint.project} - ${suBlueprint.suDraft.name} - ${(suBlueprint.durationInSec/3600).toFixed(2)}Hrs`,
             title: "",
             project: suBlueprint.project, type: 'SCHEDULE',
             name: suBlueprint.suDraft.name,
-            band: antennaSet?antennaSet.split("_")[0]:"",
+            band: antennaSet ? antennaSet.split("_")[0] : "",
             antennaSet: antennaSet,
             scheduleMethod: suBlueprint.suDraft.scheduling_constraints_doc.scheduler,
-            duration: suBlueprint.durationInSec?`${(suBlueprint.durationInSec/3600).toFixed(2)}Hrs`:"",
+            duration: suBlueprint.durationInSec ? `${(suBlueprint.durationInSec / 3600).toFixed(2)}Hrs` : "",
             start_time: moment.utc(suBlueprint.start_time),
             end_time: moment.utc(suBlueprint.stop_time),
-            bgColor: suBlueprint.status? SU_STATUS_COLORS[suBlueprint.status.toUpperCase()]:"#2196f3",
+            bgColor: suBlueprint.status ? SU_STATUS_COLORS[suBlueprint.status.toUpperCase()] : "#2196f3",
             // selectedBgColor: suBlueprint.status? SU_STATUS_COLORS[suBlueprint.status.toUpperCase()]:"#2196f3"}; 
             selectedBgColor: "none",
-            status: suBlueprint.status.toLowerCase()};
+            status: suBlueprint.status.toLowerCase()
+        };
         return item;
     }
 
@@ -244,43 +287,58 @@ export class TimelineView extends Component {
             let items = [], itemGroup = [];
             const subtaskTemplates = this.subtaskTemplates;
             for (let task of suBlueprint.tasks) {
-                if (task.specifications_template.type_value.toLowerCase() === "observation" && task.start_time && task.stop_time) {
+                if (this.state.selectedTaskTypes.indexOf(task.task_type)>=0 && task.start_time && task.stop_time) {
                     const antennaSet = task.specifications_doc.antenna_set;
                     const start_time = moment.utc(task.start_time);
                     const end_time = moment.utc(task.stop_time);
                     if ((start_time.isBetween(startTime, endTime) ||
-                         end_time.isBetween(startTime, endTime))	 
-                         || (start_time.isSameOrBefore(startTime) && end_time.isSameOrAfter(endTime))) {
+                        end_time.isBetween(startTime, endTime))
+                        || (start_time.isSameOrBefore(startTime) && end_time.isSameOrAfter(endTime))) {
                         const subTaskIds = task.subtasks.filter(subtask => {
-                                                const template = _.find(subtaskTemplates, ['id', subtask.specifications_template_id]);
-                                                return (template && template.name.indexOf('control')) > 0;
+                            const template = _.find(subtaskTemplates, ['id', subtask.specifications_template_id]);
+                            return (template && template.name.indexOf('control')) > 0;
                         });
-                        const controlId = subTaskIds.length>0 ? subTaskIds[0].id : ''; 
-                        let item = { id: `${suBlueprint.id}_${task.id}`, 
-                                    suId: suBlueprint.id,
-                                    taskId: task.id,
-                                    controlId: controlId,
-                                    group: `${this.state.groupByProject?suBlueprint.project:suBlueprint.suDraft.id}_${this.state.groupByProject?'observations':task.draft_id}`,
-                                    // group: `${suBlueprint.suDraft.id}_Tasks`,    // For single row task grouping
-                                    title: '',
-                                    project: suBlueprint.project, type: 'TASK',
-                                    name: task.name,
-                                    typeValue:task.specifications_template.type_value,
-                                    band: antennaSet?antennaSet.split("_")[0]:"",
-                                    antennaSet: antennaSet?antennaSet:"",
-                                    scheduleMethod: suBlueprint.suDraft.scheduling_constraints_doc.scheduler,
-                                    duration: `${(end_time.diff(start_time, 'seconds')/3600).toFixed(2)}Hrs`,
-                                    start_time: start_time,
-                                    end_time: end_time,
-                                    bgColor: task.status? TASK_STATUS_COLORS[task.status.toUpperCase()]:"#2196f3",
-                                    selectedBgColor: "none",
-                                    status: task.status.toLowerCase()};
+                        const controlId = subTaskIds.length > 0 ? subTaskIds[0].id : '';
+                        let item = {
+                            id: `${suBlueprint.id}_${task.id}`,
+                            suId: suBlueprint.id,
+                            taskId: task.id,
+                            controlId: controlId,
+<<<<<<< HEAD
+                            group: `${this.state.groupByProject ? suBlueprint.project : suBlueprint.suDraft.id}_${this.state.groupByProject ? task.task_type : task.draft_id}`,
+=======
+                            group: `${this.state.groupByProject ? suBlueprint.project : suBlueprint.suDraft.id}_${this.state.groupByProject ? 'observations' : task.draft_id}`,
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+                            // group: `${suBlueprint.suDraft.id}_Tasks`,    // For single row task grouping
+                            title: '',
+                            project: suBlueprint.project, type: 'TASK',
+                            name: task.name,
+                            typeValue: task.specifications_template.type_value,
+                            band: antennaSet ? antennaSet.split("_")[0] : "",
+                            antennaSet: antennaSet ? antennaSet : "",
+                            scheduleMethod: suBlueprint.suDraft.scheduling_constraints_doc.scheduler,
+                            duration: `${(end_time.diff(start_time, 'seconds') / 3600).toFixed(2)}Hrs`,
+                            start_time: start_time,
+                            end_time: end_time,
+                            bgColor: task.status ? TASK_STATUS_COLORS[task.status.toUpperCase()] : "#2196f3",
+                            selectedBgColor: "none",
+                            status: task.status.toLowerCase()
+                        };
                         items.push(item);
                         if (!_.find(itemGroup, ['id', `${suBlueprint.suDraft.id}_${task.draft_id}`])) {
-                            itemGroup.push({'id': `${this.state.groupByProject?suBlueprint.project:suBlueprint.suDraft.id}_${this.state.groupByProject?'observations':task.draft_id}`, 
-                                            parent: this.state.groupByProject?suBlueprint.project:suBlueprint.suDraft.id, 
-                                            start: start_time, 
-                                            title: `${!this.state.showSUs?(this.state.groupByProject?suBlueprint.project:suBlueprint.suDraft.name):""} -- ${this.state.groupByProject?'observations':task.name}`});
+                            itemGroup.push({
+<<<<<<< HEAD
+                                'id': `${this.state.groupByProject ? suBlueprint.project : suBlueprint.suDraft.id}_${this.state.groupByProject ? task.task_type : task.draft_id}`,
+                                parent: this.state.groupByProject ? suBlueprint.project : suBlueprint.suDraft.id,
+                                start: start_time,
+                                title: `${!this.state.showSUs ? (this.state.groupByProject ? suBlueprint.project : suBlueprint.suDraft.name) : ""} -- ${this.state.groupByProject ? task.task_type : task.name}`
+=======
+                                'id': `${this.state.groupByProject ? suBlueprint.project : suBlueprint.suDraft.id}_${this.state.groupByProject ? 'observations' : task.draft_id}`,
+                                parent: this.state.groupByProject ? suBlueprint.project : suBlueprint.suDraft.id,
+                                start: start_time,
+                                title: `${!this.state.showSUs ? (this.state.groupByProject ? suBlueprint.project : suBlueprint.suDraft.name) : ""} -- ${this.state.groupByProject ? 'observations' : task.name}`
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+                            });
                         }
                         /* >>>>>> If all tasks should be shown in single row remove the above 2 lines and uncomment these lines
                         if (!_.find(itemGroup, ['id', `${suBlueprint.suDraft.id}_Tasks`])) {
@@ -302,11 +360,11 @@ export class TimelineView extends Component {
      * @param {Object} item 
      */
     onItemClick(item) {
-        if (item.type === "SCHEDULE") { 
+        if (item.type === "SCHEDULE") {
             this.showSUSummary(item);
-        }   else if (item.type === "RESERVATION") {
+        } else if (item.type === "RESERVATION") {
             this.showReservationSummary(item);
-        }   else {
+        } else {
             this.showTaskSummary(item);
         }
     }
@@ -316,22 +374,26 @@ export class TimelineView extends Component {
      * @param {Object} item - Timeline SU item object.
      */
     showSUSummary(item) {
-        if (this.state.isSUDetsVisible && item.id===this.state.selectedItem.id) {
+        if (this.state.isSUDetsVisible && item.id === this.state.selectedItem.id) {
             this.closeSUDets();
-        }   else {
-            const fetchDetails = !this.state.selectedItem || item.id!==this.state.selectedItem.id
-            this.setState({selectedItem: item, isSUDetsVisible: true, isTaskDetsVisible: false,
+        } else {
+            const fetchDetails = !this.state.selectedItem || item.id !== this.state.selectedItem.id
+            this.setState({
+                selectedItem: item, isSUDetsVisible: true, isTaskDetsVisible: false,
                 isSummaryLoading: fetchDetails,
-                suTaskList: !fetchDetails?this.state.suTaskList:[],
-                canExtendSUList: false, canShrinkSUList:false});
+                suTaskList: !fetchDetails ? this.state.suTaskList : [],
+                canExtendSUList: false, canShrinkSUList: false
+            });
             if (fetchDetails) {
-                const suBlueprint = _.find(this.state.suBlueprints, {id: (this.state.stationView?parseInt(item.id.split('-')[0]):item.id)});
-                const suConstraintTemplate = _.find(this.suConstraintTemplates, {id: suBlueprint.suDraft.scheduling_constraints_template_id});
+                const suBlueprint = _.find(this.state.suBlueprints, { id: (this.state.stationView ? parseInt(item.id.split('-')[0]) : item.id) });
+                const suConstraintTemplate = _.find(this.suConstraintTemplates, { id: suBlueprint.suDraft.scheduling_constraints_template_id });
                 /* If tasks are not loaded on component mounting fetch from API */
                 if (suBlueprint.tasks) {
-                    this.setState({suTaskList: _.sortBy(suBlueprint.tasks, "id"), suConstraintTemplate: suConstraintTemplate,
-                                    stationGroup: this.getSUStations(suBlueprint), isSummaryLoading: false});
-                }   else {
+                    this.setState({
+                        suTaskList: _.sortBy(suBlueprint.tasks, "id"), suConstraintTemplate: suConstraintTemplate,
+                        stationGroup: this.getSUStations(suBlueprint), isSummaryLoading: false
+                    });
+                } else {
                     ScheduleService.getTaskBPWithSubtaskTemplateOfSU(suBlueprint)
                         .then(taskList => {
                             for (let task of taskList) {
@@ -341,14 +403,16 @@ export class TimelineView extends Component {
                                     const template = _.find(this.subtaskTemplates, ['id', subtask.specifications_template_id]);
                                     return (template && template.name.indexOf('control')) > 0;
                                 });
-                                task.subTaskID = subTaskIds.length ? subTaskIds[0].id : ''; 
+                                task.subTaskID = subTaskIds.length ? subTaskIds[0].id : '';
                                 if (task.specifications_template.type_value.toLowerCase() === "observation") {
                                     task.antenna_set = task.specifications_doc.antenna_set;
                                     task.band = task.specifications_doc.filter;
                                 }
                             }
-                            this.setState({suTaskList: _.sortBy(taskList, "id"), isSummaryLoading: false,
-                                            stationGroup: this.getSUStations(suBlueprint)});
+                            this.setState({
+                                suTaskList: _.sortBy(taskList, "id"), isSummaryLoading: false,
+                                stationGroup: this.getSUStations(suBlueprint)
+                            });
                         });
                 }
                 // Get the scheduling constraint template of the selected SU block
@@ -365,7 +429,7 @@ export class TimelineView extends Component {
      * @param {Object} item 
      */
     showReservationSummary(item) {
-        this.setState({selectedItem: item, isReservDetsVisible: true, isSUDetsVisible: false});
+        this.setState({ selectedItem: item, isReservDetsVisible: true, isSUDetsVisible: false });
     }
 
     /**
@@ -373,14 +437,14 @@ export class TimelineView extends Component {
      * @param {Object} item - Timeline task item object 
      */
     showTaskSummary(item) {
-        this.setState({isTaskDetsVisible: !this.state.isTaskDetsVisible, isSUDetsVisible: false});
+        this.setState({ isTaskDetsVisible: !this.state.isTaskDetsVisible, isSUDetsVisible: false });
     }
 
     /**
      * Closes the SU details section
      */
     closeSUDets() {
-        this.setState({isSUDetsVisible: false, isReservDetsVisible: false, isTaskDetsVisible: false, canExtendSUList: true, canShrinkSUList: false});
+        this.setState({ isSUDetsVisible: false, isReservDetsVisible: false, isTaskDetsVisible: false, canExtendSUList: true, canShrinkSUList: false });
     }
 
     /**
@@ -398,10 +462,10 @@ export class TimelineView extends Component {
      */
     onItemMouseOver(evt, item) {
         if (item.type === "SCHEDULE" || item.type === "TASK") {
-            const itemSU = _.find(this.state.suBlueprints, {id: (item.suId?item.suId:item.id)});
+            const itemSU = _.find(this.state.suBlueprints, { id: (item.suId ? item.suId : item.id) });
             const itemStations = this.getSUStations(itemSU);
             const itemStationGroups = this.groupSUStations(itemStations);
-            item.stations = {groups: "", counts: ""};
+            item.stations = { groups: "", counts: "" };
             item.suName = itemSU.name;
             for (const stationgroup of _.keys(itemStationGroups)) {
                 let groups = item.stations.groups;
@@ -411,13 +475,13 @@ export class TimelineView extends Component {
                     counts = counts.concat("/");
                 }
                 // Get station group 1st character and append 'S' to get CS,RS,IS 
-                groups = groups.concat(stationgroup.substring(0,1).concat('S'));
+                groups = groups.concat(stationgroup.substring(0, 1).concat('S'));
                 counts = counts.concat(itemStationGroups[stationgroup].length);
                 item.stations.groups = groups;
                 item.stations.counts = counts;
             }
-        }   else {
-            const reservation = _.find(this.reservations, {'id': parseInt(item.id.split("-")[1])});
+        } else {
+            const reservation = _.find(this.reservations, { 'id': parseInt(item.id.split("-")[1]) });
             const reservStations = reservation.specifications_doc.resources.stations;
             // const reservStationGroups = this.groupSUStations(reservStations);
             item.name = reservation.name;
@@ -427,7 +491,7 @@ export class TimelineView extends Component {
             item.planned = reservation.specifications_doc.activity.planned;
         }
         this.popOver.toggle(evt);
-        this.setState({mouseOverItem: item});
+        this.setState({ mouseOverItem: item });
     }
 
     /**
@@ -437,7 +501,7 @@ export class TimelineView extends Component {
     groupSUStations(stationList) {
         let suStationGroups = {};
         for (const group in this.mainStationGroups) {
-            suStationGroups[group] = _.intersection(this.mainStationGroups[group],stationList);
+            suStationGroups[group] = _.intersection(this.mainStationGroups[group], stationList);
         }
         return suStationGroups;
     }
@@ -448,27 +512,29 @@ export class TimelineView extends Component {
      * @param {moment} endTime 
      */
     async dateRangeCallback(startTime, endTime) {
-        let suBlueprintList = [], group=[], items = [];
+        let suBlueprintList = [], group = [], items = [];
         if (startTime && endTime) {
             for (const suBlueprint of this.state.suBlueprints) {
-                if (moment.utc(suBlueprint.start_time).isBetween(startTime, endTime) 
+                if (moment.utc(suBlueprint.start_time).isBetween(startTime, endTime)
                     || moment.utc(suBlueprint.stop_time).isBetween(startTime, endTime)
-                    || (moment.utc(suBlueprint.start_time).isSameOrBefore(startTime) && 
-                         moment.utc(suBlueprint.stop_time).isSameOrAfter(endTime))) {
+                    || (moment.utc(suBlueprint.start_time).isSameOrBefore(startTime) &&
+                        moment.utc(suBlueprint.stop_time).isSameOrAfter(endTime))) {
                     // Get timeline item for station view noramlly and in timeline view only if SU to be shown
-                    let timelineItem = (this.state.showSUs || this.state.stationView)?this.getTimelineItem(suBlueprint):null;
+                    let timelineItem = (this.state.showSUs || this.state.stationView) ? this.getTimelineItem(suBlueprint) : null;
                     if (this.state.stationView) {
                         this.getStationItemGroups(suBlueprint, timelineItem, this.allStationsGroup, items);
-                    }   else {
+                    } else {
                         // Add timeline SU item
                         if (timelineItem) {
                             items.push(timelineItem);
-                            if (!_.find(group, {'id': suBlueprint.suDraft.id})) {
+                            if (!_.find(group, { 'id': suBlueprint.suDraft.id })) {
                                 /* parent and start properties are added to order and display task rows below the corresponding SU row */
-                                group.push({'id': this.state.groupByProject?suBlueprint.project:suBlueprint.suDraft.id, 
-                                            parent: this.state.groupByProject?suBlueprint.project:suBlueprint.suDraft.id, 
-                                            start: moment.utc("1900-01-01", "YYYY-MM-DD"), 
-                                            title: this.state.groupByProject?suBlueprint.project:suBlueprint.suDraft.name});
+                                group.push({
+                                    'id': this.state.groupByProject ? suBlueprint.project : suBlueprint.suDraft.id,
+                                    parent: this.state.groupByProject ? suBlueprint.project : suBlueprint.suDraft.id,
+                                    start: moment.utc("1900-01-01", "YYYY-MM-DD"),
+                                    title: this.state.groupByProject ? suBlueprint.project : suBlueprint.suDraft.name
+                                });
                             }
                         }
                         // Add task item only in timeline view and when show task is enabled
@@ -479,24 +545,26 @@ export class TimelineView extends Component {
                         }
                     }
                     suBlueprintList.push(suBlueprint);
-                } 
+                }
             }
             if (this.state.stationView) {
                 items = this.addStationReservations(items, startTime, endTime);
             }
-        }   else {
+        } else {
             suBlueprintList = _.clone(this.state.suBlueprints);
             group = this.state.group;
             items = this.state.items;
         }
-        
-        this.setState({suBlueprintList: _.filter(suBlueprintList, (suBlueprint) => {return suBlueprint.start_time!=null}),
-                        currentStartTime: startTime, currentEndTime: endTime});
+
+        this.setState({
+            suBlueprintList: _.filter(suBlueprintList, (suBlueprint) => { return suBlueprint.start_time != null }),
+            currentStartTime: startTime, currentEndTime: endTime
+        });
         // On range change close the Details pane
         // this.closeSUDets();
         // console.log(_.orderBy(group, ["parent", "id"], ['asc', 'desc']));
-        group = this.state.stationView ? this.getStationsByGroupName() : _.orderBy(_.uniqBy(group, 'id'),["parent", "start"], ['asc', 'asc']);
-        return {group: group, items: items};
+        group = this.state.stationView ? this.getStationsByGroupName() : _.orderBy(_.uniqBy(group, 'id'), ["parent", "start"], ['asc', 'asc']);
+        return { group: group, items: items };
     }
 
     /**
@@ -509,7 +577,7 @@ export class TimelineView extends Component {
     getStationItemGroups(suBlueprint, timelineItem, group, items) {
         /* Get stations based on SU status */
         let stations = this.getSUStations(suBlueprint);
-        
+
         /* Group the items by station */
         for (const station of stations) {
             let stationItem = _.cloneDeep(timelineItem);
@@ -527,7 +595,7 @@ export class TimelineView extends Component {
     getSUStations(suBlueprint) {
         let stations = [];
         /* Get all observation tasks */
-        const observationTasks = _.filter(suBlueprint.tasks, (task) => { return task.specifications_template.type_value.toLowerCase() === "observation"});
+        const observationTasks = _.filter(suBlueprint.tasks, (task) => { return task.specifications_template.type_value.toLowerCase() === "observation" });
         for (const observationTask of observationTasks) {
             /** If the status of SU is before scheduled, get all stations from the station_groups from the task specification_docs */
             if (this.STATUS_BEFORE_SCHEDULED.indexOf(suBlueprint.status.toLowerCase()) >= 0
@@ -535,8 +603,8 @@ export class TimelineView extends Component {
                 for (const grpStations of _.map(observationTask.specifications_doc.station_groups, "stations")) {
                     stations = _.concat(stations, grpStations);
                 }
-            }   else if (this.STATUS_BEFORE_SCHEDULED.indexOf(suBlueprint.status.toLowerCase()) < 0 
-                            && observationTask.subtasks) {
+            } else if (this.STATUS_BEFORE_SCHEDULED.indexOf(suBlueprint.status.toLowerCase()) < 0
+                && observationTask.subtasks) {
                 /** If the status of SU is scheduled or after get the stations from the subtask specification tasks */
                 for (const subtask of observationTask.subtasks) {
                     if (subtask.specifications_doc.stations) {
@@ -558,18 +626,18 @@ export class TimelineView extends Component {
         let reservations = this.reservations;
         for (const reservation of reservations) {
             const reservationStartTime = moment.utc(reservation.start_time);
-            const reservationEndTime = reservation.duration?reservationStartTime.clone().add(reservation.duration, 'seconds'):endTime;
+            const reservationEndTime = reservation.duration ? reservationStartTime.clone().add(reservation.duration, 'seconds') : endTime;
             const reservationSpec = reservation.specifications_doc;
-            if ( (reservationStartTime.isSame(startTime) 
-                    || reservationStartTime.isSame(endTime)                       
-                    || reservationStartTime.isBetween(startTime, endTime)
-                    || reservationEndTime.isSame(startTime) 
-                    || reservationEndTime.isSame(endTime)                       
-                    || reservationEndTime.isBetween(startTime, endTime)
-                    || (reservationStartTime.isSameOrBefore(startTime)
+            if ((reservationStartTime.isSame(startTime)
+                || reservationStartTime.isSame(endTime)
+                || reservationStartTime.isBetween(startTime, endTime)
+                || reservationEndTime.isSame(startTime)
+                || reservationEndTime.isSame(endTime)
+                || reservationEndTime.isBetween(startTime, endTime)
+                || (reservationStartTime.isSameOrBefore(startTime)
                     && reservationEndTime.isSameOrAfter(endTime)))
-                    && (!this.state.reservationFilter ||                                        // No reservation filter added
-                        reservationSpec.activity.type === this.state.reservationFilter) ) {     // Reservation reason == Filtered reaseon
+                && (!this.state.reservationFilter ||                                        // No reservation filter added
+                    reservationSpec.activity.type === this.state.reservationFilter)) {     // Reservation reason == Filtered reaseon
                 if (reservationSpec.resources.stations) {
                     items = items.concat(this.getReservationItems(reservation, endTime));
                 }
@@ -587,18 +655,19 @@ export class TimelineView extends Component {
         const reservationSpec = reservation.specifications_doc;
         let items = [];
         const start_time = moment.utc(reservation.start_time);
-        const end_time = reservation.duration?start_time.clone().add(reservation.duration, 'seconds'):endTime;
+        const end_time = reservation.duration ? start_time.clone().add(reservation.duration, 'seconds') : endTime;
         for (const station of reservationSpec.resources.stations) {
             const blockColor = RESERVATION_COLORS[this.getReservationType(reservationSpec.schedulability)];
-            let item = { id: `Res-${reservation.id}-${station}`,
-                            start_time: start_time, end_time: end_time,
-                            name: reservationSpec.activity.type, project: reservation.project_id,
-                            group: station, type: 'RESERVATION',
-                            title: `${reservationSpec.activity.type}${reservation.project_id?("-"+ reservation.project_id):""}`,
-                            desc: reservation.description,
-                            duration: reservation.duration?UnitConverter.getSecsToHHmmss(reservation.duration):"Unknown",
-                            bgColor: blockColor.bgColor, selectedBgColor: blockColor.bgColor, color: blockColor.color
-                        };
+            let item = {
+                id: `Res-${reservation.id}-${station}`,
+                start_time: start_time, end_time: end_time,
+                name: reservationSpec.activity.type, project: reservation.project_id,
+                group: station, type: 'RESERVATION',
+                title: `${reservationSpec.activity.type}${reservation.project_id ? ("-" + reservation.project_id) : ""}`,
+                desc: reservation.description,
+                duration: reservation.duration ? UnitConverter.getSecsToHHmmss(reservation.duration) : "Unknown",
+                bgColor: blockColor.bgColor, selectedBgColor: blockColor.bgColor, color: blockColor.color
+            };
             items.push(item);
         }
         return items;
@@ -612,11 +681,11 @@ export class TimelineView extends Component {
     getReservationType(schedulability) {
         if (schedulability.manual && schedulability.dynamic) {
             return 'true-true';
-        }   else if (!schedulability.manual && !schedulability.dynamic) {
+        } else if (!schedulability.manual && !schedulability.dynamic) {
             return 'false-false';
-        }   else if (schedulability.manual && !schedulability.dynamic) {
+        } else if (schedulability.manual && !schedulability.dynamic) {
             return 'true-false';
-        }   else {
+        } else {
             return 'false-true';
         }
     }
@@ -626,7 +695,7 @@ export class TimelineView extends Component {
      * @param {String} filter 
      */
     setReservationFilter(filter) {
-        this.setState({reservationFilter: filter});
+        this.setState({ reservationFilter: filter });
     }
 
     /**
@@ -634,8 +703,10 @@ export class TimelineView extends Component {
      * @param {String} value 
      */
     showTimelineItems(value) {
-        this.setState({showSUs: value==='su' || value==="suTask",
-                        showTasks: value==='task' || value==="suTask"});
+        this.setState({
+            showSUs: value === 'su' || value === "suTask",
+            showTasks: value === 'task' || value === "suTask"
+        });
     }
 
     /**
@@ -647,14 +718,14 @@ export class TimelineView extends Component {
         let canShrinkSUList = this.state.canShrinkSUList;
         if (step === 1) {
             // Can Extend when fully shrunk and still extendable
-            canExtendSUList = (!canShrinkSUList && canExtendSUList)?true:false;
+            canExtendSUList = (!canShrinkSUList && canExtendSUList) ? true : false;
             canShrinkSUList = true;
-        }   else {
+        } else {
             // Can Shrink when fully extended and still shrinkable
-            canShrinkSUList = (canShrinkSUList && !canExtendSUList)?true:false;
+            canShrinkSUList = (canShrinkSUList && !canExtendSUList) ? true : false;
             canExtendSUList = true;
         }
-        this.setState({canExtendSUList: canExtendSUList, canShrinkSUList: canShrinkSUList});
+        this.setState({ canExtendSUList: canExtendSUList, canShrinkSUList: canShrinkSUList });
     }
 
     /**
@@ -662,22 +733,24 @@ export class TimelineView extends Component {
      * @param {Array} filteredData 
      */
     suListFilterCallback(filteredData) {
-        let group=[], items = [];
+        let group = [], items = [];
         const suBlueprints = this.state.suBlueprints;
         for (const data of filteredData) {
-            const suBlueprint = _.find(suBlueprints, {actionpath: data.actionpath});
-            let timelineItem = (this.state.showSUs || this.state.stationView)?this.getTimelineItem(suBlueprint):null;
+            const suBlueprint = _.find(suBlueprints, { actionpath: data.actionpath });
+            let timelineItem = (this.state.showSUs || this.state.stationView) ? this.getTimelineItem(suBlueprint) : null;
             if (this.state.stationView) {
                 this.getStationItemGroups(suBlueprint, timelineItem, this.allStationsGroup, items);
-             }   else {
+            } else {
                 if (timelineItem) {
                     items.push(timelineItem);
-                    if (!_.find(group, {'id': suBlueprint.suDraft.id})) {
+                    if (!_.find(group, { 'id': suBlueprint.suDraft.id })) {
                         /* parent and start properties are added to order and list task rows below the SU row */
-                        group.push({'id': this.state.groupByProject?suBlueprint.project:suBlueprint.suDraft.id, 
-                                    parent: this.state.groupByProject?suBlueprint.project:suBlueprint.suDraft.id, 
-                                    start: moment.utc("1900-01-01", "YYYY-MM-DD"), 
-                                    title: this.state.groupByProject?suBlueprint.project:suBlueprint.suDraft.name});
+                        group.push({
+                            'id': this.state.groupByProject ? suBlueprint.project : suBlueprint.suDraft.id,
+                            parent: this.state.groupByProject ? suBlueprint.project : suBlueprint.suDraft.id,
+                            start: moment.utc("1900-01-01", "YYYY-MM-DD"),
+                            title: this.state.groupByProject ? suBlueprint.project : suBlueprint.suDraft.name
+                        });
                     }
                 }
                 if (this.state.showTasks && !this.state.stationView) {
@@ -691,37 +764,37 @@ export class TimelineView extends Component {
             items = this.addStationReservations(items, this.state.currentStartTime, this.state.currentEndTime);
         }
         if (this.timeline) {
-            this.timeline.updateTimeline({group: this.state.stationView ? this.getStationsByGroupName() : _.orderBy(_.uniqBy(group, 'id'),["parent", "start"], ['asc', 'asc']), items: items});
+            this.timeline.updateTimeline({ group: this.state.stationView ? this.getStationsByGroupName() : _.orderBy(_.uniqBy(group, 'id'), ["parent", "start"], ['asc', 'asc']), items: items });
         }
-        
+
     }
 
-    getStationsByGroupName() {      
+    getStationsByGroupName() {
         let stations = [];
         this.state.selectedStationGroup.forEach((group) => {
             stations = [...stations, ...this.mainStationGroups[group]];
         });
-        stations = stations.map(station => ({id: station, title: station}));
+        stations = stations.map(station => ({ id: station, title: station }));
         return stations;
     }
 
     setStationView(e) {
         this.closeSUDets();
         const selectedGroups = _.keys(this.mainStationGroups);
-        this.setState({stationView: e.value, selectedStationGroup: selectedGroups});
+        this.setState({ stationView: e.value, selectedStationGroup: selectedGroups });
     }
     showOptionMenu(event) {
         this.optionsMenu.toggle(event);
     }
 
     selectOptionMenu(menuName) {
-        switch(menuName) {
+        switch (menuName) {
             case 'Reservation List': {
-                this.setState({redirect: `/reservation/list`});
+                this.setState({ redirect: `/reservation/list` });
                 break;
             }
             case 'Add Reservation': {
-                this.setState({redirect: `/reservation/create`});
+                this.setState({ redirect: `/reservation/create` });
                 break;
             }
             default: {
@@ -753,7 +826,7 @@ export class TimelineView extends Component {
             const jsonData = JSON.parse(data);
             if (jsonData.action === 'create') {
                 this.addNewData(jsonData.object_details.id, jsonData.object_type, jsonData.object_details);
-            }   else if (jsonData.action === 'update') {
+            } else if (jsonData.action === 'update') {
                 this.updateExistingData(jsonData.object_details.id, jsonData.object_type, jsonData.object_details);
             }
         }
@@ -766,7 +839,7 @@ export class TimelineView extends Component {
      * @param {Object} object - model object with certain properties
      */
     addNewData(id, type, object) {
-        switch(type) {
+        switch (type) {
             /* When a new scheduling_unit_draft is created, it should be added to the existing list of suDraft. */
             case 'scheduling_unit_draft': {
                 this.updateSUDraft(id);
@@ -802,7 +875,7 @@ export class TimelineView extends Component {
      */
     updateExistingData(id, type, object) {
         const objectProps = ['status', 'start_time', 'stop_time', 'duration'];
-        switch(type) {
+        switch (type) {
             case 'scheduling_unit_draft': {
                 this.updateSUDraft(id);
                 // let suDrafts = this.state.suDrafts;
@@ -828,7 +901,7 @@ export class TimelineView extends Component {
                 // }
                 break;
             }
-            default: { break;}
+            default: { break; }
         }
     }
 
@@ -840,13 +913,13 @@ export class TimelineView extends Component {
         let suDrafts = this.state.suDrafts;
         let suSets = this.state.suSets;
         ScheduleService.getSchedulingUnitDraftById(id)
-        .then(suDraft => {
-            _.remove(suDrafts, function(suDraft) { return suDraft.id === id});
-            suDrafts.push(suDraft);
-            _.remove(suSets, function(suSet) { return suSet.id === suDraft.scheduling_set_id});
-            suSets.push(suDraft.scheduling_set_object);
-            this.setState({suSet: suSets, suDrafts: suDrafts});
-        });
+            .then(suDraft => {
+                _.remove(suDrafts, function (suDraft) { return suDraft.id === id });
+                suDrafts.push(suDraft);
+                _.remove(suSets, function (suSet) { return suSet.id === suDraft.scheduling_set_id });
+                suSets.push(suDraft.scheduling_set_object);
+                this.setState({ suSet: suSets, suDrafts: suDrafts });
+            });
     }
 
     /**
@@ -856,29 +929,29 @@ export class TimelineView extends Component {
      */
     updateSchedulingUnit(id) {
         ScheduleService.getSchedulingUnitExtended('blueprint', id, true)
-        .then(suBlueprint => {
-            const suDraft = _.find(this.state.suDrafts, ['id', suBlueprint.draft_id]);
-            const suSet = this.state.suSets.find((suSet) => { return suDraft.scheduling_set_id===suSet.id});
-            const project = this.state.projects.find((project) => { return suSet.project_id===project.name});
-            let suBlueprints = this.state.suBlueprints;
-            suBlueprint['actionpath'] = `/schedulingunit/view/blueprint/${id}`;
-            suBlueprint.suDraft = suDraft;
-            suBlueprint.project = project.name;
-            suBlueprint.suSet = suSet;
-            suBlueprint.durationInSec = suBlueprint.duration;
-            suBlueprint.duration = UnitConverter.getSecsToHHmmss(suBlueprint.duration);
-            suBlueprint.tasks = suBlueprint.task_blueprints;
-            _.remove(suBlueprints, function(suB) { return suB.id === id});
-            suBlueprints.push(suBlueprint);
-            // Set updated suBlueprints in the state and call the dateRangeCallback to create the timeline group and items
-            this.setState({suBlueprints: suBlueprints});
-            this.dateRangeCallback(this.state.currentStartTime, this.state.currentEndTime);
-        });
+            .then(suBlueprint => {
+                const suDraft = _.find(this.state.suDrafts, ['id', suBlueprint.draft_id]);
+                const suSet = this.state.suSets.find((suSet) => { return suDraft.scheduling_set_id === suSet.id });
+                const project = this.state.projects.find((project) => { return suSet.project_id === project.name });
+                let suBlueprints = this.state.suBlueprints;
+                suBlueprint['actionpath'] = `/schedulingunit/view/blueprint/${id}`;
+                suBlueprint.suDraft = suDraft;
+                suBlueprint.project = project.name;
+                suBlueprint.suSet = suSet;
+                suBlueprint.durationInSec = suBlueprint.duration;
+                suBlueprint.duration = UnitConverter.getSecsToHHmmss(suBlueprint.duration);
+                suBlueprint.tasks = suBlueprint.task_blueprints;
+                _.remove(suBlueprints, function (suB) { return suB.id === id });
+                suBlueprints.push(suBlueprint);
+                // Set updated suBlueprints in the state and call the dateRangeCallback to create the timeline group and items
+                this.setState({ suBlueprints: suBlueprints });
+                this.dateRangeCallback(this.state.currentStartTime, this.state.currentEndTime);
+            });
     }
 
     render() {
         if (this.state.redirect) {
-            return <Redirect to={ {pathname: this.state.redirect} }></Redirect>
+            return <Redirect to={{ pathname: this.state.redirect }}></Redirect>
         }
         //  if (this.state.loader) {
         //     return <AppLoader />
@@ -891,265 +964,288 @@ export class TimelineView extends Component {
         const canShrinkSUList = this.state.canShrinkSUList;
         let suBlueprint = null, reservation = null;
         if (isSUDetsVisible) {
-            suBlueprint = _.find(this.state.suBlueprints, {id:  this.state.stationView?parseInt(this.state.selectedItem.id.split('-')[0]):this.state.selectedItem.id});
+            suBlueprint = _.find(this.state.suBlueprints, { id: this.state.stationView ? parseInt(this.state.selectedItem.id.split('-')[0]) : this.state.selectedItem.id });
         }
         if (isReservDetsVisible) {
-            reservation = _.find(this.reservations, {id: parseInt(this.state.selectedItem.id.split('-')[1])});
+            reservation = _.find(this.reservations, { id: parseInt(this.state.selectedItem.id.split('-')[1]) });
             reservation.project = this.state.selectedItem.project;
         }
         let mouseOverItem = this.state.mouseOverItem;
         return (
             <React.Fragment>
                 <TieredMenu className="app-header-menu" model={this.menuOptions} popup ref={el => this.optionsMenu = el} />
-                <PageHeader location={this.props.location} title={'Scheduling Units - Timeline View'} 
+                <PageHeader location={this.props.location} title={'Scheduling Units - Timeline View'}
                     actions={[
-                        {icon:'fa-bars',title: '', type:'button', actOn:'mouseOver', props : { callback: this.showOptionMenu},},
-                        {icon: 'fa-calendar-alt',title:'Week View', props : { pathname: `/su/timelineview/week`}}
+                        { icon: 'fa-bars', title: '', type: 'button', actOn: 'mouseOver', props: { callback: this.showOptionMenu }, },
+                        { icon: 'fa-calendar-alt', title: 'Week View', props: { pathname: `/su/timelineview/week` } }
                     ]}
                 />
                 { this.state.isLoading ? <AppLoader /> :
-                        <div className="p-grid">
-                            {/* SU List Panel */}
-                            <div className={isSUListVisible && (isSUDetsVisible || isReservDetsVisible || isTaskDetsVisible || 
-                                            (canExtendSUList && !canShrinkSUList)?"col-lg-4 col-md-4 col-sm-12":
-                                            ((canExtendSUList && canShrinkSUList)?"col-lg-5 col-md-5 col-sm-12":"col-lg-6 col-md-6 col-sm-12"))}
-                                 style={isSUListVisible?{position: "inherit", borderRight: "3px solid #efefef", paddingTop: "10px"}:{display: 'none'}}>
-                                <ViewTable 
-                                    viewInNewWindow
-                                    data={this.state.suBlueprintList} 
-                                    defaultcolumns={[{name: "Name",
-                                                        start_time:
-                                                        {
-                                                            name:"Start Time",
-                                                            format:UIConstants.CALENDAR_DATETIME_FORMAT
-                                                        },
-                                                         stop_time:{
-                                                             name:"End Time",
-                                                             format:UIConstants.CALENDAR_DATETIME_FORMAT}
-                                                        }]}
-                                    optionalcolumns={[{project:"Project",description: "Description", duration:"Duration (HH:mm:ss)", actionpath: "actionpath"}]}
-                                    columnclassname={[{"Start Time":"filter-input-50", "End Time":"filter-input-50",
-                                                        "Duration (HH:mm:ss)" : "filter-input-50",}]}
-                                    defaultSortColumn= {[{id: "Start Time", desc: false}]}
-                                    showaction="true"
-                                    tablename="timeline_scheduleunit_list"
-                                    showTopTotal={false}
-                                    filterCallback={this.suListFilterCallback}
-                                />
-                            </div>
-                            {/* Timeline Panel */}
-                            <div className={isSUListVisible?((isSUDetsVisible || isReservDetsVisible)?"col-lg-5 col-md-5 col-sm-12":
-                                                (!canExtendSUList && canShrinkSUList)?"col-lg-6 col-md-6 col-sm-12":
-                                                ((canExtendSUList && canShrinkSUList)?"col-lg-7 col-md-7 col-sm-12":"col-lg-8 col-md-8 col-sm-12")):
-                                                ((isSUDetsVisible || isReservDetsVisible || isTaskDetsVisible)?"col-lg-9 col-md-9 col-sm-12":"col-lg-12 col-md-12 col-sm-12")}
-                                // style={{borderLeft: "3px solid #efefef"}}
-                                >
-                                {/* Panel Resize buttons */}
-                                {isSUListVisible &&
+                    <div className="p-grid">
+                        {/* SU List Panel */}
+                        <div className={isSUListVisible && (isSUDetsVisible || isReservDetsVisible || isTaskDetsVisible ||
+                            (canExtendSUList && !canShrinkSUList) ? "col-lg-4 col-md-4 col-sm-12" :
+                            ((canExtendSUList && canShrinkSUList) ? "col-lg-5 col-md-5 col-sm-12" : "col-lg-6 col-md-6 col-sm-12"))}
+                            style={isSUListVisible ? { position: "inherit", borderRight: "3px solid #efefef", paddingTop: "10px" } : { display: 'none' }}>
+                            <ViewTable
+                                viewInNewWindow
+                                data={this.state.suBlueprintList}
+                                defaultcolumns={[{
+                                    name: "Name",
+                                    start_time:
+                                    {
+                                        name: "Start Time",
+                                        format: UIConstants.CALENDAR_DATETIME_FORMAT
+                                    },
+                                    stop_time: {
+                                        name: "End Time",
+                                        format: UIConstants.CALENDAR_DATETIME_FORMAT
+                                    }
+                                }]}
+                                optionalcolumns={[{ project: "Project", description: "Description", duration: "Duration (HH:mm:ss)", actionpath: "actionpath" }]}
+                                columnclassname={[{
+                                    "Start Time": "filter-input-50", "End Time": "filter-input-50",
+                                    "Duration (HH:mm:ss)": "filter-input-50",
+                                }]}
+                                defaultSortColumn={this.defaultSortColumn}
+                                showaction="true"
+                                tablename="timeline_scheduleunit_list"
+                                showTopTotal={false}
+                                filterCallback={this.suListFilterCallback}
+                                lsKeySortColumn={this.lsKeySortColumn}
+                                toggleBySorting={(sortData) => this.toggleBySorting(sortData)}
+                            />
+                        </div>
+                        {/* Timeline Panel */}
+                        <div className={isSUListVisible ? ((isSUDetsVisible || isReservDetsVisible) ? "col-lg-5 col-md-5 col-sm-12" :
+                            (!canExtendSUList && canShrinkSUList) ? "col-lg-6 col-md-6 col-sm-12" :
+                                ((canExtendSUList && canShrinkSUList) ? "col-lg-7 col-md-7 col-sm-12" : "col-lg-8 col-md-8 col-sm-12")) :
+                            ((isSUDetsVisible || isReservDetsVisible || isTaskDetsVisible) ? "col-lg-9 col-md-9 col-sm-12" : "col-lg-12 col-md-12 col-sm-12")}
+                        // style={{borderLeft: "3px solid #efefef"}}
+                        >
+                            {/* Panel Resize buttons */}
+                            {isSUListVisible &&
                                 <div className="resize-div">
-                                    <button className="p-link resize-btn" disabled={!this.state.canShrinkSUList} 
-                                            title="Shrink List/Expand Timeline"
-                                            onClick={(e)=> { this.resizeSUList(-1)}}>
+                                    <button className="p-link resize-btn" disabled={!this.state.canShrinkSUList}
+                                        title="Shrink List/Expand Timeline"
+                                        onClick={(e) => { this.resizeSUList(-1) }}>
                                         <i className="pi pi-step-backward"></i>
                                     </button>
-                                    <button className="p-link resize-btn" disabled={!this.state.canExtendSUList} 
-                                            title="Expand List/Shrink Timeline"
-                                            onClick={(e)=> { this.resizeSUList(1)}}>
+                                    <button className="p-link resize-btn" disabled={!this.state.canExtendSUList}
+                                        title="Expand List/Shrink Timeline"
+                                        onClick={(e) => { this.resizeSUList(1) }}>
                                         <i className="pi pi-step-forward"></i>
                                     </button>
-                                </div> 
-                                }
-                                <div className={isSUListVisible?"resize-div su-visible":"resize-div su-hidden"}>
-                                    {isSUListVisible &&
-                                    <button className="p-link resize-btn" 
-                                            title="Hide List"
-                                            onClick={(e)=> { this.setState({isSUListVisible: false})}}>
+                                </div>
+                            }
+                            <div className={isSUListVisible ? "resize-div su-visible" : "resize-div su-hidden"}>
+                                {isSUListVisible &&
+                                    <button className="p-link resize-btn"
+                                        title="Hide List"
+                                        onClick={(e) => { this.setState({ isSUListVisible: false }) }}>
                                         <i className="pi pi-eye-slash"></i>
                                     </button>
-                                    }
-                                    {!isSUListVisible &&
+                                }
+                                {!isSUListVisible &&
                                     <button className="p-link resize-btn"
-                                            title="Show List"
-                                            onClick={(e)=> { this.setState({isSUListVisible: true})}}>
+                                        title="Show List"
+                                        onClick={(e) => { this.setState({ isSUListVisible: true }) }}>
                                         <i className="pi pi-eye"> Show List</i>
                                     </button>
-                                    }
-                                </div>
-                                <div className={`timeline-view-toolbar ${this.state.stationView && 'alignTimeLineHeader'}`}>
-                                    <div  className="sub-header">
-                                        <label >Station View</label>
-                                        <InputSwitch checked={this.state.stationView} onChange={(e) => {this.setStationView(e)}} />                                       
-                                       { this.state.stationView && 
-                                            <>
-                                             <label style={{marginLeft: '20px'}}>Stations Group</label>
-                                             <MultiSelect data-testid="stations" id="stations" optionLabel="value" optionValue="value" 
-                                                style={{top:'2px'}}
+                                }
+                            </div>
+                            <div className={`timeline-view-toolbar ${this.state.stationView && 'alignTimeLineHeader'}`}>
+                                <div className="sub-header">
+                                    <label >Station View</label>
+                                    <InputSwitch checked={this.state.stationView} onChange={(e) => { this.setStationView(e) }} />
+                                    {this.state.stationView &&
+                                        <>
+                                            <label style={{ marginLeft: '20px' }}>Stations Group</label>
+                                            <MultiSelect data-testid="stations" id="stations" optionLabel="value" optionValue="value"
+<<<<<<< HEAD
+                                                style={{ top: '2px', width: '175px' }}
+=======
+                                                style={{ top: '2px' }}
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
                                                 tooltip="Select Stations"
-                                                value={this.state.selectedStationGroup} 
-                                                options={this.mainStationGroupOptions} 
+                                                value={this.state.selectedStationGroup}
+                                                options={this.mainStationGroupOptions}
                                                 placeholder="Select Group"
                                                 onChange={(e) => this.setSelectedStationGroup(e.value)}
                                             />
-                                         </>
-                                        }
-                                    </div>
-                                
-                                    {this.state.stationView &&
+                                        </>
+                                    }
+                                </div>
+
+                                {this.state.stationView &&
                                     <div className="sub-header">
-                                        <label style={{marginLeft: '20px'}}>Reservation</label>
-                                        <Dropdown optionLabel="name" optionValue="name" 
-                                                    style={{top:'2px'}}
-                                                    value={this.state.reservationFilter} 
-                                                    options={this.reservationReasons} 
-                                                    filter showClear={true} filterBy="name"
-                                                    onChange={(e) => {this.setReservationFilter(e.value)}} 
-                                                    placeholder="Reason"/>
-                                    
+                                        <label style={{ marginLeft: '20px' }}>Reservation</label>
+                                        <Dropdown optionLabel="name" optionValue="name"
+                                            style={{ top: '2px' }}
+                                            value={this.state.reservationFilter}
+                                            options={this.reservationReasons}
+                                            filter showClear={true} filterBy="name"
+                                            onChange={(e) => { this.setReservationFilter(e.value) }}
+                                            placeholder="Reason" />
+
                                     </div>
-                                    }
-                                    {!this.state.stationView &&
+                                }
+                                {!this.state.stationView &&
                                     <>
-                                        <label style={{marginLeft: '15px'}}>Show :</label>
+                                        <label style={{ marginLeft: '15px' }}>Show :</label>
                                         <RadioButton value="su" name="Only SUs" inputId="suOnly" onChange={(e) => this.showTimelineItems(e.value)} checked={this.state.showSUs && !this.state.showTasks} />
                                         <label htmlFor="suOnly">Only SU</label>
                                         <RadioButton value="task" name="Only Tasks" inputId="taskOnly" onChange={(e) => this.showTimelineItems(e.value)} checked={!this.state.showSUs && this.state.showTasks} />
                                         <label htmlFor="suOnly">Only Task</label>
                                         <RadioButton value="suTask" name="Both" inputId="bothSuTask" onChange={(e) => this.showTimelineItems(e.value)} checked={this.state.showSUs && this.state.showTasks} />
                                         <label htmlFor="suOnly">Both</label>
-
+                                        {this.state.showTasks &&
+                                            <MultiSelect data-testid="tasks" id="tasks" optionLabel="value" optionValue="value"
+                                                style={{ width: '120px', height: '25px', marginRight: '10px' }}
+                                                tooltip={this.state.selectedTaskTypes.length>0?
+                                                            `Showing tasks of task type(s) ${this.state.selectedTaskTypes.join(', ')}`:
+                                                            "Select task type(s) to show in the timeline"}
+                                                maxSelectedLabels="1"
+                                                selectedItemsLabel="{0} Task Types"
+                                                value={this.state.selectedTaskTypes}
+                                                options={this.state.taskTypes}
+                                                placeholder="Task Type"
+                                                onChange={(e) => {this.setState({selectedTaskTypes: e.value})}}
+                                            />
+                                        }
                                         <div className="sub-header">
                                             {this.state.groupByProject &&
-                                            <Button className="p-button-rounded toggle-btn" label="Group By SU" onClick={e => this.setState({groupByProject: false})} /> }
+                                                <Button className="p-button-rounded toggle-btn" label="Group By SU" onClick={e => this.setState({ groupByProject: false })} />}
                                             {!this.state.groupByProject &&
-                                            <Button className="p-button-rounded toggle-btn" label="Group By Project" onClick={e => this.setState({groupByProject: true})} /> }
+                                                <Button className="p-button-rounded toggle-btn" label="Group By Project" onClick={e => this.setState({ groupByProject: true })} />}
                                         </div>
                                     </>
-                                    }
-                                </div>
-    
-                                <Timeline ref={(tl)=>{this.timeline=tl}} 
-                                        group={this.state.group} 
-                                        items={this.state.items}
-                                        currentUTC={this.state.currentUTC}
-                                        rowHeight={this.state.stationView?50:50} 
-                                        sidebarWidth={!this.state.showSUs?250:200}
-                                        itemClickCallback={this.onItemClick}
-                                        itemMouseOverCallback={this.onItemMouseOver}
-                                        itemMouseOutCallback={this.onItemMouseOut}
-                                        dateRangeCallback={this.dateRangeCallback}
-                                        showSunTimings={!this.state.stationView}
-                                        // stackItems ={this.state.stationView}
-                                        stackItems
-                                        className="timeline-toolbar-margin-top-0"></Timeline>
+                                }
                             </div>
-                            {/* Details Panel */}
-                            {this.state.isSUDetsVisible &&
-                                <div className="col-lg-3 col-md-3 col-sm-12" 
-                                     style={{borderLeft: "1px solid #efefef", marginTop: "0px", backgroundColor: "#f2f2f2"}}>
-                                    {this.state.isSummaryLoading?<AppLoader /> :
-                                        <SchedulingUnitSummary schedulingUnit={suBlueprint} suTaskList={this.state.suTaskList}
-                                                viewInNewWindow        
-                                                constraintsTemplate={this.state.suConstraintTemplate}
-                                                stationGroup={this.state.stationGroup}
-                                                closeCallback={this.closeSUDets}></SchedulingUnitSummary>
-                                    }
-                                </div>
-                            }  
-                            {this.state.isTaskDetsVisible &&
-                                <div className="col-lg-3 col-md-3 col-sm-12" 
-                                     style={{borderLeft: "1px solid #efefef", marginTop: "0px", backgroundColor: "#f2f2f2"}}>
-                                    {this.state.isSummaryLoading?<AppLoader /> :
-                                        <div>Yet to be developed <i className="fa fa-times" onClick={this.closeSUDets}></i></div>
-                                    }
-                                </div>
-                            }
-                            {this.state.isReservDetsVisible &&
-                                <div className="col-lg-3 col-md-3 col-sm-12" 
-                                     style={{borderLeft: "1px solid #efefef", marginTop: "0px", backgroundColor: "#f2f2f2"}}>
-                                    {this.state.isSummaryLoading?<AppLoader /> :
-                                        <ReservationSummary reservation={reservation} closeCallback={this.closeSUDets}></ReservationSummary>
-                                    }
-                                </div>
-                            }
+
+                            <Timeline ref={(tl) => { this.timeline = tl }}
+                                group={this.state.group}
+                                items={this.state.items}
+                                currentUTC={this.state.currentUTC}
+                                rowHeight={this.state.stationView ? 50 : 50}
+                                sidebarWidth={!this.state.showSUs ? 250 : 200}
+                                itemClickCallback={this.onItemClick}
+                                itemMouseOverCallback={this.onItemMouseOver}
+                                itemMouseOutCallback={this.onItemMouseOut}
+                                dateRangeCallback={this.dateRangeCallback}
+                                showSunTimings={!this.state.stationView}
+                                // stackItems ={this.state.stationView}
+                                stackItems
+                                className="timeline-toolbar-margin-top-0"></Timeline>
                         </div>
-                    
+                        {/* Details Panel */}
+                        {this.state.isSUDetsVisible &&
+                            <div className="col-lg-3 col-md-3 col-sm-12"
+                                style={{ borderLeft: "1px solid #efefef", marginTop: "0px", backgroundColor: "#f2f2f2" }}>
+                                {this.state.isSummaryLoading ? <AppLoader /> :
+                                    <SchedulingUnitSummary schedulingUnit={suBlueprint} suTaskList={this.state.suTaskList}
+                                        viewInNewWindow
+                                        constraintsTemplate={this.state.suConstraintTemplate}
+                                        stationGroup={this.state.stationGroup}
+                                        closeCallback={this.closeSUDets}></SchedulingUnitSummary>
+                                }
+                            </div>
+                        }
+                        {this.state.isTaskDetsVisible &&
+                            <div className="col-lg-3 col-md-3 col-sm-12"
+                                style={{ borderLeft: "1px solid #efefef", marginTop: "0px", backgroundColor: "#f2f2f2" }}>
+                                {this.state.isSummaryLoading ? <AppLoader /> :
+                                    <div>Yet to be developed <i className="fa fa-times" onClick={this.closeSUDets}></i></div>
+                                }
+                            </div>
+                        }
+                        {this.state.isReservDetsVisible &&
+                            <div className="col-lg-3 col-md-3 col-sm-12"
+                                style={{ borderLeft: "1px solid #efefef", marginTop: "0px", backgroundColor: "#f2f2f2" }}>
+                                {this.state.isSummaryLoading ? <AppLoader /> :
+                                    <ReservationSummary reservation={reservation} closeCallback={this.closeSUDets}></ReservationSummary>
+                                }
+                            </div>
+                        }
+                    </div>
+
                 }
                 {/* SU Item Tooltip popover with SU status color */}
                 <OverlayPanel className="timeline-popover" ref={(el) => this.popOver = el} dismissable>
-                {(mouseOverItem && (["SCHEDULE", "TASK"].indexOf(mouseOverItem.type)>=0)) &&
-                    <div className={`p-grid su-${mouseOverItem.status}`} style={{width: '350px'}}>
-                        <h3 className={`col-12 su-${mouseOverItem.status}-icon`}>{mouseOverItem.type==='SCHEDULE'?'Scheduling Unit ':'Task '}Overview</h3>
-                        <hr></hr>
-                        <label className={`col-5 su-${mouseOverItem.status}-icon`}>Project:</label>
-                        <div className="col-7">{mouseOverItem.project}</div>
-                        <label className={`col-5 su-${mouseOverItem.status}-icon`}>Scheduling Unit:</label>
-                        <div className="col-7">{mouseOverItem.suName}</div>
-                        {mouseOverItem.type==='SCHEDULE' &&
-                        <>
-                            
-                            <label className={`col-5 su-${mouseOverItem.status}-icon`}>Scheduler:</label>
-                            <div className="col-7">{mouseOverItem.scheduleMethod}</div>
-                            <label className={`col-5 su-${mouseOverItem.status}-icon`}>Friends:</label>
-                            <div className="col-7">{mouseOverItem.friends?mouseOverItem.friends:"-"}</div>
-                        </>}
-                        {mouseOverItem.type==='TASK' &&
-                        <>
-                            <label className={`col-5 su-${mouseOverItem.status}-icon`}>Task Name:</label>
-                            <div className="col-7">{mouseOverItem.name}</div>
-                        </>}
-                        <label className={`col-5 su-${mouseOverItem.status}-icon`}>Start Time:</label>
-                        <div className="col-7">{mouseOverItem.start_time.format(UIConstants.CALENDAR_DATETIME_FORMAT)}</div>
-                        <label className={`col-5 su-${mouseOverItem.status}-icon`}>End Time:</label>
-                        <div className="col-7">{mouseOverItem.end_time.format(UIConstants.CALENDAR_DATETIME_FORMAT)}</div>
-                        {mouseOverItem.type==='SCHEDULE' &&
-                        <>
-                            <label className={`col-5 su-${mouseOverItem.status}-icon`}>Antenna Set:</label>
-                            <div className="col-7">{mouseOverItem.antennaSet}</div>
-                        </>}
-                        <label className={`col-5 su-${mouseOverItem.status}-icon`}>Stations:</label>
-                        <div className="col-7">{mouseOverItem.stations.groups}:{mouseOverItem.stations.counts}</div>
-                        <label className={`col-5 su-${mouseOverItem.status}-icon`}>Status:</label>
-                        <div className="col-7">{mouseOverItem.status}</div>
-                        <label className={`col-5 su-${mouseOverItem.status}-icon`}>Duration:</label>
-                        <div className="col-7">{mouseOverItem.duration}</div>
-                    </div>
-                }
-                {(mouseOverItem && mouseOverItem.type === "RESERVATION") &&
-                    <div className={`p-grid`} style={{width: '350px', backgroundColor: mouseOverItem.bgColor, color: mouseOverItem.color}}>
-                        <h3 className={`col-12`}>Reservation Overview</h3>
-                        <hr></hr>
-                        <label className={`col-5`} style={{color: mouseOverItem.color}}>Name:</label>
-                        <div className="col-7">{mouseOverItem.name}</div>
-                        <label className={`col-5`} style={{color: mouseOverItem.color}}>Description:</label>
-                        <div className="col-7">{mouseOverItem.desc}</div>
-                        <label className={`col-5`} style={{color: mouseOverItem.color}}>Type:</label>
-                        <div className="col-7">{mouseOverItem.activity_type}</div>
-                        <label className={`col-5`} style={{color: mouseOverItem.color}}>Stations:</label>
-                        {/* <div className="col-7"><ListBox options={mouseOverItem.stations} /></div> */}
-                        <div className="col-7 station-list">
-                            {mouseOverItem.stations.map((station, index) => (
-                                <div key={`stn-${index}`}>{station}</div>
-                            ))}
+                    {(mouseOverItem && (["SCHEDULE", "TASK"].indexOf(mouseOverItem.type) >= 0)) &&
+                        <div className={`p-grid su-${mouseOverItem.status}`} style={{ width: '350px' }}>
+                            <h3 className={`col-12 su-${mouseOverItem.status}-icon`}>{mouseOverItem.type === 'SCHEDULE' ? 'Scheduling Unit ' : 'Task '}Overview</h3>
+                            <hr></hr>
+                            <label className={`col-5 su-${mouseOverItem.status}-icon`}>Project:</label>
+                            <div className="col-7">{mouseOverItem.project}</div>
+                            <label className={`col-5 su-${mouseOverItem.status}-icon`}>Scheduling Unit:</label>
+                            <div className="col-7">{mouseOverItem.suName}</div>
+                            {mouseOverItem.type === 'SCHEDULE' &&
+                                <>
+
+                                    <label className={`col-5 su-${mouseOverItem.status}-icon`}>Scheduler:</label>
+                                    <div className="col-7">{mouseOverItem.scheduleMethod}</div>
+                                    <label className={`col-5 su-${mouseOverItem.status}-icon`}>Friends:</label>
+                                    <div className="col-7">{mouseOverItem.friends ? mouseOverItem.friends : "-"}</div>
+                                </>}
+                            {mouseOverItem.type === 'TASK' &&
+                                <>
+                                    <label className={`col-5 su-${mouseOverItem.status}-icon`}>Task Name:</label>
+                                    <div className="col-7">{mouseOverItem.name}</div>
+                                </>}
+                            <label className={`col-5 su-${mouseOverItem.status}-icon`}>Start Time:</label>
+                            <div className="col-7">{mouseOverItem.start_time.format(UIConstants.CALENDAR_DATETIME_FORMAT)}</div>
+                            <label className={`col-5 su-${mouseOverItem.status}-icon`}>End Time:</label>
+                            <div className="col-7">{mouseOverItem.end_time.format(UIConstants.CALENDAR_DATETIME_FORMAT)}</div>
+                            {mouseOverItem.type === 'SCHEDULE' &&
+                                <>
+                                    <label className={`col-5 su-${mouseOverItem.status}-icon`}>Antenna Set:</label>
+                                    <div className="col-7">{mouseOverItem.antennaSet}</div>
+                                </>}
+                            <label className={`col-5 su-${mouseOverItem.status}-icon`}>Stations:</label>
+                            <div className="col-7">{mouseOverItem.stations.groups}:{mouseOverItem.stations.counts}</div>
+                            <label className={`col-5 su-${mouseOverItem.status}-icon`}>Status:</label>
+                            <div className="col-7">{mouseOverItem.status}</div>
+                            <label className={`col-5 su-${mouseOverItem.status}-icon`}>Duration:</label>
+                            <div className="col-7">{mouseOverItem.duration}</div>
                         </div>
-                        <label className={`col-5`} style={{color: mouseOverItem.color}}>Project:</label>
-                        <div className="col-7">{mouseOverItem.project?mouseOverItem.project:"-"}</div>
-                        <label className={`col-5`} style={{color: mouseOverItem.color}}>Start Time:</label>
-                        <div className="col-7">{mouseOverItem.start_time.format(UIConstants.CALENDAR_DATETIME_FORMAT)}</div>
-                        <label className={`col-5`} style={{color: mouseOverItem.color}}>End Time:</label>
-                        <div className="col-7">{mouseOverItem.end_time.format(UIConstants.CALENDAR_DATETIME_FORMAT)}</div>
-                        {/* <label className={`col-5`} style={{color: mouseOverItem.color}}>Stations:</label>
+                    }
+                    {(mouseOverItem && mouseOverItem.type === "RESERVATION") &&
+                        <div className={`p-grid`} style={{ width: '350px', backgroundColor: mouseOverItem.bgColor, color: mouseOverItem.color }}>
+                            <h3 className={`col-12`}>Reservation Overview</h3>
+                            <hr></hr>
+                            <label className={`col-5`} style={{ color: mouseOverItem.color }}>Name:</label>
+                            <div className="col-7">{mouseOverItem.name}</div>
+                            <label className={`col-5`} style={{ color: mouseOverItem.color }}>Description:</label>
+                            <div className="col-7">{mouseOverItem.desc}</div>
+                            <label className={`col-5`} style={{ color: mouseOverItem.color }}>Type:</label>
+                            <div className="col-7">{mouseOverItem.activity_type}</div>
+                            <label className={`col-5`} style={{ color: mouseOverItem.color }}>Stations:</label>
+                            {/* <div className="col-7"><ListBox options={mouseOverItem.stations} /></div> */}
+                            <div className="col-7 station-list">
+                                {mouseOverItem.stations.map((station, index) => (
+                                    <div key={`stn-${index}`}>{station}</div>
+                                ))}
+                            </div>
+                            <label className={`col-5`} style={{ color: mouseOverItem.color }}>Project:</label>
+                            <div className="col-7">{mouseOverItem.project ? mouseOverItem.project : "-"}</div>
+                            <label className={`col-5`} style={{ color: mouseOverItem.color }}>Start Time:</label>
+                            <div className="col-7">{mouseOverItem.start_time.format(UIConstants.CALENDAR_DATETIME_FORMAT)}</div>
+                            <label className={`col-5`} style={{ color: mouseOverItem.color }}>End Time:</label>
+                            <div className="col-7">{mouseOverItem.end_time.format(UIConstants.CALENDAR_DATETIME_FORMAT)}</div>
+                            {/* <label className={`col-5`} style={{color: mouseOverItem.color}}>Stations:</label>
                         <div className="col-7">{mouseOverItem.stations.groups}:{mouseOverItem.stations.counts}</div> */}
-                        <label className={`col-5`} style={{color: mouseOverItem.color}}>Duration:</label>
-                        <div className="col-7">{mouseOverItem.duration}</div>
-                        <label className={`col-5`} style={{color: mouseOverItem.color}}>Planned:</label>
-                        <div className="col-7">{mouseOverItem.planned?'Yes':'No'}</div>
-                    </div>
-                }
+                            <label className={`col-5`} style={{ color: mouseOverItem.color }}>Duration:</label>
+                            <div className="col-7">{mouseOverItem.duration}</div>
+                            <label className={`col-5`} style={{ color: mouseOverItem.color }}>Planned:</label>
+                            <div className="col-7">{mouseOverItem.planned ? 'Yes' : 'No'}</div>
+                        </div>
+                    }
                 </OverlayPanel>
                 {!this.state.isLoading &&
-                    <Websocket url={process.env.REACT_APP_WEBSOCKET_URL} onOpen={this.onConnect} onMessage={this.handleData} onClose={this.onDisconnect} /> }
-               </React.Fragment>
-            
+                    <Websocket url={process.env.REACT_APP_WEBSOCKET_URL} onOpen={this.onConnect} onMessage={this.handleData} onClose={this.onDisconnect} />}
+            </React.Fragment>
+
         );
     }
 
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/week.view.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/week.view.js
index b4450b421a3ddf0d1c468a14ab538c24a7f1dc85..a260824d3f52aa7ebca2ee9a94edfd38548fb718 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/week.view.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/week.view.js
@@ -1,4 +1,4 @@
-import React, {Component} from 'react';
+import React, { Component } from 'react';
 import { Redirect } from 'react-router-dom/cjs/react-router-dom.min';
 import moment from 'moment';
 import _ from 'lodash';
@@ -28,34 +28,40 @@ import { Dropdown } from 'primereact/dropdown';
 import ReservationSummary from '../Reservation/reservation.summary';
 
 // Color constant for status
-const STATUS_COLORS = { "ERROR": "FF0000", "CANCELLED": "#00FF00", "DEFINED": "#00BCD4", 
-                        "SCHEDULABLE":"#0000FF", "SCHEDULED": "#abc", "OBSERVING": "#bcd",
-                        "OBSERVED": "#cde", "PROCESSING": "#cddc39", "PROCESSED": "#fed",
-                        "INGESTING": "#edc", "FINISHED": "#47d53d"};
+const STATUS_COLORS = {
+    "ERROR": "FF0000", "CANCELLED": "#00FF00", "DEFINED": "#00BCD4",
+    "SCHEDULABLE": "#0000FF", "SCHEDULED": "#abc", "OBSERVING": "#bcd",
+    "OBSERVED": "#cde", "PROCESSING": "#cddc39", "PROCESSED": "#fed",
+    "INGESTING": "#edc", "FINISHED": "#47d53d"
+};
 
-const RESERVATION_COLORS = {"true-true":{bgColor:"lightgrey", color:"#585859"}, "true-false":{bgColor:'#585859', color:"white"},
-                            "false-true":{bgColor:"#9b9999", color:"white"}, "false-false":{bgColor:"black", color:"white"}};
+const RESERVATION_COLORS = {
+    "true-true": { bgColor: "lightgrey", color: "#585859" }, "true-false": { bgColor: '#585859', color: "white" },
+    "false-true": { bgColor: "#9b9999", color: "white" }, "false-false": { bgColor: "black", color: "white" }
+};
 
 /**
  * Scheduling Unit timeline view component to view SU List and timeline
  */
 export class WeekTimelineView extends Component {
-
+    lsKeySortColumn = 'SortDataWeekTimelineView-WeekView';
+    defaultSortColumn = [];
     constructor(props) {
         super(props);
+        this.setToggleBySorting();
         this.state = {
             isLoading: true,
             suBlueprints: [],       // Scheduling Unit Blueprints
             suDrafts: [],           // Scheduling Unit Drafts
             suBlueprintList: [],    // SU Blueprints filtered to view
-            group:[],               // Timeline group from scheduling unit draft name
-            items:[],               // Timeline items from scheduling unit blueprints grouped by scheduling unit draft
+            group: [],               // Timeline group from scheduling unit draft name
+            items: [],               // Timeline items from scheduling unit blueprints grouped by scheduling unit draft
             isSUListVisible: true,
             isSUDetsVisible: false,
             canExtendSUList: true,
             canShrinkSUList: false,
             selectedItem: null,
-            suTaskList:[],
+            suTaskList: [],
             isSummaryLoading: false,
             stationGroup: [],
             reservationEnabled: true
@@ -65,10 +71,10 @@ export class WeekTimelineView extends Component {
         this.reservations = [];
         this.reservationReasons = [];
         this.optionsMenu = React.createRef();
-        this.menuOptions = [ {label:'Add Reservation', icon: "fa fa-", command: () => {this.selectOptionMenu('Add Reservation')}}, 
-                            {label:'Reservation List', icon: "fa fa-", command: () => {this.selectOptionMenu('Reservation List')}},
-                           ];
-        
+        this.menuOptions = [{ label: 'Add Reservation', icon: "fa fa-", command: () => { this.selectOptionMenu('Add Reservation') } },
+        { label: 'Reservation List', icon: "fa fa-", command: () => { this.selectOptionMenu('Reservation List') } },
+        ];
+
         this.showOptionMenu = this.showOptionMenu.bind(this);
         this.selectOptionMenu = this.selectOptionMenu.bind(this);
         this.onItemClick = this.onItemClick.bind(this);
@@ -88,25 +94,26 @@ export class WeekTimelineView extends Component {
     }
 
     async componentDidMount() {
+        this.setToggleBySorting();
         UtilService.getReservationTemplates().then(templates => {
-            this.reservationTemplate = templates.length>0?templates[0]:null;
+            this.reservationTemplate = templates.length > 0 ? templates[0] : null;
             if (this.reservationTemplate) {
                 let reasons = this.reservationTemplate.schema.properties.activity.properties.type.enum;
                 for (const reason of reasons) {
-                    this.reservationReasons.push({name: reason});
+                    this.reservationReasons.push({ name: reason });
                 }
             }
         });
-        
+
         // Fetch all details from server and prepare data to pass to timeline and table components
-        const promises = [  ProjectService.getProjectList(), 
-                            ScheduleService.getSchedulingUnitsExtended('blueprint'),
-                            ScheduleService.getSchedulingUnitDraft(),
-                            ScheduleService.getSchedulingSets(),
-                            UtilService.getUTC(),
-                            TaskService.getSubtaskTemplates(),
-                            UtilService.getReservations()] ;
-        Promise.all(promises).then(async(responses) => {
+        const promises = [ProjectService.getProjectList(),
+        ScheduleService.getSchedulingUnitsExtended('blueprint'),
+        ScheduleService.getSchedulingUnitDraft(),
+        ScheduleService.getSchedulingSets(),
+        UtilService.getUTC(),
+        TaskService.getSubtaskTemplates(),
+        UtilService.getReservations()];
+        Promise.all(promises).then(async (responses) => {
             this.subtaskTemplates = responses[5];
             const projects = responses[0];
             const suBlueprints = _.sortBy(responses[1], 'name');
@@ -119,15 +126,15 @@ export class WeekTimelineView extends Component {
             const defaultEndTime = moment.utc().day(8).hour(23).minutes(59).seconds(59);
             for (const count of _.range(11)) {
                 const groupDate = defaultStartTime.clone().add(count, 'days');
-                group.push({'id': groupDate.format("MMM DD ddd"), title: groupDate.format("MMM DD  - ddd"), value: groupDate});
+                group.push({ 'id': groupDate.format("MMM DD ddd"), title: groupDate.format("MMM DD  - ddd"), value: groupDate });
             }
             let suList = [];
             for (const suDraft of suDrafts) {
-                const suSet = suSets.find((suSet) => { return suDraft.scheduling_set_id===suSet.id});
-                const project = projects.find((project) => { return suSet.project_id===project.name});
+                const suSet = suSets.find((suSet) => { return suDraft.scheduling_set_id === suSet.id });
+                const project = projects.find((project) => { return suSet.project_id === project.name });
                 if (suDraft.scheduling_unit_blueprints.length > 0) {
                     for (const suBlueprintId of suDraft.scheduling_unit_blueprints_ids) {
-                        const suBlueprint = _.find(suBlueprints, {'id': suBlueprintId});
+                        const suBlueprint = _.find(suBlueprints, { 'id': suBlueprintId });
                         suBlueprint['actionpath'] = `/schedulingunit/view/blueprint/${suBlueprintId}`;
                         suBlueprint.suDraft = suDraft;
                         suBlueprint.project = project.name;
@@ -136,23 +143,23 @@ export class WeekTimelineView extends Component {
                         suBlueprint.duration = UnitConverter.getSecsToHHmmss(suBlueprint.duration);
                         suBlueprint.tasks = suBlueprint.task_blueprints;
                         // Select only blueprints with start_time and stop_time in the default time limit
-                        if (suBlueprint.start_time && 
+                        if (suBlueprint.start_time &&
                             ((moment.utc(suBlueprint.start_time).isBetween(defaultStartTime, defaultEndTime) ||
-                             moment.utc(suBlueprint.stop_time).isBetween(defaultStartTime, defaultEndTime))	 
-                             || (moment.utc(suBlueprint.start_time).isSameOrBefore(defaultStartTime, defaultEndTime) && 
-                                 moment.utc(suBlueprint.stop_time).isSameOrAfter(defaultStartTime, defaultEndTime)))) {
+                                moment.utc(suBlueprint.stop_time).isBetween(defaultStartTime, defaultEndTime))
+                                || (moment.utc(suBlueprint.start_time).isSameOrBefore(defaultStartTime, defaultEndTime) &&
+                                    moment.utc(suBlueprint.stop_time).isSameOrAfter(defaultStartTime, defaultEndTime)))) {
 
                             const startTime = moment.utc(suBlueprint.start_time);
                             const endTime = moment.utc(suBlueprint.stop_time);
                             if (startTime.format("MM-DD-YYYY") !== endTime.format("MM-DD-YYYY")) {
-                               let suBlueprintStart = _.cloneDeep(suBlueprint);
+                                let suBlueprintStart = _.cloneDeep(suBlueprint);
                                 let suBlueprintEnd = _.cloneDeep(suBlueprint);
                                 suBlueprintStart.stop_time = startTime.hour(23).minutes(59).seconds(59).format('YYYY-MM-DDTHH:mm:ss.00000');
                                 suBlueprintEnd.start_time = endTime.hour(0).minutes(0).seconds(0).format('YYYY-MM-DDTHH:mm:ss.00000');
                                 items.push(await this.getTimelineItem(suBlueprintStart, currentUTC));
                                 items.push(await this.getTimelineItem(suBlueprintEnd, currentUTC));
-                            
-                            }   else {
+
+                            } else {
                                 items.push(await this.getTimelineItem(suBlueprint, currentUTC));
                             }
                             suList.push(suBlueprint);
@@ -163,7 +170,7 @@ export class WeekTimelineView extends Component {
                                 const template = _.find(this.subtaskTemplates, ['id', subtask.specifications_template_id]);
                                 return (template && template.name.indexOf('control')) > 0;
                             });
-                            task.subTaskID = subTaskIds.length ? subTaskIds[0].id : ''; 
+                            task.subTaskID = subTaskIds.length ? subTaskIds[0].id : '';
                             if (task.specifications_template.type_value.toLowerCase() === "observation") {
                                 task.antenna_set = task.specifications_doc.antenna_set;
                                 task.band = task.specifications_doc.filter;
@@ -182,18 +189,36 @@ export class WeekTimelineView extends Component {
             ScheduleService.getSchedulingConstraintTemplates()
                 .then(suConstraintTemplates => {
                     this.suConstraintTemplates = suConstraintTemplates;
+                });
+            this.setState({
+                suBlueprints: suBlueprints, suDrafts: suDrafts, group: _.sortBy(group, ['value']), suSets: suSets,
+                projects: projects, suBlueprintList: suList,
+                items: items, currentUTC: currentUTC, isLoading: false,
+                startTime: defaultStartTime, endTime: defaultEndTime
             });
-            this.setState({suBlueprints: suBlueprints, suDrafts: suDrafts, group: _.sortBy(group, ['value']), suSets: suSets,
-                            projects: projects, suBlueprintList: suList, 
-                            items: items, currentUTC: currentUTC, isLoading: false,
-                            startTime: defaultStartTime, endTime: defaultEndTime
-                        });
         });
         // Get maingroup and its stations. This grouping is used to show count of stations used against each group.
         ScheduleService.getMainGroupStations()
-            .then(stationGroups => {this.mainStationGroups = stationGroups});
+            .then(stationGroups => { this.mainStationGroups = stationGroups });
     }
 
+    toggleBySorting = (sortData) => {
+        UtilService.localStore({ type: 'set', key: this.lsKeySortColumn, value: sortData });
+    }
+
+    setToggleBySorting() {    
+        let sortData = UtilService.localStore({ type: 'get', key: this.lsKeySortColumn });
+        if (sortData) {
+            if (Object.prototype.toString.call(sortData) === '[object Array]') {
+                this.defaultSortColumn = sortData;
+            }
+            else {
+                this.defaultSortColumn = [{ ...sortData }];
+            }
+        }
+        this.defaultSortColumn = this.defaultSortColumn || [];
+        UtilService.localStore({ type: 'set', key: this.lsKeySortColumn, value: [...this.defaultSortColumn] });
+    }
     /**
      * Function to get/prepare Item object to be passed to Timeline component
      * @param {Object} suBlueprint 
@@ -201,27 +226,29 @@ export class WeekTimelineView extends Component {
     async getTimelineItem(suBlueprint, displayDate) {
         let antennaSet = "";
         for (let task of suBlueprint.tasks) {
-            if (task.specifications_template.type_value.toLowerCase() === "observation" 
+            if (task.specifications_template.type_value.toLowerCase() === "observation"
                 && task.specifications_doc.antenna_set) {
                 antennaSet = task.specifications_doc.antenna_set;
             }
         }
-        let item = { id: `${suBlueprint.id}-${suBlueprint.start_time}`, 
+        let item = {
+            id: `${suBlueprint.id}-${suBlueprint.start_time}`,
             suId: suBlueprint.id,
             group: moment.utc(suBlueprint.start_time).format("MMM DD ddd"),
             title: "",
             project: suBlueprint.project,
             name: suBlueprint.name,
-            band: antennaSet?antennaSet.split("_")[0]:"",
+            band: antennaSet ? antennaSet.split("_")[0] : "",
             antennaSet: antennaSet,
             scheduleMethod: suBlueprint.suDraft.scheduling_constraints_doc.scheduler,
-            duration: suBlueprint.durationInSec?`${(suBlueprint.durationInSec/3600).toFixed(2)}Hrs`:"",
+            duration: suBlueprint.durationInSec ? `${(suBlueprint.durationInSec / 3600).toFixed(2)}Hrs` : "",
             start_time: moment.utc(`${displayDate.format('YYYY-MM-DD')} ${suBlueprint.start_time.split('T')[1]}`),
             end_time: moment.utc(`${displayDate.format('YYYY-MM-DD')} ${suBlueprint.stop_time.split('T')[1]}`),
-            bgColor: suBlueprint.status? STATUS_COLORS[suBlueprint.status.toUpperCase()]:"#2196f3",
+            bgColor: suBlueprint.status ? STATUS_COLORS[suBlueprint.status.toUpperCase()] : "#2196f3",
             selectedBgColor: 'none',
             type: 'SCHEDULE',
-            status: suBlueprint.status.toLowerCase()};
+            status: suBlueprint.status.toLowerCase()
+        };
         return item;
     }
 
@@ -229,10 +256,10 @@ export class WeekTimelineView extends Component {
      * Callback function to pass to Timeline component for item click.
      * @param {Object} item 
      */
-     onItemClick(item) {
-        if (item.type === "SCHEDULE") { 
+    onItemClick(item) {
+        if (item.type === "SCHEDULE") {
             this.showSUSummary(item);
-        }   else if (item.type === "RESERVATION") {
+        } else if (item.type === "RESERVATION") {
             this.showReservationSummary(item);
         }
     }
@@ -242,36 +269,42 @@ export class WeekTimelineView extends Component {
      * @param {Object} item - Timeline SU item object.
      */
     showSUSummary(item) {
-        if (this.state.isSUDetsVisible && item.id===this.state.selectedItem.id) {
+        if (this.state.isSUDetsVisible && item.id === this.state.selectedItem.id) {
             this.closeSUDets();
-        }   else {
-            const fetchDetails = !this.state.selectedItem || item.id!==this.state.selectedItem.id
-            this.setState({selectedItem: item, isSUDetsVisible: true, 
+        } else {
+            const fetchDetails = !this.state.selectedItem || item.id !== this.state.selectedItem.id
+            this.setState({
+                selectedItem: item, isSUDetsVisible: true,
                 isSummaryLoading: fetchDetails,
-                suTaskList: !fetchDetails?this.state.suTaskList:[],
-                canExtendSUList: false, canShrinkSUList:false});
+                suTaskList: !fetchDetails ? this.state.suTaskList : [],
+                canExtendSUList: false, canShrinkSUList: false
+            });
             if (fetchDetails) {
-                const suBlueprint = _.find(this.state.suBlueprints, {id: parseInt(item.id.split('-')[0])});
-                const suConstraintTemplate = _.find(this.suConstraintTemplates, {id: suBlueprint.suDraft.scheduling_constraints_template_id});
+                const suBlueprint = _.find(this.state.suBlueprints, { id: parseInt(item.id.split('-')[0]) });
+                const suConstraintTemplate = _.find(this.suConstraintTemplates, { id: suBlueprint.suDraft.scheduling_constraints_template_id });
                 /* If tasks are not loaded on component mounting fetch from API */
                 if (suBlueprint.tasks) {
-                    this.setState({suTaskList: _.sortBy(suBlueprint.tasks, "id"), suConstraintTemplate: suConstraintTemplate, 
-                                    stationGroup: suBlueprint.stations, isSummaryLoading: false})
-                }   else {
+                    this.setState({
+                        suTaskList: _.sortBy(suBlueprint.tasks, "id"), suConstraintTemplate: suConstraintTemplate,
+                        stationGroup: suBlueprint.stations, isSummaryLoading: false
+                    })
+                } else {
                     ScheduleService.getTaskBPWithSubtaskTemplateOfSU(suBlueprint)
                         .then(taskList => {
                             for (let task of taskList) {
                                 //Control Task ID
                                 const subTaskIds = (task.subTasks || []).filter(sTask => sTask.subTaskTemplate.name.indexOf('control') > 1);
-                                task.subTaskID = subTaskIds.length ? subTaskIds[0].id : ''; 
+                                task.subTaskID = subTaskIds.length ? subTaskIds[0].id : '';
                                 if (task.template.type_value.toLowerCase() === "observation"
                                     && task.specifications_doc.antenna_set) {
                                     task.antenna_set = task.specifications_doc.antenna_set;
                                     task.band = task.specifications_doc.filter;
                                 }
                             }
-                            this.setState({suTaskList: _.sortBy(taskList, "id"), isSummaryLoading: false, 
-                                            stationGroup: this.getSUStations(suBlueprint)})
+                            this.setState({
+                                suTaskList: _.sortBy(taskList, "id"), isSummaryLoading: false,
+                                stationGroup: this.getSUStations(suBlueprint)
+                            })
                         });
                 }
                 // Get the scheduling constraint template of the selected SU block
@@ -287,15 +320,15 @@ export class WeekTimelineView extends Component {
      * To load and show Reservation summary
      * @param {Object} item 
      */
-     showReservationSummary(item) {
-        this.setState({selectedItem: item, isReservDetsVisible: true, isSUDetsVisible: false});
+    showReservationSummary(item) {
+        this.setState({ selectedItem: item, isReservDetsVisible: true, isSUDetsVisible: false });
     }
 
     /**
      * Closes the SU details section
      */
     closeSUDets() {
-        this.setState({isSUDetsVisible: false, isReservDetsVisible: false, canExtendSUList: true, canShrinkSUList: false});
+        this.setState({ isSUDetsVisible: false, isReservDetsVisible: false, canExtendSUList: true, canShrinkSUList: false });
     }
 
     /**
@@ -313,10 +346,10 @@ export class WeekTimelineView extends Component {
      */
     onItemMouseOver(evt, item) {
         if (item.type === "SCHEDULE") {
-            const itemSU = _.find(this.state.suBlueprints, {id: parseInt(item.id.split("-")[0])});
+            const itemSU = _.find(this.state.suBlueprints, { id: parseInt(item.id.split("-")[0]) });
             const itemStations = itemSU.stations;
             const itemStationGroups = this.groupSUStations(itemStations);
-            item.stations = {groups: "", counts: ""};
+            item.stations = { groups: "", counts: "" };
             for (const stationgroup of _.keys(itemStationGroups)) {
                 let groups = item.stations.groups;
                 let counts = item.stations.counts;
@@ -324,15 +357,15 @@ export class WeekTimelineView extends Component {
                     groups = groups.concat("/");
                     counts = counts.concat("/");
                 }
-                groups = groups.concat(stationgroup.substring(0,1).concat('S'));
+                groups = groups.concat(stationgroup.substring(0, 1).concat('S'));
                 counts = counts.concat(itemStationGroups[stationgroup].length);
                 item.stations.groups = groups;
                 item.stations.counts = counts;
                 item.suStartTime = moment.utc(itemSU.start_time);
                 item.suStopTime = moment.utc(itemSU.stop_time);
             }
-        }   else {
-            const reservation = _.find(this.reservations, {'id': parseInt(item.id.split("-")[1])});
+        } else {
+            const reservation = _.find(this.reservations, { 'id': parseInt(item.id.split("-")[1]) });
             const reservStations = reservation.specifications_doc.resources.stations;
             // const reservStationGroups = this.groupSUStations(reservStations);
             item.name = reservation.name;
@@ -341,10 +374,10 @@ export class WeekTimelineView extends Component {
             item.stations = reservStations;
             item.planned = reservation.specifications_doc.activity.planned;
             item.displayStartTime = moment.utc(reservation.start_time);
-            item.displayEndTime = reservation.duration?moment.utc(reservation.stop_time):null;
+            item.displayEndTime = reservation.duration ? moment.utc(reservation.stop_time) : null;
         }
         this.popOver.toggle(evt);
-        this.setState({mouseOverItem: item});
+        this.setState({ mouseOverItem: item });
     }
 
     /**
@@ -366,7 +399,7 @@ export class WeekTimelineView extends Component {
     getSUStations(suBlueprint) {
         let stations = [];
         /* Get all observation tasks */
-        const observationTasks = _.filter(suBlueprint.tasks, (task) => { return task.specifications_template.type_value.toLowerCase() === "observation"});
+        const observationTasks = _.filter(suBlueprint.tasks, (task) => { return task.specifications_template.type_value.toLowerCase() === "observation" });
         for (const observationTask of observationTasks) {
             /** If the status of SU is before scheduled, get all stations from the station_groups from the task specification_docs */
             if (this.STATUS_BEFORE_SCHEDULED.indexOf(suBlueprint.status.toLowerCase()) >= 0
@@ -374,8 +407,8 @@ export class WeekTimelineView extends Component {
                 for (const grpStations of _.map(observationTask.specifications_doc.station_groups, "stations")) {
                     stations = _.concat(stations, grpStations);
                 }
-            }   else if (this.STATUS_BEFORE_SCHEDULED.indexOf(suBlueprint.status.toLowerCase()) < 0 
-                            && observationTask.subtasks) {
+            } else if (this.STATUS_BEFORE_SCHEDULED.indexOf(suBlueprint.status.toLowerCase()) < 0
+                && observationTask.subtasks) {
                 /** If the status of SU is scheduled or after get the stations from the subtask specification tasks */
                 for (const subtask of observationTask.subtasks) {
                     if (subtask.specifications_doc.stations) {
@@ -393,21 +426,21 @@ export class WeekTimelineView extends Component {
      * @param {moment} endTime 
      */
     async dateRangeCallback(startTime, endTime, refreshData) {
-        let suBlueprintList = [], group=[], items = [];
+        let suBlueprintList = [], group = [], items = [];
         let currentUTC = this.state.currentUTC;
         if (refreshData) {
             for (const count of _.range(11)) {
                 const groupDate = startTime.clone().add(count, 'days');
-                group.push({'id': groupDate.format("MMM DD ddd"), title: groupDate.format("MMM DD  - ddd"), value: groupDate});
+                group.push({ 'id': groupDate.format("MMM DD ddd"), title: groupDate.format("MMM DD  - ddd"), value: groupDate });
             }
             let direction = startTime.week() - this.state.startTime.week();
             currentUTC = this.state.currentUTC.clone().add(direction * 7, 'days');
             if (startTime && endTime) {
                 for (const suBlueprint of this.state.suBlueprints) {
-                    if (moment.utc(suBlueprint.start_time).isBetween(startTime, endTime) 
-                            || moment.utc(suBlueprint.stop_time).isBetween(startTime, endTime)
-                            || (moment.utc(suBlueprint.start_time).isSameOrBefore(startTime, endTime) && 
-                                 moment.utc(suBlueprint.stop_time).isSameOrAfter(startTime, endTime))) {
+                    if (moment.utc(suBlueprint.start_time).isBetween(startTime, endTime)
+                        || moment.utc(suBlueprint.stop_time).isBetween(startTime, endTime)
+                        || (moment.utc(suBlueprint.start_time).isSameOrBefore(startTime, endTime) &&
+                            moment.utc(suBlueprint.stop_time).isSameOrAfter(startTime, endTime))) {
                         suBlueprintList.push(suBlueprint);
                         const suStartTime = moment.utc(suBlueprint.start_time);
                         const suEndTime = moment.utc(suBlueprint.stop_time);
@@ -418,29 +451,31 @@ export class WeekTimelineView extends Component {
                             suBlueprintEnd.start_time = suEndTime.hour(0).minutes(0).seconds(0).format('YYYY-MM-DDTHH:mm:ss.00000');
                             items.push(await this.getTimelineItem(suBlueprintStart, currentUTC));
                             items.push(await this.getTimelineItem(suBlueprintEnd, currentUTC));
-                        
-                        }   else {
+
+                        } else {
                             items.push(await this.getTimelineItem(suBlueprint, currentUTC));
                         }
-                    } 
+                    }
                 }
                 if (this.state.reservationEnabled) {
                     items = this.addWeekReservations(items, startTime, endTime, currentUTC);
                 }
-            }   else {
+            } else {
                 suBlueprintList = _.clone(this.state.suBlueprints);
                 group = this.state.group;
                 items = this.state.items;
             }
-            this.setState({suBlueprintList: _.filter(suBlueprintList, (suBlueprint) => {return suBlueprint.start_time!=null}),
-                            group: group, items: items, currentUTC: currentUTC, startTime: startTime, endTime: endTime});
+            this.setState({
+                suBlueprintList: _.filter(suBlueprintList, (suBlueprint) => { return suBlueprint.start_time != null }),
+                group: group, items: items, currentUTC: currentUTC, startTime: startTime, endTime: endTime
+            });
             // On range change close the Details pane
             // this.closeSUDets();
-        }   else {
+        } else {
             group = this.state.group;
             items = this.state.items;
         }
-        return {group: group, items: items};
+        return { group: group, items: items };
     }
 
     /**
@@ -452,14 +487,14 @@ export class WeekTimelineView extends Component {
         let canShrinkSUList = this.state.canShrinkSUList;
         if (step === 1) {
             // Can Extend when fully shrunk and still extendable
-            canExtendSUList = (!canShrinkSUList && canExtendSUList)?true:false;
+            canExtendSUList = (!canShrinkSUList && canExtendSUList) ? true : false;
             canShrinkSUList = true;
-        }   else {
+        } else {
             // Can Shrink when fully extended and still shrinkable
-            canShrinkSUList = (canShrinkSUList && !canExtendSUList)?true:false;
+            canShrinkSUList = (canShrinkSUList && !canExtendSUList) ? true : false;
             canExtendSUList = true;
         }
-        this.setState({canExtendSUList: canExtendSUList, canShrinkSUList: canShrinkSUList});
+        this.setState({ canExtendSUList: canExtendSUList, canShrinkSUList: canShrinkSUList });
     }
 
     /**
@@ -482,7 +517,7 @@ export class WeekTimelineView extends Component {
     }
 
     filterByProject(project) {
-        this.setState({selectedProject: project});
+        this.setState({ selectedProject: project });
     }
 
     showOptionMenu(event) {
@@ -490,13 +525,13 @@ export class WeekTimelineView extends Component {
     }
 
     selectOptionMenu(menuName) {
-        switch(menuName) {
+        switch (menuName) {
             case 'Reservation List': {
-                this.setState({redirect: `/reservation/list`});
+                this.setState({ redirect: `/reservation/list` });
                 break;
             }
             case 'Add Reservation': {
-                this.setState({redirect: `/reservation/create`});
+                this.setState({ redirect: `/reservation/create` });
                 break;
             }
             default: {
@@ -528,7 +563,7 @@ export class WeekTimelineView extends Component {
             const jsonData = JSON.parse(data);
             if (jsonData.action === 'create') {
                 this.addNewData(jsonData.object_details.id, jsonData.object_type, jsonData.object_details);
-            }   else if (jsonData.action === 'update') {
+            } else if (jsonData.action === 'update') {
                 this.updateExistingData(jsonData.object_details.id, jsonData.object_type, jsonData.object_details);
             }
         }
@@ -541,18 +576,18 @@ export class WeekTimelineView extends Component {
      * @param {Object} object - model object with certain properties
      */
     addNewData(id, type, object) {
-        switch(type) {
+        switch (type) {
             /* When a new scheduling_unit_draft is created, it should be added to the existing list of suDraft. */
             case 'scheduling_unit_draft': {
                 let suDrafts = this.state.suDrafts;
                 let suSets = this.state.suSets;
                 ScheduleService.getSchedulingUnitDraftById(id)
-                .then(suDraft => {
-                    suDrafts.push(suDraft);
-                    _.remove(suSets, function(suSet) { return suSet.id === suDraft.scheduling_set_id});
-                    suSets.push(suDraft.scheduling_set_object);
-                    this.setState({suSet: suSets, suDrafts: suDrafts});
-                });
+                    .then(suDraft => {
+                        suDrafts.push(suDraft);
+                        _.remove(suSets, function (suSet) { return suSet.id === suDraft.scheduling_set_id });
+                        suSets.push(suDraft.scheduling_set_object);
+                        this.setState({ suSet: suSets, suDrafts: suDrafts });
+                    });
                 break;
             }
             case 'scheduling_unit_blueprint': {
@@ -576,7 +611,7 @@ export class WeekTimelineView extends Component {
      */
     updateExistingData(id, type, object) {
         const objectProps = ['status', 'start_time', 'stop_time', 'duration'];
-        switch(type) {
+        switch (type) {
             case 'scheduling_unit_blueprint': {
                 let suBlueprints = this.state.suBlueprints;
                 let existingSUB = _.find(suBlueprints, ['id', id]);
@@ -594,7 +629,7 @@ export class WeekTimelineView extends Component {
                 // }
                 break;
             }
-            default: { break;}
+            default: { break; }
         }
     }
 
@@ -605,46 +640,46 @@ export class WeekTimelineView extends Component {
      */
     updateSchedulingUnit(id) {
         ScheduleService.getSchedulingUnitExtended('blueprint', id, true)
-        .then(async(suBlueprint) => {
-            const suDraft = _.find(this.state.suDrafts, ['id', suBlueprint.draft_id]);
-            const suSet = this.state.suSets.find((suSet) => { return suDraft.scheduling_set_id===suSet.id});
-            const project = this.state.projects.find((project) => { return suSet.project_id===project.name});
-            let suBlueprints = this.state.suBlueprints;
-            suBlueprint['actionpath'] = `/schedulingunit/view/blueprint/${id}`;
-            suBlueprint.suDraft = suDraft;
-            suBlueprint.project = project.name;
-            suBlueprint.suSet = suSet;
-            suBlueprint.durationInSec = suBlueprint.duration;
-            suBlueprint.duration = UnitConverter.getSecsToHHmmss(suBlueprint.duration);
-            suBlueprint.tasks = suBlueprint.task_blueprints;
-            // Add Subtask Id as control id for task if subtask type us control. Also add antenna_set & band prpoerties to the task object.
-            for (let task of suBlueprint.tasks) {
-                const subTaskIds = task.subtasks.filter(subtask => {
-                    const template = _.find(this.subtaskTemplates, ['id', subtask.specifications_template_id]);
-                    return (template && template.name.indexOf('control')) > 0;
-                });
-                task.subTaskID = subTaskIds.length ? subTaskIds[0].id : ''; 
-                if (task.specifications_template.type_value.toLowerCase() === "observation"
-                    && task.specifications_doc.antenna_set) {
-                    task.antenna_set = task.specifications_doc.antenna_set;
-                    task.band = task.specifications_doc.filter;
+            .then(async (suBlueprint) => {
+                const suDraft = _.find(this.state.suDrafts, ['id', suBlueprint.draft_id]);
+                const suSet = this.state.suSets.find((suSet) => { return suDraft.scheduling_set_id === suSet.id });
+                const project = this.state.projects.find((project) => { return suSet.project_id === project.name });
+                let suBlueprints = this.state.suBlueprints;
+                suBlueprint['actionpath'] = `/schedulingunit/view/blueprint/${id}`;
+                suBlueprint.suDraft = suDraft;
+                suBlueprint.project = project.name;
+                suBlueprint.suSet = suSet;
+                suBlueprint.durationInSec = suBlueprint.duration;
+                suBlueprint.duration = UnitConverter.getSecsToHHmmss(suBlueprint.duration);
+                suBlueprint.tasks = suBlueprint.task_blueprints;
+                // Add Subtask Id as control id for task if subtask type us control. Also add antenna_set & band prpoerties to the task object.
+                for (let task of suBlueprint.tasks) {
+                    const subTaskIds = task.subtasks.filter(subtask => {
+                        const template = _.find(this.subtaskTemplates, ['id', subtask.specifications_template_id]);
+                        return (template && template.name.indexOf('control')) > 0;
+                    });
+                    task.subTaskID = subTaskIds.length ? subTaskIds[0].id : '';
+                    if (task.specifications_template.type_value.toLowerCase() === "observation"
+                        && task.specifications_doc.antenna_set) {
+                        task.antenna_set = task.specifications_doc.antenna_set;
+                        task.band = task.specifications_doc.filter;
+                    }
                 }
-            }
-            // Get stations involved for this SUB
-            let stations = this.getSUStations(suBlueprint);
-            suBlueprint.stations = _.uniq(stations);
-            // Remove the old SUB object from the existing list and add the newly fetched SUB
-            _.remove(suBlueprints, function(suB) { return suB.id === id});
-            suBlueprints.push(suBlueprint);
-            this.setState({suBlueprints: suBlueprints});
-            // Create timeline group and items
-            let updatedItemGroupData = await this.dateRangeCallback(this.state.startTime, this.state.endTime, true);
-            this.timeline.updateTimeline(updatedItemGroupData);
-        });
+                // Get stations involved for this SUB
+                let stations = this.getSUStations(suBlueprint);
+                suBlueprint.stations = _.uniq(stations);
+                // Remove the old SUB object from the existing list and add the newly fetched SUB
+                _.remove(suBlueprints, function (suB) { return suB.id === id });
+                suBlueprints.push(suBlueprint);
+                this.setState({ suBlueprints: suBlueprints });
+                // Create timeline group and items
+                let updatedItemGroupData = await this.dateRangeCallback(this.state.startTime, this.state.endTime, true);
+                this.timeline.updateTimeline(updatedItemGroupData);
+            });
     }
 
     async showReservations(e) {
-        await this.setState({reservationEnabled: e.value});
+        await this.setState({ reservationEnabled: e.value });
         let updatedItemGroupData = await this.dateRangeCallback(this.state.startTime, this.state.endTime, true);
         this.timeline.updateTimeline(updatedItemGroupData);
     }
@@ -655,28 +690,28 @@ export class WeekTimelineView extends Component {
      * @param {moment} startTime
      * @param {moment} endTime
      */
-     addWeekReservations(items, startTime, endTime, currentUTC) {
+    addWeekReservations(items, startTime, endTime, currentUTC) {
         let reservations = this.reservations;
         for (const reservation of reservations) {
             const reservationStartTime = moment.utc(reservation.start_time);
-            const reservationEndTime = reservation.duration?reservationStartTime.clone().add(reservation.duration, 'seconds'):endTime;
+            const reservationEndTime = reservation.duration ? reservationStartTime.clone().add(reservation.duration, 'seconds') : endTime;
             const reservationSpec = reservation.specifications_doc;
-            if ( (reservationStartTime.isSame(startTime) 
-                    || reservationStartTime.isSame(endTime)                       
-                    || reservationStartTime.isBetween(startTime, endTime)
-                    || reservationEndTime.isSame(startTime) 
-                    || reservationEndTime.isSame(endTime)                       
-                    || reservationEndTime.isBetween(startTime, endTime)
-                    || (reservationStartTime.isSameOrBefore(startTime)
+            if ((reservationStartTime.isSame(startTime)
+                || reservationStartTime.isSame(endTime)
+                || reservationStartTime.isBetween(startTime, endTime)
+                || reservationEndTime.isSame(startTime)
+                || reservationEndTime.isSame(endTime)
+                || reservationEndTime.isBetween(startTime, endTime)
+                || (reservationStartTime.isSameOrBefore(startTime)
                     && reservationEndTime.isSameOrAfter(endTime)))
-                    && (!this.state.reservationFilter ||                                        // No reservation filter added
-                        reservationSpec.activity.type === this.state.reservationFilter) ) {     // Reservation reason == Filtered reaseon
+                && (!this.state.reservationFilter ||                                        // No reservation filter added
+                    reservationSpec.activity.type === this.state.reservationFilter)) {     // Reservation reason == Filtered reaseon
                 reservation.stop_time = reservationEndTime;
                 let splitReservations = this.splitReservations(reservation, startTime, endTime, currentUTC);
                 for (const splitReservation of splitReservations) {
                     items.push(this.getReservationItem(splitReservation, currentUTC));
                 }
-                
+
             }
         }
         return items;
@@ -694,23 +729,23 @@ export class WeekTimelineView extends Component {
         let weekStartDate = moment(startTime).add(-1, 'day').startOf('day');
         let weekEndDate = moment(endTime).add(1, 'day').startOf('day');
         let splitReservations = [];
-        while(weekStartDate.add(1, 'days').diff(weekEndDate) < 0) {
+        while (weekStartDate.add(1, 'days').diff(weekEndDate) < 0) {
             const dayStart = weekStartDate.clone().startOf('day');
             const dayEnd = weekStartDate.clone().endOf('day');
             let splitReservation = null;
-            if (reservationStartTime.isSameOrBefore(dayStart) && 
+            if (reservationStartTime.isSameOrBefore(dayStart) &&
                 (reservation.stop_time.isBetween(dayStart, dayEnd) ||
                     reservation.stop_time.isSameOrAfter(dayEnd))) {
                 splitReservation = _.cloneDeep(reservation);
                 splitReservation.start_time = moment.utc(dayStart.format("YYYY-MM-DD HH:mm:ss"));
-            }   else if(reservationStartTime.isBetween(dayStart, dayEnd)) {
+            } else if (reservationStartTime.isBetween(dayStart, dayEnd)) {
                 splitReservation = _.cloneDeep(reservation);
-                splitReservation.start_time = reservationStartTime;                
+                splitReservation.start_time = reservationStartTime;
             }
             if (splitReservation) {
                 if (!reservation.stop_time || reservation.stop_time.isSameOrAfter(dayEnd)) {
                     splitReservation.end_time = weekStartDate.clone().hour(23).minute(59).seconds(59);
-                }   else if (reservation.stop_time.isSameOrBefore(dayEnd)) {
+                } else if (reservation.stop_time.isSameOrBefore(dayEnd)) {
                     splitReservation.end_time = weekStartDate.clone().hour(reservation.stop_time.hours()).minutes(reservation.stop_time.minutes()).seconds(reservation.stop_time.seconds);
                 }
                 splitReservations.push(splitReservation);
@@ -728,17 +763,18 @@ export class WeekTimelineView extends Component {
         const reservationSpec = reservation.specifications_doc;
         const group = moment.utc(reservation.start_time).format("MMM DD ddd");
         const blockColor = RESERVATION_COLORS[this.getReservationType(reservationSpec.schedulability)];
-        let item = { id: `Res-${reservation.id}-${group}`,
-                        start_time: moment.utc(`${displayDate.format('YYYY-MM-DD')} ${reservation.start_time.format('HH:mm:ss')}`),
-                        end_time: moment.utc(`${displayDate.format('YYYY-MM-DD')} ${reservation.end_time.format('HH:mm:ss')}`),
-                        name: reservationSpec.activity.type, project: reservation.project_id,
-                        group: group,
-                        type: 'RESERVATION',
-                        title: `${reservationSpec.activity.type}${reservation.project_id?("-"+ reservation.project_id):""}`,
-                        desc: reservation.description,
-                        duration: reservation.duration?UnitConverter.getSecsToHHmmss(reservation.duration):"Unknown",
-                        bgColor: blockColor.bgColor, selectedBgColor: blockColor.bgColor, color: blockColor.color
-                    };
+        let item = {
+            id: `Res-${reservation.id}-${group}`,
+            start_time: moment.utc(`${displayDate.format('YYYY-MM-DD')} ${reservation.start_time.format('HH:mm:ss')}`),
+            end_time: moment.utc(`${displayDate.format('YYYY-MM-DD')} ${reservation.end_time.format('HH:mm:ss')}`),
+            name: reservationSpec.activity.type, project: reservation.project_id,
+            group: group,
+            type: 'RESERVATION',
+            title: `${reservationSpec.activity.type}${reservation.project_id ? ("-" + reservation.project_id) : ""}`,
+            desc: reservation.description,
+            duration: reservation.duration ? UnitConverter.getSecsToHHmmss(reservation.duration) : "Unknown",
+            bgColor: blockColor.bgColor, selectedBgColor: blockColor.bgColor, color: blockColor.color
+        };
         return item;
     }
 
@@ -747,14 +783,14 @@ export class WeekTimelineView extends Component {
      * according to the type.
      * @param {Object} schedulability 
      */
-     getReservationType(schedulability) {
+    getReservationType(schedulability) {
         if (schedulability.manual && schedulability.dynamic) {
             return 'true-true';
-        }   else if (!schedulability.manual && !schedulability.dynamic) {
+        } else if (!schedulability.manual && !schedulability.dynamic) {
             return 'false-false';
-        }   else if (schedulability.manual && !schedulability.dynamic) {
+        } else if (schedulability.manual && !schedulability.dynamic) {
             return 'true-false';
-        }   else {
+        } else {
             return 'false-true';
         }
     }
@@ -764,14 +800,14 @@ export class WeekTimelineView extends Component {
      * @param {String} filter 
      */
     async setReservationFilter(filter) {
-        await this.setState({reservationFilter: filter});
+        await this.setState({ reservationFilter: filter });
         let updatedItemGroupData = await this.dateRangeCallback(this.state.startTime, this.state.endTime, true);
         this.timeline.updateTimeline(updatedItemGroupData);
     }
 
     render() {
         if (this.state.redirect) {
-            return <Redirect to={ {pathname: this.state.redirect} }></Redirect>
+            return <Redirect to={{ pathname: this.state.redirect }}></Redirect>
         }
         const isSUListVisible = this.state.isSUListVisible;
         const isSUDetsVisible = this.state.isSUDetsVisible;
@@ -780,20 +816,20 @@ export class WeekTimelineView extends Component {
         const canShrinkSUList = this.state.canShrinkSUList;
         let suBlueprint = null, reservation = null;
         if (isSUDetsVisible) {
-            suBlueprint = _.find(this.state.suBlueprints, {id: parseInt(this.state.selectedItem.id.split('-')[0])});
+            suBlueprint = _.find(this.state.suBlueprints, { id: parseInt(this.state.selectedItem.id.split('-')[0]) });
         }
         if (isReservDetsVisible) {
-            reservation = _.find(this.reservations, {id: parseInt(this.state.selectedItem.id.split('-')[1])});
+            reservation = _.find(this.reservations, { id: parseInt(this.state.selectedItem.id.split('-')[1]) });
             reservation.project = this.state.selectedItem.project;
         }
         const mouseOverItem = this.state.mouseOverItem;
         return (
             <React.Fragment>
-                 <TieredMenu className="app-header-menu" model={this.menuOptions} popup ref={el => this.optionsMenu = el} />
-                <PageHeader location={this.props.location} title={'Scheduling Units - Week View'} 
+                <TieredMenu className="app-header-menu" model={this.menuOptions} popup ref={el => this.optionsMenu = el} />
+                <PageHeader location={this.props.location} title={'Scheduling Units - Week View'}
                     actions={[
-                        {icon:'fa-bars',title: '', type:'button', actOn:'mouseOver', props : { callback: this.showOptionMenu},},
-                        {icon: 'fa-clock',title:'View Timeline', props : { pathname: `/su/timelineview`}}]}/>
+                        { icon: 'fa-bars', title: '', type: 'button', actOn: 'mouseOver', props: { callback: this.showOptionMenu }, },
+                        { icon: 'fa-clock', title: 'View Timeline', props: { pathname: `/su/timelineview` } }]} />
                 { this.state.isLoading ? <AppLoader /> :
                     <>
                         {/* <div className="p-field p-grid">
@@ -806,10 +842,10 @@ export class WeekTimelineView extends Component {
                         </div> */}
                         <div className="p-grid">
                             {/* SU List Panel */}
-                            <div className={isSUListVisible && (isSUDetsVisible || isReservDetsVisible || 
-                                                (canExtendSUList && !canShrinkSUList)?"col-lg-4 col-md-4 col-sm-12":
-                                                ((canExtendSUList && canShrinkSUList)?"col-lg-5 col-md-5 col-sm-12":"col-lg-6 col-md-6 col-sm-12"))}
-                                 style={isSUListVisible?{position: "inherit", borderRight: "5px solid #efefef", paddingTop: "10px"}:{display: "none"}}>
+                            <div className={isSUListVisible && (isSUDetsVisible || isReservDetsVisible ||
+                                (canExtendSUList && !canShrinkSUList) ? "col-lg-4 col-md-4 col-sm-12" :
+                                ((canExtendSUList && canShrinkSUList) ? "col-lg-5 col-md-5 col-sm-12" : "col-lg-6 col-md-6 col-sm-12"))}
+                                style={isSUListVisible ? { position: "inherit", borderRight: "5px solid #efefef", paddingTop: "10px" } : { display: "none" }}>
                                 <ViewTable viewInNewWindow
                                     data={this.state.suBlueprintList} 
                                     defaultcolumns={[{name: "Name",
@@ -823,110 +859,112 @@ export class WeekTimelineView extends Component {
                                     optionalcolumns={[{project:"Project",description: "Description", duration:"Duration (HH:mm:ss)",actionpath: "actionpath"}]}
                                     columnclassname={[{"Name":"filter-input-100", "Start Time":"filter-input-50", "End Time":"filter-input-50",
                                                         "Duration (HH:mm:ss)" : "filter-input-50",}]}
-                                    defaultSortColumn= {[{id: "Start Time", desc: false}]}
+                                    defaultSortColumn= {this.defaultSortColumn}
                                     showaction="true"
                                     tablename="timeline_scheduleunit_list"
                                     showTopTotal={false}
                                     showGlobalFilter={false}
                                     showColumnFilter={false}
                                     filterCallback={this.suListFilterCallback}
+                                    lsKeySortColumn={this.lsKeySortColumn}
+                                    toggleBySorting={(sortData) => this.toggleBySorting(sortData)}
                                 />
                             </div>
                             {/* Timeline Panel */}
-                            <div className={isSUListVisible?((isSUDetsVisible || isReservDetsVisible)?"col-lg-5 col-md-5 col-sm-12": 
-                                                (!canExtendSUList && canShrinkSUList)?"col-lg-6 col-md-6 col-sm-12":
-                                                ((canExtendSUList && canShrinkSUList)?"col-lg-7 col-md-7 col-sm-12":"col-lg-8 col-md-8 col-sm-12")):
-                                                ((isSUDetsVisible || isReservDetsVisible)?"col-lg-9 col-md-9 col-sm-12":"col-lg-12 col-md-12 col-sm-12")}
-                                // style={{borderLeft: "3px solid #efefef"}}
-                                >
+                            <div className={isSUListVisible ? ((isSUDetsVisible || isReservDetsVisible) ? "col-lg-5 col-md-5 col-sm-12" :
+                                (!canExtendSUList && canShrinkSUList) ? "col-lg-6 col-md-6 col-sm-12" :
+                                    ((canExtendSUList && canShrinkSUList) ? "col-lg-7 col-md-7 col-sm-12" : "col-lg-8 col-md-8 col-sm-12")) :
+                                ((isSUDetsVisible || isReservDetsVisible) ? "col-lg-9 col-md-9 col-sm-12" : "col-lg-12 col-md-12 col-sm-12")}
+                            // style={{borderLeft: "3px solid #efefef"}}
+                            >
                                 {/* Panel Resize buttons */}
                                 {isSUListVisible &&
-                                <div className="resize-div">
-                                    <button className="p-link resize-btn" disabled={!this.state.canShrinkSUList} 
+                                    <div className="resize-div">
+                                        <button className="p-link resize-btn" disabled={!this.state.canShrinkSUList}
                                             title="Shrink List/Expand Timeline"
-                                            onClick={(e)=> { this.resizeSUList(-1)}}>
-                                        <i className="pi pi-step-backward"></i>
-                                    </button>
-                                    <button className="p-link resize-btn" disabled={!this.state.canExtendSUList} 
+                                            onClick={(e) => { this.resizeSUList(-1) }}>
+                                            <i className="pi pi-step-backward"></i>
+                                        </button>
+                                        <button className="p-link resize-btn" disabled={!this.state.canExtendSUList}
                                             title="Expandd List/Shrink Timeline"
-                                            onClick={(e)=> { this.resizeSUList(1)}}>
-                                        <i className="pi pi-step-forward"></i>
-                                    </button>
-                                </div>
+                                            onClick={(e) => { this.resizeSUList(1) }}>
+                                            <i className="pi pi-step-forward"></i>
+                                        </button>
+                                    </div>
                                 }
-                                <div className={isSUListVisible?"resize-div su-visible":"resize-div su-hidden"}>
+                                <div className={isSUListVisible ? "resize-div su-visible" : "resize-div su-hidden"}>
                                     {isSUListVisible &&
-                                    <button className="p-link resize-btn" 
+                                        <button className="p-link resize-btn"
                                             title="Hide List"
-                                            onClick={(e)=> { this.setState({isSUListVisible: false})}}>
-                                        <i className="pi pi-eye-slash"></i>
-                                    </button>
+                                            onClick={(e) => { this.setState({ isSUListVisible: false }) }}>
+                                            <i className="pi pi-eye-slash"></i>
+                                        </button>
                                     }
                                     {!isSUListVisible &&
-                                    <button className="p-link resize-btn"
+                                        <button className="p-link resize-btn"
                                             title="Show List"
-                                            onClick={(e)=> { this.setState({isSUListVisible: true})}}>
-                                        <i className="pi pi-eye"> Show List</i>
-                                    </button>
+                                            onClick={(e) => { this.setState({ isSUListVisible: true }) }}>
+                                            <i className="pi pi-eye"> Show List</i>
+                                        </button>
                                     }
                                 </div>
                                 <div className={`timeline-view-toolbar ${this.state.reservationEnabled && 'alignTimeLineHeader'}`}>
-                                    <div  className="sub-header">
+                                    <div className="sub-header">
                                         <label >Show Reservations</label>
-                                        <InputSwitch checked={this.state.reservationEnabled} onChange={(e) => {this.showReservations(e)}} />                                       
-                                       
+                                        <InputSwitch checked={this.state.reservationEnabled} onChange={(e) => { this.showReservations(e) }} />
+
                                     </div>
-                                
+
                                     {this.state.reservationEnabled &&
-                                    <div className="sub-header">
-                                        <label style={{marginLeft: '20px'}}>Reservation</label>
-                                        <Dropdown optionLabel="name" optionValue="name" 
-                                                    style={{top:'2px'}}
-                                                    value={this.state.reservationFilter} 
-                                                    options={this.reservationReasons} 
-                                                    filter showClear={true} filterBy="name"
-                                                    onChange={(e) => {this.setReservationFilter(e.value)}} 
-                                                    placeholder="Reason"/>
-                                    
-                                    </div>
+                                        <div className="sub-header">
+                                            <label style={{ marginLeft: '20px' }}>Reservation</label>
+                                            <Dropdown optionLabel="name" optionValue="name"
+                                                style={{ top: '2px' }}
+                                                value={this.state.reservationFilter}
+                                                options={this.reservationReasons}
+                                                filter showClear={true} filterBy="name"
+                                                onChange={(e) => { this.setReservationFilter(e.value) }}
+                                                placeholder="Reason" />
+
+                                        </div>
                                     }
                                 </div>
 
-                                <Timeline ref={(tl)=>{this.timeline=tl}} 
-                                        group={this.state.group} 
-                                        items={this.state.items}
-                                        currentUTC={this.state.currentUTC}
-                                        rowHeight={50} 
-                                        itemClickCallback={this.onItemClick}
-                                        itemMouseOverCallback={this.onItemMouseOver}
-                                        itemMouseOutCallback={this.onItemMouseOut}
-                                        sidebarWidth={150}
-                                        stackItems={true}
-                                        startTime={moment.utc(this.state.currentUTC).hour(0).minutes(0).seconds(0)}
-                                        endTime={moment.utc(this.state.currentUTC).hour(23).minutes(59).seconds(59)}
-                                        zoomLevel="1 Day"
-                                        showLive={false} showDateRange={false} viewType={UIConstants.timeline.types.WEEKVIEW}
-                                        dateRangeCallback={this.dateRangeCallback}
-                                    ></Timeline>
+                                <Timeline ref={(tl) => { this.timeline = tl }}
+                                    group={this.state.group}
+                                    items={this.state.items}
+                                    currentUTC={this.state.currentUTC}
+                                    rowHeight={50}
+                                    itemClickCallback={this.onItemClick}
+                                    itemMouseOverCallback={this.onItemMouseOver}
+                                    itemMouseOutCallback={this.onItemMouseOut}
+                                    sidebarWidth={150}
+                                    stackItems={true}
+                                    startTime={moment.utc(this.state.currentUTC).hour(0).minutes(0).seconds(0)}
+                                    endTime={moment.utc(this.state.currentUTC).hour(23).minutes(59).seconds(59)}
+                                    zoomLevel="1 Day"
+                                    showLive={false} showDateRange={false} viewType={UIConstants.timeline.types.WEEKVIEW}
+                                    dateRangeCallback={this.dateRangeCallback}
+                                ></Timeline>
                             </div>
                             {/* Details Panel */}
                             {this.state.isSUDetsVisible &&
-                                <div className="col-lg-3 col-md-3 col-sm-12" 
-                                     style={{borderLeft: "1px solid #efefef", marginTop: "0px", backgroundColor: "#f2f2f2"}}>
-                                    {this.state.isSummaryLoading?<AppLoader /> :
+                                <div className="col-lg-3 col-md-3 col-sm-12"
+                                    style={{ borderLeft: "1px solid #efefef", marginTop: "0px", backgroundColor: "#f2f2f2" }}>
+                                    {this.state.isSummaryLoading ? <AppLoader /> :
                                         <SchedulingUnitSummary schedulingUnit={suBlueprint} suTaskList={this.state.suTaskList}
-                                                viewInNewWindow
-                                                constraintsTemplate={this.state.suConstraintTemplate}
-                                                closeCallback={this.closeSUDets}
-                                                stationGroup={this.state.stationGroup}
-                                                location={this.props.location}></SchedulingUnitSummary>
+                                            viewInNewWindow
+                                            constraintsTemplate={this.state.suConstraintTemplate}
+                                            closeCallback={this.closeSUDets}
+                                            stationGroup={this.state.stationGroup}
+                                            location={this.props.location}></SchedulingUnitSummary>
                                     }
                                 </div>
-                            }  
+                            }
                             {this.state.isReservDetsVisible &&
-                                <div className="col-lg-3 col-md-3 col-sm-12" 
-                                     style={{borderLeft: "1px solid #efefef", marginTop: "0px", backgroundColor: "#f2f2f2"}}>
-                                    {this.state.isSummaryLoading?<AppLoader /> :
+                                <div className="col-lg-3 col-md-3 col-sm-12"
+                                    style={{ borderLeft: "1px solid #efefef", marginTop: "0px", backgroundColor: "#f2f2f2" }}>
+                                    {this.state.isSummaryLoading ? <AppLoader /> :
                                         <ReservationSummary reservation={reservation} location={this.props.location} closeCallback={this.closeSUDets}></ReservationSummary>
                                     }
                                 </div>
@@ -936,65 +974,65 @@ export class WeekTimelineView extends Component {
                 }
                 {/* SU Item Tooltip popover with SU status color */}
                 <OverlayPanel className="timeline-popover" ref={(el) => this.popOver = el} dismissable>
-                {mouseOverItem  && mouseOverItem.type === "SCHEDULE" &&
-                    <div className={`p-grid su-${mouseOverItem.status}`} style={{width: '350px'}}>
-                        <label className={`col-5 su-${mouseOverItem.status}-icon`}>Project:</label>
-                        <div className="col-7">{mouseOverItem.project}</div>
-                        <label className={`col-5 su-${mouseOverItem.status}-icon`}>Scheduling Unit:</label>
-                        <div className="col-7">{mouseOverItem.name}</div>
-                        <label className={`col-5 su-${mouseOverItem.status}-icon`}>Scheduler:</label>
-                        <div className="col-7">{mouseOverItem.scheduleMethod}</div>
-                        <label className={`col-5 su-${mouseOverItem.status}-icon`}>Friends:</label>
-                        <div className="col-7">{mouseOverItem.friends?mouseOverItem.friends:"-"}</div>
-                        <label className={`col-5 su-${mouseOverItem.status}-icon`}>Start Time:</label>
-                        <div className="col-7">{mouseOverItem.suStartTime.format(UIConstants.CALENDAR_DATETIME_FORMAT)}</div>
-                        <label className={`col-5 su-${mouseOverItem.status}-icon`}>End Time:</label>
-                        <div className="col-7">{mouseOverItem.suStopTime.format(UIConstants.CALENDAR_DATETIME_FORMAT)}</div>
-                        <label className={`col-5 su-${mouseOverItem.status}-icon`}>Antenna Set:</label>
-                        <div className="col-7">{mouseOverItem.antennaSet}</div>
-                        <label className={`col-5 su-${mouseOverItem.status}-icon`}>Stations:</label>
-                        <div className="col-7">{mouseOverItem.stations.groups}:{mouseOverItem.stations.counts}</div>
-                        <label className={`col-5 su-${mouseOverItem.status}-icon`}>Status:</label>
-                        <div className="col-7">{mouseOverItem.status}</div>
-                        <label className={`col-5 su-${mouseOverItem.status}-icon`}>Duration:</label>
-                        <div className="col-7">{mouseOverItem.duration}</div>
-                    </div>
-                }
-                {(mouseOverItem && mouseOverItem.type === "RESERVATION") &&
-                    <div className={`p-grid`} style={{width: '350px', backgroundColor: mouseOverItem.bgColor, color: mouseOverItem.color}}>
-                        <h3 className={`col-12`}>Reservation Overview</h3>
-                        <hr></hr>
-                        <label className={`col-5`} style={{color: mouseOverItem.color}}>Name:</label>
-                        <div className="col-7">{mouseOverItem.name}</div>
-                        <label className={`col-5`} style={{color: mouseOverItem.color}}>Description:</label>
-                        <div className="col-7">{mouseOverItem.desc}</div>
-                        <label className={`col-5`} style={{color: mouseOverItem.color}}>Type:</label>
-                        <div className="col-7">{mouseOverItem.activity_type}</div>
-                        <label className={`col-5`} style={{color: mouseOverItem.color}}>Stations:</label>
-                        {/* <div className="col-7"><ListBox options={mouseOverItem.stations} /></div> */}
-                        <div className="col-7 station-list">
-                            {mouseOverItem.stations.map((station, index) => (
-                                <div key={`stn-${index}`}>{station}</div>
-                            ))}
+                    {mouseOverItem && mouseOverItem.type === "SCHEDULE" &&
+                        <div className={`p-grid su-${mouseOverItem.status}`} style={{ width: '350px' }}>
+                            <label className={`col-5 su-${mouseOverItem.status}-icon`}>Project:</label>
+                            <div className="col-7">{mouseOverItem.project}</div>
+                            <label className={`col-5 su-${mouseOverItem.status}-icon`}>Scheduling Unit:</label>
+                            <div className="col-7">{mouseOverItem.name}</div>
+                            <label className={`col-5 su-${mouseOverItem.status}-icon`}>Scheduler:</label>
+                            <div className="col-7">{mouseOverItem.scheduleMethod}</div>
+                            <label className={`col-5 su-${mouseOverItem.status}-icon`}>Friends:</label>
+                            <div className="col-7">{mouseOverItem.friends ? mouseOverItem.friends : "-"}</div>
+                            <label className={`col-5 su-${mouseOverItem.status}-icon`}>Start Time:</label>
+                            <div className="col-7">{mouseOverItem.suStartTime.format(UIConstants.CALENDAR_DATETIME_FORMAT)}</div>
+                            <label className={`col-5 su-${mouseOverItem.status}-icon`}>End Time:</label>
+                            <div className="col-7">{mouseOverItem.suStopTime.format(UIConstants.CALENDAR_DATETIME_FORMAT)}</div>
+                            <label className={`col-5 su-${mouseOverItem.status}-icon`}>Antenna Set:</label>
+                            <div className="col-7">{mouseOverItem.antennaSet}</div>
+                            <label className={`col-5 su-${mouseOverItem.status}-icon`}>Stations:</label>
+                            <div className="col-7">{mouseOverItem.stations.groups}:{mouseOverItem.stations.counts}</div>
+                            <label className={`col-5 su-${mouseOverItem.status}-icon`}>Status:</label>
+                            <div className="col-7">{mouseOverItem.status}</div>
+                            <label className={`col-5 su-${mouseOverItem.status}-icon`}>Duration:</label>
+                            <div className="col-7">{mouseOverItem.duration}</div>
                         </div>
-                        <label className={`col-5`} style={{color: mouseOverItem.color}}>Project:</label>
-                        <div className="col-7">{mouseOverItem.project?mouseOverItem.project:"-"}</div>
-                        <label className={`col-5`} style={{color: mouseOverItem.color}}>Start Time:</label>
-                        <div className="col-7">{mouseOverItem.displayStartTime.format(UIConstants.CALENDAR_DATETIME_FORMAT)}</div>
-                        <label className={`col-5`} style={{color: mouseOverItem.color}}>End Time:</label>
-                        <div className="col-7">{mouseOverItem.displayEndTime?mouseOverItem.displayEndTime.format(UIConstants.CALENDAR_DATETIME_FORMAT):'Unknown'}</div>
-                        {/* <label className={`col-5`} style={{color: mouseOverItem.color}}>Stations:</label>
+                    }
+                    {(mouseOverItem && mouseOverItem.type === "RESERVATION") &&
+                        <div className={`p-grid`} style={{ width: '350px', backgroundColor: mouseOverItem.bgColor, color: mouseOverItem.color }}>
+                            <h3 className={`col-12`}>Reservation Overview</h3>
+                            <hr></hr>
+                            <label className={`col-5`} style={{ color: mouseOverItem.color }}>Name:</label>
+                            <div className="col-7">{mouseOverItem.name}</div>
+                            <label className={`col-5`} style={{ color: mouseOverItem.color }}>Description:</label>
+                            <div className="col-7">{mouseOverItem.desc}</div>
+                            <label className={`col-5`} style={{ color: mouseOverItem.color }}>Type:</label>
+                            <div className="col-7">{mouseOverItem.activity_type}</div>
+                            <label className={`col-5`} style={{ color: mouseOverItem.color }}>Stations:</label>
+                            {/* <div className="col-7"><ListBox options={mouseOverItem.stations} /></div> */}
+                            <div className="col-7 station-list">
+                                {mouseOverItem.stations.map((station, index) => (
+                                    <div key={`stn-${index}`}>{station}</div>
+                                ))}
+                            </div>
+                            <label className={`col-5`} style={{ color: mouseOverItem.color }}>Project:</label>
+                            <div className="col-7">{mouseOverItem.project ? mouseOverItem.project : "-"}</div>
+                            <label className={`col-5`} style={{ color: mouseOverItem.color }}>Start Time:</label>
+                            <div className="col-7">{mouseOverItem.displayStartTime.format(UIConstants.CALENDAR_DATETIME_FORMAT)}</div>
+                            <label className={`col-5`} style={{ color: mouseOverItem.color }}>End Time:</label>
+                            <div className="col-7">{mouseOverItem.displayEndTime ? mouseOverItem.displayEndTime.format(UIConstants.CALENDAR_DATETIME_FORMAT) : 'Unknown'}</div>
+                            {/* <label className={`col-5`} style={{color: mouseOverItem.color}}>Stations:</label>
                         <div className="col-7">{mouseOverItem.stations.groups}:{mouseOverItem.stations.counts}</div> */}
-                        <label className={`col-5`} style={{color: mouseOverItem.color}}>Duration:</label>
-                        <div className="col-7">{mouseOverItem.duration}</div>
-                        <label className={`col-5`} style={{color: mouseOverItem.color}}>Planned:</label>
-                        <div className="col-7">{mouseOverItem.planned?'Yes':'No'}</div>
-                    </div>
-                }
+                            <label className={`col-5`} style={{ color: mouseOverItem.color }}>Duration:</label>
+                            <div className="col-7">{mouseOverItem.duration}</div>
+                            <label className={`col-5`} style={{ color: mouseOverItem.color }}>Planned:</label>
+                            <div className="col-7">{mouseOverItem.planned ? 'Yes' : 'No'}</div>
+                        </div>
+                    }
                 </OverlayPanel>
                 {/* Open Websocket after loading all initial data */}
                 {!this.state.isLoading &&
-                    <Websocket url={process.env.REACT_APP_WEBSOCKET_URL} onOpen={this.onConnect} onMessage={this.handleData} onClose={this.onDisconnect} /> }
+                    <Websocket url={process.env.REACT_APP_WEBSOCKET_URL} onOpen={this.onConnect} onMessage={this.handleData} onClose={this.onDisconnect} />}
             </React.Fragment>
         );
     }
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Workflow/index.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Workflow/index.js
index 4fe4ee6675ff8c29ff6893460177ef8010e72c48..f2baaf1d0d7b6c317da5d7f55c3b429ceabf33d4 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Workflow/index.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Workflow/index.js
@@ -199,7 +199,11 @@ export default (props) => {
     return (
         <>
             <Growl ref={(el) => growl = el} />
-            {currentView && <PageHeader location={props.location} title={`${title}`} actions={[{ icon: 'fa-window-close', link: props.history.goBack, title: 'Click to Close Workflow'}]} />}
+            {currentStep && 
+            <PageHeader location={props.location} title={`${title}`} 
+                actions={[{type:'ext_link', icon:'', label: 'SDC Helpdesk', title: 'Report major issues here', props: { pathname: 'https://support.astron.nl/sdchelpdesk' } },
+                            {icon: 'fa-window-close', link: props.history.goBack, title: 'Click to Close Workflow', props: { pathname: '/schedulingunit/1/workflow' } },
+                        ]} />}
             {loader && <AppLoader />}
             {!loader && schedulingUnit &&
                 <>
@@ -225,7 +229,7 @@ export default (props) => {
                                 <label className="col-sm-10 ">
                                     <a href=" https://proxy.lofar.eu/lofmonitor/" target="_blank">Station Monitor</a>
                                 </label>
-                            </div>
+                            </div>                                             
                         </div>}
                         <div className={`step-header-${currentStep}`}>
                             <Steps model={getStepItems()} activeIndex={currentView - 1} readOnly={false} 
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Workflow/workflow.list.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Workflow/workflow.list.js
index 77348c8570fd59492b1d20fdd91488694e1bca3a..d7d2e71e0d7fca34917c8492e3e9d956dca5d3e5 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Workflow/workflow.list.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Workflow/workflow.list.js
@@ -11,10 +11,14 @@ import ViewTable from '../../components/ViewTable';
 import WorkflowService from  '../../services/workflow.service';
 import ScheduleService from  '../../services/schedule.service';
 import UIConstants from '../../utils/ui.constants';
+import UtilService from '../../services/util.service';
 
 class WorkflowList extends Component{
+    lsKeySortColumn = 'SortDataWorkflowList';
+    defaultSortColumn = [];
     constructor(props) {
         super(props);
+        this.setToggleBySorting();
         this.state={
             ftAssignstatus: '',
             activeWorkflow: null,
@@ -65,6 +69,7 @@ class WorkflowList extends Component{
     }
  
     componentDidMount() {
+        this.setToggleBySorting();
         const promises = [  WorkflowService.getWorkflowProcesses(),
                             WorkflowService.getWorkflowTasks(), 
                             ScheduleService.getSchedulingUnitBlueprint(),];
@@ -75,7 +80,23 @@ class WorkflowList extends Component{
             this.prepareWorkflowProcesslist();
         });
     }
+    toggleBySorting = (sortData) => {
+        UtilService.localStore({ type: 'set', key: this.lsKeySortColumn, value: sortData });
+    }
 
+    setToggleBySorting() {    
+        let sortData = UtilService.localStore({ type: 'get', key: this.lsKeySortColumn });
+        if (sortData) {
+            if (Object.prototype.toString.call(sortData) === '[object Array]') {
+                this.defaultSortColumn = sortData;
+            }
+            else {
+                this.defaultSortColumn = [{ ...sortData }];
+            }
+        }
+        this.defaultSortColumn = this.defaultSortColumn || [];
+        UtilService.localStore({ type: 'set', key: this.lsKeySortColumn, value: [...this.defaultSortColumn] });
+    }
     /**
      * Prepare Workflow Process data
      */
@@ -249,6 +270,9 @@ class WorkflowList extends Component{
                             showTopTotal={true}
                             showGlobalFilter={true}
                             showColumnFilter={true}
+                            lsKeySortColumn={this.lsKeySortColumn}
+                            toggleBySorting={(sortData) => this.toggleBySorting(sortData)}
+                            defaultSortColumn= {this.defaultSortColumn}
                         />
                     </>
                     :<div>No Workflow Process SU found</div>
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/index.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/index.js
index aa2ce1fc7a253bf50bfc8f524aedbe3beffc5773..05a7d8201987f8a02b96b89921310df58d7b40fa 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/routes/index.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/index.js
@@ -19,6 +19,11 @@ import { ReservationCreate, ReservationList, ReservationView, ReservationEdit }
 import { FindObjectResult } from './Search/'
 import SchedulingSetCreate from './Scheduling/excelview.schedulingset';
 import Workflow from './Workflow';
+<<<<<<< HEAD
+import Simulator from './Simulator/index';
+=======
+>>>>>>> 735c4e2eeb8bb8fa4eb555776f47c597c2436489
+import ReportHome from './Report';
 import { Growl } from 'primereact/components/growl/Growl';
 import { setAppGrowl } from '../layout/components/AppGrowl';
 import WorkflowList from './Workflow/workflow.list'
@@ -187,11 +192,23 @@ export const routes = [
         name: 'Find Object',
         title: 'Find Object'
     },
+    {
+        path: "/schedulingunit/simulate/:id",
+        component: Simulator,
+        name: 'Scheduling Unit Simulator',
+        title: 'Scheduling Unit Simulator'
+    },
     {
         path: "/workflow",
         component: WorkflowList,
         name: 'Workflow',
         title: 'Workflow'
+    },
+    {
+        path: "/reports",
+        component: ReportHome,
+        name: 'Reports',
+        title: 'Reports'
     }
 ];
 
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/services/report.service.js b/SAS/TMSS/frontend/tmss_webapp/src/services/report.service.js
new file mode 100644
index 0000000000000000000000000000000000000000..d0224c3f1f3943ec64498d931ab6ecef69156488
--- /dev/null
+++ b/SAS/TMSS/frontend/tmss_webapp/src/services/report.service.js
@@ -0,0 +1,18 @@
+import axios from "axios";
+
+const ReportService = {
+
+    getProjectReport: async(project) => {
+        let reportData = {};
+        try {
+            const response = await axios.get(`/api/project/${project}/report/`);
+            reportData = response.data;
+        }   catch(error) {
+            console.error(error);
+            reportData.error = error;
+        }
+        return reportData;
+    }
+}
+
+export default ReportService;
\ No newline at end of file
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/services/schedule.service.js b/SAS/TMSS/frontend/tmss_webapp/src/services/schedule.service.js
index c92813a92229129653327f5cd5959cc50087a931..6cb3c2ffdb390e94bd002af08b0249424366abe8 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/services/schedule.service.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/services/schedule.service.js
@@ -363,17 +363,40 @@ const ScheduleService = {
     },
     getTaskConnectorType: async function (id, type) {
         let res;
-        const myPromise =await new Promise((resolve, reject) => {           
-             axios.get(`/api/task_connector_type/`)
+        const myPromise = await new Promise((resolve, reject) => {
+            axios.get(`/api/task_connector_type/`)
                 .then(response => {
-                    let tempRes = response.data;                 
+                    let tempRes = response.data;
                     res = [];
                     if (tempRes.results) {
-                        let tempResults = tempRes.results;                        
-                        res = tempResults.find((connector) => connector.task_template_id == id &&                           
+                        let tempResults = tempRes.results;
+                        tempResults = tempResults.filter((connector) => connector.task_template_id == id);
+                        res = tempResults.filter((connector) =>
+                            connector.role_value == "any" &&
                             connector.datatype_value == "visibilities" &&
                             connector.iotype_value == type)
-                    }                 
+                    }
+                    resolve(res);
+                }).catch(function (error) {
+                    console.error('[schedule.services.getTaskConnectorType]', error);
+                });
+        });
+        return myPromise;
+    },
+    getSpecificTaskConnectorType: async function (id, type) {
+        let res;
+        const myPromise = await new Promise((resolve, reject) => {
+            axios.get(`/api/task_connector_type/`)
+                .then(response => {
+                    let tempRes = response.data;
+                    res = [];
+                    if (tempRes.results) {
+                        let tempResults = tempRes.results;
+                        res = tempResults.find((connector) => connector.task_template_id == id &&
+                          //  connector.role_value == "any" &&
+                            connector.datatype_value == "visibilities" &&
+                            connector.iotype_value == type)
+                    }
                     resolve(res);
                 }).catch(function (error) {
                     console.error('[schedule.services.getTaskConnectorType]', error);
@@ -383,6 +406,7 @@ const ScheduleService = {
     },
     addTaskRelationDraft: async function (params, name) {
         try {
+            
             const response = await axios.post((`/api/task_relation_draft/`), params);
             return { status: true, action: 'Added', 'name': name, 'msg': response.data };
         }
@@ -644,9 +668,9 @@ const ScheduleService = {
         } catch (error) {
             console.error('[project.services.getSchedulingUnitBySet]', error);
         }
-      },
-      createSchedulingUnitBlueprintTree: async function(id) {
-          try {
+    },
+    createSchedulingUnitBlueprintTree: async function (id) {
+        try {
             const response = await axios.post(`/api/scheduling_unit_draft/${id}/create_blueprints_and_subtasks`);
             return response.data;
         } catch (error) {
@@ -746,7 +770,7 @@ const ScheduleService = {
     createTaskRelationDraft: async function (taskRelDraftObj, obj) {
         let taskRelDraftPromises = [], taskRelAddDraftObj = [];
         try {
-            if (taskRelDraftObj) {
+            if (taskRelDraftObj) {                
                 taskRelDraftObj.forEach((tObj, i) => {
                     taskRelDraftPromises.push(this.addTaskRelationDraft(tObj, obj[i].name));
                 });
@@ -786,6 +810,16 @@ const ScheduleService = {
             "selection_template": "/api/task_relation_selection_template/1",
             "tags": []
         }
+    },
+    cancelSchedulingUnit: async(id) => {
+        let cancelledSU = null;
+        try {
+            const url = `/api/scheduling_unit_blueprint_extended/${id}/cancel/`;
+            cancelledSU = (await axios.post(url, {})).data;
+        } catch(error) {
+            console.error(error);
+        }
+        return cancelledSU;
     }
 }
 
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/services/simulator.service.js b/SAS/TMSS/frontend/tmss_webapp/src/services/simulator.service.js
new file mode 100644
index 0000000000000000000000000000000000000000..2313fe0d0475587d810e8e7a44d5afc4ab6943dd
--- /dev/null
+++ b/SAS/TMSS/frontend/tmss_webapp/src/services/simulator.service.js
@@ -0,0 +1,77 @@
+import _ from 'lodash';
+import ScheduleService from './schedule.service';
+import TaskService from './task.service';
+
+const axios = require('axios');
+const finalStatuses = ['finished', 'error', 'cancelled', 'unschedulable'];
+const subtaskStatuses = ['defining', 'defined', 'scheduling', 'scheduled',
+                         'starting', 'started', 'finishing', 'finished'];
+
+const SimulatorService = {
+    simulateSUB: async function(id) {
+        let subStatus = {taskStatus: []};
+        let suUpdateStatus = "success";
+        try {
+            const suBlueprint = await ScheduleService.getSchedulingUnitExtended('blueprint', id, true);
+            if (suBlueprint && finalStatuses.indexOf(suBlueprint.status)<0) {
+                const subtaskStates = await this.getSubtaskStates();
+                const taskBlueprints = _.sortBy(suBlueprint.task_blueprints, ['id'])
+                for (const taskBP of taskBlueprints) {
+                    let taskBPStatus = {id: taskBP.id, name: taskBP.name, subtaskStatus:[]};
+                    const subtasks = _.sortBy(taskBP.subtasks, ['id']);
+                    let taskUpdateStatus = "success";
+                    for (let subtask of subtasks) {
+                        let subtaskUpdateStatus = "success";
+                        subtask = await TaskService.getSubtaskDetails(subtask.id);
+                        taskBPStatus.subtaskStatus.push({id: subtask.id, state: subtask.state_value, status: "-"});
+                        while (finalStatuses.indexOf(subtask.state_value)<0) {
+                            let subtaskStatus = {id: subtask.id, name: subtask.name};
+                            let statusIndex = subtaskStatuses.indexOf(subtask.state_value);
+                            subtask.state_value = subtaskStatuses[statusIndex+1];
+                            const subtaskState = _.find(subtaskStates, ['value', subtask.state_value]);
+                            subtask.state = subtaskState.url;
+                            let currentUpdateStatus = await this.updateSubtask(subtask);
+                            subtaskStatus.state = subtask.state_value;
+                            subtaskStatus.status = currentUpdateStatus;
+                            taskBPStatus.subtaskStatus.push(subtaskStatus);
+                            subtaskUpdateStatus = currentUpdateStatus;
+                            if (currentUpdateStatus === "failed") {
+                                break;
+                            }
+                        }
+                        taskUpdateStatus = subtaskUpdateStatus;
+                        if (subtaskUpdateStatus === "failed") {
+                            break;
+                        }
+                    }
+                    subStatus.taskStatus.push(taskBPStatus);
+                    if (taskUpdateStatus === "failed") {
+                        break;
+                    }
+                }
+            }
+        } catch (error) {
+           console.error(error);
+        }
+        return subStatus;
+    },
+    updateSubtask: async(subtask) => {
+        try {
+            const response = await axios.put(`/api/subtask/${subtask.id}/`, subtask);
+            return "success";
+        }   catch (error) {
+            console.log(error);
+            return "failed";
+        }
+    },
+    getSubtaskStates: async() => {
+        try {
+            const response = await axios.get(`/api/subtask_state/`);
+            return response.data.results;
+        }   catch (error) {
+            console.log(error);
+        }
+    }
+}
+
+export default SimulatorService;
\ No newline at end of file
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/services/task.service.js b/SAS/TMSS/frontend/tmss_webapp/src/services/task.service.js
index fd4b6d769ecc53b022be3da580317ebbae11a24d..b0cd6084d7025002ecd23782f290e3ba9e951bc4 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/services/task.service.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/services/task.service.js
@@ -52,6 +52,14 @@ const TaskService = {
         console.log(error);
       }
     },
+    getTaskTypes: async() => {
+        try {
+            const response = await axios.get('/api/task_type/');
+            return response.data.results;
+        } catch (error) {
+            console.log(error);
+        }
+    },
     getSchedulingUnit: async function(type, id) {
       try {
         const url = `/api/scheduling_unit_${type}/${id}`;
@@ -266,6 +274,21 @@ const TaskService = {
             console.error(error);
             return false;
         }
+    },
+    /**
+     * Cancel task 
+     * @param {*} type 
+     * @param {*} id 
+     */
+    cancelTask: async function(id) {
+        try {
+            const url = `/api/task_blueprint/${id}/cancel`;
+            await axios.post(url, {});
+            return true;
+        } catch(error) {
+            console.error(error);
+            return false;
+        }
     }
 }
 
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/services/util.service.js b/SAS/TMSS/frontend/tmss_webapp/src/services/util.service.js
index b6739cdcc51485e3ce5af45b19980caa77a5b1c6..d2554441a9951d1a45b80bc5c231e23030ed7ae9 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/services/util.service.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/services/util.service.js
@@ -171,7 +171,17 @@ const UtilService = {
   },
   filterByObject:function(arrObj,action){
     return arrObj.filter( (ao)=> ao.action == action);
-  }
+  },
+  localStore:function(data){
+    const {type,key,value}=data;
+    if(type=='set'){
+      localStorage.setItem(key,JSON.stringify(value));
+    }else if(type=='get'){
+      return JSON.parse(localStorage.getItem(key));
+    }else if(type=='remove'){
+      localStorage.removeItem(key);
+    }
+}
 }
 
 export default UtilService;
\ No newline at end of file
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/utils/parser.utility.js b/SAS/TMSS/frontend/tmss_webapp/src/utils/parser.utility.js
new file mode 100644
index 0000000000000000000000000000000000000000..a60078d385a0b718828e1d7377f94f92caadeb4f
--- /dev/null
+++ b/SAS/TMSS/frontend/tmss_webapp/src/utils/parser.utility.js
@@ -0,0 +1,53 @@
+import _ from 'lodash';
+import $RefParser from "@apidevtools/json-schema-ref-parser";
+
+const ParserUtility = {
+    /**
+     * Function to get the property of the parameter referred for the task.
+     * @param {*} $strategyRefs 
+     * @param {Array} paramPaths - Property reference path. 
+     * Example if the task parameter refers '#/tasks/Target Pointing 1/specifications_doc/SAPs/0/digital_pointing',
+     * then the parameter to be passed is [digital_pointing, 0, SAPs]
+     * @param {Object} taskTemplateSchema - JSON schema for the respective task template
+     * @returns 
+     */
+     getParamProperty: async($strategyRefs, paramPaths, taskTemplateSchema) => {
+        const $templateRefs = await $RefParser.resolve(taskTemplateSchema);
+        let pathIndex = 0;
+        let paramProp = {};
+        for (const paramPath of paramPaths) {
+            let property = taskTemplateSchema.properties[paramPath];
+            if (property) {
+                let rootPath = paramPaths.slice(0, pathIndex)
+                rootPath.reverse();
+                paramProp = _.cloneDeep(property);
+                if (rootPath.length > 0) {
+                    for (const path of rootPath) {
+                        if (paramProp[path]) {
+                            break;
+                        }   else {
+                            if (paramProp['$ref']) {
+                                paramProp = $templateRefs.get(paramProp['$ref']);
+                                if (paramProp.properties && paramProp.properties[path]) {
+                                    paramProp = paramProp.properties[path];
+                                }
+                            }   else {
+                                if (paramProp.type === "array") {
+                                    paramProp = paramProp.items.properties[path];
+                                }   else if (paramProp.type === "object") {
+                                    paramProp = paramProp.properties[path];
+                                }   else {
+                                    paramProp = paramProp[path];
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            pathIndex++;
+        }
+        return paramProp;
+    }
+}
+
+export default ParserUtility;
\ No newline at end of file