From c5591ea0690ff9c0ea760677e2ebe93f6e11aaca Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Thu, 11 Apr 2013 19:18:13 -0700 Subject: [PATCH 1/8] Add test files --- runtests.py | 34 ++++++++++++++++ setup.py | 6 ++- simple_history/tests/__init__.py | 0 simple_history/tests/models.py | 17 ++++++++ simple_history/tests/tests.py | 69 ++++++++++++++++++++++++++++++++ 5 files changed, 124 insertions(+), 2 deletions(-) create mode 100755 runtests.py create mode 100644 simple_history/tests/__init__.py create mode 100644 simple_history/tests/models.py create mode 100644 simple_history/tests/tests.py diff --git a/runtests.py b/runtests.py new file mode 100755 index 000000000..bfe6ad99a --- /dev/null +++ b/runtests.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +import sys +from os.path import abspath, dirname + +from django.conf import settings + + +sys.path.insert(0, abspath(dirname(__file__))) + + +if not settings.configured: + settings.configure( + INSTALLED_APPS=( + 'django.contrib.contenttypes', + 'simple_history', + 'simple_history.tests' + ), + DATABASES={ + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + } + }, + ) + + +def main(): + from django.test.simple import DjangoTestSuiteRunner + failures = DjangoTestSuiteRunner( + verbosity=1, interactive=True, failfast=False).run_tests(['tests']) + sys.exit(failures) + + +if __name__ == "__main__": + main() diff --git a/setup.py b/setup.py index 712d7d2d4..c64bfa2bb 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -from distutils.core import setup +from setuptools import setup import os # compile the list of packages available, because distutils doesn't have an easy way to do this @@ -38,4 +38,6 @@ "Development Status :: 5 - Production/Stable", "Framework :: Django", ], - ) + tests_require=["Django>=1.2"], + test_suite='runtests.main', +) diff --git a/simple_history/tests/__init__.py b/simple_history/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/simple_history/tests/models.py b/simple_history/tests/models.py new file mode 100644 index 000000000..59e61c142 --- /dev/null +++ b/simple_history/tests/models.py @@ -0,0 +1,17 @@ +from django.db import models +from simple_history.models import HistoricalRecords + + +class Poll(models.Model): + question = models.CharField(max_length=200) + pub_date = models.DateTimeField('date published') + + history = HistoricalRecords() + + +class Choice(models.Model): + poll = models.ForeignKey(Poll) + choice = models.CharField(max_length=200) + votes = models.IntegerField() + + history = HistoricalRecords() diff --git a/simple_history/tests/tests.py b/simple_history/tests/tests.py new file mode 100644 index 000000000..1928ebd2a --- /dev/null +++ b/simple_history/tests/tests.py @@ -0,0 +1,69 @@ +from datetime import datetime, timedelta +from django.test import TestCase + +from .models import Poll + + +today = datetime(2021, 1, 1, 10, 0) +tomorrow = today + timedelta(days=1) + + +class HistoricalRecordsTest(TestCase): + + def assertDatetimesEqual(self, time1, time2): + self.assertAlmostEqual(time1, time2, delta=timedelta(seconds=2)) + + def assertRecordValues(self, record, values_dict): + for key, value in values_dict.items(): + self.assertEqual(getattr(record, key), value) + + def test_create(self): + p = Poll(question="what's up?", pub_date=today) + p.save() + history = p.history.all() + record, = history + self.assertRecordValues(record, { + 'question': "what's up?", + 'pub_date': today, + 'id': p.id, + 'history_type': "+" + }) + + def test_update(self): + Poll.objects.create(question="what's up?", pub_date=today) + p = Poll.objects.get() + p.pub_date = tomorrow + p.save() + history = p.history.all() + update_record, create_record = history + self.assertRecordValues(create_record, { + 'question': "what's up?", + 'pub_date': today, + 'id': p.id, + 'history_type': "+" + }) + self.assertRecordValues(update_record, { + 'question': "what's up?", + 'pub_date': tomorrow, + 'id': p.id, + 'history_type': "~" + }) + + def test_delete(self): + p = Poll.objects.create(question="what's up?", pub_date=today) + poll_id = p.id + p.delete() + history = Poll.history.all() + delete_record, create_record = history + self.assertRecordValues(create_record, { + 'question': "what's up?", + 'pub_date': today, + 'id': poll_id, + 'history_type': "+" + }) + self.assertRecordValues(delete_record, { + 'question': "what's up?", + 'pub_date': today, + 'id': poll_id, + 'history_type': "-" + }) From 6932d260731374852ae32d62ccd71aba4d9e3738 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Thu, 11 Apr 2013 19:18:49 -0700 Subject: [PATCH 2/8] Add travis and tox files --- .travis.yml | 30 ++++++++++++++++++++++++++++ runtests.sh | 4 ++++ tox.ini | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 .travis.yml create mode 100755 runtests.sh create mode 100644 tox.ini diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..812d7af9a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,30 @@ +language: python + +python: + - "2.5" + - "2.6" + - "2.7" + +env: + - DJANGO=Django==1.2.7 SOUTH=1 + - DJANGO=Django==1.3.7 SOUTH=1 + - DJANGO=Django==1.4.5 SOUTH=1 + - DJANGO=Django==1.5.1 SOUTH=1 + - DJANGO=https://github.com/django/django/tarball/master SOUTH=1 + - DJANGO=Django==1.3.7 SOUTH=0 + +install: + - pip install $DJANGO --use-mirrors + - pip install coverage coveralls --use-mirrors + - sh -c "if [ '$SOUTH' = '1' ]; then pip install South==0.7.6; fi" + +script: coverage run -a --branch --include="simple_history/*" --omit="simple_history/tests/*" setup.py test + +matrix: + exclude: + - python: 2.5 + env: DJANGO=Django==1.5.1 SOUTH=1 + - python: 2.5 + env: DJANGO=https://github.com/django/django/tarball/master SOUTH=1 + +after_success: coveralls diff --git a/runtests.sh b/runtests.sh new file mode 100755 index 000000000..4edc36033 --- /dev/null +++ b/runtests.sh @@ -0,0 +1,4 @@ +#!/bin/sh +coverage erase +tox +coverage html --include=simple_history/* --omit=simple_history/tests/* diff --git a/tox.ini b/tox.ini new file mode 100644 index 000000000..82a5e9329 --- /dev/null +++ b/tox.ini @@ -0,0 +1,57 @@ +[tox] +envlist=py25-1.2,py25-1.3,py26-1.4,py26,py26-trunk,py27,py27-trunk,py27-1.5-nosouth + +[testenv] +deps= + django==1.5.1 + South==0.7.6 + coverage==3.6 +commands=coverage run -a --branch setup.py test + +[testenv:py25-1.2] +basepython=python2.5 +deps= + django==1.2.7 + South==0.7.6 + coverage==3.6 + +[testenv:py25-1.3] +basepython=python2.5 +deps= + django==1.3.7 + South==0.7.6 + coverage==3.6 + +[testenv:py26-1.4] +basepython=python2.6 +deps= + django==1.4.5 + South==0.7.6 + coverage==3.6 + +[testenv:py26] +basepython=python2.6 +deps= + django==1.5.1 + South==0.7.6 + coverage==3.6 + +[testenv:py26-trunk] +basepython=python2.6 +deps= + https://github.com/django/django/tarball/master + South==0.7.6 + coverage==3.6 + +[testenv:py27-trunk] +basepython=python2.7 +deps= + https://github.com/django/django/tarball/master + South==0.7.6 + coverage==3.6 + +[testenv:py27-1.5-nosouth] +basepython=python2.7 +deps= + django==1.5.0 + coverage==3.6 From e70f3ddbc6049bf48aa2e74b1da53a8c7e8d18db Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Thu, 11 Apr 2013 19:27:14 -0700 Subject: [PATCH 3/8] Rename README file to README.rst --- README => README.rst | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename README => README.rst (100%) diff --git a/README b/README.rst similarity index 100% rename from README rename to README.rst From 0cfc819b7a8f7f49ab049a7bf5fd38ed8a56762a Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Thu, 11 Apr 2013 19:28:24 -0700 Subject: [PATCH 4/8] Add travis and coveralls badges to README --- README.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.rst b/README.rst index 3a12cfbf2..05bf1035c 100644 --- a/README.rst +++ b/README.rst @@ -1,3 +1,12 @@ +================== +django-simple-history +================== + +.. image:: https://secure.travis-ci.org/treyhunner/django-simple-history.png?branch=master + :target: http://travis-ci.org/treyhunner/django-simple-history +.. image:: https://coveralls.io/repos/treyhunner/django-simple-history/badge.png?branch=master + :target: https://coveralls.io/r/treyhunner/django-simple-history + django-simple-history is a tool to store state of DB objects on every create/update/delete. It has been tested to work in django 1.X (including 1.2.3 as of 10/25/2010). == Install == From bb012f4b9173f961555b568f2045a8d5d94da8e5 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Thu, 11 Apr 2013 19:34:30 -0700 Subject: [PATCH 5/8] Improve reStructuredText in README --- README.rst | 125 +++++++++++++++++++++++++++-------------------------- 1 file changed, 63 insertions(+), 62 deletions(-) diff --git a/README.rst b/README.rst index 05bf1035c..d45c679b3 100644 --- a/README.rst +++ b/README.rst @@ -9,78 +9,79 @@ django-simple-history django-simple-history is a tool to store state of DB objects on every create/update/delete. It has been tested to work in django 1.X (including 1.2.3 as of 10/25/2010). -== Install == +Install +------- Download the tar.gz, extract it and run the following inside the directory: - python setup.py install -== Basic usage == +.. code-block:: bash + + $ python setup.py install + +Basic usage +----------- Using this package is _really_ simple; you just have to import HistoricalRecords and create an instance of it on every model you want to historically track. On your models you need to include the following line at the top: - from simple_history.models import HistoricalRecords -Then in your model class, include the following line: - history = HistoricalRecords() - -Then from either the model class or from an instance, you can access history.all() which will give you either every history item of the class, or every history item of the specific instance. +.. code-block:: python -== Example == -class Poll(models.Model): - question = models.CharField(max_length = 200) - pub_date = models.DateTimeField('date published') + from simple_history.models import HistoricalRecords - history = HistoricalRecords() +Then in your model class, include the following line: -class Choice(models.Model): - poll = models.ForeignKey(Poll) - choice = models.CharField(max_length=200) - votes = models.IntegerField() +.. code-block:: python history = HistoricalRecords() - - -$ ./manage.py shell -In [2]: from poll.models import Poll, Choice - -In [3]: Poll.objects.all() -Out[3]: [] - -In [4]: import datetime - -In [5]: p = Poll(question="what's up?", pub_date=datetime.datetime.now()) -In [6]: p.save() - -In [7]: p -Out[7]: - -In [9]: p.history.all() -Out[9]: [] - -In [10]: p.pub_date = datetime.datetime(2007,4,1,0,0) - -In [11]: p.save() - -In [13]: p.history.all() -Out[13]: [, ] - -In [14]: p.choice_set.create(choice='Not Much', votes=0) -Out[14]: - -In [15]: p.choice_set.create(choice='The sky', votes=0) -Out[15]: - -In [16]: c = p.choice_set.create(choice='Just hacking again', votes=0) - -In [17]: c.poll -Out[17]: - -In [19]: c.history.all() -Out[19]: [] - -In [20]: Choice.history -Out[20]: - -In [21]: Choice.history.all() -Out[21]: [, , ] +Then from either the model class or from an instance, you can access history.all() which will give you either every history item of the class, or every history item of the specific instance. +Example +------- +Models: + +.. code-block:: python + + class Poll(models.Model): + question = models.CharField(max_length = 200) + pub_date = models.DateTimeField('date published') + + history = HistoricalRecords() + + class Choice(models.Model): + poll = models.ForeignKey(Poll) + choice = models.CharField(max_length=200) + votes = models.IntegerField() + + history = HistoricalRecords() + +Usage: + +.. code-block:: pycon + + >>> from poll.models import Poll, Choice + >>> Poll.objects.all() + [] + >>> import datetime + >>> p = Poll(question="what's up?", pub_date=datetime.datetime.now()) + >>> p.save() + >>> p + + >>> p.history.all() + [] + >>> p.pub_date = datetime.datetime(2007,4,1,0,0) + >>> p.save() + >>> p.history.all() + [, ] + >>> p.choice_set.create(choice='Not Much', votes=0) + + >>> p.choice_set.create(choice='The sky', votes=0) + + >>> c = p.choice_set.create(choice='Just hacking again', votes=0) + >>> c.poll + + >>> c.history.all() + [] + >>> Choice.history + + >>> Choice.history.all() + [, , ] From e513fed6c2bee92f41c143c3bcf92cf43f85dd4a Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Thu, 11 Apr 2013 19:39:24 -0700 Subject: [PATCH 6/8] Add gitignore and remove hgignore --- .gitignore | 8 ++++++++ .hgignore | 7 ------- 2 files changed, 8 insertions(+), 7 deletions(-) create mode 100644 .gitignore delete mode 100644 .hgignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..373f51d9e --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*.pyc +dist/ +*.egg-info/ +build/ +MANIFEST +.coverage +.tox/ +htmlcov/ diff --git a/.hgignore b/.hgignore deleted file mode 100644 index 3f9e56383..000000000 --- a/.hgignore +++ /dev/null @@ -1,7 +0,0 @@ -syntax: glob -*.pyc -dist -MANIFEST -simple_history.egg-info -simple_history/.project -simple_history/.pydevproject From 6cd2b8e62c8999135d93d2696515dceb30276e1c Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Thu, 11 Apr 2013 20:50:43 -0700 Subject: [PATCH 7/8] Fix Travis configuration file for Python 2.5 --- .travis.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 812d7af9a..d6a3b3bf3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: python python: - - "2.5" - "2.6" - "2.7" @@ -15,16 +14,18 @@ env: install: - pip install $DJANGO --use-mirrors - - pip install coverage coveralls --use-mirrors - sh -c "if [ '$SOUTH' = '1' ]; then pip install South==0.7.6; fi" + - sh -c "if [ '$COVERALLS' != '0' ]; then pip install coverage coveralls; fi" script: coverage run -a --branch --include="simple_history/*" --omit="simple_history/tests/*" setup.py test matrix: - exclude: + include: - python: 2.5 - env: DJANGO=Django==1.5.1 SOUTH=1 + env: DJANGO=Django==1.2.7 SOUTH=1 COVERALLS=0 - python: 2.5 - env: DJANGO=https://github.com/django/django/tarball/master SOUTH=1 + env: DJANGO=Django==1.3.7 SOUTH=1 COVERALLS=0 + - python: 2.5 + env: DJANGO=Django==1.4.5 SOUTH=1 COVERALLS=0 after_success: coveralls From 22b060ebc4194d18bedf3e234ba12ccd384e1b3c Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Thu, 11 Apr 2013 20:52:39 -0700 Subject: [PATCH 8/8] Add missing django.contrib.auth dependency --- runtests.py | 1 + 1 file changed, 1 insertion(+) diff --git a/runtests.py b/runtests.py index bfe6ad99a..fa0e5d555 100755 --- a/runtests.py +++ b/runtests.py @@ -12,6 +12,7 @@ settings.configure( INSTALLED_APPS=( 'django.contrib.contenttypes', + 'django.contrib.auth', 'simple_history', 'simple_history.tests' ),