first data model for backend
authorymh <ymh.work@gmail.com>
Thu, 08 Jun 2017 17:57:57 +0200
changeset 24 3b3999550508
parent 23 4c3ae065f22c
child 25 e04714a1d4eb
first data model for backend
.hgignore
src/.env.tmpl
src/.pylintrc
src/CHANGE
src/LICENSE
src/README
src/irinotes/__init__.py
src/irinotes/apps.py
src/irinotes/settings.py
src/irinotes/urls.py
src/irinotes/wsgi.py
src/manage.py
src/notes/__init__.py
src/notes/admin/__init__.py
src/notes/admin/auth.py
src/notes/apps.py
src/notes/locale/en/LC_MESSAGES/django.mo
src/notes/locale/en/LC_MESSAGES/django.po
src/notes/locale/fr/LC_MESSAGES/django.mo
src/notes/locale/fr/LC_MESSAGES/django.po
src/notes/migrations/0001_initial.py
src/notes/migrations/__init__.py
src/notes/models/__init__.py
src/notes/models/auth.py
src/notes/models/base.py
src/notes/models/category.py
src/notes/models/core.py
src/notes/tests.py
src/notes/views.py
src/requirements/base.txt
src/requirements/base.txt.in
src/requirements/dev.txt
src/requirements/prod.txt
src/setup.py
--- a/.hgignore	Thu Jun 08 14:46:34 2017 +0200
+++ b/.hgignore	Thu Jun 08 17:57:57 2017 +0200
@@ -24,3 +24,11 @@
 \.swo$
 \.swp$
 \.orig$
+
+^src/.env$
+^src/run/
+^src/dist/
+^src/.vscode/
+^src/MANIFEST.in
+^src/MANIFEST
+^src/irinotes.egg-info/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/.env.tmpl	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,37 @@
+# Base url for the application. default=''
+BASE_URL=
+
+# base url for static resources (ends with "/")
+# default : /static/
+STATIC_URL=/static/
+
+# The absolute path to the directory where collectstatic will collect static files for deployment. (https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-STATIC_ROOT)
+# default: <path to irinotes repository clone>/run/web/static
+STATIC_ROOT=
+
+# Absolute filesystem path to the directory that will hold user-uploaded files (https://docs.djangoproject.com/en/1.11/ref/settings/#media-root)
+# default: <path to irinotes repository clone>/run/web/media
+MEDIA_ROOT=
+
+# Secret key for the application. cf. https://docs.djangoproject.com/en/1.11/ref/settings/#secret-key
+SECRET_KEY=ARANDOMSECRETKEY
+
+# Debug the application. Default True
+DEBUG=<true|False>
+
+# Comma separated values of authorized host. cf. https://docs.djangoproject.com/en/1.11/ref/settings/#allowed-hosts
+# default: empty
+ALLOWED_HOSTS=127.0.0.1,localhost
+
+# 12factor inspired DATABASE_URL environment variable cf.https://github.com/kennethreitz/dj-database-url
+# examples: postgres://<user>:<password>@<host>:<port>/<db_name>
+# examples: sqlite:////full/path/to/your/database/file.sqlite
+# default: sqlite:///<path to irinotes repository clone>/run/db/db.sqlite3
+DATABASE_URL=sqlite:///<path to irinotes repository clone>/run/db/db.sqlite3
+
+# path for the log file
+# default: <path to irinotes repository clone>/run/log/log.txt
+LOG_FILE=
+
+# log level one of CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET. Default: ERROR
+LOG_LEVEL=DEBUG
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/.pylintrc	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,425 @@
+[MASTER]
+
+# A comma-separated list of package or module names from where C extensions may
+# be loaded. Extensions are loading into the active Python interpreter and may
+# run arbitrary code
+extension-pkg-whitelist=
+
+# Add files or directories to the blacklist. They should be base names, not
+# paths.
+ignore=CVS
+
+# Add files or directories matching the regex patterns to the blacklist. The
+# regex matches against base names, not paths.
+ignore-patterns=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Use multiple processes to speed up Pylint.
+jobs=1
+
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# Specify a configuration file.
+#rcfile=
+
+# Allow loading of arbitrary C extensions. Extensions are imported into the
+# active Python interpreter and may run arbitrary code.
+unsafe-load-any-extension=no
+
+
+[MESSAGES CONTROL]
+
+# Only show warnings with the listed confidence levels. Leave empty to show
+# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
+confidence=
+
+# Disable the message, report, category or checker with the given id(s). You
+# can either give multiple identifiers separated by comma (,) or put this
+# option multiple times (only on the command line, not in the configuration
+# file where it should appear only once).You can also use "--disable=all" to
+# disable everything first and then reenable specific checks. For example, if
+# you want to run only the similarities checker, you can use "--disable=all
+# --enable=similarities". If you want to run only the classes checker, but have
+# no Warning level messages displayed, use"--disable=all --enable=classes
+# --disable=W"
+disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call
+
+# Enable the message, report, category or checker with the given id(s). You can
+# either give multiple identifier separated by comma (,) or put this option
+# multiple time (only on the command line, not in the configuration file where
+# it should appear only once). See also the "--disable" option for examples.
+enable=
+
+
+[REPORTS]
+
+# Python expression which should return a note less than 10 (10 is the highest
+# note). You have access to the variables errors warning, statement which
+# respectively contain the number of errors / warnings messages and the total
+# number of statements analyzed. This is used by the global evaluation report
+# (RP0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Template used to display messages. This is a python new-style format string
+# used to format the message information. See doc for all details
+#msg-template=
+
+# Set the output format. Available formats are text, parseable, colorized, json
+# and msvs (visual studio).You can also give a reporter class, eg
+# mypackage.mymodule.MyReporterClass.
+output-format=text
+
+# Tells whether to display a full report or only the messages
+reports=no
+
+# Activate the evaluation score.
+score=yes
+
+
+[REFACTORING]
+
+# Maximum number of nested blocks for function / method body
+max-nested-blocks=5
+
+
+[BASIC]
+
+# Naming hint for argument names
+argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
+
+# Regular expression matching correct argument names
+argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
+
+# Naming hint for attribute names
+attr-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
+
+# Regular expression matching correct attribute names
+attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
+
+# Bad variable names which should always be refused, separated by a comma
+bad-names=foo,bar,baz,toto,tutu,tata
+
+# Naming hint for class attribute names
+class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
+
+# Regular expression matching correct class attribute names
+class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
+
+# Naming hint for class names
+class-name-hint=[A-Z_][a-zA-Z0-9]+$
+
+# Regular expression matching correct class names
+class-rgx=[A-Z_][a-zA-Z0-9]+$
+
+# Naming hint for constant names
+const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$
+
+# Regular expression matching correct constant names
+const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
+
+# Minimum line length for functions/classes that require docstrings, shorter
+# ones are exempt.
+docstring-min-length=30
+
+# Naming hint for function names
+function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
+
+# Regular expression matching correct function names
+function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
+
+# Good variable names which should always be accepted, separated by a comma
+good-names=i,j,k,ex,Run,_
+
+# Include a hint for the correct naming format with invalid-name
+include-naming-hint=no
+
+# Naming hint for inline iteration names
+inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$
+
+# Regular expression matching correct inline iteration names
+inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
+
+# Naming hint for method names
+method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
+
+# Regular expression matching correct method names
+method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
+
+# Naming hint for module names
+module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Regular expression matching correct module names
+module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Colon-delimited sets of names that determine each other's naming style when
+# the name regexes allow several styles.
+name-group=
+
+# Regular expression which should only match function or class names that do
+# not require a docstring.
+no-docstring-rgx=^_
+
+# List of decorators that produce properties, such as abc.abstractproperty. Add
+# to this list to register other decorators that produce valid properties.
+property-classes=abc.abstractproperty
+
+# Naming hint for variable names
+variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
+
+# Regular expression matching correct variable names
+variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
+
+
+[FORMAT]
+
+# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
+expected-line-ending-format=
+
+# Regexp for a line that is allowed to be longer than the limit.
+ignore-long-lines=^\s*(# )?<?https?://\S+>?$
+
+# Number of spaces of indent required inside a hanging  or continued line.
+indent-after-paren=4
+
+# String used as indentation unit. This is usually "    " (4 spaces) or "\t" (1
+# tab).
+indent-string='    '
+
+# Maximum number of characters on a single line.
+max-line-length=100
+
+# Maximum number of lines in a module
+max-module-lines=1000
+
+# List of optional constructs for which whitespace checking is disabled. `dict-
+# separator` is used to allow tabulation in dicts, etc.: {1  : 1,\n222: 2}.
+# `trailing-comma` allows a space between comma and closing bracket: (a, ).
+# `empty-line` allows space-only lines.
+no-space-check=trailing-comma,dict-separator
+
+# Allow the body of a class to be on the same line as the declaration if body
+# contains single statement.
+single-line-class-stmt=no
+
+# Allow the body of an if to be on the same line as the test if there is no
+# else.
+single-line-if-stmt=no
+
+
+[LOGGING]
+
+# Logging modules to check that the string format arguments are in logging
+# function parameter format
+logging-modules=logging
+
+
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,XXX,TODO
+
+
+[SIMILARITIES]
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+# Ignore imports when computing similarities.
+ignore-imports=no
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+
+[SPELLING]
+
+# Spelling dictionary name. Available dictionaries: none. To make it working
+# install python-enchant package.
+spelling-dict=
+
+# List of comma separated words that should not be checked.
+spelling-ignore-words=
+
+# A path to a file that contains private dictionary; one word per line.
+spelling-private-dict-file=
+
+# Tells whether to store unknown words to indicated private dictionary in
+# --spelling-private-dict-file option instead of raising a message.
+spelling-store-unknown-words=no
+
+
+[TYPECHECK]
+
+# List of decorators that produce context managers, such as
+# contextlib.contextmanager. Add to this list to register other decorators that
+# produce valid context managers.
+contextmanager-decorators=contextlib.contextmanager
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E1101 when accessed. Python regular
+# expressions are accepted.
+generated-members=
+
+# Tells whether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# This flag controls whether pylint should warn about no-member and similar
+# checks whenever an opaque object is returned when inferring. The inference
+# can return multiple potential results while evaluating a Python object, but
+# some branches might not be evaluated, which results in partial inference. In
+# that case, it might be useful to still emit no-member and other checks for
+# the rest of the inferred objects.
+ignore-on-opaque-inference=yes
+
+# List of class names for which member attributes should not be checked (useful
+# for classes with dynamically set attributes). This supports the use of
+# qualified names.
+ignored-classes=optparse.Values,thread._local,_thread._local
+
+# List of module names for which member attributes should not be checked
+# (useful for modules/projects where namespaces are manipulated during runtime
+# and thus existing member attributes cannot be deduced by static analysis. It
+# supports qualified module names, as well as Unix pattern matching.
+ignored-modules=
+
+# Show a hint with possible names when a member name was not found. The aspect
+# of finding the hint is based on edit distance.
+missing-member-hint=yes
+
+# The minimum edit distance a name should have in order to be considered a
+# similar match for a missing member name.
+missing-member-hint-distance=1
+
+# The total number of similar names that should be taken in consideration when
+# showing a hint for a missing member.
+missing-member-max-choices=1
+
+
+[VARIABLES]
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
+
+# Tells whether unused global variables should be treated as a violation.
+allow-global-unused-variables=yes
+
+# List of strings which can identify a callback function by name. A callback
+# name must start or end with one of those strings.
+callbacks=cb_,_cb
+
+# A regular expression matching the name of dummy variables (i.e. expectedly
+# not used).
+dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
+
+# Argument names that match this expression will be ignored. Default to name
+# with leading underscore
+ignored-argument-names=_.*|^ignored_|^unused_
+
+# Tells whether we should check for unused import in __init__ files.
+init-import=no
+
+# List of qualified module names which can have objects that can redefine
+# builtins.
+redefining-builtins-modules=six.moves,future.builtins
+
+
+[CLASSES]
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,__new__,setUp
+
+# List of member names, which should be excluded from the protected access
+# warning.
+exclude-protected=_asdict,_fields,_replace,_source,_make
+
+# List of valid names for the first argument in a class method.
+valid-classmethod-first-arg=cls
+
+# List of valid names for the first argument in a metaclass class method.
+valid-metaclass-classmethod-first-arg=mcs
+
+
+[DESIGN]
+
+# Maximum number of arguments for function / method
+max-args=5
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=7
+
+# Maximum number of boolean expressions in a if statement
+max-bool-expr=5
+
+# Maximum number of branch for function / method body
+max-branches=12
+
+# Maximum number of locals for function / method body
+max-locals=15
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=20
+
+# Maximum number of return / yield for function / method body
+max-returns=6
+
+# Maximum number of statements in function / method body
+max-statements=50
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=2
+
+
+[IMPORTS]
+
+# Allow wildcard imports from modules that define __all__.
+allow-wildcard-with-all=no
+
+# Analyse import fallback blocks. This can be used to support both Python 2 and
+# 3 compatible code, which means that the block might have code that exists
+# only in one or another interpreter, leading to false positives when analysed.
+analyse-fallback-blocks=no
+
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=optparse,tkinter.tix
+
+# Create a graph of external dependencies in the given file (report RP0402 must
+# not be disabled)
+ext-import-graph=
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report RP0402 must not be disabled)
+import-graph=
+
+# Create a graph of internal dependencies in the given file (report RP0402 must
+# not be disabled)
+int-import-graph=
+
+# Force import order to recognize a module as part of the standard
+# compatibility libraries.
+known-standard-library=
+
+# Force import order to recognize a module as part of a third party library.
+known-third-party=enchant
+
+
+[EXCEPTIONS]
+
+# Exceptions that will emit a warning when being caught. Defaults to
+# "Exception"
+overgeneral-exceptions=Exception
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/LICENSE	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,520 @@
+Copyright (c) 2016, IRI (Institute d Recherche de d'Innovation)
+All rights reserved.
+
+
+
+
+CeCILL-B FREE SOFTWARE LICENSE AGREEMENT
+
+
+    Notice
+
+This Agreement is a Free Software license agreement that is the result
+of discussions between its authors in order to ensure compliance with
+the two main principles guiding its drafting:
+
+    * firstly, compliance with the principles governing the distribution
+      of Free Software: access to source code, broad rights granted to
+      users,
+    * secondly, the election of a governing law, French law, with which
+      it is conformant, both as regards the law of torts and
+      intellectual property law, and the protection that it offers to
+      both authors and holders of the economic rights over software.
+
+The authors of the CeCILL-B (for Ce[a] C[nrs] I[nria] L[ogiciel] L[ibre])
+license are:
+
+Commissariat à l'Energie Atomique - CEA, a public scientific, technical
+and industrial research establishment, having its principal place of
+business at 25 rue Leblanc, immeuble Le Ponant D, 75015 Paris, France.
+
+Centre National de la Recherche Scientifique - CNRS, a public scientific
+and technological establishment, having its principal place of business
+at 3 rue Michel-Ange, 75794 Paris cedex 16, France.
+
+Institut National de Recherche en Informatique et en Automatique -
+INRIA, a public scientific and technological establishment, having its
+principal place of business at Domaine de Voluceau, Rocquencourt, BP
+105, 78153 Le Chesnay cedex, France.
+
+
+    Preamble
+
+This Agreement is an open source software license intended to give users
+significant freedom to modify and redistribute the software licensed
+hereunder.
+
+The exercising of this freedom is conditional upon a strong obligation
+of giving credits for everybody that distributes a software
+incorporating a software ruled by the current license so as all
+contributions to be properly identified and acknowledged.
+
+In consideration of access to the source code and the rights to copy,
+modify and redistribute granted by the license, users are provided only
+with a limited warranty and the software's author, the holder of the
+economic rights, and the successive licensors only have limited liability.
+
+In this respect, the risks associated with loading, using, modifying
+and/or developing or reproducing the software by the user are brought to
+the user's attention, given its Free Software status, which may make it
+complicated to use, with the result that its use is reserved for
+developers and experienced professionals having in-depth computer
+knowledge. Users are therefore encouraged to load and test the
+suitability of the software as regards their requirements in conditions
+enabling the security of their systems and/or data to be ensured and,
+more generally, to use and operate it in the same conditions of
+security. This Agreement may be freely reproduced and published,
+provided it is not altered, and that no provisions are either added or
+removed herefrom.
+
+This Agreement may apply to any or all software for which the holder of
+the economic rights decides to submit the use thereof to its provisions.
+
+
+    Article 1 - DEFINITIONS
+
+For the purpose of this Agreement, when the following expressions
+commence with a capital letter, they shall have the following meaning:
+
+Agreement: means this license agreement, and its possible subsequent
+versions and annexes.
+
+Software: means the software in its Object Code and/or Source Code form
+and, where applicable, its documentation, "as is" when the Licensee
+accepts the Agreement.
+
+Initial Software: means the Software in its Source Code and possibly its
+Object Code form and, where applicable, its documentation, "as is" when
+it is first distributed under the terms and conditions of the Agreement.
+
+Modified Software: means the Software modified by at least one
+Contribution.
+
+Source Code: means all the Software's instructions and program lines to
+which access is required so as to modify the Software.
+
+Object Code: means the binary files originating from the compilation of
+the Source Code.
+
+Holder: means the holder(s) of the economic rights over the Initial
+Software.
+
+Licensee: means the Software user(s) having accepted the Agreement.
+
+Contributor: means a Licensee having made at least one Contribution.
+
+Licensor: means the Holder, or any other individual or legal entity, who
+distributes the Software under the Agreement.
+
+Contribution: means any or all modifications, corrections, translations,
+adaptations and/or new functions integrated into the Software by any or
+all Contributors, as well as any or all Internal Modules.
+
+Module: means a set of sources files including their documentation that
+enables supplementary functions or services in addition to those offered
+by the Software.
+
+External Module: means any or all Modules, not derived from the
+Software, so that this Module and the Software run in separate address
+spaces, with one calling the other when they are run.
+
+Internal Module: means any or all Module, connected to the Software so
+that they both execute in the same address space.
+
+Parties: mean both the Licensee and the Licensor.
+
+These expressions may be used both in singular and plural form.
+
+
+    Article 2 - PURPOSE
+
+The purpose of the Agreement is the grant by the Licensor to the
+Licensee of a non-exclusive, transferable and worldwide license for the
+Software as set forth in Article 5 hereinafter for the whole term of the
+protection granted by the rights over said Software.
+
+
+    Article 3 - ACCEPTANCE
+
+3.1 The Licensee shall be deemed as having accepted the terms and
+conditions of this Agreement upon the occurrence of the first of the
+following events:
+
+    * (i) loading the Software by any or all means, notably, by
+      downloading from a remote server, or by loading from a physical
+      medium;
+    * (ii) the first time the Licensee exercises any of the rights
+      granted hereunder.
+
+3.2 One copy of the Agreement, containing a notice relating to the
+characteristics of the Software, to the limited warranty, and to the
+fact that its use is restricted to experienced users has been provided
+to the Licensee prior to its acceptance as set forth in Article 3.1
+hereinabove, and the Licensee hereby acknowledges that it has read and
+understood it.
+
+
+    Article 4 - EFFECTIVE DATE AND TERM
+
+
+      4.1 EFFECTIVE DATE
+
+The Agreement shall become effective on the date when it is accepted by
+the Licensee as set forth in Article 3.1.
+
+
+      4.2 TERM
+
+The Agreement shall remain in force for the entire legal term of
+protection of the economic rights over the Software.
+
+
+    Article 5 - SCOPE OF RIGHTS GRANTED
+
+The Licensor hereby grants to the Licensee, who accepts, the following
+rights over the Software for any or all use, and for the term of the
+Agreement, on the basis of the terms and conditions set forth hereinafter.
+
+Besides, if the Licensor owns or comes to own one or more patents
+protecting all or part of the functions of the Software or of its
+components, the Licensor undertakes not to enforce the rights granted by
+these patents against successive Licensees using, exploiting or
+modifying the Software. If these patents are transferred, the Licensor
+undertakes to have the transferees subscribe to the obligations set
+forth in this paragraph.
+
+
+      5.1 RIGHT OF USE
+
+The Licensee is authorized to use the Software, without any limitation
+as to its fields of application, with it being hereinafter specified
+that this comprises:
+
+   1. permanent or temporary reproduction of all or part of the Software
+      by any or all means and in any or all form.
+
+   2. loading, displaying, running, or storing the Software on any or
+      all medium.
+
+   3. entitlement to observe, study or test its operation so as to
+      determine the ideas and principles behind any or all constituent
+      elements of said Software. This shall apply when the Licensee
+      carries out any or all loading, displaying, running, transmission
+      or storage operation as regards the Software, that it is entitled
+      to carry out hereunder.
+
+
+      5.2 ENTITLEMENT TO MAKE CONTRIBUTIONS
+
+The right to make Contributions includes the right to translate, adapt,
+arrange, or make any or all modifications to the Software, and the right
+to reproduce the resulting software.
+
+The Licensee is authorized to make any or all Contributions to the
+Software provided that it includes an explicit notice that it is the
+author of said Contribution and indicates the date of the creation thereof.
+
+
+      5.3 RIGHT OF DISTRIBUTION
+
+In particular, the right of distribution includes the right to publish,
+transmit and communicate the Software to the general public on any or
+all medium, and by any or all means, and the right to market, either in
+consideration of a fee, or free of charge, one or more copies of the
+Software by any means.
+
+The Licensee is further authorized to distribute copies of the modified
+or unmodified Software to third parties according to the terms and
+conditions set forth hereinafter.
+
+
+        5.3.1 DISTRIBUTION OF SOFTWARE WITHOUT MODIFICATION
+
+The Licensee is authorized to distribute true copies of the Software in
+Source Code or Object Code form, provided that said distribution
+complies with all the provisions of the Agreement and is accompanied by:
+
+   1. a copy of the Agreement,
+
+   2. a notice relating to the limitation of both the Licensor's
+      warranty and liability as set forth in Articles 8 and 9,
+
+and that, in the event that only the Object Code of the Software is
+redistributed, the Licensee allows effective access to the full Source
+Code of the Software at a minimum during the entire period of its
+distribution of the Software, it being understood that the additional
+cost of acquiring the Source Code shall not exceed the cost of
+transferring the data.
+
+
+        5.3.2 DISTRIBUTION OF MODIFIED SOFTWARE
+
+If the Licensee makes any Contribution to the Software, the resulting
+Modified Software may be distributed under a license agreement other
+than this Agreement subject to compliance with the provisions of Article
+5.3.4.
+
+
+        5.3.3 DISTRIBUTION OF EXTERNAL MODULES
+
+When the Licensee has developed an External Module, the terms and
+conditions of this Agreement do not apply to said External Module, that
+may be distributed under a separate license agreement.
+
+
+        5.3.4 CREDITS
+
+Any Licensee who may distribute a Modified Software hereby expressly
+agrees to:
+
+   1. indicate in the related documentation that it is based on the
+      Software licensed hereunder, and reproduce the intellectual
+      property notice for the Software,
+
+   2. ensure that written indications of the Software intended use,
+      intellectual property notice and license hereunder are included in
+      easily accessible format from the Modified Software interface,
+
+   3. mention, on a freely accessible website describing the Modified
+      Software, at least throughout the distribution term thereof, that
+      it is based on the Software licensed hereunder, and reproduce the
+      Software intellectual property notice,
+
+   4. where it is distributed to a third party that may distribute a
+      Modified Software without having to make its source code
+      available, make its best efforts to ensure that said third party
+      agrees to comply with the obligations set forth in this Article .
+
+If the Software, whether or not modified, is distributed with an
+External Module designed for use in connection with the Software, the
+Licensee shall submit said External Module to the foregoing obligations.
+
+
+        5.3.5 COMPATIBILITY WITH THE CeCILL AND CeCILL-C LICENSES
+
+Where a Modified Software contains a Contribution subject to the CeCILL
+license, the provisions set forth in Article 5.3.4 shall be optional.
+
+A Modified Software may be distributed under the CeCILL-C license. In
+such a case the provisions set forth in Article 5.3.4 shall be optional.
+
+
+    Article 6 - INTELLECTUAL PROPERTY
+
+
+      6.1 OVER THE INITIAL SOFTWARE
+
+The Holder owns the economic rights over the Initial Software. Any or
+all use of the Initial Software is subject to compliance with the terms
+and conditions under which the Holder has elected to distribute its work
+and no one shall be entitled to modify the terms and conditions for the
+distribution of said Initial Software.
+
+The Holder undertakes that the Initial Software will remain ruled at
+least by this Agreement, for the duration set forth in Article 4.2.
+
+
+      6.2 OVER THE CONTRIBUTIONS
+
+The Licensee who develops a Contribution is the owner of the
+intellectual property rights over this Contribution as defined by
+applicable law.
+
+
+      6.3 OVER THE EXTERNAL MODULES
+
+The Licensee who develops an External Module is the owner of the
+intellectual property rights over this External Module as defined by
+applicable law and is free to choose the type of agreement that shall
+govern its distribution.
+
+
+      6.4 JOINT PROVISIONS
+
+The Licensee expressly undertakes:
+
+   1. not to remove, or modify, in any manner, the intellectual property
+      notices attached to the Software;
+
+   2. to reproduce said notices, in an identical manner, in the copies
+      of the Software modified or not.
+
+The Licensee undertakes not to directly or indirectly infringe the
+intellectual property rights of the Holder and/or Contributors on the
+Software and to take, where applicable, vis-à-vis its staff, any and all
+measures required to ensure respect of said intellectual property rights
+of the Holder and/or Contributors.
+
+
+    Article 7 - RELATED SERVICES
+
+7.1 Under no circumstances shall the Agreement oblige the Licensor to
+provide technical assistance or maintenance services for the Software.
+
+However, the Licensor is entitled to offer this type of services. The
+terms and conditions of such technical assistance, and/or such
+maintenance, shall be set forth in a separate instrument. Only the
+Licensor offering said maintenance and/or technical assistance services
+shall incur liability therefor.
+
+7.2 Similarly, any Licensor is entitled to offer to its licensees, under
+its sole responsibility, a warranty, that shall only be binding upon
+itself, for the redistribution of the Software and/or the Modified
+Software, under terms and conditions that it is free to decide. Said
+warranty, and the financial terms and conditions of its application,
+shall be subject of a separate instrument executed between the Licensor
+and the Licensee.
+
+
+    Article 8 - LIABILITY
+
+8.1 Subject to the provisions of Article 8.2, the Licensee shall be
+entitled to claim compensation for any direct loss it may have suffered
+from the Software as a result of a fault on the part of the relevant
+Licensor, subject to providing evidence thereof.
+
+8.2 The Licensor's liability is limited to the commitments made under
+this Agreement and shall not be incurred as a result of in particular:
+(i) loss due the Licensee's total or partial failure to fulfill its
+obligations, (ii) direct or consequential loss that is suffered by the
+Licensee due to the use or performance of the Software, and (iii) more
+generally, any consequential loss. In particular the Parties expressly
+agree that any or all pecuniary or business loss (i.e. loss of data,
+loss of profits, operating loss, loss of customers or orders,
+opportunity cost, any disturbance to business activities) or any or all
+legal proceedings instituted against the Licensee by a third party,
+shall constitute consequential loss and shall not provide entitlement to
+any or all compensation from the Licensor.
+
+
+    Article 9 - WARRANTY
+
+9.1 The Licensee acknowledges that the scientific and technical
+state-of-the-art when the Software was distributed did not enable all
+possible uses to be tested and verified, nor for the presence of
+possible defects to be detected. In this respect, the Licensee's
+attention has been drawn to the risks associated with loading, using,
+modifying and/or developing and reproducing the Software which are
+reserved for experienced users.
+
+The Licensee shall be responsible for verifying, by any or all means,
+the suitability of the product for its requirements, its good working
+order, and for ensuring that it shall not cause damage to either persons
+or properties.
+
+9.2 The Licensor hereby represents, in good faith, that it is entitled
+to grant all the rights over the Software (including in particular the
+rights set forth in Article 5).
+
+9.3 The Licensee acknowledges that the Software is supplied "as is" by
+the Licensor without any other express or tacit warranty, other than
+that provided for in Article 9.2 and, in particular, without any warranty
+as to its commercial value, its secured, safe, innovative or relevant
+nature.
+
+Specifically, the Licensor does not warrant that the Software is free
+from any error, that it will operate without interruption, that it will
+be compatible with the Licensee's own equipment and software
+configuration, nor that it will meet the Licensee's requirements.
+
+9.4 The Licensor does not either expressly or tacitly warrant that the
+Software does not infringe any third party intellectual property right
+relating to a patent, software or any other property right. Therefore,
+the Licensor disclaims any and all liability towards the Licensee
+arising out of any or all proceedings for infringement that may be
+instituted in respect of the use, modification and redistribution of the
+Software. Nevertheless, should such proceedings be instituted against
+the Licensee, the Licensor shall provide it with technical and legal
+assistance for its defense. Such technical and legal assistance shall be
+decided on a case-by-case basis between the relevant Licensor and the
+Licensee pursuant to a memorandum of understanding. The Licensor
+disclaims any and all liability as regards the Licensee's use of the
+name of the Software. No warranty is given as regards the existence of
+prior rights over the name of the Software or as regards the existence
+of a trademark.
+
+
+    Article 10 - TERMINATION
+
+10.1 In the event of a breach by the Licensee of its obligations
+hereunder, the Licensor may automatically terminate this Agreement
+thirty (30) days after notice has been sent to the Licensee and has
+remained ineffective.
+
+10.2 A Licensee whose Agreement is terminated shall no longer be
+authorized to use, modify or distribute the Software. However, any
+licenses that it may have granted prior to termination of the Agreement
+shall remain valid subject to their having been granted in compliance
+with the terms and conditions hereof.
+
+
+    Article 11 - MISCELLANEOUS
+
+
+      11.1 EXCUSABLE EVENTS
+
+Neither Party shall be liable for any or all delay, or failure to
+perform the Agreement, that may be attributable to an event of force
+majeure, an act of God or an outside cause, such as defective
+functioning or interruptions of the electricity or telecommunications
+networks, network paralysis following a virus attack, intervention by
+government authorities, natural disasters, water damage, earthquakes,
+fire, explosions, strikes and labor unrest, war, etc.
+
+11.2 Any failure by either Party, on one or more occasions, to invoke
+one or more of the provisions hereof, shall under no circumstances be
+interpreted as being a waiver by the interested Party of its right to
+invoke said provision(s) subsequently.
+
+11.3 The Agreement cancels and replaces any or all previous agreements,
+whether written or oral, between the Parties and having the same
+purpose, and constitutes the entirety of the agreement between said
+Parties concerning said purpose. No supplement or modification to the
+terms and conditions hereof shall be effective as between the Parties
+unless it is made in writing and signed by their duly authorized
+representatives.
+
+11.4 In the event that one or more of the provisions hereof were to
+conflict with a current or future applicable act or legislative text,
+said act or legislative text shall prevail, and the Parties shall make
+the necessary amendments so as to comply with said act or legislative
+text. All other provisions shall remain effective. Similarly, invalidity
+of a provision of the Agreement, for any reason whatsoever, shall not
+cause the Agreement as a whole to be invalid.
+
+
+      11.5 LANGUAGE
+
+The Agreement is drafted in both French and English and both versions
+are deemed authentic.
+
+
+    Article 12 - NEW VERSIONS OF THE AGREEMENT
+
+12.1 Any person is authorized to duplicate and distribute copies of this
+Agreement.
+
+12.2 So as to ensure coherence, the wording of this Agreement is
+protected and may only be modified by the authors of the License, who
+reserve the right to periodically publish updates or new versions of the
+Agreement, each with a separate number. These subsequent versions may
+address new issues encountered by Free Software.
+
+12.3 Any Software distributed under a given version of the Agreement may
+only be subsequently distributed under the same version of the Agreement
+or a subsequent version.
+
+
+    Article 13 - GOVERNING LAW AND JURISDICTION
+
+13.1 The Agreement is governed by French law. The Parties agree to
+endeavor to seek an amicable solution to any disagreements or disputes
+that may arise during the performance of the Agreement.
+
+13.2 Failing an amicable solution within two (2) months as from their
+occurrence, and unless emergency proceedings are necessary, the
+disagreements or disputes shall be referred to the Paris Courts having
+jurisdiction, by the more diligent Party.
+
+
+Version 1.0 dated 2006-09-05.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/README	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,41 @@
+# IRINOTES backoffice
+
+Back office for the IRINOTES application
+
+## Installation
+
+Please follow the command below to bootstrap the project
+
+```shell
+$ mkdir -p run/{db,log,web}
+$ mkdir -p run/web/{media,static}
+$ cp .env.tmpl .env
+$ vi .env
+$ mkvirtualenv irinotes
+$ pip install requirements/dev.txt
+$ python manage.py migrate
+$ python manage.py collectstatic
+$ python manage.py createsuperuser
+$ python manage.py runserver
+
+```
+
+You can now visit the following url in your browser <http://127.0.0.1:8000> .
+
+Admin interface is at <http://127.0.0.1:8000/admin>
+
+## Usage
+
+TODO: Write usage instructions
+
+## History
+
+TODO: Write history
+
+## Credits
+
+TODO: Write credits
+
+## License
+
+TODO: Write license
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/irinotes/__init__.py	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,48 @@
+VERSION = (0, 0, 1, "final", 0)
+
+VERSION_STR = ".".join(map(lambda i:"%02d" % (i,), VERSION[:2]))
+
+###
+# https://github.com/django/django/blob/1.9.1/django/utils/version.py
+#
+def get_version(version=None):
+    "Returns a PEP 440-compliant version number from VERSION."
+    if not version:
+        version = VERSION
+    version = get_complete_version(version)
+
+    # Now build the two parts of the version number:
+    # main = X.Y[.Z]
+    # sub = .devN - for pre-alpha releases
+    #     | {a|b|rc}N - for alpha, beta, and rc releases
+
+    main = get_main_version(version)
+
+    sub = ''
+    if version[3] == 'alpha' and version[4] == 0:
+        sub = '.dev'
+
+    elif version[3] != 'final':
+        mapping = {'alpha': 'a', 'beta': 'b', 'rc': 'rc'}
+        sub = mapping[version[3]] + str(version[4])
+
+    return str(main + sub)
+
+def get_complete_version(version):
+    """
+    then checks for correctness of the tuple provided.
+    """
+    assert len(version) == 5
+    assert version[3] in ('alpha', 'beta', 'rc', 'final')
+
+    return version
+
+def get_main_version(version=None):
+    "Returns main version (X.Y[.Z]) from VERSION."
+    version = get_complete_version(version)
+    parts = 2 if version[2] == 0 else 3
+    return '.'.join(str(x) for x in version[:parts])
+
+__version__ = get_version(VERSION)
+
+default_app_config = 'irinotes.apps.IriNotesConfig'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/irinotes/apps.py	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+class IriNotesConfig(AppConfig):
+    name = 'irinotes'
+    verbose_name = "IRI Notes app"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/irinotes/settings.py	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,189 @@
+"""
+Django settings for irinotes project.
+
+Generated by 'django-admin startproject' using Django 1.11.2.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/1.11/topics/settings/
+
+For the full list of settings and their values, see
+https://docs.djangoproject.com/en/1.11/ref/settings/
+"""
+
+import logging
+
+from decouple import Csv, config
+from dj_database_url import parse as db_url
+from unipath import Path
+
+# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
+BASE_DIR = Path(__file__).parent
+
+RUN_DIR = BASE_DIR.parent.child('run')
+
+STATIC_ROOT = config('STATIC_ROOT', default=RUN_DIR.child('web').child('static'))
+MEDIA_ROOT = config('MEDIA_ROOT', default=RUN_DIR.child('web').child('media'))
+
+# base url
+BASE_URL = config('BASE_URL', default='')
+
+# Quick-start development settings - unsuitable for production
+# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
+
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY = config('SECRET_KEY', '@povoyn_1_3dhfjktisno5l_0)_l1+m8$&mr8r2-srvd+m-58d')
+
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG = config('DEBUG', default=True, cast=bool)
+
+ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='', cast=Csv())
+
+
+# Application definition
+
+INSTALLED_APPS = [
+    'django.contrib.admin',
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.messages',
+    'django.contrib.staticfiles',
+    'colorful',
+    'concurrency',
+    'notes'
+]
+
+MIDDLEWARE = [
+    'django.middleware.security.SecurityMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.middleware.common.CommonMiddleware',
+    'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+    'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+
+ROOT_URLCONF = 'irinotes.urls'
+
+TEMPLATES = [
+    {
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'DIRS': [],
+        'APP_DIRS': True,
+        'OPTIONS': {
+            'context_processors': [
+                'django.template.context_processors.debug',
+                'django.template.context_processors.request',
+                'django.contrib.auth.context_processors.auth',
+                'django.contrib.messages.context_processors.messages',
+            ],
+        },
+    },
+]
+
+WSGI_APPLICATION = 'irinotes.wsgi.application'
+
+
+# Database
+# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
+
+DATABASES = {
+    'default': config(
+        'DATABASE_URL',
+        default='sqlite:///' + RUN_DIR.child('db').child('db.sqlite3'),
+        cast=db_url
+    )
+}
+
+
+# Password validation
+# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+    {
+        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+    },
+]
+
+AUTH_USER_MODEL = 'notes.User'
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/1.11/topics/i18n/
+
+LANGUAGE_CODE = 'en-us'
+
+TIME_ZONE = 'UTC'
+
+USE_I18N = True
+
+USE_L10N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/1.11/howto/static-files/
+
+STATIC_URL = config('STATIC_URL', default=BASE_URL + '/static/')
+
+# Logger
+
+LOG_FILE = config('LOG_FILE', default=RUN_DIR.child('log').child('log.txt'))
+LOG_LEVEL = config('LOG_LEVEL', default=logging.DEBUG, cast=lambda l: logging.getLevelName(l))
+
+LOGGING = {
+    'version': 1,
+    'disable_existing_loggers': True,
+    'filters': {
+        'require_debug_false': {
+            '()': 'django.utils.log.RequireDebugFalse'
+        }
+    },
+    'formatters' : {
+        'simple' : {
+            'format': "%(asctime)s - %(levelname)s : %(message)s",
+        },
+        'semi-verbose': {
+            'format': '%(levelname)s %(asctime)s %(module)s %(message)s'
+        },
+    },
+    'handlers': {
+        'mail_admins': {
+            'level': 'ERROR',
+            'filters': ['require_debug_false'],
+            'class': 'django.utils.log.AdminEmailHandler'
+        },
+        'stream_to_console': {
+            'level': LOG_LEVEL,
+            'class': 'logging.StreamHandler'
+        },
+        'file': {
+            'level': LOG_LEVEL,
+            'class': 'logging.FileHandler',
+            'filename': LOG_FILE,
+            'formatter': 'semi-verbose',
+        },
+    },
+    'loggers': {
+        'django.request': {
+            'handlers': ['file'],
+            'level': LOG_LEVEL,
+            'propagate': True,
+        },
+        'irinotes': {
+            'handlers': ['file'],
+            'level': LOG_LEVEL,
+            'propagate': True,
+        },
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/irinotes/urls.py	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,21 @@
+"""irinotes URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+    https://docs.djangoproject.com/en/1.11/topics/http/urls/
+Examples:
+Function views
+    1. Add an import:  from my_app import views
+    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
+Class-based views
+    1. Add an import:  from other_app.views import Home
+    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
+Including another URLconf
+    1. Import the include() function: from django.conf.urls import url, include
+    2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
+"""
+from django.conf.urls import url
+from django.contrib import admin
+
+urlpatterns = [
+    url(r'^admin/', admin.site.urls),
+]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/irinotes/wsgi.py	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,16 @@
+"""
+WSGI config for irinotes project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/
+"""
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "irinotes.settings")
+
+application = get_wsgi_application()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/manage.py	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+import os
+import sys
+
+if __name__ == "__main__":
+    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "irinotes.settings")
+    try:
+        from django.core.management import execute_from_command_line
+    except ImportError:
+        # The above import may fail for some other reason. Ensure that the
+        # issue is really that Django is missing to avoid masking other
+        # exceptions on Python 2.
+        try:
+            import django
+        except ImportError:
+            raise ImportError(
+                "Couldn't import Django. Are you sure it's installed and "
+                "available on your PYTHONPATH environment variable? Did you "
+                "forget to activate a virtual environment?"
+            )
+        raise
+    execute_from_command_line(sys.argv)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/notes/admin/__init__.py	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,22 @@
+"""
+Admin registrations
+"""
+from django.contrib import admin
+from django.contrib.auth.models import Group
+
+from ..models import User, Note, Session, Protocol, Category
+from .auth import UserAdmin
+from .auth import GroupAdmin
+
+
+# Re-register UserAdmin
+admin.site.register(User, UserAdmin)
+
+# Re-register GroupAdmin
+admin.site.unregister(Group)
+admin.site.register(Group, GroupAdmin)
+
+admin.site.register(Note)
+admin.site.register(Session)
+admin.site.register(Protocol)
+admin.site.register(Category)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/notes/admin/auth.py	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,25 @@
+from django.contrib import admin
+from django.contrib.auth.admin import GroupAdmin as BaseGroupAdmin
+from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
+
+from notes.models import GroupProfile, UserProfile
+
+
+# Define an inline admin descriptor for Employee model
+# which acts a bit like a singleton
+class UserProfileInline(admin.StackedInline):
+    model = UserProfile
+    can_delete = False
+
+# Define a new User admin
+class UserAdmin(BaseUserAdmin):
+    inlines = (UserProfileInline, )
+
+
+class GroupProfileInline(admin.StackedInline):
+    model = GroupProfile
+    can_delete = False
+
+
+class GroupAdmin(BaseGroupAdmin):
+    inlines = (GroupProfileInline, )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/notes/apps.py	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class NotesConfig(AppConfig):
+    name = 'notes'
Binary file src/notes/locale/en/LC_MESSAGES/django.mo has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/notes/locale/en/LC_MESSAGES/django.po	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,157 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-06-08 15:29+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: models/auth.py:11
+msgid "User"
+msgstr "User"
+
+#: models/auth.py:12
+msgid "Users"
+msgstr "Users"
+
+#: models/auth.py:17
+msgid "UserProfile"
+msgstr "User profile"
+
+#: models/auth.py:18
+msgid "UserProfiles"
+msgstr "User profiles"
+
+#: models/auth.py:25
+msgid "GroupProfile"
+msgstr "Group profile"
+
+#: models/auth.py:26
+msgid "GroupProfiles"
+msgstr "Group profiles"
+
+#: models/base.py:18
+msgid "Model|created"
+msgstr "created"
+
+#: models/base.py:19
+msgid "Model|updated"
+msgstr "updated"
+
+#: models/base.py:20
+msgid "Model|ext_id"
+msgstr "external id"
+
+#: models/base.py:21
+msgid "Model|version"
+msgstr "version"
+
+#: models/category.py:13
+msgid "Protocol|title"
+msgstr "title"
+
+#: models/category.py:16
+msgid "Protocol"
+msgstr "Protocol"
+
+#: models/category.py:17
+msgid "Protocols"
+msgstr "Protocols"
+
+#: models/category.py:22
+msgid "Category|title"
+msgstr "title"
+
+#: models/category.py:23
+msgid "Category|color"
+msgstr "color"
+
+#: models/category.py:24
+msgid "Category|need_comment"
+msgstr "need comment"
+
+#: models/category.py:25
+msgid "Category|description"
+msgstr "description"
+
+#: models/category.py:28
+msgid "Category|protocol"
+msgstr "protocol"
+
+#: models/category.py:33
+msgid "Category"
+msgstr "Category"
+
+#: models/category.py:34
+msgid "Categories"
+msgstr "Categories"
+
+#: models/core.py:13
+msgid "Session"
+msgstr "Session"
+
+#: models/core.py:14
+msgid "Sessions"
+msgstr "Sessions"
+
+#: models/core.py:20
+msgid "Session|title"
+msgstr "title"
+
+#: models/core.py:21
+msgid "Session|description"
+msgstr "description"
+
+#: models/core.py:22
+msgid "Session|protocol"
+msgstr "protocol"
+
+#: models/core.py:26
+msgid "Note"
+msgstr "Note"
+
+#: models/core.py:27
+msgid "Notes"
+msgstr "Notes"
+
+#: models/core.py:30
+msgid "Note|tc_start"
+msgstr "start timecode"
+
+#: models/core.py:31
+msgid "Note|tc_end"
+msgstr "end timeocde"
+
+#: models/core.py:32
+msgid "Note|session"
+msgstr "session"
+
+#: models/core.py:33
+msgid "Note|text_plain"
+msgstr "text plain"
+
+#: models/core.py:34
+msgid "Note|text_html"
+msgstr "text html"
+
+#: models/core.py:35
+msgid "Note|text_raw"
+msgstr "text raw"
+
+#: models/core.py:36
+msgid "Note|margin_note"
+msgstr "margin note"
+
+#: models/core.py:37
+msgid "Note|categorization"
+msgstr "categorization"
Binary file src/notes/locale/fr/LC_MESSAGES/django.mo has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/notes/locale/fr/LC_MESSAGES/django.po	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,158 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-06-08 15:29+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+#: models/auth.py:11
+msgid "User"
+msgstr "Utilisateur"
+
+#: models/auth.py:12
+msgid "Users"
+msgstr "Utilisateurs"
+
+#: models/auth.py:17
+msgid "UserProfile"
+msgstr "Profil utilisateur"
+
+#: models/auth.py:18
+msgid "UserProfiles"
+msgstr "Profils utilisateur"
+
+#: models/auth.py:25
+msgid "GroupProfile"
+msgstr "Profile groupe"
+
+#: models/auth.py:26
+msgid "GroupProfiles"
+msgstr "Profiles groupe"
+
+#: models/base.py:18
+msgid "Model|created"
+msgstr "créé"
+
+#: models/base.py:19
+msgid "Model|updated"
+msgstr "mis à jour"
+
+#: models/base.py:20
+msgid "Model|ext_id"
+msgstr "identifiant externe"
+
+#: models/base.py:21
+msgid "Model|version"
+msgstr "version"
+
+#: models/category.py:13
+msgid "Protocol|title"
+msgstr "titre"
+
+#: models/category.py:16
+msgid "Protocol"
+msgstr "Protocole"
+
+#: models/category.py:17
+msgid "Protocols"
+msgstr "Protocoles"
+
+#: models/category.py:22
+msgid "Category|title"
+msgstr "titre"
+
+#: models/category.py:23
+msgid "Category|color"
+msgstr "couleur"
+
+#: models/category.py:24
+msgid "Category|need_comment"
+msgstr "a un commentaire"
+
+#: models/category.py:25
+msgid "Category|description"
+msgstr "description"
+
+#: models/category.py:28
+msgid "Category|protocol"
+msgstr "protocole"
+
+#: models/category.py:33
+msgid "Category"
+msgstr "Categorie"
+
+#: models/category.py:34
+msgid "Categories"
+msgstr "Categories"
+
+#: models/core.py:13
+msgid "Session"
+msgstr "Session"
+
+#: models/core.py:14
+msgid "Sessions"
+msgstr "Sessions"
+
+#: models/core.py:20
+msgid "Session|title"
+msgstr "titre"
+
+#: models/core.py:21
+msgid "Session|description"
+msgstr "description"
+
+#: models/core.py:22
+msgid "Session|protocol"
+msgstr "protocole"
+
+#: models/core.py:26
+msgid "Note"
+msgstr "Note"
+
+#: models/core.py:27
+msgid "Notes"
+msgstr "Notes"
+
+#: models/core.py:30
+msgid "Note|tc_start"
+msgstr "timecode début"
+
+#: models/core.py:31
+msgid "Note|tc_end"
+msgstr "timecode fin"
+
+#: models/core.py:32
+msgid "Note|session"
+msgstr "session"
+
+#: models/core.py:33
+msgid "Note|text_plain"
+msgstr "texte seul"
+
+#: models/core.py:34
+msgid "Note|text_html"
+msgstr "texte html"
+
+#: models/core.py:35
+msgid "Note|text_raw"
+msgstr "texte brut"
+
+#: models/core.py:36
+msgid "Note|margin_note"
+msgstr "note de marge"
+
+#: models/core.py:37
+msgid "Note|categorization"
+msgstr "catégorisation"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/notes/migrations/0001_initial.py	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,117 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.2 on 2017-06-08 15:10
+from __future__ import unicode_literals
+
+import concurrency.fields
+from django.conf import settings
+import django.contrib.auth.models
+import django.contrib.auth.validators
+from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
+import uuid
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        ('auth', '0008_alter_user_username_max_length'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='User',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('password', models.CharField(max_length=128, verbose_name='password')),
+                ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
+                ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
+                ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
+                ('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
+                ('last_name', models.CharField(blank=True, max_length=30, verbose_name='last name')),
+                ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
+                ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
+                ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
+                ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
+                ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
+                ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
+            ],
+            options={
+                'verbose_name': 'User',
+                'verbose_name_plural': 'Users',
+            },
+            managers=[
+                ('objects', django.contrib.auth.models.UserManager()),
+            ],
+        ),
+        migrations.CreateModel(
+            name='GroupProfile',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('description', models.TextField(blank=True, null=True)),
+                ('group', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='auth.Group')),
+            ],
+            options={
+                'verbose_name': 'GroupProfile',
+                'verbose_name_plural': 'GroupProfiles',
+            },
+        ),
+        migrations.CreateModel(
+            name='Note',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('created', models.DateTimeField(auto_now_add=True, verbose_name='Model|created')),
+                ('updated', models.DateTimeField(auto_now=True, verbose_name='Model|updated')),
+                ('ext_id', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='Model|ext_id')),
+                ('version', concurrency.fields.AutoIncVersionField(default=1, help_text='record revision number', verbose_name='Model|version')),
+                ('tc_start', models.DateTimeField()),
+                ('tc_end', models.DateTimeField()),
+                ('text_plain', models.TextField(blank=True, null=True, verbose_name='Note|text_plain')),
+                ('text_html', models.TextField(blank=True, null=True, verbose_name='Note|text_html')),
+                ('text_raw', models.TextField(blank=True, null=True, verbose_name='Note|text_raw')),
+                ('margin_note', models.TextField(blank=True, null=True, verbose_name='Note|margin_note')),
+                ('categorization', models.TextField(blank=True, null=True, verbose_name='Note|categorization')),
+            ],
+            options={
+                'verbose_name': 'Note',
+                'verbose_name_plural': 'Notes',
+                'ordering': ['tc_start'],
+            },
+        ),
+        migrations.CreateModel(
+            name='Session',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('created', models.DateTimeField(auto_now_add=True, verbose_name='Model|created')),
+                ('updated', models.DateTimeField(auto_now=True, verbose_name='Model|updated')),
+                ('ext_id', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='Model|ext_id')),
+                ('version', concurrency.fields.AutoIncVersionField(default=1, help_text='record revision number', verbose_name='Model|version')),
+                ('title', models.TextField(blank=True, null=True, verbose_name='Session|title')),
+                ('description', models.TextField(blank=True, null=True, verbose_name='Session|description')),
+                ('protocol', models.TextField(blank=True, null=True, verbose_name='Session|protocol')),
+                ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+            ],
+            options={
+                'verbose_name': 'Session',
+                'verbose_name_plural': 'Sessions',
+            },
+        ),
+        migrations.CreateModel(
+            name='UserProfile',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+            ],
+            options={
+                'verbose_name': 'UserProfile',
+                'verbose_name_plural': 'UserProfiles',
+            },
+        ),
+        migrations.AddField(
+            model_name='note',
+            name='session',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='notes.Session', verbose_name='Note|session'),
+        ),
+    ]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/notes/models/__init__.py	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,8 @@
+"""
+Models for notes
+"""
+from .auth import User, UserProfile, GroupProfile
+from .core import Note, Session
+from .category import Protocol, Category
+
+__all__ = ["User", "UserProfile", "GroupProfile", "Note", "Session", "Protocol", "Category"]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/notes/models/auth.py	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,27 @@
+"""
+auth module that defines irinotes' users and group
+"""
+from django.contrib.auth.models import AbstractUser, Group
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+
+
+class User(AbstractUser):
+    class Meta:
+        verbose_name = _('User')
+        verbose_name_plural = _('Users')
+
+class UserProfile(models.Model):
+    user = models.OneToOneField(User, on_delete=models.CASCADE)
+    class Meta:
+        verbose_name = _('UserProfile')
+        verbose_name_plural = _('UserProfiles')
+
+
+class GroupProfile(models.Model):
+    group = models.OneToOneField(Group, unique=True, on_delete=models.CASCADE)
+    description = models.TextField(null=True, blank=True)
+    class Meta:
+        verbose_name = _('GroupProfile')
+        verbose_name_plural = _('GroupProfiles')
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/notes/models/base.py	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,27 @@
+"""
+base abstract models
+"""
+import uuid
+
+from concurrency.fields import AutoIncVersionField
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+
+
+class ModelManager(models.Manager):
+    def get_by_natural_key(self, ext_id):
+        return self.get(ext_id=ext_id)
+
+class Model(models.Model):
+    objects = ModelManager()
+
+    created = models.DateTimeField(auto_now_add=True, verbose_name=_('Model|created'))
+    updated = models.DateTimeField(auto_now=True, verbose_name=_('Model|updated'))
+    ext_id = models.UUIDField(unique=True, default=uuid.uuid4, verbose_name=_('Model|ext_id'))
+    version = AutoIncVersionField(verbose_name=_('Model|version'))
+
+    def natural_key(self):
+        return (self.ext_id, )
+
+    class Meta:
+        abstract = True
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/notes/models/category.py	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,34 @@
+"""
+models for metacategories and protocol
+"""
+from colorful.fields import RGBColorField
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+
+from .base import Model
+from .auth import GroupProfile
+
+
+class Protocol(Model):
+    title = models.CharField(max_length=255, verbose_name=_('Protocol|title'))
+    group_profile = models.OneToOneField(GroupProfile, on_delete=models.CASCADE)
+    class Meta:
+        verbose_name = _('Protocol')
+        verbose_name_plural = _('Protocols')
+
+
+
+class Category(models.Model):
+    title = models.CharField(max_length=255, verbose_name=_('Category|title'))
+    color = RGBColorField(verbose_name=_('Category|color'))
+    need_comment = models.BooleanField(default=False, verbose_name=_('Category|need_comment'))
+    description = models.TextField(null=True, blank=True, verbose_name=_('Category|description'))
+    protocol = models.ForeignKey(
+        Protocol,
+        verbose_name=_('Category|protocol'),
+        related_name='categories',
+        on_delete=models.CASCADE
+    )
+    class Meta:
+        verbose_name = _('Category')
+        verbose_name_plural = _('Categories')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/notes/models/core.py	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,38 @@
+"""
+irinotes core module
+"""
+from django.conf import settings
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+
+from .base import Model
+
+
+class Session(Model):
+    class Meta:
+        verbose_name = _('Session')
+        verbose_name_plural = _('Sessions')
+
+    owner = models.ForeignKey(
+        settings.AUTH_USER_MODEL,
+        on_delete=models.CASCADE,
+    )
+    title = models.TextField(null=True, blank=True, verbose_name=_('Session|title'))
+    description = models.TextField(null=True, blank=True, verbose_name=_('Session|description'))
+    protocol = models.TextField(null=True, blank=True, verbose_name=_('Session|protocol'))
+
+class Note(Model):
+    class Meta:
+        verbose_name = _('Note')
+        verbose_name_plural = _('Notes')
+        ordering = ["tc_start"]
+
+    tc_start = models.DateTimeField(verbose_name=_('Note|tc_start'))
+    tc_end = models.DateTimeField(verbose_name=_('Note|tc_end'))
+    session = models.ForeignKey(Session, on_delete=models.CASCADE, verbose_name=_('Note|session'))
+    text_plain = models.TextField(null=True, blank=True, verbose_name=_('Note|text_plain'))
+    text_html = models.TextField(null=True, blank=True, verbose_name=_('Note|text_html'))
+    text_raw = models.TextField(null=True, blank=True, verbose_name=_('Note|text_raw'))
+    margin_note = models.TextField(null=True, blank=True, verbose_name=_('Note|margin_note'))
+    categorization = models.TextField(null=True, blank=True, verbose_name=_('Note|categorization'))
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/notes/tests.py	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/notes/views.py	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,3 @@
+from django.shortcuts import render
+
+# Create your views here.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/requirements/base.txt	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,11 @@
+dj-database-url==0.4.2
+Django==1.11.2
+django-colorful==1.2
+django-concurrency==1.3.2
+django-guardian==1.4.8
+djangorestframework==3.6.3
+irinotes==0.0.1
+python-decouple==3.0
+pytz==2017.2
+six==1.10.0
+Unipath==1.1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/requirements/base.txt.in	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,2 @@
+# must run "pip install -r base.txt.in" in src/requirements
+-e ..
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/requirements/dev.txt	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,1 @@
+-r base.txt
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/requirements/prod.txt	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,4 @@
+-r base.txt
+pylibmc
+uWSGI
+psycopg2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/setup.py	Thu Jun 08 17:57:57 2017 +0200
@@ -0,0 +1,156 @@
+import os
+try:
+    from setuptools import setup
+except ImportError:
+    from distutils.core import setup
+from distutils.command.install_data import install_data
+from distutils.command.install import INSTALL_SCHEMES
+import sys
+
+
+class osx_install_data(install_data):
+    """
+    On MacOS, the platform-specific lib dir is /System/Library/Framework/Python/.../
+    which is wrong. Python 2.5 supplied with MacOS 10.5 has an Apple-specific fix
+    for this in distutils.command.install_data#306. It fixes install_lib but not
+    install_data, which is why we roll our own install_data class.
+    """
+
+    def finalize_options(self):
+        """
+        By the time finalize_options is called, install.install_lib is set to the
+        fixed directory, so we set the installdir to install_lib. The
+        install_data class uses ('install_data', 'install_dir') instead.
+        """
+        self.set_undefined_options('install', ('install_lib', 'install_dir'))
+        install_data.finalize_options(self)
+
+def fullsplit(path, result=None):
+    """
+    Split a pathname into components (the opposite of os.path.join) in a
+    platform-neutral way.
+    """
+    if result is None:
+        result = []
+    head, tail = os.path.split(path)
+    if head == '':
+        return [tail] + result
+    if head == path:
+        return result
+    return fullsplit(head, [tail] + result)
+
+
+def launch_setup(setup_script_name, setup_script_args):
+    """
+    Start setup
+    """
+    if sys.platform == "darwin":
+        cmdclasses = {'install_data': osx_install_data}
+    else:
+        cmdclasses = {'install_data': install_data}
+
+
+    root_dir = os.path.dirname(__file__)
+    if root_dir != '':
+        os.chdir(root_dir)
+    source_dirs = ['irinotes', 'notes']
+
+    version_variables = {}
+    try:
+        with open(os.path.join(source_dirs[0], "__init__.py")) as f:
+            code = compile(f.read(), "__init__.py", 'exec')
+            exec(code, version_variables)
+    except:
+        pass
+
+    version = version_variables['__version__']
+
+    packages, data_files = [], []
+
+    for source_dir in source_dirs:
+        for dirpath, dirnames, filenames in os.walk(source_dir):
+            # Ignore dirnames that start with '.'
+            for i, dirname in enumerate(dirnames):
+                if dirname.startswith('.') or dirname.startswith('__pycache__'): del dirnames[i]
+            if '__init__.py' in filenames:
+                packages.append('.'.join(fullsplit(dirpath)))
+            elif filenames:
+                data_files.append([dirpath, [os.path.join(dirpath, f) for f in filenames]])
+
+
+    # Tell distutils to put the data_files in platform-specific installation
+    # locations. See here for an explanation:
+    # http://groups.google.com/group/comp.lang.python/browse_thread/thread/35ec7b2fed36eaec/2105ee4d9e8042cb
+    for scheme in INSTALL_SCHEMES.values():
+        scheme['data'] = scheme['purelib']
+
+    # Small hack for working with bdist_wininst.
+    # See http://mail.python.org/pipermail/distutils-sig/2004-August/004134.html
+    if len(sys.argv) > 1 and sys.argv[1] == 'bdist_wininst':
+        for file_info in data_files:
+            file_info[0] = '\\PURELIB\\%s' % file_info[0]
+
+    #write MANIFEST.in
+
+    with open("MANIFEST.in", "w") as m:
+        m.write("include CHANGES\n")
+        m.write("include LICENSE\n")
+        m.write("include README\n")
+        m.write("include MANIFEST.in\n")
+        m.write("include requirements/base.txt\n")
+        m.write("include requirements/dev.txt\n")
+        m.write("include requirements/prod.txt\n")
+        for entry in data_files:
+            file_list = entry[1]
+            for filename in file_list:
+                m.write("include %s\n" % (filename))
+
+    long_description = ''
+    with open('README', 'r') as f:
+        long_description = f.read()
+
+    setup(
+        script_name=setup_script_name,
+        script_args=setup_script_args,
+        name='irinotes',
+        version=version,
+        author='IRI',
+        author_email='contact@iri.centrepompidou.fr',
+        packages=packages,
+        data_files=data_files,
+        cmdclass=cmdclasses,
+        scripts=[],
+        url='http://www.iri.centrepompidou.fr/dev/hg/irinotes',
+        license='CECILL-C',
+        description='projet Irinotes',
+        long_description=long_description,
+        classifiers=[
+            'Development Status :: 4 - Beta',
+            'Environment :: Web Environment',
+            'Framework :: Django',
+            'Intended Audience :: Developers',
+            'License :: Ceccil-B',
+            'Operating System :: OS Independent',
+            'Programming Language :: Python',
+            'Topic :: Utilities'
+        ],
+        install_requires=[
+            "Django >= 1.11",
+            "python-decouple",
+            "Unipath",
+            "dj-database-url",
+            "six",
+            "djangorestframework >= 3.6",
+            "django-guardian >= 1.4",
+            "django-colorful",
+            "django-concurrency"
+        ],
+    )
+
+
+if __name__ == "__main__":
+
+    script_name = os.path.basename(sys.argv[0])
+    script_args = sys.argv[1:]
+
+    launch_setup(script_name, script_args)