def test_fill_range_only(self): tr = TimeRange(range_hr=120) now = now_utc_aware() expected_start = now - timedelta(hours=120) self.assertAlmostEquals( 0, abs((expected_start - tr.start).total_seconds()), 0) self.assertAlmostEquals( 0, abs((expected_start - tr.start_org).total_seconds()), 0) self.assertAlmostEquals(0, abs((tr.end - now).total_seconds()), 0) self.assertIsNone(tr.tzinfo_) self.assertTrue(tr.expandable) self.assertFalse(tr.expanded) self.assertFalse(tr.is_inf) self.assertAlmostEquals(120, tr.hr_length_org, 0) self.assertAlmostEquals(120, tr.hr_length, 0) self.assertEqual( f"{expected_start.strftime('%m-%d')} ~ {now.strftime('%m-%d')}", tr.expr_period_short) self.assertAlmostEquals(tr.end_time_seconds, time_to_seconds(now), 0) prd = tr.get_periods() self.assertListEqual([tr], prd)
def test_end_hr_range(self): end_dt = datetime(2020, 4, 4, 1, 1, 1, tzinfo=pytz.UTC) start_dt_expected = end_dt - timedelta(hours=120) tr = TimeRange(end=end_dt, range_hr=120) self.assertAlmostEquals( 0, abs((start_dt_expected - tr.start).total_seconds()), 0) self.assertAlmostEquals( 0, abs((start_dt_expected - tr.start_org).total_seconds()), 0) self.assertAlmostEquals(0, abs((tr.end - end_dt).total_seconds()), 0) self.assertIsNone(tr.tzinfo_) self.assertTrue(tr.expandable) self.assertFalse(tr.expanded) self.assertFalse(tr.is_inf) self.assertAlmostEquals(120, tr.hr_length_org, 0) self.assertAlmostEquals(120, tr.hr_length, 0) self.assertEqual( f"{start_dt_expected.strftime('%m-%d')} ~ {end_dt.strftime('%m-%d')}", tr.expr_period_short) self.assertAlmostEquals(tr.end_time_seconds, time_to_seconds(end_dt.time()), 0) prd = tr.get_periods() self.assertListEqual([tr], prd)
def test_nfill_start(self): start_dt = datetime(2020, 4, 4, 1, 1, 1, tzinfo=pytz.UTC) tr = TimeRange(start=start_dt, end_autofill_now=False) now = now_utc_aware() hr_diff = (now - start_dt).total_seconds() / 3600 self.assertAlmostEquals(0, abs((start_dt - tr.start).total_seconds()), 0) self.assertAlmostEquals(0, abs((start_dt - tr.start_org).total_seconds()), 0) self.assertIsNone(tr.end) self.assertIsNone(tr.tzinfo_) self.assertFalse(tr.expandable) self.assertFalse(tr.expanded) self.assertTrue(tr.is_inf) self.assertAlmostEquals(hr_diff, tr.hr_length_org, 0) self.assertAlmostEquals(hr_diff, tr.hr_length, 0) self.assertEqual(f"{start_dt.strftime('%m-%d')} ~ -", tr.expr_period_short) self.assertAlmostEquals(tr.end_time_seconds, time_to_seconds(now), 0) prd = tr.get_periods() self.assertListEqual([tr], prd)
def get_user_messages_total_count( self, channel_oids: Union[ObjectId, List[ObjectId]], *, hours_within: Optional[int] = None, start: Optional[datetime] = None, end: Optional[datetime] = None, period_count: int = 3, tzinfo_: Optional[tzinfo] = None) \ -> MemberMessageCountResult: match_d = self._channel_oids_filter_(channel_oids) trange = TimeRange(range_hr=hours_within, start=start, end=end, range_mult=period_count, tzinfo_=tzinfo_) self._attach_time_range_(match_d, trange=trange) # $switch expression for time range switch_branches = [] # Check for full range (inf) # `start` and `end` cannot be `None` for generating `ObjectId`, # however `start` and `end` for full range are `None`. if not trange.is_inf: for idx, range_ in enumerate(trange.get_periods()): start_id = dt_to_objectid(range_.start) if not start_id: continue end_id = dt_to_objectid(range_.end) if not end_id: continue switch_branches.append({ "case": { "$and": [{ "$gte": ["$" + OID_KEY, start_id] }, { "$lt": ["$" + OID_KEY, end_id] }] }, "then": str(idx) }) group_key = { MemberMessageCountResult.KEY_MEMBER_ID: "$" + MessageRecordModel.UserRootOid.key } if switch_branches: group_key[MemberMessageCountResult.KEY_INTERVAL_IDX] = { "$switch": { "branches": switch_branches } } aggr_pipeline = [{ "$match": match_d }, { "$group": { OID_KEY: group_key, MemberMessageCountResult.KEY_COUNT: { "$sum": 1 } } }] return MemberMessageCountResult(list(self.aggregate(aggr_pipeline)), period_count, trange)