Changeset - 509ed963b5e1
[Not reviewed]
default
1 11 3
Branko Majic (branko) - 11 years ago 2013-07-07 15:57:32
branko@majic.rs
CONNT-2: Implemented project editing, and removal. Added tests for project add/edit/remove views. Added rendering of messages in the base template. Renamed the project_add view to project_create in order to follow the view naming schema. Cleaned-up some PEP8 errors (unrelated to issue).
15 files changed with 301 insertions and 46 deletions:
0 comments (0 inline, 0 general)
conntrackt/models.py
Show inline comments
 
@@ -105,7 +105,6 @@ class Entity(models.Model):
 
        # Enforce uniqueness of entity name in a project.
 
        unique_together = ("name", "project")
 

	
 

	
 
    def __unicode__(self):
 
        """
 
        Returns:
conntrackt/templates/conntrackt/base.html
Show inline comments
 
@@ -49,8 +49,13 @@
 

	
 
    <div class="container">
 
      <div id="content" class="block">
 
      {% block content %}
 
      {% endblock %}
 
        {% if messages %}
 
          {% for message in messages %}
 
            <div class="{{message.tags}}">{{ message }}</div>
 
          {% endfor %}
 
        {% endif %}
 
        {% block content %}
 
        {% endblock %}
 
      </div>
 
    </div>
 

	
conntrackt/templates/conntrackt/index.html
Show inline comments
 
@@ -13,7 +13,7 @@
 
<hr>
 
<div class="row">
 
  <div class="span12">
 
    {% html_link "Add project" "project_add" class="btn btn-primary" %}
 
    {% html_link "Add project" "project_create" class="btn btn-primary" %}
 
  </div>
 
</div>
 
<hr>
conntrackt/templates/conntrackt/project_confirm_delete.html
Show inline comments
 
new file 100644
 
{% extends "conntrackt/base.html" %}
 

	
 
{# For html_link #}
 
{% load conntrackt_tags %}
 
{# For Bootstrapped forms #}
 
{% load crispy_forms_tags %}
 

	
 
{% block content %}
 
<div class="row">
 
  <h1 class="span12">Remove project {{project.name}}</h1>
 
</div>
 
<div class="row">
 
  <div class="span12">
 
    <form action="" method="post">
 
      <div class="controls controls-row">
 
        {% csrf_token %}
 
        {{ form }}
 
        Are you sure you want to remove this project?
 
      </div>
 
      <hr>
 
      <div class="controls">
 
        <button type="submit" class="btn btn-primary">Remove</button>
 
      </div>
 
    </form>
 
  </div>
 
</div>
 
{% endblock content %}
conntrackt/templates/conntrackt/project_create_form.html
Show inline comments
 
new file 100644
 
{% extends "conntrackt/base.html" %}
 

	
 
{# For html_link #}
 
{% load conntrackt_tags %}
 
{# For Bootstrapped forms #}
 
{% load crispy_forms_tags %}
 

	
 
{% block content %}
 
<div class="row">
 
  <h1 class="span12">Add new project</h1>
 
</div>
 
<div class="row">
 
  <div class="span6">
 
    <form action="" method="post">
 
      <div class="controls controls-row">
 
      {% csrf_token %}
 
      {{ form | crispy }}
 
      </div>
 
      <div class="controls">
 
        <button type="submit" class="btn btn-primary">Add</button>
 
      </div>
 
    </form>
 
  </div>
 
</div>
 
{% endblock content %}
conntrackt/templates/conntrackt/project_detail.html
Show inline comments
 
@@ -14,6 +14,13 @@
 
<hr>
 
{% endif %}
 

	
 
<div class="row">
 
  <div class="span12">
 
    {% html_link "Edit" "project_update" project.id class="btn btn-primary" %}
 
    {% html_link "Remove" "project_delete" project.id class="btn btn-primary" %}
 
  </div>
 
</div>
 
<hr>
 
{% if location_entities %}
 
<div class="row">
 
  {% for location, entities in location_entities %}
conntrackt/templates/conntrackt/project_form.html
Show inline comments
 
deleted file
conntrackt/templates/conntrackt/project_update_form.html
Show inline comments
 
new file 100644
 
{% extends "conntrackt/base.html" %}
 

	
 
{# For html_link #}
 
{% load conntrackt_tags %}
 
{# For Bootstrapped forms #}
 
{% load crispy_forms_tags %}
 

	
 
{% block content %}
 
<div class="row">
 
  <h1 class="span12">Edit project {{project.name}}</h1>
 
</div>
 
<div class="row">
 
  <div class="span6">
 
    <form action="" method="post">
 
      <div class="controls controls-row">
 
      {% csrf_token %}
 
      {{ form | crispy }}
 
      </div>
 
      <div class="controls">
 
        <button type="submit" class="btn btn-primary">Update</button>
 
      </div>
 
    </form>
 
  </div>
 
</div>
 
{% endblock content %}
conntrackt/tests/test_models.py
Show inline comments
 
@@ -97,7 +97,6 @@ class EntityTest(TestCase):
 

	
 
        self.assertEqual(str(ent), representation)
 

	
 

	
 
    def test_unique_name(self):
 
        """
 
        Test if unique entity name is enforced across same project.
 
@@ -131,11 +130,10 @@ class InterfaceTest(TestCase):
 

	
 
        interface = entity.interface_set.get(pk=1)
 

	
 
        duplicate = Interface(name=interface.name, description = "Duplicate interface.", entity=entity, address="10.10.10.10", netmask="255.255.255.255")
 
        duplicate = Interface(name=interface.name, description="Duplicate interface.", entity=entity, address="10.10.10.10", netmask="255.255.255.255")
 

	
 
        self.assertRaises(IntegrityError, duplicate.save)
 

	
 

	
 
    def test_representation_single(self):
 
        """
 
        Test representation of single IP address.
conntrackt/tests/test_views.py
Show inline comments
 
@@ -379,16 +379,15 @@ class ProjectCreateViewTest(TestCase):
 
        self.user["fullperms"].user_permissions.add(Permission.objects.get(codename="add_project"))
 
        self.user["noperms"] = User.objects.create_user("noperms", "noperms@example.com", "noperms")
 

	
 

	
 
    def test_permission_denied(self):
 
        """
 
        Tests if permission will be denied for client without sufficient privileges.
 
        """
 

	
 
        self.client.login(username="noperms", password="noperms")
 
        
 
        response = self.client.get(reverse("project_add"))
 
 
 

	
 
        response = self.client.get(reverse("project_create"))
 

	
 
        self.assertContains(response, "You have insufficient privileges to access this resource. Please contact your local system administrator if you believe you should have been granted access.", status_code=403)
 

	
 
    def test_permission_granted(self):
 
@@ -398,7 +397,58 @@ class ProjectCreateViewTest(TestCase):
 

	
 
        self.client.login(username="fullperms", password="fullperms")
 

	
 
        response = self.client.get(reverse("project_add"))
 
        response = self.client.get(reverse("project_create"))
 

	
 
        self.assertEqual(response.status_code, 200)
 

	
 
    def test_form_styling(self):
 
        """
 
        Tests if proper form styling is being applied.
 
        """
 

	
 
        self.client.login(username="fullperms", password="fullperms")
 

	
 
        response = self.client.get(reverse("project_create"))
 

	
 
        self.assertContains(response, 'class="span6 textinput')
 
        self.assertContains(response, 'class="span6 textarea')
 
        self.assertContains(response, 'placeholder="New Project"')
 
        self.assertContains(response, 'placeholder="Description for new project."')
 

	
 

	
 
class ProjectUpdateViewTest(TestCase):
 

	
 
    fixtures = ['test-data.json']
 

	
 
    def setUp(self):
 
        # Set-up web client.
 
        self.client = Client()
 

	
 
        # Set-up users with different view permissions.
 
        self.user = {}
 
        self.user["fullperms"] = User.objects.create_user("fullperms", "fullperms@example.com", "fullperms")
 
        self.user["fullperms"].user_permissions.add(Permission.objects.get(codename="change_project"))
 
        self.user["noperms"] = User.objects.create_user("noperms", "noperms@example.com", "noperms")
 

	
 
    def test_permission_denied(self):
 
        """
 
        Tests if permission will be denied for client without sufficient privileges.
 
        """
 

	
 
        self.client.login(username="noperms", password="noperms")
 

	
 
        response = self.client.get(reverse("project_update", args=(1,)))
 

	
 
        self.assertContains(response, "You have insufficient privileges to access this resource. Please contact your local system administrator if you believe you should have been granted access.", status_code=403)
 

	
 
    def test_permission_granted(self):
 
        """
 
        Tests if permission will be granted for user with correct privileges.
 
        """
 

	
 
        self.client.login(username="fullperms", password="fullperms")
 

	
 
        response = self.client.get(reverse("project_update", args=(1,)))
 

	
 
        self.assertEqual(response.status_code, 200)
 

	
 
@@ -409,10 +459,87 @@ class ProjectCreateViewTest(TestCase):
 

	
 
        self.client.login(username="fullperms", password="fullperms")
 

	
 
        response = self.client.get(reverse("project_add"))
 
        response = self.client.get(reverse("project_update", args=(1,)))
 

	
 
        self.assertContains(response, 'class="span6 textinput')
 
        self.assertContains(response, 'class="span6 textarea')
 
        self.assertContains(response, 'placeholder="New Project"')
 
        self.assertContains(response, 'placeholder="Description for new project."')
 
        self.assertContains(response, 'placeholder="Project name"')
 
        self.assertContains(response, 'placeholder="Description for project."')
 

	
 
    def test_content(self):
 
        """
 
        Tests if the form comes pre-populated with proper content.
 
        """
 

	
 
        self.client.login(username="fullperms", password="fullperms")
 

	
 
        response = self.client.get(reverse("project_update", args=(1,)))
 

	
 
        self.assertContains(response, ">Edit project Test Project 1<")
 
        self.assertContains(response, 'value="Test Project 1"')
 
        self.assertContains(response, "This is a test project 1.")
 

	
 

	
 
class ProjectDeleteViewTest(TestCase):
 

	
 
    fixtures = ['test-data.json']
 

	
 
    def setUp(self):
 
        # Set-up web client.
 
        self.client = Client()
 

	
 
        # Set-up users with different view permissions.
 
        self.user = {}
 
        self.user["fullperms"] = User.objects.create_user("fullperms", "fullperms@example.com", "fullperms")
 
        self.user["fullperms"].user_permissions.add(Permission.objects.get(codename="delete_project"))
 
        self.user["fullperms"].user_permissions.add(Permission.objects.get(codename="view"))
 
        self.user["noperms"] = User.objects.create_user("noperms", "noperms@example.com", "noperms")
 

	
 
    def test_permission_denied(self):
 
        """
 
        Tests if permission will be denied for client without sufficient privileges.
 
        """
 

	
 
        self.client.login(username="noperms", password="noperms")
 

	
 
        response = self.client.get(reverse("project_delete", args=(1,)))
 

	
 
        self.assertContains(response, "You have insufficient privileges to access this resource. Please contact your local system administrator if you believe you should have been granted access.", status_code=403)
 

	
 
    def test_permission_granted(self):
 
        """
 
        Tests if permission will be granted for user with correct privileges.
 
        """
 

	
 
        self.client.login(username="fullperms", password="fullperms")
 

	
 
        response = self.client.get(reverse("project_delete", args=(1,)))
 

	
 
        self.assertEqual(response.status_code, 200)
 

	
 
    def test_content(self):
 
        """
 
        Tests if the form comes pre-populated with proper content.
 
        """
 

	
 
        self.client.login(username="fullperms", password="fullperms")
 

	
 
        response = self.client.get(reverse("project_delete", args=(1,)))
 

	
 
        self.assertContains(response, ">Remove project Test Project 1<")
 
        self.assertContains(response, "Are you sure you want to remove this project?")
 

	
 
    def test_message(self):
 
        """
 
        Tests if the message gets added when the project is deleted.
 
        """
 

	
 
        self.client.login(username="fullperms", password="fullperms")
 

	
 
        response = self.client.get(reverse("project_delete", args=(1,)))
 

	
 
        response = self.client.post(reverse("project_delete", args=(1,)),
 
                                    {'csrfmiddlewaretoken': response.context['request'].META['CSRF_COOKIE']},
 
                                    follow=True)
 

	
 
        self.assertContains(response, "Project Test Project 1 has been removed.")
conntrackt/urls.py
Show inline comments
 
@@ -3,7 +3,7 @@ from django.conf.urls import patterns, u
 
from django.contrib.auth.views import login, logout
 

	
 
# Application imports.
 
from .views import IndexView, ProjectView, ProjectCreateView, EntityView, entity_iptables, project_iptables
 
from .views import IndexView, ProjectView, ProjectCreateView, ProjectUpdateView, ProjectDeleteView, EntityView, entity_iptables, project_iptables
 

	
 

	
 
urlpatterns = patterns(
 
@@ -14,7 +14,11 @@ urlpatterns = patterns(
 
    url(r'^project/(?P<pk>\d+)/$', ProjectView.as_view(),
 
        name='project'),
 
    # View for creating a new project.
 
    url(r'^project/add/$', ProjectCreateView.as_view(), name="project_add"),
 
    url(r'^project/add/$', ProjectCreateView.as_view(), name="project_create"),
 
    # View for updating an existing project.
 
    url(r'^project/(?P<pk>\d+)/edit/$', ProjectUpdateView.as_view(), name="project_update"),
 
    # View for deleting a project.
 
    url(r'^project/(?P<pk>\d+)/remove/$', ProjectDeleteView.as_view(), name="project_delete"),
 
    # View for showing information about an entity.
 
    url(r'^entity/(?P<pk>\d+)/$', EntityView.as_view(),
 
        name='entity'),
conntrackt/views.py
Show inline comments
 
@@ -4,9 +4,11 @@ from zipfile import ZipFile, ZIP_DEFLATE
 

	
 
# Django imports.
 
from django.contrib.auth.decorators import permission_required
 
from django.contrib import messages
 
from django.core.urlresolvers import reverse_lazy
 
from django.http import HttpResponse
 
from django.shortcuts import render_to_response, get_object_or_404
 
from django.views.generic import TemplateView, DetailView, CreateView
 
from django.views.generic import TemplateView, DetailView, CreateView, UpdateView, DeleteView
 

	
 
# Third-party application imports.
 
from braces.views import MultiplePermissionsRequiredMixin
 
@@ -237,7 +239,8 @@ class ProjectCreateView(MultiplePermissi
 
    """
 

	
 
    model = Project
 
    
 
    template_name_suffix = "_create_form"
 

	
 
    # Required permissions.
 
    permissions = {
 
        "all": ("conntrackt.add_project",),
 
@@ -260,3 +263,61 @@ class ProjectCreateView(MultiplePermissi
 

	
 
        return form
 

	
 

	
 
class ProjectUpdateView(MultiplePermissionsRequiredMixin, UpdateView):
 
    """
 
    View for modifying an existing project.
 
    """
 

	
 
    model = Project
 
    template_name_suffix = "_update_form"
 

	
 
    # Required permissions.
 
    permissions = {
 
        "all": ("conntrackt.change_project",),
 
        }
 

	
 
    # Raise authorisation denied exception for unmet permissions.
 
    raise_exception = True
 

	
 
    def get_form(self, form_class):
 
        """
 
        Implements an override for the default form constructed for the create
 
        view that includes some better styling of input widgets.
 
        """
 

	
 
        form = super(ProjectUpdateView, self).get_form(form_class)
 
        form.fields["name"].widget.attrs["class"] = "span6"
 
        form.fields["name"].widget.attrs["placeholder"] = "Project name"
 
        form.fields["description"].widget.attrs["class"] = "span6"
 
        form.fields["description"].widget.attrs["placeholder"] = "Description for project."
 

	
 
        return form
 

	
 

	
 
class ProjectDeleteView(MultiplePermissionsRequiredMixin, DeleteView):
 
    """
 
    View for deleting a project.
 
    """
 

	
 
    model = Project
 

	
 
    # Required permissions.
 
    permissions = {
 
        "all": ("conntrackt.delete_project",),
 
        }
 

	
 
    # Raise authorisation denied exception for unmet permissions.
 
    raise_exception = True
 

	
 
    success_url = reverse_lazy("index")
 

	
 
    def post(self, *args, **kwargs):
 
        """
 
        Add a success message that will be displayed to the user to confirm the
 
        project deletion.
 
        """
 

	
 
        messages.success(self.request, "Project %s has been removed." % self.get_object().name, extra_tags="alert alert-success")
 

	
 
        return super(ProjectDeleteView, self).post(*args, **kwargs)
projtest/projtest/settings.py
Show inline comments
 
@@ -10,7 +10,7 @@ MANAGERS = ADMINS
 

	
 
DATABASES = {
 
    'default': {
 
        'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
 
        'ENGINE': 'django.db.backends.sqlite3',  # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
 
        'NAME': 'projtest.db',                      # Or path to database file if using sqlite3.
 
        'USER': '',                      # Not used with sqlite3.
 
        'PASSWORD': '',                  # Not used with sqlite3.
 
@@ -167,7 +167,7 @@ LOGGING = {
 
}
 

	
 
# View that should be called for log-in action.
 
LOGIN_URL="login"
 
LOGIN_URL = "login"
 

	
 
# Use custom test runner.
 
TEST_RUNNER = 'discover_runner.DiscoverRunner'
projtest/projtest/urls.py
Show inline comments
 
@@ -13,7 +13,7 @@ urlpatterns = patterns(
 
    # Examples:
 
    # url(r'^$', 'projtest.views.home', name='home'),
 
    # url(r'^projtest/', include('projtest.foo.urls')),
 
    url(r'^$', lambda r : HttpResponseRedirect('conntrackt/')),
 
    url(r'^$', lambda r: HttpResponseRedirect('conntrackt/')),
 
    url(r'^conntrackt/', include('conntrackt.urls')),
 

	
 

	
 
@@ -23,4 +23,3 @@ urlpatterns = patterns(
 
    # Uncomment the next line to enable the admin:
 
    url(r'^admin/', include(admin.site.urls)),
 
)
 

	
setup.py
Show inline comments
 
@@ -26,7 +26,7 @@ setup(
 
        'Environment :: Web Environment',
 
        'Framework :: Django',
 
        'Intended Audience :: Developers',
 
        'License :: OSI Approved :: GPLv3', # example license
 
        'License :: OSI Approved :: GPLv3',
 
        'Operating System :: OS Independent',
 
        'Programming Language :: Python',
 
        'Programming Language :: Python :: 2.7',
0 comments (0 inline, 0 general)