Commit 7195b7ae authored by felix.herrmann's avatar felix.herrmann
Browse files

Merge branch '434-add-overview-page-for-each-category' into 'master'

Resolve "Add overview page for each category"

Closes #434

See merge request !408
parents d1766d06 4d6ebad3
......@@ -16,6 +16,7 @@ from discuss_data.core.views import (
login_page,
)
from discuss_data.ddusers.views import datasets_act_feed
from discuss_data.dddatasets.views.core import category_page, category_list
urlpatterns = [
......@@ -49,6 +50,8 @@ urlpatterns = [
path("security/", security_page, name="core.security_page"),
path("login/", login_page, name="core.login_page"),
path("dashboard/", datasets_act_feed, name="ddusers.dashboard_page"),
path("categories/", category_list, name="category_list"),
path("categories/<slug:slug>/", category_page, name="category_page"),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
# wagtail URL def
......
......@@ -79,71 +79,75 @@ class ProtectedView(LoginRequiredMixin, View):
redirect_field_name = getattr(settings, "LOGIN_REDIRECT_URL", None)
def core_search_view(request, search_index, objects_on_page):
query = request.GET.get("q")
def get_search_params(request, order_by_filter=None, category=None):
search_params = dict()
search_params["q"] = request.GET.get("q")
# extract country slugs from GET
countries = request.GET.getlist("countries")
# set countries/categories to None if only element in list is an empty string
if len(countries) == 1 and countries[0] == "":
countries = None
countries_all = Country.objects.all()
search_params["countries"] = request.GET.getlist("countries")
search_params["countries_all"] = Country.objects.all()
# extract category slugs from GET
categories = request.GET.getlist("categories")
if len(categories) == 1 and categories[0] == "":
categories = None
categories_all = Category.objects.all()
if category:
categories.append(category.name)
search_params["categories"] = categories
search_params["categories_all"] = Category.objects.all()
# extract keyword slugs from GET
keywords = request.GET.getlist("keywords")
if len(keywords) == 1 and keywords[0] == "":
keywords = None
keywords_all = KeywordTagged.objects.all()
search_params["keywords"] = request.GET.getlist("keywords")
search_params["keywords_all"] = KeywordTagged.objects.all()
# extract language slugs from GET
languages = request.GET.getlist("languages")
if len(languages) == 1 and languages[0] == "":
languages = None
languages_all = LanguageTagged.objects.all()
search_params["languages"] = request.GET.getlist("languages")
search_params["languages_all"] = LanguageTagged.objects.all()
# extract disciplines slugs from GET
disciplines = request.GET.getlist("disciplines")
if len(disciplines) == 1 and disciplines[0] == "":
disciplines = None
disciplines_all = DisciplinesTagged.objects.all()
search_params["disciplines"] = request.GET.getlist("disciplines")
search_params["disciplines_all"] = DisciplinesTagged.objects.all()
# extract methods_of_data_collection slugs from GET
methods_of_data_collection = request.GET.getlist("methods_of_data_collection")
if len(methods_of_data_collection) == 1 and methods_of_data_collection[0] == "":
methods_of_data_collection = None
methods_of_data_collection_all = CollectionMethodsTagged.objects.all()
search_params["methods_of_data_collection"] = request.GET.getlist(
"methods_of_data_collection"
)
search_params[
"methods_of_data_collection_all"
] = CollectionMethodsTagged.objects.all()
# extract methods_of_data_analysis slugs from GET
methods_of_data_analysis = request.GET.getlist("methods_of_data_analysis")
if len(methods_of_data_analysis) == 1 and methods_of_data_analysis[0] == "":
methods_of_data_analysis = None
methods_of_data_analysis_all = AnalysisMethodsTagged.objects.all()
search_params["methods_of_data_analysis"] = request.GET.getlist(
"methods_of_data_analysis"
)
search_params["methods_of_data_analysis_all"] = AnalysisMethodsTagged.objects.all()
try:
search_params["orderby"] = [order_by_filter]
except UnboundLocalError:
pass
return search_params
def core_search_view(request, search_index, objects_on_page, category=None):
# search result ordering
order_by_filter = request.GET.get("orderby")
if order_by_filter == "alpha":
order_by = "title"
else:
order_by = "-publication_date"
search_params = get_search_params(request, order_by_filter, category)
if search_index == "dataset_index":
template = "dddatasets/search_results.html"
queryset = index_search(
"dataset_index",
query,
countries=countries,
categories=categories,
keywords=keywords,
languages=languages,
methods_of_data_analysis=methods_of_data_analysis,
methods_of_data_collection=methods_of_data_collection,
disciplines=disciplines,
search_params["q"],
countries=search_params["countries"],
categories=search_params["categories"],
keywords=search_params["keywords"],
languages=search_params["languages"],
methods_of_data_analysis=search_params["methods_of_data_analysis"],
methods_of_data_collection=search_params["methods_of_data_collection"],
disciplines=search_params["disciplines"],
)
# search result ordering
order_by_filter = request.GET.get("orderby")
if order_by_filter == "alpha":
order_by = "title"
else:
order_by = "-publication_date"
# only the dataset with the highest published version per dsmo will be listed in search results
# generate SQL join using Django F() expressions
# https://docs.djangoproject.com/en/3.0/topics/db/queries/#filters-can-reference-fields-on-the-model
......@@ -152,19 +156,21 @@ def core_search_view(request, search_index, objects_on_page):
).order_by(order_by)
elif search_index == "user_index":
# default search index is user_index
template = "ddusers/_search_results.html"
queryset = (
index_search(
"user_index", query, countries=countries, categories=categories
"user_index",
search_params["q"],
countries=search_params["countries"],
keywords=search_params["keywords"],
)
.exclude(profile_accessibility="HID")
.exclude(profile_accessibility="HID") # exclude hidden users from search
.order_by("last_name")
)
elif search_index == "help_index":
template = "pages/index_page_search.html"
queryset = Page.objects.live().search(query)
queryset = Page.objects.live().search(search_params["q"])
else:
logger.debug("no search index given")
......@@ -198,20 +204,6 @@ def core_search_view(request, search_index, objects_on_page):
else:
objects = queryset
search_params = dict()
search_params["q"] = query
search_params["countries"] = countries
search_params["categories"] = categories
search_params["keywords"] = keywords
search_params["languages"] = languages
search_params["disciplines"] = disciplines
search_params["methods_of_data_analysis"] = methods_of_data_analysis
search_params["methods_of_data_collection"] = methods_of_data_collection
try:
search_params["orderby"] = [order_by_filter]
except UnboundLocalError:
pass
return render(
request,
template,
......@@ -221,22 +213,9 @@ def core_search_view(request, search_index, objects_on_page):
"paginator_range": paginator_range,
"paginator_last_page": paginator_last_page,
"search_params": search_params,
"countries": countries,
"countries_all": countries_all,
"categories": categories,
"categories_all": categories_all,
"keywords": keywords,
"keywords_all": keywords_all,
"languages": languages,
"languages_all": languages_all,
"methods_of_data_analysis": methods_of_data_analysis,
"methods_of_data_analysis_all": methods_of_data_analysis_all,
"methods_of_data_collection": methods_of_data_collection,
"methods_of_data_collection_all": methods_of_data_collection_all,
"disciplines": disciplines,
"disciplines_all": disciplines_all,
"query": query,
"query": search_params["q"],
"get_params": get_params_without_page,
"category": category,
},
)
......
# Generated by Django 2.2.17 on 2021-11-26 19:54
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('dddatasets', '0052_reset_created_at'),
]
operations = [
migrations.AlterModelOptions(
name='category',
options={'ordering': ['name']},
),
]
......@@ -77,6 +77,9 @@ class DataList(models.Model):
class Category(models.Model):
class Meta:
ordering = ["name"]
name = models.CharField(max_length=400)
slug = models.SlugField()
image = models.ImageField(blank=True, null=True)
......@@ -122,6 +125,9 @@ class Category(models.Model):
)
return datasets_categories | dataset_main_category
def get_published_datasets_category_all_count(self):
return self.get_published_datasets_category_all().count()
def __str__(self):
return self.name
......
......@@ -12,6 +12,7 @@ from discuss_data.core.decorators import dd_tou_accepted
from discuss_data.core.helpers import check_perms, is_curator_or_check_perms_for_ds
from discuss_data.core.views import core_search_view
from discuss_data.dddatasets.models import (
Category,
DataFile,
DataSet,
DataSetAccessRequest,
......@@ -46,6 +47,21 @@ def listing(request):
)
def category_list(request):
categories = Category.objects.all()
return render(
request,
"dddatasets/category_list.html",
{"categories": categories, "pagetitle": _("Categories")},
)
def category_page(request, slug):
logger.debug("category slug: {}".format(slug))
category = Category.objects.get(slug=slug)
return core_search_view(request, "dataset_index", 10, category=category)
def detail(request, uuid):
ds = check_published(uuid)
return render(request, "dddatasets/detail.html", {"ds": ds, "type": "description"})
......
......@@ -573,6 +573,7 @@ a:hover
.badge-RA {
background-color: theme-color("warning");
color: theme-color("black");
border: 1px solid theme-color("border-gray");
font-weight: 500;
}
......@@ -617,6 +618,10 @@ a:hover
height: 2.3rem;
}
.category-card .avatar {
height: 1.4rem;
}
.status {
padding-top: 1rem;
padding-bottom: 1.5rem;
......@@ -629,6 +634,10 @@ a:hover
margin-right: .2rem;
}
.dataset-buttons {
padding-left: .9rem;
}
/*
* No underlining in button links
*/
......@@ -775,7 +784,8 @@ $icons: (
people: '<g><rect fill="none" height="24" width="24"/></g><g><g/><g><g><path d="M16.67,13.13C18.04,14.06,19,15.32,19,17v3h4v-3 C23,14.82,19.43,13.53,16.67,13.13z" fill-rule="evenodd"/></g><g><circle cx="9" cy="8" fill-rule="evenodd" r="4"/></g><g><path d="M15,12c2.21,0,4-1.79,4-4c0-2.21-1.79-4-4-4c-0.47,0-0.91,0.1-1.33,0.24 C14.5,5.27,15,6.58,15,8s-0.5,2.73-1.33,3.76C14.09,11.9,14.53,12,15,12z" fill-rule="evenodd"/></g><g><path d="M9,13c-2.67,0-8,1.34-8,4v3h16v-3C17,14.34,11.67,13,9,13z" fill-rule="evenodd"/></g></g></g>',
search: '<path d="M0 0h24v24H0V0z" fill="none"/><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>',
shared-data: '<path d="M0 0h24v24H0V0z" fill="none"/><path d="M22 6H12l-2-2H2v16h20V6zm-7 3c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm4 8h-8v-1c0-1.33 2.67-2 4-2s4 .67 4 2v1z"/>',
);
categories: '<path d="M4 19H13L11 21H4C3.5 21 2.97 20.79 2.59 20.41C2.21 20.03 2 19.5 2 19V9H4V19M17.63 5.84C17.27 5.33 16.67 5 16 5H8C6.9 5 6 5.9 6 7V15C6 16.1 6.9 17 8 17H16C16.67 17 17.27 16.66 17.63 16.15L22 11L17.63 5.84Z" />'
);
/* SIDENAV ICONS */
......@@ -821,6 +831,8 @@ $sizes: (
height: $size;
width: $size;
$color: encodecolor(theme-color("primary"));
position: relative;
top: calc(1rem / 3);
background-repeat: no-repeat; /** disable repeating */
background-position: center; /** center the background image */
background-size: contain; /** optimal background size to fit in icon */
......
{% load static i18n %}
<div class="card smallskip category-card flex-fill">
<div class="card-body">
<div class="media">
<div class="media-body align-items-center">
<div class="row">
<div class="col-12">
<h2 class="mt-0"><a href="{% url "category_page" category.slug %}">{{ category.name }}</a></h2>
<div class="smallskip">
{% trans "Curated by" %}:
{% for user in category.curators.all %}
{% include 'ddusers/_user-image.html' with user_uuid=user.uuid user_photo=user.photo height=30 %}
<a href="{% url 'ddusers:detail' user.uuid %}">{{ user.get_academic_name }}</a>{% if not forloop.last %}, {% endif %}
{% endfor %}
</div>
<p>{{ category.description }}</p>
{% if category.sponsor %}
<p>{% trans 'Sponsor' %}: {{ category.sponsor }}</p>
{% endif %}
<p><a href="{% url "category_page" category.slug %}">{{ category.get_published_datasets_category_all_count }} datasets</a>
</div>
</div>
</div>
</div>
</div>
</div>
\ No newline at end of file
......@@ -4,25 +4,29 @@
<div class="media">
<div class="media-body row align-items-center">
<div class="col-12">
<h4 class="mt-0">
<a href="{% if prep %}{% url 'dddatasets:prep_edit' dataset.uuid %}{% else %}{% url 'dddatasets:detail' dataset.uuid %}{% endif %}">{{dataset.title}}</a>
<span class="status-badges">
{% if prep %}
{% if dataset.published %}
<span class="badge badge-success">{% trans 'Published' %}</span>
{% else %}
<span class="badge badge-private">{% trans "Not published" %}</span>
<div class="row smallskip">
<div class="col-8">
<h4 class="mt-0">
<a href="{% if prep %}{% url 'dddatasets:prep_edit' dataset.uuid %}{% else %}{% url 'dddatasets:detail' dataset.uuid %}{% endif %}">{{dataset.title}}</a>
</h4>
{% if dataset.subtitle %}<h5 class="media-heading">{{ dataset.subtitle }}</h5>{% endif %}
</div>
<div class="col-4 text-right">
<span class="status-badges">
{% if prep %}
{% if dataset.published %}
<span class="badge badge-success">{% trans 'Published' %}</span>
{% else %}
<span class="badge badge-private">{% trans "Not published" %}</span>
{% endif %}
{% endif %}
{% endif %}
<span class="badge badge-{{ dataset.data_access }}">{{ dataset.get_data_access_display }}</span>
{% if dataset.data_access == "RA" and dataset|user_has_restricted_access:request.user %}
<span class="badge badge-success">{% trans 'Access granted' %}</span>
{% endif %}
<br>
</span>
</h4>
{% if dataset.subtitle %}<h5 class="media-heading">{{ dataset.subtitle }}</h5>{% endif %}
<span class="badge badge-{{ dataset.data_access }}">{{ dataset.get_data_access_display }}</span>
{% if dataset.data_access == "RA" and dataset|user_has_restricted_access:request.user %}
<span class="badge badge-success">{% trans 'Access granted' %}</span>
{% endif %}
</span>
</div>
</div>
{% if userpage %}
<div class="smallskip">{{ dataset.publication_date }}</div>
......@@ -31,60 +35,62 @@
<div class="col-1">
{% include 'ddusers/_user-image.html' with user_uuid=dataset.owner.uuid user_photo=dataset.owner.photo %}
</div>
<div class="col-7">
<div class="col-8">
{{ dataset.owner.get_academic_name }}<br>
{{ dataset.publication_date }}
</div>
<div class="col-4">
<span class="i-label-md"></span> <b>{{ dataset.get_main_category }}</b>
<div class="col-3 text-right">
<span class="i-label-md"></span> <b><a href="{% url "category_page" dataset.get_main_category.slug %}">{{ dataset.get_main_category }}</a></b>
{% if dataset.get_categories %} <br>
<span class="i-labels-md"></span> {% for field in dataset.get_categories.all %}{{ field|title }}{% if not forloop.last %}, {% endif %}{% endfor %}
<span class="i-labels-md"></span> {% for field in dataset.get_categories.all %}<a href="{% url "category_page" field.slug %}">{{ field|title }}</a>{% if not forloop.last %}, {% endif %}{% endfor %}
{% endif %}
</div>
</div>
{% endif %}
<div class="row">
<a href="{% if prep %}{% url 'dddatasets:prep_edit' dataset.uuid %}#comment-block{% else %}{% url 'dddatasets:discuss' dataset.uuid %}{% endif %}"><button type="button" class="btn btn-outline-primary align-middle">{% trans 'Discuss' %}</button></a>
{% if prep %}
<a href="{% url 'dddatasets:prep_edit' dataset.uuid %}"><button type="button" class="btn btn-outline-primary align-middle">{% trans 'Edit' %}</button></a>
{% endif %}
{% if prep %}
{% if dataset.published %}
<a href="{% url 'dddatasets:detail' dataset.uuid %}"><button type="button" class="btn btn-outline-primary align-middle">{% trans 'View' %}</button></a>
<p class="dataset-buttons">
<a href="{% if prep %}{% url 'dddatasets:prep_edit' dataset.uuid %}#comment-block{% else %}{% url 'dddatasets:discuss' dataset.uuid %}{% endif %}"><button type="button" class="btn btn-outline-primary align-middle">{% trans 'Discuss' %}</button></a>
{% if prep %}
<a href="{% url 'dddatasets:prep_edit' dataset.uuid %}"><button type="button" class="btn btn-outline-primary align-middle">{% trans 'Edit' %}</button></a>
{% endif %}
{% if prep %}
{% if dataset.published %}
<a href="{% url 'dddatasets:detail' dataset.uuid %}"><button type="button" class="btn btn-outline-primary align-middle">{% trans 'View' %}</button></a>
{% endif %}
{% endif %}
{% endif %}
{% if dataset.published %}
<p>
<button class="btn btn-primary" type="button" data-toggle="collapse" data-target="#data-list-listing-{{dataset.uuid}}" aria-expanded="false" aria-controls="data-list-listing-{{dataset.uuid}}">
{% trans "Add to list" %}
</button>
</p>
{% if dataset.published %}
<p>
<button class="btn btn-primary" type="button" data-toggle="collapse" data-target="#data-list-listing-{{dataset.uuid}}" aria-expanded="false" aria-controls="data-list-listing-{{dataset.uuid}}">
{% trans "Add to list" %}
</button>
</p>
<div class="collapse" id="data-list-listing-{{dataset.uuid}}">
<div class="card card-body border-0">
<h5>{% trans 'Your Lists' %}</h5>
{% for data_list in request.user.get_lists_all %}
<form id="ds-{{dataset.uuid}}-to-{{data_list.uuid}}">
{% csrf_token %}
<input type="hidden" name="ds_uuid" value="{{dataset.uuid}}">
<input type="hidden" name="dl_uuid" value="{{data_list.uuid}}">
<input type="hidden" name="form_type" value="card">
<span id="ds-{{dataset.uuid}}-to-{{data_list.uuid}}-button">
{% if dataset in data_list.datasets.all %}
{% include 'ddusers/_datalist_button.html' with datalist=data_list dataset=dataset btn_mode="add" %}
{% else %}
{% include 'ddusers/_datalist_button.html' with datalist=data_list dataset=dataset btn_mode="delete" %}
{% endif %}
</span>
</form>
{% endfor %}
<div class="collapse" id="data-list-listing-{{dataset.uuid}}">
<div class="card card-body border-0">
<h5>{% trans 'Your Lists' %}</h5>
{% for data_list in request.user.get_lists_all %}
<form id="ds-{{dataset.uuid}}-to-{{data_list.uuid}}">
{% csrf_token %}
<input type="hidden" name="ds_uuid" value="{{dataset.uuid}}">
<input type="hidden" name="dl_uuid" value="{{data_list.uuid}}">
<input type="hidden" name="form_type" value="card">
<span id="ds-{{dataset.uuid}}-to-{{data_list.uuid}}-button">
{% if dataset in data_list.datasets.all %}
{% include 'ddusers/_datalist_button.html' with datalist=data_list dataset=dataset btn_mode="add" %}
{% else %}
{% include 'ddusers/_datalist_button.html' with datalist=data_list dataset=dataset btn_mode="delete" %}
{% endif %}
</span>
</form>
{% endfor %}
</div>
</div>
</div>
{% if not dataset.owner == request.user %}
{% include 'ddusers/_follow_button.html' with object=dataset follow_url='dddatasets:follow' follow_text='Follow' %}
{% if not dataset.owner == request.user %}
{% include 'ddusers/_follow_button.html' with object=dataset follow_url='dddatasets:follow' follow_text='Follow' %}
{% endif %}
{% endif %}
{% endif %}
</p>
</div>
</div>
<div class="col-4">
......
{% extends request.is_intercooler|yesno:"blank.html,base.html" %}
{% load static i18n core_tags %}
{% block title %}{{ pagetitle }}{% endblock %}
{% block content %}
{% block ic-content %}
<h1>{% trans "Dicuss Data Categories" %}</h1>
<div class="row">
{% for category in categories %}
<div class="col-6 d-flex align-items-stretch">{% include "dddatasets/_category_card.html" with category=category %}</div>
{% endfor %}
</div>
{% endblock ic-content %}
{% endblock content %}
{% load static i18n core_tags %}
<form class="search-data">
<h2>{% trans "Filters" %}</h2>
{# search_index #}
{% include 'core/_search_index.html' with filters=search_index filters_all=search_index_all filter_applied=filter_applied search_params=search_params %}
{# countries #}
{% include 'core/_search_filter.html' with filters=search_params.countries filters_all=search_params.countries_all filtertype="countries" search_params=search_params %}
{# keywords #}
{% include 'core/_search_filter.html' with filters=search_params.keywords filters_all=search_params.keywords_all filtertype="keywords" search_params=search_params %}
{% if filter_applied == 'dataset_index' %}
{# categories #}
{% include 'core/_search_filter.html' with filters=search_params.categories filters_all=search_params.categories_all filtertype="categories" search_params=search_params %}
{# languages #}
{% include 'core/_search_filter.html' with filters=search_params.languages filters_all=search_params.languages_all filtertype="languages" search_params=search_params %}
{# disciplines #}
{% include 'core/_search_filter.html' with filters=search_params.disciplines filters_all=search_params.disciplines_all filtertype="disciplines" search_params=search_params %}
{# methods_of_data_collection #}
{% include 'core/_search_filter.html' with filters=search_params.methods_of_data_collection filters_all=search_params.methods_of_data_collection_all filtertype="methods_of_data_collection" search_params=search_params %}
{# methods_of_data_analysis #}
{% include 'core/_search_filter.html' with filters=search_params.methods_of_data_analysis filters_all=search_params.methods_of_data_analysis_all filtertype="methods_of_data_analysis" search_params=search_params %}
{% endif %}
</form>
......@@ -8,7 +8,13 @@
<div id="search-list">
<div class="row">
<div class="col-md-9">
<h2>{% trans "Data Sets" %}</h2>
{% if category %}
{# this is a category page #}
{% include "dddatasets/_category_card.html" with category=category %}
{% else %}
<h2>{% trans "Data Sets" %}</h2>
{% endif %}
{# big search field #}
<div class="input-group mb-3 search-field-main">
<input class="form-control search-field-main search-middle search-data search-ui"{% if query %}value="{{ query }}"{% else %}placeholder="Search"{% endif %} name="q" aria-label="Search">
......@@ -17,6 +23,7 @@
</div>
</div>
{# search object list #}
{% if object_list %}
<div class="row smallskip">
......@@ -48,34 +55,9 @@
{% endif %}
</div>
{# filters #}
{# filters #}
<div class="col-md-3">
<form class="search-data">
<h2>{% trans "Filters" %}</h2>
{# search_index #}
{% include 'core/_search_index.html' with filters=search_index filters_all=search_index_all filter_applied="dataset_index" search_params=search_params %}
{# countries #}
{% include 'core/_search_filter.html' with filters=countries filters_all=countries_all filtertype="countries" search_params=search_params %}