# Path: narrative/models.py
# class Datum(models.Model):
#     # When was the datum created
#     timestamp = models.DateTimeField(auto_now_add=True, db_index=True)
#     expiration_time = models.DateTimeField(null=True, blank=True, default=None)
#     # Origin of this datum; ie, what piece of software created it
#     origin = models.CharField(max_length=64)
#     datum_name = models.CharField(max_length=64)
# 
#     # Additional information about the datum; this is very datum specific
#     # This approach, storing json in a textfield and having an
#     # explicit accessor and mutator, is very ugly.  Really this is because
#     # we are storing non-structured/sparse data in a relational format.
#     datum_note_json = models.TextField(null=True, blank=True, default=None)
# 
#     # An ID to tie particular datums together
#     thread_id = models.CharField(max_length=36, null=True, blank=True)
# 
#     log_level = models.IntegerField(choices=DatumLogLevel.types, default=DatumLogLevel.INFO)
# 
#     objects = DatumManager()
# 
#     def __init__(self, *args, **kwargs):
#         if 'ttl' in kwargs:
#             kwargs['expiration_time'] = self.get_utc_now() + kwargs.pop('ttl')
# 
#         super(Datum, self).__init__(*args, **kwargs)
# 
#     def __str__(self):
#         note_snippet = self.datum_note_json[:50] if self.datum_note_json else ''
#         return 'origin:{0} datum_name:{1} note:{2}'.format(
#             self.origin, self.datum_name, note_snippet
#         )
# 
#     def get_note(self):
#         if self.datum_note_json:
#             return json.loads(self.datum_note_json)
#         else:
#             return []
# 
#     def set_note(self, note):
#         self.datum_note_json = json.dumps(note)
# 
#     def get_utc_now(self):
#         return utc_tz.localize(datetime.datetime.utcnow())
# 
#     @property
#     def timestamp_with_tz(self):
#         return utc_tz.localize(self.timestamp)
# 
#     def save(self, *args, **kwargs):
#         if None == self.thread_id:
#             self.thread_id = str(uuid.uuid4())
# 
#         super(Datum, self).save(*args, **kwargs)
# 
# Path: narrative/executor.py
# class Executor(object):
#     def get_action_handler(self, action_name):
#         return 'do_{0}'.format(action_name)
# 
#     def can_execute(self, action_sequence):
#         """"
#         Examine all of the steps in an action sequence and verify
#         that they are supported by the Executor.
#         """
#         for step in action_sequence:
#             action, kwargs = step
# 
#             if not hasattr(self, self.get_action_handler(action)):
#                 return (False, step)
#         else:
#             return (True, None)
# 
#     def execute(self, action_sequence):
#         for step in action_sequence:
#             action, kwargs = step
# 
#             getattr(self, self.get_action_handler(action))(**kwargs)
# 
#     # do_* methods for executing particular operations such as notifying individuals ---
#     def do_defer_to_admins(self, subject, message, message_html=None):
#         admin_group = Group.objects.get(name=settings.NARRATIVE_ADMIN_GROUP_NAME)
#         admins = admin_group.user_set.all()
# 
#         admin_emails = [admin.email for admin in admins]
# 
#         blast_email(subject, message, message_html, admin_emails)
# 
#     def do_defer_multiple_solutions_to_admins(self, resolution_steps, assertion_display_name):
#         subject = 'Impasse: "{0}" has Multiple resolution steps proposed'.format(
#             assertion_display_name)
# 
#         message_kwargs = {
#             'assertion_name': assertion_display_name,
#             'solution_count': len(resolution_steps),
#             'proposed_steps': resolution_steps,
#         }
# 
#         message = render_to_string(
#             'multiple_solution_impasse_message.txt',
#             message_kwargs)
#         message_html = render_to_string(
#             'multiple_solution_impasse_message.html',
#             message_kwargs)
# 
#         self.do_defer_to_admins(subject, message, message_html)
# 
#     def do_email(self, address, subject, message_txt, message_html):
#         blast_email(subject, message_txt, message_html, [address])


import abc
import copy
import datetime
import json
import six
from narrative.models import Datum
from narrative.executor import Executor
        return self.__class__.__name__

    def get_utc_now(self):
        return datetime.datetime.utcnow()

    def event_instance_detected(self):
        """
        This method is called when the event instance is first detected.
        """
        pass

    def get_or_create_summary_datum(self, *args, **kwargs):
        """
        Create the summary datum to represent this occurrence of this event.
        """
        datum_kwargs = {
            'origin': self.origin_name,
            'datum_name': self.event_meta.display_name,
            'datum_note_json': json.dumps(self.instance_summary(*args, **kwargs)),
        }

        defaults = copy.copy(datum_kwargs)

        datum_kwargs['defaults'] = defaults

        # If this event has defined a TTL for it's summary datum, then
        # respect it.
        if self.summary_datum_ttl:
            defaults['ttl'] = self.summary_datum_ttl


# Path: narrative/models.py
# class Datum(models.Model):
#     # When was the datum created
#     timestamp = models.DateTimeField(auto_now_add=True, db_index=True)
#     expiration_time = models.DateTimeField(null=True, blank=True, default=None)
#     # Origin of this datum; ie, what piece of software created it
#     origin = models.CharField(max_length=64)
#     datum_name = models.CharField(max_length=64)
# 
#     # Additional information about the datum; this is very datum specific
#     # This approach, storing json in a textfield and having an
#     # explicit accessor and mutator, is very ugly.  Really this is because
#     # we are storing non-structured/sparse data in a relational format.
#     datum_note_json = models.TextField(null=True, blank=True, default=None)
# 
#     # An ID to tie particular datums together
#     thread_id = models.CharField(max_length=36, null=True, blank=True)
# 
#     log_level = models.IntegerField(choices=DatumLogLevel.types, default=DatumLogLevel.INFO)
# 
#     objects = DatumManager()
# 
#     def __init__(self, *args, **kwargs):
#         if 'ttl' in kwargs:
#             kwargs['expiration_time'] = self.get_utc_now() + kwargs.pop('ttl')
# 
#         super(Datum, self).__init__(*args, **kwargs)
# 
#     def __str__(self):
#         note_snippet = self.datum_note_json[:50] if self.datum_note_json else ''
#         return 'origin:{0} datum_name:{1} note:{2}'.format(
#             self.origin, self.datum_name, note_snippet
#         )
# 
#     def get_note(self):
#         if self.datum_note_json:
#             return json.loads(self.datum_note_json)
#         else:
#             return []
# 
#     def set_note(self, note):
#         self.datum_note_json = json.dumps(note)
# 
#     def get_utc_now(self):
#         return utc_tz.localize(datetime.datetime.utcnow())
# 
#     @property
#     def timestamp_with_tz(self):
#         return utc_tz.localize(self.timestamp)
# 
#     def save(self, *args, **kwargs):
#         if None == self.thread_id:
#             self.thread_id = str(uuid.uuid4())
# 
#         super(Datum, self).save(*args, **kwargs)
# 
# Path: narrative/executor.py
# class Executor(object):
#     def get_action_handler(self, action_name):
#         return 'do_{0}'.format(action_name)
# 
#     def can_execute(self, action_sequence):
#         """"
#         Examine all of the steps in an action sequence and verify
#         that they are supported by the Executor.
#         """
#         for step in action_sequence:
#             action, kwargs = step
# 
#             if not hasattr(self, self.get_action_handler(action)):
#                 return (False, step)
#         else:
#             return (True, None)
# 
#     def execute(self, action_sequence):
#         for step in action_sequence:
#             action, kwargs = step
# 
#             getattr(self, self.get_action_handler(action))(**kwargs)
# 
#     # do_* methods for executing particular operations such as notifying individuals ---
#     def do_defer_to_admins(self, subject, message, message_html=None):
#         admin_group = Group.objects.get(name=settings.NARRATIVE_ADMIN_GROUP_NAME)
#         admins = admin_group.user_set.all()
# 
#         admin_emails = [admin.email for admin in admins]
# 
#         blast_email(subject, message, message_html, admin_emails)
# 
#     def do_defer_multiple_solutions_to_admins(self, resolution_steps, assertion_display_name):
#         subject = 'Impasse: "{0}" has Multiple resolution steps proposed'.format(
#             assertion_display_name)
# 
#         message_kwargs = {
#             'assertion_name': assertion_display_name,
#             'solution_count': len(resolution_steps),
#             'proposed_steps': resolution_steps,
#         }
# 
#         message = render_to_string(
#             'multiple_solution_impasse_message.txt',
#             message_kwargs)
#         message_html = render_to_string(
#             'multiple_solution_impasse_message.html',
#             message_kwargs)
# 
#         self.do_defer_to_admins(subject, message, message_html)
# 
#     def do_email(self, address, subject, message_txt, message_html):
#         blast_email(subject, message_txt, message_html, [address])


# Path: narrative/events.py
import abc
import copy
import datetime
import json
import six
from narrative.models import Datum
from narrative.executor import Executor

        return self.__class__.__name__

    def get_utc_now(self):
        return datetime.datetime.utcnow()

    def event_instance_detected(self):
        """
        This method is called when the event instance is first detected.
        """
        pass

    def get_or_create_summary_datum(self, *args, **kwargs):
        """
        Create the summary datum to represent this occurrence of this event.
        """
        datum_kwargs = {
            'origin': self.origin_name,
            'datum_name': self.event_meta.display_name,
            'datum_note_json': json.dumps(self.instance_summary(*args, **kwargs)),
        }

        defaults = copy.copy(datum_kwargs)

        datum_kwargs['defaults'] = defaults

        # If this event has defined a TTL for it's summary datum, then
        # respect it.
        if self.summary_datum_ttl:
            defaults['ttl'] = self.summary_datum_ttl


        datum, created = Datum.objects.get_or_create(**datum_kwargs)