passerelle/tests/test_import_export.py

319 lines
10 KiB
Python

import json
import os
import sys
import tempfile
from io import BytesIO, StringIO
import pytest
from django.contrib.contenttypes.models import ContentType
from django.core.files import File
from django.core.management import call_command
from django.test import Client
from django.urls import reverse
from django.utils import timezone
from django.utils.encoding import force_bytes, force_str
from passerelle.apps.base_adresse.models import BaseAdresse
from passerelle.apps.bdp.models import Bdp
from passerelle.apps.csvdatasource.models import CsvDataSource, Query
from passerelle.apps.ovh.models import OVHSMSGateway
from passerelle.apps.sector.models import SectorResource
from passerelle.base.models import AccessRight, ApiUser
from passerelle.utils import ImportSiteError, export_site, import_site
data = """121;69981;DELANOUE;Eliot;H
525;6;DANIEL WILLIAMS;Shanone;F
253;67742;MARTIN;Sandra;F
511;38142;MARCHETTI;Lucie;F
235;22;MARTIN;Sandra;F
620;52156;ARNAUD;Mathis;H
902;36;BRIGAND;Coline;F
2179;48548;THEBAULT;Salima;F
3420;46;WILSON-LUZAYADIO;Anaëlle;F
1421;55486;WONE;Fadouma;F
2841;51;FIDJI;Zakia;F
2431;59;BELICARD;Sacha;H
4273;60;GOUBERT;Adrien;H
4049;64;MOVSESSIAN;Dimitri;H
4605;67;ABDOU BACAR;Kyle;H
4135;22231;SAVERIAS;Marius;H
4809;75;COROLLER;Maelys;F
5427;117;KANTE;Aliou;H
116642;118;ZAHMOUM;Yaniss;H
216352;38;Dupont;Benoît;H
"""
data_bom = force_str(data, 'utf-8').encode('utf-8-sig')
SECTOR_CSV = """street_id,street_name,parity,min_housenumber,max_housenumber,sector_id,sector_name
75114_1912,rue du Château,P,,, gs-moulin, Groupe Scolaire Moulin"""
pytestmark = pytest.mark.django_db
TEST_BASE_DIR = os.path.join(os.path.dirname(__file__), 'data', 'csvdatasource')
def get_file_content(filename):
return open(os.path.join(TEST_BASE_DIR, filename), 'rb').read()
@pytest.fixture
def setup():
def maker(
columns_keynames='fam,id,lname,fname,sex', filename='data.csv', sheet_name='Feuille2', data=b''
):
api = ApiUser.objects.create(username='all', keytype='', key='')
csv = CsvDataSource.objects.create(
csv_file=File(BytesIO(data), filename),
sheet_name=sheet_name,
columns_keynames=columns_keynames,
slug='test',
title='a title',
description='a description',
)
obj_type = ContentType.objects.get_for_model(csv)
AccessRight.objects.create(
codename='can_access', apiuser=api, resource_type=obj_type, resource_pk=csv.pk
)
url = reverse(
'generic-endpoint',
kwargs={
'connector': 'csvdatasource',
'slug': csv.slug,
'endpoint': 'data',
},
)
return csv, url
return maker
def parse_response(response):
return json.loads(response.content)['data']
@pytest.fixture
def client():
return Client()
@pytest.fixture(params=['data.csv', 'data.ods', 'data.xls', 'data.xlsx'])
def filetype(request):
return request.param
def get_output_of_command(command, *args, **kwargs):
old_stdout = sys.stdout
output = sys.stdout = StringIO()
call_command(command, *args, **kwargs)
sys.stdout = old_stdout
return output.getvalue()
def clear():
Query.objects.all().delete()
CsvDataSource.objects.all().delete()
ApiUser.objects.all().delete()
BaseAdresse.objects.all().delete()
SectorResource.objects.all().delete()
def test_export_csvdatasource(app, setup, filetype):
csvdata, _ = setup('id,whatever,nom,prenom,sexe', filename=filetype, data=get_file_content(filetype))
query = Query(slug='query-1_', resource=csvdata, structure='array')
query.projections = '\n'.join(['id:int(id)', 'prenom:prenom'])
query.save()
first = export_site()
clear()
import_site(first, import_users=True)
second = export_site()
# we don't care about file names
first['resources'][0]['csv_file']['name'] = 'whocares'
second['resources'][0]['csv_file']['name'] = 'whocares'
assert first == second
output = get_output_of_command('export_site')
third = json.loads(output)
third['resources'][0]['csv_file']['name'] = 'whocares'
assert first == third
clear()
with tempfile.NamedTemporaryFile() as f:
f.write(force_bytes(output))
f.flush()
call_command('import_site', f.name, import_users=True)
fourth = export_site()
fourth['resources'][0]['csv_file']['name'] = 'whocares'
assert first == fourth
Query.objects.all().delete()
with tempfile.NamedTemporaryFile() as f:
f.write(force_bytes(output))
f.flush()
call_command('import_site', f.name, import_users=True)
assert Query.objects.count() == 0
with tempfile.NamedTemporaryFile() as f:
f.write(force_bytes(output))
f.flush()
call_command('import_site', f.name, overwrite=True, import_users=True)
assert Query.objects.count() == 1
query = Query(slug='query-2_', resource=CsvDataSource.objects.get(), structure='array')
query.projections = '\n'.join(['id:int(id)', 'prenom:prenom'])
query.save()
assert Query.objects.count() == 2
with tempfile.NamedTemporaryFile() as f:
f.write(force_bytes(output))
f.flush()
call_command('import_site', f.name, clean=True, overwrite=True, import_users=True)
assert Query.objects.count() == 1
assert Query.objects.filter(slug='query-1_').exists()
def test_import_export_empty_filefield(app, setup, filetype):
Bdp.objects.create(service_url='https://bdp.example.com/')
first = export_site()
clear()
import_site(first)
second = export_site()
assert first == second
assert first['resources'][0]['keystore'] is None
assert second['resources'][0]['keystore'] is None
def test_export_to_file(app, setup, filetype):
bdp = Bdp.objects.create(service_url='https://bdp.example.com/')
with tempfile.NamedTemporaryFile() as f:
call_command('export_site', output=f.name)
assert Bdp.objects.count() == 1
bdp.delete()
assert Bdp.objects.count() == 0
with open(f.name) as fd:
import_site(json.load(fd), overwrite=True)
assert Bdp.objects.count() == 1
def test_export_log_level(app, setup):
bdp = Bdp.objects.create(service_url='https://bdp.example.com/')
bdp.set_log_level('DEBUG')
first = export_site()
Bdp.objects.all().delete()
import_site(first)
second = export_site()
assert first == second
assert Bdp.objects.all().first().log_level == 'DEBUG'
def test_export_access_rights(app, setup):
bdp = Bdp.objects.create(service_url='https://bdp.example.com/')
obj_type = ContentType.objects.get_for_model(bdp)
api = ApiUser.objects.create(username='all', keytype='', key='')
AccessRight.objects.create(codename='can_access', apiuser=api, resource_type=obj_type, resource_pk=bdp.pk)
first = export_site()
Bdp.objects.all().delete()
AccessRight.objects.all().delete()
ApiUser.objects.all().delete()
import_site(first, import_users=True)
second = export_site()
assert first == second
# partial import, with missing ApiUser
del first['apiusers']
Bdp.objects.all().delete()
AccessRight.objects.all().delete()
ApiUser.objects.all().delete()
with pytest.raises(ImportSiteError) as excinfo:
import_site(first, import_users=True)
assert '%s' % excinfo.value == 'Api user "all" not found'
def test_export_by_slug():
Bdp.objects.create(service_url='https://bdp.example.com/', slug='bdp')
Bdp.objects.create(service_url='https://bdp2.example.com/', slug='bdp2')
Bdp.objects.create(service_url='https://bdp3.example.com/', slug='bdp3')
output = get_output_of_command('export_site', '--slugs', 'bdp', 'bdp3')
export = json.loads(output)
assert len(export['resources']) == 2
assert 'bdp2' not in [res['slug'] for res in export['resources']]
def test_export_base_adresse():
ba = BaseAdresse.objects.create(
slug='ba',
service_url='https://api-adresse.data.gouv.fr/',
api_geo_url='https://geo.api.gouv.fr/',
zipcode='75013',
latitude=0.2,
longitude=1.4,
)
ba_export = ba.export_json()
new_ba = BaseAdresse.import_json(ba_export, overwrite=True)
assert ba == new_ba
# test sectors relationships
assert new_ba.sectors.count() == 0
assert ba_export['sectors'] == []
sector = SectorResource.objects.create(
slug='sector1',
title='sector 1',
csv_file=File(StringIO(SECTOR_CSV), 'sectorization.csv'),
)
ba.sectors.add(sector)
ba_export = ba.export_json()
assert ba_export['sectors'] == ['sector1']
new_ba = BaseAdresse.import_json(ba_export, overwrite=True)
assert new_ba.sectors.count() == 1
assert new_ba.sectors.get() == sector
new_ba = BaseAdresse.import_json(ba_export, overwrite=True)
assert new_ba.sectors.count() == 1
assert new_ba.sectors.get() == sector
# export all site: handle dependencies
export = export_site()
clear()
assert SectorResource.objects.count() == BaseAdresse.objects.count() == 0
ba = [d for d in export['resources'] if d['slug'] == 'ba'][0]
sector1 = [d for d in export['resources'] if d['slug'] == 'sector1'][0]
export['resources'] = [ba, sector1] # import must be clever: create sector1 before ba
import_site(export, import_users=True)
ba = BaseAdresse.objects.get()
assert ba.sectors.get() == SectorResource.objects.get()
def test_export_ovh():
ovh = OVHSMSGateway.objects.create(
slug='test-ovh',
title='Test OVH',
account='sms-test42',
application_key='RHrTdU2oTsrVC0pu',
application_secret='CLjtS69tTcPgCKxedeoZlgMSoQGSiXMa',
consumer_key='iF0zi0MJrbjNcI3hvuvwkhNk8skrigxz',
credit_threshold_alert=100,
credit_left=102,
credit_alert_timestamp=timezone.now(),
alert_emails=['test@entrouvert.org'],
)
ovh_export = ovh.export_json()
ovh.delete()
new_ovh = OVHSMSGateway.import_json(ovh_export)
assert new_ovh.alert_emails == ovh.alert_emails
assert new_ovh.credit_alert_timestamp is None
assert new_ovh.credit_left == 0
assert len(export_site(slugs=['test-ovh'])['resources']) == 1