Skip to content
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

Dp 3072 data product detail page #92

Merged
merged 15 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 41 additions & 3 deletions home/service/details.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,53 @@
from data_platform_catalogue.search_types import MultiSelectFilter, ResultType
from django.core.exceptions import ObjectDoesNotExist


from .base import GenericService


class DetailsService(GenericService):
LavMatt marked this conversation as resolved.
Show resolved Hide resolved
class DataProductDetailsService(GenericService):
def __init__(self, urn: str):
self.urn = urn
self.client = self._get_catalogue_client()

filter_value = [MultiSelectFilter("urn", [urn])]
search_results = self.client.search(
query="", page=None, filters=filter_value)
search_results = self.client.search(query="", page=None, filters=filter_value)
LavMatt marked this conversation as resolved.
Show resolved Hide resolved

if not search_results.page_results:
raise ObjectDoesNotExist(urn)

self.result = search_results.page_results[0]
self.assets_in_data_product = self._get_data_product_entities()
self.context = self._get_context()

def _get_data_product_entities(self):
# we might want to implement pagination for data product children
# details at some point
data_product_search = self.client.list_data_product_assets(
urn=self.urn, count=500
).page_results

assets_in_data_product = []
for result in data_product_search:
# name is like that as fully qualified name ({data_product}.{asset}) seems
# too verbose to display here.
assets_in_data_product.append(
{
"name": (
LavMatt marked this conversation as resolved.
Show resolved Hide resolved
result.name
if not result.name.split(".")[0] == self.result.name
else result.name.split(".")[1]
),
"urn": result.id,
"description": result.description,
"type": "TABLE",
}
)

assets_in_data_product = sorted(assets_in_data_product, key=lambda d: d["name"])

return assets_in_data_product

def _get_context(self):
context = {
"result": self.result,
Expand All @@ -27,7 +56,16 @@ def _get_context(self):
if self.result.result_type == ResultType.DATA_PRODUCT
else "Table"
),
"tables": self.assets_in_data_product,
"page_title": f"{self.result.name} - Data catalogue",
}

return context


class DatasetDetailsService(GenericService):
def __init__(self, urn: str):
self.context = self._get_context()

def _get_context(self):
return {}
7 changes: 5 additions & 2 deletions home/service/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,13 @@ def _highlight_results(self):
return highlighted_results

else:
pattern = f'({re.escape(query)})'
pattern = f"({re.escape(query)})"
for result in highlighted_results.page_results:
result.description = re.sub(
pattern, r'**\1**', result.description, flags=re.IGNORECASE,
pattern,
r"**\1**",
result.description,
flags=re.IGNORECASE,
)

return highlighted_results
Expand Down
6 changes: 5 additions & 1 deletion home/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
urlpatterns = [
path("", views.home_view, name="home"),
path("search", views.search_view, name="search"),
path("details/<str:id>/", views.details_view, name="details"),
path(
"details/<str:result_type>/<str:id>",
views.details_view,
name="details",
),
path("pagination/<str:page>", views.search_view, name="pagination"),
]
28 changes: 24 additions & 4 deletions home/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from django.shortcuts import render

from home.forms.search import SearchForm
from home.service.details import DetailsService
from home.service.details import DataProductDetailsService, DatasetDetailsService
from home.service.search import SearchService


Expand All @@ -12,15 +12,35 @@ def home_view(request):
return render(request, "home.html", context)


def details_view(request, id):
def details_view(request, result_type, id):
if result_type == "data_product":
context = data_product_details(request, id)
return render(request, "details_data_product.html", context)
if result_type == "table":
context = dataset_details(request, id)
return render(request, "details_dataset.html", context)


def data_product_details(request, id):
try:
service = DataProductDetailsService(id)
except ObjectDoesNotExist:
raise Http404("Asset does not exist")

context = service.context

return context


def dataset_details(request, id):
try:
service = DetailsService(id)
service = DatasetDetailsService(id)
except ObjectDoesNotExist:
raise Http404("Asset does not exist")

context = service.context

return render(request, "details.html", context)
return context


def search_view(request, page: str = "1"):
Expand Down
10 changes: 5 additions & 5 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ django = "^5.0.1"
pyyaml = "^6.0.1"
gunicorn = "^21.2.0"
whitenoise = "^6.6.0"
ministryofjustice-data-platform-catalogue = "^0.10.0"
ministryofjustice-data-platform-catalogue = "^0.15.0"
markdown = "^3.5.2"
python-dotenv = "^1.0.1"
faker = "^22.6.0"
Expand Down
38 changes: 19 additions & 19 deletions templates/details.html → templates/details_data_product.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<a onclick="history.back()" class="govuk-back-link">Back</a>
<main class="govuk-main-wrapper">
<div class="govuk-grid-row">
<span class="govuk-caption-m">{{result_type}}</span>
<span class="govuk-caption-m">Data product</span>
<h2 class="govuk-heading-l">{{result.name}}</h2>
<div class="govuk-grid-column-two-thirds">
<div class="govuk-summary-card__title-wrapper" >
Expand All @@ -33,7 +33,7 @@ <h2 class="govuk-heading-l">{{result.name}}</h2>
<div class="govuk-summary-list__row">
<dt class="govuk-summary-list__key">Domain name</dt>
<dd class="govuk-summary-list__value">
{{result.metadata.domain.properties.name}}
{{result.metadata.domain_name}}
</dd>
</div>

Expand All @@ -57,7 +57,7 @@ <h2 class="govuk-heading-l">{{result.name}}</h2>
<div class="govuk-summary-list__row">
<dt class="govuk-summary-list__key">Retention period</dt>
<dd class="govuk-summary-list__value">

{{result.metadata.retention_period_in_days|intcomma}}
</dd>
</div>
<div class="govuk-summary-list__row">
Expand All @@ -82,7 +82,7 @@ <h2 class="govuk-heading-l">{{result.name}}</h2>
</div>
<br/>
<table class="govuk-table">
<caption class="govuk-table__caption govuk-table__caption--m">Database content</caption>
<caption class="govuk-table__caption govuk-table__caption--m">Data product content</caption>
<thead class="govuk-table__head">
<tr class="govuk-table__row">
<th scope="col" class="govuk-table__header app-custom-class">Table name</th>
Expand All @@ -91,21 +91,21 @@ <h2 class="govuk-heading-l">{{result.name}}</h2>
</tr>
</thead>
<tbody class="govuk-table__body">
<tr class="govuk-table__row">
<th scope="row" class="govuk-table__header">Table 1</th>
<td class="govuk-table__cell">This is description about the table</td>
<td class="govuk-table__cell"> <a href="#" class="govuk-link">Schema details</a></td>
</tr>
<tr class="govuk-table__row">
<th scope="row" class="govuk-table__header">Table 2</th>
<td class="govuk-table__cell">This is description about the table</td>
<td class="govuk-table__cell"> <a href="#" class="govuk-link">Schema details</a></td>
</tr>
<tr class="govuk-table__row">
<th scope="row" class="govuk-table__header">Table 3</th>
<td class="govuk-table__cell">This is description about the table</td>
<td class="govuk-table__cell"> <a href="#" class="govuk-link">Schema details</a></td>
</tr>
{% for table in tables %}
{% with table_type=table.type|lower %}
<tr class="govuk-table__row">
<th scope="row" class="govuk-table__header">{{table.name}}</th>
<td class="govuk-table__cell">
{% if table.description|length > 200 %}
{{ table.description|slice:":200"|add:"..."|markdown }}
{% else %}
{{ table.description|markdown }}
{% endif %}
</td>
<td class="govuk-table__cell"> <a href="{% url 'home:details' result_type=table_type id=table.urn %}" class="govuk-link">Schema details</a></td>
</tr>
{% endwith %}
{% endfor %}
</tbody>
</table>
</div>
Expand Down
1 change: 1 addition & 0 deletions templates/details_dataset.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
TODO
8 changes: 5 additions & 3 deletions templates/partial/search_result.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
<div class="govuk-grid-row">
<div class="govuk-grid-column-full">
<h3 class="govuk-heading-m govuk-!-margin-bottom-2">
<a href="{% url 'home:details' id=result.id %}" class="govuk-link">{{result.name}}</a>
{% with result_type=result.result_type.name|lower %}
<a href="{% url 'home:details' result_type=result_type id=result.id %}" class="govuk-link">{{result.name}}</a>
{% endwith %}
{% if result.result_type.name == "DATA_PRODUCT" %}
<strong class="govuk-tag govuk-!-margin-left-2">
<strong class="govuk-tag govuk-!-margin-left-2" id="result-type">
Data product
</strong>
{% elif result.result_type.name == "TABLE" %}
<strong class="govuk-tag govuk-!-margin-left-2">
<strong class="govuk-tag govuk-!-margin-left-2" id="result-type">
Table
</strong>
{% endif %}
Expand Down
45 changes: 40 additions & 5 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@
from faker import Faker

from home.forms.search import SearchForm
from home.service.details import DetailsService
from home.service.details import DataProductDetailsService
from home.service.search import SearchService

from datahub.metadata.schema_classes import (
DataProductPropertiesClass,
DataProductAssociationClass,
)

fake = Faker()


Expand All @@ -29,7 +34,8 @@ def chromedriver_path(request):
return request.config.getoption("--chromedriver-path")


def generate_page(page_size=20):
def generate_page(page_size=20, result_type: ResultType = None):

"""
Generate a fake search page
"""
Expand All @@ -38,7 +44,11 @@ def generate_page(page_size=20):
results.append(
SearchResult(
id=fake.unique.name(),
result_type=choice((ResultType.DATA_PRODUCT, ResultType.TABLE)),
result_type=(
choice((ResultType.DATA_PRODUCT, ResultType.TABLE))
if result_type is None
else result_type
),
name=fake.name(),
description=fake.paragraph(),
metadata={"search_summary": "a"},
Expand Down Expand Up @@ -79,6 +89,11 @@ def mock_catalogue():
mock_catalogue, page_results=generate_page(), total_results=100
)
mock_search_facets_response(mock_catalogue, domains=generate_options())
mock_list_data_product_response(
mock_catalogue,
page_results=generate_page(page_size=1, result_type=ResultType.TABLE),
total_results=1,
)

yield mock_catalogue

Expand All @@ -96,6 +111,25 @@ def mock_search_facets_response(mock_catalogue, domains):
mock_catalogue.search_facets.return_value = SearchFacets({"domains": domains})


def mock_list_data_product_response(mock_catalogue, total_results, page_results=()):
search_response = SearchResponse(
total_results=total_results, page_results=page_results
)
mock_catalogue.list_data_product_assets.return_value = search_response


def mock_get_dataproduct_aspect(mock_catalogue):
data_product_association = DataProductAssociationClass(
destinationUrn="urn:li:dataset:(urn:li:dataPlatform:glue,test.test,PROD)",
sourceUrn="urn:li:dataProduct:test",
)

response = DataProductPropertiesClass(
name="test", assets=[data_product_association], description="test"
)
mock_catalogue.graph.get_aspect.return_value = response


@pytest.fixture
def valid_form():
valid_form = SearchForm(
Expand Down Expand Up @@ -123,10 +157,11 @@ def search_context(search_service):


@pytest.fixture
def detail_context(mock_catalogue):
def detail_dataproduct_context(mock_catalogue):
mock_catalogue.search.return_value = SearchResponse(
total_results=1, page_results=generate_page(page_size=1)
)
details_service = DetailsService(urn="urn:li:dataProduct:test")

details_service = DataProductDetailsService(urn="urn:li:dataProduct:test")
context = details_service._get_context()
return context
Loading
Loading