diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..ec1f7e4 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,12 @@ +[run] +branch = True +omit = */migrations/__init__.py +source = eventkit + +[report] +exclude_lines = + def __repr__ + pragma: no cover + raise AssertionError + raise NotImplementedError +show_missing = True diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b11492e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# http://editorconfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{css,html,js,json}] +indent_style = tab diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..acd160f --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Only include files that are actually generated or required by the app. + +# For anything that is local to your environment, e.g. dotfiles created by your +# IDE, use your own `~/.gitignore_global` file. + +*.egg-info/ +*.py[co] +/.coverage +/.tox/ +/db.sqlite3 +/htmlcov/ +/venv/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..c9b2ce9 --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +# Readme + +Docs can be found in the [docs](docs/index.md) folder. + +## App Template + +This is a bare-bones skeleton app template, for use with the +`django-admin.py startapp` command. + +You will need `django 1.4+`, `git`, `python 2.7+`, `pip`, and `virtualenv` to +create a new app with this template. + +Create environment variables for the app and module name (e.g. `django-foo-bar` +and `foo_bar`), so we can use them in subsequent commands: + + $ export APP= + $ export MODULE= + +Create an app from the template: + + $ mkdir $APP + $ django-admin.py startapp -e md,yml -n .coveragerc \ + --template=https://github.com/ixc/ixc-app-template/archive/master.zip \ + $MODULE $APP + +Make the `manage.py` file executable, for convenience: + + $ cd $APP + $ chmod 755 manage.py + +Create a remote repository on [GitHub], then initialise a local repository and +push an initial commit: + + $ git init + $ git add -A + $ git commit -m 'Initial commit.' + $ git remote add origin git@github.com:ixc/$APP.git + $ git push + +Create a virtualenv and install the dependencies: + + $ virtualenv venv + $ source venv/bin/activate + (venv)$ pip install -r requirements.txt + +Run the tests: + + (venv)$ tox + +Now, write your app! You might want to start with: + + * Remove the `App Template` section (these instructions) from `README.md` + (this file). + * Add a `LICENSE` file (e.g. [MIT]). + * Update the `docs/index.md` file (e.g. the overview, installation and usage + sections). + * Read the [contributing] docs. + +[contributing]: docs/contributing.md +[GitHub]: https://github.com +[MIT]: http://choosealicense.com/licenses/mit/ diff --git a/docs/changelog.md b/docs/changelog.md new file mode 100644 index 0000000..482da74 --- /dev/null +++ b/docs/changelog.md @@ -0,0 +1,9 @@ +# Changelog + +## 0.1 (in development) + +Features: + + * Created app from [ixc-app-template] + +[ixc-app-template]: https://github.com/ixc/ixc-app-template/ diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 0000000..c15657c --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1,79 @@ +# Contributing + +Please follow these guidelines when making contributions to this app. + +## Getting Started + +Get the code and setup a virtualenv: + + $ git clone git@github.com:ixc/.git + $ cd + $ virtualenv venv + $ source venv/bin/activate + (venv)$ pip install -r requirements.txt + +Run the tests: + + # All environments, just the given environments, or just the virtualenv. + (venv)$ tox + (venv)$ tox -e django17-py27,django18-py27 + (venv)$ ./manage.py test + +Run the test project interactively: + + (venv)$ ./manage.py migrate + (venv)$ ./manage.py runserver + +## Git + +We are using the [Gitflow branching model]. Basically: + + * The `master` branch always contains production ready code, and each commit + represents a release to production. + * The `develop` branch serves as an integration branch for new features, and + is merged into `master` when we are ready to tag a new release. + * New features are developed in `feature/*` branches. Create a pull request + when you are ready to merge a feature branch back into `develop`. + +The [SourceTree] app (OS X and Windows) has built-in support for Gitflow, and +there is also a collection of [git-extensions] for command line users. + +## Code Style + +It's important that we all adopt a consistent code style to minimise code churn +and make collaboration easier. + + * Follow [PEP8] for Python code, unless there is a good reason not to. + * Install the [EditorConfig] plugin for your preferred code editor. + +## Documentation + +Docs are probably more important than tests! + + * Write [Markdown] docs for all notable changes and additions. + * Include examples so new contributors can get started quickly. + * Include rationale when there are competing solutions, so people know why + they should use our solution. + * Keep the [changelog] up to date. Use plain language to describe changes, + as it may be read by people who are not as familiar with the project or a + particular feature as you. + +## Tests + +We don't need 100% test coverage, but we should at least have: + + * Unit tests for all regression bugs. + * Unit or integration tests for complex, fragile, or important functionality. + +## Releases + + * When the changelog for a release gets sufficiently long (half a page to a + page) or major features or fixes are implemented, tag a release. + +[changelog]: changelog.md +[EditorConfig]: http://editorconfig.org/ +[git-extensions]: https://github.com/nvie/gitflow/ +[Gitflow branching model]: http://atlassian.com/git/workflows#!workflow-gitflow +[Markdown]: http://daringfireball.net/projects/markdown/ +[PEP8]: http://legacy.python.org/dev/peps/pep-0008/ +[SourceTree]: http://sourcetreeapp.com/ diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..96312ce --- /dev/null +++ b/docs/index.md @@ -0,0 +1,42 @@ +# Overview + +TODO + +## Table of Contents + + * [Changelog] + * [Contributing] + +## Installation + +Install the app into your virtualenv: + + (venv)$ pip install -e git+ssh://git@github.com/ixc/.git#egg= + +Update your settings module: + + INSTALLED_APPS += ('eventkit', ) + +## Usage + +TODO + +## HTML Docs + +Docs are written in [Markdown]. You can use [MkDocs] to build a static HTML +version that you can host anywhere: + + (venv)$ mkdocs build + +Or you can use the built-in dev server to preview your documentation as you're +writing it: + + (venv)$ mkdocs serve + +It will even auto-reload whenever you save any changes, so all you need to do +to see your latest edits is refresh your browser. + +[Changelog]: changelog.md +[Contributing]: contributing.md +[Markdown]: http://daringfireball.net/projects/markdown/ +[MkDocs]: http://mkdocs.org diff --git a/eventkit/__init__.py b/eventkit/__init__.py new file mode 100644 index 0000000..327beae --- /dev/null +++ b/eventkit/__init__.py @@ -0,0 +1,3 @@ +__version__ = '0.1' + +default_app_config = 'eventkit.apps.AppConfig' diff --git a/eventkit/admin.py b/eventkit/admin.py new file mode 100644 index 0000000..a6f5dfd --- /dev/null +++ b/eventkit/admin.py @@ -0,0 +1,10 @@ +""" +Admin configuration for ``eventkit`` app. +""" + +# Define `list_display`, `list_filter` and `search_fields` for each model. +# These go a long way to making the admin more usable. + +from django.contrib import admin + +from eventkit import models diff --git a/eventkit/apps.py b/eventkit/apps.py new file mode 100644 index 0000000..df5d734 --- /dev/null +++ b/eventkit/apps.py @@ -0,0 +1,12 @@ +""" +App configuration for ``eventkit`` app. +""" + +# Register signal handlers, but avoid interacting with the database. +# See: https://docs.djangoproject.com/en/1.8/ref/applications/#django.apps.AppConfig.ready + +from django.apps import AppConfig + + +class AppConfig(AppConfig): + name = 'eventkit' diff --git a/eventkit/forms.py b/eventkit/forms.py new file mode 100644 index 0000000..c026638 --- /dev/null +++ b/eventkit/forms.py @@ -0,0 +1,7 @@ +""" +Forms for ``eventkit`` app. +""" + +from django import forms + +from eventkit import models diff --git a/eventkit/migrations/__init__.py b/eventkit/migrations/__init__.py new file mode 100644 index 0000000..fd1fa0c --- /dev/null +++ b/eventkit/migrations/__init__.py @@ -0,0 +1,7 @@ +try: + from django.db import migrations +except ImportError: + from django.core.exceptions import ImproperlyConfigured + raise ImproperlyConfigured( + 'These migrations are for use with Django 1.7 and above. For Django ' + '1.6 and below, upgrade to South 1.0.') diff --git a/eventkit/models.py b/eventkit/models.py new file mode 100644 index 0000000..6582b1c --- /dev/null +++ b/eventkit/models.py @@ -0,0 +1,31 @@ +""" +Models for ``eventkit`` app. +""" + +# Compose concrete models from abstract models and mixins, to facilitate reuse. + +from django.db import models +from django.utils import timezone + + +class AbstractBaseModel(models.Model): + """ + Abstract base model. + """ + + created = models.DateTimeField( + default=timezone.now, db_index=True, editable=False) + modified = models.DateTimeField( + default=timezone.now, db_index=True, editable=False) + + class Meta: + abstract = True + get_latest_by = 'pk' + ordering = ('-id', ) + + def save(self, *args, **kwargs): + """ + Update ``self.modified``. + """ + self.modified = timezone.now() + super(AbstractBaseModel, self).save(*args, **kwargs) diff --git a/eventkit/templates/eventkit/index.html b/eventkit/templates/eventkit/index.html new file mode 100644 index 0000000..557db03 --- /dev/null +++ b/eventkit/templates/eventkit/index.html @@ -0,0 +1 @@ +Hello World diff --git a/eventkit/tests/__init__.py b/eventkit/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/eventkit/tests/models.py b/eventkit/tests/models.py new file mode 100644 index 0000000..5ae6c40 --- /dev/null +++ b/eventkit/tests/models.py @@ -0,0 +1,12 @@ +""" +Test models for ``eventkit`` app. +""" + +from eventkit import models + + +class BaseModel(models.AbstractBaseModel): + """ + Concrete base model. + """ + pass diff --git a/eventkit/tests/settings.py b/eventkit/tests/settings.py new file mode 100644 index 0000000..91d8488 --- /dev/null +++ b/eventkit/tests/settings.py @@ -0,0 +1,33 @@ +""" +Test settings for ``eventkit`` app. +""" + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': 'db.sqlite3', + } +} + +DEBUG = True +EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' + +INSTALLED_APPS = ( + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django_nose', + 'eventkit', + 'eventkit.tests' +) + +MIDDLEWARE_CLASSES = ( + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', +) + +ROOT_URLCONF = 'eventkit.urls' +SECRET_KEY = 'secret-key' +STATIC_URL = '/static/' +TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' diff --git a/eventkit/tests/tests.py b/eventkit/tests/tests.py new file mode 100644 index 0000000..808e07b --- /dev/null +++ b/eventkit/tests/tests.py @@ -0,0 +1,34 @@ +""" +Tests for ``eventkit`` app. +""" + +# WebTest API docs: http://webtest.readthedocs.org/en/latest/api.html + +from django.core.urlresolvers import reverse +from django_dynamic_fixture import G +from django_webtest import WebTest + +from eventkit import forms, models, views +from eventkit.tests import models as test_models + + +class Forms(WebTest): + def test(self): + pass + + +class Models(WebTest): + def test_BaseModel(self): + """ + Test that ``modified`` field is updated on save. + """ + obj = G(test_models.BaseModel) + modified = obj.modified + obj.save() + self.assertNotEqual(obj.modified, modified) + + +class Views(WebTest): + def test_index(self): + response = self.app.get(reverse('eventkit_index')) + response.mustcontain('Hello World') diff --git a/eventkit/urls.py b/eventkit/urls.py new file mode 100644 index 0000000..b099125 --- /dev/null +++ b/eventkit/urls.py @@ -0,0 +1,13 @@ +""" +URLconf for ``eventkit`` app. +""" + +# Prefix URL names with the app name. Avoid URL namespaces unless it is likely +# this app will be installed multiple times in a single project. + +from django.conf.urls import include, patterns, url + +urlpatterns = patterns( + 'eventkit.views', + url(r'^$', 'index', name='eventkit_index'), +) diff --git a/eventkit/views.py b/eventkit/views.py new file mode 100644 index 0000000..df37ef5 --- /dev/null +++ b/eventkit/views.py @@ -0,0 +1,16 @@ +""" +Views for ``eventkit`` app. +""" + +# Do not use generic class based views unless there is a really good reason to. +# Functional views are much easier to comprehend and maintain. + +from django.core.urlresolvers import reverse +from django.http import Http404, HttpResponseRedirect +from django.shortcuts import get_object_or_404 +from django.template.response import TemplateResponse + + +def index(request): + context = {} + return TemplateResponse(request, 'eventkit/index.html', context) diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..f12e932 --- /dev/null +++ b/manage.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +import coverage +import os +import sys + +if __name__ == "__main__": + + if 'test' in sys.argv: + cov = coverage.coverage() + cov.erase() + cov.start() + + os.environ.setdefault( + "DJANGO_SETTINGS_MODULE", "eventkit.tests.settings") + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) + + if 'test' in sys.argv: + cov.stop() + cov.save() + cov.report() + cov.html_report() diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..c30fe6f --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1 @@ +site_name: 'eventkit' diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..006dca8 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +# Forked, pre-release and private dependencies. +-e . +-e git+https://github.com/django-nose/django-nose.git#egg=django-nose diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..11b9d94 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[nosetests] +with-progressive = True diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..295df22 --- /dev/null +++ b/setup.py @@ -0,0 +1,19 @@ +import setuptools + +from eventkit import __version__ + +setuptools.setup( + name='eventkit', + version=__version__, + packages=setuptools.find_packages(), + install_requires=[ + 'coverage', + 'django-dynamic-fixture', + 'django-nose', + 'django-webtest', + 'mkdocs', + 'nose-progressive', + 'tox', + 'WebTest', + ], +) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..23986c3 --- /dev/null +++ b/tox.ini @@ -0,0 +1,20 @@ +[tox] +envlist = + django14-py{26,27}, + django{17,18}-py{27,32,33,34}, + +[testenv] +basepython = + py26: python2.6 + py27: python2.7 + py32: python3.2 + py33: python3.3 + py34: python3.4 + +deps = + -r{toxinidir}/requirements.txt + django14: django>=1.4,<1.5 + django17: django>=1.7,<1.8 + django18: django>=1.8,<1.9 + +commands = {toxinidir}/manage.py test