Projet

Général

Profil

0001-manager-add-UI-to-export-import-agendas-25985.patch

Frédéric Péters, 22 septembre 2018 16:27

Télécharger (11,6 ko)

Voir les différences:

Subject: [PATCH] manager: add UI to export/import agendas (#25985)

 chrono/agendas/models.py                      |  1 +
 chrono/manager/forms.py                       |  4 ++
 .../templates/chrono/agendas_import.html      | 17 ++++++
 .../chrono/manager_agenda_settings.html       |  1 +
 .../templates/chrono/manager_home.html        |  1 +
 chrono/manager/urls.py                        |  4 ++
 chrono/manager/utils.py                       |  8 ++-
 chrono/manager/views.py                       | 53 ++++++++++++++++++-
 tests/test_manager.py                         | 53 +++++++++++++++++++
 9 files changed, 140 insertions(+), 2 deletions(-)
 create mode 100644 chrono/manager/templates/chrono/agendas_import.html
chrono/agendas/models.py
153 153
            for desk in desks:
154 154
                desk['agenda'] = agenda
155 155
                Desk.import_json(desk).save()
156
        return created
156 157

  
157 158
WEEKDAYS_LIST = sorted(WEEKDAYS.items(), key=lambda x: x[0])
158 159

  
chrono/manager/forms.py
201 201
            help_text=_('ICS file containing events which will be considered as exceptions.'))
202 202
    ics_url = forms.URLField(label=_('URL'), required=False,
203 203
            help_text=_('URL to remote calendar which will be synchronised hourly.'))
204

  
205

  
206
class AgendasImportForm(forms.Form):
207
    agendas_json = forms.FileField(label=_('Agendas Export File'))
chrono/manager/templates/chrono/agendas_import.html
1
{% extends "chrono/manager_base.html" %}
2
{% load i18n %}
3

  
4
{% block appbar %}
5
<h2>{% trans "Agendas Import" %}</h2>
6
{% endblock %}
7

  
8
{% block content %}
9
<form method="post" enctype="multipart/form-data">
10
  {% csrf_token %}
11
  {{ form.as_p }}
12
  <div class="buttons">
13
    <button class="submit-button">{% trans "Import" %}</button>
14
    <a class="cancel" href="{% url 'chrono-manager-homepage' %}">{% trans 'Cancel' %}</a>
15
  </div>
16
</form>
17
{% endblock %}
chrono/manager/templates/chrono/manager_agenda_settings.html
15 15
  <a rel="popup" href="{% url 'chrono-manager-agenda-delete' pk=object.id %}">{% trans 'Delete' %}</a>
16 16
{% endif %}
17 17
{% if user_can_manage %}
18
  <a href="{% url 'chrono-manager-agenda-export' pk=object.id %}">{% trans 'Export' %}</a>
18 19
  <a rel="popup" href="{% url 'chrono-manager-agenda-edit' pk=object.id %}">{% trans 'Options' %}</a>
19 20
  {% if object.kind == "events" %}
20 21
    <a rel="popup" href="{% url 'chrono-manager-agenda-import-events' pk=object.id %}">{% trans 'Import Events' %}</a>
chrono/manager/templates/chrono/manager_home.html
5 5
<h2>{% trans 'Agendas' %}</h2>
6 6
{% if user.is_staff %}
7 7
<span class="actions">
8
<a rel="popup" href="{% url 'chrono-manager-agendas-import' %}">{% trans 'Import' %}</a>
8 9
<a rel="popup" href="{% url 'chrono-manager-agenda-add' %}">{% trans 'New' %}</a>
9 10
</span>
10 11
{% endif %}
chrono/manager/urls.py
22 22
        url(r'^$', views.homepage, name='chrono-manager-homepage'),
23 23
        url(r'^agendas/add/$', views.agenda_add,
24 24
            name='chrono-manager-agenda-add'),
25
        url(r'^agendas/import/$', views.agendas_import,
26
            name='chrono-manager-agendas-import'),
25 27
        url(r'^agendas/(?P<pk>\w+)/$', views.agenda_view,
26 28
            name='chrono-manager-agenda-view'),
27 29
        url(r'^agendas/(?P<pk>\w+)/(?P<year>[0-9]{4})/(?P<month>[0-9]+)/$', views.agenda_monthly_view,
......
34 36
            name='chrono-manager-agenda-edit'),
35 37
        url(r'^agendas/(?P<pk>\w+)/delete$', views.agenda_delete,
36 38
            name='chrono-manager-agenda-delete'),
39
        url(r'^agendas/(?P<pk>\w+)/export$', views.agenda_export,
40
            name='chrono-manager-agenda-export'),
37 41
        url(r'^agendas/(?P<pk>\w+)/add-event$', views.agenda_add_event,
38 42
            name='chrono-manager-agenda-add-event'),
39 43
        url(r'^agendas/(?P<pk>\w+)/import-events$', views.agenda_import_events,
chrono/manager/utils.py
33 33
    if clean:
34 34
        Agenda.objects.all().delete()
35 35

  
36
    results = {'created': 0, 'updated': 0}
36 37
    with transaction.atomic():
37 38
        for data in data.get('agendas', []):
38
            Agenda.import_json(data, overwrite=overwrite)
39
            created = Agenda.import_json(data, overwrite=overwrite)
40
            if created:
41
                results['created'] += 1
42
            else:
43
                results['updated'] += 1
44
    return results
chrono/manager/views.py
37 37

  
38 38
from .forms import (AgendaAddForm, AgendaEditForm, EventForm, NewMeetingTypeForm, MeetingTypeForm,
39 39
                    TimePeriodForm, ImportEventsForm, NewDeskForm, DeskForm, TimePeriodExceptionForm,
40
                    ExceptionsImportForm)
40
                    ExceptionsImportForm, AgendasImportForm)
41
from .utils import import_site
41 42

  
42 43

  
43 44
class HomepageView(ListView):
......
77 78
agenda_add = AgendaAddView.as_view()
78 79

  
79 80

  
81
class AgendasImportView(FormView):
82
    form_class = AgendasImportForm
83
    template_name = 'chrono/agendas_import.html'
84
    success_url = reverse_lazy('chrono-manager-homepage')
85

  
86
    def dispatch(self, request, *args, **kwargs):
87
        if not request.user.is_staff:
88
            raise PermissionDenied()
89
        return super(AgendasImportView, self).dispatch(request, *args, **kwargs)
90

  
91
    def form_valid(self, form):
92
        try:
93
            agendas_json = json.load(self.request.FILES['agendas_json'])
94
        except ValueError:
95
            form.add_error('agendas_json', _('File is not in the expected JSON format.'))
96
            return self.form_invalid(form)
97

  
98
        results = import_site(agendas_json, overwrite=True)
99
        if results.get('created') == 0 and results.get('updated') == 0:
100
            messages.info(self.request, _('No agendas were found.'))
101
        else:
102
            if results.get('created') == 0:
103
                message1 = _('No agenda created.')
104
            else:
105
                message1 = ungettext('An agenda has been created.',
106
                                '%(count)d agendas have been created.', results['created'])
107

  
108
            if results.get('updated') == 0:
109
                message2 = _('No agenda updated.')
110
            else:
111
                message2 = ungettext('An agenda has been updated.',
112
                                '%(count)d agendas have been updated.', results['updated'])
113
            messages.info(self.request, '%s %s' % (message1, message2))
114

  
115
        return super(AgendasImportView, self).form_valid(form)
116

  
117
agendas_import = AgendasImportView.as_view()
118

  
119

  
80 120
class AgendaEditView(UpdateView):
81 121
    template_name = 'chrono/manager_agenda_form.html'
82 122
    model = Agenda
......
488 528
agenda_settings = AgendaSettings.as_view()
489 529

  
490 530

  
531
class AgendaExport(ManagedAgendaMixin, DetailView):
532
    model = Agenda
533

  
534
    def get(self, request, *args, **kwargs):
535
        response = HttpResponse(content_type='application/json')
536
        json.dump({'agendas': [self.get_object().export_json()]}, response, indent=2)
537
        return response
538

  
539
agenda_export = AgendaExport.as_view()
540

  
541

  
491 542
class AgendaAddEventView(ManagedAgendaMixin, CreateView):
492 543
    template_name = 'chrono/manager_event_form.html'
493 544
    model = Event
tests/test_manager.py
1 1
# -*- coding: utf-8 -*-
2 2

  
3 3
from __future__ import unicode_literals
4
import json
4 5

  
5 6
from django.contrib.auth.models import User, Group
6 7
from django.utils.timezone import make_aware, now, localtime
......
14 15

  
15 16
from chrono.agendas.models import (Agenda, Event, Booking, MeetingType,
16 17
                                   TimePeriod, Desk, TimePeriodException)
18
from chrono.manager.utils import export_site
17 19

  
18 20
pytestmark = pytest.mark.django_db
19 21

  
......
1447 1449
    login(app)
1448 1450
    resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, today.year, today.month))
1449 1451
    assert resp.text.count('<div class="booking"') == 0
1452

  
1453
def test_import_agenda_as_manager(app, manager_user):
1454
    # open /manage/ access to manager_user, and check agenda import is not
1455
    # allowed.
1456
    agenda = Agenda(label='Foo bar')
1457
    agenda.view_role = manager_user.groups.all()[0]
1458
    agenda.save()
1459
    app = login(app, username='manager', password='manager')
1460
    resp = app.get('/manage/', status=200)
1461
    resp = app.get('/manage/agendas/import/', status=403)
1462

  
1463
def test_import_agenda(app, admin_user):
1464
    agenda = Agenda(label=u'Foo bar')
1465
    agenda.save()
1466

  
1467
    app = login(app)
1468
    resp = app.get('/manage/agendas/%s/settings' % agenda.id)
1469
    resp = resp.click('Export')
1470
    assert resp.headers['content-type'] == 'application/json'
1471
    agenda_export = resp.body
1472

  
1473
    # invalid json
1474
    resp = app.get('/manage/', status=200)
1475
    resp = resp.click('Import')
1476
    resp.form['agendas_json'] = Upload('export.json', b'garbage', 'application/json')
1477
    resp = resp.form.submit()
1478
    assert 'File is not in the expected JSON format.' in resp.text
1479

  
1480
    # empty json
1481
    resp = app.get('/manage/', status=200)
1482
    resp = resp.click('Import')
1483
    resp.form['agendas_json'] = Upload('export.json', b'{}', 'application/json')
1484
    resp = resp.form.submit().follow()
1485
    assert 'No agendas were found.' in resp.text
1486

  
1487
    # existing agenda
1488
    resp = app.get('/manage/', status=200)
1489
    resp = resp.click('Import')
1490
    resp.form['agendas_json'] = Upload('export.json', agenda_export, 'application/json')
1491
    resp = resp.form.submit().follow()
1492
    assert 'No agenda created. An agenda has been updated.' in resp.text
1493
    assert Agenda.objects.count() == 1
1494

  
1495
    # new agenda
1496
    Agenda.objects.all().delete()
1497
    resp = app.get('/manage/', status=200)
1498
    resp = resp.click('Import')
1499
    resp.form['agendas_json'] = Upload('export.json', agenda_export, 'application/json')
1500
    resp = resp.form.submit().follow()
1501
    assert 'An agenda has been created. No agenda updated.' in resp.text
1502
    assert Agenda.objects.count() == 1
1450
-