bifurqué depuis cliss21/creme_center_fibois
init
révision
f64331e692
|
@ -0,0 +1,26 @@
|
|||
# http://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{py,rst,ini}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.py]
|
||||
line_length = 80
|
||||
|
||||
[*.{html,css,scss,js,json,yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
|
@ -0,0 +1,3 @@
|
|||
center_pilote/static/** -diff
|
||||
assets/img/** -diff
|
||||
assets/fonts/** -diff
|
|
@ -0,0 +1,42 @@
|
|||
# Editors
|
||||
*~
|
||||
*.sw[po]
|
||||
|
||||
# Python
|
||||
*.py[cod]
|
||||
__pycache__
|
||||
|
||||
# Virtual environment
|
||||
.env
|
||||
venv
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
pip-log.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
.coverage
|
||||
.tox
|
||||
nosetests.xml
|
||||
htmlcov
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Builds
|
||||
center_pilote/static/
|
||||
docs/build/
|
||||
|
||||
# Databases
|
||||
sqlite.db
|
||||
|
||||
# Local configuration
|
||||
config.env
|
||||
|
||||
# Local overrides and variable content
|
||||
local/
|
||||
var/
|
||||
_generated_media_names.py
|
||||
TAGS
|
|
@ -0,0 +1,8 @@
|
|||
# Changelog
|
||||
All notable changes to creme_center_fibois will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
|
@ -0,0 +1 @@
|
|||
This software is developped by Cliss XXI.
|
Fichier diff supprimé car celui-ci est trop grand
Voir la Diff
|
@ -0,0 +1,14 @@
|
|||
recursive-include center_pilote *.js
|
||||
recursive-include center_pilote *.css
|
||||
recursive-include center_pilote *.woff
|
||||
recursive-include center_pilote *.woff2
|
||||
recursive-include center_pilote *.ttf
|
||||
recursive-include center_pilote *.png
|
||||
recursive-include center_pilote *.jpg
|
||||
recursive-include center_pilote *.svg
|
||||
recursive-include center_pilote *.ico
|
||||
recursive-include center_pilote *.po
|
||||
recursive-include center_pilote *.mo
|
||||
recursive-include center_pilote *.txt
|
||||
recursive-include center_pilote *.html
|
||||
recursive-include center_pilote *.xml
|
|
@ -0,0 +1,151 @@
|
|||
# -*- mode: makefile-gmake -*-
|
||||
## Définition des variables
|
||||
# Le nom de l'exécutable Python à utiliser ou son chemin absolu
|
||||
# (ex. : python ou python3).
|
||||
PYTHON_EXE := python3
|
||||
# S'il faut utiliser un environnement virtuel (y ou n).
|
||||
USE_VENV := y
|
||||
# Configuration de l'environnement virtuel.
|
||||
VENV_DIR := venv
|
||||
VENV_OPT := --system-site-packages
|
||||
|
||||
# Définis les chemins et options des exécutables.
|
||||
PYTHON_EXE_BASENAME := $(shell basename $(PYTHON_EXE))
|
||||
VENV_PYTHON := --python=$(PYTHON_EXE_BASENAME)
|
||||
ifeq ($(USE_VENV), y)
|
||||
PYTHON := $(VENV_DIR)/bin/$(PYTHON_EXE_BASENAME)
|
||||
PIP := $(VENV_DIR)/bin/pip
|
||||
else
|
||||
PYTHON := $(shell which $(PYTHON_EXE))
|
||||
PIP := $(shell which pip)
|
||||
endif
|
||||
|
||||
# Détermine l'environnement à utiliser.
|
||||
ifndef ENV
|
||||
ifdef DJANGO_SETTINGS_MODULE
|
||||
ENV = $(shell echo $(DJANGO_SETTINGS_MODULE) | cut -d. -f3)
|
||||
else
|
||||
DEFAULT_ENV := production
|
||||
ENV = $(shell \
|
||||
sed -n '/^ENV/s/[^=]*=\(.*\)/\1/p' config.env 2> /dev/null \
|
||||
| tail -n 1 | grep -Ee '^..*' || echo "$(DEFAULT_ENV)")
|
||||
endif
|
||||
endif
|
||||
|
||||
# Définis EDITOR pour l'édition interactive.
|
||||
ifndef EDITOR
|
||||
ifdef VISUAL
|
||||
EDITOR := $(VISUAL)
|
||||
else
|
||||
EDITOR := vi
|
||||
endif
|
||||
endif
|
||||
|
||||
# Définition des cibles -------------------------------------------------------
|
||||
|
||||
.PHONY: clean-pyc clean-build clean-static clear-venv help check check-config
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
# Commentaire d'une cible : #-> interne ##-> aide production+dev ###-> aide dev
|
||||
help: ## affiche cette aide
|
||||
ifeq ($(ENV), production)
|
||||
@perl -nle'print $& if m{^[a-zA-Z_-]+:[^#]*?## .*$$}' $(MAKEFILE_LIST) \
|
||||
| sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-15s\033[0m %s\n", $$1, $$2}'
|
||||
else
|
||||
@perl -nle'print $& if m{^[a-zA-Z_-]+:[^#]*?###? .*$$}' $(MAKEFILE_LIST) \
|
||||
| sort | awk 'BEGIN {FS = ":.*?###? "}; {printf "\033[36m%-15s\033[0m %s\n", $$1, $$2}'
|
||||
endif
|
||||
|
||||
clean: clean-build clean-pyc clean-static ## nettoie tous les fichiers temporaires
|
||||
|
||||
clean-build: ### nettoie les fichiers de construction du paquet
|
||||
rm -rf build/
|
||||
rm -rf dist/
|
||||
rm -rf *.egg-info
|
||||
|
||||
clean-pyc: ### nettoie les fichiers temporaires python
|
||||
find center_pilote/ \
|
||||
\( -name '*.pyc' -o -name '*.pyo' -o -name '*~' \) -exec rm -f {} +
|
||||
|
||||
clean-static: ### nettoie les fichiers "static" collectés
|
||||
rm -rf var/static
|
||||
|
||||
init: create-venv config.env ## initialise l'environnement et l'application
|
||||
@$(MAKE) --no-print-directory update
|
||||
|
||||
config.env:
|
||||
cp config.env.example config.env
|
||||
chmod go-rwx config.env
|
||||
$(EDITOR) config.env
|
||||
|
||||
update: check-config install-deps migrate static populate ## mets à jour l'application et ses dépendances
|
||||
touch center_pilote/wsgi.py
|
||||
|
||||
check: check-config ## vérifie la configuration de l'instance
|
||||
$(PYTHON) manage.py check
|
||||
|
||||
check-config:
|
||||
@find . -maxdepth 1 -name config.env -perm /o+rwx -exec false {} + || \
|
||||
{ echo "\033[31mErreur :\033[0m les permissions de config.env ne sont pas bonnes, \
|
||||
vous devriez au moins faire : chmod o-rwx config.env"; false; }
|
||||
|
||||
install-deps: ## installe les dépendances de l'application
|
||||
$(PIP) install --upgrade --requirement requirements/$(ENV).txt
|
||||
|
||||
migrate: ## mets à jour le schéma de la base de données
|
||||
$(PYTHON) manage.py migrate
|
||||
|
||||
static: ## collecte les fichiers statiques
|
||||
@echo "Collecte des fichiers statiques..."
|
||||
$(PYTHON) manage.py generatemedia
|
||||
|
||||
populate: ## peuple la base de donnee
|
||||
$(PYTHON) manage.py creme_populate
|
||||
|
||||
## Cibles liées à l'environnement virtuel
|
||||
|
||||
create-venv: $(PYTHON)
|
||||
|
||||
$(PYTHON):
|
||||
ifeq ($(USE_VENV), y)
|
||||
virtualenv $(VENV_OPT) $(VENV_PYTHON) $(VENV_DIR)
|
||||
else
|
||||
@echo "\033[31mErreur !\033[0m Impossible de trouver l'exécutable Python $(PYTHON)"
|
||||
@exit 1
|
||||
endif
|
||||
|
||||
clear-venv: ## supprime l'environnement virtuel
|
||||
-rm -rf $(VENV_DIR)
|
||||
|
||||
## Cibles pour le développement
|
||||
|
||||
serve: ### démarre un serveur local pour l'application
|
||||
$(PYTHON) manage.py runserver
|
||||
|
||||
test: ### lance les tests de l'application
|
||||
$(PYTHON) -m pytest --cov --cov-report=term:skip-covered
|
||||
|
||||
test-wip: #### lance les tests marqués 'wip'
|
||||
$(PYTHON) -m pytest -vv -m 'wip' --pdb
|
||||
|
||||
test-failed: #### lance les tests qui ont échoué
|
||||
$(PYTHON) -m pytest --lf
|
||||
|
||||
coverage: test ### vérifie la couverture de code
|
||||
$(PYTHON) -m coverage html
|
||||
@echo open htmlcov/index.html
|
||||
|
||||
lint: ### vérifie la syntaxe du code Python
|
||||
@$(PYTHON) -m flake8 center_pilote || \
|
||||
{ echo "\033[31mErreur !\033[0m Veuillez corriger la syntaxe avec : make format"; false; }
|
||||
|
||||
format: ### formate le code Python
|
||||
$(PYTHON) -m isort center_pilote
|
||||
$(PYTHON) -m black center_pilote
|
||||
|
||||
shell: ### lance un shell Python dans l'environnement
|
||||
ifeq ($(ENV), production)
|
||||
$(PYTHON) manage.py shell
|
||||
else
|
||||
$(PYTHON) manage.py shell_plus
|
||||
endif
|
|
@ -0,0 +1,154 @@
|
|||
# creme_center_fibois
|
||||
|
||||
CremeCRM pour Fibois
|
||||
|
||||
**Table of content**
|
||||
|
||||
- [Give a try](#give-a-try)
|
||||
- [Installation](#installation)
|
||||
- [Deployment](#deployment)
|
||||
- [Structure](#structure)
|
||||
- [Development](#development)
|
||||
|
||||
## Give a try
|
||||
|
||||
On a Debian-based host - running at least Debian Stretch:
|
||||
|
||||
```shell
|
||||
$ sudo apt install python3 virtualenv git make
|
||||
$ git clone https://forge.cliss21.org/cliss21/creme_center_fibois
|
||||
$ cd creme_center_fibois/
|
||||
|
||||
$ make init
|
||||
# A configuration file will be created interactively; you can uncomment:
|
||||
# ENV=development
|
||||
|
||||
$ make serve
|
||||
```
|
||||
|
||||
Then visit [http://127.0.0.1:8000/](http://127.0.0.1:8000/) in your web browser.
|
||||
|
||||
## Installation
|
||||
### Requirements
|
||||
|
||||
On a Debian-based host - running at least Debian Stretch, you will need the
|
||||
following packages:
|
||||
- `python3`
|
||||
- `virtualenv`
|
||||
- `make`
|
||||
- `git` (recommended for getting the source)
|
||||
- `python3-mysqldb` (optional, in case of a MySQL / MariaDB database)
|
||||
- `python3-psycopg2` (optional, in case of a PostgreSQL database)
|
||||
|
||||
### Quick start
|
||||
|
||||
It assumes that you already have the application source code locally - the best
|
||||
way is by cloning this repository - and that you are in this folder.
|
||||
|
||||
1. Define your local configuration in a file named `config.env`, which can be
|
||||
copied from `config.env.example` and edited to suits your needs.
|
||||
|
||||
Depending on your environment, you will have to create your database and the
|
||||
user at first.
|
||||
|
||||
2. Run `make init`.
|
||||
|
||||
Note that if there is no `config.env` file, it will be created interactively.
|
||||
|
||||
That's it! Your environment is now initialized with the application installed.
|
||||
To update it, once the source code is checked out, simply run `make update`.
|
||||
|
||||
You can also check that your application is well configured by running
|
||||
`make check`.
|
||||
|
||||
## Deployment
|
||||
### Web application
|
||||
|
||||
Here is an example deployment using NGINX - as the Web server - and uWSGI - as
|
||||
the application server.
|
||||
|
||||
#### uWSGI
|
||||
|
||||
The uWSGI configuration doesn't require a special configuration, except that we
|
||||
are using Python 3 and a virtual environment. Note that if you serve the
|
||||
application on a sub-location, you will have to add `route-run = fixpathinfo:`
|
||||
to your uWSGI configuration (available since [v2.0.11][1]).
|
||||
|
||||
[1]: https://uwsgi-docs.readthedocs.io/en/latest/Changelog-2.0.11.html#fixpathinfo-routing-action
|
||||
|
||||
#### NGINX
|
||||
|
||||
In the `server` block of your NGINX configuration, add the following blocks and
|
||||
set the path to your application instance and to the uWSGI socket:
|
||||
|
||||
```nginx
|
||||
location / {
|
||||
include uwsgi_params;
|
||||
uwsgi_pass unix:<uwsgi_socket_path>;
|
||||
}
|
||||
location /media {
|
||||
alias <app_instance_path>/var/media;
|
||||
}
|
||||
location /static {
|
||||
alias <app_instance_path>/var/static;
|
||||
# Optional: don't log access to assets
|
||||
access_log off;
|
||||
}
|
||||
location = /favicon.ico {
|
||||
alias <app_instance_path>/var/static/favicon/favicon.ico;
|
||||
# Optional: don't log access to the favicon
|
||||
access_log off;
|
||||
}
|
||||
```
|
||||
|
||||
## Structure
|
||||
### Overview
|
||||
|
||||
All the application files - e.g. Django code including settings, templates and
|
||||
statics - are located into `creme_center_fibois/`.
|
||||
|
||||
Two environments are defined - either for requirements and settings:
|
||||
- `development`: for local application development and testing. It uses a
|
||||
SQLite3 database and enable debugging by default, add some useful settings
|
||||
and applications for development purpose - i.e. the `django-debug-toolbar`.
|
||||
- `production`: for production. It checks that configuration is set and
|
||||
correct, try to optimize performances and enforce some settings - i.e. HTTPS
|
||||
related ones.
|
||||
|
||||
### Local changes
|
||||
|
||||
You can override and extend statics and templates locally. This can be useful
|
||||
if you have to change the logo for a specific instance for example. For that,
|
||||
just put your files under the `local/static/` and `local/templates/` folders.
|
||||
|
||||
Regarding the statics, do not forget to collect them after that. Note also that
|
||||
the `local/` folder is ignored by *git*.
|
||||
|
||||
### Variable content
|
||||
|
||||
All the variable content - e.g. user-uploaded media, collected statics - are
|
||||
stored inside the `var/` folder. It is also ignored by *git* as it's specific
|
||||
to each application installation.
|
||||
|
||||
So, you will have to configure your Web server to serve the `var/media/` and
|
||||
`var/static/` folders, which should point to `/media/` and `/static/`,
|
||||
respectively.
|
||||
|
||||
## Development
|
||||
|
||||
The easiest way to deploy a development environment is by using the `Makefile`.
|
||||
|
||||
Before running `make init`, ensure that you have either set `ENV=development`
|
||||
in the `config.env` file or have this environment variable. Note that you can
|
||||
still change this variable later and run `make init` again.
|
||||
|
||||
There is some additional rules when developing, which are mainly wrappers for
|
||||
`manage.py`. You can list all of them by running `make help`. Here are the main ones:
|
||||
- `make serve`: run a development server
|
||||
- `make test`: test the whole application
|
||||
- `make lint`: check the Python code syntax
|
||||
|
||||
## License
|
||||
|
||||
creme_center_fibois is developed by Cliss XXI and licensed under the
|
||||
[AGPLv3+](LICENSE).
|
|
@ -0,0 +1 @@
|
|||
__version__ = '0.1.0'
|
|
@ -0,0 +1 @@
|
|||
default_app_config = 'center_pilote.bleach.apps.BleachConfig'
|
|
@ -0,0 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from creme.creme_core.apps import CremeAppConfig
|
||||
|
||||
|
||||
class BleachConfig(CremeAppConfig):
|
||||
name = 'center_pilote.bleach'
|
||||
verbose_name = "Nettoyage"
|
|
@ -0,0 +1,8 @@
|
|||
from creme.creme_core.management.commands.creme_populate import BasePopulator
|
||||
|
||||
|
||||
class Populator(BasePopulator):
|
||||
dependencies = ['creme_core', 'persons', 'pilote_extended']
|
||||
|
||||
def populate(self):
|
||||
pass
|
|
@ -0,0 +1 @@
|
|||
urlpatterns = []
|
|
@ -0,0 +1,27 @@
|
|||
# -*- mode: python -*-
|
||||
from django_fieldbustier.fieldbustier_config import DeleteFieldBustierConfig, FieldBustierConfig
|
||||
|
||||
|
||||
ADD_FIELDS_<MODEL> = FieldBustierConfig(
|
||||
"app_name",
|
||||
"model_klass",
|
||||
"field_name",
|
||||
"field_klass",
|
||||
"list: args",
|
||||
"dict: kwargs",
|
||||
)
|
||||
|
||||
REPLACE_FIELDS_<MODEL> = FieldBustierConfig(
|
||||
"app_name",
|
||||
"model_klass",
|
||||
"field_name",
|
||||
"field_klass",
|
||||
"list: args",
|
||||
"dict: kwargs",
|
||||
)
|
||||
|
||||
DELETE_FIELDS_<MODEL> = DeleteFieldBustierConfig(
|
||||
"app_name",
|
||||
"model_klass",
|
||||
"field_name",
|
||||
)
|
|
@ -0,0 +1,195 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import csv
|
||||
import logging
|
||||
|
||||
from django.forms import ValidationError
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BaseProcessCsv:
|
||||
form_klass = None
|
||||
models_builders = []
|
||||
built_models = None
|
||||
stats = {
|
||||
'err_line': 0,
|
||||
'line': 0,
|
||||
'err_model': 0,
|
||||
}
|
||||
|
||||
def __init__(self, filename, dryrun=True, noheader=False):
|
||||
# internals
|
||||
self.dryrun = dryrun
|
||||
self.noheader = noheader
|
||||
self.filename = filename
|
||||
if self.form_klass is None or not self.models_builders:
|
||||
raise NotImplementedError(
|
||||
"You need to provide property: form_klass and models_builders"
|
||||
)
|
||||
self.fieldnames = self.form_klass().fields.keys()
|
||||
|
||||
def run(self):
|
||||
with open(self.filename, 'r') as csvfile:
|
||||
csvreader = csv.reader(csvfile, delimiter=',', quotechar='"')
|
||||
logger.info(f"### Process {self.filename}")
|
||||
if not self.noheader:
|
||||
next(csvreader)
|
||||
for linenb, row in enumerate(csvreader):
|
||||
self.linenb = linenb + 1 + int(not self.noheader)
|
||||
self.row = row
|
||||
self._process_row()
|
||||
|
||||
def _process_row(self):
|
||||
if self.get_cleaned_data_from_row() is None:
|
||||
return
|
||||
try:
|
||||
self._build_simple_models()
|
||||
except ValidationError:
|
||||
self.stats['err_model'] += 1
|
||||
else:
|
||||
self.process_built_model()
|
||||
|
||||
def get_cleaned_data_from_row(self):
|
||||
"""
|
||||
get cleaned data from row using form_class
|
||||
"""
|
||||
self.stats['line'] += 1
|
||||
form = self.form_klass(
|
||||
{k: v for k, v in zip(self.fieldnames, self.row)}
|
||||
)
|
||||
if not form.is_valid():
|
||||
logger.warning(f"{self.linenb:4}:X {repr(form.errors)}")
|
||||
self.stats['err_line'] += 1
|
||||
self.cleaned_data = None
|
||||
else:
|
||||
self.cleaned_data = form.cleaned_data
|
||||
return self.cleaned_data
|
||||
|
||||
def _build_simple_models(self):
|
||||
"""
|
||||
for each model in models_builder, get an existing instance or
|
||||
create a new one
|
||||
"""
|
||||
self.built_models = {
|
||||
model_builder.model.__name__: model_builder(
|
||||
self.cleaned_data, self.linenb, self.dryrun
|
||||
).process()
|
||||
for model_builder in self.models_builders
|
||||
}
|
||||
|
||||
def process_built_model(self):
|
||||
"""
|
||||
a place to handle post save treatments that need several models
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class BaseModelBuilder:
|
||||
model = None
|
||||
instance = None
|
||||
simple_fill_instance_list = None
|
||||
constant_fill_instance_dict = None
|
||||
count = 0
|
||||
dryrun = True
|
||||
linenb = 0
|
||||
|
||||
def __init__(self, cleaned_data, linenb, dryrun=True):
|
||||
self.cleaned_data = cleaned_data
|
||||
self.linenb = linenb
|
||||
self.dryrun = dryrun
|
||||
if self.model is None:
|
||||
raise NotImplementedError("model is not defined")
|
||||
|
||||
def get_key_id(self):
|
||||
"""
|
||||
return a dictionnary that will be used to lookup
|
||||
existing model in DB. For example:
|
||||
{'name': self.cleaned_data['name'],
|
||||
'billing_address__zipcode': self.cleaned_data['zipcode']}
|
||||
"""
|
||||
pass
|
||||
|
||||
def _get_existing_instance(self):
|
||||
"""
|
||||
return existing model in base or None
|
||||
"""
|
||||
key_id = self.get_key_id()
|
||||
qs = self.model.objects.filter(**key_id)
|
||||
self.count = qs.count()
|
||||
if self.count == 1:
|
||||
logger.info(
|
||||
f"{self.linenb:4}:u "
|
||||
f"{self.model.__name__}({key_id}) found in base"
|
||||
)
|
||||
self.instance = qs.first()
|
||||
elif self.count == 0:
|
||||
logger.info(
|
||||
f"{self.linenb:4}:c "
|
||||
f"no object found for {self.model.__name__}({key_id})"
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
f"{self.linenb:4}:X "
|
||||
f"{self.count} objects found "
|
||||
f"for {self.model.__name__}({key_id})"
|
||||
)
|
||||
|
||||
def simple_fill_instance(self):
|
||||
"""
|
||||
helper that fill instance with a subset list of field that match
|
||||
both cleaned_data and model instance
|
||||
"""
|
||||
return {
|
||||
key: value
|
||||
for key, value in self.cleaned_data.items()
|
||||
if key in self.simple_fill_instance_list
|
||||
}
|
||||
|
||||
def constant_fill_instance(self):
|
||||
"""
|
||||
helper that fill instance with constant
|
||||
"""
|
||||
if self.constant_fill_instance_dict is None:
|
||||
return {}
|
||||
return self.constant_fill_instance_dict
|
||||
|
||||
def complex_fill_instance(self):
|
||||
"""
|
||||
helper that fill instance with more complex logic
|
||||
"""
|
||||
return {}
|
||||
|
||||
def fill_instance(self):
|
||||
"""
|
||||
fill the instance with the rest of cleaned_data
|
||||
"""
|
||||
# don't touch existing data
|
||||
constant_fill = self.constant_fill_instance()
|
||||
simple_fill = self.simple_fill_instance()
|
||||
complex_fill = self.complex_fill_instance()
|
||||
merged_fill = {**constant_fill, **simple_fill, **complex_fill}
|
||||
self.instance = self.model(**merged_fill)
|
||||
return self.instance
|
||||
|
||||
def post_save(self):
|
||||
"""
|
||||
populate M2M and thing that need a saved instance in base
|
||||
"""
|
||||
pass
|
||||
|
||||
def process(self):
|
||||
self._get_existing_instance()
|
||||
if self.count == 0:
|
||||
self.fill_instance()
|
||||
try:
|
||||
self.instance.full_clean()
|
||||
except ValidationError as err:
|
||||
logger.warning(
|
||||
f"{self.linenb:4}:X" f"{self.model.__name__} {err}"
|
||||
)
|
||||
raise err
|
||||
else:
|
||||
if not self.dryrun:
|
||||
self.instance.save()
|
||||
self.post_save()
|
||||
return self
|
|
@ -0,0 +1,36 @@
|
|||
from django.forms.fields import CharField
|
||||
|
||||
|
||||
class TruncatedCharField(CharField):
|
||||
def to_python(self, value):
|
||||
return super().to_python(value)[: self.max_length]
|
||||
|
||||
|
||||
class CharToForeignKeyId(CharField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
try:
|
||||
model = kwargs.pop('model')
|
||||
dbfield = kwargs.pop('dbfield')
|
||||
except KeyError:
|
||||
raise NotImplementedError("model and dbfield kwargs is required")
|
||||
self.mtx = dict(model.objects.values_list(dbfield, 'id'))
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def to_python(self, value):
|
||||
value = super().to_python(value)
|
||||
value = self.mtx.get(value, None)
|
||||
if value is not None:
|
||||
return str(value)
|
||||
return None
|
||||
|
||||
|
||||
class CharToChar(CharField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.func = kwargs.pop('func', lambda x: x)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def to_python(self, value):
|
||||
value = self.func(super().to_python(value))
|
||||
if value is not None:
|
||||
return str(value)
|
||||
return None
|
|
@ -0,0 +1,24 @@
|
|||
from django.core.management.base import BaseCommand
|
||||
|
||||
from center_pilote.moulinette import my
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'import csv file into the CRM'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('file', nargs=None, type=str)
|
||||
parser.add_argument('--dryrun', default=False, action='store_true')
|
||||
parser.add_argument('--no-header', default=False, action='store_true')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
process_csv = my.ProcessCsv(
|
||||
options['file'], options['dryrun'], options['no_header']
|
||||
)
|
||||
process_csv.run()
|
||||
stt = process_csv.stats
|
||||
print(
|
||||
f"Stats: total lines: {stt['line']}"
|
||||
f" csv errors: {stt['err_line']}"
|
||||
f" model errors: {stt['err_model']}"
|
||||
)
|
|
@ -0,0 +1,22 @@
|
|||
from django.core.management.base import BaseCommand
|
||||
|
||||
from center_pilote.moulinette import my
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'show expected csv file header'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
form_klass = getattr(my.ProcessCsv, 'form_klass', None)
|
||||
if form_klass is None:
|
||||
raise NotImplementedError(
|
||||
"You need to provide property form_klass"
|
||||
)
|
||||
fields = form_klass().fields
|
||||
if options['verbosity'] > 1:
|
||||
fieldsname = [
|
||||
f"{k}({type(v).__name__})" for k, v in fields.items()
|
||||
]
|
||||
else:
|
||||
fieldsname = fields.keys()
|
||||
print(','.join(fieldsname))
|
|
@ -0,0 +1,37 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from django.forms import Form, fields
|
||||
|
||||
from creme.creme_core.models import Relation
|
||||
from creme.persons import (
|
||||
get_address_model,
|
||||
get_contact_model,
|
||||
get_organisation_model,
|
||||
)
|
||||
from creme.persons.constants import REL_SUB_MANAGES
|
||||
|
||||
from center_pilote.info_tables.models import Epci
|
||||
|
||||
from .base import BaseModelBuilder, BaseProcessCsv
|
||||
from .fields import CharToForeignKeyId, TruncatedCharField
|
||||
|
||||
|
||||
class ImportForm(Form):
|
||||
"""
|
||||
déclarer ici les champs venant du fichier CSV
|
||||
dans le meme ordre
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class MyModelBuilder(BaseModelBuilder):
|
||||
model = None # model in use
|
||||
simple_fill_instance_list = None # direct field assignement
|
||||
constant_fill_instance_dict = None # constant field assignement
|
||||
def get_key_id(self):
|
||||
"return a dict to get existing instance in base"
|
||||
pass
|
||||
|
||||
|
||||
class ProcessCsv(BaseProcessCsv):
|
||||
form_klass = ImportForm
|
||||
models_builders = None
|
|
@ -0,0 +1 @@
|
|||
urlpatterns = []
|
|
@ -0,0 +1 @@
|
|||
default_app_config = 'center_pilote.pilote_extended.apps.PiloteExtendedConfig'
|
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class PiloteExtendedConfig(AppConfig):
|
||||
name = 'center_pilote.pilote_extended'
|
||||
verbose_name = "Pilote Extented"
|
|
@ -0,0 +1 @@
|
|||
/* custom CSS for chantilly theme */
|
|
@ -0,0 +1 @@
|
|||
/* custom CSS for icecream theme */
|
|
@ -0,0 +1 @@
|
|||
urlpatterns = []
|
|
@ -0,0 +1,24 @@
|
|||
import environ
|
||||
|
||||
"""The default environment to use."""
|
||||
DEFAULT_ENVIRONMENT = 'production'
|
||||
|
||||
"""The environment variables of the app instance."""
|
||||
env = environ.Env()
|
||||
|
||||
"""Path to the package root - e.g. Django project."""
|
||||
root_dir = environ.Path(__file__) - 2
|
||||
|
||||
"""Path to the base directory of the app instance."""
|
||||
base_dir = env.path('BASE_DIR', default=str(root_dir - 1))
|
||||
|
||||
# Load config.env, OS environment variables will take precedence
|
||||
env.read_env(str(base_dir.path('config.env')))
|
||||
|
||||
"""The Django settings module's name to use."""
|
||||
DJANGO_SETTINGS_MODULE = env(
|
||||
'DJANGO_SETTINGS_MODULE',
|
||||
default='center_pilote.settings.{}'.format(
|
||||
env('ENV', default=DEFAULT_ENVIRONMENT)
|
||||
),
|
||||
)
|
|
@ -0,0 +1,242 @@
|
|||
"""
|
||||
Django settings for creme_center_fibois project.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/stable/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/stable/ref/settings/
|
||||
"""
|
||||
import os.path
|
||||
import sys
|
||||
from email.utils import getaddresses
|
||||
|
||||
from creme.settings import * # NOQA
|
||||
from creme.settings import CREME_OPT_CSS, INSTALLED_DJANGO_APPS, TEMPLATES
|
||||
|
||||
from . import base_dir, env, root_dir
|
||||
|
||||
if sys.warnoptions:
|
||||
import warnings
|
||||
|
||||
warnings.resetwarnings()
|
||||
for arg in sys.warnoptions:
|
||||
warnings._setoption(arg)
|
||||
|
||||
# ENVIRONMENT VARIABLES AND PATHS
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Local directory used for static and templates overrides
|
||||
local_dir = base_dir.path('local')
|
||||
|
||||
# Directory for variable stuffs, i.e. user-uploaded media
|
||||
var_dir = base_dir.path('var')
|
||||
if not os.path.isdir(var_dir()):
|
||||
os.mkdir(var_dir(), mode=0o755)
|
||||
|
||||
# Location on which the application is served
|
||||
APP_LOCATION = env('APP_LOCATION', default='/')
|
||||
|
||||
# GENERAL
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#debug
|
||||
DEBUG = env.bool('DJANGO_DEBUG', default=True)
|
||||
|
||||
# Local time zone for this installation
|
||||
TIME_ZONE = 'Europe/Paris'
|
||||
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#language-code
|
||||
LANGUAGE_CODE = 'fr'
|
||||
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#site-id
|
||||
SITE_ID = 1
|
||||
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#use-i18n
|
||||
USE_I18N = True
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#use-l10n
|
||||
USE_L10N = False
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#use-tz
|
||||
USE_TZ = True
|
||||
|
||||
# DATABASES
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#databases
|
||||
# https://django-environ.readthedocs.io/en/stable/#supported-types
|
||||
DATABASES = {
|
||||
'default': env.db(
|
||||
'DJANGO_DATABASE_URL',
|
||||
default='sqlite:///{}'.format(base_dir('sqlite.db')),
|
||||
)
|
||||
}
|
||||
|
||||
# URLS
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#root-urlconf
|
||||
# ROOT_URLCONF already defined by creme
|
||||
ROOT_URLCONF = 'center_pilote.urls'
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#wsgi-application
|
||||
WSGI_APPLICATION = 'center_pilote.wsgi.application'
|
||||
|
||||
# APP CONFIGURATION
|
||||
# ------------------------------------------------------------------------------
|
||||
# INSTALLED_DJANGO_APPS already defined by creme
|
||||
|
||||
# Project applications
|
||||
LOCAL_APPS = ['django_fieldbustier', 'center_pilote.pilote_extended']
|
||||
|
||||
INSTALLED_CREME_APPS = [
|
||||
# CREME CORE APPS
|
||||
'creme.creme_core',
|
||||
'creme.creme_config',
|
||||
'creme.documents',
|
||||
'creme.activities', # Extra features if 'assistants' is installed.
|
||||
'creme.persons',
|
||||
# CREME OPTIONAL APPS (can be safely commented)
|
||||
'creme.assistants',
|
||||
'creme.graphs',
|
||||
'creme.reports',
|
||||
'creme.products',
|
||||
'creme.recurrents',
|
||||
'creme.billing', # Need 'products'
|
||||
'creme.opportunities', # Need 'products'. Extra features with 'billing'
|
||||
'creme.commercial', # Need 'opportunities'
|
||||
'creme.events', # Need 'opportunities'
|
||||
'creme.crudity',
|
||||
'creme.emails', # Extra features if 'crudity' is installed.
|
||||
# 'creme.sms', # Work In Progress
|
||||
'creme.projects',
|
||||
'creme.tickets',
|
||||
# 'creme.cti',
|
||||
'creme.vcfs',
|
||||
# 'creme.polls', # Need 'commercial'
|
||||
# 'creme.mobile',
|
||||
'creme.geolocation',
|
||||
'center_pilote.bleach',
|
||||
]
|
||||
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#installed-apps
|
||||
INSTALLED_APPS = INSTALLED_DJANGO_APPS + LOCAL_APPS + INSTALLED_CREME_APPS
|
||||
|
||||
|
||||
# PASSWORDS
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#password-hashers
|
||||
PASSWORD_HASHERS = [
|
||||
# https://docs.djangoproject.com/en/stable/topics/auth/passwords/#using-argon2-with-django
|
||||
# 'django.contrib.auth.hashers.Argon2PasswordHasher',
|
||||
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
|
||||
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
|
||||
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
|
||||
'django.contrib.auth.hashers.BCryptPasswordHasher',
|
||||
]
|
||||
|
||||
# https://docs.djangoproject.com/en/stable/topics/auth/passwords/#password-validation
|
||||
# AUTH_PASSWORD_VALIDATORS # already defined by creme
|
||||
|
||||
# MIDDLEWARE
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#middleware
|
||||
# MIDDLEWARE # already defined by creme
|
||||
|
||||
# STATIC
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#static-files
|
||||
STATIC_ROOT = var_dir('static')
|
||||
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#static-url
|
||||
STATIC_URL = os.path.join(APP_LOCATION, 'static_media/')
|
||||
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#staticfiles-dirs
|
||||
STATICFILES_DIRS = [root_dir('static')]
|
||||
if os.path.isdir(local_dir('static')):
|
||||
STATICFILES_DIRS.insert(0, local_dir('static'))
|
||||
|
||||
# https://docs.djangoproject.com/en/stable/ref/contrib/staticfiles/#staticfiles-finders
|
||||
STATICFILES_FINDERS = [
|
||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||
]
|
||||
|
||||
# MEDIA
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#media-root
|
||||
# MEDIA_ROOT = var_dir('media') # already defined by creme
|
||||
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#media-url
|
||||
# MEDIA_URL = os.path.join(APP_LOCATION, 'media/') # already defined by creme
|
||||
|
||||
# Optional js/css bundles for extending projects.
|
||||
# Beware to clashes with existing bundles ('main.js', 'l10n.js').
|
||||
|
||||
CREME_OPT_CSS += [
|
||||
(
|
||||
'center_pilote.pilote_extended',
|
||||
'pilote_extended/css/pilote_extended.css',
|
||||
),
|
||||
]
|
||||
GENERATED_MEDIA_DIR = var_dir('static/')
|
||||
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#file-upload-directory-permissions
|
||||
FILE_UPLOAD_DIRECTORY_PERMISSIONS = 0o755
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#file-upload-permissions
|
||||
FILE_UPLOAD_PERMISSIONS = 0o644
|
||||
|
||||
# TEMPLATES
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#templates
|
||||
# TEMPLATES # already defined by creme
|
||||
TEMPLATES[0]['DIRS'].insert(0, root_dir('customer_templates'))
|
||||
if os.path.isdir(local_dir('templates')):
|
||||
TEMPLATES[0]['DIRS'].insert(0, local_dir('templates'))
|
||||
|
||||
# FIXTURES
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#fixture-dirs
|
||||
FIXTURE_DIRS = [root_dir('fixtures')]
|
||||
|
||||
# EMAIL
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/stable/topics/email/#email-backends
|
||||
# https://django-environ.readthedocs.io/en/stable/#supported-types
|
||||
vars().update(env.email_url('DJANGO_EMAIL_URL', default='smtp://localhost:25'))
|
||||
|
||||
DEFAULT_FROM_EMAIL = env('DEFAULT_FROM_EMAIL', default='webmaster@localhost')
|
||||
# Use the same email address for error messages
|
||||
SERVER_EMAIL = DEFAULT_FROM_EMAIL
|
||||
|
||||
# ADMIN
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#admins
|
||||
ADMINS = getaddresses([env('ADMINS', default='Cliss XXI <tech@cliss21.com>')])
|
||||
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#managers
|
||||
MANAGERS = ADMINS
|
||||
|
||||
# SESSIONS AND COOKIES
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#session-cookie-path
|
||||
SESSION_COOKIE_PATH = APP_LOCATION
|
||||
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#csrf-cookie-path
|
||||
CSRF_COOKIE_PATH = APP_LOCATION
|
||||
|
||||
# FIELDBUSTIER
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://github.com/mrjmad/django-fieldbustier
|
||||
# Set to True if you want fieldbustier to generate migrations
|
||||
GENERATE_FIELDBUSTIER_MIGRATIONS = False
|
||||
# where to store fieldbustier migrations, remember to
|
||||
# 1. create python module (create directory and __init__.py within)
|
||||
# 2. copy migrations from original django application
|
||||
# 3. hack model with fieldbustier then ./manage makemigrations ...
|
||||
# copy corresponding migrations from original
|
||||
# MIGRATION_MODULES = {'persons': 'center_pilote.creme_migrations.persons'}
|
||||
|
||||
ADD_FIELD_DJANGO_FIELDBUSTIER = []
|
||||
REPLACE_FIELD_DJANGO_FIELDBUSTIER = []
|
||||
DELETE_FIELD_DJANGO_FIELDBUSTIER = []
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# APPLICATION AND 3RD PARTY LIBRARY SETTINGS
|
||||
# ------------------------------------------------------------------------------
|
|
@ -0,0 +1,50 @@
|
|||
"""
|
||||
Development settings.
|
||||
|
||||
- use Console backend for emails sending by default
|
||||
- add the django-debug-toolbar
|
||||
"""
|
||||
from .base import * # noqa
|
||||
from .base import INSTALLED_APPS, MIDDLEWARE, TEMPLATES, env
|
||||
|
||||
# GENERAL
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#secret-key
|
||||
SECRET_KEY = env('DJANGO_SECRET_KEY', default='CHANGEME!!!')
|
||||
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#allowed-hosts
|
||||
ALLOWED_HOSTS = env.list(
|
||||
'DJANGO_ALLOWED_HOSTS', default=['localhost', '0.0.0.0', '127.0.0.1']
|
||||
)
|
||||
|
||||
TEMPLATES[0]['OPTIONS']['loaders'] = (
|
||||
'django.template.loaders.app_directories.Loader',
|
||||
'django.template.loaders.filesystem.Loader',
|
||||
)
|
||||
|
||||
# EMAIL
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/stable/topics/email/#email-backends
|
||||
# https://django-environ.readthedocs.io/en/stable/#supported-types
|
||||
vars().update(env.email_url('DJANGO_EMAIL_URL', default='consolemail://'))
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# APPLICATION AND 3RD PARTY LIBRARY SETTINGS
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# DJANGO DEBUG TOOLBAR
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://django-debug-toolbar.readthedocs.io/en/stable/installation.html
|
||||
if env.bool('DJANGO_DEBUG_TOOLBAR', default=False):
|
||||
MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware']
|
||||
INSTALLED_APPS += ['debug_toolbar']
|
||||
INTERNAL_IPS = ['127.0.0.1']
|
||||
DEBUG_TOOLBAR_CONFIG = {
|
||||
'DISABLE_PANELS': ['debug_toolbar.panels.redirects.RedirectsPanel'],
|
||||
'SHOW_TEMPLATE_CONTEXT': True,
|
||||
}
|
||||
|
||||
# DJANGO EXTENSIONS
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://django-extensions.readthedocs.io/en/stable/index.html
|
||||
INSTALLED_APPS += ['django_extensions']
|
|
@ -0,0 +1,106 @@
|
|||
"""
|
||||
Production settings.
|
||||
|
||||
- validate the configuration
|
||||
- disable debug mode
|
||||
- load secret key from environment variables
|
||||
- set other production configurations
|
||||
"""
|
||||
import os
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
from .base import * # noqa
|
||||
from .base import TEMPLATES, env, var_dir
|
||||
|
||||
# CONFIGURATION VALIDATION
|
||||
# ------------------------------------------------------------------------------
|
||||
# Ensure that the database configuration has been set
|
||||
if not env('DJANGO_DATABASE_URL', default=None):
|
||||
raise ImproperlyConfigured(
|
||||
"No database configuration has been set, you should check "
|
||||
"the value of your DATABASE_URL environment variable."
|
||||
)
|
||||
|
||||
# Ensure that the default email address has been set
|
||||
if not env('DEFAULT_FROM_EMAIL', default=None):
|
||||
raise ImproperlyConfigured(
|
||||
"No default email address has been set, you should check "
|
||||
"the value of your DEFAULT_FROM_EMAIL environment variable."
|
||||
)
|
||||
|
||||
# GENERAL
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#debug
|
||||
DEBUG = False
|
||||
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#secret-key
|
||||
SECRET_KEY = env('DJANGO_SECRET_KEY')
|
||||
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#allowed-hosts
|
||||
ALLOWED_HOSTS = env.list('DJANGO_ALLOWED_HOSTS', default=[])
|
||||
|
||||
# TEMPLATES
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#templates
|
||||
TEMPLATES[0]['OPTIONS']['debug'] = DEBUG
|
||||
TEMPLATES[0]['OPTIONS']['loaders'] = [
|
||||
(
|
||||
'django.template.loaders.cached.Loader',
|
||||
[
|
||||
'django.template.loaders.filesystem.Loader',
|
||||
'django.template.loaders.app_directories.Loader',
|
||||
],
|
||||
)
|
||||
]
|
||||
|
||||
# LOGGING
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/stable/topics/logging/
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'formatters': {
|
||||
'verbose': {
|
||||
'format': '%(asctime)s - %(levelname)s - %(module)s: %(message)s'
|
||||
}
|
||||
},
|
||||
'handlers': {
|
||||
'mail_admins': {
|
||||
'level': 'ERROR',
|
||||
'class': 'django.utils.log.AdminEmailHandler',
|
||||
},
|
||||
'file': {
|
||||
'level': 'DEBUG',
|
||||
'class': 'logging.handlers.TimedRotatingFileHandler',
|
||||
'filename': var_dir('log/creme_center_fibois.log'),
|
||||
'formatter': 'verbose',
|
||||
'when': 'midnight',
|
||||
'interval': 1,
|
||||
'backupCount': 30,
|
||||
},
|
||||
},
|
||||
'loggers': {
|
||||
'django': {
|
||||
'level': 'WARNING',
|
||||
'handlers': ['file'],
|
||||
'propagate': True,
|
||||
},
|
||||
'django.request': {
|
||||
'level': 'WARNING',
|
||||
'handlers': ['file', 'mail_admins'],
|
||||
'propagate': True,
|
||||
},
|
||||
'center_pilote': {
|
||||
'level': 'INFO',
|
||||
'handlers': ['file', 'mail_admins'],
|
||||
'propagate': True,
|
||||
},
|
||||
},
|
||||
}
|
||||
if not os.path.isdir(var_dir('log')):
|
||||
os.mkdir(var_dir('log'), mode=0o750)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# APPLICATION AND 3RD PARTY LIBRARY SETTINGS
|
||||
# ------------------------------------------------------------------------------
|
|
@ -0,0 +1,54 @@
|
|||
"""
|
||||
With these settings, tests run faster.
|
||||
"""
|
||||
from .base import * # noqa
|
||||
from .base import TEMPLATES, env
|
||||
|
||||
# GENERAL
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#debug
|
||||
DEBUG = False
|
||||
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#secret-key
|
||||
SECRET_KEY = env('DJANGO_SECRET_KEY', default='CHANGEME!!!')
|
||||
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#test-runner
|
||||
TEST_RUNNER = 'django.test.runner.DiscoverRunner'
|
||||
|
||||
# CACHES
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#caches
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||
'LOCATION': '',
|
||||
}
|
||||
}
|
||||
|
||||
# PASSWORDS
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#password-hashers
|
||||
PASSWORD_HASHERS = ['django.contrib.auth.hashers.MD5PasswordHasher']
|
||||
|
||||
# TEMPLATES
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#templates
|
||||
TEMPLATES[0]['OPTIONS']['debug'] = DEBUG
|
||||
TEMPLATES[0]['OPTIONS']['loaders'] = [
|
||||
(
|
||||
'django.template.loaders.cached.Loader',
|
||||
[
|
||||
'django.template.loaders.filesystem.Loader',
|
||||
'django.template.loaders.app_directories.Loader',
|
||||
],
|
||||
)
|
||||
]
|
||||
|
||||
# EMAIL
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#email-backend
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#email-host
|
||||
EMAIL_HOST = 'localhost'
|
||||
# https://docs.djangoproject.com/en/stable/ref/settings/#email-port
|
||||
EMAIL_PORT = 1025
|
|
@ -0,0 +1,25 @@
|
|||
from django.conf import settings
|
||||
from django.urls import include, path
|
||||
|
||||
import creme.urls
|
||||
|
||||
urlpatterns = creme.urls.urlpatterns
|
||||
|
||||
if settings.DEBUG:
|
||||
from django.conf.urls.static import static
|
||||
from django.views import defaults as default_views
|
||||
|
||||
# This allows the error pages to be debugged during development, just visit
|
||||
# these url in browser to see how these error pages look like.
|
||||
urlpatterns += [
|
||||
path('400/', default_views.bad_request,
|
||||
kwargs={'exception': Exception('Bad Request!')}),
|
||||
path('403/', default_views.permission_denied,
|
||||
kwargs={'exception': Exception('Permission Denied')}),
|
||||
path('404/', default_views.page_not_found,
|
||||
kwargs={'exception': Exception('Page not Found')}),
|
||||
path('500/', default_views.server_error),
|
||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
||||
if 'debug_toolbar' in settings.INSTALLED_APPS:
|
||||
import debug_toolbar
|
|
@ -0,0 +1,21 @@
|
|||
"""
|
||||
WSGI config for creme_center_fibois project.
|
||||
|
||||
This module contains the WSGI application used by Django's development server
|
||||
and any production WSGI deployments. It should expose a module-level variable
|
||||
named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
|
||||
this application via the ``WSGI_APPLICATION`` setting.
|
||||
"""
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
from center_pilote.settings import DJANGO_SETTINGS_MODULE
|
||||
|
||||
# Set the default settings module to use.
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', DJANGO_SETTINGS_MODULE)
|
||||
|
||||
# This application object is used by any WSGI server configured to use this
|
||||
# file. This includes Django's development server, if the WSGI_APPLICATION
|
||||
# setting points here.
|
||||
application = get_wsgi_application()
|
|
@ -0,0 +1,105 @@
|
|||
###########################################################
|
||||
# #
|
||||
# Edit the following configuration to suits your needs. #
|
||||
# #
|
||||
###########################################################
|
||||
|
||||
###############################################################################
|
||||
# MAINS SETTINGS
|
||||
###############################################################################
|
||||
|
||||
# Environment to use within the application.
|
||||
#
|
||||
# The environment is used to load the proper settings for your application
|
||||
# instance. There is two ways for defining it, with the following precedence:
|
||||
# - DJANGO_SETTINGS_MODULE: the Python path to the settings module to use. It
|
||||
# allows you to define and use your own settings module. Use it with care!
|
||||
# Note: the module name will be used as the environment.
|
||||
# - ENV: the environment to use, which is one of 'production' or 'development'.
|
||||
#
|
||||
# Default is the 'production' environment.
|
||||
#ENV=production
|
||||
#ENV=development
|
||||
|
||||
# The secret key used to provide cryptographic signing.
|
||||
#
|
||||
# It should be set to a unique, unpredictable value. On a GNU/Linux system, you
|
||||
# could generate a new one with:
|
||||
#
|
||||
# $ head -c50 /dev/urandom | base64
|
||||
#
|
||||
# /!\ Required in production.
|
||||
#DJANGO_SECRET_KEY=CHANGEME!!!
|
||||
|
||||
# A coma-separated string representing the host/domain names that this
|
||||
# application instance can serve.
|
||||
#
|
||||
# /!\ Required in production.
|
||||
#DJANGO_ALLOWED_HOSTS=example.org,
|
||||
|
||||
###############################################################################
|
||||
# DATABASE SETTINGS
|
||||
###############################################################################
|
||||
|
||||
# Database configuration, as an URI.
|
||||
#
|
||||
# In production, the recommended database backend for better performances is
|
||||
# PostgreSQL - or MySQL if you prefer.
|
||||
#
|
||||
# Default is a SQLite database in development only.
|
||||
#
|
||||
# /!\ Required in production.
|
||||
#DJANGO_DATABASE_URL=postgres://user:password@127.0.0.1:5432/creme_center_fibois
|
||||
#DJANGO_DATABASE_URL=mysql://user:password@127.0.0.1:3306/creme_center_fibois
|
||||
|
||||
###############################################################################
|
||||
# EMAILS SETTINGS
|
||||
###############################################################################
|
||||
|
||||
# Email configuration for sending messages, as an URI.
|
||||
#
|
||||
# In production, you should either use a local SMTP server or a relay one. The
|
||||
# URI will be in that case of the form:
|
||||
#
|
||||
# PROTOCOL://[USER:PASSWORD@]HOST[:PORT]
|
||||
#
|
||||
# PROTOCOL can be smtp, smtp+ssl or smtp+tls. Note that special characters
|
||||
# in USER and PASSWORD - e.g. @ - must be escaped. It can be achieve with:
|
||||
#
|
||||
# $ python3 -c 'from urllib.parse import quote as q;print(q("USER")+":"+q("PASSWORD"))'
|
||||
#
|
||||
# Default is the local SMTP server in production and the console in development.
|
||||
#DJANGO_EMAIL_URL=smtp://localhost:25
|
||||
|
||||
# Default email address to use for various automated correspondence.
|
||||
#
|
||||
# /!\ Required in production.
|
||||
#DEFAULT_FROM_EMAIL=webmaster@example.org
|
||||
|
||||
# A comma separated list of all the people who get production error
|
||||
# notifications, following rfc2822 format
|
||||
#ADMINS='Cliss XXI <tech@cliss21.com>'
|
||||
|
||||
###############################################################################
|
||||
# MISC SETTINGS
|
||||
###############################################################################
|
||||
|
||||
# URL prefix on which the application is served.
|
||||
#
|
||||
# This is used to generate the static and media URLs, but also links to the
|
||||
# application which require an absolute URL.
|
||||
#
|
||||
# Default is '/', e.g. at the domain root.
|
||||
#APP_LOCATION=/
|
||||
|
||||
# Base directory of the app instance, where the local and var folders are
|
||||
# located.
|
||||
#
|
||||
# Default is the current directory.
|
||||
#BASE_DIR=
|
||||
|
||||
# Turn on/off debug mode.
|
||||
#
|
||||
# Note that it's always disabled in production.
|
||||
#DJANGO_DEBUG=off
|
||||
#DJANGO_DEBUG_TOOLBAR=on
|
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
|
||||
from center_pilote.settings import DJANGO_SETTINGS_MODULE
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Set the default settings module to use.
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', DJANGO_SETTINGS_MODULE)
|
||||
|
||||
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 # noqa
|
||||
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)
|
|
@ -0,0 +1,25 @@
|
|||
[tool.black]
|
||||
line-length = 79
|
||||
skip-string-normalization = true
|
||||
exclude = '''
|
||||
/(
|
||||
\.git
|
||||
| venv
|
||||
| local
|
||||
| var
|
||||
| migrations
|
||||
| node_modules
|
||||
| assets
|
||||
)/
|
||||
| urls(|_.+|/.+).py
|
||||
'''
|
||||
|
||||
[tool.isort]
|
||||
profile = 'black'
|
||||
line_length = 80
|
||||
known_django = 'django'
|
||||
known_first_party = 'center_pilote'
|
||||
sections = [
|
||||
'FUTURE', 'STDLIB', 'DJANGO', 'THIRDPARTY', 'FIRSTPARTY', 'LOCALFOLDER'
|
||||
]
|
||||
skip_glob = '**/migrations/*.py'
|
|
@ -0,0 +1,3 @@
|
|||
# This file is here because many Platforms as a Service look for
|
||||
# requirements.txt in the root directory of a project.
|
||||
-r requirements/production.txt
|
|
@ -0,0 +1,9 @@
|
|||
# Django
|
||||
# ------------------------------------------------------------------------------
|
||||
django-environ ==0.4.5
|
||||
|
||||
# Creme CRM (include django)
|
||||
# ------------------------------------------------------------------------------
|
||||
git+https://github.com/HybirdCorp/creme_crm.git@2.2.4#egg=creme
|
||||
|
||||
django-fieldbustier
|
|
@ -0,0 +1,6 @@
|
|||
-r test.txt
|
||||
|
||||
# Django
|
||||
# ------------------------------------------------------------------------------
|
||||
django-debug-toolbar
|
||||
django-extensions
|
|
@ -0,0 +1,3 @@
|
|||
-r base.txt
|
||||
|
||||
# PRECAUTION: avoid production dependencies that aren't in development.
|
|
@ -0,0 +1,15 @@
|
|||
-r base.txt
|
||||
|
||||
# Testing
|
||||
# ------------------------------------------------------------------------------
|
||||
pytest
|
||||
pytest-django
|
||||
|
||||
# Code quality
|
||||
# ------------------------------------------------------------------------------
|
||||
black
|
||||
flake8 >=3.5.0
|
||||
flake8-black
|
||||
flake8-isort
|
||||
isort >=5.0
|
||||
pytest-cov
|
|
@ -0,0 +1,69 @@
|
|||
[metadata]
|
||||
name = creme_center_fibois
|
||||
version = attr:creme_center_fibois.__version__
|
||||
license = AGPL-3.0
|
||||
license_file = LICENSE
|
||||
author = hybird.org / Cliss XXI
|
||||
author_email = tech@cliss21.com
|
||||
url = https://forge.cliss21.org/cliss21/creme_center_fibois
|
||||
description = CremeCRM pour Fibois
|
||||
long_description = file: README
|
||||
|
||||
keywords = CRM
|
||||
classifiers =
|
||||
Development Status :: 5 - Production/Stable
|
||||
Environment :: Web Environment
|
||||
Framework :: Django
|
||||
Intended Audience :: Customer Service
|
||||
Intended Audience :: Developers
|
||||
Intended Audience :: End Users/Desktop
|
||||
License :: OSI Approved :: GNU Affero General Public License v3
|
||||
Natural Language :: French
|
||||
Operating System :: OS Independent
|
||||
Programming Language :: Python :: 3.6
|
||||
Topic :: Office/Business
|
||||
|
||||
[options]
|
||||
zip_safe = False
|
||||
packages = find:
|
||||
include_package_data = True
|
||||
|
||||
[tool:pytest]
|
||||
addopts = --ds=center_pilote.settings.test
|
||||
python_files = tests.py test_*.py
|
||||
testpaths = center_pilote
|
||||
markers =
|
||||
wip: mark a test as a work in progress
|
||||
|
||||
[coverage:run]
|
||||
branch = True
|
||||
source =
|
||||
center_pilote
|
||||
omit =
|
||||
center_pilote/*tests*,
|
||||
center_pilote/*/migrations/*,
|
||||
center_pilote/settings/*,
|
||||
center_pilote/wsgi.py
|
||||
|
||||
[coverage:report]
|
||||
exclude_lines =
|
||||
pragma: no cover
|
||||
if settings.DEBUG:
|
||||
raise NotImplementedError
|
||||
show_missing = True
|
||||
|
||||
[flake8]
|
||||
exclude =
|
||||
.git,
|
||||
.tox,
|
||||
venv,
|
||||
*/migrations/*,
|
||||
*/static/*,
|
||||
assets,
|
||||
build,
|
||||
dist,
|
||||
docs,
|
||||
node_modules
|
||||
per-file-ignores =
|
||||
urls.py: BLK
|
||||
max-line-length = 80
|
|
@ -0,0 +1,3 @@
|
|||
from setuptool import setup
|
||||
|
||||
setup()
|
Chargement…
Référencer dans un nouveau ticket