Skip to content

Commit

Permalink
Simplify the CCDB landing page view and template (#6684)
Browse files Browse the repository at this point in the history
* Simplify the CCDB landing page view and template

The CCDB legacy landing view contained a routine for ensuring that
sample complaint narratives and data were available and up-to-date,
and it handled conditions when different banner messages would be displayed.

This can now be simplified.

Complaint narrative samples are no longer used on the landing page and
don't need to be checked. In addition, the check for stale data never worked,
because of a bug. The code was checking for a nonexistent [response.status](https://github.com/cfpb/consumerfinance.gov/blob/main/cfgov/legacy/views/complaint.py#L48) value
instead of `response.status_code`, and always returned blank json.

Data for the page's high-charts map and metadata for staleness were moved to S3,
where they are updated every morning. Those resources are called from the page.

All this allows us to stop making view calls to Elasticsearch entirely.
The view just needs to deliver the news when the CCDB_TECHNICAL_ISSUES flag is
activated, and that's all we need to test now.

Testing
- Pull in the branch and check that the CCDB landing page renders locally
at http://localhost:8000/data-research/consumer-complaints/
- in the local Wagtail admin, go to /admin/flags/ and enable the flag
` CCDB_TECHNICAL_ISSUES`
- The landing page should now show a warning dialog below the hero:

<img width="1223" alt="ccdb_warning" src="https://user-images.githubusercontent.com/515885/133856030-9ca0bbc1-b8fe-4ca5-a8d7-b51edd10fefa.png">

* remove unneeded classes and css

* remove space after class

* have test look for different class
  • Loading branch information
higs4281 authored Sep 21, 2021
1 parent e48a61b commit 4a8b346
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 209 deletions.
15 changes: 5 additions & 10 deletions cfgov/jinja2/v1/complaint/complaint-landing.html
Original file line number Diff line number Diff line change
Expand Up @@ -68,27 +68,22 @@ <h1 class="m-hero_heading">Consumer Complaint Database</h1>

{% block content_main %}

<div class="m-notification m-notification__warning {% if data_down %} m-notification__visible show-data-notification {% elif narratives_down %} m-notification__visible show-narratives-notification {% endif %}">
{% if technical_issues %}
<div class="m-notification m-notification__warning m-notification__visible">
{{ svg_icon("warning-round") }}
<div class="m-notification_content">
<div class="m-notification_content">
<div class="h4 m-notification_message">Warning!</div>
<p class="data-text m-notification_explanation">
<p class="m-notification_explanation">
We're currently experiencing technical issues that have delayed the refresh of data in the Consumer Complaint database.
</p>
<p class="narratives-text m-notification_explanation">
We're currently experiencing technical issues that have delayed the refresh of consumer complaint narratives in the Consumer Complaint database.
</p>
<p class="m-notification_explanation">{% if technical_issues %}
<p class="m-notification_explanation">
We expect a refresh of the data and restoration of access soon.
{% else %}
We expect to refresh the data in the next few days.
{% endif %}
</p>
</div>
</div>
</div>

{% endif %}
<div class="block block__padded-top o-well">
<h2 class="h3">
Things to know before you use the database
Expand Down
128 changes: 17 additions & 111 deletions cfgov/legacy/tests/views/test_complaint.py
Original file line number Diff line number Diff line change
@@ -1,116 +1,22 @@
from datetime import datetime, timedelta
from unittest.mock import patch

from django.test import RequestFactory, TestCase, override_settings

from complaint_search import views as ComplaintViews
from rest_framework.response import Response

from legacy.views.complaint import ComplaintLandingView
from django.test import TestCase, override_settings
from django.urls import reverse


class ComplaintLandingViewTests(TestCase):
test_url = 'https://test.url/foo.json'

def two_days_ago(self):
return (datetime.now() - timedelta(2)).strftime("%Y-%m-%d")

def setUp(self):
self.request = RequestFactory().get('/')

def assertNoBanner(self, response):
self.assertNotContains(response, 'show-')

@override_settings(COMPLAINT_LANDING_STATS_SOURCE=None)
def test_no_stats_source(self):
response = ComplaintLandingView.as_view()(self.request)
self.assertNoBanner(response)

@patch.object(ComplaintViews, 'search')
def test_elasticsearch_down(self, mock_view):
response = Response({})
response.status = 404
mock_view.return_value = response
response = ComplaintLandingView.as_view()(self.request)
self.assertTrue(mock_view.call_count == 1)
self.assertNoBanner(response)

@patch.object(ComplaintViews, 'search')
def test_data_up_to_date(self, mock_view):
data_json = {
'_meta': {
'last_indexed': self.two_days_ago(),
'last_updated': self.two_days_ago(),
},
}
response = Response(data_json)
mock_view.return_value = response
response = ComplaintLandingView.as_view()(self.request)
self.assertTrue(mock_view.call_count == 1)
self.assertNoBanner(response)

@patch.object(ComplaintViews, 'search')
def test_data_out_of_date(self, mock_view):
data_json = {
'_meta': {
'last_indexed': '2010-01-01',
'last_updated': self.two_days_ago(),
}
}
response = Response(data_json)
response.status = 200
mock_view.return_value = response
response = ComplaintLandingView.as_view()(self.request)
self.assertTrue(mock_view.call_count == 1)
self.assertContains(response, 'show-data-notification')

@patch.object(ComplaintViews, 'search')
def test_narratives_out_of_date(self, mock_view):
data_json = {
'_meta': {
'last_indexed': self.two_days_ago(),
'last_updated': '2010-01-01',
},
}
response = Response(data_json)
mock_view.return_value = response
response.status = 200
response = ComplaintLandingView.as_view()(self.request)
self.assertTrue(mock_view.call_count == 1)
self.assertContains(response, 'show-narratives-notification')

@patch.object(ComplaintViews, 'search')
def test_no_banner_when_data_invalid(self, mock_view):
data_json = {
'wrong_key': 5
}
response = Response(data_json)
mock_view.return_value = response
response = ComplaintLandingView.as_view()(self.request)
self.assertTrue(mock_view.call_count == 1)
self.assertNoBanner(response)

@patch.object(ComplaintViews, 'search')
def test_no_banner_when_data_not_json(self, mock_view):
data_json = "not json"
response = Response(data_json)
mock_view.return_value = response
response = ComplaintLandingView.as_view()(self.request)
self.assertTrue(mock_view.call_count == 1)
self.assertNoBanner(response)

@patch.object(ComplaintViews, 'search')
def test_no_banner_when_data_fetch_fails(self, mock_view):
mock_view.return_value = Response('bleh')
mock_view.side_effect = ValueError("test value error")
response = ComplaintLandingView.as_view()(self.request)
self.assertTrue(mock_view.call_count == 1)
self.assertNoBanner(response)

@patch.object(ComplaintViews, 'search')
def test_no_banner_when_key_error(self, mock_view):
mock_view.return_value = Response('bleh')
mock_view.side_effect = KeyError("test key error")
response = ComplaintLandingView.as_view()(self.request)
self.assertTrue(mock_view.call_count == 1)
self.assertNoBanner(response)
self.landing_url = reverse('complaint-landing')

@override_settings(
FLAGS={"CCDB_TECHNICAL_ISSUES": [("boolean", False)]})
def test_no_banner_when_flag_disabled(self):
response = self.client.get(self.landing_url)
self.assertEqual(response.status_code, 200)
self.assertNotContains(response, 'm-notification_explanation')

@override_settings(
FLAGS={"CCDB_TECHNICAL_ISSUES": [("boolean", True)]})
def test_banner_when_flag_enabled(self):
response = self.client.get(self.landing_url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'm-notification_explanation')
73 changes: 3 additions & 70 deletions cfgov/legacy/views/complaint.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
import logging
from datetime import datetime, timedelta

from django.views.generic import TemplateView

from complaint_search import views
from flags.state import flag_enabled
from rest_framework.test import APIRequestFactory


logger = logging.getLogger(__name__)


class ComplaintLandingView(TemplateView):
Expand All @@ -17,77 +9,18 @@ class ComplaintLandingView(TemplateView):
This view renders the template for the CCDB landing page.
That template includes a hardcoded value for percent of timely complaint
responses, which is currently 97%.
responses, which is currently 98%.
It also optionally pulls down a JSON file containing CCDB status, and uses
the contents of that file to display a warning banner if the data is out
of date, or if a feature flag has been set to indicate other problems.
It also displays a warning banner if a trouble feature flag has been set.
"""

template_name = 'complaint/complaint-landing.html'

def get_context_data(self, **kwargs):
context = super(ComplaintLandingView, self).get_context_data(**kwargs)

ccdb_status_json = self.get_ccdb_status_json()
context.update(self.is_ccdb_out_of_date(ccdb_status_json))

context.update({
'technical_issues': flag_enabled('CCDB_TECHNICAL_ISSUES'),
})

return context

def get_ccdb_status_json(self):
"""Retrieve JSON describing the CCDB's status from a given URL."""
try:
args = {'field': 'all', 'size': '1', 'no_aggs': 'true'}
factory = APIRequestFactory()
request = factory.get('/search/', args, format='json')
response = views.search(request)

if response.status == 200:
res_json = response.data
else:
logger.exception("Elasticsearch failed to return a valid " +
"response. Response data returned: {}"
.format(response.data))
res_json = {}
except ValueError:
logger.exception("CCDB status data not valid JSON.")
res_json = {}
except Exception:
logger.exception("CCDB status data fetch failed.")
res_json = {}

return res_json

def is_ccdb_out_of_date(self, res_json):
"""Parse JSON describing CCDB status to determine if it is out of date.
Returns a dict with two keys: data_down and narratives_down. Values
for both of these are booleans.
"""
data_down = flag_enabled('CCDB_TECHNICAL_ISSUES')
narratives_down = False
# show notification starting fifth business day data has not been
# updated M-Th, data needs to have been updated 6 days ago; F-S,
# preceding Monday
now = datetime.now()
weekday = datetime.weekday(now)
delta = weekday if weekday > 3 else 6
four_business_days_ago = (now -
timedelta(delta)).strftime("%Y-%m-%d")

try:
if res_json['_meta']['last_indexed'] < four_business_days_ago:
data_down = True
elif (res_json['_meta']['last_updated'] <
four_business_days_ago):
narratives_down = True
except (TypeError, KeyError):
logger.exception("CCDB JSON status not in expected format.")

return {
'data_down': data_down,
'narratives_down': narratives_down,
}
18 changes: 0 additions & 18 deletions cfgov/unprocessed/apps/ccdb-landing-map/css/landing-page.less
Original file line number Diff line number Diff line change
Expand Up @@ -60,24 +60,6 @@
text-transform: none;
}

.m-notification {

.data-text,
.narratives-text {
display: none;
}

&.show-data-notification,
&.show-narratives-notification {
display: block;
}

&.show-data-notification .data-text,
&.show-narratives-notification .narratives-text {
display: block;
}

}

.section-one {
.content-l_col {
Expand Down

0 comments on commit 4a8b346

Please sign in to comment.