From 0f5851fd23089a5b24fc18b620d329bade3e377e Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Thu, 11 Apr 2013 19:18:13 -0700 Subject: [PATCH 1/6] Add test files --- runtests.py | 35 ++++++++++++++++ setup.py | 6 ++- simple_history/tests/__init__.py | 0 simple_history/tests/models.py | 17 ++++++++ simple_history/tests/tests.py | 69 ++++++++++++++++++++++++++++++++ 5 files changed, 125 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..fa0e5d555 --- /dev/null +++ b/runtests.py @@ -0,0 +1,35 @@ +#!/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', + 'django.contrib.auth', + '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 83e979a8aa515f6150f44db48a8633589a744f66 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Thu, 11 Apr 2013 19:18:49 -0700 Subject: [PATCH 2/6] Add travis and tox files --- .travis.yml | 31 +++++++++++++++++++++++++++++ runtests.sh | 4 ++++ tox.ini | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 92 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..67773c6f1 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,31 @@ +language: python + +python: + - "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 coverage $DJANGO --use-mirrors + - sh -c "if [ '$SOUTH' = '1' ]; then pip install South==0.7.6; fi" + - sh -c "if [ '$COVERALLS' != '0' ]; then pip install coveralls; fi" + +script: coverage run -a --branch --include="simple_history/*" --omit="simple_history/tests/*" setup.py test + +matrix: + include: + - python: 2.5 + env: DJANGO=Django==1.2.7 SOUTH=1 COVERALLS=0 + - python: 2.5 + 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 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 b96b0136bfd82ec1368707d2ab5b63eb35472a12 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Thu, 11 Apr 2013 19:27:14 -0700 Subject: [PATCH 3/6] 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 5c59062d1307891fd3828412009719ae3182a20f Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Thu, 11 Apr 2013 19:34:30 -0700 Subject: [PATCH 4/6] Improve reStructuredText in README --- README.rst | 125 +++++++++++++++++++++++++++-------------------------- 1 file changed, 63 insertions(+), 62 deletions(-) diff --git a/README.rst b/README.rst index 3a12cfbf2..5950c91aa 100644 --- a/README.rst +++ b/README.rst @@ -1,77 +1,78 @@ 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 e6799d052530ff8f725b0bafdfd653e06b8bfd8b Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Thu, 11 Apr 2013 19:28:24 -0700 Subject: [PATCH 5/6] Add travis and coveralls badges to README --- README.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.rst b/README.rst index 5950c91aa..d45c679b3 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 51def731e8708dab217afcfe58cdeaf39f95be01 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Thu, 11 Apr 2013 19:39:24 -0700 Subject: [PATCH 6/6] 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