django-atris
============

Django-atris stores a snapshot of each tracked model on create/
update/delete operations.

Snapshots are available in a global form as well.

This app requires:
   - Django>=1.9 (For Django <1.9 please use django-atris <1.0.0)
   - Postgresql
   - Python>=2.7 or Python>=3.4

Integration guide
-----------------

In order to use the app you must do the following:
 * Add 'atris' to INSTALLED_APPS in settings
 * You MUST have 'django.contrib.postgres' in your INSTALLED_APPS
 * Add 'atris.middleware.LoggingRequestMiddleware' to MIDDLEWARE_CLASSES in order for the app to be able to get the user which made the changes
 * Put a field (named as you wish) in the model class that you desire to track that contains a HistoryLogging instance (i.e. history = HistoryLogging() )

Additional features:
   - Additional data -
                       if you wish to store some additional data regarding
                       an instance of your model, you can do so by adding a
                       dict to your model, which contains said additional data.
                       After creating that dict, use it when instantiating the
                       HistoryLogging field.
                            additional_data = {'changed_from':'djadmin'}
                            history = HistoryLogging(additional_data='additional_data')
   - Exclude fields -
                      if you wish to not track some fields, all you need to do
                      is add a list to your model which contains the fields you
                      do not wish to track in a string format and use that list
                      when instantiating the HistoryLogging field.
                           exclude_fields = ['last_modified'] # as it would always appear to have been updated
                           history = HistoryLogging(exclude_fields='exclude_fields')
   - Ignore changes by user -
                      if you wish to not track changes made by a specific user,
                      such as a user specially set up for smoke tests, you can declare
                      an additional field called however you like and pass it on
                      to the HistoryLogging object. This field must be a dictionary
                      that contains the keys 'user_ids' and 'user_names', both values
                      for these keys must be lists containing the appropriate information.
                           ignore_history_for_users = {'user_ids': [1010101], 'user_names': ['ignore_user']}
                           history = HistoryLogging(ignore_history_for_users='ignore_history_for_users')

Usage guide
-----------

After integrating the app in your own app, you can make use of it in several different ways.

For starters, the fields made available to you when inspecting a history instance are the following:
    * content_type = django contenttype
    * object_id = the model instance id that the history is kept of
    * history_date = the date that the history instance was created
    * history_user = the user that triggered the history instance (taken from middleware); For this string, the value it takes is prioritised in this order: fullname > email > username, if none are available it remains None.
    * history_user_id = the id of the user that triggered the history instance (taken from middleware)
    * history_type = type of history, +: Create, ~: Update, -:Delete (the method 'get_history_type_display()' gets you the string interpretation)
    * data = hstore field, contains a snapshot (in the form of a dict) of the model instance that the history is being kept of, doesn't contain excluded fields nor additional data fields
    * additional_data = hstore field, contains additional data of the model instance in the form of a dict

Example of usage in code:
* Classes we will use in example::

    >>> class Foo(models.Model):
    ...   field_1 = models.CharField(max_length=255)
    ...   field_2 = models.IntField()
    ...   last_modified = models.DateTimeField(auto_now=True)
    ...   excluded_fields = ['last_modified']
    ...   ignore_history_for_users = {'user_ids': [1010101], 'user_names': ['ignore_user']}
    ...   history = HistoryLogging(excluded_fields='excluded_fields',
    ...                               ignore_history_for_users='ignore_history_for_users)

    >>> class Bar(models.Model):
    ...   field_1 = models.CharField(max_length=255)
    ...   field_2 = models.IntField()
    ...   last_modified = models.DateTimeField(auto_now=True)
          # setting this specifies the default value for your additional data
    ...   additional_data = {'modified_from': 'code'}
    ...   excluded_fields = ['last_modified']
    ...   history = HistoryLogging('additional_data', 'excluded_fields')

    >>> foo = Foo.objects.create(field_1='aaa', field_2=0)
    >>> foo_1 = Foo.objects.create(field_1='bar', field_2=1)

* Get all the history information for the first model instance that was just created::

    >>> foo.history.all()
    [<HistoricalRecord: Create foo id=1>]

* Get all the history information for the Foo model::

    >>> Foo.history.all()
    [<HistoricalRecord: Create foo id=1>, <HistoricalRecord: Create foo id=2>]

* Get the global history information (ordered by history_date desc)::

    >>> from atris.models import HistoricalRecord
    >>> HistoricalRecord.objects.all()
    [<HistoricalRecord: Create bar id=1>, <HistoricalRecord: Create foo id=2>]

* Get all the history information for the Bar model::

    Bar.objects.create(field_1='aaa', field_2=0)
    >>> Bar.history.all()
    [<HistoricalRecord: Create bar id=1>]

* Get the global history information again::

    >>> HistoricalRecord.objects.all()
    [<HistoricalRecord: Create bar id=1>, <HistoricalRecord: Create foo id=2>,
     <HistoricalRecord: Create foo id=1>]

* Another way of getting history for a model::

    >>> HistoricalRecord.objects.by_model(Foo)
    [<HistoricalRecord: Create foo id=1>, <HistoricalRecord: Create foo id=2>]

* Another way of getting history for an instance of a model useful for deleted objects that you still want a history for::

    >>> HistoricalRecord.objects.by_model_and_model_id(Foo, foo.id)
    [<HistoricalRecord: Create foo id=1>]

* Get the snapshot of the bar instance created::

    >>> bar.history.first().data
    {u'field_1':u'aaa',u'field_2':u'0'}

* Get the additional data of the bar instance::

    >>> bar.history.first().additional_data
    {u'modified_from':u'code'}

* If you have a situation where the user cannot be determined from the django middleware you can also do the following::

    >>> bar.history_user = User(username='username') # where User is the django User model
    >>> bar.save()
    >>> bar.history.first().history_user
    u'username'

* You can also mark a user such that the history for that user does not get saved. You can do so either by user name(KEEP IN MIND: user name is considered the full name or email or user name of the user instance associated with the history, depending on which is available first, in that order) or ID. You can use this to tell atris to ignore changes made by certain users such as a smoke test user::

    >>> bar.history_user = User(username='ignore_user') # where User is the django User model
    >>> bar.save()
    >>> bar.history.filter(history_user='ignore_user').count()
    0

