add skeleton model and management pages for agendas
This commit is contained in:
parent
d13dbf8178
commit
101c462157
|
@ -4,7 +4,7 @@ recursive-include chrono/locale *.po *.mo
|
|||
# static
|
||||
|
||||
# templates
|
||||
recursive-include combo/manager/templates *.html
|
||||
recursive-include chrono/manager/templates *.html
|
||||
|
||||
include COPYING README
|
||||
include MANIFEST.in
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Agenda',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('label', models.CharField(max_length=50, verbose_name='Label')),
|
||||
('slug', models.SlugField(verbose_name='Slug')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['label'],
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,46 @@
|
|||
# chrono - agendas system
|
||||
# Copyright (C) 2016 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/>.
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import models
|
||||
from django.utils.text import slugify
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class Agenda(models.Model):
|
||||
label = models.CharField(_('Label'), max_length=50)
|
||||
slug = models.SlugField(_('Slug'))
|
||||
|
||||
class Meta:
|
||||
ordering = ['label']
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.slug:
|
||||
base_slug = slugify(self.label)
|
||||
slug = base_slug
|
||||
i = 1
|
||||
while True:
|
||||
try:
|
||||
Agenda.objects.get(slug=slug)
|
||||
except self.DoesNotExist:
|
||||
break
|
||||
slug = '%s-%s' % (base_slug, i)
|
||||
i += 1
|
||||
self.slug = slug
|
||||
super(Agenda, self).save(*args, **kwargs)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('chrono-manager-agenda-view', kwargs={'pk': self.id})
|
|
@ -0,0 +1,22 @@
|
|||
{% extends "chrono/manager_home.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block appbar %}
|
||||
{% if object.id %}
|
||||
<h2>{% trans "Edit Agenda" %}</h2>
|
||||
{% else %}
|
||||
<h2>{% trans "New Agenda" %}</h2>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<div class="buttons">
|
||||
<button>{% trans "Save" %}</button>
|
||||
<a class="cancel" href="{% url 'chrono-manager-homepage' %}">{% trans 'Cancel' %}</a>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -0,0 +1,18 @@
|
|||
{% extends "chrono/manager_home.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block appbar %}
|
||||
<h2>{% trans 'Agenda' %} - {{ object.label }}</h2>
|
||||
<a rel="popup" href="{% url 'chrono-manager-agenda-delete' pk=object.id %}">{% trans 'Delete' %}</a>
|
||||
<a rel="popup" href="{% url 'chrono-manager-agenda-edit' pk=object.id %}">{% trans 'Rename' %}</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumb %}
|
||||
{{ block.super }}
|
||||
{% if object %}
|
||||
<a href="{% url 'chrono-manager-agenda-view' pk=object.id %}">{{object.label}}</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,17 @@
|
|||
{% extends "chrono/manager_home.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block appbar %}
|
||||
<h2>{{ view.model.get_verbose_name }}</h2>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{% blocktrans %}Are you sure you want to delete this?{% endblocktrans %}
|
||||
<div class="buttons">
|
||||
<button>{% trans 'Confirm Deletion' %}</button>
|
||||
<a class="cancel" href="{{ object.get_absolute_url }}">{% trans 'Cancel' %}</a>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -3,12 +3,18 @@
|
|||
|
||||
{% block appbar %}
|
||||
<h2>{% trans 'Agendas' %}</h2>
|
||||
<a rel="popup" href="{% url 'chrono-manager-agenda-add' %}">{% trans 'New' %}</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% if object_list %}
|
||||
<div class="objects-list">
|
||||
<div>
|
||||
<ul class="objects-list single-links">
|
||||
{% for object in object_list %}
|
||||
<li><a href="{% url 'chrono-manager-agenda-view' pk=object.id %}">{{ object.label }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="big-msg-info">
|
||||
|
|
|
@ -20,5 +20,13 @@ from . import views
|
|||
|
||||
urlpatterns = patterns('chrono.views',
|
||||
url(r'^$', views.homepage, name='chrono-manager-homepage'),
|
||||
url(r'^agendas/add/$', views.agenda_add,
|
||||
name='chrono-manager-agenda-add'),
|
||||
url(r'^agendas/(?P<pk>\w+)/$', views.agenda_view,
|
||||
name='chrono-manager-agenda-view'),
|
||||
url(r'^agendas/(?P<pk>\w+)/edit$', views.agenda_edit,
|
||||
name='chrono-manager-agenda-edit'),
|
||||
url(r'^agendas/(?P<pk>\w+)/delete$', views.agenda_delete,
|
||||
name='chrono-manager-agenda-delete'),
|
||||
url(r'^menu.json$', views.menu_json),
|
||||
)
|
||||
|
|
|
@ -16,19 +16,54 @@
|
|||
|
||||
import json
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.urlresolvers import reverse_lazy, reverse
|
||||
from django.http import HttpResponse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.encoding import force_text
|
||||
from django.views.generic import TemplateView
|
||||
from django.views.generic import (DetailView, CreateView, UpdateView,
|
||||
ListView, DeleteView)
|
||||
|
||||
from chrono.agendas.models import Agenda
|
||||
|
||||
|
||||
class HomepageView(TemplateView):
|
||||
class HomepageView(ListView):
|
||||
template_name = 'chrono/manager_home.html'
|
||||
model = Agenda
|
||||
|
||||
homepage = HomepageView.as_view()
|
||||
|
||||
|
||||
class AgendaAddView(CreateView):
|
||||
template_name = 'chrono/manager_agenda_form.html'
|
||||
model = Agenda
|
||||
fields = ['label']
|
||||
|
||||
agenda_add = AgendaAddView.as_view()
|
||||
|
||||
|
||||
class AgendaEditView(UpdateView):
|
||||
template_name = 'chrono/manager_agenda_form.html'
|
||||
model = Agenda
|
||||
fields = ['label']
|
||||
|
||||
agenda_edit = AgendaEditView.as_view()
|
||||
|
||||
|
||||
class AgendaDeleteView(DeleteView):
|
||||
template_name = 'chrono/manager_confirm_delete.html'
|
||||
model = Agenda
|
||||
success_url = reverse_lazy('chrono-manager-homepage')
|
||||
|
||||
agenda_delete = AgendaDeleteView.as_view()
|
||||
|
||||
|
||||
class AgendaView(DetailView):
|
||||
template_name = 'chrono/manager_agenda_view.html'
|
||||
model = Agenda
|
||||
|
||||
agenda_view = AgendaView.as_view()
|
||||
|
||||
|
||||
def menu_json(request):
|
||||
response = HttpResponse(content_type='application/json')
|
||||
label = _('Agendas')
|
||||
|
|
|
@ -53,6 +53,7 @@ INSTALLED_APPS = (
|
|||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'gadjo',
|
||||
'chrono.agendas',
|
||||
'chrono.manager',
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import pytest
|
||||
|
||||
from chrono.agendas.models import Agenda
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_slug():
|
||||
agenda = Agenda(label=u'Foo bar')
|
||||
agenda.save()
|
||||
assert agenda.slug == 'foo-bar'
|
||||
|
||||
def test_existing_slug():
|
||||
agenda = Agenda(label=u'Foo bar', slug='bar')
|
||||
agenda.save()
|
||||
assert agenda.slug == 'bar'
|
||||
|
||||
def test_duplicate_slugs():
|
||||
agenda = Agenda(label=u'Foo baz')
|
||||
agenda.save()
|
||||
assert agenda.slug == 'foo-baz'
|
||||
agenda = Agenda(label=u'Foo baz')
|
||||
agenda.save()
|
||||
assert agenda.slug == 'foo-baz-1'
|
||||
agenda = Agenda(label=u'Foo baz')
|
||||
agenda.save()
|
||||
assert agenda.slug == 'foo-baz-2'
|
|
@ -4,6 +4,8 @@ from webtest import TestApp
|
|||
|
||||
from chrono.wsgi import application
|
||||
|
||||
from chrono.agendas.models import Agenda
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
@pytest.fixture
|
||||
|
@ -38,3 +40,47 @@ def test_logout(admin_user):
|
|||
app = login(TestApp(application))
|
||||
app.get('/logout/')
|
||||
assert app.get('/manage/', status=302).location == 'http://localhost:80/login/?next=/manage/'
|
||||
|
||||
def test_menu_json(admin_user):
|
||||
app = login(TestApp(application))
|
||||
resp = app.get('/manage/menu.json', status=200)
|
||||
assert resp.json[0]['url'] == 'http://localhost:80/manage/'
|
||||
assert resp.json[0]['label'] == 'Agendas'
|
||||
resp2 = app.get('/manage/menu.json?callback=Q', status=200)
|
||||
assert resp2.body == 'Q(%s);' % resp.body
|
||||
|
||||
def test_add_agenda(admin_user):
|
||||
app = login(TestApp(application))
|
||||
resp = app.get('/manage/', status=200)
|
||||
resp = resp.click('New')
|
||||
resp.form['label'] = 'Foo bar'
|
||||
resp = resp.form.submit()
|
||||
assert resp.location == 'http://localhost:80/manage/agendas/1/'
|
||||
resp = resp.follow()
|
||||
assert '<h2>Agenda - Foo bar</h2>' in resp.body
|
||||
|
||||
def test_rename_agenda(admin_user):
|
||||
agenda = Agenda(label=u'Foo bar')
|
||||
agenda.save()
|
||||
app = login(TestApp(application))
|
||||
resp = app.get('/manage/', status=200)
|
||||
resp = resp.click('Foo bar')
|
||||
resp = resp.click('Rename')
|
||||
assert resp.form['label'].value == 'Foo bar'
|
||||
resp.form['label'] = 'Foo baz'
|
||||
resp = resp.form.submit()
|
||||
assert resp.location == 'http://localhost:80/manage/agendas/1/'
|
||||
resp = resp.follow()
|
||||
assert '<h2>Agenda - Foo baz</h2>' in resp.body
|
||||
|
||||
def test_delete_agenda(admin_user):
|
||||
agenda = Agenda(label=u'Foo bar')
|
||||
agenda.save()
|
||||
app = login(TestApp(application))
|
||||
resp = app.get('/manage/', status=200)
|
||||
resp = resp.click('Foo bar')
|
||||
resp = resp.click('Delete')
|
||||
resp = resp.form.submit()
|
||||
assert resp.location == 'http://localhost:80/manage/'
|
||||
resp = resp.follow()
|
||||
assert not 'Foo bar' in resp.body
|
||||
|
|
Loading…
Reference in New Issue