From 026ecbe416a600124ea793ef02314aa29156db2c Mon Sep 17 00:00:00 2001 From: Jorrit Schaap <schaap@astron.nl> Date: Wed, 11 Oct 2023 20:34:24 +0200 Subject: [PATCH] TMSS-2809: removed 'placed' status. For simplicity, just schedule everything. --- .../lib/postgres_listener.py | 10 - .../services/scheduling/lib/constraints.py | 3 - .../scheduling/lib/dynamic_scheduling.py | 217 ++++++------------ .../scheduling/test/t_dynamic_scheduling.py | 76 +++--- .../websocket/lib/websocket_service.py | 5 - .../src/tmss/tmssapp/adapters/plots.py | 18 +- ...0_remove_schedulingunitblueprint_placed.py | 17 ++ .../src/tmss/tmssapp/models/specification.py | 1 - .../tmss/tmssapp/serializers/specification.py | 3 +- SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py | 17 +- SAS/TMSS/backend/src/tmss/tmssapp/tasks.py | 27 +-- .../tmss/tmssapp/viewsets/specification.py | 6 +- .../test/acceptance_test/acceptance_test.py | 12 +- SAS/TMSS/client/lib/tmssbuslistener.py | 8 - .../helpers/timeline.renderer.helper.js | 2 - .../src/services/schedule.service.js | 1 - .../tmss_webapp/src/utils/ui.constants.js | 1 - 17 files changed, 150 insertions(+), 274 deletions(-) create mode 100644 SAS/TMSS/backend/src/tmss/tmssapp/migrations/0050_remove_schedulingunitblueprint_placed.py diff --git a/SAS/TMSS/backend/services/postgres_listener/lib/postgres_listener.py b/SAS/TMSS/backend/services/postgres_listener/lib/postgres_listener.py index 395ea36da6c..619b78367c7 100644 --- a/SAS/TMSS/backend/services/postgres_listener/lib/postgres_listener.py +++ b/SAS/TMSS/backend/services/postgres_listener/lib/postgres_listener.py @@ -125,9 +125,6 @@ class TMSSPGListener(PostgresListener): self.executeQuery(makePostgresNotificationQueries('', 'tmssapp_schedulingunitblueprint', 'update', columns=['rank'])) self.subscribe('tmssapp_schedulingunitblueprint_update_rank', self.onSchedulingUnitBlueprintRankUpdated) - self.executeQuery(makePostgresNotificationQueries('', 'tmssapp_schedulingunitblueprint', 'update', columns=['placed'])) - self.subscribe('tmssapp_schedulingunitblueprint_update_placed', self.onSchedulingUnitBlueprintPlacedUpdated) - # SchedulingConstraintsWeightFactor @@ -306,13 +303,6 @@ class TMSSPGListener(PostgresListener): self._sendNotification(TMSS_SCHEDULINGUNITBLUEPRINT_OBJECT_EVENT_PREFIX + '.Rank.Updated', {'id': id, 'rank': rank}) - def onSchedulingUnitBlueprintPlacedUpdated(self, payload=None): - payload_dict = json.loads(payload) - id = payload_dict['id'] - placed = payload_dict['new']['placed'] - self._sendNotification(TMSS_SCHEDULINGUNITBLUEPRINT_OBJECT_EVENT_PREFIX + '.Placed.Updated', - {'id': id, 'placed': placed}) - def onSchedulingUnitBlueprintPriorityQueueUpdated(self, payload=None): payload_dict = json.loads(payload) id = payload_dict['id'] diff --git a/SAS/TMSS/backend/services/scheduling/lib/constraints.py b/SAS/TMSS/backend/services/scheduling/lib/constraints.py index 9dfa61ead1e..49a4241bdd5 100644 --- a/SAS/TMSS/backend/services/scheduling/lib/constraints.py +++ b/SAS/TMSS/backend/services/scheduling/lib/constraints.py @@ -2234,9 +2234,6 @@ def get_blocking_scheduled_units(scheduling_unit: models.SchedulingUnitBlueprint def get_blocking_scheduled_or_observing_units(scheduling_unit: models.SchedulingUnitBlueprint, proposed_start_time: datetime=None) -> QuerySet: return get_blocking_units(scheduling_unit, proposed_start_time, (models.SchedulingUnitStatus.Choices.SCHEDULED.value, models.SchedulingUnitStatus.Choices.OBSERVING.value)) -def get_blocking_placed_units(scheduling_unit: models.SchedulingUnitBlueprint, proposed_start_time: datetime=None) -> QuerySet: - return get_blocking_units(scheduling_unit, proposed_start_time, (models.SchedulingUnitStatus.Choices.SCHEDULABLE.value,)).filter(placed=True) - def get_blocking_units(scheduling_unit: models.SchedulingUnitBlueprint, proposed_start_time: datetime = None, blocking_statuses: Iterable[str]=None) -> QuerySet: from .dynamic_scheduling import DEFAULT_INTER_OBSERVATION_GAP units = models.SchedulingUnitBlueprint.objects.filter(obsolete_since__isnull=True) diff --git a/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py b/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py index 250ca852f94..6deb9699d5e 100644 --- a/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py +++ b/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py @@ -74,9 +74,6 @@ from operator import attrgetter # LOFAR needs to have a gap in between observations to (re)initialize hardware. DEFAULT_NEXT_STARTTIME_GAP = timedelta(seconds=180) from lofar.sas.tmss.tmss.tmssapp.subtasks import DEFAULT_INTER_OBSERVATION_GAP -# how far ahead do we schedule? (beyond this limit we just place the units without scheduling them). -DEFAULT_SCHEDULING_WINDOW_WIDTH = timedelta(days=3) - class Scheduler: """ @@ -93,13 +90,12 @@ class Scheduler: - schedule the best next dynamic scheduling unit - position the rest of the dynamic scheduling units at their most likely timestamp where they will be observed. """ - def __init__(self, scheduling_window_width: timedelta=DEFAULT_SCHEDULING_WINDOW_WIDTH) -> None: + def __init__(self) -> None: self._scheduling_thread = None self._scheduling_thread_running = False self._do_schedule_event = Event() self.search_gridder = Gridder(grid_minutes=1*60) self.fine_gridder = Gridder(grid_minutes=1) - self.scheduling_window_width = scheduling_window_width super().__init__() # make sure initial status is idle models.Subsystem.Activator('scheduler').deactivate() @@ -237,7 +233,7 @@ class Scheduler: try: # first put the unit at it's requested 'at' timestamp. at_timestamp = get_at_constraint_timestamp(schedulable_unit) - set_scheduling_unit_blueprint_start_times(schedulable_unit, first_start_time=at_timestamp, placed=True) + set_scheduling_unit_blueprint_start_times(schedulable_unit, first_start_time=at_timestamp) if not can_run_at(schedulable_unit, at_timestamp, self.fine_gridder): unschedulable_unit = determine_unschedulable_reason_and_mark_unschedulable_if_needed(schedulable_unit, at_timestamp, at_timestamp + schedulable_unit.specified_observation_duration, @@ -252,7 +248,7 @@ class Scheduler: logger.info("Scheduled fixed_time unit [%s/%s] id=%d at '%s'", i, len(schedulable_units), schedulable_unit.id, at_timestamp) scheduled_units.append(scheduled_unit) - scheduled_B_units = self.place_or_schedule_B_priority_units_in_gaps_around_scheduling_unit(scheduled_unit, do_schedule=True) + scheduled_B_units = self.schedule_B_priority_units_in_gaps_around_scheduling_unit(scheduled_unit) scheduled_units.extend(scheduled_B_units) else: unschedulable_unit = determine_unschedulable_reason_and_mark_unschedulable_if_needed(schedulable_unit, at_timestamp, at_timestamp + schedulable_unit.specified_observation_duration, @@ -302,7 +298,7 @@ class Scheduler: with models.Subsystem.Activator('scheduler'): logger.info("Updating (dynamic) schedule....") - placed_or_scheduled_units = [] + scheduled_units = [] lower_bound = round_to_second_precision(datetime.utcnow() + DEFAULT_NEXT_STARTTIME_GAP) upper_bound = lower_bound + timedelta(days=1) @@ -312,37 +308,27 @@ class Scheduler: while lower_bound < upper_bound: self._raise_if_triggered() - # scheduled units in the near upcoming window of [now, now+scheduling_window_width] - # 'place' units in the rest of the cycle - # scheduled-vs-placed rationale: - # 1) scheduling/unscheduling takes time and creates/prepares/deletes dataproducts etc. - # 2) users "expect" a scheduled unit to be executed at that starttime. Unscheduling moving and scheduling again, confuses them. - # 3) user "accept" non-scheduled-but-placed units to be moved around if needed - do_schedule = lower_bound - datetime.utcnow() < self.scheduling_window_width - - # find and schedule/place the next best unit - # ignore/exclude as candidates the unit(s) which are already placed/scheduled in this round. - # when new candidates are overlapping with already placed/scheduled units, they are re-evaluated to see who wins. - to_be_excluded_units = set(placed_or_scheduled_units) - set(get_dynamically_schedulable_scheduling_units().filter(placed=False).all()) - placed_or_scheduled_unit = self.place_or_schedule_next_scheduling_unit(lower_bound, - lower_bound + timedelta(hours=24), - do_schedule=do_schedule, - exclude_units=to_be_excluded_units) - - if placed_or_scheduled_unit: - placed_or_scheduled_units.append(placed_or_scheduled_unit) + + # find and schedule the next best unit + # ignore/exclude as candidates the unit(s) which are already scheduled in this round. + # when new candidates are overlapping with already scheduled units, they are re-evaluated to see who wins. + to_be_excluded_units = set(scheduled_units) - set(get_dynamically_schedulable_scheduling_units().all()) + scheduled_unit = self.schedule_next_scheduling_unit(lower_bound, + lower_bound + timedelta(hours=24), + exclude_units=to_be_excluded_units) + + if scheduled_unit: + scheduled_units.append(scheduled_unit) # see if we can fit any B-prio units in the new gap(s) in the schedule? - placed_or_scheduled_B_units = self.place_or_schedule_B_priority_units_in_gaps_around_scheduling_unit(placed_or_scheduled_unit, do_schedule=do_schedule) - placed_or_scheduled_units.extend(placed_or_scheduled_B_units) + scheduled_B_units = self.schedule_B_priority_units_in_gaps_around_scheduling_unit(scheduled_unit) + scheduled_units.extend(scheduled_B_units) else: - # nothing was placed/scheduled in the window lower_bound+24h, so advance and see if anything fits + # nothing was scheduled in the window lower_bound+24h, so advance and see if anything fits # check for any overlapping units... blocking_units = models.SchedulingUnitBlueprint.objects.filter( obsolete_since__isnull=True).filter( - placed__isnull=True).filter( - status__value__in=(models.SchedulingUnitStatus.Choices.SCHEDULABLE.value, - models.SchedulingUnitStatus.Choices.SCHEDULED.value, + status__value__in=(models.SchedulingUnitStatus.Choices.SCHEDULED.value, models.SchedulingUnitStatus.Choices.OBSERVING.value)).filter( on_sky_stop_time__gte=lower_bound).filter( on_sky_stop_time__lte=upper_bound) @@ -364,17 +350,17 @@ class Scheduler: logger.info("Finished updating dynamic schedule") - # loop over all remaining non-placed schedulable units, and make them unschedulable for the big search window (if they are unschedulable) + # loop over all remaining non-scheduled schedulable units, and make them unschedulable for the big search window (if they are unschedulable) # so they are ignored next time. # It's up to the user/operator to tweak their constraints which makes them schedulable again, for a next try. - for su in get_dynamically_schedulable_scheduling_units().filter(placed=False).all(): + for su in get_dynamically_schedulable_scheduling_units().all(): determine_unschedulable_reason_and_mark_unschedulable_if_needed(su, datetime.utcnow(), su.latest_possible_cycle_start_time, proposed_start_time=None, gridder=self.search_gridder, raise_if_interruped=self._raise_if_triggered) - return placed_or_scheduled_units + return scheduled_units def find_best_next_schedulable_unit(self, scheduling_units:[models.SchedulingUnitBlueprint], lower_bound_start_time: datetime, upper_bound_stop_time: datetime) -> ScoredSchedulingUnit: @@ -555,12 +541,10 @@ class Scheduler: return None - def place_or_schedule_next_scheduling_unit(self, lower_bound: datetime=None, upper_bound: datetime=None, do_schedule: bool=False, exclude_units: []=None) -> models.SchedulingUnitBlueprint: - '''find the best next upcoming schedulable scheduling unit and try to place/schedule it. - Overlapping existing scheduled units are unplaced/unscheduled if their score is lower. - :return: the placed/scheduled scheduling unit.''' - - log_prefix = 'schedule_next_scheduling_unit' if do_schedule else 'place_or_schedule_next_scheduling_unit' + def schedule_next_scheduling_unit(self, lower_bound: datetime=None, upper_bound: datetime=None, exclude_units: []=None) -> models.SchedulingUnitBlueprint: + '''find the best next upcoming schedulable scheduling unit and try to schedule it. + Overlapping existing scheduled units are unscheduled if their score is lower. + :return: the scheduled scheduling unit.''' queue_A = models.PriorityQueueType.objects.get(value=models.PriorityQueueType.Choices.A.value) queue_B = models.PriorityQueueType.objects.get(value=models.PriorityQueueType.Choices.B.value) @@ -594,13 +578,13 @@ class Scheduler: if len(candidate_units) == 0: continue # continue with the next in order schedulable_units - logger.info("%s: %s candidate_units: %s", log_prefix, len(candidate_units), ','.join([str(su.id) for su in sorted(candidate_units, key=lambda x: x.id)]) or 'None') + logger.info("schedule_next_scheduling_unit: %s candidate_units: %s", len(candidate_units), ','.join([str(su.id) for su in sorted(candidate_units, key=lambda x: x.id)]) or 'None') # search in a forward sliding window for the best scheduling_unit that can be scheduled if lower_bound is None: lower_bound = datetime.utcnow() + DEFAULT_NEXT_STARTTIME_GAP if upper_bound is None: - upper_bound = lower_bound + self.scheduling_window_width + upper_bound = lower_bound + timedelta(hours=24) lower_bound_start_time = round_to_second_precision(lower_bound) upper_bound_stop_time = round_to_second_precision(upper_bound) @@ -620,7 +604,7 @@ class Scheduler: window_upper_bound_stop_time = round_to_second_precision(window_upper_bound_stop_time) # try to find the best next scheduling_unit - logger.info("%s: searching for best scheduling unit to schedule in window ['%s', '%s']", log_prefix, window_lower_bound_start_time, window_upper_bound_stop_time) + logger.info("schedule_next_scheduling_unit: searching for best scheduling unit to schedule in window ['%s', '%s']", window_lower_bound_start_time, window_upper_bound_stop_time) best_scored_scheduling_unit = self.find_best_next_schedulable_unit(candidate_units, window_lower_bound_start_time, window_upper_bound_stop_time) @@ -636,29 +620,26 @@ class Scheduler: # make start_time "look nice" for us humans best_start_time = round_to_second_precision(best_start_time) - logger.info("%s: found best candidate id=%s '%s' weighted_score=%.3f start_time=%s interrupts_telescope=%s", - log_prefix, best_scheduling_unit.id, best_scheduling_unit.name, best_scheduling_unit_score, best_start_time, best_scheduling_unit.interrupts_telescope) + logger.info("schedule_next_scheduling_unit: found best candidate id=%s '%s' weighted_score=%.3f start_time=%s interrupts_telescope=%s", + best_scheduling_unit.id, best_scheduling_unit.name, best_scheduling_unit_score, best_start_time, best_scheduling_unit.interrupts_telescope) - if do_schedule: - placed_or_scheduled_unit = self.try_schedule_unit(best_scheduling_unit, best_start_time) - else: - placed_or_scheduled_unit = self.try_place_unit(best_scheduling_unit, best_start_time) + scheduled_unit = self.try_schedule_unit(best_scheduling_unit, best_start_time) - if placed_or_scheduled_unit is None: - # we had a best_scored_scheduling_unit, but it could not be placed/scheduled here. + if scheduled_unit is None: + # we had a best_scored_scheduling_unit, but it could not be scheduled here. # remove it from the candidates, and do not evaluate it again in this window. candidate_units.remove(best_scheduling_unit) continue else: self.log_schedule(log_level=logging.INFO) - # done, just return the placed/scheduled_unit - return placed_or_scheduled_unit + # done, just return the scheduled_unit + return scheduled_unit else: - logger.info("%s: no scheduling unit found which could be scheduled in window ['%s', '%s']", log_prefix, window_lower_bound_start_time, window_upper_bound_stop_time) + logger.info("schedule_next_scheduling_unit: no scheduling unit found which could be scheduled in window ['%s', '%s']", window_lower_bound_start_time, window_upper_bound_stop_time) except SubtaskSchedulingException as e: - logger.error("%s: Could not schedule scheduling_unit id=%s name='%s'. Error: %s", log_prefix, best_scheduling_unit.id, best_scheduling_unit.name, e) + logger.error("%s: Could not schedule scheduling_unit id=%s name='%s'. Error: %s", best_scheduling_unit.id, best_scheduling_unit.name, e) # prevent that it keeps trying to schedule this failed unit in this scheduler-round candidate_units.remove(best_scheduling_unit) @@ -671,14 +652,14 @@ class Scheduler: return None else: if can_run_after(best_scheduling_unit, best_start_time, self.search_gridder) and not best_scheduling_unit.interrupts_telescope: - logger.info("%s: Unschedulable scheduling_unit id=%s can run later than '%s'. Marking it as schedulable again...", log_prefix, best_scheduling_unit.id, best_start_time) + logger.info("schedule_next_scheduling_unit: Unschedulable scheduling_unit id=%s can run later than '%s'. Marking it as schedulable again...", best_scheduling_unit.id, best_start_time) # yep, can run later, so mark it as schedulable again, and let it be handled in a new scheduler-round mark_independent_subtasks_in_scheduling_unit_blueprint_as_schedulable(best_scheduling_unit) else: mark_independent_subtasks_in_scheduling_unit_blueprint_as_unschedulable(best_scheduling_unit, str(e)) if not candidate_units: - logger.debug("%s: no more candidate units...", log_prefix) + logger.debug("%s: no more candidate units...") return None # advance the window at the upper side only so more candidates fit in @@ -729,72 +710,22 @@ class Scheduler: return scheduled_scheduling_unit - def try_place_unit(self, scheduling_unit: models.SchedulingUnitBlueprint, start_time: datetime) -> models.SchedulingUnitBlueprint: - '''Try placing the unit at the given start_time - un-places any overlapping placed units''' - logger.info("try_place_unit id=%s queue=%s '%s' start_time=%s", scheduling_unit.id, scheduling_unit.priority_queue.value, scheduling_unit.name, start_time) - - # check if the given unit was already placed at the given time. - scheduling_unit_from_db = models.SchedulingUnitBlueprint.objects.get(id=scheduling_unit.id) - if scheduling_unit_from_db.placed: - if abs((scheduling_unit_from_db.scheduled_start_time - start_time).total_seconds() < 1): - logger.info("scheduling_unit id=%s '%s' was already placed at start_time='%s', skipping...", scheduling_unit.id, scheduling_unit.name, start_time) - return None - - # are there any blocking scheduled or observing scheduling_units still in the way? - blocking_scheduling_units = get_blocking_scheduled_or_observing_units(scheduling_unit, start_time) - - if blocking_scheduling_units.exists(): - logger.warning("cannot place scheduling_unit id=%s '%s' at start_time=%s because there are %d other units blocking it", - scheduling_unit.id, scheduling_unit.name, start_time, blocking_scheduling_units.count()) - update_subtasks_start_times_for_scheduling_unit(scheduling_unit, start_time, placed=False) - return None - - # un-place any previously overlapping/blocking placed unit with a lower score - blocking_placed_units = list(get_blocking_placed_units(scheduling_unit, start_time)) - if blocking_placed_units: - # compute weighted scores for all blocking placed- and the candidate scheduling units - # because the weighted scores are also normalized over the given list of units. - # So, to make a fair comparison if the candidate is 'better', it has to be scored in the same set. - to_be_rescored_units = [SchedulingUnitAndStartTime(u, u.scheduled_start_time) for u in blocking_placed_units] + [SchedulingUnitAndStartTime(scheduling_unit, start_time)] - logger.info("re-scoring units %s", ','.join(str(s.scheduling_unit.id) for s in to_be_rescored_units)) - scored_scheduling_units = compute_scores_for_units_with_start_time(to_be_rescored_units, - min([x.start_time for x in to_be_rescored_units]), - max([x.start_time+x.scheduling_unit.specified_observation_duration for x in to_be_rescored_units]), - gridder=self.fine_gridder) - - rescored_unit = next((ssu for ssu in scored_scheduling_units if ssu.scheduling_unit.id==scheduling_unit.id), None) - - if rescored_unit: - lower_scoring_units = [ssu for ssu in scored_scheduling_units if ssu.weighted_score < rescored_unit.weighted_score] - for blocking_placed_unit in lower_scoring_units: - blocking_placed_unit.scheduling_unit.placed = False - blocking_placed_unit.scheduling_unit.save() - - # we tried to make room for our candidate - # place it if no more blocking other units, else just assign the new starttime - if get_blocking_placed_units(scheduling_unit, start_time).exists(): - update_subtasks_start_times_for_scheduling_unit(scheduling_unit, start_time, placed=False) - return None - return update_subtasks_start_times_for_scheduling_unit(scheduling_unit, start_time, placed=True) - - - def place_or_schedule_B_priority_units_in_gaps_around_scheduling_unit(self, scheduling_unit: models.SchedulingUnitBlueprint, do_schedule: bool=False) -> [models.SchedulingUnitBlueprint]: - '''try to place/schedule one or more scheduling units from queue B in the gap between the given scheduled_unit and its previous observed+ unit''' - placed_units = [] + def schedule_B_priority_units_in_gaps_around_scheduling_unit(self, scheduling_unit: models.SchedulingUnitBlueprint) -> [models.SchedulingUnitBlueprint]: + '''try to schedule one or more scheduling units from queue B in the gap between the given scheduled_unit and its previous observed+ unit''' + scheduled_units = [] if not self.dynamic_scheduling_enabled: - logger.debug("skipping place_B_priority_units_in_gaps because dynamic_scheduling is not enabled") - return placed_units + logger.debug("skipping schedule_B_priority_units_in_gaps because dynamic_scheduling is not enabled") + return scheduled_units - logger.debug("place_B_priority_units_in_gaps: looking for B-queue units to be placed in gap(s) around unit id=%s", scheduling_unit.id) + logger.debug("schedule_B_priority_units_in_gaps: looking for B-queue units to be scheduled in gap(s) around unit id=%s", scheduling_unit.id) - schedulable_units_queue_B = get_dynamically_schedulable_scheduling_units(priority_queue=models.PriorityQueueType.objects.get(value=models.PriorityQueueType.Choices.B.value)).exclude(id=scheduling_unit.id).filter(placed=False) + schedulable_units_queue_B = get_dynamically_schedulable_scheduling_units(priority_queue=models.PriorityQueueType.objects.get(value=models.PriorityQueueType.Choices.B.value)).exclude(id=scheduling_unit.id) if not schedulable_units_queue_B.exists(): - return placed_units + return scheduled_units - gaps = get_gaps_to_previous_and_next_observations_in_scheduling_unit(scheduling_unit, include_schedulable_unschedulable=not do_schedule) + gaps = get_gaps_to_previous_and_next_observations_in_scheduling_unit(scheduling_unit, include_schedulable_unschedulable=False) gaps = [gap for gap in gaps if gap[1] != datetime.max] # skip open-ended future gaps for gap in gaps: @@ -805,7 +736,7 @@ class Scheduler: if (upper_bound_stop_time - lower_bound_start_time) <= 2*DEFAULT_INTER_OBSERVATION_GAP + timedelta(minutes=1): continue # gap is too small - logger.info("place_B_priority_units_in_gaps: evaluating %s B-queue units in %d[min]-wide gap ['%s', '%s') %s unit id=%s", + logger.info("schedule_B_priority_units_in_gaps: evaluating %s B-queue units in %d[min]-wide gap ['%s', '%s') %s unit id=%s", schedulable_units_queue_B.count(), (upper_bound_stop_time-lower_bound_start_time).total_seconds()/60, lower_bound_start_time, upper_bound_stop_time, @@ -815,40 +746,34 @@ class Scheduler: best_B_candidate_for_gap = self.find_best_next_schedulable_unit(schedulable_units_queue_B, lower_bound_start_time=lower_bound_start_time, upper_bound_stop_time=upper_bound_stop_time) if best_B_candidate_for_gap is not None and best_B_candidate_for_gap.scheduling_unit is not None: try: - logger.info("place_B_priority_units_in_gaps: trying to %s B-queue unit id=%s at start_time='%s' in gap ['%s', '%s')", - 'schedule' if do_schedule else 'place', + logger.info("schedule_B_priority_units_in_gaps: trying to schedule B-queue unit id=%s at start_time='%s' in gap ['%s', '%s')", best_B_candidate_for_gap.scheduling_unit.id, best_B_candidate_for_gap.start_time, lower_bound_start_time, upper_bound_stop_time) - if do_schedule: - maybe_scheduled_unit = self.try_schedule_unit(best_B_candidate_for_gap.scheduling_unit, best_B_candidate_for_gap.start_time) - if maybe_scheduled_unit is not None and maybe_scheduled_unit.status.value==models.SchedulingUnitStatus.Choices.SCHEDULED.value: - placed_units.append(best_B_candidate_for_gap.scheduling_unit) - else: - maybe_placed_unit = update_subtasks_start_times_for_scheduling_unit(best_B_candidate_for_gap.scheduling_unit, best_B_candidate_for_gap.start_time, placed=True) - if maybe_placed_unit is not None and maybe_placed_unit.placed: - placed_units.append(best_B_candidate_for_gap.scheduling_unit) + maybe_scheduled_unit = self.try_schedule_unit(best_B_candidate_for_gap.scheduling_unit, best_B_candidate_for_gap.start_time) + if maybe_scheduled_unit is not None and maybe_scheduled_unit.status.value==models.SchedulingUnitStatus.Choices.SCHEDULED.value: + scheduled_units.append(best_B_candidate_for_gap.scheduling_unit) except Exception as e: - logger.exception("place_B_priority_units_in_gaps: Could not schedule/place B-queue unit id=%s in gap( ['%s', '%s'). %s", + logger.exception("schedule_B_priority_units_in_gaps: Could not schedule B-queue unit id=%s in gap( ['%s', '%s'). %s", best_B_candidate_for_gap.scheduling_unit.id, lower_bound_start_time, upper_bound_stop_time, str(e)) try: - if best_B_candidate_for_gap.scheduling_unit in placed_units: - # unit has successfully been scheduled/placed. + if best_B_candidate_for_gap.scheduling_unit in scheduled_units: + # unit has successfully been scheduled. # Recurse. There may be a new gap, so let's try to squeeze in more. best_B_candidate_for_gap.scheduling_unit.refresh_from_db() - logger.debug("place_B_priority_units_in_gaps: recursing to place more B-queue units next to just placed unit id=%s", best_B_candidate_for_gap.scheduling_unit.id) - placed_units.extend(self.place_or_schedule_B_priority_units_in_gaps_around_scheduling_unit(best_B_candidate_for_gap.scheduling_unit, do_schedule=do_schedule)) + logger.debug("schedule_B_priority_units_in_gaps: recursing to schedule more B-queue units next to just scheduled unit id=%s", best_B_candidate_for_gap.scheduling_unit.id) + scheduled_units.extend(self.schedule_B_priority_units_in_gaps_around_scheduling_unit(best_B_candidate_for_gap.scheduling_unit)) except RecursionError as e: logger.error("Max recursion depth reached. Skipping further scheduling of B-queue units in %d[min]-wide gap( ['%s', '%s')", (upper_bound_stop_time-lower_bound_start_time).total_seconds()/60, lower_bound_start_time, upper_bound_stop_time) else: - logger.info("place_B_priority_units_in_gaps: could not find any B-queue unit out of the %d candidates which fits in %dmin gap ['%s', '%s') next to unit id=%s", + logger.info("schedule_B_priority_units_in_gaps: could not find any B-queue unit out of the %d candidates which fits in %dmin gap ['%s', '%s') next to unit id=%s", schedulable_units_queue_B.count(), (upper_bound_stop_time-lower_bound_start_time).total_seconds()/60.0, lower_bound_start_time, upper_bound_stop_time, scheduling_unit.id) - return placed_units + return scheduled_units def log_schedule(self, log_level: int=logging.INFO): @@ -872,8 +797,8 @@ class Scheduler: for unit in units_in_schedule: try: - if unit.status.value==models.SchedulingUnitStatus.Choices.SCHEDULABLE.value and not unit.placed: - # skip non-placed schedulable units + if unit.status.value==models.SchedulingUnitStatus.Choices.SCHEDULABLE.value: + # skip schedulable units continue task_center_time, transit_time, offset_to_transit, lowest_elevation, elevation_at_center, elevation_at_transit = get_timestamps_elevations_and_offset_to_transit(unit, unit.scheduled_start_time) @@ -1026,11 +951,11 @@ class TMSSDynamicSchedulingMessageHandler(TMSSEventMessageHandler): logger.info("triggering scheduler for fixed_time unit id=%s status=%s", id, status) self.scheduler.trigger() elif scheduling_unit.is_dynamically_scheduled and self.scheduler.dynamic_scheduling_enabled and not self.scheduler.is_scheduling: - if (status in [models.SchedulingUnitStatus.Choices.SCHEDULABLE.value] and not scheduling_unit.placed) or \ - status in [models.SchedulingUnitStatus.Choices.OBSERVING.value, + if status in [models.SchedulingUnitStatus.Choices.SCHEDULABLE.value, + models.SchedulingUnitStatus.Choices.OBSERVING.value, models.SchedulingUnitStatus.Choices.CANCELLED.value, models.SchedulingUnitStatus.Choices.OBSERVED.value]: - logger.info("triggering scheduler for dynamic unit id=%s status=%s placed=%s", id, status, scheduling_unit.placed) + logger.info("triggering scheduler for dynamic unit id=%s status=%s", id, status) self.scheduler.trigger() @@ -1040,7 +965,7 @@ class TMSSDynamicSchedulingMessageHandler(TMSSEventMessageHandler): scheduling_unit_blueprint = models.SchedulingUnitBlueprint.objects.get(id=id) at = get_at_constraint_timestamp(scheduling_unit_blueprint) if at is not None: - update_subtasks_start_times_for_scheduling_unit(scheduling_unit_blueprint, at, placed=False) + update_subtasks_start_times_for_scheduling_unit(scheduling_unit_blueprint, at) except: pass self.onSchedulingUnitBlueprintConstraintsRankOrQueueUpdated(id) @@ -1062,7 +987,7 @@ class TMSSDynamicSchedulingMessageHandler(TMSSEventMessageHandler): elif scheduling_unit_blueprint.status.value == models.SchedulingUnitStatus.Choices.SCHEDULED.value: logger.info("constraints/queue/priority for scheduled scheduling unit id=%s changed: unscheduling it, which will triggering a dynamic scheduling update...", id) unschedule_subtasks_in_scheduling_unit_blueprint(scheduling_unit_blueprint) - elif scheduling_unit_blueprint.status.value == models.SchedulingUnitStatus.Choices.SCHEDULABLE.value and not scheduling_unit_blueprint.placed: + elif scheduling_unit_blueprint.status.value == models.SchedulingUnitStatus.Choices.SCHEDULABLE.value and not self.scheduler.is_scheduling: logger.info("constraints/queue/priority for schedulable scheduling unit id=%s changed: triggering a dynamic scheduling update...", id) self.scheduler.trigger() @@ -1381,7 +1306,7 @@ def unschededule_blocking_scheduled_units_if_needed_and_possible(candidate_sched 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.scheduled_start_time) - unschedule_subtasks_in_scheduling_unit_blueprint(scheduled_scheduling_unit, placed=False) + unschedule_subtasks_in_scheduling_unit_blueprint(scheduled_scheduling_unit) if not can_run_after(scheduled_scheduling_unit, scheduled_scheduling_unit.scheduled_start_time, gridder): logger.info("marking id=%s '%s' unschedulable because it cannot run after start_time=%s", scheduled_scheduling_unit.id, scheduled_scheduling_unit.name, scheduled_scheduling_unit.scheduled_start_time) @@ -1396,7 +1321,7 @@ def unschededule_blocking_scheduled_units_if_needed_and_possible(candidate_sched scheduled_scheduling_unit.id, scheduled_scheduling_unit.name, scheduled_scheduling_unit.priority_queue.value, candidate_scheduling_unit.id, candidate_scheduling_unit.name, candidate_scheduling_unit.priority_queue.value, candidate_scheduling_unit.scheduled_start_time) - unschedule_subtasks_in_scheduling_unit_blueprint(scheduled_scheduling_unit, placed=False) + unschedule_subtasks_in_scheduling_unit_blueprint(scheduled_scheduling_unit) # compare the re-scored weighted_scores elif rescored_candidate.weighted_score > rescored_scheduled_unit.weighted_score: # the candidate clearly wins, let's try to make some space by unscheduling the scheduled_scheduling_unit @@ -1404,7 +1329,7 @@ def unschededule_blocking_scheduled_units_if_needed_and_possible(candidate_sched scheduled_scheduling_unit.id, scheduled_scheduling_unit.name, candidate_scheduling_unit.id, candidate_scheduling_unit.name, candidate_scheduling_unit.scheduled_start_time) - unschedule_subtasks_in_scheduling_unit_blueprint(scheduled_scheduling_unit, placed=False) + unschedule_subtasks_in_scheduling_unit_blueprint(scheduled_scheduling_unit) elif enough_stations_available_for_scheduling_unit(scheduled_scheduling_unit, unavailable_stations=candidate_scheduling_unit.main_observation_specified_stations): # the candidate does not win, but it's ok to unschedule the scheduled_scheduling_unit because it can run without the candidate's stations. logger.info("rescheduling id=%s '%s' start_time=%s without the stations of the best candidate id=%s '%s' start_time=%s", 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 2d82c672d2e..140960fa1b1 100755 --- a/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py +++ b/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py @@ -90,7 +90,7 @@ class BaseDynamicSchedulingTestCase(unittest.TestCase): cls.scheduling_set_medium = models.SchedulingSet.objects.create(**SchedulingSet_test_data(project=cls.project_medium)) cls.scheduling_set_high = models.SchedulingSet.objects.create(**SchedulingSet_test_data(project=cls.project_high)) - cls.scheduler = Scheduler(scheduling_window_width=timedelta(hours=48)) + cls.scheduler = Scheduler() cls.message_handler = TMSSDynamicSchedulingMessageHandler() def setUp(self) -> None: @@ -653,7 +653,7 @@ class TestFixedTimeScheduling(BaseDynamicSchedulingTestCase): scheduling_unit_blueprint.save() wipe_evaluate_constraints_caches() mark_independent_subtasks_in_scheduling_unit_blueprint_as_schedulable(scheduling_unit_blueprint) - self.scheduler.place_or_schedule_next_scheduling_unit(do_schedule=True) + self.scheduler.schedule_next_scheduling_unit() scheduling_unit_blueprint.refresh_from_db() self.assertEqual(models.SchedulingUnitStatus.Choices.SCHEDULED.value, scheduling_unit_blueprint.status.value) @@ -1406,9 +1406,9 @@ class TestFixedTimeScheduling(BaseDynamicSchedulingTestCase): - def test_bug_fix_TMSS_2538_B_prio_unit_should_be_placed_and_scheduled_in_gaps_only(self): + def test_bug_fix_TMSS_2538_B_prio_unit_should_be_scheduled_in_gaps_only(self): """ - Test to reproduce and fix the reported bug that a B-prio unit is placed/scheduled overlapping an A-prio unit. + Test to reproduce and fix the reported bug that a B-prio unit is scheduled overlapping an A-prio unit. See: https://support.astron.nl/jira/browse/TMSS-2538 """ # schedule a normal 4hour-long unit at 1hour into the future @@ -1453,15 +1453,14 @@ class TestFixedTimeScheduling(BaseDynamicSchedulingTestCase): self.assertEqual('dynamic', scheduling_unit_blueprint_B.scheduling_constraints_doc['scheduler']) self.assertEqual(scheduling_unit_blueprint_B.status.value, models.SchedulingUnitStatus.Choices.SCHEDULABLE.value) - # try to place the B unit... - self.scheduler.place_or_schedule_next_scheduling_unit(lower_bound=round_to_second_precision(datetime.utcnow()), do_schedule=False) + # try to schedule the B unit... + self.scheduler.schedule_next_scheduling_unit(lower_bound=round_to_second_precision(datetime.utcnow())) - # Assert the scheduling_unit has been placed and is schedulable. + # Assert the scheduling_unit has been scheduled. scheduling_unit_blueprint_B.refresh_from_db() - self.assertEqual(models.SchedulingUnitStatus.Choices.SCHEDULABLE.value, scheduling_unit_blueprint_B.status.value) - self.assertTrue(scheduling_unit_blueprint_B.placed) + self.assertEqual(models.SchedulingUnitStatus.Choices.SCHEDULED.value, scheduling_unit_blueprint_B.status.value) - # check that the bug has been solved: unitB should NOT be overlapping with unitA, and be placed before it. + # check that the bug has been solved: unitB should NOT be overlapping with unitA, and be scheduled before it. self.assertFalse(scheduling_unit_blueprint_B.scheduled_start_time > scheduling_unit_blueprint_A.scheduled_start_time and scheduling_unit_blueprint_B.scheduled_start_time < scheduling_unit_blueprint_A.scheduled_stop_time) self.assertTrue(scheduling_unit_blueprint_B.scheduled_stop_time < scheduling_unit_blueprint_A.scheduled_start_time) @@ -1474,15 +1473,15 @@ class TestFixedTimeScheduling(BaseDynamicSchedulingTestCase): self.assertEqual(models.SchedulingUnitStatus.Choices.SCHEDULED.value, scheduling_unit_blueprint_A.status.value) self.assertEqual(at, scheduling_unit_blueprint_A.scheduled_start_time) - # check that the bug has been solved: unitB should NOT be overlapping with unitA, and be placed before it. + # check that the bug has been solved: unitB should NOT be overlapping with unitA, and be scheduled before it. self.assertFalse(scheduling_unit_blueprint_B.scheduled_start_time > scheduling_unit_blueprint_A.scheduled_start_time and scheduling_unit_blueprint_B.scheduled_start_time < scheduling_unit_blueprint_A.scheduled_stop_time) self.assertTrue(scheduling_unit_blueprint_B.scheduled_stop_time < scheduling_unit_blueprint_A.scheduled_start_time) - def test_bug_fix_TMSS_2563_place_B_priority_units_incorrectly_placed(self): + def test_bug_fix_TMSS_2563_schedule_B_priority_units_incorrect_start_time(self): """ - Test to reproduce and fix the reported bug that placing B-prio unit(s) in a gap between A-prio units sometimes yields to the B-unit being placed at the incorrect timestamp and blocking other B-units. + Test to reproduce and fix the reported bug that placing B-prio unit(s) in a gap between A-prio units sometimes yields to the B-unit being scheduled at the incorrect timestamp and blocking other B-units. See: https://support.astron.nl/jira/browse/TMSS-2563 """ # schedule two fixed-time 1hour-long units, starting in just 4min and in 2h @@ -1861,7 +1860,7 @@ class TestDynamicScheduling(BaseDynamicSchedulingTestCase): reservation.save() # # call the method-under-test. - scheduled_unit = self.scheduler.place_or_schedule_next_scheduling_unit(do_schedule=True) + scheduled_unit = self.scheduler.schedule_next_scheduling_unit() # if the reservation is exclusive for project_A, then unit A should be scheduled, and unit B unchedulable # and vice versa @@ -2984,8 +2983,7 @@ class TestDynamicScheduling(BaseDynamicSchedulingTestCase): self.assertTrue(abs((best_scored_scheduling_unit.start_time - expected_start_time).total_seconds()) < 300) placed_scheduling_unit = update_subtasks_start_times_for_scheduling_unit(best_scored_scheduling_unit.scheduling_unit, - best_scored_scheduling_unit.start_time, - placed=True) + best_scored_scheduling_unit.start_time) self.assertTrue(abs((placed_scheduling_unit.scheduled_start_time - expected_start_time).total_seconds()) < 300) from datetime import date, time, datetime as dt @@ -3290,7 +3288,7 @@ class TestDynamicScheduling(BaseDynamicSchedulingTestCase): su_blueprint_1977 = create_scheduling_unit_blueprint_and_tasks_and_subtasks_from_scheduling_unit_draft(su_draft_1977) # schedule it dynamically to find a nice timestamp where it will be able to run - scheduled_unit = self.scheduler.place_or_schedule_next_scheduling_unit(do_schedule=True) + scheduled_unit = self.scheduler.schedule_next_scheduling_unit() self.assertIsNotNone(scheduled_unit) self.assertEqual(su_blueprint_1977.id, scheduled_unit.id) @@ -3624,7 +3622,7 @@ class TestDynamicScheduling(BaseDynamicSchedulingTestCase): start_time=datetime.utcnow()+timedelta(hours=1), stop_time =datetime.utcnow()+timedelta(hours=1, minutes=1)) - # let the scheduler try to place/schedule the unit + # let the scheduler try to schedule the unit # it should not find a spot (because of the high min_elevation) self.scheduler.do_dynamic_schedule() @@ -3787,7 +3785,7 @@ class TestDynamicScheduling(BaseDynamicSchedulingTestCase): self.assertNotIn('CS032', su_other_scheduled.main_observation_used_stations) self.scheduler.log_schedule() # for reference - def test_bugfix_TMSS_2638_TMSS_2639__place_B_prio_units_in_gaps_with_and_without_overlapping_IDOLS_observation(self): + def test_bugfix_TMSS_2638_TMSS_2639_schedule_B_prio_units_in_gaps_with_and_without_overlapping_IDOLS_observation(self): '''See https://support.astron.nl/jira/browse/TMSS-2638 and https://support.astron.nl/jira/browse/TMSS-2639 ''' @@ -3848,18 +3846,14 @@ class TestDynamicScheduling(BaseDynamicSchedulingTestCase): sub_B = create_scheduling_unit_blueprint_and_tasks_and_subtasks_from_scheduling_unit_draft(sud_B) self.assertEqual('B', sub_B.priority_queue.value) - # make sure the unit is not placed yet, so the scheduler will consider it as a candidate - sub_B.placed = False - sub_B.save() - # and make sure we enable dynamic (B-prio) scheduling for this moment ... setting = models.Setting.objects.get(name=models.SystemSettingFlag.Choices.DYNAMIC_SCHEDULING_ENABLED.value) setting.value = True setting.save() - # try to schedule it via the place_B_priority_units_in_gaps_around_scheduling_unit for both sub_fixed_one and sub_fixed_two + # try to schedule it via the schedule_B_priority_units_in_gaps_around_scheduling_unit for both sub_fixed_one and sub_fixed_two for sub_fixed in (sub_fixed_one, sub_fixed_two): - scheduled_units = self.scheduler.place_or_schedule_B_priority_units_in_gaps_around_scheduling_unit(sub_fixed, do_schedule=True) + scheduled_units = self.scheduler.schedule_B_priority_units_in_gaps_around_scheduling_unit(sub_fixed) self.assertEqual(1, len(scheduled_units)) self.assertTrue(sub_B in scheduled_units) sub_B.refresh_from_db() @@ -3867,8 +3861,6 @@ class TestDynamicScheduling(BaseDynamicSchedulingTestCase): # reset/unschedule it sub_B = unschedule_subtasks_in_scheduling_unit_blueprint(sub_B) - sub_B.placed = False - sub_B.save() # again, everything ok so far # now we have two scheduled fixed units with an expected gap in between. @@ -3931,9 +3923,9 @@ class TestDynamicScheduling(BaseDynamicSchedulingTestCase): setting.value = True setting.save() - # ... and try to schedule the B-prio unit again via the place_B_priority_units_in_gaps_around_scheduling_unit for both sub_fixed_one and sub_fixed_two + # ... and try to schedule the B-prio unit again via the schedule_B_priority_units_in_gaps_around_scheduling_unit for both sub_fixed_one and sub_fixed_two for sub_fixed in (sub_fixed_one, sub_fixed_two): - scheduled_units = self.scheduler.place_or_schedule_B_priority_units_in_gaps_around_scheduling_unit(sub_fixed, do_schedule=True) + scheduled_units = self.scheduler.schedule_B_priority_units_in_gaps_around_scheduling_unit(sub_fixed) self.assertEqual(1, len(scheduled_units)) self.assertTrue(sub_B in scheduled_units) sub_B.refresh_from_db() @@ -3941,11 +3933,9 @@ class TestDynamicScheduling(BaseDynamicSchedulingTestCase): # reset/unschedule it sub_B = unschedule_subtasks_in_scheduling_unit_blueprint(sub_B) - sub_B.placed = False - sub_B.save() - def test_competing_units_are_placed_non_overlapping(self): - """ Have a set of similar survey-like units, and make sure they are scheduled/placed one after the other in a non-overlapping way""" + def test_competing_units_are_scheduled_non_overlapping(self): + """ Have a set of similar survey-like units, and make sure they are scheduled one after the other in a non-overlapping way""" # prepare the test with a fixed unit, schedule it, and make it run. next_full_hour = datetime.utcnow().replace(minute=0, second=0, microsecond=0) + timedelta(hours=1) @@ -4009,9 +3999,9 @@ class TestDynamicScheduling(BaseDynamicSchedulingTestCase): self.scheduler.do_full_schedule_computation() - # now check the order of the placed/scheduled units. - placed_units = list(models.SchedulingUnitBlueprint.objects.exclude(id=su_fixed.id).order_by('scheduled_start_time').all()) - first_unit = placed_units[0] + # now check the order of the scheduled units. + scheduled_units = list(models.SchedulingUnitBlueprint.objects.exclude(id=su_fixed.id).order_by('scheduled_start_time').all()) + first_unit = scheduled_units[0] self.assertEqual(models.SchedulingUnitStatus.Choices.SCHEDULED.value, first_unit.status.value) # the first unit partially overlaps with the running su_fixed unit from above. @@ -4020,15 +4010,15 @@ class TestDynamicScheduling(BaseDynamicSchedulingTestCase): self.assertTrue((first_unit.on_sky_start_time - su_fixed.on_sky_stop_time) < timedelta(minutes=5)) # and check that they cover the expected number of days - last_unit = placed_units[-1] + last_unit = scheduled_units[-1] total_span = last_unit.on_sky_stop_time - first_unit.on_sky_start_time self.assertGreaterEqual(total_span, timedelta(days=3)) self.assertLessEqual(total_span, timedelta(days=4)) # make sure they don't overlap prev_unit = first_unit - for unit in placed_units[1:]: - self.assertTrue(unit.placed) + for unit in scheduled_units[1:]: + self.assertEqual(models.SchedulingUnitStatus.Choices.SCHEDULED.value, unit.status.value) self.assertGreater(unit.on_sky_start_time, prev_unit.on_sky_stop_time) prev_unit = unit @@ -4512,7 +4502,7 @@ class TestTriggers(BaseDynamicSchedulingTestCase): def test_trigger_get_scheduled_close_to_submission_time(self): # we expect triggers to run as soon as possible - # Set weight factor to prevent that dynamically scheduled units get placed in the future. + # Set weight factor to prevent that dynamically scheduled units get scheduled in the future. # Note: If TMSS is configured with a non-zero weight factor, triggers require strict time constraints to make # them get scheduled close to submission time! self._set_density_vs_optimal_weight_factor(0) @@ -4536,7 +4526,7 @@ class TestTriggers(BaseDynamicSchedulingTestCase): def test_trigger_get_scheduled_according_to_before_constraint(self): # we expect triggers to start within the allowed time window. - # Set weight factor to prevent that dynamically scheduled unit gets placed to start right away by default. + # Set weight factor to prevent that dynamically scheduled unit gets scheduled to start right away by default. # Triggers with strict time constraints should not start within the permitted time window nonetheless. self._set_density_vs_optimal_weight_factor(0.5) @@ -4664,7 +4654,7 @@ class TestTriggers(BaseDynamicSchedulingTestCase): # RT-PH-T1(test code, see: https://support.astron.nl/confluence/display/TMSS/DOC-01-a+Test+Plan+-+Verification+tests) # a trigger that gets dynamically scheduled should have priority over a regular fixed time observation - # Set weight factor to prevent that the dynamically scheduled trigger get placed in the future and the units + # Set weight factor to prevent that the dynamically scheduled trigger get scheduled in the future and the units # conflict with each other. self._set_density_vs_optimal_weight_factor(0) @@ -4962,7 +4952,7 @@ class TestTriggers(BaseDynamicSchedulingTestCase): # an observation that is already happening should be interrupted when a trigger comes in so that the trigger # can run right away, also when the trigger is dynamically scheduled and the regular obs is fixed time. - # Set weight factor to prevent that the dynamically scheduled trigger get placed in the future and the units + # Set weight factor to prevent that the dynamically scheduled trigger get scheduled in the future and the units # conflict with each other. self._set_density_vs_optimal_weight_factor(0) diff --git a/SAS/TMSS/backend/services/websocket/lib/websocket_service.py b/SAS/TMSS/backend/services/websocket/lib/websocket_service.py index adcab4dbc3d..2c6380431fc 100644 --- a/SAS/TMSS/backend/services/websocket/lib/websocket_service.py +++ b/SAS/TMSS/backend/services/websocket/lib/websocket_service.py @@ -208,8 +208,6 @@ class TMSSEventMessageHandlerForWebsocket(TMSSEventMessageHandler): json_blob['object_details']['status_value'] = model_instance.status.value if hasattr(model_instance, 'state'): json_blob['object_details']['state_value'] = model_instance.state.value - if hasattr(model_instance, 'placed'): - json_blob['object_details']['placed'] = model_instance.placed except Exception as e: logger.error("Cannot get object details for %s: %s", json_blob, e) @@ -258,9 +256,6 @@ class TMSSEventMessageHandlerForWebsocket(TMSSEventMessageHandler): def onSchedulingUnitBlueprintUpdated(self, id: int): self._post_update_on_websocket(id, self.ObjTypes.SCHED_UNIT_BLUEPRINT, self.ObjActions.UPDATE) - def onSchedulingUnitBlueprintPlacedUpdated(self, id: int, placed: bool): - self._post_update_on_websocket(id, self.ObjTypes.SCHED_UNIT_BLUEPRINT, self.ObjActions.UPDATE) - def onSchedulingUnitBlueprintDeleted(self, id: int): self._post_update_on_websocket(id, self.ObjTypes.SCHED_UNIT_BLUEPRINT, self.ObjActions.DELETE) diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/adapters/plots.py b/SAS/TMSS/backend/src/tmss/tmssapp/adapters/plots.py index 5d4dc6be919..8b91b487f15 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/adapters/plots.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/adapters/plots.py @@ -434,16 +434,15 @@ def scheduling_constraints_plot(scheduling_units: Union[models.SchedulingUnitBlu def scheduling_constraints_plot_for_date(proposed_date: date, station: str=None) -> bytes: - '''create a scheduling_constraints plot for all placed/scheduled/running/finished scheduling_units on the given date''' - scheduling_units = models.SchedulingUnitBlueprint.objects.filter(Q(status__value__in=(models.SchedulingUnitStatus.Choices.SCHEDULED.value, + '''create a scheduling_constraints plot for all scheduled/running/finished scheduling_units on the given date''' + scheduling_units = models.SchedulingUnitBlueprint.objects.filter(status__value__in=(models.SchedulingUnitStatus.Choices.SCHEDULED.value, models.SchedulingUnitStatus.Choices.OBSERVING.value, models.SchedulingUnitStatus.Choices.OBSERVED.value, models.SchedulingUnitStatus.Choices.PROCESSING.value, models.SchedulingUnitStatus.Choices.PROCESSED.value, models.SchedulingUnitStatus.Choices.INGESTING.value, models.SchedulingUnitStatus.Choices.INGESTED.value, - models.SchedulingUnitStatus.Choices.FINISHED.value)) | - Q(placed=True, status__value=models.SchedulingUnitStatus.Choices.SCHEDULABLE.value)) + models.SchedulingUnitStatus.Choices.FINISHED.value)) lower_bound = datetime(proposed_date.year, proposed_date.month, proposed_date.day, 0, 0, 0) upper_bound = lower_bound + timedelta(hours=24) scheduling_units = scheduling_units.filter(scheduled_stop_time__gte=lower_bound) @@ -467,7 +466,6 @@ def lst_pressure_plot(lower_bound: datetime=None, upper_bound: datetime=None, in lst_values = [lower_bound_day_start + timedelta(minutes=m) for m in range(24*60-3)] # LST day is 23h56m4s, so we have 24*60 minus 3 full minutes bins num_bins = len(lst_values) pressure_series = {'scheduled': {'color': 'blue', 'data': [0 for _ in lst_values]}, - 'placed': {'color': 'cornflowerblue', 'data': [0 for _ in lst_values]}, 'schedulable': {'color': 'lightblue', 'data': [0 for _ in lst_values]}, 'unschedulable': {'color': 'darkred', 'data': [0 for _ in lst_values]}, 'observing': {'color': 'yellow', 'data': [0 for _ in lst_values]}, @@ -479,8 +477,7 @@ def lst_pressure_plot(lower_bound: datetime=None, upper_bound: datetime=None, in # prepare some filters to query for observation tasks with certain statuses filters = { 'scheduled': Q(status__value=models.TaskStatus.Choices.SCHEDULED.value), - 'placed': Q(scheduling_unit_blueprint__placed=True, status__value=models.SchedulingUnitStatus.Choices.SCHEDULABLE.value), - 'schedulable': Q(scheduling_unit_blueprint__placed=False, status__value=models.SchedulingUnitStatus.Choices.SCHEDULABLE.value), + 'schedulable': Q(status__value=models.SchedulingUnitStatus.Choices.SCHEDULABLE.value), 'unschedulable': Q(status__value=models.TaskStatus.Choices.UNSCHEDULABLE.value), 'observing': Q(status__value__in=(models.TaskStatus.Choices.QUEUED.value, models.TaskStatus.Choices.STARTED.value)), 'observed': Q(status__value=models.TaskStatus.Choices.FINISHED.value), @@ -491,7 +488,7 @@ def lst_pressure_plot(lower_bound: datetime=None, upper_bound: datetime=None, in for status, q_filter in filters.items(): observation_tasks = models.TaskBlueprint.objects.filter(q_filter).filter(obsolete_since__isnull=True).filter(specifications_template__type__value=models.TaskType.Choices.OBSERVATION.value) - if status not in ('schedulable', 'unschedulable'): # 'placed' is actually 'schedulable' with placed=True, but we call it "status" here. + if status not in ('schedulable', 'unschedulable'): if lower_bound: observation_tasks = observation_tasks.filter(on_sky_stop_time__gte=lower_bound) if upper_bound: @@ -558,8 +555,7 @@ def lst_pressure_plot(lower_bound: datetime=None, upper_bound: datetime=None, in if upper_bound is None: # derive upper_bound from selected units from django.db.models import Max - scheduling_units = models.SchedulingUnitBlueprint.objects.filter(Q(status__value=models.SchedulingUnitStatus.Choices.SCHEDULED.value) | - Q(placed=True, status__value=models.SchedulingUnitStatus.Choices.SCHEDULABLE.value) ) + scheduling_units = models.SchedulingUnitBlueprint.objects.filter(status__value=models.SchedulingUnitStatus.Choices.SCHEDULED.value) scheduling_units = scheduling_units.filter(obsolete_since__isnull=True) upper_bound = scheduling_units.aggregate(Max('on_sky_stop_time'))['on_sky_stop_time__max'] @@ -586,7 +582,7 @@ def lst_pressure_plot(lower_bound: datetime=None, upper_bound: datetime=None, in axes.grid(True) # plot the lst-pressure graph - labels = ['observed', 'error', 'cancelled', 'observing', 'scheduled', 'placed', 'schedulable', 'unschedulable'] + labels = ['observed', 'error', 'cancelled', 'observing', 'scheduled', 'schedulable', 'unschedulable'] y_values = [pressure_series[label]['data'] for label in labels] colors = [pressure_series[label]['color'] for label in labels] axes.stackplot(lst_values, y_values, labels=labels, colors=colors, alpha=0.75) diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0050_remove_schedulingunitblueprint_placed.py b/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0050_remove_schedulingunitblueprint_placed.py new file mode 100644 index 00000000000..74b1354de21 --- /dev/null +++ b/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0050_remove_schedulingunitblueprint_placed.py @@ -0,0 +1,17 @@ +# Generated by Django 3.0.9 on 2023-10-11 17:14 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('tmssapp', '0049_cyclereport'), + ] + + operations = [ + migrations.RemoveField( + model_name='schedulingunitblueprint', + name='placed', + ), + ] diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py index 11b25f6fad9..082d0dd9dc7 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py @@ -1316,7 +1316,6 @@ class SchedulingUnitBlueprint(ProjectPropertyMixin, TemplateSchemaMixin, Schedul results_accepted = BooleanField(default=None, null=True, db_index=True, help_text='boolean (default None) which indicates whether this unit was accepted as successful or not.') global_identifier = OneToOneField('SIPidentifier', null=False, editable=False, on_delete=PROTECT, help_text='The global unique identifier for LTA SIP.') path_to_project = 'draft__scheduling_set__project' - placed = BooleanField(default=False, db_index=True, null=False, help_text='Was this unit placed by the scheduler, or are the scheduled_start/stop_time just best guesses?') class Meta(NamedCommon.Meta): constraints = [CheckConstraint(check=Q(rank__gte=SchedulingUnitRank.HIGHEST.value) & Q(rank__lte=SchedulingUnitRank.LOWEST.value), name='schedulingunitblueprint_rank_range_constraint')] diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py index e24e455ba86..97afde7bdfb 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py @@ -278,7 +278,7 @@ class SchedulingUnitBlueprintSerializer(DynamicRelationalHyperlinkedModelSeriali class Meta: model = models.SchedulingUnitBlueprint fields = '__all__' - read_only_fields = ['interrupts_telescope', 'unschedulable_reason', 'error_reason', 'placed'] + read_only_fields = ['interrupts_telescope', 'unschedulable_reason', 'error_reason'] extra_fields = ['task_blueprints', 'output_pinned', 'unschedulable_reason', 'error_reason'] expandable_fields = { 'specifications_template': 'lofar.sas.tmss.tmss.tmssapp.serializers.SchedulingUnitTemplateSerializer', @@ -311,7 +311,6 @@ class SchedulingUnitBlueprintSlimSerializer(serializers.ModelSerializer): 'status', 'unschedulable_reason', 'error_reason', - 'placed', 'scheduler', 'scheduling_constraints_template_id', 'process_start_time', diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py b/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py index 8a0d9a6e6ca..0469a9f39f8 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py @@ -1113,7 +1113,7 @@ def schedule_subtask_and_update_successor_start_times(subtask: Subtask, misc_una return scheduled_subtask -def update_subtasks_start_times_for_scheduling_unit(scheduling_unit: SchedulingUnitBlueprint, start_time: datetime, placed: bool=None): +def update_subtasks_start_times_for_scheduling_unit(scheduling_unit: SchedulingUnitBlueprint, start_time: datetime): with transaction.atomic(): for task_blueprint in scheduling_unit.task_blueprints.all(): defined_independend_subtasks = task_blueprint.subtasks.filter(state__value='defined').filter(inputs=None).all() @@ -1123,19 +1123,15 @@ def update_subtasks_start_times_for_scheduling_unit(scheduling_unit: SchedulingU else: update_start_time_and_shift_successors_until_after_stop_time(subtask, start_time + subtask.task_blueprint.relative_start_time) + for observation_subtask in scheduling_unit.subtasks.filter(specifications_template__type__value=SubtaskType.Choices.OBSERVATION.value).all(): + compute_scheduled_central_lst(observation_subtask) + # update cached start/stop times scheduling_unit.refresh_from_db() - if placed is not None: - scheduling_unit.placed = placed - scheduling_unit.save() - - if placed: - for observation_subtask in scheduling_unit.subtasks.filter(specifications_template__type__value=SubtaskType.Choices.OBSERVATION.value).all(): - compute_scheduled_central_lst(observation_subtask) - return scheduling_unit + def update_start_time_and_shift_successors_until_after_stop_time(subtask: Subtask, start_time: datetime): if subtask.state.value == SubtaskState.Choices.DEFINED.value: subtask.scheduled_start_time = start_time @@ -2597,9 +2593,6 @@ def get_gaps_to_previous_and_next_observations(subtask: Subtask, exclude_subtask # prepare query for non-obsolete non-cancelled/error observations qs = Subtask.objects.filter(specifications_template__type__value=SubtaskType.Choices.OBSERVATION.value).filter(obsolete_since__isnull=True).exclude(state__value__in=(SubtaskState.Choices.CANCELLED.value, SubtaskState.Choices.ERROR.value)) - # non-placed units do not have a relevant start/stoptime. - qs = qs.exclude(Q(placed=False) and Q(state__value=SubtaskState.Choices.DEFINED.value)) - if not include_defined_unschedulable: qs = qs.exclude(state__value=SubtaskState.Choices.DEFINED.value).exclude(state__value=SubtaskState.Choices.UNSCHEDULABLE.value) diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/tasks.py b/SAS/TMSS/backend/src/tmss/tmssapp/tasks.py index e2c52c74b96..b2a823dcff7 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/tasks.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/tasks.py @@ -549,15 +549,15 @@ def create_scheduling_unit_blueprint_and_tasks_and_subtasks_from_scheduling_unit time_constraints = constraints.get('time', {}) if 'at' in time_constraints: at = parser.parse(time_constraints['at'], ignoretz=True) - set_scheduling_unit_blueprint_start_times(scheduling_unit_blueprint, at, placed=True) + set_scheduling_unit_blueprint_start_times(scheduling_unit_blueprint, at) elif 'after' in time_constraints: after = parser.parse(time_constraints['after'], ignoretz=True) - set_scheduling_unit_blueprint_start_times(scheduling_unit_blueprint, after, placed=True) + set_scheduling_unit_blueprint_start_times(scheduling_unit_blueprint, after) elif 'between' in time_constraints: between_froms = [parser.parse(between['from'], ignoretz=True) for between in time_constraints['between'] if 'from' in between] if between_froms: earliest_from = min(between_froms) - set_scheduling_unit_blueprint_start_times(scheduling_unit_blueprint, earliest_from, placed=True) + set_scheduling_unit_blueprint_start_times(scheduling_unit_blueprint, earliest_from) if scheduling_unit_blueprint.interrupts_telescope: # check the trigger accounting, may result in scheduling_unit_blueprint ERROR status @@ -662,15 +662,10 @@ def schedule_independent_subtasks_in_scheduling_unit_blueprint(scheduling_unit_b schedule_independent_subtasks_in_task_blueprint(task_blueprint, start_time=start_time+task_blueprint.relative_start_time, misc_unavailable_stations=misc_unavailable_stations) scheduling_unit_blueprint.refresh_from_db() - - if not scheduling_unit_blueprint.placed: - scheduling_unit_blueprint.placed = True - scheduling_unit_blueprint.save() - return scheduling_unit_blueprint -def unschedule_subtasks_in_scheduling_unit_blueprint(scheduling_unit_blueprint: SchedulingUnitBlueprint, placed: bool=False) -> models.SchedulingUnitBlueprint: +def unschedule_subtasks_in_scheduling_unit_blueprint(scheduling_unit_blueprint: SchedulingUnitBlueprint) -> models.SchedulingUnitBlueprint: '''Convenience method: Unschedule all scheduled subtasks in the scheduling_unit_blueprint''' with transaction.atomic(): task_blueprints = list(scheduling_unit_blueprint.task_blueprints.all()) @@ -680,10 +675,6 @@ def unschedule_subtasks_in_scheduling_unit_blueprint(scheduling_unit_blueprint: scheduling_unit_blueprint.refresh_from_db() - if scheduling_unit_blueprint != placed: - scheduling_unit_blueprint.placed = placed - scheduling_unit_blueprint.save() - return scheduling_unit_blueprint @@ -694,7 +685,7 @@ def reschedule_subtasks_in_scheduling_unit_blueprint(scheduling_unit_blueprint: return schedule_independent_subtasks_in_scheduling_unit_blueprint(unscheduled_unit, misc_unavailable_stations=misc_unavailable_stations) -def set_scheduling_unit_blueprint_start_times(scheduling_unit_blueprint: SchedulingUnitBlueprint, first_start_time: datetime, placed: bool=None) -> models.SchedulingUnitBlueprint: +def set_scheduling_unit_blueprint_start_times(scheduling_unit_blueprint: SchedulingUnitBlueprint, first_start_time: datetime) -> models.SchedulingUnitBlueprint: '''Convenience method: Set the subtask.scheduled_start_time such that all scheduling_relations and dependencies are taken into account and the first subtask has a scheduled_start_time equal to first_start_time''' with transaction.atomic(): for task_blueprint in scheduling_unit_blueprint.task_blueprints.all(): @@ -703,10 +694,6 @@ def set_scheduling_unit_blueprint_start_times(scheduling_unit_blueprint: Schedul scheduling_unit_blueprint.refresh_from_db() - if placed is not None: - scheduling_unit_blueprint.placed = placed - scheduling_unit_blueprint.save() - return scheduling_unit_blueprint def mark_independent_subtasks_in_scheduling_unit_blueprint_as_unschedulable(scheduling_unit_blueprint: SchedulingUnitBlueprint, reason: str) -> models.SchedulingUnitBlueprint: @@ -991,9 +978,9 @@ def get_schedulable_stations(observation_task: TaskBlueprint, proposed_start_tim def mark_scheduling_unit_fixed_time_scheduled_at_scheduled_starttime(scheduling_unit_blueprint: SchedulingUnitBlueprint) -> models.SchedulingUnitBlueprint: - '''Convenience method: When the scheduling_unit is placed or scheduled, use its scheduled_start_time as 'at' constraint and set the scheduled-type to 'fixed_time'. As a result, the scheduler will try to scheduled it using these new constraints. + '''Convenience method: When the scheduling_unit is scheduled, use its scheduled_start_time as 'at' constraint and set the scheduled-type to 'fixed_time'. As a result, the scheduler will try to scheduled it using these new constraints. This is the 'inverse' of mark_scheduling_unit_dynamically_scheduled''' - if scheduling_unit_blueprint.placed or scheduling_unit_blueprint.status.value == models.SchedulingUnitStatus.Choices.SCHEDULED.value: + if scheduling_unit_blueprint.status.value in (models.SchedulingUnitStatus.Choices.SCHEDULABLE.value, models.SchedulingUnitStatus.Choices.SCHEDULED.value): logger.info("marking unit id=%s scheduling_constraints as fixed_time scheduled at starttime='%s'", scheduling_unit_blueprint.id, scheduling_unit_blueprint.scheduled_start_time) scheduling_unit_blueprint.scheduling_constraints_doc['scheduler'] = 'fixed_time' scheduling_unit_blueprint.scheduling_constraints_doc['time']['at'] = scheduling_unit_blueprint.scheduled_start_time.isoformat()+'Z' diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py index 0ab0a32c5c3..5d036be2b73 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py @@ -1233,13 +1233,13 @@ class SchedulingUnitBlueprintViewSet(LOFARViewSet): def scheduling_constraints_plot(self, request, pk=None, station=None, proposed_start_time_or_date=None): scheduling_unit = get_object_or_404(models.SchedulingUnitBlueprint, pk=pk) - if not scheduling_unit.placed and proposed_start_time_or_date is None: + if proposed_start_time_or_date is None: from lofar.sas.tmss.services.scheduling.constraints import get_at_constraint_timestamp at_timestamp = get_at_constraint_timestamp(scheduling_unit) if at_timestamp is not None: proposed_start_time_or_date = at_timestamp.isoformat() else: - return HttpResponseBadRequest("Cannot create scheduling constraints plot: scheduling_unit_blueprint id=%s is not placed/scheduled yet." %(pk,)) + return HttpResponseBadRequest("Cannot create scheduling constraints plot: scheduling_unit_blueprint id=%s is not scheduled yet." %(pk,)) proposed_start_time = None proposed_date = None @@ -1348,7 +1348,7 @@ class SchedulingUnitBlueprintViewSet(LOFARViewSet): timestamp = dateutil.parser.parse(timestamp_str) for unit in SchedulingUnitBlueprint.objects.filter(status__value='schedulable', obsolete_since__isnull=True).all(): - update_subtasks_start_times_for_scheduling_unit(unit, start_time=timestamp, placed=False) + update_subtasks_start_times_for_scheduling_unit(unit, start_time=timestamp) # just return a success return HttpResponse(status=200) diff --git a/SAS/TMSS/backend/test/acceptance_test/acceptance_test.py b/SAS/TMSS/backend/test/acceptance_test/acceptance_test.py index fabba0440fb..5ab859ab329 100755 --- a/SAS/TMSS/backend/test/acceptance_test/acceptance_test.py +++ b/SAS/TMSS/backend/test/acceptance_test/acceptance_test.py @@ -109,9 +109,9 @@ def populate_pulsar_timing_test_campaign(project: models.Project, between=None, elems = line.split() target = elems[0] - if models.SchedulingUnitDraft.objects.filter(name=target).exists(): - logger.info("skipping creation of unit with name='%s' because it already exists", target) - continue + # if models.SchedulingUnitDraft.objects.filter(name=target).exists(): + # logger.info("skipping creation of unit with name='%s' because it already exists", target) + # continue # read angle1 as h:m:s, and angle2 as d:m:s angle1 = Angle(elems[1].replace(':','h', 1).replace(':','m', 1)+'s') @@ -137,7 +137,7 @@ def populate_pulsar_timing_test_campaign(project: models.Project, between=None, scheduling_set, name=target, description="SU for test pulsar campaign target?%s" % target, - priority_queue=models.PriorityQueueType.objects.get(value=models.PriorityQueueType.Choices.B.value), + priority_queue=models.PriorityQueueType.objects.get(value=models.PriorityQueueType.Choices.A.value), specifications_doc_overrides={ 'tasks': { 'Observation': { @@ -159,8 +159,8 @@ def populate_pulsar_timing_test_campaign(project: models.Project, between=None, 'transit_offset': {'to': 2*60*60, 'from': -2*60*60}} } }) - - create_scheduling_unit_blueprint_and_tasks_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft) + for i in range(5): + create_scheduling_unit_blueprint_and_tasks_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft) if max_num_targets > 0: diff --git a/SAS/TMSS/client/lib/tmssbuslistener.py b/SAS/TMSS/client/lib/tmssbuslistener.py index 282e6d98abe..aa868b7180a 100644 --- a/SAS/TMSS/client/lib/tmssbuslistener.py +++ b/SAS/TMSS/client/lib/tmssbuslistener.py @@ -146,8 +146,6 @@ class TMSSEventMessageHandler(AbstractMessageHandler): self.onProjectCyclesUpdated(**msg.content) elif stripped_subject == 'ProjectCycles.Object.Deleted': self.onProjectCyclesDeleted(**msg.content) - elif stripped_subject == 'SchedulingUnitBlueprint.Object.Placed.Updated': - self.onSchedulingUnitBlueprintPlacedUpdated(**msg.content) elif stripped_subject == 'SchedulingUnitBlueprint.Object.Constraints.Updated': self.onSchedulingUnitBlueprintConstraintsUpdated(**msg.content) elif stripped_subject == 'SchedulingUnitBlueprint.Object.Rank.Updated': @@ -274,12 +272,6 @@ class TMSSEventMessageHandler(AbstractMessageHandler): ''' pass - def onSchedulingUnitBlueprintPlacedUpdated(self, id: int, placed: bool): - '''onSchedulingUnitBlueprintPlacedUpdated is called upon receiving a SchedulingUnitBlueprint.Object.Placed.Updated message, which is sent when the placed property on a SchedulingUnitDrafts was updated. - :param id: the TMSS id of the SchedulingUnitBlueprint - ''' - pass - def onSchedulingUnitBlueprintPriorityQueueUpdated(self, id: int, priority_queue: str): '''onSchedulingUnitBlueprintPriorityQueueUpdated is called upon receiving a SchedulingUnitBlueprint.Object.PriorityQueue.Updated message, which is sent when a the priority_queue on a SchedulingUnitDrafts was updated. :param id: the TMSS id of the SchedulingUnitBlueprint diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/helpers/timeline.renderer.helper.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/helpers/timeline.renderer.helper.js index ef4b250a7b9..4c1d452c702 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/helpers/timeline.renderer.helper.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/helpers/timeline.renderer.helper.js @@ -92,11 +92,9 @@ function renderSUItem({ itemContext, getItemProps }, setPopPositionCallback, setMouseOverItemCallback, setSummaryItemCallback) { - const placedStatusTextStyle = item.status === "schedulable" && !item.placed ? 0.5 : 1 const scheduleMethodBorder = item.scheduler === "dynamic" ? "1.5px dashed black" : "1.5px solid black" let itemDivStyle = getItemDivStyle(itemContext, item); itemDivStyle.fontWeight = 600 - itemDivStyle.opacity = placedStatusTextStyle itemDivStyle.border = scheduleMethodBorder let itemContentStyle = getItemContentStyle(itemContext, 3, 12) 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 08aba6eead0..16a898a4f20 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/services/schedule.service.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/services/schedule.service.js @@ -17,7 +17,6 @@ const SU_FETCH_FIELDS = ["id", "short_description", "status", "output_pinned", - "placed", "results_accepted", "unschedulable_reason", "scheduling_constraints_doc", diff --git a/SAS/TMSS/frontend/tmss_webapp/src/utils/ui.constants.js b/SAS/TMSS/frontend/tmss_webapp/src/utils/ui.constants.js index b7beb2adaca..cba11c84d76 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/utils/ui.constants.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/utils/ui.constants.js @@ -185,7 +185,6 @@ const UIConstants = { subtitle: "Indication about the settings", elements: [{classKey: "su-fixed_time", value: "Fixed Time"}, {classKey: "su-dynamic", value: "Dynamic"}, - {classKey: "su-placed", value: "Placed"}, {classKey: "su-unplaced", value: "Unplaced"} ] }, -- GitLab