Skip to content

Commit

Permalink
Merge pull request #2050 from teovin/copyright-language
Browse files Browse the repository at this point in the history
add custom ali copyright language
  • Loading branch information
teovin authored May 22, 2024
2 parents 7a2b0f2 + 9ff0c5c commit 9749150
Show file tree
Hide file tree
Showing 7 changed files with 364 additions and 1 deletion.
8 changes: 8 additions & 0 deletions web/config/settings/settings_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import os
from typing import Any, TypedDict
import json

BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
ALLOWED_HOSTS: list[str] = []
Expand Down Expand Up @@ -299,3 +300,10 @@ class LoggerConfig(TypedDict, total=False):
SENTRY_SEND_DEFAULT_PII = False

COVER_IMAGES = False

# Load ali materials
try:
with open(f"{STATIC_ROOT}/data/ali_materials.json", "r") as file:
ALI_MATERIALS = json.load(file)
except FileNotFoundError as fileNotFoundError:
print("Error opening file.", fileNotFoundError)
23 changes: 23 additions & 0 deletions web/main/migrations/0046_auto_20240516_1322.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 3.2.25 on 2024-05-16 13:22

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('main', '0045_add_courtlistener_api_source'),
]

operations = [
migrations.AddField(
model_name='contentnode',
name='ali_licensed',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='historicalcontentnode',
name='ali_licensed',
field=models.BooleanField(default=False),
)
]
67 changes: 67 additions & 0 deletions web/main/migrations/0047_auto_20240516_1754.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Generated by Django 3.2.25 on 2024-05-16 17:54

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('main', '0046_auto_20240516_1322'),
]

operations = [
migrations.AlterModelOptions(
name='historicalcasebook',
options={'get_latest_by': ('history_date', 'history_id'), 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical casebook', 'verbose_name_plural': 'historical casebooks'},
),
migrations.AlterModelOptions(
name='historicalcontentannotation',
options={'get_latest_by': ('history_date', 'history_id'), 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical content annotation', 'verbose_name_plural': 'historical content annotations'},
),
migrations.AlterModelOptions(
name='historicalcontentnode',
options={'get_latest_by': ('history_date', 'history_id'), 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical content node', 'verbose_name_plural': 'historical content nodes'},
),
migrations.AlterModelOptions(
name='historicallegaldocument',
options={'get_latest_by': ('history_date', 'history_id'), 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical legal document', 'verbose_name_plural': 'historical legal documents'},
),
migrations.AlterModelOptions(
name='historicallink',
options={'get_latest_by': ('history_date', 'history_id'), 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical link', 'verbose_name_plural': 'historical links'},
),
migrations.AlterModelOptions(
name='historicaltextblock',
options={'get_latest_by': ('history_date', 'history_id'), 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical text block', 'verbose_name_plural': 'historical text blocks'},
),
migrations.AlterField(
model_name='historicalcasebook',
name='history_date',
field=models.DateTimeField(db_index=True),
),
migrations.AlterField(
model_name='historicalcontentannotation',
name='history_date',
field=models.DateTimeField(db_index=True),
),
migrations.AlterField(
model_name='historicalcontentnode',
name='history_date',
field=models.DateTimeField(db_index=True),
),
migrations.AlterField(
model_name='historicallegaldocument',
name='history_date',
field=models.DateTimeField(db_index=True),
),
migrations.AlterField(
model_name='historicallink',
name='history_date',
field=models.DateTimeField(db_index=True),
),
migrations.AlterField(
model_name='historicaltextblock',
name='history_date',
field=models.DateTimeField(db_index=True),
),
]
37 changes: 37 additions & 0 deletions web/main/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1503,6 +1503,9 @@ class ContentNode(
help_text="This content should only be made available on the front end to verified professors",
)

# Indicates whether node includes material licensed by the American Law Institute
ali_licensed = models.BooleanField(default=False)

@classmethod
def nodes_for_user_by_casebook(
cls,
Expand Down Expand Up @@ -1653,6 +1656,39 @@ def export_content(self, request):
return rich_text_export(self.resource.content, request=request, id_prefix=str(self.id))
return self.resource.content

def get_ali_license_text(self):
"""Returns custom license text if title contains all items of the match_words list
>>> node = getfixture('content_node')
>>> # title including both items in the form of single word strings
>>> node.title = 'Restatement (Third) of Property (Servitudes) Notes and Questions'
>>> assert node.get_ali_license_text() == 'Restatement of the Law, Property, copyright @ 1977-2023 by the American Law Institute. Reproduced with permission, not as part of a Creative Commons license.'
>>> # title including both items in the form of one single word string and one multi-word string
>>> node.title = 'Excerpts from Restatement (First) and (Second) of Conflict of Laws'
>>> assert node.get_ali_license_text() == 'Restatement of the Law, Conflict of Laws, copyright @ 1971-2023 by the American Law Institute. Reproduced with permission, not as part of a Creative Commons license.'
>>> # title including both items in the form of two multi-word strings in lower case
>>> node.title = 'section from model penal code sexual assault and related offenses publication'
>>> assert node.get_ali_license_text() == 'Model Penal Code, Sexual Assault and Related Offenses, copyright @ 2021-2022 by the American Law Institute. Reproduced with permission, not as part of a Creative Commons license.'
>>> # title that only includes one of the words
>>> node.title = 'section from the principles of law publications'
>>> assert node.get_ali_license_text() == ''
>>> # title that doesn't include any of the words
>>> node.title = 'Text that does not include any of the match words'
>>> assert node.get_ali_license_text() == ''
"""

title = self.title.lower()
license_txt = ""

for item in settings.ALI_MATERIALS:
if all(word in title for word in item["match_words"]):
license_txt = (
f"{item['title']}, copyright @ {item['years']} by the American Law Institute. "
f"Reproduced with permission, not as part of a Creative Commons license."
)
break
return license_txt

@property
def is_temporary(self):
return self.resource_type == "Temp"
Expand Down Expand Up @@ -1765,6 +1801,7 @@ def save(self, *args, **kwargs):
"""
cleanse_html_field(self, "headnote", True)
self.headnote_doc_class = self.identify_headnote_type()
self.ali_licensed = bool(self.get_ali_license_text())
super().save(*args, **kwargs)

def delete(self, *args, **kwargs):
Expand Down
5 changes: 4 additions & 1 deletion web/main/templates/includes/casebook_copyright_notice.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
for sharing and re-use with the exception of certain excerpts.


{# TODO make this dynamic in response to this casebook's content #}
<span class="ali-license">
{% if section.ali_licensed %}
{{ section.get_ali_license_text }}
{% else %}
Any excerpts from the <i>Restatements of the Law</i>, <i>Principles of the Law</i>, and the <i>Model Penal Code</i> are copyright by The American Law Institute. Excerpts are reproduced with permission, not as part of a Creative Commons license.
{% endif %}
</span>
</p>
210 changes: 210 additions & 0 deletions web/static/data/ali_materials.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
[
{
"title": "Restatement of the Law, Agency",
"years": "2006-2023",
"match_words": [
"restatement",
"agency"
]
},
{
"title": "Restatement of the Law, The Law of American Indians",
"years": "2022-2023",
"match_words": [
"restatement",
"the law of american indians"
]
},
{
"title": "Restatement of the Law, Charitable Nonprofit Organizations",
"years": "2021-2023",
"match_words": [
"restatement",
"charitable nonprofit organizations"
]
},
{
"title": "Restatement of the Law, Conflict of Laws",
"years": "1971-2023",
"match_words": [
"restatement",
"conflict of laws"
]
},
{
"title": "Restatement of the Law, Contracts",
"years": "1981-2023",
"match_words": [
"restatement",
"contracts"
]
},
{
"title": "Restatement of the Law, Employment Law",
"years": "2015-2023",
"match_words": [
"restatement",
"employment law"
]
},
{
"title": "Restatement of the Law, Foreign Relations Law of the United States",
"years": "1987-2023",
"match_words": [
"restatement",
"foreign relations law of the united states"
]
},
{
"title": "Restatement of the Law, Judgments",
"years": "1982-2023",
"match_words": [
"restatement",
"judgments"
]
},
{
"title": "Restatement of the Law, The Law Governing Lawyers",
"years": "2000-2023",
"match_words": [
"restatement",
"the law governing lawyers"
]
},
{
"title": "Restatement of the Law, Liability Insurance",
"years": "2019-2023",
"match_words": [
"restatement",
"liability insurance"
]
},
{
"title": "Restatement of the Law, Property",
"years": "1977-2023",
"match_words": [
"restatement",
"property"
]
},
{
"title": "Restatement of the Law, Restitution and Unjust Enrichment",
"years": "2011-2023",
"match_words": [
"restatement",
"restitution and unjust enrichment"
]
},
{
"title": "Restatement of the Law, Suretyship and Guaranty",
"years": "1996-2023",
"match_words": [
"restatement",
"suretyship and guaranty"
]
},
{
"title": "Restatement of the Law, Torts",
"years": "1965-2023",
"match_words": [
"restatement",
"torts"
]
},
{
"title": "Restatement of the Law, Trusts",
"years": "2003-2023",
"match_words": [
"restatement",
"trusts"
]
},
{
"title": "Restatement of the Law, Unfair Competition",
"years": "1995-2023",
"match_words": [
"restatement",
"unfair competition"
]
},
{
"title": "Restatement of the Law, The U.S. Law of International Commercial and Investor-State Arbitration",
"years": "2023",
"match_words": [
"restatement",
"the u.s. law of international commercial and investor-state arbitrationn"
]
},
{
"title": "Principles of the Law, Aggregate Litigation",
"years": "2010-2023",
"match_words": [
"principle",
"aggregate litigation"
]
},
{
"title": "Principles of the Law, Corporate Governance",
"years": "1994-2023",
"match_words": [
"principle",
"corporate governance"
]
},
{
"title": "Principles of the Law, Data Privacy",
"years": "2020",
"match_words": [
"principle",
"data privacy"
]
},
{
"title": "Principles of the Law, Election Administration: Non-Precinct Voting and Resolution of Ballot-Counting Disputes",
"years": "2019-2023",
"match_words": [
"principle",
"election administration: non-precinct voting and resolution of ballot-counting disputes"
]
},
{
"title": "Principles of the Law, Family Dissolution",
"years": "2002-2023",
"match_words": [
"principle",
"family dissolution"
]
},
{
"title": "Principles of the Law, Intellectual Property: Principles Governing Jurisdiction, Choice of Law, and Judgments in Transnational Disputes",
"years": "2008",
"match_words": [
"principle",
"intellectual property: principles governing jurisdiction, choice of law, and judgments in transnational disputes"
]
},
{
"title": "Principles of the Law, Software Contracts",
"years": "2010",
"match_words": [
"principle",
"software contracts"
]
},
{
"title": "Model Penal Code, Sentencing",
"years": "2023",
"match_words": [
"model penal code",
"sentencing"
]
},
{
"title": "Model Penal Code, Sexual Assault and Related Offenses",
"years": "2021-2022",
"match_words": [
"model penal code",
"sexual assault and related offenses"
]
}
]
Loading

0 comments on commit 9749150

Please sign in to comment.