Reasons
- ApplicationEvents are hyper focused on applicants. We need events focused on visitors
- We Also need org user events
- we want full funnel metrics (from visitor to applicants to followups and on)
- We will need auditable events
- types and uses of events are conflated
- hard to know if where we send PII to mixpanel
Goals:
-
useful distinctions between types of events
-
make it easy to tie events to different models
-
make it easy to get a consistent, unique individual identifier for analytics
-
UUIDs for mixpanel are always people
-
events between people:
- fire event on each person involved, other people can & should identified in metadata
- if we can fire one event and get funnels for all involved, we should (don't think this is possible)
Goals:
- Route applications based on things that happen to them (for example, followups)
- itemize existing logic uses
- itemize types to identify which are valuable
- Log more events to mixpanel in a consistent way that excludes PII
- Don't break existing functionality
- Same or greater clarity about what has happened to someone's case (including existing data)
- Just enough state-event architecture to capture existing data and history about applications
------ longer term (consider, don't build)
- Create a consistent way to add things to app history
- Create audit events for auditabilty
- Add state to applications
class Event(models.Model):
time = models.DateTimeField()
def log_to_mixpanel(self):
cleaned_data = self.remove_pii()
mixpanel.delay(self.get_uuid(), cleaned_data)
class VisitorEvent(models.Model):
visitor = models.ForeignKey(
'intake.Visitor', related_name='visitor_events')
event = models.OneToOneField(Event, primary_key=True)
class ApplicantEvent(models.Model):
applicant = models.ForeignKey(
'intake.Applicant', related_name='applicant_events')
event = models.OneToOneField(Event, primary_key=True)
class UserEvent(models.Model):
user = models.ForeignKey(
'auth.User', related_name='user_events')
event = models.OneToOneField(Event, primary_key=True)
----
class VisitorEvent(Event):
visitor = models.ForeignKey(
'intake.Visitor', related_name='visitor_events')
class ApplicantEvent(Event):
applicant = models.ForeignKey(
'intake.Applicant', related_name='applicant_events')
class ApplicationEvent(Event):
application = models.ForeignKey(
'intake.Application', related_name='application_events')
class UserEvent(Event):
user = models.ForeignKey(
'auth.User', related_name='user_events')
Two key uses: - logic stuff, in this case we are querying in a very targeted way (does this exist) - mixpanel, in which case we are not querying them - craft items that can be rolled up into history - initial application history base (queryable)
Both of these should make use of a one-to-one relationship - audit log
Example:
# application event type
# status update created
# tied to application id
@classmethod
def log_status_updated(cls, application_id, applicant_id, org_user, data):
new = ApplicationEvent(type='status_updated', **kwargs)
# so we can then query it.
self.log_to_mixpanel()
class ApplicationHistoryEvent(models.Model):
time = models.DateTimeField()
applicaton = ForeignKey
actor = ForeignKey(null=True) # null implies automated
def log_to_mixpanel()
pass
def remove_pii():
pass
class NoteAdded(models.Model):
event = models.OneToOneField(ApplicationHistoryEvent, primary_key=True)
notification = models.ForeignKey('intake.Note')
def log_to_mixpanel():
# get uuid for applicant
# get uuid for user
# remove PII
# log both to mixpanel.