nanterre: BO pour la synchronisation des fédérations métiers (fixes #21198)
This commit is contained in:
parent
94055f35a0
commit
7fa7a218e7
|
@ -9,6 +9,7 @@ import random
|
|||
import faker
|
||||
|
||||
from django.core.management import call_command
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from zoo.zoo_meta.models import EntitySchema, RelationSchema
|
||||
from zoo.zoo_data.models import Entity, Transaction, Relation
|
||||
|
@ -266,6 +267,14 @@ def rsu(rsu_schema):
|
|||
return entities
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def admin(db):
|
||||
user = User.objects.create(username='admin', is_staff=True, is_superuser=True)
|
||||
user.set_password('admin')
|
||||
user.save()
|
||||
return user
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app(request):
|
||||
wtm = django_webtest.WebTestMixin()
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import csv
|
||||
import StringIO
|
||||
|
||||
from webtest import Upload
|
||||
|
||||
|
||||
def test_synchronize_federations(settings, app, nanterre_classic_family, admin):
|
||||
settings.NANTERRE_SYNCHRONIZE_FEDERATIONS_DO_LATER = False
|
||||
f = nanterre_classic_family
|
||||
|
||||
# ajout de clés de fédération technocarte
|
||||
for entity in f.values():
|
||||
entity.content.setdefault('cles_de_federation', {})['technocarte'] = str(entity.id + 1000)
|
||||
entity.save()
|
||||
|
||||
response = app.get('/admin/').follow()
|
||||
response.form.set('username', 'admin')
|
||||
response.form.set('password', 'admin')
|
||||
response = response.form.submit().follow()
|
||||
|
||||
response = response.click(u'Synchroniser les fédérations')
|
||||
response = response.click(u'Nouvel import')
|
||||
response.form.set('app_id', 'technocarte')
|
||||
content = '\n'.join(map(str, [f['kevin'].id + 1000, f['marie'].id + 1000, '1222']))
|
||||
response.form.set('csv_uploaded', Upload('federations.csv', content, 'application/octet-stream'))
|
||||
response = response.form.submit().follow()
|
||||
assert len(response.pyquery('table#result-list tbody tr')) == 1
|
||||
response = response.click('Rapport')
|
||||
|
||||
def check_csv_response(csv_response):
|
||||
reader = csv.DictReader(StringIO.StringIO(csv_response.content))
|
||||
reader.fieldnames = reader.reader.next()
|
||||
rows = list(reader)
|
||||
|
||||
def rows_by_action(action):
|
||||
return [row for row in rows if row['action'] == action]
|
||||
|
||||
assert len(rows_by_action('KEEP')) == 2
|
||||
assert len(rows_by_action('DELETE')) == 2
|
||||
assert len(rows_by_action('UNKNOWN')) == 1
|
||||
assert len(set(row['action'] for row in rows)) == 3
|
||||
|
||||
csv_response = response.click('CSV')
|
||||
check_csv_response(csv_response)
|
||||
|
||||
response = response.click(u'Synchroniser les fédérations')
|
||||
|
||||
response = response.form.submit().follow()
|
||||
|
||||
response = response.click(u'Rapport d\'exécution')
|
||||
|
||||
csv_response = response.click('CSV')
|
||||
check_csv_response(csv_response)
|
|
@ -50,7 +50,6 @@ ALLOWED_HOSTS = ['*']
|
|||
|
||||
INSTALLED_APPS = [
|
||||
'gadjo',
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
|
@ -63,6 +62,7 @@ INSTALLED_APPS = [
|
|||
'zoo.zoo_data',
|
||||
'zoo.zoo_nanterre',
|
||||
'zoo.zoo_demo',
|
||||
'django.contrib.admin',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# zoo - versatile objects management
|
||||
# Copyright (C) 2016 Entr'ouvert
|
||||
#
|
||||
|
@ -17,7 +19,9 @@
|
|||
import functools
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.conf.urls import url
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.db.models.signals import post_migrate
|
||||
|
||||
|
||||
class ZooNanterreConfig(AppConfig):
|
||||
|
@ -41,3 +45,62 @@ class ZooNanterreConfig(AppConfig):
|
|||
def get_job_list_filter(self):
|
||||
from .admin import JobApplicationListFilter
|
||||
return [JobApplicationListFilter]
|
||||
|
||||
def get_entityadmin_urls(self, model_admin):
|
||||
from . import views
|
||||
descs = [
|
||||
{
|
||||
're': r'',
|
||||
'view': '',
|
||||
'name': '',
|
||||
},
|
||||
{
|
||||
're': r'add/',
|
||||
'view': '_add',
|
||||
'name': '-new-import',
|
||||
},
|
||||
{
|
||||
're': r'(?P<job_id>\d+)/download-report/(?P<filename>.*)',
|
||||
'view': '_download_report',
|
||||
'name': '-download-report',
|
||||
},
|
||||
{
|
||||
're': r'(?P<job_id>\d+)/report/(?P<filename>.*)',
|
||||
'view': '_report',
|
||||
'name': '-show-report',
|
||||
},
|
||||
{
|
||||
're': r'(?P<job_id>\d+)/download-apply-report/(?P<filename>.*)',
|
||||
'view': '_download_apply_report',
|
||||
'name': '-download-apply-report',
|
||||
},
|
||||
{
|
||||
're': r'(?P<job_id>\d+)/apply-report/(?P<filename>.*)',
|
||||
'view': '_apply_report',
|
||||
'name': '-show-apply-report',
|
||||
},
|
||||
{
|
||||
're': r'(?P<job_id>\d+)/apply/',
|
||||
'view': '_apply',
|
||||
'name': '-apply',
|
||||
},
|
||||
]
|
||||
|
||||
urls = []
|
||||
for desc in descs:
|
||||
urls.append(url(
|
||||
r'^synchronize-federations/%s$' % desc['re'],
|
||||
model_admin.admin_site.admin_view(
|
||||
getattr(views, 'synchronize_federations' + desc['view'])),
|
||||
kwargs={'model_admin': model_admin},
|
||||
name='synchronize-federations' + desc['name'],
|
||||
))
|
||||
return urls
|
||||
|
||||
def post_migrate(self, *args, **kwargs):
|
||||
from django.contrib.auth.models import Permission
|
||||
|
||||
Permission.objects.filter(codename='action1_entity').update(name=u'Peut synchroniser les fédérations')
|
||||
|
||||
def ready(self):
|
||||
post_migrate.connect(self.post_migrate)
|
||||
|
|
|
@ -15,8 +15,10 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from .utils import PersonSearch
|
||||
from .utils import PersonSearch, get_applications, get_application
|
||||
from . import synchronize_federations
|
||||
|
||||
|
||||
class SearchForm(forms.Form):
|
||||
|
@ -40,3 +42,22 @@ class SearchForm(forms.Form):
|
|||
except ValueError:
|
||||
limit = 0.5
|
||||
return iter(PersonSearch(limit=limit).search_query(query))
|
||||
|
||||
|
||||
def application_choices():
|
||||
yield ('', '---')
|
||||
for slug in get_applications(rsu_ws_url=True):
|
||||
dfn = get_application(slug)
|
||||
yield (slug, dfn.get('name', slug))
|
||||
|
||||
|
||||
class SynchronizeFederationsForm(forms.Form):
|
||||
app_id = forms.ChoiceField(
|
||||
choices=application_choices,
|
||||
label=_('Application'))
|
||||
csv_uploaded = forms.FileField(
|
||||
label=_('CSV file'))
|
||||
|
||||
def save(self):
|
||||
synchronize_federations.SynchronizeFederationsAction.synchronize(
|
||||
**self.cleaned_data)
|
||||
|
|
|
@ -0,0 +1,283 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# zoo - versatile objects management
|
||||
# Copyright (C) 2018 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import csv
|
||||
import StringIO
|
||||
|
||||
from django.core.files.storage import default_storage
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
from django.db import DatabaseError
|
||||
from django.db.transaction import atomic
|
||||
|
||||
from zoo.zoo_meta.models import EntitySchema
|
||||
from zoo.zoo_data.models import Job, Entity, Transaction, Log
|
||||
|
||||
from . import utils
|
||||
|
||||
|
||||
class SynchronizeFederationsImport(object):
|
||||
def __init__(self, action):
|
||||
self.action = action
|
||||
self.federations = []
|
||||
self.errors = []
|
||||
self.actions = []
|
||||
self.keep_count = 0
|
||||
self.new_count = 0
|
||||
self.delete_count = 0
|
||||
self.unknown_count = 0
|
||||
|
||||
def analyze(self):
|
||||
self.federations = []
|
||||
self.actions = []
|
||||
self.keep_count = 0
|
||||
self.new_count = 0
|
||||
self.delete_count = 0
|
||||
self.unknown_count = 0
|
||||
max_federation = 0
|
||||
for i, line in enumerate(default_storage.open(self.action.csv_filepath)):
|
||||
line = line.strip()
|
||||
self.federations.append(line)
|
||||
try:
|
||||
max_federation = max(max_federation, int(line))
|
||||
except ValueError as e:
|
||||
pass
|
||||
self.federations.sort()
|
||||
app_id = self.action.app_id
|
||||
individus = Entity.objects.filter(
|
||||
schema__slug=utils.INDIVIDU_ENT,
|
||||
**{'content__cles_de_federation__%s__isnull' % app_id: False})
|
||||
individus = individus.order_by('id')
|
||||
federations_set = set(self.federations)
|
||||
seen = set()
|
||||
for individu in individus:
|
||||
cle = individu.content['cles_de_federation'][app_id]
|
||||
seen.add(cle)
|
||||
if cle in federations_set:
|
||||
action = 'KEEP'
|
||||
self.keep_count += 1
|
||||
elif cle.isdigit() and int(cle) > max_federation:
|
||||
action = 'NEW'
|
||||
self.new_count += 1
|
||||
else:
|
||||
action = 'DELETE'
|
||||
self.delete_count += 1
|
||||
self.actions.append((
|
||||
individu.id,
|
||||
individu.content['prenoms'],
|
||||
individu.content['nom_de_naissance'],
|
||||
individu.content['nom_d_usage'],
|
||||
app_id,
|
||||
cle,
|
||||
action))
|
||||
|
||||
for federation in self.federations:
|
||||
if federation not in seen:
|
||||
self.unknown_count += 1
|
||||
self.actions.append((
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
app_id,
|
||||
federation,
|
||||
'UNKNOWN'))
|
||||
|
||||
def analyze_and_report(self):
|
||||
self.analyze()
|
||||
self.report('report')
|
||||
|
||||
def report(self, target):
|
||||
output_file = StringIO.StringIO()
|
||||
writer = csv.writer(output_file)
|
||||
writer.writerow(['RSU ID', 'prenoms', 'nom de naissance',
|
||||
'nom d\'usage', 'application', 'federation', 'action'])
|
||||
for action in self.actions:
|
||||
action = [unicode(v).encode('utf-8') for v in action]
|
||||
writer.writerow(action)
|
||||
setattr(self.action, target + '_csv_filename',
|
||||
self.action.csv_filename + '-report.csv')
|
||||
setattr(self.action, target + '_csv_filepath',
|
||||
default_storage.save(self.action.csv_filepath + '-report-.csv', output_file))
|
||||
self.action.counts = {
|
||||
'keep': self.keep_count,
|
||||
'new': self.new_count,
|
||||
'delete': self.delete_count,
|
||||
'unknown': self.unknown_count,
|
||||
}
|
||||
|
||||
def apply(self):
|
||||
self.analyze()
|
||||
|
||||
deletes = {}
|
||||
for row in self.actions:
|
||||
entity_id, _, _, _, app_id, federation, action = row
|
||||
assert app_id == self.action.app_id
|
||||
if action != 'DELETE':
|
||||
continue
|
||||
deletes[entity_id] = federation
|
||||
|
||||
entities = Entity.objects.filter(id__in=deletes.keys())
|
||||
app_name = utils.get_application(app_id)['name']
|
||||
|
||||
try:
|
||||
with atomic():
|
||||
transaction = Transaction.objects.create()
|
||||
transaction.content = {
|
||||
'action': 'synchronize-federation',
|
||||
'job_id': self.action.job.id,
|
||||
'app_id': app_id,
|
||||
'federations_deleted': [],
|
||||
}
|
||||
logs = []
|
||||
self.action.job.transaction = transaction
|
||||
for entity in entities:
|
||||
federation = deletes[entity.id]
|
||||
if entity.content.get('cles_de_federation', {}).get(app_id) == federation:
|
||||
del entity.content['cles_de_federation'][app_id]
|
||||
transaction.content['federations_deleted'].append(
|
||||
[entity.id, app_id, federation])
|
||||
entity.modified = transaction
|
||||
text = u'Supression de la fédération %s: %s' % (app_name, federation)
|
||||
logs.append(
|
||||
Log(transaction=transaction,
|
||||
entity=entity,
|
||||
content={
|
||||
'text': text,
|
||||
}))
|
||||
entity.save()
|
||||
else:
|
||||
raise ValueError
|
||||
transaction.save()
|
||||
self.action.job.save()
|
||||
Log.objects.bulk_create(logs)
|
||||
except ValueError:
|
||||
return False, 'atomic update failed'
|
||||
except DatabaseError:
|
||||
return False, 'concurrent update'
|
||||
self.report('apply_report')
|
||||
return True, None
|
||||
|
||||
|
||||
class SynchronizeFederationsAction(object):
|
||||
report_csv_filename = None
|
||||
report_csv_filepath = None
|
||||
apply_report_csv_filename = None
|
||||
apply_report_csv_filepath = None
|
||||
apply = False
|
||||
errors = None
|
||||
|
||||
def __init__(self, app_id, csv_filename, csv_filepath,
|
||||
report_csv_filename=None, report_csv_filepath=None, errors=None,
|
||||
counts=None, apply_report_csv_filename=None, apply_report_csv_filepath=None, apply=False, **kwargs):
|
||||
self.app_id = app_id
|
||||
self.csv_filename = csv_filename
|
||||
self.csv_filepath = csv_filepath
|
||||
self.report_csv_filename = report_csv_filename
|
||||
self.report_csv_filepath = report_csv_filepath
|
||||
self.counts = counts
|
||||
self.apply_report_csv_filename = apply_report_csv_filename
|
||||
self.apply_report_csv_filepath = apply_report_csv_filepath
|
||||
self.apply = apply
|
||||
|
||||
def to_json(self):
|
||||
return {
|
||||
'app_id': self.app_id,
|
||||
'csv_filename': self.csv_filename,
|
||||
'csv_filepath': self.csv_filepath,
|
||||
'report_csv_filename': self.report_csv_filename,
|
||||
'report_csv_filepath': self.report_csv_filepath,
|
||||
'counts': self.counts,
|
||||
'apply_report_csv_filename': self.apply_report_csv_filename,
|
||||
'apply_report_csv_filepath': self.apply_report_csv_filepath,
|
||||
'apply': self.apply,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, d):
|
||||
return cls(**d)
|
||||
|
||||
def do(self, job=None, **kwargs):
|
||||
synchronize_federations_import = SynchronizeFederationsImport(self)
|
||||
if self.apply:
|
||||
result, errors = synchronize_federations_import.apply()
|
||||
if not result:
|
||||
self.errors = errors
|
||||
return Job.STATE_ERROR
|
||||
else:
|
||||
synchronize_federations_import.analyze_and_report()
|
||||
return Job.STATE_SUCCESS
|
||||
|
||||
@classmethod
|
||||
def synchronize(cls, app_id, csv_uploaded):
|
||||
csv_filepath = default_storage.save(
|
||||
csv_uploaded.name, csv_uploaded)
|
||||
|
||||
self = cls(app_id, csv_uploaded.name, csv_filepath)
|
||||
Job.create(self, do_later=getattr(settings, 'NANTERRE_SYNCHRONIZE_FEDERATIONS_DO_LATER', True))
|
||||
|
||||
@classmethod
|
||||
def get_jobs(cls):
|
||||
qs = Job.objects.by_action(cls)
|
||||
return qs
|
||||
|
||||
@property
|
||||
def report(self):
|
||||
if not self.report_csv_filepath:
|
||||
return None
|
||||
return default_storage.open(self.report_csv_filepath)
|
||||
|
||||
def make_url(self, action, prefix):
|
||||
stream = getattr(self, prefix)
|
||||
if not stream:
|
||||
return None
|
||||
url_name = 'admin:synchronize-federations-%s-%s' % (action, prefix.replace('_', '-'))
|
||||
filename = getattr(self, prefix + '_csv_filename')
|
||||
return reverse(url_name, kwargs={
|
||||
'job_id': self.job.id,
|
||||
'filename': filename,
|
||||
})
|
||||
|
||||
@property
|
||||
def download_report_url(self):
|
||||
return self.make_url('download', 'report')
|
||||
|
||||
@property
|
||||
def report_url(self):
|
||||
return self.make_url('show', 'report')
|
||||
|
||||
@property
|
||||
def download_apply_report_url(self):
|
||||
return self.make_url('download', 'apply_report')
|
||||
|
||||
@property
|
||||
def apply_report_url(self):
|
||||
return self.make_url('show', 'apply_report')
|
||||
|
||||
@property
|
||||
def apply_report(self):
|
||||
if not self.apply_report_csv_filepath:
|
||||
return None
|
||||
return default_storage.open(self.apply_report_csv_filepath)
|
||||
|
||||
def set_apply(self, job):
|
||||
job.content['apply'] = True
|
||||
job.state = job.STATE_TODO
|
||||
job.save()
|
||||
if not getattr(settings, 'NANTERRE_SYNCHRONIZE_FEDERATIONS_DO_LATER', True):
|
||||
job.do()
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "admin/base.html" %}
|
||||
|
||||
{% block title %}Administration technique du RSU{% endblock %}
|
||||
|
||||
{% block branding %}
|
||||
<h1 id="site-name"><a href="{% url 'admin:index' %}">Administration technique du RSU</a></h1>
|
||||
{% endblock %}
|
|
@ -0,0 +1,99 @@
|
|||
{% extends "admin/base_site.html" %}
|
||||
{% load i18n static %}
|
||||
|
||||
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/dashboard.css" %}" />{% endblock %}
|
||||
|
||||
{% block coltype %}colMS{% endblock %}
|
||||
|
||||
{% block bodyclass %}{{ block.super }} dashboard{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="content-main">
|
||||
{% if app_list %}
|
||||
{% for app in app_list %}
|
||||
<div class="app-{{ app.app_label }} module">
|
||||
<table>
|
||||
<caption>
|
||||
<a href="{{ app.app_url }}" class="section" title="{% blocktrans with name=app.name %}Models in the {{ name }} application{% endblocktrans %}">{{ app.name }}</a>
|
||||
</caption>
|
||||
{% for model in app.models %}
|
||||
<tr class="model-{{ model.object_name|lower }}">
|
||||
{% if model.admin_url %}
|
||||
<th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th>
|
||||
{% else %}
|
||||
<th scope="row">{{ model.name }}</th>
|
||||
{% endif %}
|
||||
|
||||
{% if model.add_url %}
|
||||
<td><a href="{{ model.add_url }}" class="addlink">{% trans 'Add' %}</a></td>
|
||||
{% else %}
|
||||
<td> </td>
|
||||
{% endif %}
|
||||
|
||||
{% if model.admin_url %}
|
||||
<td><a href="{{ model.admin_url }}" class="changelink">{% trans 'Change' %}</a></td>
|
||||
{% else %}
|
||||
<td> </td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p>{% trans "You don't have permission to edit anything." %}</p>
|
||||
{% endif %}
|
||||
<div class="module">
|
||||
<table>
|
||||
<caption>
|
||||
Actions globales
|
||||
</caption>
|
||||
{% if perms.zoo_data.action1_entity %}
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<a href="{% url "admin:synchronize-federations" %}">
|
||||
Synchroniser les fédérations
|
||||
</a>
|
||||
</th>
|
||||
<td> </td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block sidebar %}
|
||||
<div id="content-related">
|
||||
<div class="module" id="recent-actions-module">
|
||||
<h2>{% trans 'Recent actions' %}</h2>
|
||||
<h3>{% trans 'My actions' %}</h3>
|
||||
{% load log %}
|
||||
{% get_admin_log 10 as admin_log for_user user %}
|
||||
{% if not admin_log %}
|
||||
<p>{% trans 'None available' %}</p>
|
||||
{% else %}
|
||||
<ul class="actionlist">
|
||||
{% for entry in admin_log %}
|
||||
<li class="{% if entry.is_addition %}addlink{% endif %}{% if entry.is_change %}changelink{% endif %}{% if entry.is_deletion %}deletelink{% endif %}">
|
||||
{% if entry.is_deletion or not entry.get_admin_url %}
|
||||
{{ entry.object_repr }}
|
||||
{% else %}
|
||||
<a href="{{ entry.get_admin_url }}">{{ entry.object_repr }}</a>
|
||||
{% endif %}
|
||||
<br/>
|
||||
{% if entry.content_type %}
|
||||
<span class="mini quiet">{% filter capfirst %}{{ entry.content_type }}{% endfilter %}</span>
|
||||
{% else %}
|
||||
<span class="mini quiet">{% trans 'Unknown content' %}</span>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,64 @@
|
|||
{% extends "admin/base_site.html" %}
|
||||
{% load i18n admin_urls static %}
|
||||
|
||||
{% block extrastyle %}
|
||||
{{ block.super }}
|
||||
<link rel="stylesheet" type="text/css" href="{% static "admin/css/changelists.css" %}" />
|
||||
{% endblock %}
|
||||
|
||||
{% block coltype %}flex{% endblock %}
|
||||
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<div class="breadcrumbs">
|
||||
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
|
||||
› Synchroniser les fédérations
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="content-main">
|
||||
<ul class="object-tools">
|
||||
<li><a href="{% url "admin:synchronize-federations-new-import" %}">Nouvel import</a></li>
|
||||
</ul>
|
||||
<table id="result-list">
|
||||
{% comment %}Liste des imports {% endcomment %}
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Application</th>
|
||||
<th>Statut</th>
|
||||
<th>Nom du fichier</th>
|
||||
<th>Rapport d'import</th>
|
||||
<th>Synchroniser ?</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for job in jobs %}
|
||||
<tr>
|
||||
<td>{{ job.created }}</td>
|
||||
<td>{{ job.content.app_id }}</td>
|
||||
<td>{{ job.get_state_display }}</td>
|
||||
<td>{{ job.content.csv_filename }}</td>
|
||||
<td>
|
||||
{% if job.action.report_url %}
|
||||
<a href="{{ job.action.report_url }}">Rapport</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if job.action.download_apply_report_url %}
|
||||
<a href="{{ job.action.apply_report_url }}">Rapport d'exécution</a>
|
||||
{% elif job.state == job.STATE_SUCCESS %}
|
||||
<form action="{% url "admin:synchronize-federations-apply" job_id=job.id %}" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="submit" value="{% trans "Apply" %}">
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,21 @@
|
|||
{% extends "admin/base_site.html" %}
|
||||
{% load i18n admin_urls %}
|
||||
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<div class="breadcrumbs">
|
||||
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
|
||||
› <a href="{% url 'admin:synchronize-federations' %}">Synchroniser les fédérations</a>
|
||||
› Nouvel import
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="content-main">
|
||||
<form enctype="multipart/form-data" method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<input type="submit" value="Importer">
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,48 @@
|
|||
{% extends "admin/base_site.html" %}
|
||||
{% load i18n admin_urls static %}
|
||||
|
||||
{% block extrastyle %}
|
||||
{{ block.super }}
|
||||
<link rel="stylesheet" type="text/css" href="{% static "admin/css/changelists.css" %}" />
|
||||
{% endblock %}
|
||||
|
||||
{% block coltype %}flex{% endblock %}
|
||||
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<div class="breadcrumbs">
|
||||
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
|
||||
› <a href="{% url 'admin:synchronize-federations' %}">Synchroniser les fédérations</a>
|
||||
› {{ job.created }} - {{ job.action.app_id }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="content-main">
|
||||
<a href="{{ csv_url }}">CSV</a> ( {{ csv_filesize|filesizeformat }} )
|
||||
<div id="changelist">
|
||||
<table id="result-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>RSU ID</th>
|
||||
<th>Prénoms</th>
|
||||
<th>Nom de naissance</th>
|
||||
<th>Nom d'usage</th>
|
||||
<th>Application</th>
|
||||
<th>Fédération</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in csv %}
|
||||
<tr>
|
||||
{% for cell in row %}
|
||||
<td>{{ cell }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -16,11 +16,20 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import csv
|
||||
|
||||
from django.template.response import TemplateResponse
|
||||
from django.views.generic import TemplateView
|
||||
from django.shortcuts import redirect, get_object_or_404
|
||||
from django.http import Http404, FileResponse
|
||||
from django.db.transaction import non_atomic_requests
|
||||
from django.db import connection
|
||||
from django.contrib.auth.decorators import permission_required
|
||||
|
||||
from zoo.zoo_data.models import Job
|
||||
|
||||
from . import forms
|
||||
from .synchronize_federations import SynchronizeFederationsAction
|
||||
|
||||
|
||||
class Demo(TemplateView):
|
||||
|
@ -76,3 +85,104 @@ class ImportControl(TemplateView):
|
|||
search = non_atomic_requests(Search.as_view())
|
||||
|
||||
import_control = ImportControl.as_view()
|
||||
|
||||
|
||||
@permission_required('zoo_data.action1_entity')
|
||||
def synchronize_federations(request, model_admin, *args, **kwargs):
|
||||
jobs = SynchronizeFederationsAction.get_jobs()
|
||||
context = dict(
|
||||
model_admin.admin_site.each_context(request),
|
||||
title='Synchronises les applications',
|
||||
jobs=jobs,
|
||||
)
|
||||
return TemplateResponse(request, "admin/zoo_data/entity/synchronize_federations.html", context)
|
||||
|
||||
|
||||
@permission_required('zoo_data.action1_entity')
|
||||
def synchronize_federations_report(request, job_id, model_admin, *args, **kwargs):
|
||||
jobs = SynchronizeFederationsAction.get_jobs()
|
||||
job = get_object_or_404(jobs, id=job_id)
|
||||
report = job.action.report
|
||||
if not report:
|
||||
raise Http404('no report')
|
||||
reader = csv.reader(report)
|
||||
next(reader)
|
||||
actions = [row for row in reader if row[6] != 'KEEP']
|
||||
context = dict(
|
||||
model_admin.admin_site.each_context(request),
|
||||
title=job.created,
|
||||
job=job,
|
||||
csv=actions,
|
||||
csv_url=job.action.download_report_url,
|
||||
csv_filesize=report.size,
|
||||
)
|
||||
return TemplateResponse(request, "admin/zoo_data/entity/synchronize_federations_report.html", context)
|
||||
|
||||
|
||||
@permission_required('zoo_data.action1_entity')
|
||||
def synchronize_federations_download_report(request, job_id, model_admin, *args, **kwargs):
|
||||
jobs = SynchronizeFederationsAction.get_jobs()
|
||||
job = get_object_or_404(jobs, id=job_id)
|
||||
report = job.action.report
|
||||
if not report:
|
||||
raise Http404('no report')
|
||||
return FileResponse(report, content_type='text/csv')
|
||||
|
||||
|
||||
@permission_required('zoo_data.action1_entity')
|
||||
def synchronize_federations_apply_report(request, job_id, model_admin, *args, **kwargs):
|
||||
jobs = SynchronizeFederationsAction.get_jobs()
|
||||
job = get_object_or_404(jobs, id=job_id)
|
||||
report = job.action.apply_report
|
||||
if not report:
|
||||
raise Http404('no report')
|
||||
reader = csv.reader(report)
|
||||
next(reader)
|
||||
actions = [row for row in reader if row[6] != 'KEEP']
|
||||
context = dict(
|
||||
model_admin.admin_site.each_context(request),
|
||||
title=u'Application - %s' % job.created,
|
||||
job=job,
|
||||
csv=actions,
|
||||
csv_url=job.action.download_apply_report_url,
|
||||
csv_filesize=report.size,
|
||||
)
|
||||
return TemplateResponse(request, "admin/zoo_data/entity/synchronize_federations_report.html", context)
|
||||
|
||||
|
||||
@permission_required('zoo_data.action1_entity')
|
||||
def synchronize_federations_download_apply_report(request, job_id, model_admin, *args, **kwargs):
|
||||
jobs = SynchronizeFederationsAction.get_jobs()
|
||||
job = get_object_or_404(jobs, id=job_id)
|
||||
report = job.action.apply_report
|
||||
if not report:
|
||||
raise Http404('no report')
|
||||
return FileResponse(report, content_type='text/csv')
|
||||
|
||||
|
||||
@permission_required('zoo_data.action1_entity')
|
||||
def synchronize_federations_add(request, model_admin, *args, **kwargs):
|
||||
if request.method == 'POST':
|
||||
form = forms.SynchronizeFederationsForm(request.POST, request.FILES)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
return redirect('admin:synchronize-federations')
|
||||
else:
|
||||
form = forms.SynchronizeFederationsForm()
|
||||
context = dict(
|
||||
model_admin.admin_site.each_context(request),
|
||||
form=form,
|
||||
)
|
||||
return TemplateResponse(request, "admin/zoo_data/entity/synchronize_federations_add.html", context)
|
||||
|
||||
|
||||
@permission_required('zoo_data.action1_entity')
|
||||
def synchronize_federations_apply(request, job_id, model_admin, **kwargs):
|
||||
if request.method == 'POST':
|
||||
jobs = SynchronizeFederationsAction.get_jobs()
|
||||
job = get_object_or_404(jobs, id=job_id)
|
||||
apply_report = job.action.apply_report
|
||||
if apply_report:
|
||||
raise Http404
|
||||
job.action.set_apply(job)
|
||||
return redirect('admin:synchronize-federations')
|
||||
|
|
Loading…
Reference in New Issue