Projet

Général

Profil

0001-start-a-Publik-Django-application-64765.patch

Thomas Noël, 03 mai 2022 00:27

Télécharger (54,8 ko)

Voir les différences:

Subject: [PATCH] start a Publik Django application (#64765)

 .gitignore                                  |   3 +
 Jenkinsfile                                 |  49 +++++
 MANIFEST.in                                 |  12 ++
 debian/changelog                            |   5 +
 debian/control                              |  33 ++++
 debian/debian_config.py                     |  18 ++
 debian/lingo-manage                         |  25 +++
 debian/lingo.dirs                           |   7 +
 debian/lingo.docs                           |   3 +
 debian/lingo.init                           | 168 +++++++++++++++++
 debian/lingo.install                        |   4 +
 debian/lingo.postinst                       |  50 ++++++
 debian/lingo.service                        |  26 +++
 debian/lingo.triggers                       |   1 +
 debian/nginx-example.conf                   |  44 +++++
 debian/py3dist-overrides                    |   2 +
 debian/python3-lingo.dirs                   |   1 +
 debian/python3-lingo.docs                   |   2 +
 debian/rules                                |  12 ++
 debian/settings.py                          |  26 +++
 debian/source/format                        |   1 +
 debian/uwsgi.ini                            |  51 ++++++
 getlasso3.sh                                |  22 +++
 lingo/__init__.py                           |   0
 lingo/locale/fr/LC_MESSAGES/django.po       |  24 +++
 lingo/manager/__init__.py                   |   0
 lingo/manager/urls.py                       |  24 +++
 lingo/manager/views.py                      |  50 ++++++
 lingo/settings.py                           | 190 ++++++++++++++++++++
 lingo/templates/lingo/base.html             |   9 +
 lingo/templates/lingo/homepage.html         |   1 +
 lingo/templates/lingo/manager_homepage.html |   5 +
 lingo/templates/registration/login.html     |  10 ++
 lingo/urls.py                               |  53 ++++++
 lingo/urls_utils.py                         |  65 +++++++
 lingo/views.py                              |  62 +++++++
 lingo/wsgi.py                               |  23 +++
 manage.py                                   |  10 ++
 pylint.rc                                   | 130 ++++++++++++++
 pylint.sh                                   |   5 +
 setup.py                                    | 177 ++++++++++++++++++
 tests/__init__.py                           |   0
 tests/conftest.py                           |  28 +++
 tests/settings.py                           |  20 +++
 tests/test_homepage.py                      |   7 +
 tests/test_manager.py                       |  29 +++
 tox.ini                                     |  59 ++++++
 47 files changed, 1546 insertions(+)
 create mode 100644 Jenkinsfile
 create mode 100644 MANIFEST.in
 create mode 100644 debian/changelog
 create mode 100644 debian/control
 create mode 100644 debian/debian_config.py
 create mode 100755 debian/lingo-manage
 create mode 100644 debian/lingo.dirs
 create mode 100644 debian/lingo.docs
 create mode 100644 debian/lingo.init
 create mode 100644 debian/lingo.install
 create mode 100644 debian/lingo.postinst
 create mode 100644 debian/lingo.service
 create mode 100644 debian/lingo.triggers
 create mode 100644 debian/nginx-example.conf
 create mode 100644 debian/py3dist-overrides
 create mode 100644 debian/python3-lingo.dirs
 create mode 100644 debian/python3-lingo.docs
 create mode 100755 debian/rules
 create mode 100644 debian/settings.py
 create mode 100644 debian/source/format
 create mode 100644 debian/uwsgi.ini
 create mode 100755 getlasso3.sh
 create mode 100644 lingo/__init__.py
 create mode 100644 lingo/locale/fr/LC_MESSAGES/django.po
 create mode 100644 lingo/manager/__init__.py
 create mode 100644 lingo/manager/urls.py
 create mode 100644 lingo/manager/views.py
 create mode 100644 lingo/settings.py
 create mode 100644 lingo/templates/lingo/base.html
 create mode 100644 lingo/templates/lingo/homepage.html
 create mode 100644 lingo/templates/lingo/manager_homepage.html
 create mode 100644 lingo/templates/registration/login.html
 create mode 100644 lingo/urls.py
 create mode 100644 lingo/urls_utils.py
 create mode 100644 lingo/views.py
 create mode 100644 lingo/wsgi.py
 create mode 100755 manage.py
 create mode 100644 pylint.rc
 create mode 100755 pylint.sh
 create mode 100644 setup.py
 create mode 100644 tests/__init__.py
 create mode 100644 tests/conftest.py
 create mode 100644 tests/settings.py
 create mode 100644 tests/test_homepage.py
 create mode 100644 tests/test_manager.py
 create mode 100644 tox.ini
.gitignore
11 11
.cache
12 12
.coverage
13 13
.pytest_cache/
14
junit*xml
15
pylint.out
16
*.swp
Jenkinsfile
1
@Library('eo-jenkins-lib@main') import eo.Utils
2

  
3
pipeline {
4
    agent any
5
    options {
6
        disableConcurrentBuilds()
7
        timeout(time: 20, unit: 'MINUTES')
8
    }
9
    stages {
10
        stage('Unit Tests') {
11
            steps {
12
                sh 'tox -rv'
13
            }
14
            post {
15
                always {
16
                    script {
17
                        utils = new Utils()
18
                        utils.publish_coverage('coverage.xml')
19
                        utils.publish_coverage_native('index.html')
20
                        utils.publish_pylint('pylint.out')
21
                    }
22
                    mergeJunitResults()
23
                }
24
            }
25
        }
26
        stage('Packaging') {
27
            steps {
28
                script {
29
                    if (env.JOB_NAME == 'lingo' && env.GIT_BRANCH == 'origin/main') {
30
                        sh 'sudo -H -u eobuilder /usr/local/bin/eobuilder -d buster,bullseye lingo'
31
                    } else if (env.GIT_BRANCH.startsWith('hotfix/')) {
32
                        sh "sudo -H -u eobuilder /usr/local/bin/eobuilder -d buster,bullseye --branch ${env.GIT_BRANCH} --hotfix lingo"
33
                    }
34
                }
35
            }
36
        }
37
    }
38
    post {
39
        always {
40
            script {
41
                utils = new Utils()
42
                utils.mail_notify(currentBuild, env, 'ci+jenkins-lingo@entrouvert.org')
43
            }
44
        }
45
        success {
46
            cleanWs()
47
        }
48
    }
49
}
MANIFEST.in
1
# locales
2
recursive-include lingo/locale *.po *.mo
3

  
4
# static
5
recursive-include lingo/static *.gif *.png *.css *.js
6

  
7
# templates
8
recursive-include lingo/templates *.html
9

  
10
include COPYING README
11
include MANIFEST.in
12
include VERSION
debian/changelog
1
lingo (0.0-1) unstable; urgency=low
2

  
3
  * Initial release
4

  
5
 -- Thomas NOËL <tnoel@entrouvert.com>  Mon, 2 May 2022 23:22:44 +0200
debian/control
1
Source: lingo
2
Maintainer: Thomas NOËL <tnoel@entrouvert.com>
3
Section: python
4
Priority: optional
5
Build-Depends: python3-setuptools, python3-all, python3-django, debhelper-compat (= 12), dh-python
6
Standards-Version: 3.9.6
7

  
8
Package: python3-lingo
9
Architecture: all
10
Depends: ${misc:Depends}, ${python3:Depends},
11
    python3-distutils,
12
    python3-django,
13
    python3-djangorestframework,
14
    python3-gadjo,
15
    python3-requests,
16
    python3-eopayment
17
Recommends: python3-django-mellon
18
Description: Payment and Billing System (Python module)
19

  
20
Package: lingo
21
Architecture: all
22
Depends: ${misc:Depends},
23
    python3-lingo (= ${binary:Version}),
24
    python3-hobo,
25
    python3-django-tenant-schemas,
26
    python3-psycopg2,
27
    python3-django-mellon,
28
    uwsgi,
29
    uwsgi-plugin-python3,
30
    python3-uwsgidecorators
31
Recommends: nginx
32
Suggests: postgresql
33
Description: Payment and Billing System
debian/debian_config.py
1
# This file is sourced by "execfile" from lingo.settings
2

  
3
import os
4

  
5
PROJECT_NAME = 'lingo'
6

  
7
#
8
# hobotization (multitenant)
9
#
10
exec(open('/usr/lib/hobo/debian_config_common.py').read())
11

  
12
#
13
# local settings
14
#
15
exec(open(os.path.join(ETC_DIR, 'settings.py')).read())
16

  
17
# run additional settings snippets
18
exec(open('/usr/lib/hobo/debian_config_settings_d.py').read())
debian/lingo-manage
1
#!/bin/sh
2

  
3
NAME=lingo
4
MANAGE=/usr/lib/$NAME/manage.py
5

  
6
# load Debian default configuration
7
export LINGO_SETTINGS_FILE=/usr/lib/$NAME/debian_config.py
8

  
9
# check user
10
if test x$1 = x"--forceuser"
11
then
12
    shift
13
elif test $(id -un) != "$NAME"
14
then
15
    echo "error: must use $0 with user ${NAME}"
16
    exit 1
17
fi
18

  
19
if test $# -eq 0
20
then
21
    python3 ${MANAGE} help
22
    exit 1
23
fi
24

  
25
python3 ${MANAGE} "$@"
debian/lingo.dirs
1
/etc/lingo
2
/usr/share/lingo/themes
3
/usr/lib/lingo
4
/var/lib/lingo/collectstatic
5
/var/lib/lingo/tenants
6
/var/log/lingo
7
/var/lib/lingo/spooler
debian/lingo.docs
1
COPYING
2
README
3
debian/nginx-example.conf
debian/lingo.init
1
#!/bin/sh
2
### BEGIN INIT INFO
3
# Provides:          lingo
4
# Required-Start:    $network $local_fs $remote_fs $syslog
5
# Required-Stop:     $network $local_fs $remote_fs $syslog
6
# Should-start:      postgresql
7
# Should-stop:       postgresql
8
# Default-Start:     2 3 4 5
9
# Default-Stop:      0 1 6
10
# Short-Description: Billing and Payment System
11
# Description:       Billing and Payment System
12
### END INIT INFO
13

  
14
# Author:  Entr'ouvert <info@entrouvert.com>
15
set -e
16

  
17
PATH=/sbin:/usr/sbin:/bin:/usr/bin
18
DESC="Billing and Payment System"
19
NAME=lingo
20
DAEMON=/usr/bin/uwsgi
21
RUN_DIR=/run/$NAME
22
PIDFILE=$RUN_DIR/$NAME.pid
23
LOG_DIR=/var/log/$NAME
24
SCRIPTNAME=/etc/init.d/$NAME
25
BIND=unix:$RUN_DIR/$NAME.sock
26

  
27
LINGO_SETTINGS_FILE=/usr/lib/$NAME/debian_config.py
28
MANAGE_SCRIPT="/usr/bin/$NAME-manage"
29

  
30
USER=$NAME
31
GROUP=$NAME
32

  
33
# Exit if the package is not installed
34
[ -x $MANAGE_SCRIPT ] || exit 0
35

  
36
# Read configuration variable file if it is present
37
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
38

  
39
DAEMON_ARGS=${DAEMON_ARGS:-"--pidfile=$PIDFILE
40
--uid $USER --gid $GROUP
41
--ini /etc/$NAME/uwsgi.ini
42
--spooler /var/lib/$NAME/spooler/
43
--daemonize /var/log/uwsgi.$NAME.log"}
44

  
45
# Load the VERBOSE setting and other rcS variables
46
. /lib/init/vars.sh
47

  
48
# Define LSB log_* functions.
49
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
50
. /lib/lsb/init-functions
51

  
52
# Create /run directory
53
if [ ! -d $RUN_DIR ]; then
54
    install -d -m 755 -o $USER -g $GROUP $RUN_DIR
55
fi
56

  
57
# environment for wsgi
58
export LINGO_SETTINGS_FILE
59

  
60
#
61
# Function that starts the daemon/service
62
#
63
do_start()
64
{
65
    # Return
66
    #   0 if daemon has been started
67
    #   1 if daemon was already running
68
    #   2 if daemon could not be started
69
    start-stop-daemon --start --quiet --user $USER --exec $DAEMON -- \
70
        $DAEMON_ARGS \
71
        || return 2
72
}
73

  
74
#
75
# Function that stops the daemon/service
76
#
77
do_stop()
78
{
79
    # Return
80
    #   0 if daemon has been stopped
81
    #   1 if daemon was already stopped
82
    #   2 if daemon could not be stopped
83
    #   other if a failure occurred
84
    $DAEMON --stop $PIDFILE
85
    rm -f $PIDFILE
86
    return 0 # hopefully
87
}
88

  
89
#
90
# Function that sends a SIGHUP to the daemon/service
91
#
92
do_reload() {
93
    $DAEMON --reload $PIDFILE
94
    return 0
95
}
96

  
97
do_migrate() {
98
    log_action_msg "Applying migrations (migrate_schemas).."
99
    su $USER -s /bin/sh -p -c "$MANAGE_SCRIPT migrate_schemas --noinput"
100
    log_action_msg "done"
101
}
102

  
103
do_collectstatic() {
104
    log_action_msg "Collect static files (collectstatic).."
105
    su $USER -s /bin/sh -p -c "$MANAGE_SCRIPT collectstatic --noinput"
106
    log_action_msg "done"
107
}
108

  
109
case "$1" in
110
  start)
111
    log_daemon_msg "Starting $DESC " "$NAME"
112
    do_migrate
113
    do_collectstatic
114
    do_start
115
    case "$?" in
116
        0|1) log_end_msg 0 ;;
117
        2) log_end_msg 1 ;;
118
    esac
119
  ;;
120
  stop)
121
    log_daemon_msg "Stopping $DESC" "$NAME"
122
    do_stop
123
    case "$?" in
124
        0|1) log_end_msg 0 ;;
125
        2) log_end_msg 1 ;;
126
    esac
127
    ;;
128
  status)
129
    status_of_proc -p $PIDFILE "$DAEMON" "$NAME" && exit 0 || exit $?
130
    ;;
131
  reload|force-reload)
132
    #
133
    # If do_reload() is not implemented then leave this commented out
134
    # and leave 'force-reload' as an alias for 'restart'.
135
    #
136
    log_daemon_msg "Reloading $DESC" "$NAME"
137
    do_reload
138
    log_end_msg $?
139
    ;;
140
  restart|force-reload)
141
    #
142
    # If the "reload" option is implemented then remove the
143
    # 'force-reload' alias
144
    #
145
    log_daemon_msg "Restarting $DESC" "$NAME"
146
    do_stop
147
    case "$?" in
148
      0|1)
149
        do_migrate
150
        do_collectstatic
151
        do_start
152
        case "$?" in
153
            0) log_end_msg 0 ;;
154
            1) log_end_msg 1 ;; # Old process is still running
155
            *) log_end_msg 1 ;; # Failed to start
156
        esac
157
        ;;
158
      *)
159
        # Failed to stop
160
        log_end_msg 1
161
        ;;
162
    esac
163
    ;;
164
  *)
165
    echo "Usage: $SCRIPTNAME {start|stop|status|restart|reload|force-reload}" >&2
166
    exit 3
167
    ;;
168
esac
debian/lingo.install
1
debian/lingo-manage       /usr/bin
2
debian/settings.py        /etc/lingo
3
debian/uwsgi.ini          /etc/lingo
4
debian/debian_config.py   /usr/lib/lingo
debian/lingo.postinst
1
#! /bin/sh
2

  
3
set -e
4

  
5
NAME="lingo"
6
USER=$NAME
7
GROUP=$NAME
8
CONFIG_DIR="/etc/$NAME"
9
MANAGE_SCRIPT="/usr/bin/$NAME-manage"
10

  
11
case "$1" in
12
  configure)
13

  
14
    # make sure the administrative user exists
15
    if ! getent passwd $USER >/dev/null; then
16
      adduser --disabled-password  --quiet --system \
17
        --no-create-home --home /var/lib/$NAME \
18
        --gecos "$NAME user" --group $USER
19
    fi
20
    # ensure dirs ownership
21
    chown $USER:$GROUP /var/log/$NAME
22
    chown $USER:$GROUP /var/lib/$NAME/collectstatic
23
    chown $USER:$GROUP /var/lib/$NAME/tenants
24
    chown $USER:$GROUP /var/lib/$NAME/spooler
25
    # create a secret file
26
    SECRET_FILE=$CONFIG_DIR/secret
27
    if [ ! -f $SECRET_FILE ]; then
28
        echo -n "Generating Django secret..." >&2
29
        cat /dev/urandom | tr -dc [:alnum:]-_\!\%\^:\; | head -c70 > $SECRET_FILE
30
        chown root:$GROUP $SECRET_FILE
31
        chmod 0440 $SECRET_FILE
32
    fi
33
  ;;
34

  
35
  triggered)
36
      su -s /bin/sh -c "$MANAGE_SCRIPT hobo_deploy --redeploy" $USER
37
  ;;
38

  
39
  abort-upgrade|abort-remove|abort-deconfigure)
40
  ;;
41

  
42
  *)
43
    echo "postinst called with unknown argument \`$1'" >&2
44
    exit 1
45
  ;;
46
esac
47

  
48
#DEBHELPER#
49

  
50
exit 0
debian/lingo.service
1
[Unit]
2
Description=Lingo
3
After=network.target syslog.target postgresql.service
4
Wants=postgresql.service
5

  
6
[Service]
7
Environment=LINGO_SETTINGS_FILE=/usr/lib/%p/debian_config.py
8
Environment=LANG=C.UTF-8
9
User=%p
10
Group=%p
11
ExecStartPre=/usr/bin/lingo-manage migrate_schemas --noinput --verbosity 1
12
ExecStartPre=/usr/bin/lingo-manage collectstatic --noinput --link
13
ExecStartPre=/bin/mkdir -p /var/lib/lingo/spooler/%m/
14
ExecStart=/usr/bin/uwsgi --ini /etc/%p/uwsgi.ini --spooler /var/lib/lingo/spooler/%m/
15
ExecReload=/bin/kill -HUP $MAINPID
16
KillSignal=SIGQUIT
17
TimeoutStartSec=0
18
PrivateTmp=true
19
Restart=on-failure
20
RuntimeDirectory=lingo
21
Type=notify
22
StandardError=syslog
23
NotifyAccess=all
24

  
25
[Install]
26
WantedBy=multi-user.target
debian/lingo.triggers
1
interest-noawait hobo-redeploy
debian/nginx-example.conf
1
server {
2
        listen   443;
3
        server_name  *-lingo.example.org;
4

  
5
        ssl                  on;
6
        ssl_certificate      /etc/ssl/certs/ssl-cert-snakeoil.pem;
7
        ssl_certificate_key  /etc/ssl/private/ssl-cert-snakeoil.key;
8

  
9
        access_log  /var/log/nginx/lingo.example.org-access.log combined;
10
        error_log  /var/log/nginx/lingo.example.org-error.log;
11

  
12
        location ~ ^/static/(.+)$ {
13
            root /;
14
            try_files /var/lib/lingo/tenants/$host/static/$1
15
                      /var/lib/lingo/tenants/$host/theme/static/$1
16
                      /var/lib/lingo/collectstatic/$1
17
                      =404;
18
            add_header Access-Control-Allow-Origin *;
19
        }
20

  
21
        location ~ ^/media/(.+)$ {
22
            alias /var/lib/lingo/tenants/$host/media/$1;
23
        }
24

  
25
        location / {
26
            proxy_pass         http://unix:/var/run/lingo/lingo.sock;
27
            proxy_set_header   Host $http_host;
28
            proxy_set_header   X-Forwarded-SSL on;
29
            proxy_set_header   X-Forwarded-Protocol ssl;
30
            proxy_set_header   X-Forwarded-Proto https;
31
            proxy_set_header   X-Real-IP       $remote_addr;
32
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
33
        }
34
}
35

  
36
server {
37
        listen   80;
38
        server_name  *-lingo.example.org;
39

  
40
        access_log  /var/log/nginx/lingo.example.org-access.log combined;
41
        error_log  /var/log/nginx/lingo.example.org-error.log;
42

  
43
        return 302 https://$host$request_uri;
44
}
debian/py3dist-overrides
1
eopayment python3-eopayment
2
gadjo python3-gadjo
debian/python3-lingo.dirs
1
/usr/lib/lingo
debian/python3-lingo.docs
1
COPYING
2
README
debian/rules
1
#!/usr/bin/make -f
2
# -*- makefile -*-
3

  
4
export PYBUILD_NAME=lingo
5
export PYBUILD_DISABLE=test
6

  
7
%:
8
	dh $@ --with python3 --buildsystem=pybuild
9

  
10
override_dh_install:
11
	dh_install
12
	mv $(CURDIR)/debian/python3-lingo/usr/bin/manage.py $(CURDIR)/debian/lingo/usr/lib/lingo/manage.py
debian/settings.py
1
# Configuration for lingo.
2

  
3
# Override with /etc/lingo/settings.d/ files
4

  
5
# Lingo is a Django application: for the full list of settings and their
6
# values, see https://docs.djangoproject.com/en/3.2/ref/settings/
7
# For more information on settings see
8
# https://docs.djangoproject.com/en/3.2/topics/settings/
9

  
10
# WARNING! Quick-start development settings unsuitable for production!
11
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
12

  
13
# This file is sourced by "exec(open(...).read())" from
14
# /usr/lib/lingo/debian_config.py
15

  
16
# SECURITY WARNING: don't run with debug turned on in production!
17
DEBUG = False
18

  
19
# ALLOWED_HOSTS must be correct in production!
20
# See https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts
21
ALLOWED_HOSTS = [
22
    '*',
23
]
24

  
25
LANGUAGE_CODE = 'fr-fr'
26
TIME_ZONE = 'Europe/Paris'
debian/source/format
1
3.0 (quilt)
debian/uwsgi.ini
1
[uwsgi]
2
auto-procname = true
3
procname-prefix-spaced = lingo
4
strict = true
5

  
6
plugin = python3
7
single-interpreter = true
8
module = lingo.wsgi:application
9
need-app = true
10

  
11
http-socket = /run/lingo/lingo.sock
12
chmod-socket = 666
13
vacuum = true
14

  
15
spooler-processes = 3
16
spooler-python-import = lingo.utils.spooler
17
spooler-python-import = hobo.provisionning.spooler
18
spooler-max-tasks = 20
19

  
20
master = true
21
enable-threads = true
22
harakiri = 120
23

  
24
processes = 500
25

  
26
plugin = cheaper_busyness
27
cheaper-algo = busyness
28
cheaper = 5
29
cheaper-initial = 10
30
cheaper-overload = 5
31
cheaper-step = 10
32
cheaper-busyness-multiplier = 30
33
cheaper-busyness-min = 20
34
cheaper-busyness-max = 70
35
cheaper-busyness-backlog-alert = 16
36
cheaper-busyness-backlog-step = 2
37

  
38
max-requests = 500
39
max-worker-lifetime = 7200
40

  
41
buffer-size = 32768
42

  
43
py-tracebacker = /run/lingo/py-tracebacker.sock.
44
stats = /run/lingo/stats.sock
45

  
46
ignore-sigpipe = true
47
disable-write-exception = true
48

  
49
if-file = /etc/lingo/uwsgi-local.ini
50
  include = /etc/lingo/uwsgi-local.ini
51
endif =
getlasso3.sh
1
#!/bin/sh
2

  
3
# Get venv site-packages path
4
DSTDIR=`python3 -c 'from distutils.sysconfig import get_python_lib; print(get_python_lib())'`
5

  
6
# Get not venv site-packages path
7
# Remove first path (assuming that is the venv path)
8
NONPATH=`echo $PATH | sed 's/^[^:]*://'`
9
SRCDIR=`PATH=$NONPATH python3 -c 'from distutils.sysconfig import get_python_lib; print(get_python_lib())'`
10

  
11
# Clean up
12
rm -f $DSTDIR/lasso.*
13
rm -f $DSTDIR/_lasso.*
14

  
15
# Link
16
ln -sv /usr/lib/python3/dist-packages/lasso.py $DSTDIR/
17
for SOFILE in /usr/lib/python3/dist-packages/_lasso.cpython-*.so
18
do
19
  ln -sv $SOFILE $DSTDIR/
20
done
21

  
22
exit 0
lingo/locale/fr/LC_MESSAGES/django.po
1
# lingo - payment and billing system, french translation
2
# Copyright (C) 2022  Entr'ouvert
3
# This file is distributed under the same license as the lingo package.
4
#
5
msgid ""
6
msgstr ""
7
"Project-Id-Version: lingo 0\n"
8
"Report-Msgid-Bugs-To: \n"
9
"POT-Creation-Date: 2022-05-02 22:19+0000\n"
10
"PO-Revision-Date: 2022-05-02 22:13+0000\n"
11
"Last-Translator: Thomas NOËL <tnoel@entrouvert.com>\n"
12
"Language: French\n"
13
"MIME-Version: 1.0\n"
14
"Content-Type: text/plain; charset=UTF-8\n"
15
"Content-Transfer-Encoding: 8bit\n"
16
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
17

  
18
#: manager/views.py templates/lingo/manager_homepage.html
19
msgid "Payments"
20
msgstr "Paiements"
21

  
22
#: templates/registration/login.html
23
msgid "Log in"
24
msgstr "S’identifier"
lingo/manager/urls.py
1
# lingo - billing and payment system
2
# Copyright (C) 2022  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
from django.conf.urls import url
18

  
19
from . import views
20

  
21
urlpatterns = [
22
    url(r'^$', views.homepage, name='manager-homepage'),
23
    url(r'^menu.json$', views.menu_json),
24
]
lingo/manager/views.py
1
# lingo - payment and billing system
2
# Copyright (C) 2022  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
import json
18

  
19
from django.http import HttpResponse
20
from django.urls import reverse
21
from django.views.generic import TemplateView
22

  
23

  
24
class HomepageView(TemplateView):
25
    template_name = 'lingo/manager_homepage.html'
26

  
27

  
28
homepage = HomepageView.as_view()
29

  
30

  
31
def menu_json(request):
32
    label = _('Payments')
33
    json_str = json.dumps(
34
        [
35
            {
36
                'label': label,
37
                'slug': 'lingo',
38
                'url': request.build_absolute_uri(reverse('manage-homepage')),
39
            }
40
        ]
41
    )
42
    content_type = 'application/json'
43
    for variable in ('jsonpCallback', 'callback'):
44
        if variable in request.GET:
45
            json_str = '%s(%s);' % (request.GET[variable], json_str)
46
            content_type = 'application/javascript'
47
            break
48
    response = HttpResponse(content_type=content_type)
49
    response.write(json_str)
50
    return response
lingo/settings.py
1
# lingo - payment and bill system
2
# Copyright (C) 2022  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
"""
18
Django settings file; it loads the default settings, and local settings
19
(from a local_settings.py file, or a configuration file set in the
20
LINGO_SETTINGS_FILE environment variable).
21

  
22
The local settings file should exist, at least to set a suitable SECRET_KEY,
23
and to disable DEBUG mode in production.
24
"""
25

  
26
import os
27

  
28
from django.conf import global_settings
29

  
30
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
31
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
32

  
33
# Quick-start development settings - unsuitable for production
34
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
35

  
36
# SECURITY WARNING: keep the secret key used in production secret!
37
SECRET_KEY = 'r^(w+o4*txe1=t+0w*w3*9%idij!yeq1#axpsi4%5*u#3u&)1t'
38

  
39
# SECURITY WARNING: don't run with debug turned on in production!
40
DEBUG = True
41

  
42
ALLOWED_HOSTS = []
43

  
44

  
45
# Application definition
46

  
47
INSTALLED_APPS = (
48
    'django.contrib.admin',
49
    'django.contrib.auth',
50
    'django.contrib.contenttypes',
51
    'django.contrib.sessions',
52
    'django.contrib.messages',
53
    'django.contrib.staticfiles',
54
    'django.contrib.humanize',
55
    'eopayment',
56
    'gadjo',
57
)
58

  
59
MIDDLEWARE = (
60
    'django.contrib.sessions.middleware.SessionMiddleware',
61
    'django.middleware.common.CommonMiddleware',
62
    'django.middleware.csrf.CsrfViewMiddleware',
63
    'django.contrib.auth.middleware.AuthenticationMiddleware',
64
    'django.contrib.messages.middleware.MessageMiddleware',
65
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
66
)
67

  
68
# Serve xstatic files, required for gadjo
69
STATICFILES_FINDERS = list(global_settings.STATICFILES_FINDERS) + ['gadjo.finders.XStaticFinder']
70

  
71
# Templates
72
TEMPLATES = [
73
    {
74
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
75
        'DIRS': [
76
            os.path.join(BASE_DIR, 'lingo', 'templates'),
77
        ],
78
        'APP_DIRS': True,
79
        'OPTIONS': {
80
            'context_processors': [
81
                'django.contrib.auth.context_processors.auth',
82
                'django.template.context_processors.debug',
83
                'django.template.context_processors.i18n',
84
                'django.template.context_processors.media',
85
                'django.template.context_processors.request',
86
                'django.template.context_processors.static',
87
                'django.template.context_processors.tz',
88
                'django.contrib.messages.context_processors.messages',
89
            ],
90
            'builtins': [
91
                'django.contrib.humanize.templatetags.humanize',
92
            ],
93
        },
94
    },
95
]
96

  
97
ROOT_URLCONF = 'lingo.urls'
98

  
99
WSGI_APPLICATION = 'lingo.wsgi.application'
100

  
101

  
102
# Database
103

  
104
DATABASES = {
105
    'default': {
106
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
107
    }
108
}
109

  
110
# Internationalization
111

  
112
LANGUAGE_CODE = 'fr-fr'
113

  
114
TIME_ZONE = 'UTC'
115

  
116
USE_I18N = True
117

  
118
USE_L10N = True
119

  
120
USE_TZ = True
121

  
122
LOCALE_PATHS = (os.path.join(BASE_DIR, 'lingo', 'locale'),)
123

  
124
# Static files (CSS, JavaScript, Images)
125

  
126
STATIC_URL = '/static/'
127

  
128
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
129
MEDIA_URL = '/media/'
130

  
131
# mode for newly updated files
132
FILE_UPLOAD_PERMISSIONS = 0o644
133

  
134
# extra variables for templates
135
TEMPLATE_VARS = {}
136

  
137
# Authentication settings
138
try:
139
    import mellon
140
except ImportError:
141
    mellon = None
142

  
143
if mellon is not None:
144
    INSTALLED_APPS += ('mellon',)
145
    AUTHENTICATION_BACKENDS = (
146
        'mellon.backends.SAMLBackend',
147
        'django.contrib.auth.backends.ModelBackend',
148
    )
149

  
150
LOGIN_URL = '/login/'
151
LOGIN_REDIRECT_URL = '/'
152
LOGOUT_URL = '/logout/'
153

  
154
MELLON_ATTRIBUTE_MAPPING = {
155
    'email': '{attributes[email][0]}',
156
    'first_name': '{attributes[first_name][0]}',
157
    'last_name': '{attributes[last_name][0]}',
158
}
159

  
160
MELLON_SUPERUSER_MAPPING = {
161
    'is_superuser': 'true',
162
}
163

  
164
MELLON_USERNAME_TEMPLATE = '{attributes[name_id_content]}'
165

  
166
MELLON_IDENTITY_PROVIDERS = []
167

  
168

  
169
# default site
170
SITE_BASE_URL = 'http://localhost'
171

  
172
# known services
173
KNOWN_SERVICES = {}
174

  
175

  
176
def debug_show_toolbar(request):
177
    from debug_toolbar.middleware import show_toolbar as dt_show_toolbar  # pylint: disable=import-error
178

  
179
    return dt_show_toolbar(request) and not request.path.startswith('/__skeleton__/')
180

  
181

  
182
DEBUG_TOOLBAR_CONFIG = {'SHOW_TOOLBAR_CALLBACK': debug_show_toolbar}
183

  
184

  
185
local_settings_file = os.environ.get(
186
    'LINGO_SETTINGS_FILE', os.path.join(os.path.dirname(__file__), 'local_settings.py')
187
)
188
if os.path.exists(local_settings_file):
189
    with open(local_settings_file) as fd:
190
        exec(fd.read())
lingo/templates/lingo/base.html
1
{% extends "gadjo/base.html" %}
2
{% load i18n staticfiles gadjo %}
3
{% block page-title %}Lingo{% endblock %}
4
{% block site-title %}Lingo{% endblock %}
5

  
6
{% block logout-url %}{% url "auth_logout" %}{% endblock %}
7

  
8
{% block content %}
9
{% endblock %}
lingo/templates/lingo/homepage.html
1
{% extends "lingo/base.html" %}
lingo/templates/lingo/manager_homepage.html
1
{% extends "lingo/base.html" %}
2
{% load i18n %}
3
{% block appbar %}
4
  <h2>{% trans 'Payments' %}</h2>
5
{% endblock %}
lingo/templates/registration/login.html
1
{% extends "lingo/base.html" %}
2
{% load i18n %}
3

  
4
{% block content %}
5
<form method="post">
6
{% csrf_token %}
7
{{ form.as_p }}
8
<button>{% trans 'Log in' %}</button>
9
</form>
10
{% endblock %}
lingo/urls.py
1
# lingo - payment and billing system
2
# Copyright (C) 2022  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
from django.conf import settings
18
from django.conf.urls import include, url
19
from django.conf.urls.static import static
20
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
21

  
22
from .manager.urls import urlpatterns as lingo_manager_urls
23
from .urls_utils import decorated_includes, manager_required
24
from .views import homepage, login, logout
25

  
26
urlpatterns = [
27
    url(r'^$', homepage, name='homepage'),
28
    url(r'^manage/', decorated_includes(manager_required, include(lingo_manager_urls))),
29
    url(r'^login/$', login, name='auth_login'),
30
    url(r'^logout/$', logout, name='auth_logout'),
31
]
32

  
33
if 'mellon' in settings.INSTALLED_APPS:
34
    urlpatterns.append(
35
        url(
36
            r'^accounts/mellon/',
37
            include('mellon.urls'),
38
            kwargs={
39
                'template_base': 'lingo/base.html',
40
            },
41
        )
42
    )
43

  
44
# static and media files
45
urlpatterns += staticfiles_urlpatterns()
46
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
47

  
48
if settings.DEBUG and 'debug_toolbar' in settings.INSTALLED_APPS:
49
    import debug_toolbar  # pylint: disable=import-error
50

  
51
    urlpatterns = [
52
        url(r'^__debug__/', include(debug_toolbar.urls)),
53
    ] + urlpatterns
lingo/urls_utils.py
1
# lingo - payment and billing system
2
# Copyright (C) 2022  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
# Decorating URL includes, <https://djangosnippets.org/snippets/2532/>
18

  
19
from django.contrib.auth.decorators import user_passes_test
20
from django.core.exceptions import PermissionDenied
21
from django.urls.resolvers import URLPattern, URLResolver
22

  
23

  
24
class DecoratedURLPattern(URLPattern):
25
    def resolve(self, *args, **kwargs):
26
        result = super().resolve(*args, **kwargs)
27
        if result:
28
            result.func = self._decorate_with(result.func)
29
        return result
30

  
31

  
32
class DecoratedURLResolver(URLResolver):
33
    def resolve(self, *args, **kwargs):
34
        result = super().resolve(*args, **kwargs)
35
        if result:
36
            result.func = self._decorate_with(result.func)
37
        return result
38

  
39

  
40
def decorated_includes(func, includes, *args, **kwargs):
41
    urlconf_module, app_name, namespace = includes
42

  
43
    for item in urlconf_module:
44
        if isinstance(item, URLResolver):
45
            item.__class__ = DecoratedURLResolver
46
        else:
47
            item.__class__ = DecoratedURLPattern
48
        item._decorate_with = func
49

  
50
    return urlconf_module, app_name, namespace
51

  
52

  
53
def manager_required(function=None, login_url=None):
54
    def check_manager(user):
55
        if user and user.is_staff:
56
            return True
57
        if user and not user.is_anonymous:
58
            raise PermissionDenied()
59
        # As the last resort, show the login form
60
        return False
61

  
62
    actual_decorator = user_passes_test(check_manager, login_url=login_url)
63
    if function:
64
        return actual_decorator(function)
65
    return actual_decorator
lingo/views.py
1
# lingo - payment and billing system
2
# Copyright (C) 2022  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
from django.conf import settings
18
from django.contrib.auth import logout as auth_logout
19
from django.contrib.auth import views as auth_views
20
from django.http import HttpResponseRedirect
21
from django.shortcuts import resolve_url
22
from django.utils.http import quote
23
from django.views.generic import TemplateView
24

  
25
if 'mellon' in settings.INSTALLED_APPS:
26
    from mellon.utils import get_idps  # pylint: disable=import-error
27
else:
28

  
29
    def get_idps():
30
        return []
31

  
32

  
33
class LoginView(auth_views.LoginView):
34
    def dispatch(self, request, *args, **kwargs):
35
        if any(get_idps()):
36
            if 'next' not in request.GET:
37
                return HttpResponseRedirect(resolve_url('mellon_login'))
38
            return HttpResponseRedirect(
39
                resolve_url('mellon_login') + '?next=' + quote(request.GET.get('next'))
40
            )
41
        return super().dispatch(request, *args, **kwargs)
42

  
43

  
44
login = LoginView.as_view()
45

  
46

  
47
def logout(request, next_page=None):
48
    if any(get_idps()):
49
        return HttpResponseRedirect(resolve_url('mellon_logout'))
50
    auth_logout(request)
51
    if next_page is not None:
52
        next_page = resolve_url(next_page)
53
    else:
54
        next_page = '/'
55
    return HttpResponseRedirect(next_page)
56

  
57

  
58
class HomepageView(TemplateView):
59
    template_name = 'lingo/homepage.html'
60

  
61

  
62
homepage = HomepageView.as_view()
lingo/wsgi.py
1
# lingo - payment and billing system
2
# Copyright (C) 2022  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
import os
18

  
19
from django.core.wsgi import get_wsgi_application
20

  
21
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lingo.settings")
22

  
23
application = get_wsgi_application()
manage.py
1
#!/usr/bin/env python3
2
import os
3
import sys
4

  
5
if __name__ == "__main__":
6
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lingo.settings")
7

  
8
    from django.core.management import execute_from_command_line
9

  
10
    execute_from_command_line(sys.argv)
pylint.rc
1
[MASTER]
2
profile=no
3
persistent=yes
4
ignore=vendor,Bouncers,ezt.py
5
cache-size=500
6

  
7
[MESSAGES CONTROL]
8
disable=
9
    abstract-method,
10
    arguments-differ,
11
    assignment-from-none,
12
    attribute-defined-outside-init,
13
    bad-super-call,
14
    broad-except,
15
    consider-using-dict-comprehension,
16
    consider-using-f-string,
17
    consider-using-set-comprehension,
18
    cyclic-import,
19
    duplicate-code,
20
    exec-used,
21
    fixme,
22
    global-variable-undefined,
23
    import-outside-toplevel,
24
    inconsistent-return-statements,
25
    invalid-name,
26
    keyword-arg-before-vararg,
27
    missing-class-docstring,
28
    missing-function-docstring,
29
    missing-module-docstring,
30
    no-else-return,
31
    no-member,
32
    no-self-use,
33
    non-parent-init-called,
34
    not-callable,
35
    possibly-unused-variable,
36
    protected-access,
37
    raise-missing-from,
38
    redefined-argument-from-local,
39
    redefined-builtin,
40
    redefined-outer-name,
41
    signature-differs,
42
    stop-iteration-return,
43
    super-init-not-called,
44
    superfluous-parens,
45
    too-many-ancestors,
46
    too-many-arguments,
47
    too-many-branches,
48
    too-many-instance-attributes,
49
    too-many-lines,
50
    too-many-locals,
51
    too-many-nested-blocks,
52
    too-many-return-statements,
53
    too-many-statements,
54
    undefined-loop-variable,
55
    unnecessary-comprehension,
56
    unspecified-encoding,
57
    unsubscriptable-object,
58
    unsupported-membership-test,
59
    unused-argument,
60
    use-a-generator,
61
    use-implicit-booleaness-not-comparison
62

  
63

  
64
[REPORTS]
65
output-format=parseable
66
include-ids=yes
67

  
68

  
69
[BASIC]
70
no-docstring-rgx=__.*__|_.*
71
class-rgx=[A-Z_][a-zA-Z0-9_]+$
72
function-rgx=[a-zA_][a-zA-Z0-9_]{2,70}$
73
method-rgx=[a-z_][a-zA-Z0-9_]{2,70}$
74
const-rgx=(([A-Z_][A-Z0-9_]*)|([a-z_][a-z0-9_]*)|(__.*__)|register|urlpatterns)$
75
good-names=_,i,j,k,e,x,Run,,setUp,tearDown,r,p,s,v,fd
76

  
77
[TYPECHECK]
78

  
79
# Tells whether missing members accessed in mixin class should be ignored. A
80
# mixin class is detected if its name ends with "mixin" (case insensitive).
81
ignore-mixin-members=yes
82

  
83
# List of classes names for which member attributes should not be checked
84
# (useful for classes with attributes dynamically set).
85
ignored-classes=SQLObject,WSGIRequest,Publisher,NullSessionManager
86

  
87
# When zope mode is activated, add a predefined set of Zope acquired attributes
88
# to generated-members.
89
zope=no
90

  
91
# List of members which are set dynamically and missed by pylint inference
92
# system, and so shouldn't trigger E0201 when accessed.
93
generated-members=objects,DoesNotExist,id,pk,_meta,base_fields,context
94

  
95
# List of method names used to declare (i.e. assign) instance attributes
96
defining-attr-methods=__init__,__new__,setUp
97

  
98

  
99
[VARIABLES]
100
init-import=no
101
dummy-variables-rgx=_|dummy
102
additional-builtins=_,N_,ngettext
103
good-names=_,i,j,k,e,x,Run,,setUp,tearDown,r,p,s,v,fd
104

  
105
[SIMILARITIES]
106
min-similarity-lines=6
107
ignore-comments=yes
108
ignore-docstrings=yes
109

  
110

  
111
[MISCELLANEOUS]
112
notes=FIXME,XXX,TODO
113

  
114

  
115
[FORMAT]
116
max-line-length=160
117
max-module-lines=2000
118
indent-string='    '
119

  
120

  
121
[DESIGN]
122
max-args=10
123
max-locals=15
124
max-returns=6
125
max-branchs=12
126
max-statements=50
127
max-parents=7
128
max-attributes=7
129
min-public-methods=0
130
max-public-methods=50
pylint.sh
1
#!/bin/bash
2
set -e -x
3
env
4

  
5
pylint -f parseable --rcfile pylint.rc "$@" | tee pylint.out; test $PIPESTATUS -eq 0
setup.py
1
#! /usr/bin/env python
2

  
3
import glob
4
import itertools
5
import os
6
import re
7
import subprocess
8
import sys
9
from distutils.cmd import Command
10
from distutils.command.build import build as _build
11
from distutils.command.sdist import sdist
12
from distutils.errors import CompileError
13
from distutils.spawn import find_executable
14

  
15
from setuptools import find_packages, setup
16
from setuptools.command.install_lib import install_lib as _install_lib
17

  
18

  
19
class eo_sdist(sdist):
20
    def run(self):
21
        if os.path.exists('VERSION'):
22
            os.remove('VERSION')
23
        version = get_version()
24
        with open('VERSION', 'w') as fd:
25
            fd.write(version)
26
        with open('lingo/version.py', 'w') as fd:
27
            fd.write('VERSION = %r' % version)
28
        sdist.run(self)
29
        if os.path.exists('VERSION'):
30
            os.remove('VERSION')
31

  
32

  
33
def get_version():
34
    """Use the VERSION, if absent generates a version with git describe, if not
35
    tag exists, take 0.0- and add the length of the commit log.
36
    """
37
    if os.path.exists('VERSION'):
38
        with open('VERSION') as v:
39
            return v.read()
40
    if os.path.exists('.git'):
41
        p = subprocess.Popen(
42
            ['git', 'describe', '--dirty=.dirty', '--match=v*'],
43
            stdout=subprocess.PIPE,
44
            stderr=subprocess.PIPE,
45
        )
46
        result = p.communicate()[0]
47
        if p.returncode == 0:
48
            result = result.decode('ascii').strip()[1:]  # strip spaces/newlines and initial v
49
            if '-' in result:  # not a tagged version
50
                real_number, commit_count, commit_hash = result.split('-', 2)
51
                version = '%s.post%s+%s' % (real_number, commit_count, commit_hash)
52
            else:
53
                version = result
54
            return version
55
        else:
56
            return '0.0.post%s' % len(subprocess.check_output(['git', 'rev-list', 'HEAD']).splitlines())
57
    return '0.0'
58

  
59

  
60
def data_tree(destdir, sourcedir):
61
    extensions = ['.css', '.png', '.jpeg', '.jpg', '.gif', '.xml', '.html', '.js']
62
    r = []
63
    for root, dirs, files in os.walk(sourcedir):
64
        l = [os.path.join(root, x) for x in files if os.path.splitext(x)[1] in extensions]
65
        r.append((root.replace(sourcedir, destdir, 1), l))
66
    return r
67

  
68

  
69
class compile_translations(Command):
70
    description = 'compile message catalogs to MO files via django compilemessages'
71
    user_options = []
72

  
73
    def initialize_options(self):
74
        pass
75

  
76
    def finalize_options(self):
77
        pass
78

  
79
    def run(self):
80
        orig_dir = os.getcwd()
81
        try:
82
            from django.core.management import call_command
83

  
84
            for path, dirs, files in os.walk('lingo'):
85
                if 'locale' not in dirs:
86
                    continue
87
                curdir = os.getcwd()
88
                os.chdir(os.path.realpath(path))
89
                call_command('compilemessages')
90
                os.chdir(curdir)
91
        except ImportError:
92
            sys.stderr.write('!!! Please install Django >= 1.4 to build translations\n')
93
        os.chdir(orig_dir)
94

  
95

  
96
class compile_scss(Command):
97
    description = 'compile scss files into css files'
98
    user_options = []
99

  
100
    def initialize_options(self):
101
        pass
102

  
103
    def finalize_options(self):
104
        pass
105

  
106
    def run(self):
107
        sass_bin = None
108
        for program in ('sassc', 'sass'):
109
            sass_bin = find_executable(program)
110
            if sass_bin:
111
                break
112
        if not sass_bin:
113
            raise CompileError(
114
                'A sass compiler is required but none was found.  See sass-lang.com for choices.'
115
            )
116

  
117
        for path, dirnames, filenames in os.walk('lingo'):
118
            for filename in filenames:
119
                if not filename.endswith('.scss'):
120
                    continue
121
                if filename.startswith('_'):
122
                    continue
123
                subprocess.check_call(
124
                    [
125
                        sass_bin,
126
                        '%s/%s' % (path, filename),
127
                        '%s/%s' % (path, filename.replace('.scss', '.css')),
128
                    ]
129
                )
130

  
131

  
132
class build(_build):
133
    sub_commands = [('compile_translations', None), ('compile_scss', None)] + _build.sub_commands
134

  
135

  
136
class install_lib(_install_lib):
137
    def run(self):
138
        self.run_command('compile_translations')
139
        _install_lib.run(self)
140

  
141

  
142
setup(
143
    name='lingo',
144
    version=get_version(),
145
    description='Payments and Bills System',
146
    author='Thomas NOËL',
147
    author_email='tnoel@entrouvert.com',
148
    packages=find_packages(exclude=['tests']),
149
    include_package_data=True,
150
    scripts=('manage.py',),
151
    url='https://dev.entrouvert.org/projects/lingo/',
152
    classifiers=[
153
        'Development Status :: 4 - Beta',
154
        'Environment :: Web Environment',
155
        'Framework :: Django',
156
        'Intended Audience :: Developers',
157
        'License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)',
158
        'Operating System :: OS Independent',
159
        'Programming Language :: Python',
160
        'Programming Language :: Python :: 3',
161
    ],
162
    install_requires=[
163
        'django>=2.2, <2.3',
164
        'gadjo>=0.53',
165
        'requests',
166
        'eopayment>=1.60',
167
        'djangorestframework>=3.3, <3.10',
168
    ],
169
    zip_safe=False,
170
    cmdclass={
171
        'build': build,
172
        'compile_scss': compile_scss,
173
        'compile_translations': compile_translations,
174
        'install_lib': install_lib,
175
        'sdist': eo_sdist,
176
    },
177
)
tests/conftest.py
1
import django_webtest
2
import pytest
3
from django.contrib.auth.models import User
4
from django.core.cache import cache
5

  
6

  
7
@pytest.fixture(autouse=True)
8
def media(settings, tmpdir):
9
    settings.MEDIA_ROOT = str(tmpdir.mkdir('media'))
10

  
11

  
12
@pytest.fixture
13
def app(request):
14
    wtm = django_webtest.WebTestMixin()
15
    wtm._patch_settings()
16
    request.addfinalizer(wtm._unpatch_settings)
17
    cache.clear()
18
    return django_webtest.DjangoTestApp()
19

  
20

  
21
@pytest.fixture
22
def simple_user():
23
    return User.objects.create_user('user', password='user')
24

  
25

  
26
@pytest.fixture
27
def admin_user():
28
    return User.objects.create_superuser('admin', email=None, password='admin')
tests/settings.py
1
import os
2

  
3
LANGUAGE_CODE = 'en-us'
4
TIME_ZONE = 'UTC'
5

  
6
CACHES = {
7
    'default': {
8
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
9
    },
10
    'dummy': {'BACKEND': 'django.core.cache.backends.dummy.DummyCache'},
11
}
12

  
13
DATABASES = {
14
    'default': {
15
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
16
        'TEST': {
17
            'NAME': ('lingo-test-%s' % os.environ.get("BRANCH_NAME", "").replace('/', '-'))[:63],
18
        },
19
    }
20
}
tests/test_homepage.py
1
import pytest
2

  
3
pytestmark = pytest.mark.django_db
4

  
5

  
6
def test_homepage(app):
7
    assert app.get('/', status=200)
tests/test_manager.py
1
import pytest
2

  
3
pytestmark = pytest.mark.django_db
4

  
5

  
6
def login(app, username='admin', password='admin'):
7
    login_page = app.get('/login/')
8
    login_form = login_page.forms[0]
9
    login_form['username'] = username
10
    login_form['password'] = password
11
    resp = login_form.submit()
12
    assert resp.status_int == 302
13
    return app
14

  
15

  
16
def test_unlogged_access(app):
17
    # connect while not being logged in
18
    assert app.get('/manage/', status=302).location.endswith('/login/?next=/manage/')
19

  
20

  
21
def test_simple_user_access(app, simple_user):
22
    # connect while being logged as a simple user
23
    app = login(app, username='user', password='user')
24
    assert app.get('/manage/', status=403)
25

  
26

  
27
def test_access(app, admin_user):
28
    app = login(app)
29
    assert app.get('/manage/', status=200)
tox.ini
1
[tox]
2
toxworkdir = {env:TMPDIR:/tmp}/tox-{env:USER}/lingo/{env:BRANCH_NAME:}
3
envlist = coverage-py3-django22-codestyle, pylint
4

  
5
[testenv]
6
usedevelop = True
7
setenv =
8
  DJANGO_SETTINGS_MODULE=lingo.settings
9
  LINGO_SETTINGS_FILE=tests/settings.py
10
  TOX_WORK_DIR={toxworkdir}
11
  SETUPTOOLS_USE_DISTUTILS=stdlib
12
  coverage: COVERAGE=--cov-report xml --cov-report html --cov=lingo/ --cov-config .coveragerc -v
13
  DB_ENGINE=django.db.backends.postgresql_psycopg2
14
passenv =
15
  BRANCH_NAME
16
deps =
17
  django22: django>=2.2,<2.3
18
  pytest-cov
19
  pytest-django
20
  pytest-freezegun
21
  pytest!=5.3.3
22
  WebTest
23
  mock<4
24
  httmock
25
  pylint
26
  pylint-django
27
  django-webtest<1.9.3
28
  pyquery
29
  psycopg2-binary<2.9
30
  django-mellon>=1.13
31
  pre-commit
32
commands =
33
  ./getlasso3.sh
34
  python manage.py compilemessages
35
  py.test {env:COVERAGE:} {posargs: --junitxml=junit-{envname}.xml tests/}
36
  codestyle: pre-commit run --all-files --show-diff-on-failure
37

  
38
[testenv:pylint]
39
setenv =
40
  DJANGO_SETTINGS_MODULE=lingo.settings
41
  LINGO_SETTINGS_FILE=tests/settings.py
42
  TOX_WORK_DIR={toxworkdir}
43
  SETUPTOOLS_USE_DISTUTILS=stdlib
44
  DB_ENGINE=django.db.backends.postgresql_psycopg2
45
deps =
46
  pytest-django
47
  pytest!=5.3.3
48
  WebTest
49
  mock<4
50
  httmock
51
  django-mellon>=1.13
52
  pylint
53
  pylint-django
54
  django-webtest<1.9.3
55
  pyquery
56
  psycopg2-binary<2.9
57
commands =
58
  ./getlasso3.sh
59
  ./pylint.sh lingo/ tests/
0
-