def test_passes_clean(self): """UNIT test: services.simulation.models = passes CLEAN UP """ pass_models.PassSlots.objects.propagate() sc_passes_n_1 = pass_models.PassSlots.objects.filter( spacecraft=self.__sc_1).count() simulation_tasks.clean_passes() sc_passes_n_2 = pass_models.PassSlots.objects.filter( spacecraft=self.__sc_1).count() self.assertEquals(sc_passes_n_1, sc_passes_n_2) interval_2 = (sn_misc.get_next_midnight() + py_timedelta(days=5), sn_misc.get_next_midnight() + py_timedelta(days=6)) pass_models.PassSlots.objects.propagate(interval=interval_2) sc_passes_n_3 = pass_models.PassSlots.objects.filter( spacecraft=self.__sc_1).count() self.assertGreater(sc_passes_n_3, sc_passes_n_2) simulation_tasks.clean_passes(threshold=sn_misc.get_next_midnight() + py_timedelta(days=10)) sc_passes_n_4 = pass_models.PassSlots.objects.filter( spacecraft=self.__sc_1).count() self.assertEquals(sc_passes_n_4, 0)
def test_create_availability(self): """UNIT test: services.scheduling.models.availability This test validates the creation of an availability slot and the further automatic rejections for the creation of availability slots that match the start and end of this one. """ slot_s = sn_misc.get_next_midnight() slot_e = slot_s + py_timedelta(days=1) self.assertIsNotNone( availability_models.AvailabilitySlot.objects.create( groundstation=self.__gs, start=slot_s, end=slot_e ) ) self.assertIsNone( availability_models.AvailabilitySlot.objects.create( groundstation=self.__gs, start=slot_s, end=slot_e ) ) slot_s = slot_s + py_timedelta(days=1) slot_e = slot_s + py_timedelta(days=1) self.assertIsNotNone( availability_models.AvailabilitySlot.objects.create( groundstation=self.__gs, start=slot_s, end=slot_e ) ) self.assertIsNone( availability_models.AvailabilitySlot.objects.create( groundstation=self.__gs, start=slot_s, end=slot_e ) )
def generate_available_slots_daily(rule_values, interval=None): """ This method generates the available slots for a daily rule that starts and ends in the given dates, during the specified interval. TODO :: improve the generation algorithm in order to avoid generating slots since the start of the rule until the validity of the current interval (can be long) :param interval: Interval of applicability :param rule_values: The values for the ONCE availability rule """ if interval is None: interval = simulation.OrbitalSimulator.get_simulation_window() try: r = AvailabilityRuleDaily.objects.get( availabilityrule_ptr_id=rule_values['id']) except dj_exceptions.ObjectDoesNotExist as ex: logger.warning( '>>> Child rule does not exist, returning no slots, ex = ' + str(ex)) return [] result = [] slot_i = slots.position(interval, (r.starting_time, r.ending_time)) while slot_i[0] < interval[1]: slot = slots.cutoff(interval, slot_i) result.append((slot[0], slot[1])) slot_i = (slot_i[0] + py_timedelta(days=1), slot_i[1] + py_timedelta(days=1)) return result
def test_create_availability(self): """UNIT test: services.scheduling.models.availability This test validates the creation of an availability slot and the further automatic rejections for the creation of availability slots that match the start and end of this one. """ slot_s = sn_misc.get_next_midnight() slot_e = slot_s + py_timedelta(days=1) self.assertIsNotNone( availability_models.AvailabilitySlot.objects.create( groundstation=self.__gs, start=slot_s, end=slot_e)) self.assertIsNone( availability_models.AvailabilitySlot.objects.create( groundstation=self.__gs, start=slot_s, end=slot_e)) slot_s = slot_s + py_timedelta(days=1) slot_e = slot_s + py_timedelta(days=1) self.assertIsNotNone( availability_models.AvailabilitySlot.objects.create( groundstation=self.__gs, start=slot_s, end=slot_e)) self.assertIsNone( availability_models.AvailabilitySlot.objects.create( groundstation=self.__gs, start=slot_s, end=slot_e))
def test_create_passes(self): """UNIT test: services.simulation.models.passes This test validates the creation of an availability slot and the further automatic rejections for the creation of availability slots that match the start and end of this one. """ slot_s = sn_misc.get_next_midnight() slot_e = slot_s + py_timedelta(days=1) self.assertIsNotNone( pass_models.PassSlots.objects.create(spacecraft=self.__sc_1, groundstation=self.__gs_1, start=slot_s, end=slot_e)) self.assertIsNone( pass_models.PassSlots.objects.create(spacecraft=self.__sc_1, groundstation=self.__gs_1, start=slot_s, end=slot_e)) slot_s = slot_s + py_timedelta(days=1) slot_e = slot_s + py_timedelta(days=1) self.assertIsNotNone( pass_models.PassSlots.objects.create(spacecraft=self.__sc_1, groundstation=self.__gs_1, start=slot_s, end=slot_e)) self.assertIsNone( pass_models.PassSlots.objects.create(spacecraft=self.__sc_1, groundstation=self.__gs_1, start=slot_s, end=slot_e))
def create(self, spacecraft, groundstation, start, end, **kwargs): """Overriden method Overriden method that creates the provided pass slot in case a similar one does not exist. :param spacecraft: The spacecraft involved in the operational slot :param groundstation: The groundstation for the pass :param start: The start datetime object of the pass :param end: The end datetime object of the pass :param kwargs: Additional parameters :return: A reference to the just created object """ s_range = (start - py_timedelta(seconds=30), start + py_timedelta(seconds=30)) e_range = (end - py_timedelta(seconds=30), end + py_timedelta(seconds=30)) if self.filter(groundstation=groundstation, spacecraft=spacecraft, start__range=s_range, end__range=e_range).exists(): logger.warn('@PassManager.create(), CONFLICTING SLOT:\n' + '\t * slot already exists GS = ' + str(groundstation.identifier) + ', start = <' + start.isoformat() + '>, end = <' + end.isoformat() + '>') return None return super(PassManager, self).create(spacecraft=spacecraft, groundstation=groundstation, start=start, end=end, **kwargs)
def test_passes_reboot(self): """UNIT test: services.simulation.models - passes generation REBOOT This test validates that subsequent attempts to generate passes do not succeed in case that for the given update window, the passes had already been generated. """ # 1) consecutive propagations should not be permitted logger.debug('#### FIRST PART OF THE TEST, CURRENT INTERVAL') pass_models.PassSlots.objects.propagate() sc_passes_n_1 = pass_models.PassSlots.objects.filter( spacecraft=self.__sc_1).count() pass_models.PassSlots.objects.propagate() sc_passes_n_2 = pass_models.PassSlots.objects.filter( spacecraft=self.__sc_1).count() self.assertEquals(sc_passes_n_1, sc_passes_n_2) # 2) now, we change the interval of application for avoiding reboots logger.debug('#### SECOND PART OF THE TEST, FUTURE INTERVAL') interval = (sn_misc.get_next_midnight() + py_timedelta(days=30), sn_misc.get_next_midnight() + py_timedelta(days=33)) pass_models.PassSlots.objects.propagate(interval=interval) sc_passes_n_3 = pass_models.PassSlots.objects.filter( spacecraft=self.__sc_1).count() self.assertGreater(sc_passes_n_3, sc_passes_n_2) pass_models.PassSlots.objects.propagate(interval=interval) sc_passes_n_4 = pass_models.PassSlots.objects.filter( spacecraft=self.__sc_1).count() self.assertEquals(sc_passes_n_4, sc_passes_n_3)
def test_groundtracks_reboot(self): """UNIT test: services.simulation.models - gts generation REBOOT """ # 1) consecutive propagations should not be permitted logger.debug('#### FIRST PART OF THE TEST, CURRENT INTERVAL') groundtrack_models.GroundTrack.objects.propagate() sc_gts_n_1 = len( groundtrack_models.GroundTrack.objects.get( spacecraft=self.__sc_1).timestamp) groundtrack_models.GroundTrack.objects.propagate() sc_gts_n_2 = len( groundtrack_models.GroundTrack.objects.get( spacecraft=self.__sc_1).timestamp) self.assertEquals(sc_gts_n_1, sc_gts_n_2) # 2) now, we change the interval of application for avoiding reboots logger.debug('#### SECOND PART OF THE TEST, FUTURE INTERVAL') interval = (sn_misc.get_next_midnight() + py_timedelta(days=3), sn_misc.get_next_midnight() + py_timedelta(days=4)) groundtrack_models.GroundTrack.objects.propagate(interval=interval) sc_gts_n_3 = len( groundtrack_models.GroundTrack.objects.get( spacecraft=self.__sc_1).timestamp) self.assertGreater(sc_gts_n_3, sc_gts_n_2) groundtrack_models.GroundTrack.objects.propagate(interval=interval) sc_gts_n_4 = len( groundtrack_models.GroundTrack.objects.get( spacecraft=self.__sc_1).timestamp) self.assertEquals(sc_gts_n_4, sc_gts_n_3)
def test_groundtracks_clean(self): """UNIT test: services.simulation.models = groundtracks CLEAN UP """ groundtrack_models.GroundTrack.objects.propagate() sc_gts_n_1 = len( groundtrack_models.GroundTrack.objects.get( spacecraft=self.__sc_1).timestamp) simulation_tasks.clean_passes() sc_gts_n_2 = len( groundtrack_models.GroundTrack.objects.get( spacecraft=self.__sc_1).timestamp) self.assertEquals(sc_gts_n_1, sc_gts_n_2) interval_2 = (sn_misc.get_next_midnight() + py_timedelta(days=5), sn_misc.get_next_midnight() + py_timedelta(days=6)) groundtrack_models.GroundTrack.objects.propagate(interval=interval_2) sc_gts_n_3 = len( groundtrack_models.GroundTrack.objects.get( spacecraft=self.__sc_1).timestamp) self.assertGreater(sc_gts_n_3, sc_gts_n_2) simulation_tasks.clean_groundtracks( threshold=sn_misc.get_next_midnight() + py_timedelta(days=10)) sc_gts_n_4 = len( groundtrack_models.GroundTrack.objects.get( spacecraft=self.__sc_1).timestamp) self.assertEquals(sc_gts_n_4, 0)
def test_passes_clean(self): """UNIT test: services.simulation.models = passes CLEAN UP """ pass_models.PassSlots.objects.propagate() sc_passes_n_1 = pass_models.PassSlots.objects.filter( spacecraft=self.__sc_1 ).count() simulation_tasks.clean_passes() sc_passes_n_2 = pass_models.PassSlots.objects.filter( spacecraft=self.__sc_1 ).count() self.assertEquals(sc_passes_n_1, sc_passes_n_2) interval_2 = ( sn_misc.get_next_midnight() + py_timedelta(days=5), sn_misc.get_next_midnight() + py_timedelta(days=6) ) pass_models.PassSlots.objects.propagate(interval=interval_2) sc_passes_n_3 = pass_models.PassSlots.objects.filter( spacecraft=self.__sc_1 ).count() self.assertGreater(sc_passes_n_3, sc_passes_n_2) simulation_tasks.clean_passes( threshold=sn_misc.get_next_midnight() + py_timedelta(days=10) ) sc_passes_n_4 = pass_models.PassSlots.objects.filter( spacecraft=self.__sc_1 ).count() self.assertEquals(sc_passes_n_4, 0)
def cutoff(interval, slot): """Slot manipulation library This function cuts off the given slot within the given interval. This way, in case the slot starting time happens before the starting of the interval, the slot starting time is cutoff to the starting time of the interval. The same is done with the ending time of the slot. In case the slot ends too early (ends before the interval starts) or starts too late (starts after the interval ends), an exception is raised. :param interval: 2-tuple with the datetime objects for the interval :param slot: 2-tuple with the datetime objects for the slot :return: Processed slot with the starting and ending times cutted off """ if not slot or not interval: raise ValueError('@cutoff_slot: wrong parameters') if slot[0] > slot[1]: raise ValueError('@cutoff_slot: slot[0] > slot[1]') if interval[0] > interval[1]: raise ValueError('@cutoff_slot: interval[0] > interval[1]') # backup copy for cutting off and not ruining the original outter objects interval_w = ( interval[0] + py_timedelta(days=0), interval[1] + py_timedelta(days=0) ) slot_w = ( slot[0] + py_timedelta(days=0), slot[1] + py_timedelta(days=0) ) # CASE A: discard, too late if slot_w[1] <= interval_w[0]: raise ValueError('@cutoff_slot: slot[1] <= interval[0]') # CASE E: discard, too early if slot_w[0] >= interval_w[1]: raise ValueError('@cutoff_slot: slot[0] >= interval[1]') if slot_w[0] >= interval_w[0]: # CASE C: no cutoff is necessary if slot_w[1] <= interval_w[1]: return slot_w else: # CASE D: cutting off the ending time of the slot return slot_w[0], interval_w[1] # CASE B: cutting off slot's starting time return interval_w[0], slot_w[1]
def test_groundtracks_clean(self): """UNIT test: services.simulation.models = groundtracks CLEAN UP """ groundtrack_models.GroundTrack.objects.propagate() sc_gts_n_1 = len( groundtrack_models.GroundTrack.objects.get( spacecraft=self.__sc_1 ).timestamp ) simulation_tasks.clean_passes() sc_gts_n_2 = len( groundtrack_models.GroundTrack.objects.get( spacecraft=self.__sc_1 ).timestamp ) self.assertEquals(sc_gts_n_1, sc_gts_n_2) interval_2 = ( sn_misc.get_next_midnight() + py_timedelta(days=5), sn_misc.get_next_midnight() + py_timedelta(days=6) ) groundtrack_models.GroundTrack.objects.propagate(interval=interval_2) sc_gts_n_3 = len( groundtrack_models.GroundTrack.objects.get( spacecraft=self.__sc_1 ).timestamp ) self.assertGreater(sc_gts_n_3, sc_gts_n_2) simulation_tasks.clean_groundtracks( threshold=sn_misc.get_next_midnight() + py_timedelta(days=10) ) sc_gts_n_4 = len( groundtrack_models.GroundTrack.objects.get( spacecraft=self.__sc_1 ).timestamp ) self.assertEquals(sc_gts_n_4, 0)
def generate_available_slots_daily(rule_values, interval=None): """ This method generates the available slots for a daily rule that starts and ends in the given dates, during the specified interval. TODO :: improve the generation algorithm in order to avoid generating slots since the start of the rule until the validity of the current interval (can be long) :param interval: Interval of applicability :param rule_values: The values for the ONCE availability rule """ if interval is None: interval = simulation.OrbitalSimulator.get_simulation_window() try: r = AvailabilityRuleDaily.objects.get( availabilityrule_ptr_id=rule_values['id'] ) except dj_exceptions.ObjectDoesNotExist as ex: logger.warning( '>>> Child rule does not exist, returning no slots, ex = ' + str(ex) ) return [] result = [] slot_i = slots.position(interval, (r.starting_time, r.ending_time)) while slot_i[0] < interval[1]: slot = slots.cutoff(interval, slot_i) result.append((slot[0], slot[1])) slot_i = ( slot_i[0] + py_timedelta(days=1), slot_i[1] + py_timedelta(days=1) ) return result
def test_create_passes(self): """UNIT test: services.simulation.models.passes This test validates the creation of an availability slot and the further automatic rejections for the creation of availability slots that match the start and end of this one. """ slot_s = sn_misc.get_next_midnight() slot_e = slot_s + py_timedelta(days=1) self.assertIsNotNone( pass_models.PassSlots.objects.create( spacecraft=self.__sc_1, groundstation=self.__gs_1, start=slot_s, end=slot_e ) ) self.assertIsNone( pass_models.PassSlots.objects.create( spacecraft=self.__sc_1, groundstation=self.__gs_1, start=slot_s, end=slot_e ) ) slot_s = slot_s + py_timedelta(days=1) slot_e = slot_s + py_timedelta(days=1) self.assertIsNotNone( pass_models.PassSlots.objects.create( spacecraft=self.__sc_1, groundstation=self.__gs_1, start=slot_s, end=slot_e ) ) self.assertIsNone( pass_models.PassSlots.objects.create( spacecraft=self.__sc_1, groundstation=self.__gs_1, start=slot_s, end=slot_e ) )
def __convert__(self, value): if isinstance(value, py_timedelta): return value elif DateTimeDeltaType is not None and \ type(value) == DateTimeDeltaType: ret = py_timedelta(0, hours=value.hour, minutes=value.minute, seconds=value.second) return ret else: raise ValueError("This dbattribute may only be set to "+\ "datetime.datetime instances, not %s!" % \ repr(type(value)))
def test_passes_reboot(self): """UNIT test: services.simulation.models - passes generation REBOOT This test validates that subsequent attempts to generate passes do not succeed in case that for the given update window, the passes had already been generated. """ # 1) consecutive propagations should not be permitted logger.debug('#### FIRST PART OF THE TEST, CURRENT INTERVAL') pass_models.PassSlots.objects.propagate() sc_passes_n_1 = pass_models.PassSlots.objects.filter( spacecraft=self.__sc_1 ).count() pass_models.PassSlots.objects.propagate() sc_passes_n_2 = pass_models.PassSlots.objects.filter( spacecraft=self.__sc_1 ).count() self.assertEquals(sc_passes_n_1, sc_passes_n_2) # 2) now, we change the interval of application for avoiding reboots logger.debug('#### SECOND PART OF THE TEST, FUTURE INTERVAL') interval = ( sn_misc.get_next_midnight() + py_timedelta(days=30), sn_misc.get_next_midnight() + py_timedelta(days=33) ) pass_models.PassSlots.objects.propagate(interval=interval) sc_passes_n_3 = pass_models.PassSlots.objects.filter( spacecraft=self.__sc_1 ).count() self.assertGreater(sc_passes_n_3, sc_passes_n_2) pass_models.PassSlots.objects.propagate(interval=interval) sc_passes_n_4 = pass_models.PassSlots.objects.filter( spacecraft=self.__sc_1 ).count() self.assertEquals(sc_passes_n_4, sc_passes_n_3)
def position(interval, slot): """Slot manipulation library This function repositions the given slot within the given interval, so that the calculation of the generated slots can start right away. :param interval: The interval to reposition the slot within :param slot: The slot to be repositioned :return: The slot repositioned (start, end) might've changed """ if not slot or not interval: raise ValueError('@cutoff_slot: wrong parameters') if slot[0] > slot[1]: raise ValueError('@cutoff_slot: slot[0] > slot[1]') if interval[0] > interval[1]: raise ValueError('@cutoff_slot: interval[0] > interval[1]') r0_p = py_datetime.combine(interval[0].date(), slot[0].timetz()) r1_p = r0_p + (slot[1] - slot[0]) if r1_p < interval[0]: r0_p += py_timedelta(days=1) r1_p += py_timedelta(days=1) return r0_p, r1_p
def test_groundtracks_reboot(self): """UNIT test: services.simulation.models - gts generation REBOOT """ # 1) consecutive propagations should not be permitted logger.debug('#### FIRST PART OF THE TEST, CURRENT INTERVAL') groundtrack_models.GroundTrack.objects.propagate() sc_gts_n_1 = len(groundtrack_models.GroundTrack.objects.get( spacecraft=self.__sc_1 ).timestamp) groundtrack_models.GroundTrack.objects.propagate() sc_gts_n_2 = len(groundtrack_models.GroundTrack.objects.get( spacecraft=self.__sc_1 ).timestamp) self.assertEquals(sc_gts_n_1, sc_gts_n_2) # 2) now, we change the interval of application for avoiding reboots logger.debug('#### SECOND PART OF THE TEST, FUTURE INTERVAL') interval = ( sn_misc.get_next_midnight() + py_timedelta(days=3), sn_misc.get_next_midnight() + py_timedelta(days=4) ) groundtrack_models.GroundTrack.objects.propagate(interval=interval) sc_gts_n_3 = len(groundtrack_models.GroundTrack.objects.get( spacecraft=self.__sc_1 ).timestamp) self.assertGreater(sc_gts_n_3, sc_gts_n_2) groundtrack_models.GroundTrack.objects.propagate(interval=interval) sc_gts_n_4 = len(groundtrack_models.GroundTrack.objects.get( spacecraft=self.__sc_1 ).timestamp) self.assertEquals(sc_gts_n_4, sc_gts_n_3)
def create(self, groundstation, operation, periodicity, dates): """ This method creates a new object in the database. :param groundstation: reference to the ground station for the rule :param operation: type of operation (create or remove slots) :param periodicity: periodicity for the rule (once, daily, weekly) :param dates: applicability dates for the rule """ localtime = pytz_ref.LocalTimezone() starting_date_tz = localtime.tzname(dates[0]) ending_date_tz = localtime.tzname(dates[1]) if starting_date_tz != ending_date_tz: raise ValueError( 'Invalid DAILY rule, dates TZ differ: ' + '( starting_date_tz = ' + starting_date_tz + 'ending_date_tz = ' + ending_date_tz + ' )' ) starting_dt = dates[2].astimezone(pytz.utc) ending_dt = dates[3].astimezone(pytz.utc) diff_dt = ending_dt - starting_dt if diff_dt > py_timedelta(hours=24): raise ValueError( 'Invalid DAILY rule, diff_dt = ' + str(diff_dt) + ' > 24 hours' ) if ending_dt <= starting_dt: raise ValueError( 'Invalid DAILY rule, ending (' + ending_dt.isoformat() + ') ' + '<= starting (' + starting_dt.isoformat() + ')' ) return super(AvailabilityRuleDailyManager, self).create( groundstation=groundstation, operation=operation, periodicity=periodicity, starting_date=dates[0], ending_date=dates[1], starting_time=starting_dt, ending_time=ending_dt )
def create(self, groundstation, operation, periodicity, dates): """ This method creates a new object in the database. :param groundstation: reference to the ground station for the rule :param operation: type of operation (create or remove slots) :param periodicity: periodicity for the rule (once, daily, weekly) :param dates: applicability dates for the rule """ localtime = pytz_ref.LocalTimezone() starting_date_tz = localtime.tzname(dates[0]) ending_date_tz = localtime.tzname(dates[1]) if starting_date_tz != ending_date_tz: raise ValueError('Invalid DAILY rule, dates TZ differ: ' + '( starting_date_tz = ' + starting_date_tz + 'ending_date_tz = ' + ending_date_tz + ' )') starting_dt = dates[2].astimezone(pytz.utc) ending_dt = dates[3].astimezone(pytz.utc) diff_dt = ending_dt - starting_dt if diff_dt > py_timedelta(hours=24): raise ValueError('Invalid DAILY rule, diff_dt = ' + str(diff_dt) + ' > 24 hours') if ending_dt <= starting_dt: raise ValueError('Invalid DAILY rule, ending (' + ending_dt.isoformat() + ') ' + '<= starting (' + starting_dt.isoformat() + ')') return super(AvailabilityRuleDailyManager, self).create(groundstation=groundstation, operation=operation, periodicity=periodicity, starting_date=dates[0], ending_date=dates[1], starting_time=starting_dt, ending_time=ending_dt)
def test_generate_slots_daily_rule(self): """UNIT test: services.configuration.models.rules - DAILY slots This test validates that a DAILY rule generates the right amount of slots depending on the interval relation with the start/end moments of the rule itself. """ if self.__verbose_testing: print('>>> test_generate_slots_once_rule:') ####################################################################### # ### XXXX SIGNAL DISCONNECTED django_signals.post_save.disconnect( availability_signals.daily_rule_saved, sender=rule_models.AvailabilityRuleDaily ) ####################################################################### # ### 1) rule starts ends interval, no slot the first day rule_id = rule_jrpc.add_rule( self.__gs_1_id, sn_helpers.create_jrpc_daily_rule( starting_time=sn_misc.get_next_midnight() + py_timedelta( hours=2 ), ending_time=sn_misc.get_next_midnight() + py_timedelta( hours=3 ) ) ) rule_db_values = rule_models.AvailabilityRule.objects.filter( pk=rule_id ).values() interval = ( sn_misc.get_next_midnight() + py_timedelta(days=300, hours=4), sn_misc.get_next_midnight() + py_timedelta(days=303) ) slots = rule_models.AvailabilityRuleManager\ .generate_available_slots_daily( rule_db_values[0], interval=interval ) expected = [ ( sn_misc.get_next_midnight() + py_timedelta(days=301, hours=2), sn_misc.get_next_midnight() + py_timedelta(days=301, hours=3) ), ( sn_misc.get_next_midnight() + py_timedelta(days=302, hours=2), sn_misc.get_next_midnight() + py_timedelta(days=302, hours=3) ), ] self.assertListEqual(slots, expected) ####################################################################### # ### 2) rule starts before interval, ends within, # first day slot truncated rule_id = rule_jrpc.add_rule( self.__gs_1_id, sn_helpers.create_jrpc_daily_rule( starting_time=sn_misc.get_next_midnight() + py_timedelta( hours=2 ), ending_time=sn_misc.get_next_midnight() + py_timedelta( hours=6 ) ) ) rule_db_values = rule_models.AvailabilityRule.objects.filter( pk=rule_id ).values() interval = ( sn_misc.get_next_midnight() + py_timedelta(days=300, hours=4), sn_misc.get_next_midnight() + py_timedelta(days=303) ) slots = rule_models.AvailabilityRuleManager\ .generate_available_slots_daily( rule_db_values[0], interval=interval ) expected = [ ( sn_misc.get_next_midnight() + py_timedelta(days=300, hours=4), sn_misc.get_next_midnight() + py_timedelta(days=300, hours=6) ), ( sn_misc.get_next_midnight() + py_timedelta(days=301, hours=2), sn_misc.get_next_midnight() + py_timedelta(days=301, hours=6) ), ( sn_misc.get_next_midnight() + py_timedelta(days=302, hours=2), sn_misc.get_next_midnight() + py_timedelta(days=302, hours=6) ), ] self.assertListEqual(slots, expected) ####################################################################### # ### XXXX SIGNAL RECONNECTED django_signals.post_save.connect( availability_signals.daily_rule_saved, sender=rule_models.AvailabilityRuleDaily )
def test_generate_slots_once_rule(self): """UNIT test: services.configuration.models.rules - ONCE slots This test validates that a ONCE rule generates the right amount of slots depending on the interval relation with the start/end moments of the rule itself. """ if self.__verbose_testing: print('>>> test_generate_slots_once_rule:') ####################################################################### # ### XXXX SIGNAL DISCONNECTED django_signals.post_save.disconnect( availability_signals.once_rule_saved, sender=rule_models.AvailabilityRuleOnce ) ####################################################################### # ### 1) rule after interval rule_1_id = rule_jrpc.add_rule( self.__gs_1_id, sn_helpers.create_jrpc_once_rule( starting_time=sn_misc.get_next_midnight(), ending_time=sn_misc.get_next_midnight() + py_timedelta(hours=4) ) ) rule_1_db_values = rule_models.AvailabilityRule.objects.filter( pk=rule_1_id ).values() self.assertListEqual( rule_models.AvailabilityRuleManager.generate_available_slots_once( rule_1_db_values[0], ( sn_misc.get_next_midnight() - py_timedelta(hours=12), sn_misc.get_next_midnight() - py_timedelta(hours=3) ) ), [] ) self.assertTrue(rule_jrpc.remove_rule(self.__gs_1_id, rule_1_id)) ####################################################################### # ### 2) rule before interval rule_2_id = rule_jrpc.add_rule( self.__gs_1_id, sn_helpers.create_jrpc_once_rule( starting_time=sn_misc.get_next_midnight() - py_timedelta( hours=6 ), ending_time=sn_misc.get_next_midnight() - py_timedelta( hours=4 ) ) ) rule_2_db_values = rule_models.AvailabilityRule.objects.filter( pk=rule_2_id ).values() self.assertListEqual( rule_models.AvailabilityRuleManager.generate_available_slots_once( rule_2_db_values[0], ( sn_misc.get_next_midnight(), sn_misc.get_next_midnight() + py_timedelta(hours=9) ) ), [] ) self.assertTrue(rule_jrpc.remove_rule(self.__gs_1_id, rule_2_id)) ####################################################################### # ### 3) rule FULLY inside interval rule_3_id = rule_jrpc.add_rule( self.__gs_1_id, sn_helpers.create_jrpc_once_rule( starting_time=sn_misc.get_next_midnight() + py_timedelta( hours=2 ), ending_time=sn_misc.get_next_midnight() + py_timedelta( hours=4 ) ) ) rule_3_db_values = rule_models.AvailabilityRule.objects.filter( pk=rule_3_id ).values() self.assertListEqual( rule_models.AvailabilityRuleManager.generate_available_slots_once( rule_3_db_values[0], ( sn_misc.get_next_midnight(), sn_misc.get_next_midnight() + py_timedelta(hours=9) ) ), [( sn_misc.get_next_midnight() + py_timedelta(hours=2), sn_misc.get_next_midnight() + py_timedelta(hours=4) )] ) self.assertTrue(rule_jrpc.remove_rule(self.__gs_1_id, rule_3_id)) ####################################################################### # ### 4) rule start before the interval rule_4_id = rule_jrpc.add_rule( self.__gs_1_id, sn_helpers.create_jrpc_once_rule( starting_time=sn_misc.get_next_midnight() - py_timedelta( hours=1 ), ending_time=sn_misc.get_next_midnight() + py_timedelta( hours=4 ) ) ) rule_4_db_values = rule_models.AvailabilityRule.objects.filter( pk=rule_4_id ).values() self.assertListEqual( rule_models.AvailabilityRuleManager.generate_available_slots_once( rule_4_db_values[0], ( sn_misc.get_next_midnight(), sn_misc.get_next_midnight() + py_timedelta(hours=9) ) ), [( sn_misc.get_next_midnight(), sn_misc.get_next_midnight() + py_timedelta(hours=4) )] ) self.assertTrue(rule_jrpc.remove_rule(self.__gs_1_id, rule_4_id)) ####################################################################### # ### 5) rule ends after the interval rule_5_id = rule_jrpc.add_rule( self.__gs_1_id, sn_helpers.create_jrpc_once_rule( starting_time=sn_misc.get_next_midnight() + py_timedelta( hours=2 ), ending_time=sn_misc.get_next_midnight() + py_timedelta( hours=12 ) ) ) rule_5_db_values = rule_models.AvailabilityRule.objects.filter( pk=rule_5_id ).values() self.assertListEqual( rule_models.AvailabilityRuleManager.generate_available_slots_once( rule_5_db_values[0], ( sn_misc.get_next_midnight(), sn_misc.get_next_midnight() + py_timedelta(hours=9) ) ), [( sn_misc.get_next_midnight() + py_timedelta(hours=2), sn_misc.get_next_midnight() + py_timedelta(hours=9) )] ) self.assertTrue(rule_jrpc.remove_rule(self.__gs_1_id, rule_5_id)) ####################################################################### # ### XXXX SIGNAL RECONNECTED django_signals.post_save.connect( availability_signals.once_rule_saved, sender=rule_models.AvailabilityRuleOnce )
def merge_slots(p_slots, m_slots): """Slot manipulation library This function merges the slots that define the availability of the ground station with the slots that define the non-availability of a ground station. The result are the actual availability slots. IMPORTANT: input lists of slots must be order by starting datetime. :param p_slots: The list of (+) slots. :param m_slots: The list of (-) slots. :return: Resulting list with the actual available slots. """ if p_slots is None or m_slots is None: return [] if len(p_slots) < 1: return [] # Algorithm initialization slots = [] p_next = True # ### indicates whether the 'p' vector has to be updated p_n = len(p_slots) p_i = 0 m_i = 0 m_n = len(m_slots) if m_n > 0: m = m_slots[0] m_i = 1 else: # All slots will be generated from today on, so this will be the # "oldest" slot independently of the rest... m = ( misc.get_today_utc() - py_timedelta(days=1), misc.get_today_utc() - py_timedelta(days=1) ) # The algorithm is executed for all the add slots, since negative slots # do not generate actual slots at all, they only limit the range of the # add slots. while True: if p_next: # ### stop condition if p_i == p_n: break p = p_slots[p_i] p_i += 1 else: p_next = True if p[1] <= m[0]: # ### CASE A: slots.append(p) continue if p[0] >= m[1]: # ### CASE F: if m_i < m_n: m = m_slots[m_i] m_i += 1 else: slots.append(p) continue if p[0] < m[0]: if (p[1] > m[0]) and (p[1] <= m[1]): # ### CASE B: slots.append((p[0], m[0])) if p[1] > m[1]: # ### CASE C: slots.append((p[0], m[0])) p = (m[1], p[1]) p_next = False else: # ### CASE D: if p[1] > m[1]: p = (m[1], p[1]) p_next = False if m_i < m_n: m = m_slots[m_i] m_i += 1 return slots
def test_generate_slots_daily_rule(self): """UNIT test: services.configuration.models.rules - DAILY slots This test validates that a DAILY rule generates the right amount of slots depending on the interval relation with the start/end moments of the rule itself. """ if self.__verbose_testing: print('>>> test_generate_slots_once_rule:') ####################################################################### # ### XXXX SIGNAL DISCONNECTED django_signals.post_save.disconnect( availability_signals.daily_rule_saved, sender=rule_models.AvailabilityRuleDaily) ####################################################################### # ### 1) rule starts ends interval, no slot the first day rule_id = rule_jrpc.add_rule( self.__gs_1_id, sn_helpers.create_jrpc_daily_rule( starting_time=sn_misc.get_next_midnight() + py_timedelta(hours=2), ending_time=sn_misc.get_next_midnight() + py_timedelta(hours=3))) rule_db_values = rule_models.AvailabilityRule.objects.filter( pk=rule_id).values() interval = (sn_misc.get_next_midnight() + py_timedelta(days=300, hours=4), sn_misc.get_next_midnight() + py_timedelta(days=303)) slots = rule_models.AvailabilityRuleManager\ .generate_available_slots_daily( rule_db_values[0], interval=interval ) expected = [ (sn_misc.get_next_midnight() + py_timedelta(days=301, hours=2), sn_misc.get_next_midnight() + py_timedelta(days=301, hours=3)), (sn_misc.get_next_midnight() + py_timedelta(days=302, hours=2), sn_misc.get_next_midnight() + py_timedelta(days=302, hours=3)), ] self.assertListEqual(slots, expected) ####################################################################### # ### 2) rule starts before interval, ends within, # first day slot truncated rule_id = rule_jrpc.add_rule( self.__gs_1_id, sn_helpers.create_jrpc_daily_rule( starting_time=sn_misc.get_next_midnight() + py_timedelta(hours=2), ending_time=sn_misc.get_next_midnight() + py_timedelta(hours=6))) rule_db_values = rule_models.AvailabilityRule.objects.filter( pk=rule_id).values() interval = (sn_misc.get_next_midnight() + py_timedelta(days=300, hours=4), sn_misc.get_next_midnight() + py_timedelta(days=303)) slots = rule_models.AvailabilityRuleManager\ .generate_available_slots_daily( rule_db_values[0], interval=interval ) expected = [ (sn_misc.get_next_midnight() + py_timedelta(days=300, hours=4), sn_misc.get_next_midnight() + py_timedelta(days=300, hours=6)), (sn_misc.get_next_midnight() + py_timedelta(days=301, hours=2), sn_misc.get_next_midnight() + py_timedelta(days=301, hours=6)), (sn_misc.get_next_midnight() + py_timedelta(days=302, hours=2), sn_misc.get_next_midnight() + py_timedelta(days=302, hours=6)), ] self.assertListEqual(slots, expected) ####################################################################### # ### XXXX SIGNAL RECONNECTED django_signals.post_save.connect( availability_signals.daily_rule_saved, sender=rule_models.AvailabilityRuleDaily)
def test_generate_slots_once_rule(self): """UNIT test: services.configuration.models.rules - ONCE slots This test validates that a ONCE rule generates the right amount of slots depending on the interval relation with the start/end moments of the rule itself. """ if self.__verbose_testing: print('>>> test_generate_slots_once_rule:') ####################################################################### # ### XXXX SIGNAL DISCONNECTED django_signals.post_save.disconnect( availability_signals.once_rule_saved, sender=rule_models.AvailabilityRuleOnce) ####################################################################### # ### 1) rule after interval rule_1_id = rule_jrpc.add_rule( self.__gs_1_id, sn_helpers.create_jrpc_once_rule( starting_time=sn_misc.get_next_midnight(), ending_time=sn_misc.get_next_midnight() + py_timedelta(hours=4))) rule_1_db_values = rule_models.AvailabilityRule.objects.filter( pk=rule_1_id).values() self.assertListEqual( rule_models.AvailabilityRuleManager.generate_available_slots_once( rule_1_db_values[0], (sn_misc.get_next_midnight() - py_timedelta(hours=12), sn_misc.get_next_midnight() - py_timedelta(hours=3))), []) self.assertTrue(rule_jrpc.remove_rule(self.__gs_1_id, rule_1_id)) ####################################################################### # ### 2) rule before interval rule_2_id = rule_jrpc.add_rule( self.__gs_1_id, sn_helpers.create_jrpc_once_rule( starting_time=sn_misc.get_next_midnight() - py_timedelta(hours=6), ending_time=sn_misc.get_next_midnight() - py_timedelta(hours=4))) rule_2_db_values = rule_models.AvailabilityRule.objects.filter( pk=rule_2_id).values() self.assertListEqual( rule_models.AvailabilityRuleManager.generate_available_slots_once( rule_2_db_values[0], (sn_misc.get_next_midnight(), sn_misc.get_next_midnight() + py_timedelta(hours=9))), []) self.assertTrue(rule_jrpc.remove_rule(self.__gs_1_id, rule_2_id)) ####################################################################### # ### 3) rule FULLY inside interval rule_3_id = rule_jrpc.add_rule( self.__gs_1_id, sn_helpers.create_jrpc_once_rule( starting_time=sn_misc.get_next_midnight() + py_timedelta(hours=2), ending_time=sn_misc.get_next_midnight() + py_timedelta(hours=4))) rule_3_db_values = rule_models.AvailabilityRule.objects.filter( pk=rule_3_id).values() self.assertListEqual( rule_models.AvailabilityRuleManager.generate_available_slots_once( rule_3_db_values[0], (sn_misc.get_next_midnight(), sn_misc.get_next_midnight() + py_timedelta(hours=9))), [(sn_misc.get_next_midnight() + py_timedelta(hours=2), sn_misc.get_next_midnight() + py_timedelta(hours=4))]) self.assertTrue(rule_jrpc.remove_rule(self.__gs_1_id, rule_3_id)) ####################################################################### # ### 4) rule start before the interval rule_4_id = rule_jrpc.add_rule( self.__gs_1_id, sn_helpers.create_jrpc_once_rule( starting_time=sn_misc.get_next_midnight() - py_timedelta(hours=1), ending_time=sn_misc.get_next_midnight() + py_timedelta(hours=4))) rule_4_db_values = rule_models.AvailabilityRule.objects.filter( pk=rule_4_id).values() self.assertListEqual( rule_models.AvailabilityRuleManager.generate_available_slots_once( rule_4_db_values[0], (sn_misc.get_next_midnight(), sn_misc.get_next_midnight() + py_timedelta(hours=9))), [(sn_misc.get_next_midnight(), sn_misc.get_next_midnight() + py_timedelta(hours=4))]) self.assertTrue(rule_jrpc.remove_rule(self.__gs_1_id, rule_4_id)) ####################################################################### # ### 5) rule ends after the interval rule_5_id = rule_jrpc.add_rule( self.__gs_1_id, sn_helpers.create_jrpc_once_rule( starting_time=sn_misc.get_next_midnight() + py_timedelta(hours=2), ending_time=sn_misc.get_next_midnight() + py_timedelta(hours=12))) rule_5_db_values = rule_models.AvailabilityRule.objects.filter( pk=rule_5_id).values() self.assertListEqual( rule_models.AvailabilityRuleManager.generate_available_slots_once( rule_5_db_values[0], (sn_misc.get_next_midnight(), sn_misc.get_next_midnight() + py_timedelta(hours=9))), [(sn_misc.get_next_midnight() + py_timedelta(hours=2), sn_misc.get_next_midnight() + py_timedelta(hours=9))]) self.assertTrue(rule_jrpc.remove_rule(self.__gs_1_id, rule_5_id)) ####################################################################### # ### XXXX SIGNAL RECONNECTED django_signals.post_save.connect( availability_signals.once_rule_saved, sender=rule_models.AvailabilityRuleOnce)