Projet

Général

Profil

0001-mail-feed-from-MaarchCourrier-22550.patch

Benjamin Dauvergne, 29 juin 2018 16:14

Télécharger (29,7 ko)

Voir les différences:

Subject: [PATCH] mail: feed from MaarchCourrier (#22550)

 debian/control                                |   2 +-
 setup.py                                      |   1 +
 tests/conftest.py                             |  51 ++++
 tests/test_source_maarch.py                   | 243 ++++++++++++++++++
 tests/test_source_phone.py                    |  10 -
 welco/sources/mail/__init__.py                |  26 ++
 welco/sources/mail/maarch.py                  | 233 +++++++++++++++++
 .../management/commands/feed_mail_maarch.py   |  57 ++++
 welco/sources/mail/utils.py                   |  72 ++++++
 welco/sources/mail/views.py                   |  19 +-
 10 files changed, 702 insertions(+), 12 deletions(-)
 create mode 100644 tests/conftest.py
 create mode 100644 tests/test_source_maarch.py
 create mode 100644 welco/sources/mail/maarch.py
 create mode 100644 welco/sources/mail/management/commands/feed_mail_maarch.py
 create mode 100644 welco/sources/mail/utils.py
debian/control
11 11
Depends: ${misc:Depends}, ${python:Depends},
12 12
    python-django (>= 1.7),
13 13
    python-gadjo,
14
    python-requests,
14
    python-requests (>= 2.11),
15 15
    python-django-haystack (>= 2.4.0),
16 16
    python-django-reversion (>= 2.0.12),
17 17
    python-django-taggit (>= 0.17.4),
setup.py
106 106
        'requests',
107 107
        'whoosh',
108 108
        'XStatic-Select2',
109
        'python-dateutil',
109 110
        ],
110 111
    zip_safe=False,
111 112
    cmdclass={
tests/conftest.py
1
# welco - multichannel request processing
2
# Copyright (C) 2015  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 pytest
18
import django_webtest
19

  
20

  
21
@pytest.fixture
22
def app(request):
23
    wtm = django_webtest.WebTestMixin()
24
    wtm._patch_settings()
25
    request.addfinalizer(wtm._unpatch_settings)
26
    return django_webtest.DjangoTestApp()
27

  
28

  
29
@pytest.fixture
30
def user(db):
31
    from django.contrib.auth.models import User
32

  
33
    user = User.objects.create(username='toto')
34
    user.set_password('toto')
35
    user.save()
36
    return user
37

  
38

  
39
@pytest.fixture
40
def mail_group(db, settings, user):
41
    from django.contrib.auth.models import Group
42

  
43
    # add mail group to default user
44
    group = Group.objects.create(name='mail')
45
    user.groups.add(group)
46

  
47
    # define authorization of mail group on mail channel
48
    channel_roles = getattr(settings, 'CHANNEL_ROLES', {})
49
    mail_roles = channel_roles.setdefault('mail', [])
50
    mail_roles.append('mail')
51
    return group
tests/test_source_maarch.py
1
# welco - multichannel request processing
2
# Copyright (C) 2015  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
import pytest
20

  
21
from httmock import urlmatch, HTTMock
22

  
23

  
24
class BaseMock(object):
25
    def __init__(self, netloc):
26
        self.netloc = netloc
27
        self.clear()
28

  
29
    def clear(self):
30
        self.requests = []
31
        self.responses = []
32

  
33
    def next_response(self):
34
        response, self.responses = self.responses[0], self.responses[1:]
35
        return response
36

  
37
    @property
38
    def ctx_manager(self):
39
        '''Create an HTTMock context manager for all endpoints of a mocked Maarch instance'''
40
        endpoints = []
41
        for attribute, value in self.__class__.__dict__.items():
42
            if hasattr(value, 'path'):
43
                value = getattr(self, attribute)
44
                match_decorator = urlmatch(netloc=self.netloc, path=value.path)
45
                print value, self.netloc, '^/rest' + value.path
46
                endpoints.append(match_decorator(value))
47
        return HTTMock(*endpoints)
48

  
49

  
50
class MaarchMock(BaseMock):
51
    def list_endpoint(self, url, request):
52
        self.requests.append(('list_endpoint', url, request, json.loads(request.body)))
53
        return {
54
            'content': json.dumps(self.next_response()),
55
            'headers': {
56
                'content-type': 'application/json',
57
            },
58
        }
59
    list_endpoint.path = '^/rest/res/list$'
60

  
61
    def update_external_infos(self, url, request):
62
        self.requests.append(('update_external_infos', url, request, json.loads(request.body)))
63
        return json.dumps({})
64
    update_external_infos.path = '^/rest/res/externalInfos$'
65

  
66
    def update_status(self, url, request):
67
        self.requests.append(('update_status', url, request, json.loads(request.body)))
68
        return {
69
            'content': json.dumps(self.next_response()),
70
            'headers': {
71
                'content-type': 'application/json',
72
            },
73
        }
74
    update_status.path = '^/rest/res/resource/status$'
75

  
76
    def post_courrier(self, url, request):
77
        self.requests.append(('post_courrier', url, request, json.loads(request.body)))
78
    post_courrier.path = '^/rest/res$'
79

  
80

  
81
@pytest.fixture
82
def maarch(settings, mail_group):
83
    # configure maarch server
84
    settings.MAARCH_FEED = {
85
        'ENABLE': True,
86
        'URL': 'http://maarch.example.net/',
87
        'USERNAME': 'admin',
88
        'PASSWORD': 'admin',
89
    }
90
    return MaarchMock('maarch.example.net')
91

  
92

  
93
class WcsMock(BaseMock):
94
    def api_formdefs(self, url, request):
95
        return json.dumps({
96
            'data': [{
97
                'slug': 'slug1',
98
                'title': 'title1',
99
            }]
100
        })
101
    api_formdefs.path = '^/api/formdefs/$'
102

  
103
    def json(self, url, request):
104
        return json.dumps({
105
            'data': [{
106
                'slug': 'slug1',
107
                'title': 'title1',
108
                'category': 'category1',
109
            }]
110
        })
111
    json.path = '^/json$'
112

  
113
    def api_formdefs_slug1_schema(self, url, request):
114
        return json.dumps({
115
        })
116
    api_formdefs_slug1_schema.path = '^/api/formdefs/slug-1/schema$'
117

  
118
    def api_formdefs_slug1_submit(self, url, request):
119
        return json.dumps({
120
            'err': 0,
121
            'data': {
122
                'id': 1,
123
                'backoffice_url': 'http://wcs.example.net/slug-1/1',
124
            },
125
        })
126
    api_formdefs_slug1_submit.path = '^/api/formdefs/slug-1/submit$'
127

  
128

  
129
@pytest.fixture
130
def wcs(settings):
131
    settings.KNOWN_SERVICES = {
132
        'wcs': {
133
            'demarches': {
134
                'url': 'http://wcs.example.net/',
135
            }
136
        }
137
    }
138
    return WcsMock('wcs.example.net')
139

  
140

  
141
def test_utils(maarch):
142
    from welco.sources.mail.utils import get_maarch
143

  
144
    welco_maarch_obj = get_maarch()
145
    assert welco_maarch_obj.url == 'http://maarch.example.net/'
146
    assert welco_maarch_obj.username == 'admin'
147
    assert welco_maarch_obj.password == 'admin'
148
    assert welco_maarch_obj.grc_status == 'GRC'
149
    assert welco_maarch_obj.grc_received_status == 'GRC_TRT'
150
    assert welco_maarch_obj.grc_send_status == 'GRCSENT'
151
    assert welco_maarch_obj.grc_refused_status == 'GRCREFUSED'
152

  
153

  
154
PDF_MOCK = '%PDF-1.4 ...'
155

  
156

  
157
def test_feed(app, maarch, wcs, user):
158
    import base64
159
    from django.core.management import call_command
160
    from django.contrib.contenttypes.models import ContentType
161
    from welco.sources.mail.models import Mail
162

  
163
    app.set_user(user.username)
164
    response = app.get('/').follow()
165
    # no mail
166
    assert len(response.pyquery('li[data-registered-mail-number]')) == 0
167

  
168
    # feed mails from maarch
169
    with maarch.ctx_manager:
170
        # list request
171
        maarch.responses.append({
172
            'resources': [
173
                {
174
                    'res_id': 1,
175
                    'fileBase64Content': base64.b64encode(PDF_MOCK),
176
                }
177
            ],
178
        })
179
        # update status request
180
        maarch.responses.append({})
181
        # last list request
182
        maarch.responses.append({'resources': []})
183
        call_command('feed_mail_maarch')
184
        assert len(maarch.requests) == 3
185
        assert maarch.requests[0][3] == {
186
            'select': '*',
187
            'clause': "status='GRC'",
188
            'withFile': True,
189
            'orderBy': ['res_id'],
190
            'limit': 10,
191
        }
192
        assert maarch.requests[1][3] == {
193
            'resId': [1],
194
            'status': 'GRC_TRT',
195
        }
196
        assert maarch.requests[2][3] == {
197
            'select': '*',
198
            'clause': "status='GRC'",
199
            'withFile': True,
200
            'orderBy': ['res_id'],
201
            'limit': 10,
202
        }
203
    response = app.get('/').follow()
204

  
205
    # new mail is visible
206
    assert len(response.pyquery('li[data-registered-mail-number]')) == 1
207
    assert len(response.pyquery('li[data-registered-mail-number=maarch-1]')) == 1
208

  
209
    # start qualification
210
    maarch.clear()
211
    pk = Mail.objects.get().pk
212
    with wcs.ctx_manager, maarch.ctx_manager:
213
        source_type = str(ContentType.objects.get_for_model(Mail).pk),
214
        source_pk = str(pk)
215

  
216
        response = app.get('/ajax/qualification', params={
217
            'source_type': source_type,
218
            'source_pk': source_pk,
219
        })
220

  
221
        assert len(response.pyquery('a[data-association-pk]')) == 0
222
        response = app.post('/ajax/qualification', params={
223
            'source_type': source_type,
224
            'source_pk': str(pk),
225
            'formdef_reference': 'demarches:slug-1',
226
        })
227

  
228
        # verify qualification was done
229
        assert len(response.pyquery('a[data-association-pk]')) == 1
230
        association_pk = response.pyquery('a[data-association-pk]')[0].attrib['data-association-pk']
231

  
232
        response = app.post('/ajax/create-formdata/%s' % association_pk)
233
    assert len(maarch.requests) == 1
234
    assert maarch.requests[0][3] == {
235
        'status': 'GRCSENT',
236
        'externalInfos': [
237
            {
238
                'external_id': 1,
239
                'external_link': 'http://wcs.example.net/slug-1/1',
240
                'res_id': 1,
241
            }
242
        ]
243
    }
tests/test_source_phone.py
28 28
pytestmark = pytest.mark.django_db
29 29

  
30 30

  
31
@pytest.fixture
32
def user():
33
    from django.contrib.auth.models import User
34

  
35
    user = User.objects.create(username='toto')
36
    user.set_password('toto')
37
    user.save()
38
    return user
39

  
40

  
41 31
def test_call_start_stop(client):
42 32
    assert models.PhoneCall.objects.count() == 0
43 33
    payload = {
welco/sources/mail/__init__.py
16 16

  
17 17
import django.apps
18 18

  
19

  
19 20
class AppConfig(django.apps.AppConfig):
20 21
    name = 'welco.sources.mail'
21 22

  
......
23 24
        from . import urls
24 25
        return urls.urlpatterns
25 26

  
27
    def ready(self):
28
        from welco.qualif.models import Association
29
        from django.db.models import signals
30

  
31
        signals.post_save.connect(self.association_post_save,
32
            sender=Association)
33

  
34
    def association_post_save(self, sender, instance, **kwargs):
35
        from .utils import get_maarch
36

  
37
        if not instance.formdata_id:
38
            return
39
        source = instance.source
40
        if not hasattr(source, 'registered_mail_number'):
41
            return
42
        registered_mail_number = source.registered_mail_number
43
        if not registered_mail_number.startswith('maarch-'):
44
            return
45
        maarch_pk = int(registered_mail_number.split('-', 1)[-1])
46
        maarch = get_maarch()
47
        maarch.set_grc_sent_status(
48
            mail_pk=maarch_pk,
49
            formdata_id=instance.formdata_id,
50
            formdata_url_backoffice=instance.formdata_url_backoffice)
51

  
26 52
default_app_config = 'welco.sources.mail.AppConfig'
welco/sources/mail/maarch.py
1
# welco - multichannel request processing
2
# Copyright (C) 2015  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 urlparse
18
import base64
19

  
20
from dateutil.parser import parse as parse_datetime
21

  
22
import requests
23
from requests.adapters import HTTPAdapter
24
from requests.packages.urllib3.util.retry import Retry
25

  
26

  
27
class MaarchError(Exception):
28
    pass
29

  
30

  
31
class MaarchCourrier(object):
32
    url = None
33
    username = None
34
    password = None
35
    default_limit = 100
36
    max_retries = 3
37

  
38
    def __init__(self, url, username, password):
39
        self.url = url
40
        self.username = username
41
        self.password = password
42

  
43
    def __repr__(self):
44
        return '<MaarchCourrier url:%s>' % self.url
45

  
46
    class Courrier(object):
47
        content = None
48
        format = None
49
        status = None
50

  
51
        def __init__(self, maarch_courrier, **kwargs):
52
            self.maarch_courrier = maarch_courrier
53
            self.pk = kwargs.pop('res_id', None)
54
            # decode file content
55
            if 'fileBase64Content' in kwargs:
56
                kwargs['content'] = base64.b64decode(kwargs.pop('fileBase64Content'))
57
            # decode date fields
58
            for key in kwargs:
59
                if key.endswith('_date') and kwargs[key]:
60
                    kwargs[key] = parse_datetime(kwargs[key])
61
            self.__dict__.update(kwargs)
62

  
63
        def __repr__(self):
64
            descriptions = []
65
            for key in ['pk', 'status']:
66
                if getattr(self, key, None):
67
                    descriptions.append('%s:%s' % (key, getattr(self, key)))
68
            return '<Courrier %s>' % ' '.join(descriptions)
69

  
70
        @classmethod
71
        def new_with_file(cls, maarch_courrier, content, format, status, **kwargs):
72
            if hasattr(content, 'read'):
73
                content = content.read()
74
            else:
75
                content = content
76
            return cls(maarch_courrier, content=content, format=format, status=status, **kwargs)
77

  
78
        def post_serialize(self):
79
            payload = {}
80
            assert self.content
81
            assert self.status
82
            payload['encodedFile'] = base64.b64encode(self.content)
83
            payload['collId'] = 'letterbox_coll'
84
            payload['table'] = 'res_letterbox'
85
            payload['fileFormat'] = self.fileFormat
86
            payload['data'] = d = []
87
            excluded_keys = ['content', 'format', 'status', 'maarch_courrier', 'pk']
88
            data = {key: self.__dict__[key] for key in self.__dict__ if key not in excluded_keys}
89
            if data:
90
                for key, value in data.iteritems():
91
                    if isinstance(value, basestring):
92
                        d.append({'column': key, 'value': value, 'type': 'string'})
93
                    elif isinstance(value, int):
94
                        d.append({'column': key, 'value': str(value), 'type': 'int'})
95
                    else:
96
                        raise NotImplementedError
97
            payload['status'] = self.status
98
            return payload
99

  
100
        def get_serialize(self):
101
            d = {'res_id': self.pk}
102
            for key in self.__dict__:
103
                if key in ['pk', 'maarch_courrier']:
104
                    continue
105
                value = getattr(self, key)
106
                if key == 'content':
107
                    value = base64.b64encode(value)
108
                    key = 'fileBase64Content'
109
                if key.endswith('_date'):
110
                    value = value.isoformat()
111
                d[key] = value
112
            return d
113

  
114
    def new_courrier_with_file(self, content, format, status, **kwargs):
115
        return self.Courrier.new_with_file(self, content, format, status, **kwargs)
116

  
117
    @property
118
    def session(self):
119
        s = requests.Session()
120
        if self.username and self.password:
121
            s.auth = (self.username, self.password)
122
        retry = Retry(
123
            total=self.max_retries,
124
            read=self.max_retries,
125
            connect=self.max_retries,
126
            backoff_factor=0.5,
127
            status_forcelist=(500, 502, 504)
128
        )
129
        adapter = HTTPAdapter(max_retries=retry)
130
        s.mount('http://', adapter)
131
        s.mount('https://', adapter)
132
        return s
133

  
134
    def post_json(self, url, payload, verb='post'):
135
        try:
136
            method = getattr(self.session, verb)
137
            response = method(url, json=payload)
138
        except requests.RequestException as e:
139
            raise MaarchError('HTTP request to maarch failed', e, payload)
140
        try:
141
            response.raise_for_status()
142
        except requests.RequestException as e:
143
            raise MaarchError('HTTP request to maarch failed', e, payload, repr(response.content[:1000]))
144
        try:
145
            response_payload = response.json()
146
        except ValueError:
147
            raise MaarchError('maarch returned non-JSON data', repr(response.content[:1000]), payload)
148
        return response_payload
149

  
150
    def put_json(self, url, payload):
151
        return self.post_json(url, payload, verb='put')
152

  
153
    @property
154
    def list_url(self):
155
        return urlparse.urljoin(self.url, 'rest/res/list')
156

  
157
    @property
158
    def update_external_infos_url(self):
159
        return urlparse.urljoin(self.url, 'rest/res/externalInfos')
160

  
161
    @property
162
    def update_status_url(self):
163
        return urlparse.urljoin(self.url, 'rest/res/resource/status')
164

  
165
    @property
166
    def post_courrier_url(self):
167
        return urlparse.urljoin(self.url, 'rest/res')
168

  
169
    def get_courriers(self, clause, fields=None, limit=None, include_file=False, order_by=None):
170
        if fields:
171
            # res_id is mandatory
172
            fields = set(fields)
173
            fields.add('res_id')
174
        fields = ','.join(fields) if fields else '*'
175
        limit = limit or self.default_limit
176
        order_by = order_by or []
177
        response = self.post_json(self.list_url, {
178
            'select': fields,
179
            'clause': clause,
180
            'limit': limit,
181
            'withFile': include_file,
182
            'orderBy': order_by,
183
        })
184
        if not hasattr(response.get('resources'), 'append'):
185
            raise MaarchError('missing resources field or bad type', response)
186
        return [self.Courrier(self, **resource) for resource in response['resources']]
187

  
188
    def update_external_infos(self, courriers, status):
189
        if not courriers:
190
            return
191
        external_infos = []
192
        payload = {
193
            'externalInfos': external_infos,
194
            'status': status,
195
        }
196
        for courrier in courriers:
197
            assert courrier.pk, 'courrier must already exist in Maarch and have a pk'
198
            external_info = {'res_id': courrier.pk}
199
            if getattr(courrier, 'external_id', None):
200
                external_info['external_id'] = courrier.external_id
201
            if getattr(courrier, 'external_link', None):
202
                external_info['external_link'] = courrier.external_link
203
            external_infos.append(external_info)
204
        response = self.put_json(self.update_external_infos_url, payload)
205
        if 'errors' in response:
206
            raise MaarchError('update_external_infos failed with errors', response['errors'], response)
207

  
208
    def update_status(self, courriers, status, history_message=None):
209
        if not courriers:
210
            return
211
        res_ids = []
212
        for courrier in courriers:
213
            assert courrier.pk
214
            res_ids.append(courrier.pk)
215
        payload = {
216
            'status': status,
217
            'resId': res_ids,
218
        }
219
        if history_message:
220
            payload['historyMessage'] = history_message
221
        response = self.put_json(self.update_status_url, payload)
222

  
223
        if 'errors' in response:
224
            raise MaarchError('update_status failed with errors', response['errors'], response)
225

  
226
    def post_courrier(self, courrier):
227
        response = self.post_json(self.post_courrier_url, courrier.post_serialize())
228
        if 'errors' in response:
229
            raise MaarchError('update_external_infos failed with errors', response['errors'], response)
230
        if 'resId' not in response:
231
            raise MaarchError('update_external_infos failed with errors, missing resId', response)
232
        courrier.pk = response['resId']
233
        return courrier
welco/sources/mail/management/commands/feed_mail_maarch.py
1
# welco - multichannel request processing
2
# Copyright (C) 2015  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 optparse import make_option
18
import os
19

  
20
from django.core.files.base import ContentFile
21
from django.core.management.base import BaseCommand, CommandError
22
from django.conf import settings
23
from django.db import transaction
24

  
25
from ...models import Mail
26
from ...utils import get_maarch
27

  
28
class Command(BaseCommand):
29
    """Inject mail coming from Maarch into welco.
30

  
31
       Only mail with a status "GRC" are injected,
32
       After injection, their status is immediately changed to "GRC_TRT".
33
       After injection in w.c.s., their status is changed to "GRCSENT" and an
34
       id and an URL of the request in w.c.s. is attached to the mail in
35
       Maarch.
36
    """
37

  
38
    def handle(self, *args, **kwargs):
39
        verbosity = kwargs['verbosity']
40
        maarch = get_maarch()
41

  
42
        maarch_mails = maarch.get_mails()
43
        count = 0
44
        while maarch_mails:
45
            with transaction.atomic():
46
                for maarch_mail in maarch_mails:
47
                    Mail.objects.create(
48
                        content=ContentFile(maarch_mail.content, name='maarch-%s' % maarch_mail.pk),
49
                        registered_mail_number='maarch-%s' % str(maarch_mail.pk),  # res_id
50
                    )
51
                # update maarch inside transaction, if it fails all imports will be
52
                # rollbacked
53
                maarch.set_grc_received_status(maarch_mails)
54
                count += len(maarch_mails)
55
            maarch_mails = maarch.get_mails()
56
        if verbosity > 1:
57
            self.stdout.write('Injected %d mails from %s.' % (count, maarch.url))
welco/sources/mail/utils.py
1
# welco - multichannel request processing
2
# Copyright (C) 2015  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

  
19
from .maarch import MaarchCourrier
20

  
21

  
22
class WelcoMaarchCourrier(MaarchCourrier):
23
    def __init__(self, url, username, password, grc_status,
24
                 grc_received_status, grc_send_status, grc_refused_status,
25
                 batch_size=10):
26
        super(WelcoMaarchCourrier, self).__init__(url, username, password)
27
        self.grc_status = grc_status
28
        self.grc_received_status = grc_received_status
29
        self.grc_send_status = grc_send_status
30
        self.grc_refused_status = grc_refused_status
31
        self.batch_size = batch_size
32

  
33
    def get_mails(self):
34
        return self.get_courriers(
35
            clause="status='%s'" % self.grc_status,
36
            include_file=True,
37
            order_by=['res_id'],
38
            limit=self.batch_size)
39

  
40
    def get_mail(self, mail_id):
41
        return self.get_courriers(clause="res_id=%s" % mail_id)[0]
42

  
43
    def set_grc_received_status(self, mails):
44
        self.update_status(mails, self.grc_received_status)
45

  
46
    def set_grc_sent_status(self, mail_pk, formdata_id, formdata_url_backoffice):
47
        mail = self.Courrier(self, pk=mail_pk)
48
        mail.external_id = formdata_id
49
        mail.external_link = formdata_url_backoffice
50
        self.update_external_infos([mail], self.grc_send_status)
51

  
52
    def set_grc_refused_status(self, mail_pk):
53
        mail = self.Courrier(self, pk=mail_pk)
54
        self.update_status([mail], self.grc_refused_status)
55

  
56

  
57
def get_maarch():
58
    config = getattr(settings, 'MAARCH_FEED', {})
59
    if not config.get('ENABLE'):
60
        return
61
    url = config['URL']
62
    username = config['USERNAME']
63
    password = config['PASSWORD']
64
    return WelcoMaarchCourrier(
65
        url=url,
66
        username=username,
67
        password=password,
68
        grc_status=config.get('STATUS_GRC', 'GRC'),
69
        grc_received_status=config.get('STATUS_RECEIVED', 'GRC_TRT'),
70
        grc_send_status=config.get('STATUS_SEND', 'GRCSENT'),
71
        grc_refused_status=config.get('STATUS_REFUSED', 'GRCREFUSED'))
72

  
welco/sources/mail/views.py
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17 17
import json
18
import logging
18 19

  
19 20
from django import template
20 21
from django.contrib.auth.decorators import login_required
......
26 27
from django.utils.translation import ugettext_lazy as _
27 28
from django.views.decorators.csrf import csrf_exempt
28 29
from django.views.generic import TemplateView
30
from django.db.transaction import atomic
29 31

  
30 32
from welco.utils import response_for_json
31 33

  
32 34
from .models import Mail
33 35
from .forms import MailQualificationForm
36
from .utils import get_maarch
37

  
38

  
39
logger = logging.getLogger(__name__)
34 40

  
35 41
def viewer(request, *args, **kwargs):
36 42
    if not 'file' in request.GET:
......
124 130
@login_required
125 131
@csrf_exempt
126 132
def reject(request, *args, **kwargs):
127
    Mail.objects.filter(id=request.POST['source_pk']).delete()
133
    maarch = get_maarch()
134
    mail = Mail.objects.filter(id=request.POST['source_pk']).first()
135
    if mail:
136
        try:
137
            with atomic():
138
                if maarch and mail.registered_mail_number and mail.registered_mail_number.startswith('maarch-'):
139
                    mail_pk = mail.registered_mail_number.split('-', 1)[1]
140
                    maarch.set_grc_refused_status(mail_pk)
141
                mail.delete()
142
        except Exception:
143
            logger.exception('rejection request to maarch failed')
144
            messages.error(request, _('Rejection request to Maarch failed'))
128 145
    return HttpResponse()
129 146

  
130 147

  
131
-