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

Resolve base page #1679

Draft
wants to merge 22 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
7e663ea
logging.warn is deprecated
dragon-dxw Dec 12, 2024
8a1418b
Resolve base page
dragon-dxw Dec 10, 2024
abb6dbb
Fix XML link
dragon-dxw Dec 10, 2024
9c9075d
Handle case where 'format' is not set correctly
dragon-dxw Dec 10, 2024
45b0c3f
Sweep typing issues under the carpet for now
dragon-dxw Dec 10, 2024
202d75b
Resolve typing issues by making urls DocumentURIStrings
dragon-dxw Dec 10, 2024
a05176a
Resolve for real, not at /demo
dragon-dxw Dec 12, 2024
dd75f97
Revisit this -- Fix XML download filename -- not ideal
dragon-dxw Dec 12, 2024
f59ba97
Revisit this -- bad pdf filename replace
dragon-dxw Dec 12, 2024
6bc0b91
Revisit this -- half a disambiguation view
dragon-dxw Dec 13, 2024
1ad454a
Fix tests.py
dragon-dxw Dec 18, 2024
2ee85ba
Half fix a test
dragon-dxw Jan 3, 2025
8c011bb
warning: comment out the code which redirects non-canonical URLs, it …
dragon-dxw Jan 3, 2025
797f49c
fix a test
dragon-dxw Jan 3, 2025
6fa3315
Use a class fixture to provide Identifier Resolution to tests
dragon-dxw Jan 6, 2025
c9014f6
MockAPI working well
dragon-dxw Jan 6, 2025
9d46f73
Improve logging on failure
dragon-dxw Jan 6, 2025
3c1f98b
Fix uuid in factory being the uuid module, not a nice string
dragon-dxw Jan 6, 2025
7a47dd2
Refactor test_detail tests to use MockAPI
dragon-dxw Jan 7, 2025
1f23b9a
Actual fix: trailing slash is a 404 not a 500
dragon-dxw Jan 8, 2025
6791ea6
Fix TestPressSummaries via MockAPI
dragon-dxw Jan 8, 2025
93f0553
Make ViewRelatedDocumentButton not use socket, but not fix tests yet
dragon-dxw Jan 8, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ <h3>
</div>
<div class="judgment-download-options__download-option">
<h3>
<a href="{% formatted_document_uri document.uri 'xml' %}"
<a href="{% formatted_document_uri request.path 'xml' %}"
aria-label="Download {{ document|get_title_to_display_in_html }} as XML">
{% if document.document_noun == 'press summary' %}
Download this press summary as XML
Expand Down
16 changes: 16 additions & 0 deletions ds_judgements_public_ui/templates/judgment/disambiguation.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{% extends "layouts/base.html" %}
{% block title %}
Multiple matches - Find Case Law
{% endblock title %}
{% block content %}
<h1>Multiple matches</h1>
<p>There were multiple results for an identifier of {{ uri }}.</p>
<h3>Choices</h3>
(This should all be SQID)
<ul>
{% for resolution in resolutions %}
<li><a href="{{ resolution.identifier_uuid }}{{ file_format }}">{{ resolution.identifier_uuid }}</a></li>
{% endfor %}
</ul>
<p>{{ resolutions }}</p>
{% endblock content %}
49 changes: 49 additions & 0 deletions judgments/resolvers/document_resolver_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,59 @@
from django.http.request import HttpRequest
from django.views.generic import View

from judgments.utils.utils import api_client
from judgments.views.detail import best_pdf, detail_html, detail_xml, generated_pdf
from judgments.views.disambiguation import DisambiguationView
from judgments.views.press_summaries import press_summaries


class MarklogicInDocumentClothing(DocumentURIString):
def __new__(self, value):
return value.strip("/").replace(".xml", "")


class IdentifierResolverEngine(View):
# TODO: make this not just a copy-paste

def dispatch(
self,
request: HttpRequest,
document_uri: str,
file_format: Optional[str] = None,
component: Optional[str] = None,
):
fileformat_lookup = {
"data.pdf": best_pdf,
"generated.pdf": generated_pdf,
"data.xml": detail_xml,
"data.html": detail_html,
}
component_lookup = {
"press-summary": press_summaries,
}

if document_uri[-1] == "/":
raise Http404("Paths cannot end with a slash")
resolutions = api_client.resolve_from_identifier(document_uri)
if not resolutions:
msg = f"No resolutions for {document_uri}"
raise Http404(msg)
if len(resolutions) > 1:
return DisambiguationView.as_view()(
request, uri=document_uri, resolutions=resolutions, file_format=file_format
)

resolution_uri = MarklogicInDocumentClothing(resolutions[0].document_uri)

if file_format:
return fileformat_lookup[file_format](request, resolution_uri)

if component:
return component_lookup[component](request, resolution_uri)

return detail_html(request, resolution_uri)


class DocumentResolverEngine(View):
def dispatch(
self,
Expand Down
22 changes: 22 additions & 0 deletions judgments/tests/factories.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import uuid

from caselawclient.identifier_resolution import IdentifierResolution, IdentifierResolutions
from caselawclient.models.documents import DocumentURIString
from caselawclient.xquery_type_dicts import MarkLogicDocumentURIString
from factory.django import DjangoModelFactory

from judgments.models import CourtDates
Expand All @@ -10,3 +15,20 @@ class Meta:
param = "uksc"
start_year = 2001
end_year = 2024


class IdentifierResolutionsFactory:
@classmethod
def build(cls, identifier_uuid=None, uri="/ml_example_url.xml", slug="uksc/1979/999") -> IdentifierResolutions:
if not identifier_uuid:
identifier_uuid = str(uuid.uuid4())
return IdentifierResolutions(
[
IdentifierResolution(
identifier_uuid=f"id-{identifier_uuid}",
document_uri=MarkLogicDocumentURIString(uri),
identifier_slug=DocumentURIString(slug),
document_published=True,
)
]
)
58 changes: 45 additions & 13 deletions judgments/tests/test_detail.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from django.template.defaultfilters import filesizeformat
from django.test import Client, TestCase

from judgments.resolvers.document_resolver_engine import api_client
from judgments.tests.factories import IdentifierResolutionsFactory
from judgments.tests.utils.assertions import (
assert_contains_html,
assert_response_contains_text,
Expand All @@ -21,7 +23,23 @@
)


class TestWeasyWithoutCSS(TestCase):
def echo_resolution(url):
assert "ml-" not in url
return IdentifierResolutionsFactory.build(slug=url, uri=f"ml-{url}.xml")


class TestCaseWithMockAPI(TestCase):
@pytest.fixture(scope="class", autouse=True)
def setup(self):
with patch.object(
api_client,
"resolve_from_identifier",
side_effect=echo_resolution,
):
yield


class TestWeasyWithoutCSS(TestCaseWithMockAPI):
@patch.object(PdfDetailView, "pdf_stylesheets", [])
@patch("judgments.views.detail.generated_pdf.get_published_document_by_uri")
def test_weasy_without_css_runs_in_ci(self, mock_get_document_by_uri):
Expand All @@ -32,7 +50,7 @@ def test_weasy_without_css_runs_in_ci(self, mock_get_document_by_uri):
assert b"%PDF-1.7" in response.content


class TestJudgment(TestCase):
class TestJudgment(TestCaseWithMockAPI):
@patch("judgments.views.detail.detail_html.DocumentPdf")
@patch("judgments.views.detail.detail_html.get_published_document_by_uri")
def test_published_judgment_response(self, mock_get_document_by_uri, mock_pdf):
Expand Down Expand Up @@ -62,13 +80,13 @@ def test_query_passed_to_api_client(self, mock_get_document_by_uri, mock_pdf):
response = self.client.get("/test/2023/123?query=Query")

assert mock_get_document_by_uri.mock_calls[0] == call(
"test/2023/123", search_query="Query"
"ml-test/2023/123", search_query="Query"
) # We do make subsequent calls as part of getting related documents, but they're not relevant here

self.assertEqual(response.status_code, 200)


class TestJudgmentBackToSearchLink(TestCase):
class TestJudgmentBackToSearchLink(TestCaseWithMockAPI):
@patch("judgments.views.detail.detail_html.DocumentPdf")
@patch("judgments.views.detail.detail_html.get_published_document_by_uri")
def test_no_link_if_no_context(self, mock_get_document_by_uri, mock_pdf):
Expand All @@ -81,7 +99,7 @@ def test_no_link_if_no_context(self, mock_get_document_by_uri, mock_pdf):
assert "Back to search results" not in decoded_response


class TestJudgmentPdfLinkText(TestCase):
class TestJudgmentPdfLinkText(TestCaseWithMockAPI):
@patch("judgments.views.detail.detail_html.DocumentPdf")
@patch("judgments.views.detail.detail_html.get_published_document_by_uri")
@patch.dict(environ, {"ASSETS_CDN_BASE_URL": "https://example.com"})
Expand Down Expand Up @@ -116,15 +134,17 @@ def test_pdf_link_with_no_size(self, mock_get_document_by_uri, mock_pdf):
class TestDocumentDownloadOptions:
@patch("judgments.views.detail.detail_html.DocumentPdf")
@patch("judgments.views.detail.detail_html.get_published_document_by_uri")
@patch("judgments.resolvers.document_resolver_engine.api_client")
@pytest.mark.parametrize(
"uri,document_factory_class",
"doc_uri,document_factory_class",
[("eat/2023/1/press-summary/1", PressSummaryFactory), ("eat/2023/1", JudgmentFactory)],
)
def test_download_options(
self,
mock_api_client,
mock_get_document_by_uri,
mock_pdf,
uri,
doc_uri,
document_factory_class,
):
"""
Expand All @@ -135,6 +155,10 @@ def test_download_options(
AND this contains the Download XML button
AND the descriptions refer to the document's type
"""
uri = doc_uri
mock_api_client.resolve_from_identifier.return_value = IdentifierResolutionsFactory.build(
slug=uri, uri="ml.xml"
)
mock_get_document_by_uri.return_value = document_factory_class.build(uri=uri, is_published=True)
mock_pdf.return_value.size = 112
mock_pdf.return_value.uri = "http://example.com/test.pdf"
Expand Down Expand Up @@ -166,7 +190,7 @@ def test_download_options(
assert_contains_html(response, download_options_html)


class TestPressSummaryLabel(TestCase):
class TestPressSummaryLabel(TestCaseWithMockAPI):
@patch("judgments.views.detail.detail_html.DocumentPdf", autospec=True)
@patch("judgments.views.detail.detail_html.get_published_document_by_uri")
def test_label_when_press_summary(self, mock_get_document_by_uri, mock_pdf):
Expand Down Expand Up @@ -203,6 +227,7 @@ def test_no_press_summary_label_when_on_judgment(self, mock_get_document_by_uri,
class TestViewRelatedDocumentButton:
@patch("judgments.views.detail.detail_html.DocumentPdf", autospec=True)
@patch("judgments.views.detail.detail_html.get_published_document_by_uri")
@patch("judgments.resolvers.document_resolver_engine.api_client", side_effect=echo_resolution)
@pytest.mark.parametrize(
"uri,expected_text,expected_href,document_class_factory",
[
Expand All @@ -222,6 +247,7 @@ class TestViewRelatedDocumentButton:
)
def test_view_related_document_button_when_document_with_related_document(
self,
mock_api_client,
mock_get_document_by_uri,
mock_pdf,
uri,
Expand Down Expand Up @@ -250,6 +276,7 @@ def get_document_by_uri_side_effect(document_uri, cache_if_not_found=False, sear

@patch("judgments.views.detail.detail_html.DocumentPdf", autospec=True)
@patch("judgments.views.detail.detail_html.get_published_document_by_uri")
@patch("judgments.resolvers.document_resolver_engine.api_client", side_effect=echo_resolution)
@pytest.mark.parametrize(
"uri,expected_text,expected_href,document_class_factory",
[
Expand All @@ -269,6 +296,7 @@ def get_document_by_uri_side_effect(document_uri, cache_if_not_found=False, sear
)
def test_view_related_document_button_when_document_with_related_document_and_query_string(
self,
mock_api_client,
mock_get_document_by_uri,
mock_pdf,
uri,
Expand Down Expand Up @@ -296,6 +324,7 @@ def get_document_by_uri_side_effect(document_uri, cache_if_not_found=False, sear

@patch("judgments.views.detail.detail_html.DocumentPdf", autospec=True)
@patch("judgments.views.detail.detail_html.get_published_document_by_uri")
@patch("judgments.resolvers.document_resolver_engine.api_client", side_effect=echo_resolution)
@pytest.mark.parametrize(
"uri,unexpected_text,unexpected_href",
[
Expand All @@ -305,6 +334,7 @@ def get_document_by_uri_side_effect(document_uri, cache_if_not_found=False, sear
)
def test_no_view_related_document_button_when_document_without_related_document(
self,
mock_api_client,
mock_get_document_by_uri,
mock_pdf,
uri,
Expand Down Expand Up @@ -424,7 +454,7 @@ def get_document_by_uri_side_effect(document_uri, cache_if_not_found=False, sear
assert_response_contains_text(response, expected_breadcrumb, "//div[@class='breadcrumbs']")


class TestDocumentHeadings(TestCase):
class TestDocumentHeadings(TestCaseWithMockAPI):
@patch("judgments.views.detail.detail_html.DocumentPdf", autospec=True)
@patch("judgments.views.detail.detail_html.get_published_document_by_uri")
def test_document_headings_when_press_summary(self, mock_get_document_by_uri, mock_pdf):
Expand Down Expand Up @@ -456,7 +486,7 @@ def get_document_by_uri_side_effect(document_uri, cache_if_not_found=False, sear
neutral_citation="Judgment_A_NCN",
)
else:
raise DocumentNotFoundError()
raise DocumentNotFoundError(document_uri)

mock_get_document_by_uri.side_effect = get_document_by_uri_side_effect
response = self.client.get("/eat/2023/1/press-summary/1")
Expand All @@ -475,6 +505,7 @@ def test_document_headings_when_judgment(self, mock_get_document_by_uri, mock_pd
THEN the response should contain the heading HTML with the judgment name
AND a p tag subheading with the judgment's NCN
"""

mock_get_document_by_uri.return_value = JudgmentFactory.build(
uri=DocumentURIString("eat/2023/1"),
is_published=True,
Expand All @@ -489,7 +520,7 @@ def test_document_headings_when_judgment(self, mock_get_document_by_uri, mock_pd
assert_response_contains_text(response, "Judgment_A_NCN", reference_xpath_query)


class TestHTMLTitle(TestCase):
class TestHTMLTitle(TestCaseWithMockAPI):
@patch("judgments.views.detail.detail_html.DocumentPdf", autospec=True)
@patch("judgments.views.detail.detail_html.get_published_document_by_uri")
def test_html_title_when_press_summary(self, mock_get_document_by_uri, mock_pdf):
Expand All @@ -501,7 +532,7 @@ def test_html_title_when_press_summary(self, mock_get_document_by_uri, mock_pdf)
"""

def get_document_by_uri_side_effect(document_uri, cache_if_not_found=False, search_query: Optional[str] = None):
if document_uri == "eat/2023/1/press-summary/1":
if document_uri == "ml-eat/2023/1/press-summary/1":
return JudgmentFactory.build(
uri=DocumentURIString("eat/2023/1/press-summary/1"),
is_published=True,
Expand Down Expand Up @@ -539,13 +570,14 @@ def test_html_title_when_judgment(self, mock_get_document_by_uri, mock_pdf):
is_published=True,
body=DocumentBodyFactory.build(name="Judgment A"),
)

response = self.client.get("/eat/2023/1")
title = "Judgment A - Find Case Law - The National Archives"
xpath_query = "//title"
assert_response_contains_text(response, title, xpath_query)


class TestNoNeutralCitation(TestCase):
class TestNoNeutralCitation(TestCaseWithMockAPI):
@patch("judgments.views.detail.detail_html.get_published_document_by_uri")
def test_document_baseclass_raises_error(self, get_document):
doc = JudgmentFactory.build(
Expand Down
20 changes: 19 additions & 1 deletion judgments/tests/test_press_summaries.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
from unittest.mock import Mock, patch

import pytest
from caselawclient.factories import PressSummaryFactory
from django.test import TestCase
from django.urls import reverse

from judgments.resolvers.document_resolver_engine import api_client
from judgments.tests.factories import IdentifierResolutionsFactory
from judgments.utils import linked_doc_title, press_summary_list_breadcrumbs
from judgments.views.press_summaries import press_summaries


class TestPressSummaries(TestCase):
def echo_resolution(url):
return IdentifierResolutionsFactory.build(slug=url, uri=f"ml-{url}.xml")


class TestCaseWithMockAPI(TestCase):
@pytest.fixture(scope="class", autouse=True)
def setup(self):
with patch.object(
api_client,
"resolve_from_identifier",
side_effect=echo_resolution,
):
yield


class TestPressSummaries(TestCaseWithMockAPI):
@patch("judgments.views.press_summaries.TemplateResponse", autospec=True)
@patch("judgments.views.press_summaries.get_press_summaries_for_document_uri")
def test_view_returns_template_response_for_multiple_press_summaries(
Expand Down
Loading
Loading