agendas: refresh an exception source (#29209)

This commit is contained in:
Lauréline Guérin 2019-12-12 10:56:14 +01:00
parent 2aed92d132
commit 7a33c41586
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
6 changed files with 201 additions and 3 deletions

View File

@ -33,6 +33,7 @@ from chrono.agendas.models import (
TimePeriod,
Desk,
TimePeriodException,
TimePeriodExceptionSource,
WEEKDAYS_LIST,
)
@ -283,5 +284,17 @@ class ExceptionsImportForm(forms.ModelForm):
raise forms.ValidationError(_('Please provide an ICS File or an URL.'))
class TimePeriodExceptionSourceReplaceForm(forms.ModelForm):
ics_file = forms.FileField(
label=_('ICS File'),
required=False,
help_text=_('ICS file containing events which will be considered as exceptions.'),
)
class Meta:
model = TimePeriodExceptionSource
fields = []
class AgendasImportForm(forms.Form):
agendas_json = forms.FileField(label=_('Agendas Export File'))

View File

@ -31,9 +31,11 @@
</span>
</td>
<td>
<a rel="popup" href="">
{% if object.ics_filename %}{% trans "replace" %}{% else %}{% trans "refresh" %}{% endif %}
</a>
{% if object.ics_filename %}
<a rel="popup" href="{% url 'chrono-manager-time-period-exception-source-replace' object.pk %}">{% trans "replace" %}</a>
{% else %}
<a href="{% url 'chrono-manager-time-period-exception-source-refresh' object.pk %}">{% trans "refresh" %}</a>
{% endif %}
</td>
<td><a rel="popup" href="{% url 'chrono-manager-time-period-exception-source-delete' object.pk %}">{% trans "remove" %}</a></td>
</tr>

View File

@ -0,0 +1,28 @@
{% extends "chrono/manager_import_exceptions.html" %}
{% load i18n %}
{% block appbar %}
<h2>{% if form.instance.ics_filename %}{% trans "Replace exceptions" %}{% else %}{% trans "Refresh exceptions" %}{% endif %}</h2>
{% endblock %}
{% block content %}
<form method="post" enctype="multipart/form-data">
{% if form.instance.ics_filename %}
<p class="notice">{% trans "To replace existing exceptions, please upload a new file." %}</p>
{% else %}
<p class="notice">
{% trans 'Press the button "Refresh" to refresh existing exceptions from:' %}
<br />
<a href="{{ form.instance.ics_url }}">{{ form.instance.ics_url }}</a>
</p>
{% endif %}
{% csrf_token %}
{{ form.as_p }}
<p>
</p>
<div class="buttons">
<button>{% if form.instance.ics_filename %}{% trans "Replace" %}{% else %}{% trans "Refresh" %}{% endif %}</button>
<a class="cancel" href="{% url 'chrono-manager-agenda-settings' pk=agenda.id %}">{% trans 'Cancel' %}</a>
</div>
</form>
{% endblock %}

View File

@ -105,6 +105,16 @@ urlpatterns = [
views.time_period_exception_source_delete,
name='chrono-manager-time-period-exception-source-delete',
),
url(
r'^time-period-exceptions-source/(?P<pk>\d+)/refresh$',
views.time_period_exception_source_refresh,
name='chrono-manager-time-period-exception-source-refresh',
),
url(
r'^time-period-exceptions-source/(?P<pk>\d+)/replace$',
views.time_period_exception_source_replace,
name='chrono-manager-time-period-exception-source-replace',
),
url(
r'^agendas/events.csv$',
views.agenda_import_events_sample_csv,

View File

@ -69,6 +69,7 @@ from .forms import (
ExceptionsImportForm,
AgendasImportForm,
TimePeriodAddForm,
TimePeriodExceptionSourceReplaceForm,
)
from .utils import import_site
@ -926,6 +927,59 @@ class TimePeriodExceptionSourceDeleteView(ManagedDeskSubobjectMixin, DeleteView)
time_period_exception_source_delete = TimePeriodExceptionSourceDeleteView.as_view()
class TimePeriodExceptionSourceReplaceView(ManagedDeskSubobjectMixin, UpdateView):
model = TimePeriodExceptionSource
form_class = TimePeriodExceptionSourceReplaceForm
template_name = 'chrono/manager_replace_exceptions.html'
def form_valid(self, form):
exceptions = None
try:
exceptions = form.instance.desk.import_timeperiod_exceptions_from_ics_file(
form.cleaned_data['ics_file'], source=form.instance
)
except ICSError as e:
form.add_error(None, force_text(e))
return self.form_invalid(form)
if exceptions is not None:
message = ungettext(
'An exception has been imported.', '%(count)d exceptions have been imported.', exceptions
)
message = message % {'count': exceptions}
messages.info(self.request, message)
return super(TimePeriodExceptionSourceReplaceView, self).form_valid(form)
time_period_exception_source_replace = TimePeriodExceptionSourceReplaceView.as_view()
class TimePeriodExceptionSourceRefreshView(ManagedDeskSubobjectMixin, DetailView):
model = TimePeriodExceptionSource
def get(self, request, *args, **kwargs):
try:
source = self.get_object()
exceptions = source.desk.import_timeperiod_exceptions_from_remote_ics(
source.ics_url, source=source
)
except ICSError as e:
messages.error(self.request, force_text(e))
else:
message = ungettext(
'An exception has been imported.', '%(count)d exceptions have been imported.', exceptions
)
message = message % {'count': exceptions}
messages.info(self.request, message)
# redirect to settings
return HttpResponseRedirect(
reverse('chrono-manager-agenda-settings', kwargs={'pk': source.desk.agenda_id})
)
time_period_exception_source_refresh = TimePeriodExceptionSourceRefreshView.as_view()
def menu_json(request):
label = _('Agendas')
json_str = json.dumps(

View File

@ -1470,6 +1470,97 @@ END:VCALENDAR"""
assert TimePeriodExceptionSource.objects.filter(pk=source1.pk).exists() is False
def test_meetings_agenda_replace_time_period_exception_source(app, admin_user):
agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
MeetingType(agenda=agenda, label='Blah').save()
TimePeriod.objects.create(
weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
)
ics_file_content = b"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//foo.bar//EN
BEGIN:VEVENT
DTSTART:20180101
DTEND:20180101
SUMMARY:New Year's Eve
RRULE:FREQ=YEARLY
END:VEVENT
END:VCALENDAR"""
login(app)
# import a source from a file
resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
resp = resp.click('Settings')
resp = resp.click('upload')
resp.form['ics_file'] = Upload('exceptions.ics', ics_file_content, 'text/calendar')
resp = resp.form.submit(status=302).follow()
assert TimePeriodException.objects.filter(desk=desk).count() == 2
source = TimePeriodExceptionSource.objects.latest('pk')
assert source.timeperiodexception_set.count() == 2
exceptions = list(source.timeperiodexception_set.order_by('pk'))
# replace the source
resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
resp = resp.click('Settings')
resp = resp.click('upload')
resp = resp.click(href='/manage/time-period-exceptions-source/%d/replace' % source.pk)
resp.form['ics_file'] = Upload('exceptions.ics', ics_file_content, 'text/calendar')
resp = resp.form.submit().follow()
assert TimePeriodException.objects.count() == 2
assert source.timeperiodexception_set.count() == 2
new_exceptions = list(source.timeperiodexception_set.order_by('pk'))
assert exceptions[0].pk != new_exceptions[0].pk
assert exceptions[1].pk != new_exceptions[1].pk
@mock.patch('chrono.agendas.models.requests.get')
def test_meetings_agenda_refresh_time_period_exception_source(mocked_get, app, admin_user):
agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
MeetingType(agenda=agenda, label='Blah').save()
TimePeriod.objects.create(
weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
)
ics_url_content = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//foo.bar//EN
BEGIN:VEVENT
DTSTART:20180101
DTEND:20180101
SUMMARY:New Year's Eve
END:VEVENT
END:VCALENDAR"""
login(app)
# import a source from an url
resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
resp = resp.click('Settings')
resp = resp.click('upload')
resp.form['ics_url'] = 'http://example.com/foo.ics'
mocked_response = mock.Mock()
mocked_response.text = ics_url_content
mocked_get.return_value = mocked_response
resp = resp.form.submit(status=302).follow()
assert TimePeriodException.objects.filter(desk=desk).count() == 1
source = TimePeriodExceptionSource.objects.latest('pk')
assert source.timeperiodexception_set.count() == 1
exceptions = list(source.timeperiodexception_set.order_by('pk'))
# refresh the source
resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
resp = resp.click('Settings')
resp = resp.click('upload')
mocked_response = mock.Mock()
mocked_response.text = ics_url_content
mocked_get.return_value = mocked_response
resp = resp.click(href='/manage/time-period-exceptions-source/%d/refresh' % source.pk)
assert TimePeriodException.objects.count() == 1
assert source.timeperiodexception_set.count() == 1
new_exceptions = list(source.timeperiodexception_set.order_by('pk'))
assert exceptions[0].pk != new_exceptions[0].pk
def test_agenda_day_view(app, admin_user, manager_user, api_user):
agenda = Agenda.objects.create(label='New Example', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='New Desk')