Commits

Anonymous committed 9053479

Initial commit.

Comments (0)

Files changed (67)

+syntax: glob
+*.pyc
+*.log
+*.egg
+*.egg-info
+*.swp
+*.bak
+build
+_build
+dist
+.DS_Store
+=============================
+django-projector installation
+=============================
+
+Refer to file located at ``docs/installation.rst`` in order to get detailed
+information on how to get ``django-projector`` up and running.
+Copyright (c) 2009 Lukasz Balcerzak <lukasz.balcerzak@python-center.pl>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+* Neither the name of the python-center.pl nor the names of its contributors
+  may be used to endorse or promote products derived from this software without
+  specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+include README.rst 
+include INSTALL.rst
+include LICENSE
+include MANIFEST.in
+recursive-include docs *
+recursive-include projector/locale *
+recursive-include projector/media *
+recursive-include projector/templates *
+================
+django-projector
+================
+
+``django-projector`` is a project management application with task tracker
+and repository backend integration. Aimed to work with upcoming, version
+1.2 of Django_ framework. We are sick of Trac [1]_ and wanted to create
+simple application which can be easily customized or plugged into
+existing systems.
+
+This application is at early-development stage but we strongly encourage
+you to give it a try if you are looking for project management toolkit
+for your Django_ based projet.
+
+------------
+Installation
+------------
+
+See http://packages.python.org/django-projector/installation.html
+for information on installing ``django-projector``. It is also
+available in ``docs`` directory at ``docs/installation.rst``.
+
+-------------
+Documentation
+-------------
+
+Online documentation for development version is available at
+http://packages.python.org/django-projector/.
+
+You may also build documentation for yourself but you would need Sphinx_
+for that. Go into ``docs/`` and run::
+
+   make html
+
+-------
+License
+-------
+
+``django-projector`` is released under MIT license. You should get a copy
+of the license with source distribution, at the root location, within
+``LICENSE`` file.
+
+.. _Django: http://www.djangoproject.com/
+.. _Trac: http://trac.edgewall.org/
+.. _Sphinx: http://sphinx.pocoo.org/
+
+.. [1] Don't get us wrong, Trac_ is great tool but we believe that
+   django's pluggable applications are far easier to configure and
+   deploy.
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+BUILDDIR      = _build
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html      to make standalone HTML files"
+	@echo "  dirhtml   to make HTML files named index.html in directories"
+	@echo "  pickle    to make pickle files"
+	@echo "  json      to make JSON files"
+	@echo "  htmlhelp  to make HTML files and a HTML help project"
+	@echo "  qthelp    to make HTML files and a qthelp project"
+	@echo "  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  changes   to make an overview of all changed/added/deprecated items"
+	@echo "  linkcheck to check all external links for integrity"
+	@echo "  doctest   to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+	-rm -rf $(BUILDDIR)/*
+
+html:
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+pickle:
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+json:
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+	@echo
+	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
+	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-projector.qhcp"
+	@echo "To view the help file:"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-projector.qhc"
+
+latex:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+	@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
+	      "run these through (pdf)latex."
+
+changes:
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+	@echo
+	@echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+	@echo "Testing of doctests in the sources finished, look at the " \
+	      "results in $(BUILDDIR)/doctest/output.txt."

docs/_static/django-projector-01.png

Added
New image

docs/_theme/nature/static/nature.css_t

+/**
+ * Sphinx stylesheet -- default theme
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+ 
+@import url("basic.css");
+ 
+/* -- page layout ----------------------------------------------------------- */
+ 
+body {
+    font-family: Arial, sans-serif;
+    font-size: 100%;
+    background-color: #111;
+    color: #555;
+    margin: 0;
+    padding: 0;
+}
+
+div.documentwrapper {
+    float: left;
+    width: 100%;
+}
+
+div.bodywrapper {
+    margin: 0 0 0 230px;
+}
+
+hr{
+    border: 1px solid #B1B4B6;
+}
+ 
+div.document {
+    background-color: #eee;
+}
+ 
+div.body {
+    background-color: #ffffff;
+    color: #3E4349;
+    padding: 0 30px 30px 30px;
+    font-size: 0.8em;
+}
+ 
+div.footer {
+    color: #555;
+    width: 100%;
+    padding: 13px 0;
+    text-align: center;
+    font-size: 75%;
+}
+ 
+div.footer a {
+    color: #444;
+    text-decoration: underline;
+}
+ 
+div.related {
+    background-color: #6BA81E;
+    line-height: 32px;
+    color: #fff;
+    text-shadow: 0px 1px 0 #444;
+    font-size: 0.80em;
+}
+ 
+div.related a {
+    color: #E2F3CC;
+}
+ 
+div.sphinxsidebar {
+    font-size: 0.75em;
+    line-height: 1.5em;
+}
+
+div.sphinxsidebarwrapper{
+    padding: 20px 0;
+}
+ 
+div.sphinxsidebar h3,
+div.sphinxsidebar h4 {
+    font-family: Arial, sans-serif;
+    color: #222;
+    font-size: 1.2em;
+    font-weight: normal;
+    margin: 0;
+    padding: 5px 10px;
+    background-color: #ddd;
+    text-shadow: 1px 1px 0 white
+}
+
+div.sphinxsidebar h4{
+    font-size: 1.1em;
+}
+ 
+div.sphinxsidebar h3 a {
+    color: #444;
+}
+ 
+ 
+div.sphinxsidebar p {
+    color: #888;
+    padding: 5px 20px;
+}
+ 
+div.sphinxsidebar p.topless {
+}
+ 
+div.sphinxsidebar ul {
+    margin: 10px 20px;
+    padding: 0;
+    color: #000;
+}
+ 
+div.sphinxsidebar a {
+    color: #444;
+}
+ 
+div.sphinxsidebar input {
+    border: 1px solid #ccc;
+    font-family: sans-serif;
+    font-size: 1em;
+}
+
+div.sphinxsidebar input[type=text]{
+    margin-left: 20px;
+}
+ 
+/* -- body styles ----------------------------------------------------------- */
+ 
+a {
+    color: #005B81;
+    text-decoration: none;
+}
+ 
+a:hover {
+    color: #E32E00;
+    text-decoration: underline;
+}
+ 
+div.body h1,
+div.body h2,
+div.body h3,
+div.body h4,
+div.body h5,
+div.body h6 {
+    font-family: Arial, sans-serif;
+    background-color: #BED4EB;
+    font-weight: normal;
+    color: #212224;
+    margin: 30px 0px 10px 0px;
+    padding: 5px 0 5px 10px;
+    text-shadow: 0px 1px 0 white
+}
+ 
+div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; }
+div.body h2 { font-size: 150%; background-color: #C8D5E3; }
+div.body h3 { font-size: 120%; background-color: #D8DEE3; }
+div.body h4 { font-size: 110%; background-color: #D8DEE3; }
+div.body h5 { font-size: 100%; background-color: #D8DEE3; }
+div.body h6 { font-size: 100%; background-color: #D8DEE3; }
+ 
+a.headerlink {
+    color: #c60f0f;
+    font-size: 0.8em;
+    padding: 0 4px 0 4px;
+    text-decoration: none;
+}
+ 
+a.headerlink:hover {
+    background-color: #c60f0f;
+    color: white;
+}
+ 
+div.body p, div.body dd, div.body li {
+    line-height: 1.5em;
+}
+ 
+div.admonition p.admonition-title + p {
+    display: inline;
+}
+
+div.highlight{
+    background-color: white;
+}
+
+div.note {
+    background-color: #eee;
+    border: 1px solid #ccc;
+}
+ 
+div.seealso {
+    background-color: #ffc;
+    border: 1px solid #ff6;
+}
+ 
+div.topic {
+    background-color: #eee;
+}
+ 
+div.warning {
+    background-color: #ffe4e4;
+    border: 1px solid #f66;
+}
+ 
+p.admonition-title {
+    display: inline;
+}
+ 
+p.admonition-title:after {
+    content: ":";
+}
+ 
+pre {
+    padding: 10px;
+    background-color: White;
+    color: #222;
+    line-height: 1.2em;
+    border: 1px solid #C6C9CB;
+    font-size: 1.2em;
+    margin: 1.5em 0 1.5em 0;
+    -webkit-box-shadow: 1px 1px 1px #d8d8d8;
+    -moz-box-shadow: 1px 1px 1px #d8d8d8;
+}
+ 
+tt {
+    background-color: #ecf0f3;
+    color: #222;
+    padding: 1px 2px;
+    font-size: 1.2em;
+    font-family: monospace;
+}

docs/_theme/nature/static/pygments.css

+.c { color: #999988; font-style: italic } /* Comment */
+.k { font-weight: bold } /* Keyword */
+.o { font-weight: bold } /* Operator */
+.cm { color: #999988; font-style: italic } /* Comment.Multiline */
+.cp { color: #999999; font-weight: bold } /* Comment.preproc */
+.c1 { color: #999988; font-style: italic } /* Comment.Single */
+.gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
+.ge { font-style: italic } /* Generic.Emph */
+.gr { color: #aa0000 } /* Generic.Error */
+.gh { color: #999999 } /* Generic.Heading */
+.gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
+.go { color: #111 } /* Generic.Output */
+.gp { color: #555555 } /* Generic.Prompt */
+.gs { font-weight: bold } /* Generic.Strong */
+.gu { color: #aaaaaa } /* Generic.Subheading */
+.gt { color: #aa0000 } /* Generic.Traceback */
+.kc { font-weight: bold } /* Keyword.Constant */
+.kd { font-weight: bold } /* Keyword.Declaration */
+.kp { font-weight: bold } /* Keyword.Pseudo */
+.kr { font-weight: bold } /* Keyword.Reserved */
+.kt { color: #445588; font-weight: bold } /* Keyword.Type */
+.m { color: #009999 } /* Literal.Number */
+.s { color: #bb8844 } /* Literal.String */
+.na { color: #008080 } /* Name.Attribute */
+.nb { color: #999999 } /* Name.Builtin */
+.nc { color: #445588; font-weight: bold } /* Name.Class */
+.no { color: #ff99ff } /* Name.Constant */
+.ni { color: #800080 } /* Name.Entity */
+.ne { color: #990000; font-weight: bold } /* Name.Exception */
+.nf { color: #990000; font-weight: bold } /* Name.Function */
+.nn { color: #555555 } /* Name.Namespace */
+.nt { color: #000080 } /* Name.Tag */
+.nv { color: purple } /* Name.Variable */
+.ow { font-weight: bold } /* Operator.Word */
+.mf { color: #009999 } /* Literal.Number.Float */
+.mh { color: #009999 } /* Literal.Number.Hex */
+.mi { color: #009999 } /* Literal.Number.Integer */
+.mo { color: #009999 } /* Literal.Number.Oct */
+.sb { color: #bb8844 } /* Literal.String.Backtick */
+.sc { color: #bb8844 } /* Literal.String.Char */
+.sd { color: #bb8844 } /* Literal.String.Doc */
+.s2 { color: #bb8844 } /* Literal.String.Double */
+.se { color: #bb8844 } /* Literal.String.Escape */
+.sh { color: #bb8844 } /* Literal.String.Heredoc */
+.si { color: #bb8844 } /* Literal.String.Interpol */
+.sx { color: #bb8844 } /* Literal.String.Other */
+.sr { color: #808000 } /* Literal.String.Regex */
+.s1 { color: #bb8844 } /* Literal.String.Single */
+.ss { color: #bb8844 } /* Literal.String.Symbol */
+.bp { color: #999999 } /* Name.Builtin.Pseudo */
+.vc { color: #ff99ff } /* Name.Variable.Class */
+.vg { color: #ff99ff } /* Name.Variable.Global */
+.vi { color: #ff99ff } /* Name.Variable.Instance */
+.il { color: #009999 } /* Literal.Number.Integer.Long */

docs/_theme/nature/theme.conf

+[theme]
+inherit = basic
+stylesheet = nature.css
+pygments_style = tango

docs/authorization.rst

+.. _authorization:
+
+Authorization and permissions
+=============================
+
+This project management system aims to be used by small to middle companies.
+
+Object-level permissions
+------------------------
+
+``django-projector`` make use of `django-authority`_ to handle object-level
+permissions. Check out it's `documentation
+<http://packages.python.org/django-authority/>`_ to see detailed information
+on the topic.
+
+Project permissions
+-------------------
+
+Following permissions are defined for each project:
+
+- ``view``
+- ``add_member_to``
+- ``delete_member_from``
+- ``view_tasks_for``
+- ``can_add_task_to``
+- ``can_change_task_to``
+- ``can_read_repository``
+- ``can_write_repository``
+
+.. note::
+   ``django-authority`` adds suffix ``_project`` for each of this permissions.
+
+.. _django-authority: http://bitbucket.org/jezdez/django-authority/
+# -*- coding: utf-8 -*-
+#
+# django-projector documentation build configuration file, created by
+# sphinx-quickstart on Thu Feb 18 23:18:28 2010.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+sys.path.append(os.path.abspath('..'))
+os.environ['DJANGO_SETTINGS_MODULE'] = 'django.conf.global_settings'
+
+# -- General configuration -----------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'django-projector'
+copyright = u'2010, Lukasz Balcerzak'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '0.1'
+# The full version, including alpha/beta/rc tags.
+release = '0.1.1.beta'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+#unused_docs = []
+
+# List of directories, relative to source directory, that shouldn't be searched
+# for source files.
+exclude_trees = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  Major themes that come with
+# Sphinx are currently 'default' and 'sphinxdoc'.
+html_theme = 'nature'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+html_theme_path = ['_theme']
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_use_modindex = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = ''
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'django-projectordoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+  ('index', 'django-projector.tex', u'django-projector Documentation',
+   u'Lukasz Balcerzak', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_use_modindex = True

docs/configuration.rst

+.. _configuration:
+
+Configuration
+=============
+
+After you hook ``django-projector`` into your project [1]_
+you should probably change some of configuration variables. All of them have
+default values but some functionality is turned off at the beginning (for
+instance repository backend [2]_).
+
+
+.. [1] See :ref:`installation`
+.. [2] See :ref:`repository_backend`
+.. django-projector documentation master file, created by
+   sphinx-quickstart on Thu Feb 18 23:18:28 2010.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+Welcome to django-projector's documentation!
+============================================
+
+``django-projector`` project management application with task tracker
+and repository backend integration. Aimed to work with upcoming, version
+1.2 of Django_ framework. We are sick of Trac_ and wanted to create
+simple application which can be easily customized or plugged into
+existing systems.
+
+.. image:: _static/django-projector-01.png
+   :width: 760px
+
+Features
+--------
+
+- Task tracker with full history of changes
+- Mercurial_ repository integration
+- Repository web browser (basic... really)
+- Granual permissions management (see :ref:`authorization`)
+- Documents based on `restructuredText`_
+- Make use of `django-richtemplates`_ so templates are ready to use
+  out of the box
+- More to come
+
+Incoming
+--------
+
+Here are some additional points which are the target for future
+release.
+
+- Wiki per project
+- Other version control systems in backend (git_, subversion_...)
+- Customizable workflow for each project
+- Charts, statistics, graphs, plots, analyzies
+- Functional roadmap/timeline
+- Email notification
+
+.. warning::
+   This application is at early-development stage but we strongly encourage
+   you to give it a try if you are looking for project management toolkit
+   for your Django_ based project. Still, it probably should **NOT** be used
+   in production as it wasn't fully tested and may contain security issues.
+
+Documentation
+=============
+
+**Installation:**
+
+.. toctree::
+   :maxdepth: 1
+
+   installation
+   configuration
+   authorization
+
+Other topics
+============
+
+* :ref:`genindex`
+* :ref:`search`
+
+.. _django: http://www.djangoproject.com/
+.. _django-richtemplates: http://bitbucket.org/lukaszb/django-richtemplates/
+.. _restructuredText: http://docutils.sourceforge.net/rst.html
+.. _mercurial: http://mercurial.selenic.com/
+.. _subversion: http://subversion.tigris.org/
+.. _git: http://git-scm.com/
+.. _trac: http://trac.edgewall.org/

docs/installation.rst

+.. _installation:
+
+Installation
+============
+
+``django-projector`` is aimed to work with Django 1.2 which is going to be
+released in march 2010. Till then you would need to use alpha/beta/trunk of
+Django. Moreover, I strongly suggest to use ``virtualenv`` and
+``virtualenvwrapper`` - great tools for creating temporary environment to work
+on.
+
+.. note::
+    Well, in fact, as the time of writing only ``messages`` application is
+    used from new `Django`_ version. And to
+    use this new application you can simply copy ``django/contrib/messages``
+    folder into working installation of Django on your system.
+
+Requirements
+------------
+
+Following packages are needed to get ``django-projector`` working:
+
+- `Django`_ >= 1.2
+- `django-authority`_
+- `django-annoying`_
+- `django-attachments`_
+- `django-extensions`_
+- `django-pagination`_
+- `django-richtemplates`_
+- `django-sorting`_
+- `django-tagging`_
+- `docutils`_
+- `Pygments`_
+- `Mercurial`_
+
+
+.. [1] `pip <http://pip.openplans.org/>`_ is tool similar to `easy_install
+    <http://pypi.python.org/pypi/setuptools>`_ with some more power (like smooth
+    integration with `virtualenv <http://virtualenv.openplans.org/>`_, ``freeze``
+    command, package uninstallation and others.
+
+.. _django: http://www.djangoproject.com
+.. _django-authority: http://bitbucket.org/jezdez/django-authority/
+.. _django-annoying: http://bitbucket.org/offline/django-annoying/
+.. _django-attachments: http://github.com/bartTC/django-attachments 
+.. _django-extensions: http://code.google.com/p/django-command-extensions/
+.. _django-pagination: http://code.google.com/p/django-pagination/
+.. _django-richtemplates: http://bitbucket.org/lukaszb/richtemplates/
+.. _django-sorting: http://github.com/directeur/django-sorting
+.. _django-tagging: http://code.google.com/p/django-tagging/
+.. _docutils: http://docutils.sourceforge.net/
+.. _pygments: http://pygments.org/
+.. _mercurial: http://mercurial.selenic.com/
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+set SPHINXBUILD=sphinx-build
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+	set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+	:help
+	echo.Please use `make ^<target^>` where ^<target^> is one of
+	echo.  html      to make standalone HTML files
+	echo.  dirhtml   to make HTML files named index.html in directories
+	echo.  pickle    to make pickle files
+	echo.  json      to make JSON files
+	echo.  htmlhelp  to make HTML files and a HTML help project
+	echo.  qthelp    to make HTML files and a qthelp project
+	echo.  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+	echo.  changes   to make an overview over all changed/added/deprecated items
+	echo.  linkcheck to check all external links for integrity
+	echo.  doctest   to run all doctests embedded in the documentation if enabled
+	goto end
+)
+
+if "%1" == "clean" (
+	for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+	del /q /s %BUILDDIR%\*
+	goto end
+)
+
+if "%1" == "html" (
+	%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+	echo.
+	echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+	goto end
+)
+
+if "%1" == "dirhtml" (
+	%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+	echo.
+	echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+	goto end
+)
+
+if "%1" == "pickle" (
+	%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+	echo.
+	echo.Build finished; now you can process the pickle files.
+	goto end
+)
+
+if "%1" == "json" (
+	%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+	echo.
+	echo.Build finished; now you can process the JSON files.
+	goto end
+)
+
+if "%1" == "htmlhelp" (
+	%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+	echo.
+	echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+	goto end
+)
+
+if "%1" == "qthelp" (
+	%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+	echo.
+	echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+	echo.^> qcollectiongenerator %BUILDDIR%\qthelp\django-projector.qhcp
+	echo.To view the help file:
+	echo.^> assistant -collectionFile %BUILDDIR%\qthelp\django-projector.ghc
+	goto end
+)
+
+if "%1" == "latex" (
+	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+	echo.
+	echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+	goto end
+)
+
+if "%1" == "changes" (
+	%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+	echo.
+	echo.The overview file is in %BUILDDIR%/changes.
+	goto end
+)
+
+if "%1" == "linkcheck" (
+	%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+	echo.
+	echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+	goto end
+)
+
+if "%1" == "doctest" (
+	%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+	echo.
+	echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+	goto end
+)
+
+:end

docs/repository_backend.rst

+.. _repository_backend:
+
+Repository backends
+===================
+
+.. note::
+   TODO: write about version control systems here.
+#!python
+"""Bootstrap setuptools installation
+
+If you want to use setuptools in your package's setup.py, just include this
+file in the same directory with it, and add this to the top of your setup.py::
+
+    from ez_setup import use_setuptools
+    use_setuptools()
+
+If you want to require a specific version of setuptools, set a download
+mirror, or use an alternate download directory, you can do so by supplying
+the appropriate options to ``use_setuptools()``.
+
+This file can also be run as a script to install or upgrade setuptools.
+"""
+import sys
+DEFAULT_VERSION = "0.6c11"
+DEFAULT_URL     = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
+
+md5_data = {
+    'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
+    'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
+    'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
+    'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
+    'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
+    'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
+    'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
+    'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
+    'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
+    'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
+    'setuptools-0.6c10-py2.3.egg': 'ce1e2ab5d3a0256456d9fc13800a7090',
+    'setuptools-0.6c10-py2.4.egg': '57d6d9d6e9b80772c59a53a8433a5dd4',
+    'setuptools-0.6c10-py2.5.egg': 'de46ac8b1c97c895572e5e8596aeb8c7',
+    'setuptools-0.6c10-py2.6.egg': '58ea40aef06da02ce641495523a0b7f5',
+    'setuptools-0.6c11-py2.3.egg': '2baeac6e13d414a9d28e7ba5b5a596de',
+    'setuptools-0.6c11-py2.4.egg': 'bd639f9b0eac4c42497034dec2ec0c2b',
+    'setuptools-0.6c11-py2.5.egg': '64c94f3bf7a72a13ec83e0b24f2749b2',
+    'setuptools-0.6c11-py2.6.egg': 'bfa92100bd772d5a213eedd356d64086',
+    'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
+    'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
+    'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
+    'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
+    'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
+    'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
+    'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
+    'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
+    'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
+    'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
+    'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
+    'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
+    'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
+    'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
+    'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
+    'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
+    'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
+    'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
+    'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
+    'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
+    'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03',
+    'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a',
+    'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6',
+    'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a',
+}
+
+import sys, os
+try: from hashlib import md5
+except ImportError: from md5 import md5
+
+def _validate_md5(egg_name, data):
+    if egg_name in md5_data:
+        digest = md5(data).hexdigest()
+        if digest != md5_data[egg_name]:
+            print >>sys.stderr, (
+                "md5 validation of %s failed!  (Possible download problem?)"
+                % egg_name
+            )
+            sys.exit(2)
+    return data
+
+def use_setuptools(
+    version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+    download_delay=15
+):
+    """Automatically find/download setuptools and make it available on sys.path
+
+    `version` should be a valid setuptools version number that is available
+    as an egg for download under the `download_base` URL (which should end with
+    a '/').  `to_dir` is the directory where setuptools will be downloaded, if
+    it is not already available.  If `download_delay` is specified, it should
+    be the number of seconds that will be paused before initiating a download,
+    should one be required.  If an older version of setuptools is installed,
+    this routine will print a message to ``sys.stderr`` and raise SystemExit in
+    an attempt to abort the calling script.
+    """
+    was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
+    def do_download():
+        egg = download_setuptools(version, download_base, to_dir, download_delay)
+        sys.path.insert(0, egg)
+        import setuptools; setuptools.bootstrap_install_from = egg
+    try:
+        import pkg_resources
+    except ImportError:
+        return do_download()       
+    try:
+        pkg_resources.require("setuptools>="+version); return
+    except pkg_resources.VersionConflict, e:
+        if was_imported:
+            print >>sys.stderr, (
+            "The required version of setuptools (>=%s) is not available, and\n"
+            "can't be installed while this script is running. Please install\n"
+            " a more recent version first, using 'easy_install -U setuptools'."
+            "\n\n(Currently using %r)"
+            ) % (version, e.args[0])
+            sys.exit(2)
+        else:
+            del pkg_resources, sys.modules['pkg_resources']    # reload ok
+            return do_download()
+    except pkg_resources.DistributionNotFound:
+        return do_download()
+
+def download_setuptools(
+    version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+    delay = 15
+):
+    """Download setuptools from a specified location and return its filename
+
+    `version` should be a valid setuptools version number that is available
+    as an egg for download under the `download_base` URL (which should end
+    with a '/'). `to_dir` is the directory where the egg will be downloaded.
+    `delay` is the number of seconds to pause before an actual download attempt.
+    """
+    import urllib2, shutil
+    egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
+    url = download_base + egg_name
+    saveto = os.path.join(to_dir, egg_name)
+    src = dst = None
+    if not os.path.exists(saveto):  # Avoid repeated downloads
+        try:
+            from distutils import log
+            if delay:
+                log.warn("""
+---------------------------------------------------------------------------
+This script requires setuptools version %s to run (even to display
+help).  I will attempt to download it for you (from
+%s), but
+you may need to enable firewall access for this script first.
+I will start the download in %d seconds.
+
+(Note: if this machine does not have network access, please obtain the file
+
+   %s
+
+and place it in this directory before rerunning this script.)
+---------------------------------------------------------------------------""",
+                    version, download_base, delay, url
+                ); from time import sleep; sleep(delay)
+            log.warn("Downloading %s", url)
+            src = urllib2.urlopen(url)
+            # Read/write all in one block, so we don't create a corrupt file
+            # if the download is interrupted.
+            data = _validate_md5(egg_name, src.read())
+            dst = open(saveto,"wb"); dst.write(data)
+        finally:
+            if src: src.close()
+            if dst: dst.close()
+    return os.path.realpath(saveto)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+def main(argv, version=DEFAULT_VERSION):
+    """Install or upgrade setuptools and EasyInstall"""
+    try:
+        import setuptools
+    except ImportError:
+        egg = None
+        try:
+            egg = download_setuptools(version, delay=0)
+            sys.path.insert(0,egg)
+            from setuptools.command.easy_install import main
+            return main(list(argv)+[egg])   # we're done here
+        finally:
+            if egg and os.path.exists(egg):
+                os.unlink(egg)
+    else:
+        if setuptools.__version__ == '0.0.1':
+            print >>sys.stderr, (
+            "You have an obsolete version of setuptools installed.  Please\n"
+            "remove it from your system entirely before rerunning this script."
+            )
+            sys.exit(2)
+
+    req = "setuptools>="+version
+    import pkg_resources
+    try:
+        pkg_resources.require(req)
+    except pkg_resources.VersionConflict:
+        try:
+            from setuptools.command.easy_install import main
+        except ImportError:
+            from easy_install import main
+        main(list(argv)+[download_setuptools(delay=0)])
+        sys.exit(0) # try to force an exit
+    else:
+        if argv:
+            from setuptools.command.easy_install import main
+            main(argv)
+        else:
+            print "Setuptools version",version,"or greater has been installed."
+            print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
+
+def update_md5(filenames):
+    """Update our built-in md5 registry"""
+
+    import re
+
+    for name in filenames:
+        base = os.path.basename(name)
+        f = open(name,'rb')
+        md5_data[base] = md5(f.read()).hexdigest()
+        f.close()
+
+    data = ["    %r: %r,\n" % it for it in md5_data.items()]
+    data.sort()
+    repl = "".join(data)
+
+    import inspect
+    srcfile = inspect.getsourcefile(sys.modules[__name__])
+    f = open(srcfile, 'rb'); src = f.read(); f.close()
+
+    match = re.search("\nmd5_data = {\n([^}]+)}", src)
+    if not match:
+        print >>sys.stderr, "Internal error!"
+        sys.exit(2)
+
+    src = src[:match.start(1)] + repl + src[match.end(1):]
+    f = open(srcfile,'w')
+    f.write(src)
+    f.close()
+
+
+if __name__=='__main__':
+    if len(sys.argv)>2 and sys.argv[1]=='--md5update':
+        update_md5(sys.argv[2:])
+    else:
+        main(sys.argv[1:])
+
+
+
+
+
+

projector/__init__.py

+"""
+Project management Django application with task
+tracker and repository backend integration.
+"""
+
+VERSION = (0, 1, 3, 'beta')
+
+__version__ = '.'.join((str(each) for each in VERSION[:4]))
+

projector/admin.py

+import logging
+
+from django.contrib import admin
+from django.utils.translation import ugettext as _
+
+from projector.models import Status, Task, Priority,\
+    Project, ProjectCategory, TaskType, Milestone, ProjectComponent,\
+    TaskRevision
+from projector.models import Membership
+
+from richtemplates.forms import LimitingModelForm
+from attachments.admin import AttachmentInlines
+
+from django_extensions.admin import ForeignKeyAutocompleteAdmin
+
+def author_editor_save_model(self, request, obj, form, change):
+    """
+    Overrides standard admin.ModelAdmin save_model method.
+    It sets editor (and his/her IP) based on data from requet.
+    If model is new (it's primary key is not yet set) sets
+    author/author_ip fields too.
+    """
+
+    logging.debug("%s.save_model method called!" % self.__class__.__name__)
+    
+    if getattr(obj, 'pk', None) is None:
+        # Its a new instance so we need to
+        # set author and author_ip fields
+        obj.author = request.user
+        obj.author_ip = request.META['REMOTE_ADDR']
+    obj.editor = request.user
+    obj.editor_ip = request.META['REMOTE_ADDR']
+
+    return super(self.__class__, self).save_model(request, obj, form, change)
+
+class StatusAdmin(admin.ModelAdmin):
+    list_display = ( 'id', 'name', 'order', 'is_resolved', )
+    list_display_links = ('id',)
+    list_editable = ( 'name', 'order', 'is_resolved', )
+
+class TaskAdminForm(LimitingModelForm):
+    class Meta:
+        choices_limiting_fields = ['project']
+        model = Task
+
+class TaskAdmin(admin.ModelAdmin):
+    list_display = ( 'pk', 'project', 'component', 'id', 'summary',
+        'created_at', 'author', 'status', 'priority', 'type')
+    list_display_links = ('summary',)
+    list_filter = ('project', 'type', 'priority', 'status')
+    date_hierarchy = 'created_at'
+    save_on_top = True
+    search_fields = ['summary', 'description']
+    inlines = [AttachmentInlines]
+    form = TaskAdminForm
+
+    # Overrides save_model *method*
+    '''
+    def save_model(self, request, obj, form, change):
+        """
+        Overrides standard admin.ModelAdmin save_model method.
+        It sets editor (and his/her IP) based on data from request.
+        If model is new (it's primary key is not yet set) sets
+        author/author_ip fields too.
+        """
+
+        logging.debug("%s.save_model method called!" % self.__class__.__name__)
+        
+        if getattr(obj, 'pk', None) is None:
+            # Its a new instance so we need to
+            # set author and author_ip fields
+            obj.author = request.user
+            obj.author_ip = request.META['REMOTE_ADDR']
+        obj.editor = request.user
+        obj.editor_ip = request.META['REMOTE_ADDR']
+        #obj.save()
+        return super(TaskAdmin, self).save_model(request, obj, form, change)
+    '''
+
+    fieldsets = (
+        (_("Task details"), {
+            'fields': (
+                'summary',
+                ('project', 'component'),
+                'description',
+                'status',
+                'priority',
+                'type',
+                'owner',
+                'milestone',
+                'deadline',
+            )
+        }),
+        (_("Author & editor"), {
+            'classes': ['collapsed collapse-toggle'],
+            'fields': (
+                ('author', 'author_ip'),
+                ('editor', 'editor_ip'),
+            ),
+            'description': _("""This are readonly fields. In Django 1.2
+                they will be probably really uneditable. For now, they are
+                here but even if you change them, they will be overriden
+                at the time of pushing data into database.""")
+        }),
+    )
+
+    # This option would be used from Django 1.2
+    readonly_fields = ('author_ip', 'editor_ip')
+
+    class Media:
+        css = {
+            'all': ['projector/css/monoarea.css'],
+        }
+
+class TaskAutocompleteAdmin(TaskAdmin, ForeignKeyAutocompleteAdmin):
+    related_search_fields = {
+        'owner': ('username',),
+    }
+
+class TaskRevisionAdmin(admin.ModelAdmin):
+    list_display = ( 'task', 'revision',
+        'created_at', 'author', 'status', 'priority', 'type')
+    #list_display_links = ('summary',)
+    #list_filter = ('project', 'type', 'priority', 'status')
+    date_hierarchy = 'created_at'
+    save_on_top = True
+    #search_fields = ['summary', 'description']
+
+class OrderedDictModelAdmin(admin.ModelAdmin):
+    list_display = ( 'id', 'name', 'order', 'description' )
+    list_display_links = ( 'id', 'name' )
+    list_editable = ( 'order', )
+
+class MilestoneInline(admin.StackedInline):
+    model = Milestone
+    extra = 1
+
+class TaskTypeInline(admin.StackedInline):
+    model = TaskType
+    extra = 1
+
+class ProjectComponentInline(admin.StackedInline):
+    model = ProjectComponent 
+    extra = 1
+
+class ProjectAdmin(admin.ModelAdmin):
+    list_display = ('id', 'name', 'category', 'home_page_url', 'active',
+        'public', 'author', 'created_at', 'outdated')
+    list_display_links = ('name',)
+    list_filter = ('category', 'active', 'public', 'outdated')
+    save_on_top = True
+    search_fields = ['name', 'description']    
+    prepopulated_fields = {"slug": ("name",)}
+
+    inlines = [MilestoneInline, ProjectComponentInline, TaskTypeInline]
+
+class ProjectComponentAdmin(admin.ModelAdmin):
+    list_display = 'project', 'name', 'description'
+    list_display_links = 'name',
+    list_filter = 'project',
+    search_fields = ['project', 'name']
+
+
+class TaskTypeAdmin(admin.ModelAdmin):
+    list_display = ['id', 'project', 'name', 'order']
+    list_display_links = ['name']
+    list_filter = ['project']
+    search_field = ['name', 'project']
+    
+
+#admin.site.register(Status, StatusAdmin)
+#admin.site.register(Priority, OrderedDictModelAdmin)
+admin.site.register(TaskType, TaskTypeAdmin)
+admin.site.register(Task, TaskAutocompleteAdmin)
+admin.site.register(TaskRevision, TaskRevisionAdmin)
+admin.site.register(Project, ProjectAdmin)
+admin.site.register(ProjectCategory)
+admin.site.register(Membership)
+#admin.site.register(ProjectComponent)
+#admin.site.register(Milestone) # Should be maintain with project itself
+

projector/exceptions.py

+class ProjectorError(Exception):
+    pass
+

projector/feeds.py

+from django.contrib.syndication.feeds import Feed
+
+from projector.models import Project
+
+class LatestProjectsFeed(Feed):
+    title = "Latest projects"
+    link = "/projects/"
+    description = "Updates on additions to projects."
+
+    def items(self):
+        return Project.objects\
+            .order_by('-created_at')\
+            .filter(public=True)\
+            [:5] # Limiting query
+

projector/fixtures/example_data.xml

+<?xml version="1.0" encoding="utf-8"?>
+<django-objects version="1.0">
+    <object pk="25" model="auth.permission">
+        <field type="CharField" name="name">Can add log entry</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">9</field>
+        <field type="CharField" name="codename">add_logentry</field>
+    </object>
+    <object pk="26" model="auth.permission">
+        <field type="CharField" name="name">Can change log entry</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">9</field>
+        <field type="CharField" name="codename">change_logentry</field>
+    </object>
+    <object pk="27" model="auth.permission">
+        <field type="CharField" name="name">Can delete log entry</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">9</field>
+        <field type="CharField" name="codename">delete_logentry</field>
+    </object>
+    <object pk="35" model="auth.permission">
+        <field type="CharField" name="name">Can add attachment</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">12</field>
+        <field type="CharField" name="codename">add_attachment</field>
+    </object>
+    <object pk="36" model="auth.permission">
+        <field type="CharField" name="name">Can change attachment</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">12</field>
+        <field type="CharField" name="codename">change_attachment</field>
+    </object>
+    <object pk="37" model="auth.permission">
+        <field type="CharField" name="name">Can delete attachment</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">12</field>
+        <field type="CharField" name="codename">delete_attachment</field>
+    </object>
+    <object pk="38" model="auth.permission">
+        <field type="CharField" name="name">Can delete foreign attachments</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">12</field>
+        <field type="CharField" name="codename">delete_foreign_attachments</field>
+    </object>
+    <object pk="4" model="auth.permission">
+        <field type="CharField" name="name">Can add group</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">2</field>
+        <field type="CharField" name="codename">add_group</field>
+    </object>
+    <object pk="5" model="auth.permission">
+        <field type="CharField" name="name">Can change group</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">2</field>
+        <field type="CharField" name="codename">change_group</field>
+    </object>
+    <object pk="6" model="auth.permission">
+        <field type="CharField" name="name">Can delete group</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">2</field>
+        <field type="CharField" name="codename">delete_group</field>
+    </object>
+    <object pk="10" model="auth.permission">
+        <field type="CharField" name="name">Can add message</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">4</field>
+        <field type="CharField" name="codename">add_message</field>
+    </object>
+    <object pk="11" model="auth.permission">
+        <field type="CharField" name="name">Can change message</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">4</field>
+        <field type="CharField" name="codename">change_message</field>
+    </object>
+    <object pk="12" model="auth.permission">
+        <field type="CharField" name="name">Can delete message</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">4</field>
+        <field type="CharField" name="codename">delete_message</field>
+    </object>
+    <object pk="1" model="auth.permission">
+        <field type="CharField" name="name">Can add permission</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">1</field>
+        <field type="CharField" name="codename">add_permission</field>
+    </object>
+    <object pk="2" model="auth.permission">
+        <field type="CharField" name="name">Can change permission</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">1</field>
+        <field type="CharField" name="codename">change_permission</field>
+    </object>
+    <object pk="3" model="auth.permission">
+        <field type="CharField" name="name">Can delete permission</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">1</field>
+        <field type="CharField" name="codename">delete_permission</field>
+    </object>
+    <object pk="7" model="auth.permission">
+        <field type="CharField" name="name">Can add user</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">3</field>
+        <field type="CharField" name="codename">add_user</field>
+    </object>
+    <object pk="8" model="auth.permission">
+        <field type="CharField" name="name">Can change user</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">3</field>
+        <field type="CharField" name="codename">change_user</field>
+    </object>
+    <object pk="9" model="auth.permission">
+        <field type="CharField" name="name">Can delete user</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">3</field>
+        <field type="CharField" name="codename">delete_user</field>
+    </object>
+    <object pk="13" model="auth.permission">
+        <field type="CharField" name="name">Can add user profile</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">5</field>
+        <field type="CharField" name="codename">add_userprofile</field>
+    </object>
+    <object pk="14" model="auth.permission">
+        <field type="CharField" name="name">Can change user profile</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">5</field>
+        <field type="CharField" name="codename">change_userprofile</field>
+    </object>
+    <object pk="15" model="auth.permission">
+        <field type="CharField" name="name">Can delete user profile</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">5</field>
+        <field type="CharField" name="codename">delete_userprofile</field>
+    </object>
+    <object pk="39" model="auth.permission">
+        <field type="CharField" name="name">Can add permission</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">13</field>
+        <field type="CharField" name="codename">add_permission</field>
+    </object>
+    <object pk="44" model="auth.permission">
+        <field type="CharField" name="name">Can approve permission requests</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">13</field>
+        <field type="CharField" name="codename">approve_permission_requests</field>
+    </object>
+    <object pk="42" model="auth.permission">
+        <field type="CharField" name="name">Can change foreign permissions</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">13</field>
+        <field type="CharField" name="codename">change_foreign_permissions</field>
+    </object>
+    <object pk="40" model="auth.permission">
+        <field type="CharField" name="name">Can change permission</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">13</field>
+        <field type="CharField" name="codename">change_permission</field>
+    </object>
+    <object pk="43" model="auth.permission">
+        <field type="CharField" name="name">Can delete foreign permissions</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">13</field>
+        <field type="CharField" name="codename">delete_foreign_permissions</field>
+    </object>
+    <object pk="41" model="auth.permission">
+        <field type="CharField" name="name">Can delete permission</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">13</field>
+        <field type="CharField" name="codename">delete_permission</field>
+    </object>
+    <object pk="28" model="auth.permission">
+        <field type="CharField" name="name">Can add comment</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">10</field>
+        <field type="CharField" name="codename">add_comment</field>
+    </object>
+    <object pk="31" model="auth.permission">
+        <field type="CharField" name="name">Can moderate comments</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">10</field>
+        <field type="CharField" name="codename">can_moderate</field>
+    </object>
+    <object pk="29" model="auth.permission">
+        <field type="CharField" name="name">Can change comment</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">10</field>
+        <field type="CharField" name="codename">change_comment</field>
+    </object>
+    <object pk="30" model="auth.permission">
+        <field type="CharField" name="name">Can delete comment</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">10</field>
+        <field type="CharField" name="codename">delete_comment</field>
+    </object>
+    <object pk="32" model="auth.permission">
+        <field type="CharField" name="name">Can add comment flag</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">11</field>
+        <field type="CharField" name="codename">add_commentflag</field>
+    </object>
+    <object pk="33" model="auth.permission">
+        <field type="CharField" name="name">Can change comment flag</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">11</field>
+        <field type="CharField" name="codename">change_commentflag</field>
+    </object>
+    <object pk="34" model="auth.permission">
+        <field type="CharField" name="name">Can delete comment flag</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">11</field>
+        <field type="CharField" name="codename">delete_commentflag</field>
+    </object>
+    <object pk="16" model="auth.permission">
+        <field type="CharField" name="name">Can add content type</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">6</field>
+        <field type="CharField" name="codename">add_contenttype</field>
+    </object>
+    <object pk="17" model="auth.permission">
+        <field type="CharField" name="name">Can change content type</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">6</field>
+        <field type="CharField" name="codename">change_contenttype</field>
+    </object>
+    <object pk="18" model="auth.permission">
+        <field type="CharField" name="name">Can delete content type</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">6</field>
+        <field type="CharField" name="codename">delete_contenttype</field>
+    </object>
+    <object pk="121" model="auth.permission">
+        <field type="CharField" name="name">Can add Bookmark</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">36</field>
+        <field type="CharField" name="codename">add_bookmark</field>
+    </object>
+    <object pk="122" model="auth.permission">
+        <field type="CharField" name="name">Can change Bookmark</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">36</field>
+        <field type="CharField" name="codename">change_bookmark</field>
+    </object>
+    <object pk="123" model="auth.permission">
+        <field type="CharField" name="name">Can delete Bookmark</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">36</field>
+        <field type="CharField" name="codename">delete_bookmark</field>
+    </object>
+    <object pk="124" model="auth.permission">
+        <field type="CharField" name="name">Can add Bookmark Item</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">37</field>
+        <field type="CharField" name="codename">add_bookmarkitem</field>
+    </object>
+    <object pk="125" model="auth.permission">
+        <field type="CharField" name="name">Can change Bookmark Item</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">37</field>
+        <field type="CharField" name="codename">change_bookmarkitem</field>
+    </object>
+    <object pk="126" model="auth.permission">
+        <field type="CharField" name="name">Can delete Bookmark Item</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">37</field>
+        <field type="CharField" name="codename">delete_bookmarkitem</field>
+    </object>
+    <object pk="127" model="auth.permission">
+        <field type="CharField" name="name">Can add Help</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">38</field>
+        <field type="CharField" name="codename">add_help</field>
+    </object>
+    <object pk="128" model="auth.permission">
+        <field type="CharField" name="name">Can change Help</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">38</field>
+        <field type="CharField" name="codename">change_help</field>
+    </object>
+    <object pk="129" model="auth.permission">
+        <field type="CharField" name="name">Can delete Help</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">38</field>
+        <field type="CharField" name="codename">delete_help</field>
+    </object>
+    <object pk="130" model="auth.permission">
+        <field type="CharField" name="name">Can add Help Entry</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">39</field>
+        <field type="CharField" name="codename">add_helpitem</field>
+    </object>
+    <object pk="131" model="auth.permission">
+        <field type="CharField" name="name">Can change Help Entry</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">39</field>
+        <field type="CharField" name="codename">change_helpitem</field>
+    </object>
+    <object pk="132" model="auth.permission">
+        <field type="CharField" name="name">Can delete Help Entry</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">39</field>
+        <field type="CharField" name="codename">delete_helpitem</field>
+    </object>
+    <object pk="115" model="auth.permission">
+        <field type="CharField" name="name">Can add Navigation</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">34</field>
+        <field type="CharField" name="codename">add_navigation</field>
+    </object>
+    <object pk="116" model="auth.permission">
+        <field type="CharField" name="name">Can change Navigation</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">34</field>
+        <field type="CharField" name="codename">change_navigation</field>
+    </object>
+    <object pk="117" model="auth.permission">
+        <field type="CharField" name="name">Can delete Navigation</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">34</field>
+        <field type="CharField" name="codename">delete_navigation</field>
+    </object>
+    <object pk="118" model="auth.permission">
+        <field type="CharField" name="name">Can add Navigation Item</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">35</field>
+        <field type="CharField" name="codename">add_navigationitem</field>
+    </object>
+    <object pk="119" model="auth.permission">
+        <field type="CharField" name="name">Can change Navigation Item</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">35</field>
+        <field type="CharField" name="codename">change_navigationitem</field>
+    </object>
+    <object pk="120" model="auth.permission">
+        <field type="CharField" name="name">Can delete Navigation Item</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">35</field>
+        <field type="CharField" name="codename">delete_navigationitem</field>
+    </object>
+    <object pk="45" model="auth.permission">
+        <field type="CharField" name="name">Can add repository</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">14</field>
+        <field type="CharField" name="codename">add_repository</field>
+    </object>
+    <object pk="46" model="auth.permission">
+        <field type="CharField" name="name">Can change repository</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">14</field>
+        <field type="CharField" name="codename">change_repository</field>
+    </object>
+    <object pk="47" model="auth.permission">
+        <field type="CharField" name="name">Can delete repository</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">14</field>
+        <field type="CharField" name="codename">delete_repository</field>
+    </object>
+    <object pk="54" model="auth.permission">
+        <field type="CharField" name="name">Can add notice</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">17</field>
+        <field type="CharField" name="codename">add_notice</field>
+    </object>
+    <object pk="55" model="auth.permission">
+        <field type="CharField" name="name">Can change notice</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">17</field>
+        <field type="CharField" name="codename">change_notice</field>
+    </object>
+    <object pk="56" model="auth.permission">
+        <field type="CharField" name="name">Can delete notice</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">17</field>
+        <field type="CharField" name="codename">delete_notice</field>
+    </object>
+    <object pk="57" model="auth.permission">
+        <field type="CharField" name="name">Can add notice queue batch</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">18</field>
+        <field type="CharField" name="codename">add_noticequeuebatch</field>
+    </object>
+    <object pk="58" model="auth.permission">
+        <field type="CharField" name="name">Can change notice queue batch</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">18</field>
+        <field type="CharField" name="codename">change_noticequeuebatch</field>
+    </object>
+    <object pk="59" model="auth.permission">
+        <field type="CharField" name="name">Can delete notice queue batch</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">18</field>
+        <field type="CharField" name="codename">delete_noticequeuebatch</field>
+    </object>
+    <object pk="51" model="auth.permission">
+        <field type="CharField" name="name">Can add notice setting</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">16</field>
+        <field type="CharField" name="codename">add_noticesetting</field>
+    </object>
+    <object pk="52" model="auth.permission">
+        <field type="CharField" name="name">Can change notice setting</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">16</field>
+        <field type="CharField" name="codename">change_noticesetting</field>
+    </object>
+    <object pk="53" model="auth.permission">
+        <field type="CharField" name="name">Can delete notice setting</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">16</field>
+        <field type="CharField" name="codename">delete_noticesetting</field>
+    </object>
+    <object pk="48" model="auth.permission">
+        <field type="CharField" name="name">Can add notice type</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">15</field>
+        <field type="CharField" name="codename">add_noticetype</field>
+    </object>
+    <object pk="49" model="auth.permission">
+        <field type="CharField" name="name">Can change notice type</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">15</field>
+        <field type="CharField" name="codename">change_noticetype</field>
+    </object>
+    <object pk="50" model="auth.permission">
+        <field type="CharField" name="name">Can delete notice type</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">15</field>
+        <field type="CharField" name="codename">delete_noticetype</field>
+    </object>
+    <object pk="60" model="auth.permission">
+        <field type="CharField" name="name">Can add observed item</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">19</field>
+        <field type="CharField" name="codename">add_observeditem</field>
+    </object>
+    <object pk="61" model="auth.permission">
+        <field type="CharField" name="name">Can change observed item</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">19</field>
+        <field type="CharField" name="codename">change_observeditem</field>
+    </object>
+    <object pk="62" model="auth.permission">
+        <field type="CharField" name="name">Can delete observed item</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">19</field>
+        <field type="CharField" name="codename">delete_observeditem</field>
+    </object>
+    <object pk="91" model="auth.permission">
+        <field type="CharField" name="name">Can add membership</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">26</field>
+        <field type="CharField" name="codename">add_membership</field>
+    </object>
+    <object pk="92" model="auth.permission">
+        <field type="CharField" name="name">Can change membership</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">26</field>
+        <field type="CharField" name="codename">change_membership</field>
+    </object>
+    <object pk="93" model="auth.permission">
+        <field type="CharField" name="name">Can delete membership</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">26</field>
+        <field type="CharField" name="codename">delete_membership</field>
+    </object>
+    <object pk="94" model="auth.permission">
+        <field type="CharField" name="name">Can add milestone</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">27</field>
+        <field type="CharField" name="codename">add_milestone</field>
+    </object>
+    <object pk="95" model="auth.permission">
+        <field type="CharField" name="name">Can change milestone</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">27</field>
+        <field type="CharField" name="codename">change_milestone</field>
+    </object>
+    <object pk="96" model="auth.permission">
+        <field type="CharField" name="name">Can delete milestone</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">27</field>
+        <field type="CharField" name="codename">delete_milestone</field>
+    </object>
+    <object pk="100" model="auth.permission">
+        <field type="CharField" name="name">Can add priority level</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">29</field>
+        <field type="CharField" name="codename">add_priority</field>
+    </object>
+    <object pk="101" model="auth.permission">
+        <field type="CharField" name="name">Can change priority level</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">29</field>
+        <field type="CharField" name="codename">change_priority</field>
+    </object>
+    <object pk="102" model="auth.permission">
+        <field type="CharField" name="name">Can delete priority level</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">29</field>
+        <field type="CharField" name="codename">delete_priority</field>
+    </object>
+    <object pk="75" model="auth.permission">
+        <field type="CharField" name="name">Can add project</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">24</field>
+        <field type="CharField" name="codename">add_project</field>
+    </object>
+    <object pk="85" model="auth.permission">
+        <field type="CharField" name="name">Can add member</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">24</field>
+        <field type="CharField" name="codename">can_add_member</field>
+    </object>
+    <object pk="82" model="auth.permission">
+        <field type="CharField" name="name">Can add task</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">24</field>
+        <field type="CharField" name="codename">can_add_task</field>
+    </object>
+    <object pk="81" model="auth.permission">
+        <field type="CharField" name="name">Can change category</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">24</field>
+        <field type="CharField" name="codename">can_change_category</field>
+    </object>
+    <object pk="80" model="auth.permission">
+        <field type="CharField" name="name">Can change description</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">24</field>
+        <field type="CharField" name="codename">can_change_description</field>
+    </object>
+    <object pk="86" model="auth.permission">
+        <field type="CharField" name="name">Can change member</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">24</field>
+        <field type="CharField" name="codename">can_change_member</field>
+    </object>
+    <object pk="83" model="auth.permission">
+        <field type="CharField" name="name">Can change task</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">24</field>
+        <field type="CharField" name="codename">can_change_task</field>
+    </object>
+    <object pk="87" model="auth.permission">
+        <field type="CharField" name="name">Can delete member</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">24</field>
+        <field type="CharField" name="codename">can_delete_member</field>
+    </object>
+    <object pk="84" model="auth.permission">
+        <field type="CharField" name="name">Can delete task</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">24</field>
+        <field type="CharField" name="codename">can_delete_task</field>
+    </object>
+    <object pk="78" model="auth.permission">
+        <field type="CharField" name="name">Can read repository</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">24</field>
+        <field type="CharField" name="codename">can_read_repository</field>
+    </object>
+    <object pk="79" model="auth.permission">
+        <field type="CharField" name="name">Can write to repository</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">24</field>
+        <field type="CharField" name="codename">can_write_to_repository</field>
+    </object>
+    <object pk="76" model="auth.permission">
+        <field type="CharField" name="name">Can change project</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">24</field>
+        <field type="CharField" name="codename">change_project</field>
+    </object>
+    <object pk="77" model="auth.permission">
+        <field type="CharField" name="name">Can delete project</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">24</field>
+        <field type="CharField" name="codename">delete_project</field>
+    </object>
+    <object pk="72" model="auth.permission">
+        <field type="CharField" name="name">Can add project</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">23</field>
+        <field type="CharField" name="codename">add_projectcategory</field>
+    </object>
+    <object pk="73" model="auth.permission">
+        <field type="CharField" name="name">Can change project</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">23</field>
+        <field type="CharField" name="codename">change_projectcategory</field>
+    </object>
+    <object pk="74" model="auth.permission">
+        <field type="CharField" name="name">Can delete project</field>
+        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">23</field>
+        <field type="CharField" name="codename">delete_projectcategory</field>
+    </object>
+    <object pk="88" model="auth.permission">