def bucket_starts(timestamp, bucket_size): dt = datetime_from_timestamp(timestamp) assert isinstance(dt, datetime.datetime) if bucket_size.startswith('year'): return datetime.datetime(dt.year, 1, 1, tzinfo=utc_timezone) elif bucket_size.startswith('month'): return datetime.datetime(dt.year, dt.month, 1, tzinfo=utc_timezone) elif bucket_size.startswith('day'): return datetime.datetime(dt.year, dt.month, dt.day, tzinfo=utc_timezone) elif bucket_size.startswith('hour'): return datetime.datetime(dt.year, dt.month, dt.day, dt.hour, tzinfo=utc_timezone) elif bucket_size.startswith('minute'): return datetime.datetime(dt.year, dt.month, dt.day, dt.hour, dt.minute, tzinfo=utc_timezone) elif bucket_size.startswith('second'): return datetime.datetime(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, tzinfo=utc_timezone) else: raise ValueError("Bucket size not supported: {}".format(bucket_size))
def mutate(self, ticket: "Ticket"): ticket.history.append( TicketFieldUpdated( field="name", old_value=ticket.name, new_value=self.name, timestamp=datetime_from_timestamp(self.timestamp), )) ticket.name = self.name
def mutate(self, ticket: "Ticket"): ticket.history.append( TicketFieldUpdated( field="description", old_value=ticket.description, new_value=self.description, timestamp=datetime_from_timestamp(self.timestamp), )) ticket.description = self.description
def mutate(self, ticket: "Ticket"): # TODO: Investigate if there's a way to dependency inject this rather than reaching for the global app from project.application.tickets import get_application tickets_app = get_application() original_ticket = tickets_app.get_ticket( id=self.original_ticket_id, at=self.original_ticket_version) assert original_ticket is not None ticket.name = f"CLONED - {original_ticket.name}" ticket.description = original_ticket.description # When cloning a ticket, we decide not to copy the previous history but instead start fresh ticket.history = [ TicketCloned( original_ticket_id=self.original_ticket_id, original_ticket_name=original_ticket.name, timestamp=datetime_from_timestamp(self.timestamp), ) ]
def make_timebucket_id(log_id, timestamp, bucket_size): d = datetime_from_timestamp(timestamp) assert isinstance(d, datetime.datetime) if bucket_size.startswith('year'): boundary = '{:04}'.format(d.year) elif bucket_size.startswith('month'): boundary = '{:04}-{:02}'.format(d.year, d.month) elif bucket_size.startswith('day'): boundary = '{:04}-{:02}-{:02}'.format(d.year, d.month, d.day) elif bucket_size.startswith('hour'): boundary = '{:04}-{:02}-{:02}_{:02}'.format(d.year, d.month, d.day, d.hour) elif bucket_size.startswith('minute'): boundary = '{:04}-{:02}-{:02}_{:02}-{:02}'.format( d.year, d.month, d.day, d.hour, d.minute) elif bucket_size.startswith('second'): boundary = '{:04}-{:02}-{:02}_{:02}-{:02}-{:02}'.format( d.year, d.month, d.day, d.hour, d.minute, d.second) else: raise ValueError("Bucket size not supported: {}".format(bucket_size)) return uuid5(Namespace_Timebuckets, log_id.hex + '_' + boundary)
def make_timebucket_id(log_id: UUID, timestamp: Union[Decimal, float], bucket_size: str) -> UUID: d = datetime_from_timestamp(timestamp) assert isinstance(d, datetime.datetime) if bucket_size.startswith("year"): boundary = "{:04}".format(d.year) elif bucket_size.startswith("month"): boundary = "{:04}-{:02}".format(d.year, d.month) elif bucket_size.startswith("day"): boundary = "{:04}-{:02}-{:02}".format(d.year, d.month, d.day) elif bucket_size.startswith("hour"): boundary = "{:04}-{:02}-{:02}_{:02}".format(d.year, d.month, d.day, d.hour) elif bucket_size.startswith("minute"): boundary = "{:04}-{:02}-{:02}_{:02}-{:02}".format( d.year, d.month, d.day, d.hour, d.minute) elif bucket_size.startswith("second"): boundary = "{:04}-{:02}-{:02}_{:02}-{:02}-{:02}".format( d.year, d.month, d.day, d.hour, d.minute, d.second) else: raise ValueError("Bucket size not supported: {}".format(bucket_size)) return uuid5(Namespace_Timebuckets, log_id.hex + "_" + boundary)
def bucket_starts(timestamp: float, bucket_size: str) -> datetime.datetime: dt = datetime_from_timestamp(timestamp) assert isinstance(dt, datetime.datetime) if bucket_size.startswith("year"): return datetime.datetime(dt.year, 1, 1, tzinfo=utc_timezone) elif bucket_size.startswith("month"): return datetime.datetime(dt.year, dt.month, 1, tzinfo=utc_timezone) elif bucket_size.startswith("day"): return datetime.datetime(dt.year, dt.month, dt.day, tzinfo=utc_timezone) elif bucket_size.startswith("hour"): return datetime.datetime(dt.year, dt.month, dt.day, dt.hour, tzinfo=utc_timezone) elif bucket_size.startswith("minute"): return datetime.datetime(dt.year, dt.month, dt.day, dt.hour, dt.minute, tzinfo=utc_timezone) elif bucket_size.startswith("second"): return datetime.datetime( dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, tzinfo=utc_timezone, ) else: raise ValueError("Bucket size not supported: {}".format(bucket_size))
def updated_at(self) -> datetime: return datetime_from_timestamp(self.___last_modified__)
def test_entity_lifecycle(self): # Check the factory creates an instance. example1 = Example.__create__(a=1, b=2) self.assertIsInstance(example1, Example) # Check the instance is equal to itself. self.assertEqual(example1, example1) # Check the instance is equal to a clone of itself. clone = object.__new__(type(example1)) clone.__dict__.update(example1.__dict__) self.assertEqual(example1, clone) # Check the properties of the Example class. self.assertEqual(1, example1.a) self.assertEqual(2, example1.b) # Check the properties of the TimestampedVersionedEntity class. self.assertTrue(example1.id) self.assertEqual(example1.__version__, 0) self.assertTrue(example1.__created_on__) self.assertLess(example1.__created_on__, time.time()) self.assertGreater(example1.__created_on__, time.time() - 1) self.assertTrue(example1.__last_modified__) self.assertEqual(example1.__created_on__, example1.__last_modified__) # Test the datetime_from_timestamp() converts ok. tt = time.time() dt = datetime.datetime.utcnow() self.assertEqual(datetime_from_timestamp(tt).hour, dt.hour) # Check can get datetime from timestamps, and it corresponds to UTC. dt = datetime_from_timestamp(example1.__created_on__) self.assertLess(dt, datetime.datetime.utcnow()) self.assertGreater(dt, datetime.datetime.utcnow() - datetime.timedelta(1)) # Check a different type with the same values is not "equal" to the first. class Subclass(Example): pass other = object.__new__(Subclass) other.__dict__.update(example1.__dict__) self.assertEqual(example1.__dict__, other.__dict__) self.assertNotEqual(example1, other) # Check a second instance with the same values is not "equal" to the first. example2 = create_new_example(a=1, b=2) self.assertEqual(type(example1), type(example2)) self.assertNotEqual(example1, example2) # Check entity not hashable. with self.assertRaises(TypeError): hash(example1) # Setup the repo. repo = ExampleRepository(self.entity_event_store) # Check the example entities can be retrieved from the example repository. entity1 = repo[example1.id] self.assertIsInstance(entity1, Example) self.assertEqual(1, entity1.a) self.assertEqual(2, entity1.b) entity2 = repo[example2.id] self.assertIsInstance(entity2, Example) self.assertEqual(1, entity2.a) self.assertEqual(2, entity2.b) # Check the entity can be updated. entity1.a = 100 self.assertEqual(100, repo[entity1.id].a) entity1.b = -200 self.assertEqual(-200, repo[entity1.id].b) self.assertEqual(repo[entity1.id].__created_on__, entity1.__created_on__) self.assertEqual(repo[entity1.id].__last_modified__, entity1.__last_modified__) self.assertNotEqual(entity1.__last_modified__, entity1.__created_on__) self.assertEqual(0, entity1.count_heartbeats()) entity1.beat_heart() entity1.beat_heart() entity1.beat_heart() self.assertEqual(3, entity1.count_heartbeats()) self.assertEqual(3, repo[entity1.id].count_heartbeats()) # Check the entity can publish multiple events simultaneously. entity1.beat_heart(number_of_beats=3) self.assertEqual(6, repo[entity1.id].count_heartbeats()) # Check the entity can be discarded. entity1.__discard__() # Check the repo now raises a KeyError. self.assertRaises(RepositoryKeyError, repo.__getitem__, entity1.id) # Check the entity can't be discarded twice. self.assertRaises(AssertionError, entity1.__discard__) # Should fail to validate event with wrong entity ID. with self.assertRaises(OriginatorIDError): VersionedEntity.Event( originator_id=uuid4(), originator_version=0, ).__check_obj__(entity2) # Should fail to validate event with wrong entity version. with self.assertRaises(OriginatorVersionError): VersionedEntity.Event( originator_id=entity2.id, originator_version=0, __previous_hash__=entity2.__head__, ).__check_obj__(entity2) # Should validate event with correct entity ID and version. VersionedEntity.Event( originator_id=entity2.id, originator_version=entity2.__version__ + 1, __previous_hash__=entity2.__head__, ).__check_obj__(entity2) # Check an entity cannot be reregistered with the ID of a discarded entity. replacement_event = Example.Created( originator_id=entity1.id, a=11, b=12, originator_topic=get_topic(Example), ) with self.assertRaises(ConcurrencyError): publish(event=replacement_event)