-
Notifications
You must be signed in to change notification settings - Fork 0
Feature project list view #85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
cb9712c
49e83dd
6d0b68c
1077856
681febc
c074b88
2a3375d
7ee6ec8
b07f020
9ff105f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,208 @@ | ||
| {% extends 'base.html' %} | ||
| {% load i18n %} | ||
| {% block content %} | ||
| {% load rest_framework %} | ||
|
|
||
| <h1>{% trans "Projects list" %} | ||
| <button type="button" class="btn btn-default btn-sm" data-toggle="collapse" data-target="#collapseSort" aria-expanded="false" aria-controls="collapseSort"> | ||
| <span class="glyphicon glyphicon-th-list"></span> | ||
| </button> | ||
| </h1> | ||
|
|
||
| <div class="collapse" id="collapseSort"> | ||
| <div class="col-md-4 col-md-offset-4"> | ||
| <nav id="projects-navbar" class="navbar navbar-light" style="background-color: #e6ecff;"> | ||
| <ul class="nav nav-pills"> | ||
| <li class="nav-item"> | ||
| <a class="nav-link" href="#active" style="color:grey">{% trans "Active" %}</a> | ||
| </li> | ||
| <li class="nav-item"> | ||
| <a class="nav-link" href="#terminated" style="color:grey">{% trans "Terminated" %}</a> | ||
| </li> | ||
| <li class="nav-item"> | ||
| <a class="nav-link" href="#completed" style="color:grey">{% trans "Completed" %}</a> | ||
| </li> | ||
| <li class="nav-item"> | ||
| <a class="nav-link" href="#all" style="color:grey">{% trans "All" %}</a> | ||
| </li> | ||
| <li class="nav-item dropdown"> | ||
| <a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#" style="color:grey" role="button" aria-haspopup="true" aria-expanded="false"><span class="glyphicon glyphicon-sort-by-attributes"></span> </a> | ||
| <div class="dropdown-menu"> | ||
| <a class="dropdown-item" href="{% url 'custom-projects-list' %}?sort=name" style="color:grey">{% trans "Name" %}</a><br> | ||
| <a class="dropdown-item" href="{% url 'custom-projects-list' %}?sort=start_date" style="color:grey">{% trans "Start date" %}</a><br> | ||
| <a class="dropdown-item" href="{% url 'custom-projects-list' %}?sort=stop_date" style="color:grey">{% trans "Completed date" %}</a><br> | ||
| <a class="dropdown-item" href="{% url 'custom-projects-list' %}?sort=members_count" style="color:grey">{% trans "Members count" %}</a><br> | ||
| </div> | ||
| </li> | ||
| <li class="nav-item dropdown"> | ||
| <a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#" style="color:grey" role="button" aria-haspopup="true" aria-expanded="false"><span class="glyphicon glyphicon-sort-by-attributes-alt"></span> </a> | ||
| <div class="dropdown-menu"> | ||
| <a class="dropdown-item" href="{% url 'custom-projects-list' %}?sort=-name" style="color:grey">{% trans "Name" %}</a><br> | ||
| <a class="dropdown-item" href="{% url 'custom-projects-list' %}?sort=-start_date" style="color:grey">{% trans "Start date" %}</a><br> | ||
| <a class="dropdown-item" href="{% url 'custom-projects-list' %}?sort=-stop_date" style="color:grey">{% trans "Completed date" %}</a><br> | ||
| <a class="dropdown-item" href="{% url 'custom-projects-list' %}?sort=-members_count" style="color:grey">{% trans "Members count" %}</a><br> | ||
| </div> | ||
| </li> | ||
| </ul> | ||
| </nav> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div data-spy="scroll" data-target="#projects-navbar" data-offset="0"> | ||
|
|
||
| <div id="active" class="modal-dialog" style="margin-bottom:0"> | ||
| <button class="btn btn-primary btn-block" type="button" data-toggle="collapse" data-target="#collapseActive" aria-expanded="false" aria-controls="collapseActive"> | ||
| {% trans "Active" %} | ||
| </button> | ||
| </div> | ||
| <div class="panel-collapse collapse in" id="collapseActive"> | ||
| <div class="card card-body"> | ||
| <br/> | ||
| <div class="container"> | ||
| <div class="row"> | ||
| {% for project in object_list.filter_active %} | ||
| <div class="col-sm-4"> | ||
| <div class="list-group"> | ||
| <div class="list-group-item list-group-item-action flex-column align-items-start active"> | ||
| <div class="d-flex w-100 justify-content-between"> | ||
| <h4 class="mb-1">{{ project.name }}</h4> | ||
| </div> | ||
| <p class="mb-1">{% trans "started at" %}: {{project.start_date}}</p> | ||
| <small>{% trans "managers" %}: | ||
| {% for user in project.managers.all %} | ||
| {{user.first_name}} {{user.last_name}}<br> | ||
| {% endfor %}</small> | ||
| <small><span class="glyphicon glyphicon-user"></span> {{project.members.count}}</small> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| {% if forloop.counter|divisibleby:3 %} | ||
| </div> | ||
| <div class="row"> | ||
| {% endif %} | ||
| {% endfor %} | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div id="terminated" class="modal-dialog" style="margin-bottom:0"> | ||
| <button class="btn btn-info btn-block" type="button" data-toggle="collapse" data-target="#collapseTerminated" aria-expanded="false" aria-controls="collapseTerminated"> | ||
| {% trans "Terminated" %} | ||
| </button> | ||
| </div> | ||
| <div class="collapse" id="collapseTerminated"> | ||
| <div class="card card-body"> | ||
| <br/> | ||
| <div class="container"> | ||
| <div class="row"> | ||
| {% for project in object_list.filter_terminated %} | ||
| <div class="col-sm-4"> | ||
| <div class="list-group"> | ||
| <div class="list-group-item list-group-item-info flex-column align-items-start"> | ||
| <div class="d-flex w-100 justify-content-between"> | ||
| <h4 class="mb-1">{{ project.name }}</h4> | ||
| </div> | ||
| <p class="mb-1">{% trans "started at" %}: {{project.start_date}}</p> | ||
| <small>{% trans "managers" %}: | ||
| {% for user in project.managers.all %} | ||
| {{user.first_name}} {{user.last_name}}<br> | ||
| {% endfor %}</small> | ||
| <small><span class="glyphicon glyphicon-user"></span> {{project.members.count}}</small> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| {% if forloop.counter|divisibleby:3 %} | ||
| </div> | ||
| <div class="row"> | ||
| {% endif %} | ||
| {% endfor %} | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div id="completed" class="modal-dialog" style="margin-bottom:0"> | ||
| <button class="btn btn-success btn-block" type="button" data-toggle="collapse" data-target="#collapseCompleted" aria-expanded="false" aria-controls="collapseCompleted"> | ||
| {% trans "Completed" %} | ||
| </button> | ||
| </div> | ||
| <div class="collapse" id="collapseCompleted"> | ||
| <div class="card card-body"> | ||
| <br/> | ||
| <div class="container"> | ||
| <div class="row"> | ||
| {% for project in object_list.filter_completed %} | ||
| <div class="col-sm-4"> | ||
| <div class="list-group"> | ||
| <div class="list-group-item list-group-item-action list-group-item-success flex-column align-items-start"> | ||
| <div class="d-flex w-100 justify-content-between"> | ||
| <h4 class="mb-1">{{ project.name }}</h4> | ||
| </div> | ||
| <p class="mb-1">{% trans "completed at" %}: {{project.stop_date}}</p> | ||
| <small>{% trans "managers" %}: | ||
| {% for user in project.managers.all %} | ||
| {{user.first_name}} {{user.last_name}}<br> | ||
| {% endfor %}</small> | ||
| <small><span class="glyphicon glyphicon-user"></span> {{project.members.count}}</small> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| {% if forloop.counter|divisibleby:3 %} | ||
| </div> | ||
| <div class="row"> | ||
| {% endif %} | ||
| {% endfor %} | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div id="all" class="modal-dialog" style="margin-bottom:0"> | ||
| <button class="btn btn-dark btn-block" type="button" data-toggle="collapse" data-target="#collapseAll" aria-expanded="false" aria-controls="collapseAll"> | ||
| {% trans "All" %} | ||
| </button> | ||
| </div> | ||
| <div class="collapse" id="collapseAll"> | ||
| <div class="card card-body"> | ||
| <br/> | ||
| <div class="container"> | ||
| <div class="row"> | ||
| {% for project in object_list %} | ||
| <div class="col-sm-4"> | ||
| <div class="list-group"> | ||
| {% if project.terminated is False and project.stop_date is None %} | ||
| <div class="list-group-item list-group-item-action flex-column align-items-start active"> | ||
| {% elif project.stop_date is not None %} | ||
| <div class="list-group-item list-group-item-success flex-column align-items-start"> | ||
| {% else %} | ||
| <div class="list-group-item list-group-item-info flex-column align-items-start"> | ||
| {% endif %} | ||
| <div class="d-flex w-100 justify-content-between"> | ||
| <h4 class="mb-1">{{ project.name }}</h4> | ||
| </div> | ||
| {% if project.stop_date is None %} | ||
| <p class="mb-1">{% trans "started at" %}: {{project.start_date}}</p> | ||
| {% else %} | ||
| <p class="mb-1">{% trans "completed at" %}: {{project.stop_date}}</p> | ||
| {% endif %} | ||
| <small>{% trans "managers" %}: | ||
| {% for user in project.managers.all %} | ||
| {{user.first_name}} {{user.last_name}}<br> | ||
| {% endfor %}</small> | ||
| <small><span class="glyphicon glyphicon-user"></span> {{project.members.count}}</small> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| {% if forloop.counter|divisibleby:3 %} | ||
| </div> | ||
| <div class="row"> | ||
| {% endif %} | ||
| {% endfor %} | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
|
|
||
| </div> | ||
| {% endblock %} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| import datetime | ||
|
|
||
| from rest_framework.reverse import reverse | ||
| from rest_framework.test import APIRequestFactory | ||
|
|
||
| from django.test import TestCase | ||
|
|
||
| from managers import views | ||
| from managers.models import Project | ||
| from users.models import CustomUser | ||
|
|
||
|
|
||
| class ProjectTest(TestCase): | ||
| def setUp(self): | ||
| super().setUp() | ||
| self.user = CustomUser( | ||
MartynaAnnaGottschling marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| email='testuser@codepoets.it', | ||
| first_name='John', | ||
MartynaAnnaGottschling marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| last_name='Doe', | ||
| country='PL', | ||
| user_type=CustomUser.UserType.ADMIN.name, | ||
| ) | ||
| self.user.set_password('newuserpasswd') | ||
| self.user.full_clean() | ||
| self.user.save() | ||
|
|
||
| self.project = Project( | ||
| name='Example Project', | ||
| start_date=datetime.datetime.now().date() - datetime.timedelta(days=30), | ||
| stop_date=datetime.datetime.now().date(), | ||
| terminated=False, | ||
| ) | ||
| self.project.full_clean() | ||
| self.project.save() | ||
| self.project.managers.add(self.user) | ||
| self.project.members.add(self.user) | ||
| self.custom_projects_list_url = [ | ||
| reverse('custom-projects-list'), | ||
| ] | ||
|
|
||
|
|
||
| class ProjectsListTests(ProjectTest): | ||
| def test_project_list_view_should_display_projects_list_on_get(self): | ||
| request = APIRequestFactory().get(path=self.custom_projects_list_url[0]) | ||
| request.user = self.user | ||
| response = views.ProjectsList.as_view()(request) | ||
| self.assertEqual(response.status_code, 200) | ||
| self.assertContains(response, self.project.name) | ||
| projects_list = response.context_data['object_list'] | ||
| self.assertTrue(self.project in projects_list) | ||
|
|
||
| def test_projects_list_view_should_show_for_managers_only_own_projects(self): | ||
| self.user.user_type = CustomUser.UserType.MANAGER.name | ||
| manager_project_list = Project.objects.filter(managers__id=self.user.pk) | ||
| request = APIRequestFactory().get(path=self.custom_projects_list_url[0]) | ||
| request.user = self.user | ||
| response = views.ProjectsList.as_view()(request) | ||
| self.assertEqual(response.status_code, 200) | ||
| self.assertContains(response, self.project.name) | ||
| projects_list = response.context_data['object_list'] | ||
| self.assertEqual(list(manager_project_list), list(projects_list)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does it have to be converted to a list?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kbeker Yes, because without that the test returns |
||
|
|
||
| def test_project_list_view_should_display_projects_sorted_by_name_ascending(self): | ||
| request = APIRequestFactory().get(path=self.custom_projects_list_url[0] + '?sort=name') | ||
| request.user = self.user | ||
| response = views.ProjectsList.as_view()(request) | ||
| projects_list = response.context_data['object_list'] | ||
| self.assertTrue(projects_list.ordered) | ||
| self.assertTrue('name' in projects_list.query.order_by) | ||
|
|
||
| def test_project_list_view_should_display_projects_sorted_by_name_descending(self): | ||
| request = APIRequestFactory().get(path=self.custom_projects_list_url[0] + '?sort=-name') | ||
| request.user = self.user | ||
| response = views.ProjectsList.as_view()(request) | ||
| projects_list = response.context_data['object_list'] | ||
| self.assertTrue(projects_list.ordered) | ||
| self.assertTrue('-name' in projects_list.query.order_by) | ||
|
|
||
| def test_project_list_view_should_display_projects_sorted_by_start_date_ascending(self): | ||
| request = APIRequestFactory().get(path=self.custom_projects_list_url[0] + '?sort=start_date') | ||
| request.user = self.user | ||
| response = views.ProjectsList.as_view()(request) | ||
| projects_list = response.context_data['object_list'] | ||
| self.assertTrue(projects_list.ordered) | ||
| self.assertTrue('start_date' in projects_list.query.order_by) | ||
|
|
||
| def test_project_list_view_should_display_projects_sorted_by_start_date_descending(self): | ||
| request = APIRequestFactory().get(path=self.custom_projects_list_url[0] + '?sort=-start_date') | ||
| request.user = self.user | ||
| response = views.ProjectsList.as_view()(request) | ||
| projects_list = response.context_data['object_list'] | ||
| self.assertTrue(projects_list.ordered) | ||
| self.assertTrue('-start_date' in projects_list.query.order_by) | ||
|
|
||
| def test_project_list_view_should_display_projects_sorted_by_stop_date_ascending(self): | ||
| request = APIRequestFactory().get(path=self.custom_projects_list_url[0] + '?sort=stop_date') | ||
| request.user = self.user | ||
| response = views.ProjectsList.as_view()(request) | ||
| projects_list = response.context_data['object_list'] | ||
| self.assertTrue(projects_list.ordered) | ||
| self.assertTrue('stop_date' in projects_list.query.order_by) | ||
|
|
||
| def test_project_list_view_should_display_projects_sorted_by_stop_date_descending(self): | ||
| request = APIRequestFactory().get(path=self.custom_projects_list_url[0] + '?sort=-stop_date') | ||
| request.user = self.user | ||
| response = views.ProjectsList.as_view()(request) | ||
| projects_list = response.context_data['object_list'] | ||
| self.assertTrue(projects_list.ordered) | ||
| self.assertTrue('-stop_date' in projects_list.query.order_by) | ||
|
|
||
| def test_project_list_view_should_display_projects_sorted_by_members_count_ascending(self): | ||
| request = APIRequestFactory().get(path=self.custom_projects_list_url[0] + '?sort=members_count') | ||
| request.user = self.user | ||
| response = views.ProjectsList.as_view()(request) | ||
| projects_list = response.context_data['object_list'] | ||
| self.assertTrue(projects_list.ordered) | ||
| self.assertTrue('members_count' in projects_list.query.order_by) | ||
|
|
||
| def test_project_list_view_should_display_projects_sorted_by_members_count_descending(self): | ||
| request = APIRequestFactory().get(path=self.custom_projects_list_url[0] + '?sort=-members_count') | ||
| request.user = self.user | ||
| response = views.ProjectsList.as_view()(request) | ||
| projects_list = response.context_data['object_list'] | ||
| self.assertTrue(projects_list.ordered) | ||
| self.assertTrue('-members_count' in projects_list.query.order_by) | ||
Uh oh!
There was an error while loading. Please reload this page.