Skip to content

Commit

Permalink
Merge pull request #77 from SADiLaR/feature/projects-page
Browse files Browse the repository at this point in the history
Add projects list page:
  • Loading branch information
OnaMosimege authored Jun 19, 2024
2 parents fc3e9e5 + 09545c9 commit b47e31a
Show file tree
Hide file tree
Showing 6 changed files with 434 additions and 2 deletions.
1 change: 1 addition & 0 deletions app/app/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
path("_health/", views.health, name="health"),
path("", views.home, name="home"),
path("institutions/", views.institutions, name="institutions"),
path("projects/", views.projects, name="projects"),
path("search/", views.search, name="search"),
path("i18n/", include("django.conf.urls.i18n")),
]
Expand Down
97 changes: 96 additions & 1 deletion app/app/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
from django.db.models import Count
from django.http import HttpResponse
from django.shortcuts import render
from django.utils.translation import gettext as _

from general.models import DocumentFile, Institution
from general.models import DocumentFile, Institution, Language, Project, Subject


def health(request):
Expand All @@ -23,6 +24,100 @@ def home(request):
return render(request, template_name=template, context=context)


def get_date_range(project):
start_date = project.start_date
end_date = project.end_date

if (start_date is not None) and (end_date is not None) and (start_date.year != end_date.year):
date = f"{start_date.year}{end_date.year}"
elif (start_date is not None) and (end_date is not None) and (start_date.year == end_date.year):
date = start_date.year
elif start_date is not None:
date = _("Since {year}").format(year=start_date.year)
elif end_date is not None:
date = _("Until {year}").format(year=end_date.year)
else:
date = None
return date


def get_logo(project):
if project.logo:
logo = project.logo
elif project.institution.logo:
logo = project.institution.logo
else:
logo = None
return logo


def projects(request):
template = "app/projects.html"

subject_id = request.GET.get("subject")
language_id = request.GET.get("language")
institution_id = request.GET.get("institution")

projects = (
Project.objects.select_related("institution")
.prefetch_related("subjects", "languages")
.all()
)

if subject_id:
projects = projects.filter(subjects__id=subject_id)
if language_id:
projects = projects.filter(languages__id=language_id)
if institution_id:
projects = projects.filter(institution__id=institution_id)

subjects = Subject.objects.all()
languages = Language.objects.all()
institutions = Institution.objects.all()

project_data = []
for project in projects:
project_subjects = project.subjects.all()
project_languages = project.languages.all()

if project_languages.count() < 4:
languages_data = ", ".join(sorted(language.name for language in project_languages))
else:
languages_data = _("Multilingual")

if project_subjects.count() < 4:
subjects_data = ", ".join(sorted([subject.name for subject in project_subjects]))
else:
subjects_data = _("Multiple subjects")

logo = get_logo(project)

institution_name = project.institution.name

date = get_date_range(project)

project_data.append(
{
"project": project,
"logo": logo,
"subjects": subjects_data,
"languages": languages_data,
"date": date,
"institution_name": institution_name,
}
)

context = {
"current_page": "projects",
"projects": project_data,
"subjects": subjects,
"languages": languages,
"institutions": institutions,
}

return render(request, template_name=template, context=context)


def institutions(request):
template = "app/institutions.html"
context = {}
Expand Down
107 changes: 107 additions & 0 deletions app/general/tests/test_projects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
from datetime import date

from django.test import Client, TestCase
from django.urls import reverse

from app.views import get_date_range
from general.models import Institution, Language, Project, Subject


class ProjectViewTests(TestCase):
def setUp(self):
self.client = Client()
self.subject1 = Subject.objects.create(name="Subject 1")
self.subject2 = Subject.objects.create(name="Subject 2")
self.language1 = Language.objects.create(name="Language 1", iso_code="lang1")
self.language2 = Language.objects.create(name="Language 2", iso_code="lang2")
self.language3 = Language.objects.create(name="Language 3", iso_code="lang3")
self.language4 = Language.objects.create(name="Language 4", iso_code="lang4")
self.institution = Institution.objects.create(
name="Institution", logo="institution_logo.png"
)

self.project1 = Project.objects.create(
name="Project 1",
start_date="2020-01-01",
end_date="2021-01-01",
institution=self.institution,
logo="project_logo.png",
)
self.project1.subjects.add(self.subject1)
self.project1.languages.add(self.language1)

self.project2 = Project.objects.create(
name="Project 2",
end_date="2021-01-01",
institution=self.institution,
logo="project_logo.png",
)
self.project2.subjects.add(self.subject1)
self.project2.languages.add(self.language1)
self.project2.languages.add(self.language2)
self.project2.languages.add(self.language3)
self.project2.languages.add(self.language4)

self.url = reverse("projects")

def test_projects_view(self):
response = self.client.get(reverse("projects"))
self.assertEqual(response.status_code, 200)
self.assertIn("projects", response.context)
self.assertEqual(len(response.context["projects"]), 2)
self.assertEqual(response.context["projects"][0]["project"].name, "Project 1")
self.assertEqual(response.context["projects"][1]["project"].name, "Project 2")

def test_projects_view_with_filters(self):
response = self.client.get(reverse("projects"), {"language": self.language3.id})
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.context["projects"]), 1)
self.assertEqual(response.context["projects"][0]["project"].name, "Project 2")

def test_projects_view_multilingual(self):
response = self.client.get(reverse("projects"))
self.assertEqual(response.status_code, 200)
projects = response.context["projects"]
self.assertEqual(projects[1]["languages"], "Multilingual")

def test_projects_view_queries(self):
response = self.client.get(self.url)

with self.assertNumQueries(6):
response = self.client.get(self.url)


class GetDateRangeTests(TestCase):
def setUp(self):
self.institution = Institution.objects.create(name="Institution")
self.project = Project.objects.create(name="Project", institution=self.institution)

def test_get_date_range_different_years(self):
self.project.start_date = date(2020, 1, 1)
self.project.end_date = date(2021, 1, 1)
self.project.save()
self.assertEqual(get_date_range(self.project), "2020 – 2021")

def test_get_date_range_same_year(self):
self.project.start_date = date(2020, 1, 1)
self.project.end_date = date(2020, 12, 31)
self.project.save()
self.assertEqual(get_date_range(self.project), 2020)

def test_get_date_range_only_start_date(self):
self.project.start_date = date(2020, 1, 1)
self.project.end_date = None
self.project.save()
self.assertEqual(get_date_range(self.project), "Since 2020")

def test_get_date_range_only_end_date(self):
self.project.start_date = None
self.project.end_date = date(2020, 12, 31)
self.project.save()
self.assertEqual(get_date_range(self.project), "Until 2020")

def test_get_date_range_no_dates(self):
self.project.start_date = None
self.project.end_date = None
self.project.save()
self.assertIsNone(get_date_range(self.project))
121 changes: 120 additions & 1 deletion app/static/css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,98 @@ html {
padding: 0 10px 10px 0;
}

/*Projects page*/
.filter-form {
display: flex;
margin: 30px;
font-size: 0.875rem;
}
.filter-form label {
margin-right: 10px;
}
.filter-form .form-group {
margin-right: 10px;
}
.filter-form .form-control {
border: 1px solid #000;
}
.filter-form select {
padding: 0 10px;
width: 230px;
}
.btn-apply, .btn-reset{
color: var(--primary-fg);
outline-color: var(--primary);
background-color: var(--primary);
border-color: var(--primary);
margin-right: 10px;
}
.btn-apply:hover, .btn-reset:hover{
color: var(--primary-fg);
outline-color: var(--primary);
background-color: var(--primary);
border-color: var(--primary);
}

.project-list {
margin: 20px;
padding: 0 30px 0 30px;
}

.card-text-project {
font-size: 0.875rem;
margin: 3px 0 3px 0;
}

.project-logo {
height: 100px;
width: auto;

}
.project-border {
margin: 0 0 5px 0;
overflow: hidden;
padding: 0 5px 0 0;
word-wrap: break-word;
}
.project-left-col {
width: 70%;
}
.project-right-col {
width: 30%;
flex: 0 0 auto;
display: flex;
justify-content: flex-end;
}
.project-header {
display: flex;
justify-content: space-between;
align-items: center;
max-width: 800px;
}
.project-body {
width: 100%;
max-width: 800px;

}
.card-text-description {
word-wrap: break-word;
overflow: hidden;
font-size: 0.875rem;
margin: 3px 0 3px 0;
}
.project-row {
display: flex;
justify-content: space-between;
width: 100%;
}
.project-text {
font-size: 1.25rem;
}
.icon-text {
font-size: 0.95rem;
}

/*Error pages*/
.error-card {
border-color: var(--primary-red);
Expand All @@ -190,6 +282,10 @@ html {
.bi {
font-size: 50px;
}
.project-icon {
font-size: 20px;
margin-right: 3px;
}

/* Extra small devices (phones, 600px and down) */
@media only screen and (max-width: 600px) {
Expand All @@ -213,13 +309,18 @@ html {
.col-sm-6 {
width: 50%;
}

/*Home page*/
.content-card {
font-size: smaller;
padding-right: 20px;
margin-right: 10px;
}

/*Projects page*/
.filter-form select {
padding: 0 10px;
width: 100px;
}
}

/*small devices (tablets, 600px and up) */
Expand Down Expand Up @@ -253,6 +354,12 @@ html {
.content-card {
font-size: smaller;
}

/*Projects page*/
.filter-form select {
padding: 0 10px;
width: 150px;
}
}

/*Medium screens (tablets, between 768px and 1001px)*/
Expand Down Expand Up @@ -304,6 +411,12 @@ html {
.right-col {
width: 30%;
}

/*Projects page*/
.filter-form select {
padding: 0 10px;
width: 200px;
}
}

/*Larger screens (desktops, 1001px and up)*/
Expand Down Expand Up @@ -356,4 +469,10 @@ html {
height: 200px;
font-size: small;
}

/*Projects page*/
.filter-form select {
padding: 0 10px;
width: 230px;
}
}
Loading

0 comments on commit b47e31a

Please sign in to comment.