misc: apply pre-commit-hooks (#74976)

This commit is contained in:
Valentin Deniaud 2023-03-01 14:27:18 +01:00
parent d7035c8fe4
commit bd064c1296
12 changed files with 249 additions and 266 deletions

11
debian/control vendored
View File

@ -2,13 +2,14 @@ Source: passerelle-reunion-fsn
Maintainer: Emmanuel Cazenave <ecazenave@entrouvert.com> Maintainer: Emmanuel Cazenave <ecazenave@entrouvert.com>
Section: python Section: python
Priority: optional Priority: optional
Build-Depends: python3-setuptools, python3-all, debhelper-compat (= 12), dh-python, python3-django Build-Depends: debhelper-compat (= 12),
dh-python,
python3-all,
python3-django,
python3-setuptools,
Standards-Version: 3.9.1 Standards-Version: 3.9.1
Package: python3-passerelle-reunion-fsn Package: python3-passerelle-reunion-fsn
Architecture: all Architecture: all
Depends: ${misc:Depends}, Depends: python3-unicodecsv, ${misc:Depends}, ${python3:Depends}
${python3:Depends},
python3-unicodecsv
Description: Passerelle CR Reunion FSN (Python 3) Description: Passerelle CR Reunion FSN (Python 3)

View File

@ -1,9 +1,8 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-04-16 12:12 # Generated by Django 1.11.29 on 2020-04-16 12:12
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models
import passerelle_reunion_fsn.models import passerelle_reunion_fsn.models
@ -19,7 +18,10 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='DSDossier', name='DSDossier',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), (
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('ds_id', models.CharField(max_length=256)), ('ds_id', models.CharField(max_length=256)),
('ds_state', models.CharField(max_length=256)), ('ds_state', models.CharField(max_length=256)),
('csv_file', models.FileField(upload_to=passerelle_reunion_fsn.models.csv_file_location)), ('csv_file', models.FileField(upload_to=passerelle_reunion_fsn.models.csv_file_location)),
@ -30,7 +32,10 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='Entreprise', name='Entreprise',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), (
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('code_application', models.CharField(max_length=20)), ('code_application', models.CharField(max_length=20)),
('sequence', models.CharField(max_length=16)), ('sequence', models.CharField(max_length=16)),
('periode', models.CharField(max_length=35)), ('periode', models.CharField(max_length=35)),
@ -58,15 +63,34 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='FSNReunionConnector', name='FSNReunionConnector',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), (
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('title', models.CharField(max_length=50, verbose_name='Title')), ('title', models.CharField(max_length=50, verbose_name='Title')),
('slug', models.SlugField(unique=True, verbose_name='Identifier')), ('slug', models.SlugField(unique=True, verbose_name='Identifier')),
('description', models.TextField(verbose_name='Description')), ('description', models.TextField(verbose_name='Description')),
('api_url', models.URLField(max_length=400, verbose_name='DS API URL')), ('api_url', models.URLField(max_length=400, verbose_name='DS API URL')),
('token', models.CharField(max_length=256, verbose_name='DS token')), ('token', models.CharField(max_length=256, verbose_name='DS token')),
('demarche_number', models.IntegerField(verbose_name='Demarche number')), ('demarche_number', models.IntegerField(verbose_name='Demarche number')),
('instructeur_id', models.CharField(blank=True, help_text='Region identifier for this case', max_length=256, verbose_name='Instructeur identifier')), (
('users', models.ManyToManyField(blank=True, related_name='_fsnreunionconnector_users_+', related_query_name='+', to='base.ApiUser')), 'instructeur_id',
models.CharField(
blank=True,
help_text='Region identifier for this case',
max_length=256,
verbose_name='Instructeur identifier',
),
),
(
'users',
models.ManyToManyField(
blank=True,
related_name='_fsnreunionconnector_users_+',
related_query_name='+',
to='base.ApiUser',
),
),
], ],
options={ options={
'verbose_name': 'FSN Reunion', 'verbose_name': 'FSN Reunion',
@ -75,19 +99,25 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='entreprise', model_name='entreprise',
name='resource', name='resource',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='passerelle_reunion_fsn.FSNReunionConnector'), field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to='passerelle_reunion_fsn.FSNReunionConnector'
),
), ),
migrations.AddField( migrations.AddField(
model_name='dsdossier', model_name='dsdossier',
name='resource', name='resource',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='dossiers', to='passerelle_reunion_fsn.FSNReunionConnector'), field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='dossiers',
to='passerelle_reunion_fsn.FSNReunionConnector',
),
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='entreprise', name='entreprise',
unique_together=set([('resource', 'siren'), ('resource', 'sequence')]), unique_together={('resource', 'siren'), ('resource', 'sequence')},
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='dsdossier', name='dsdossier',
unique_together=set([('resource', 'ds_id')]), unique_together={('resource', 'ds_id')},
), ),
] ]

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-04-17 14:52 # Generated by Django 1.11.29 on 2020-04-17 14:52
from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-04-19 05:45 # Generated by Django 1.11.29 on 2020-04-19 05:45
from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.18 on 2020-04-24 15:37 # Generated by Django 1.11.18 on 2020-04-24 15:37
from __future__ import unicode_literals
from django.db import migrations, models
import django.contrib.postgres.fields.jsonb import django.contrib.postgres.fields.jsonb
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models
import passerelle_reunion_fsn.models import passerelle_reunion_fsn.models
@ -18,7 +17,10 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='Batch', name='Batch',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), (
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('batch_date', models.DateField()), ('batch_date', models.DateField()),
('last_update_datetime', models.DateTimeField(auto_now=True)), ('last_update_datetime', models.DateTimeField(auto_now=True)),
], ],
@ -29,12 +31,25 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='BatchFile', name='BatchFile',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), (
('csv_file', models.FileField(upload_to=passerelle_reunion_fsn.models.batch_csv_file_location)), 'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
(
'csv_file',
models.FileField(upload_to=passerelle_reunion_fsn.models.batch_csv_file_location),
),
('csv_filename', models.CharField(max_length=256)), ('csv_filename', models.CharField(max_length=256)),
('ready', models.BooleanField(default=False)), ('ready', models.BooleanField(default=False)),
('last_update_datetime', models.DateTimeField(auto_now=True)), ('last_update_datetime', models.DateTimeField(auto_now=True)),
('batch', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='files', to='passerelle_reunion_fsn.Batch')), (
'batch',
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='files',
to='passerelle_reunion_fsn.Batch',
),
),
], ],
), ),
migrations.AddField( migrations.AddField(
@ -50,10 +65,14 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='batch', model_name='batch',
name='resource', name='resource',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='batches', to='passerelle_reunion_fsn.FSNReunionConnector'), field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='batches',
to='passerelle_reunion_fsn.FSNReunionConnector',
),
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='batch', name='batch',
unique_together=set([('resource', 'batch_date')]), unique_together={('resource', 'batch_date')},
), ),
] ]

View File

@ -1,8 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations from django.db import migrations
from passerelle.utils.db import EnsureJsonbType from passerelle.utils.db import EnsureJsonbType

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.18 on 2020-05-19 09:58 # Generated by Django 1.11.18 on 2020-05-19 09:58
from __future__ import unicode_literals
from django.db import migrations from django.db import migrations
@ -14,6 +12,6 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='entreprise', name='entreprise',
unique_together=set([('resource', 'sequence'), ('resource', 'periode', 'siren')]), unique_together={('resource', 'sequence'), ('resource', 'periode', 'siren')},
), ),
] ]

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.18 on 2020-05-29 23:37 # Generated by Django 1.11.18 on 2020-05-29 23:37
from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
# passerelle-reunion-fsn # passerelle-reunion-fsn
# Copyright (C) 2020 Entr'ouvert # Copyright (C) 2020 Entr'ouvert
# #
@ -17,19 +15,20 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import base64 import base64
from datetime import datetime import csv
import hashlib import hashlib
from io import BytesIO
import os import os
import os.path import os.path
import tempfile import tempfile
from datetime import datetime
from io import BytesIO
from django.conf import settings from django.conf import settings
from django.contrib.postgres.fields import JSONField from django.contrib.postgres.fields import JSONField
from django.core.files import File from django.core.files import File
from django.urls import reverse
from django.db import models, transaction from django.db import models, transaction
from django.http import FileResponse, HttpResponse from django.http import FileResponse, HttpResponse
from django.urls import reverse
from django.utils import dateformat, six from django.utils import dateformat, six
from django.utils.dateparse import parse_date, parse_datetime from django.utils.dateparse import parse_date, parse_datetime
from django.utils.encoding import force_str, force_text, smart_text from django.utils.encoding import force_str, force_text, smart_text
@ -40,12 +39,6 @@ from passerelle.base.signature import sign_url
from passerelle.utils.api import endpoint from passerelle.utils.api import endpoint
from passerelle.utils.jsonresponse import APIError from passerelle.utils.jsonresponse import APIError
if six.PY3:
import csv
else:
import unicodecsv as csv
GENERATE_BATCH_SCHEMA = { GENERATE_BATCH_SCHEMA = {
"$schema": "http://json-schema.org/draft-04/schema#", "$schema": "http://json-schema.org/draft-04/schema#",
"title": "Generate batch", "title": "Generate batch",
@ -59,8 +52,8 @@ GENERATE_BATCH_SCHEMA = {
"force": { "force": {
"description": "Force", "description": "Force",
"type": "boolean", "type": "boolean",
} },
} },
} }
@ -103,7 +96,8 @@ def batch_csv_file_location(instance, filename):
return 'fsn_reunion/%s/batch/%s/%s/%s' % ( return 'fsn_reunion/%s/batch/%s/%s/%s' % (
instance.batch.resource.id, instance.batch.resource.id,
dateformat.format(instance.batch.batch_date, 'Y-m-d'), dateformat.format(instance.batch.batch_date, 'Y-m-d'),
instance.id, filename instance.id,
filename,
) )
@ -118,8 +112,10 @@ class FSNReunionConnector(BaseResource):
token = models.CharField(max_length=256, verbose_name=_('DS token')) token = models.CharField(max_length=256, verbose_name=_('DS token'))
demarche_number = models.IntegerField(verbose_name=_('Demarche number')) demarche_number = models.IntegerField(verbose_name=_('Demarche number'))
instructeur_id = models.CharField( instructeur_id = models.CharField(
max_length=256, blank=True, verbose_name=_('Instructeur identifier'), max_length=256,
help_text=_('Region identifier for this case') blank=True,
verbose_name=_('Instructeur identifier'),
help_text=_('Region identifier for this case'),
) )
wcs_form_slug = models.CharField(max_length=256, blank=True, verbose_name=_('WCS form slug')) wcs_form_slug = models.CharField(max_length=256, blank=True, verbose_name=_('WCS form slug'))
wcs_options = JSONField(null=True, blank=True) wcs_options = JSONField(null=True, blank=True)
@ -136,13 +132,8 @@ class FSNReunionConnector(BaseResource):
self.logger.info('end generate batch') self.logger.info('end generate batch')
def _ds_call(self, query, variables): def _ds_call(self, query, variables):
headers = { headers = {'Authorization': 'Bearer token=' + self.token}
'Authorization': 'Bearer token=' + self.token data = {'query': query, 'variables': variables}
}
data = {
'query': query,
'variables': variables
}
response = self.requests.post(self.api_url, headers=headers, json=data) response = self.requests.post(self.api_url, headers=headers, json=data)
if response.status_code != 200: if response.status_code != 200:
raise APIError('An error occured, status code : %s' % response.status_code) raise APIError('An error occured, status code : %s' % response.status_code)
@ -160,7 +151,7 @@ class FSNReunionConnector(BaseResource):
def _wcs_call(self, filters={}): def _wcs_call(self, filters={}):
if not getattr(settings, 'KNOWN_SERVICES', {}).get('wcs'): if not getattr(settings, 'KNOWN_SERVICES', {}).get('wcs'):
raise APIError('No wcs found') raise APIError('No wcs found')
wcs_service = list(settings.KNOWN_SERVICES['wcs'].values())[0] wcs_service = list(settings.KNOWN_SERVICES['wcs'].values())[0]
if self.wcs_options and 'instance' in self.wcs_options: if self.wcs_options and 'instance' in self.wcs_options:
@ -169,12 +160,7 @@ class FSNReunionConnector(BaseResource):
orig = wcs_service.get('orig') orig = wcs_service.get('orig')
secret = wcs_service.get('secret') secret = wcs_service.get('secret')
limit = 10 limit = 10
params = { params = {'orig': orig, 'full': 'on', 'limit': limit, 'order_by': '-receipt_time'}
'orig': orig,
'full': 'on',
'limit': limit,
'order_by': '-receipt_time'
}
params.update(self.wcs_options.get('filters', {})) params.update(self.wcs_options.get('filters', {}))
params.update(filters) params.update(filters)
@ -184,11 +170,8 @@ class FSNReunionConnector(BaseResource):
params['offset'] = offset params['offset'] = offset
query_string = urlparse.urlencode(params) query_string = urlparse.urlencode(params)
api_url = sign_url( api_url = sign_url(
urlparse.urljoin( urlparse.urljoin(base_url, 'api/forms/%s/list?%s' % (self.wcs_form_slug, query_string)),
base_url, key=secret,
'api/forms/%s/list?%s' % (self.wcs_form_slug, query_string)
),
key=secret
) )
response = self.requests.get(api_url) response = self.requests.get(api_url)
if response.status_code != 200: if response.status_code != 200:
@ -197,22 +180,18 @@ class FSNReunionConnector(BaseResource):
if not data: if not data:
has_data = False has_data = False
else: else:
for form in data: yield from data
yield form
offset += limit offset += limit
@endpoint( @endpoint(methods=['get'], perm='can_access', name='wcs-call', description=_('Call wcs debug'))
methods=['get'], perm='can_access', name='wcs-call',
description=_('Call wcs debug')
)
def wcs_call(self, request): def wcs_call(self, request):
return { return {'data': [form for form in self._wcs_call()]}
'data': [form for form in self._wcs_call()]
}
@endpoint( @endpoint(
methods=['get'], perm='can_access', name='dsproxy-get-instructeurs', methods=['get'],
description=_('DS Proxy: get instructeurs') perm='can_access',
name='dsproxy-get-instructeurs',
description=_('DS Proxy: get instructeurs'),
) )
def dsproxy_get_instructeurs(self, request): def dsproxy_get_instructeurs(self, request):
query = ''' query = '''
@ -225,15 +204,13 @@ query getInstructeurs($demarcheNumber: Int!){
} }
} }
''' '''
variables = { variables = {'demarcheNumber': self.demarche_number}
'demarcheNumber': self.demarche_number return {'data': self._ds_call(query, variables)}
}
return {
'data': self._ds_call(query, variables)
}
@endpoint( @endpoint(
methods=['get'], perm='can_access', name='dsproxy-get-liste-champs', methods=['get'],
perm='can_access',
name='dsproxy-get-liste-champs',
description=_('DS Proxy: get fields identifiers'), description=_('DS Proxy: get fields identifiers'),
) )
def dsproxy_get_liste_champs(self, request): def dsproxy_get_liste_champs(self, request):
@ -249,12 +226,8 @@ query getChampDescriptors($demarcheNumber: Int!) {
} }
} }
''' '''
variables = { variables = {'demarcheNumber': self.demarche_number}
'demarcheNumber': self.demarche_number return {'data': self._ds_call(query, variables)}
}
return {
'data': self._ds_call(query, variables)
}
def _ds_get_dossiers(self): def _ds_get_dossiers(self):
if not self.instructeur_id: if not self.instructeur_id:
@ -328,10 +301,7 @@ query getDossiers($demarcheNumber: Int!, $createdSince: ISO8601DateTime, $first:
} }
} }
''' '''
variables = { variables = {'demarcheNumber': self.demarche_number, 'after': None}
'demarcheNumber': self.demarche_number,
'after': None
}
dossiers = [] dossiers = []
has_next_page = True has_next_page = True
while has_next_page: while has_next_page:
@ -346,32 +316,26 @@ query getDossiers($demarcheNumber: Int!, $createdSince: ISO8601DateTime, $first:
def get_passage_en_construction(x): def get_passage_en_construction(x):
return parse_datetime(x['datePassageEnConstruction']) return parse_datetime(x['datePassageEnConstruction'])
return { return {'dossiers': sorted(dossiers, key=get_passage_en_construction), 'num_dossiers': len(dossiers)}
'dossiers': sorted(dossiers, key=get_passage_en_construction),
'num_dossiers': len(dossiers)
}
@endpoint( @endpoint(
methods=['get'], perm='can_access', name='dsproxy-get-dossiers', methods=['get'],
description=_('DS Proxy: get dossiers') perm='can_access',
name='dsproxy-get-dossiers',
description=_('DS Proxy: get dossiers'),
) )
def dsproxy_get_dossiers(self, request): def dsproxy_get_dossiers(self, request):
return { return {'data': self._ds_get_dossiers()}
'data': self._ds_get_dossiers()
}
@endpoint( @endpoint(
methods=['post'], perm='can_access', name='fetch-dossiers', methods=['post'],
description=_('Fetch dossiers from DS and consolidate into local data') perm='can_access',
name='fetch-dossiers',
description=_('Fetch dossiers from DS and consolidate into local data'),
) )
def fetch_dossiers(self, request): def fetch_dossiers(self, request):
dossiers = self._fetch_dossiers(request) dossiers = self._fetch_dossiers(request)
return { return {'data': {'dossiers': dossiers, 'num_dossiers': len(dossiers)}}
'data': {
'dossiers': dossiers,
'num_dossiers': len(dossiers)
}
}
def _fetch_dossiers(self, request=None): def _fetch_dossiers(self, request=None):
res = [] res = []
@ -398,8 +362,11 @@ query getDossiers($demarcheNumber: Int!, $createdSince: ISO8601DateTime, $first:
response = self.requests.get(file_url) response = self.requests.get(file_url)
assert response.status_code == 200 assert response.status_code == 200
ds_dossier = DSDossier.objects.create( ds_dossier = DSDossier.objects.create(
resource=self, ds_id=id_dossier, csv_filename=filename, resource=self,
ds_state=dossier['state'], csv_checksum=champ['file']['checksum'] ds_id=id_dossier,
csv_filename=filename,
ds_state=dossier['state'],
csv_checksum=champ['file']['checksum'],
) )
ds_dossier.csv_file.save(filename, BytesIO(response.content)) ds_dossier.csv_file.save(filename, BytesIO(response.content))
@ -430,30 +397,22 @@ query getDossiers($demarcheNumber: Int!, $createdSince: ISO8601DateTime, $first:
return res return res
@endpoint( @endpoint(methods=['get'], perm='can_access', name='get-dossiers', description=_('Get dossiers'))
methods=['get'], perm='can_access', name='get-dossiers',
description=_('Get dossiers')
)
def get_dossiers(self, request): def get_dossiers(self, request):
res = [] res = []
for dossier in self.dossiers.all(): for dossier in self.dossiers.all():
res.append(dossier.to_json(request)) res.append(dossier.to_json(request))
return { return {'data': {'dossiers': res}}
'data': {
'dossiers': res
}
}
@endpoint( @endpoint(
methods=['get'], perm='can_access', example_pattern='{dossier_pk}/', methods=['get'],
pattern='^(?P<dossier_pk>\w+)/$', name='get-dossier-file', perm='can_access',
parameters={ example_pattern='{dossier_pk}/',
'dossier_pk': { pattern=r'^(?P<dossier_pk>\w+)/$',
'description': _('Local dossier identifier'), name='get-dossier-file',
'example_value': '2' parameters={'dossier_pk': {'description': _('Local dossier identifier'), 'example_value': '2'}},
} description=_('Get csv file from dossier'),
}, description=_('Get csv file from dossier')
) )
def get_dossier_file(self, request, dossier_pk): def get_dossier_file(self, request, dossier_pk):
try: try:
@ -466,15 +425,14 @@ query getDossiers($demarcheNumber: Int!, $createdSince: ISO8601DateTime, $first:
return response return response
@endpoint( @endpoint(
methods=['get'], perm='can_access', name='get-csv', methods=['get'], perm='can_access', name='get-csv', description=_('Get consolidated data (csv file)')
description=_('Get consolidated data (csv file)')
) )
def get_csv(self, request): def get_csv(self, request):
class Echo:
class Echo(object):
"""An object that implements just the write method of the file-like """An object that implements just the write method of the file-like
interface. interface.
""" """
def write(self, value): def write(self, value):
"""Write the value by returning it, instead of storing in a buffer.""" """Write the value by returning it, instead of storing in a buffer."""
return value return value
@ -486,22 +444,16 @@ query getDossiers($demarcheNumber: Int!, $createdSince: ISO8601DateTime, $first:
pseudo_buffer = Echo() pseudo_buffer = Echo()
writer = csv.writer(pseudo_buffer) writer = csv.writer(pseudo_buffer)
response = FileResponse( response = FileResponse((writer.writerow(row) for row in get_rows()), content_type="text/csv")
(writer.writerow(row) for row in get_rows()), content_type="text/csv"
)
response['Content-Disposition'] = 'attachment; filename="somefilename.csv"' response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
return response return response
@endpoint( @endpoint(
methods=['get'], perm='can_access', name='get-data', methods=['get'], perm='can_access', name='get-data', description=_('Get data by sequence or siren')
description=_('Get data by sequence or siren')
) )
def get_data(self, request, sequence=None, siren=None): def get_data(self, request, sequence=None, siren=None):
def build_result(entreprise): def build_result(entreprise):
return { return {'data': {attr: getattr(entreprise, attr) for attr in COLUMNS_KEYNAMES}}
'data': {attr: getattr(entreprise, attr) for attr in COLUMNS_KEYNAMES}
}
if sequence is None and siren is None: if sequence is None and siren is None:
raise APIError('Need sequence or siren') raise APIError('Need sequence or siren')
@ -518,14 +470,9 @@ query getDossiers($demarcheNumber: Int!, $createdSince: ISO8601DateTime, $first:
if entreprise: if entreprise:
return build_result(entreprise) return build_result(entreprise)
return { return {'data': {}}
'data': {}
}
@endpoint( @endpoint(methods=['get'], perm='can_access', name='get-batches', description=_('Get batches'))
methods=['get'], perm='can_access', name='get-batches',
description=_('Get batches')
)
def get_batches(self, request): def get_batches(self, request):
res = [] res = []
for batch in self.batches.all(): for batch in self.batches.all():
@ -533,43 +480,37 @@ query getDossiers($demarcheNumber: Int!, $createdSince: ISO8601DateTime, $first:
{ {
'id': batch.pk, 'id': batch.pk,
'text': dateformat.format(batch.batch_date, 'l d F Y'), 'text': dateformat.format(batch.batch_date, 'l d F Y'),
'url': request.build_absolute_uri(batch.get_absolute_url()) 'url': request.build_absolute_uri(batch.get_absolute_url()),
} }
) )
return {'data': res} return {'data': res}
@endpoint( @endpoint(
methods=['get'], perm='can_access', name='batchfile', methods=['get'],
pattern='^(?P<batchfile_pk>\w+)/$', example_pattern='{batchfile_pk}/', perm='can_access',
parameters={ name='batchfile',
'batchfile_pk': { pattern=r'^(?P<batchfile_pk>\w+)/$',
'description': _('Batch file identifier'), example_pattern='{batchfile_pk}/',
'example_value': '2' parameters={'batchfile_pk': {'description': _('Batch file identifier'), 'example_value': '2'}},
} description=_('Get batch file'),
}, description=_('Get batch file')
) )
def batchfile(self, request, batchfile_pk): def batchfile(self, request, batchfile_pk):
try: try:
batch_file = BatchFile.objects.get(pk=batchfile_pk) batch_file = BatchFile.objects.get(pk=batchfile_pk)
except BatchFile.DoesNotExist: except BatchFile.DoesNotExist:
raise APIError('Unkwon batch file identifier') raise APIError('Unkwon batch file identifier')
response = HttpResponse( response = HttpResponse(batch_file.csv_file.read(), content_type="text/csv")
batch_file.csv_file.read(), content_type="text/csv"
)
response['Content-Disposition'] = 'attachment; filename="%s"' % batch_file.csv_filename response['Content-Disposition'] = 'attachment; filename="%s"' % batch_file.csv_filename
return response return response
@endpoint( @endpoint(
methods=['get'], perm='can_access', name='batch', methods=['get'],
perm='can_access',
name='batch',
description=_('Get batch'), description=_('Get batch'),
pattern='^(?P<batch_pk>\w+)/$', example_pattern='{batch_pk}/', pattern=r'^(?P<batch_pk>\w+)/$',
parameters={ example_pattern='{batch_pk}/',
'batch_pk': { parameters={'batch_pk': {'description': _('Batch identifier'), 'example_value': '2'}},
'description': _('Batch identifier'),
'example_value': '2'
}
}
) )
def batch(self, request, batch_pk): def batch(self, request, batch_pk):
try: try:
@ -593,20 +534,15 @@ query getDossiers($demarcheNumber: Int!, $createdSince: ISO8601DateTime, $first:
res['batch_files'] = batch_files res['batch_files'] = batch_files
res.update(batch.to_json()) res.update(batch.to_json())
res['url'] = request.build_absolute_uri(res['url']) res['url'] = request.build_absolute_uri(res['url'])
return { return {'data': res}
'data': res
}
@endpoint( @endpoint(
perm='can_access', name='generate-batches', perm='can_access',
name='generate-batches',
post={ post={
'description': _('Generate batches'), 'description': _('Generate batches'),
'request_body': { 'request_body': {'schema': {'application/json': GENERATE_BATCH_SCHEMA}},
'schema': { },
'application/json': GENERATE_BATCH_SCHEMA
}
}
}
) )
def generate_batches(self, request, post_data): def generate_batches(self, request, post_data):
batch_date = post_data.get('batch_date') batch_date = post_data.get('batch_date')
@ -616,10 +552,7 @@ query getDossiers($demarcheNumber: Int!, $createdSince: ISO8601DateTime, $first:
data['url'] = request.build_absolute_uri(data['url']) data['url'] = request.build_absolute_uri(data['url'])
return data return data
return { return {'data': [abs_url(batch.to_json()) for batch in self._generate_batches(batch_date, force)]}
'data': [abs_url(batch.to_json()) for batch in self._generate_batches(batch_date, force)]
}
@transaction.atomic @transaction.atomic
def _generate_batches(self, batch_date=None, force=False): def _generate_batches(self, batch_date=None, force=False):
@ -645,12 +578,8 @@ query getDossiers($demarcheNumber: Int!, $createdSince: ISO8601DateTime, $first:
def add_target_batch(refs, target): def add_target_batch(refs, target):
fd, temp_file_name = tempfile.mkstemp() fd, temp_file_name = tempfile.mkstemp()
os.close(fd) os.close(fd)
if six.PY3: f = open(temp_file_name, 'w', encoding='utf-8')
f = open(temp_file_name, 'w', encoding='utf-8') refs[target] = (csv.writer(f, delimiter=';'), f, temp_file_name)
refs[target] = (csv.writer(f, delimiter=';'), f, temp_file_name)
else:
f = open(temp_file_name, 'wb')
refs[target] = (csv.writer(f, delimiter=';', encoding='utf-8'), f, temp_file_name)
target_batches = {} target_batches = {}
now = datetime.now().date() now = datetime.now().date()
@ -686,7 +615,9 @@ query getDossiers($demarcheNumber: Int!, $createdSince: ISO8601DateTime, $first:
sequence = "%s-%s" % (code_region, form['display_id']) sequence = "%s-%s" % (code_region, form['display_id'])
period = 'MARS-VOLET2' period = 'MARS-VOLET2'
siren = form_fields['siren'] siren = form_fields['siren']
nom1 = get_data(form_fields, 'nom_responsable') + ' ' + get_data(form_fields, 'prenom_responsable') nom1 = (
get_data(form_fields, 'nom_responsable') + ' ' + get_data(form_fields, 'prenom_responsable')
)
nom2 = '' nom2 = ''
nb_salaries = form_fields.get('nb_salaries', 0) nb_salaries = form_fields.get('nb_salaries', 0)
rue = form_fields['numero_voie'] rue = form_fields['numero_voie']
@ -704,10 +635,28 @@ query getDossiers($demarcheNumber: Int!, $createdSince: ISO8601DateTime, $first:
write_row( write_row(
target_batches[batch][0], target_batches[batch][0],
[ [
code_app, sequence, period, siren, nom1, nom2, nb_salaries, rue, code_app,
bp, cp, ville, code_pays, code_region, iban, montant, devise, nom_demandeur, sequence,
prenom_demandeur, qualite, tel, courriel period,
] siren,
nom1,
nom2,
nb_salaries,
rue,
bp,
cp,
ville,
code_pays,
code_region,
iban,
montant,
devise,
nom_demandeur,
prenom_demandeur,
qualite,
tel,
courriel,
],
) )
# create batch file objects # create batch file objects
@ -725,9 +674,11 @@ query getDossiers($demarcheNumber: Int!, $createdSince: ISO8601DateTime, $first:
class Entreprise(models.Model): class Entreprise(models.Model):
class Meta: class Meta:
unique_together = (('resource', 'sequence'), ('resource', 'periode', 'siren'),) unique_together = (
('resource', 'sequence'),
('resource', 'periode', 'siren'),
)
resource = models.ForeignKey(FSNReunionConnector, on_delete=models.CASCADE) resource = models.ForeignKey(FSNReunionConnector, on_delete=models.CASCADE)
code_application = models.CharField(max_length=20) code_application = models.CharField(max_length=20)
@ -761,13 +712,10 @@ class Entreprise(models.Model):
class DSDossier(models.Model): class DSDossier(models.Model):
class Meta: class Meta:
unique_together = (('resource', 'ds_id'),) unique_together = (('resource', 'ds_id'),)
resource = models.ForeignKey( resource = models.ForeignKey(FSNReunionConnector, on_delete=models.CASCADE, related_name='dossiers')
FSNReunionConnector, on_delete=models.CASCADE, related_name='dossiers'
)
ds_id = models.CharField(max_length=256) ds_id = models.CharField(max_length=256)
ds_state = models.CharField(max_length=256) ds_state = models.CharField(max_length=256)
csv_file = models.FileField(upload_to=csv_file_location) csv_file = models.FileField(upload_to=csv_file_location)
@ -776,14 +724,17 @@ class DSDossier(models.Model):
last_update_datetime = models.DateTimeField(auto_now=True) last_update_datetime = models.DateTimeField(auto_now=True)
def to_json(self, request=None): def to_json(self, request=None):
csv_file_url = reverse( csv_file_url = (
'generic-endpoint', reverse(
kwargs={ 'generic-endpoint',
'connector': self.resource.get_connector_slug(), kwargs={
'slug': self.resource.slug, 'connector': self.resource.get_connector_slug(),
'endpoint': 'get-dossier-file' 'slug': self.resource.slug,
} 'endpoint': 'get-dossier-file',
) + '/%s/' % self.id },
)
+ '/%s/' % self.id
)
if request is not None: if request is not None:
csv_file_url = request.build_absolute_uri(csv_file_url) csv_file_url = request.build_absolute_uri(csv_file_url)
@ -794,7 +745,7 @@ class DSDossier(models.Model):
'csv_filename': self.csv_filename, 'csv_filename': self.csv_filename,
'csv_file': csv_file_url, 'csv_file': csv_file_url,
'csv_checksum': self.csv_checksum, 'csv_checksum': self.csv_checksum,
'last_update_datetime': self.last_update_datetime 'last_update_datetime': self.last_update_datetime,
} }
def get_content_without_bom(self): def get_content_without_bom(self):
@ -827,14 +778,11 @@ class DSDossier(models.Model):
class Batch(models.Model): class Batch(models.Model):
class Meta: class Meta:
unique_together = (('resource', 'batch_date'),) unique_together = (('resource', 'batch_date'),)
ordering = ('batch_date',) ordering = ('batch_date',)
resource = models.ForeignKey( resource = models.ForeignKey(FSNReunionConnector, on_delete=models.CASCADE, related_name='batches')
FSNReunionConnector, on_delete=models.CASCADE, related_name='batches'
)
batch_date = models.DateField() batch_date = models.DateField()
last_update_datetime = models.DateTimeField(auto_now=True) last_update_datetime = models.DateTimeField(auto_now=True)
@ -842,25 +790,26 @@ class Batch(models.Model):
return { return {
'batch_date': self.batch_date, 'batch_date': self.batch_date,
'last_update_datetime': self.last_update_datetime, 'last_update_datetime': self.last_update_datetime,
'url': self.get_absolute_url() 'url': self.get_absolute_url(),
} }
def get_absolute_url(self): def get_absolute_url(self):
return reverse( return (
'generic-endpoint', reverse(
kwargs={ 'generic-endpoint',
'connector': self.resource.get_connector_slug(), kwargs={
'slug': self.resource.slug, 'connector': self.resource.get_connector_slug(),
'endpoint': 'batch' 'slug': self.resource.slug,
} 'endpoint': 'batch',
) + '/%s/' % self.pk },
)
+ '/%s/' % self.pk
)
class BatchFile(models.Model): class BatchFile(models.Model):
batch = models.ForeignKey( batch = models.ForeignKey(Batch, on_delete=models.CASCADE, related_name='files')
Batch, on_delete=models.CASCADE, related_name='files'
)
csv_file = models.FileField(upload_to=batch_csv_file_location) csv_file = models.FileField(upload_to=batch_csv_file_location)
csv_filename = models.CharField(max_length=256) csv_filename = models.CharField(max_length=256)
ready = models.BooleanField(default=False) ready = models.BooleanField(default=False)
@ -871,15 +820,18 @@ class BatchFile(models.Model):
'csv_filename': self.csv_filename, 'csv_filename': self.csv_filename,
'ready': self.ready, 'ready': self.ready,
'last_update_datetime': self.last_update_datetime, 'last_update_datetime': self.last_update_datetime,
'url': self.get_absolute_url() 'url': self.get_absolute_url(),
} }
def get_absolute_url(self): def get_absolute_url(self):
return reverse( return (
'generic-endpoint', reverse(
kwargs={ 'generic-endpoint',
'connector': self.batch.resource.get_connector_slug(), kwargs={
'slug': self.batch.resource.slug, 'connector': self.batch.resource.get_connector_slug(),
'endpoint': 'batchfile' 'slug': self.batch.resource.slug,
} 'endpoint': 'batchfile',
) + '/%s/' % self.pk },
)
+ '/%s/' % self.pk
)

View File

@ -2,13 +2,12 @@
import os import os
import subprocess import subprocess
from setuptools import setup, find_packages
from distutils.command.sdist import sdist from distutils.command.sdist import sdist
from setuptools import find_packages, setup
class eo_sdist(sdist): class eo_sdist(sdist):
def run(self): def run(self):
if os.path.exists('VERSION'): if os.path.exists('VERSION'):
os.remove('VERSION') os.remove('VERSION')
@ -23,10 +22,10 @@ class eo_sdist(sdist):
def get_version(): def get_version():
'''Use the VERSION, if absent generates a version with git describe, if not '''Use the VERSION, if absent generates a version with git describe, if not
tag exists, take 0.0- and add the length of the commit log. tag exists, take 0.0- and add the length of the commit log.
''' '''
if os.path.exists('VERSION'): if os.path.exists('VERSION'):
with open('VERSION', 'r') as v: with open('VERSION') as v:
return v.read() return v.read()
if os.path.exists('.git'): if os.path.exists('.git'):
p = subprocess.Popen( p = subprocess.Popen(
@ -54,10 +53,8 @@ setup(
author='Emmanuel Cazenave', author='Emmanuel Cazenave',
author_email='ecazenave@entrouvert.com', author_email='ecazenave@entrouvert.com',
packages=find_packages(), packages=find_packages(),
install_requires=[ install_requires=['unicodecsv'],
'unicodecsv'
],
cmdclass={ cmdclass={
'sdist': eo_sdist, 'sdist': eo_sdist,
} },
) )

View File

@ -1,6 +1,5 @@
import os import os
INSTALLED_APPS += ('passerelle_reunion_fsn',) INSTALLED_APPS += ('passerelle_reunion_fsn',)
@ -8,7 +7,8 @@ DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2', 'ENGINE': 'django.db.backends.postgresql_psycopg2',
'TEST': { 'TEST': {
'NAME': 'passerelle-reunion-fsn-test-%s' % os.environ.get("BRANCH_NAME", "").replace('/', '-')[:63], 'NAME': 'passerelle-reunion-fsn-test-%s'
} % os.environ.get("BRANCH_NAME", "").replace('/', '-')[:63],
},
} }
} }

View File

@ -1,13 +1,10 @@
# -*- coding: utf-8 -*-
from django.contrib.contenttypes.models import ContentType
from django.core.cache import cache
import django_webtest import django_webtest
import pytest import pytest
from django.contrib.contenttypes.models import ContentType
from django.core.cache import cache
from passerelle.base.models import AccessRight, ApiUser
from passerelle_reunion_fsn.models import FSNReunionConnector from passerelle_reunion_fsn.models import FSNReunionConnector
from passerelle.base.models import ApiUser, AccessRight
@pytest.fixture @pytest.fixture
@ -22,14 +19,13 @@ def app(request):
@pytest.fixture @pytest.fixture
def connector(db): def connector(db):
connector = FSNReunionConnector.objects.create( connector = FSNReunionConnector.objects.create(
slug='test', api_url='https://whatever', token='token', demarche_number=1, slug='test', api_url='https://whatever', token='token', demarche_number=1, instructeur_id='xxxx'
instructeur_id='xxxx'
) )
api = ApiUser.objects.create(username='all', keytype='', key='') api = ApiUser.objects.create(username='all', keytype='', key='')
obj_type = ContentType.objects.get_for_model(connector) obj_type = ContentType.objects.get_for_model(connector)
AccessRight.objects.create( AccessRight.objects.create(
codename='can_access', apiuser=api, codename='can_access', apiuser=api, resource_type=obj_type, resource_pk=connector.pk
resource_type=obj_type, resource_pk=connector.pk) )
def test_dummny(app, connector): def test_dummny(app, connector):