agendas: refresh an exception source (#29209)
This commit is contained in:
parent
2aed92d132
commit
7a33c41586
|
@ -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'))
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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')
|
||||
|
|
Loading…
Reference in New Issue