Skip to content

Commit

Permalink
Merge branch 'main' into fix-front-end-case-name
Browse files Browse the repository at this point in the history
  • Loading branch information
flooie authored Dec 2, 2024
2 parents cb5fb64 + 3245860 commit 599659a
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 11 deletions.
2 changes: 1 addition & 1 deletion cl/assets/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ <h1>You did not supply the "private" variable to your template.
<header class="row">
<!-- Donate Banner -->
{% if FUNDRAISING_MODE %}
{% include 'includes/dismissible_nav_banner.html' with link="https://free.law/2024/01/18/new-recap-archive-search-is-live" text="A year in the making, today we are launching a huge new search engine for the RECAP Archive" emoji="&#127873;" cookie_name="no_banner"%}
{% include 'includes/dismissible_nav_banner.html' with link="https://donate.free.law/forms/givingtuesday" text="<strong>Today is GivingTuesday.</strong> Your support of Free Law Project helps make the justice system more transparent and accessible to all." cookie_name="giving_tuesday" button_text="Donate Today!"%}
{% endif %}

<!-- Broken Email Banner -->
Expand Down
24 changes: 14 additions & 10 deletions cl/assets/templates/includes/dismissible_nav_banner.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
available and takes up to four keyword arguments described below:

Parameters:
link: The URL for the "Learn More" button.
text: Text of the banner.
cookie_name: Name of the cookie used to remember if the user has already dismissed
the banner. This prevents them from seeing the same message repeatedly.
emoji: Insert an emoji next to your banner message using its decimal HTML entity
code (like &#128077;).
- text: Text of the banner.
- link: The URL for the button.
- cookie_name: Name of the cookie used to remember if the user has already
dismissed the banner. This prevents them from seeing the same message
repeatedly.
- button_text (optional): Text for the button. Defaults to "Learn More".
- button_emoji (optional): An Idiomatic Text element (<i></i>) to display
inside the button.
- emoji (optional): An HTML entity code (e.g., &#128077;) to insert an
emoji next to the banner message.

It's advisable to wrap this template within an if tag and use the parent element to add
extra conditions to handle the visibility of the banner. The current template only checks
Expand Down Expand Up @@ -36,14 +40,14 @@
</div>
</div>
<div class="row flex flex-column flex-sm-row align-items-center justify-content-between">
<div class="col-xs-12 col-sm-9 navbar-text lead">
<div class="col-xs-12 col-sm-10 navbar-text lead">
<p>{% if emoji %}{{emoji}}{% endif %} {{text}}</p>
</div>
<div class="col-xs-3 flex justify-content-center justify-content-sm-end">
<div class="col-xs-2 flex justify-content-center justify-content-sm-end">
<a href="{{link}}"
class="btn btn-primary btn-lg hidden-xs"><i class="fa fa-search"></i>&nbsp;Learn More</a>
class="btn btn-primary btn-lg hidden-xs">{% if button_emoji %}{{button_emoji}}{% endif %}&nbsp;{% if button_text %}{{button_text}}{% else %}Learn More{% endif %}</a>
<a href="{{link}}"
class="btn btn-primary btn-sm hidden-sm hidden-md hidden-lg"><i class="fa fa-search"></i>&nbsp;Learn More</a>
class="btn btn-primary btn-sm hidden-sm hidden-md hidden-lg">{% if button_emoji %}{{button_emoji}}{% endif %}&nbsp;{% if button_text %}{{button_text}}{% else %}Learn More{% endif %}</a>
</div>
</div>
</div>
Expand Down
37 changes: 37 additions & 0 deletions cl/scrapers/admin.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django.contrib import admin
from django.db import models

from cl.scrapers.models import (
PACERFreeDocumentLog,
Expand Down Expand Up @@ -29,3 +30,39 @@ class PACERFreeDocumentRowAdmin(admin.ModelAdmin):


admin.site.register(UrlHash)


class MVLatestOpinion(models.Model):
"""
Model linked to materialized view for monitoring scrapers
The SQL for creating the view is on it's migration file.
Must use `REFRESH MATERIALIZED VIEW scrapers_mv_latest_opinion`
periodically
"""

# a django model must have a primary key
court_id = models.TextField(primary_key=True)
latest_creation_date = models.DateTimeField()
time_since = models.TextField()
view_last_updated = models.DateTimeField()

class Meta:
managed = False
db_table = "scrapers_mv_latest_opinion"


@admin.register(MVLatestOpinion)
class MVLatestOpinionAdmin(admin.ModelAdmin):
"""Admin page to look at the latest opinion for each court
Use this to monitor silently failing scrapers
"""

list_display = [
"court_id",
"latest_creation_date",
"time_since",
"view_last_updated",
]
17 changes: 17 additions & 0 deletions cl/scrapers/management/commands/refresh_scrapers_status_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from django.db import connection

from cl.lib.command_utils import VerboseCommand, logger


class Command(VerboseCommand):
help = """Refreshes the `scrapers_mv_latest_opinion` materialized view.
Check the cl.scrapers.admin.py file for more info about the view
"""

def handle(self, *args, **options):
query = "REFRESH MATERIALIZED VIEW scrapers_mv_latest_opinion;"
with connection.cursor() as cursor:
cursor.execute(query)

logger.info("View refresh completed successfully")
69 changes: 69 additions & 0 deletions cl/scrapers/migrations/0004_create_mv_latest_opinion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Generated by Django 5.1.2 on 2024-11-25 15:27

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("scrapers", "0003_delete_errorlog"),
]

operations = [
migrations.CreateModel(
name="MVLatestOpinion",
fields=[
(
"court_id",
models.TextField(primary_key=True, serialize=False),
),
("latest_creation_date", models.DateTimeField()),
("time_since", models.TextField()),
("view_last_updated", models.DateTimeField()),
],
options={
"db_table": "scrapers_mv_latest_opinion",
"managed": False,
},
),
migrations.RunSQL("""
CREATE MATERIALIZED VIEW IF NOT EXISTS
scrapers_mv_latest_opinion
AS
(
SELECT
court_id,
max(so.date_created) as latest_creation_date,
DATE_TRUNC('minutes', (now() - max(so.date_created)))::text as time_since,
now() as view_last_updated
FROM
(
SELECT id, court_id
FROM search_docket
WHERE court_id IN (
SELECT id
FROM search_court
/*
Only check courts with scrapers in use
*/
WHERE
has_opinion_scraper
AND in_use
)
) sd
INNER JOIN
(SELECT id, docket_id FROM search_opinioncluster) soc ON soc.docket_id = sd.id
INNER JOIN
search_opinion so ON so.cluster_id = soc.id
GROUP BY
sd.court_id
HAVING
/*
Only return results for courts with no updates in a week
*/
now() - max(so.date_created) > interval '7 days'
ORDER BY
2 DESC
)
""")
]
49 changes: 49 additions & 0 deletions cl/scrapers/migrations/0004_create_mv_latest_opinion.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
BEGIN;
--
-- Create model MVLatestOpinion
--
-- (no-op)
--
-- Raw SQL operation
--

CREATE MATERIALIZED VIEW IF NOT EXISTS
scrapers_mv_latest_opinion
AS
(
SELECT
court_id,
max(so.date_created) as latest_creation_date,
DATE_TRUNC('minutes', (now() - max(so.date_created)))::text as time_since,
now() as view_last_updated
FROM
(
SELECT id, court_id
FROM search_docket
WHERE court_id IN (
SELECT id
FROM search_court
/*
Only check courts with scrapers in use
*/
WHERE
has_opinion_scraper
AND in_use
)
) sd
INNER JOIN
(SELECT id, docket_id FROM search_opinioncluster) soc ON soc.docket_id = sd.id
INNER JOIN
search_opinion so ON so.cluster_id = soc.id
GROUP BY
sd.court_id
HAVING
/*
Only return results for courts with no updates in a week
*/
now() - max(so.date_created) > interval '7 days'
ORDER BY
2 DESC
)
;
COMMIT;

0 comments on commit 599659a

Please sign in to comment.