Skip to content
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
136 changes: 136 additions & 0 deletions scripts/generate-checklist-pdf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
"""
Generate the RecordSponge Expungement Checklist PDF.

Requirements: pip install reportlab

Output: src/frontend/public/docs/expungement-checklist.pdf

Run from the project root:
python scripts/generate-checklist-pdf.py
"""

import os
from reportlab.lib.pagesizes import letter
from reportlab.lib.units import inch
from reportlab.lib.colors import HexColor, black, white
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, HRFlowable, Flowable
)
from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
OUTPUT_PATH = os.path.join(SCRIPT_DIR, "..", "src", "frontend", "public", "docs", "expungement-checklist.pdf")


class CheckboxStep(Flowable):
"""A checkbox followed by step text, vertically aligned."""

def __init__(self, text, style, checkbox_size=11):
super().__init__()
self.text = text
self.style = style
self.checkbox_size = checkbox_size
self._para = Paragraph(text, style)

def wrap(self, availWidth, availHeight):
text_width = availWidth - self.checkbox_size - 10
self._para_w, self._para_h = self._para.wrap(text_width, availHeight)
self.width = availWidth
self.height = self._para_h + 4
return self.width, self.height

def draw(self):
para_y = self.height - self._para_h
self._para.drawOn(self.canv, self.checkbox_size + 10, para_y)

cb_y = self.height - self._para_h + (self._para_h - self.checkbox_size) / 2 - 1

self.canv.setStrokeColor(black)
self.canv.setFillColor(white)
self.canv.setLineWidth(0.8)
self.canv.rect(0, max(0, cb_y), self.checkbox_size, self.checkbox_size, fill=1, stroke=1)


def generate():
os.makedirs(os.path.dirname(OUTPUT_PATH), exist_ok=True)

doc = SimpleDocTemplate(
OUTPUT_PATH,
pagesize=letter,
topMargin=0.75 * inch,
bottomMargin=0.75 * inch,
leftMargin=0.75 * inch,
rightMargin=0.75 * inch,
)

styles = getSampleStyleSheet()
blue = HexColor("#357edd")
dark = HexColor("#333333")
gray = HexColor("#555555")

title_style = ParagraphStyle("ChecklistTitle", parent=styles["Title"], fontSize=20, textColor=dark, spaceAfter=6)
subtitle_style = ParagraphStyle("Subtitle", parent=styles["Normal"], fontSize=11, textColor=gray, spaceAfter=4)
step_style = ParagraphStyle("StepStyle", parent=styles["Normal"], fontSize=12, textColor=dark, fontName="Helvetica-Bold", spaceBefore=0, spaceAfter=4, leading=14)
sub_style = ParagraphStyle("SubStyle", parent=styles["Normal"], fontSize=11, textColor=gray, leftIndent=28, spaceBefore=2, spaceAfter=2)
note_style = ParagraphStyle("NoteStyle", parent=styles["Normal"], fontSize=11, textColor=gray, spaceBefore=0, spaceAfter=4, borderWidth=1, borderColor=HexColor("#cccccc"), borderPadding=8)

link_str = 'color="#357edd"'
story = []

story.append(Paragraph("RecordSponge Expungement Checklist", title_style))
story.append(Paragraph("A step-by-step guide to the expungement process", subtitle_style))
story.append(HRFlowable(width="100%", thickness=1, color=blue, spaceAfter=12))

steps = [
{
"title": "Log in to OECI",
"subs": [
"You will need an OECI account to search for criminal records.",
f'Purchase a subscription at <a href="https://www.courts.oregon.gov/services/online/Pages/ojcin-signup.aspx" {link_str}>courts.oregon.gov</a>.',
],
},
{
"title": "Search records",
"subs": [
"Ensure that Assumptions are met",
"Search by name and date of birth",
],
},
{
"title": "Complete paperwork for expungement",
"subs": [
"This includes paperwork to modify financial obligations if applicable",
],
},
{
"title": "Obtain fingerprints",
"subs": [
"Mail to Oregon State Police",
],
},
{
"title": "File paperwork in appropriate courts",
"subs": [],
},
]

for i, step in enumerate(steps, 1):
story.append(Spacer(1, 10))
story.append(CheckboxStep(f'<b>Step {i}:</b> {step["title"]}', step_style))
for sub in step["subs"]:
story.append(Paragraph(f"\u2022 {sub}", sub_style))

story.append(Spacer(1, 24))
story.append(Paragraph(
f'<b>Note:</b> If new to RecordSponge, confirm results with Michael Zhang at '
f'<a href="mailto:michael@qiu-qiulaw.com" {link_str}>michael@qiu-qiulaw.com</a>.<br/><br/>'
f'For further details, visit <a href="https://recordsponge.com/manual" {link_str}>recordsponge.com/manual</a>.',
note_style,
))

doc.build(story)
print(f"PDF generated: {os.path.abspath(OUTPUT_PATH)}")


if __name__ == "__main__":
generate()
Binary file not shown.
Binary file added src/frontend/public/img/aliases.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/frontend/public/img/expanded-view.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/frontend/public/img/full-results.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/frontend/public/img/search-form.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/frontend/public/img/search-results.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/frontend/public/img/simple-search.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/frontend/public/img/smart-search.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/frontend/public/img/wildcard-search.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/frontend/src/components/App/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe("App routing", () => {
["/", "winner"],
["/oeci", "ecourt"],
["/demo-record-search", "app demo"],
["/manual", "introduction"],
["/manual", "RecordSponge"],
["/rules", "type eligibility rules"],
["/faq", "myth"],
["/appendix", "forms to file"],
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/src/components/Appendix/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class Landing extends React.Component {
counties listed here, and will use the Stock Form for those
not listed. You can also fill out the forms manually if
preferred.{" "}
<Link to="/manual#file" className="link hover-dark-blue bb">
<Link to="/manual#generate-paperwork" className="link hover-dark-blue bb">
Learn more in the Manual
</Link>
.
Expand Down
4 changes: 2 additions & 2 deletions src/frontend/src/components/FillForms/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export default function FillFormsIndex() {
available, it will be provided in the form. If it is not present
in OECI, some of the information may or may not be required in the
application; please consult the{" "}
<Link to="/manual#file" className="link hover-dark-blue bb">
<Link to="/manual#generate-paperwork" className="link hover-dark-blue bb">
Manual
</Link>
.
Expand All @@ -162,7 +162,7 @@ export default function FillFormsIndex() {

<p className="mb3">
Please read the complete instructions in the{" "}
<Link to="/manual#file" className="link hover-dark-blue bb">
<Link to="/manual#file-paperwork" className="link hover-dark-blue bb">
Manual
</Link>{" "}
for filing the required forms for expungement. After downloading
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/src/components/Footer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Link } from "react-router-dom";
export default class Footer extends React.Component {
public render() {
return (
<footer className="footer-main mw8 center f6 f5-l bg-white pt5 pb6 ph4">
<footer className="footer-main mw8 center f6 f5-l bg-white pt5 pb5 ph4">
<div className="flex">
<ul className="list mr5 mr6-ns">
<li className="pb3">
Expand Down
95 changes: 41 additions & 54 deletions src/frontend/src/components/Manual/EditingGuide.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,31 @@
import React from "react";
import { HashLink as Link } from "react-router-hash-link";
import useDisclosure from "../../hooks/useDisclosure";
import AddButton from "../RecordSearch/Record/AddButton";
import EditButton from "../RecordSearch/Record/EditButton";
import EditedBadge from "../RecordSearch/Record/EditedBadge";
import { createNextBlankCharge } from "../RecordSearch/Record/Case";
import EditChargePanel from "../RecordSearch/Record/EditChargePanel";
import DisclosureIcon from "../common/DisclosureIcon";
import Accordion from "../common/Accordion";

export default function EditingGuide() {
const {
disclosureIsExpanded,
disclosureButtonProps,
disclosureContentProps,
} = useDisclosure();

return (
<div className="bg-gray-blue-2 shadow br3 pa3 mb3">
<h3 className="f3 fw7 mb2">Editing Results</h3>
<p className="mb2">
RecordSponge allows in-line editing of search results to correct any
errors or missing information. This is an advanced feature.
</p>
<button {...disclosureButtonProps}>
<span className="flex items-center fw6 mid-gray link hover-blue pb1">
Editing Guide
<DisclosureIcon
disclosureIsExpanded={disclosureIsExpanded}
className="pt1 pl1"
/>
</span>
</button>
<div {...disclosureContentProps} className="pt3">
<h3 className="f4 fw7 mb2">Why Edit</h3>
<section className="mb4">
<Accordion title="Editing Guide" id="editing">
<div className="mt1">
<p className="mb3">
RecordSponge allows in-line editing of search results to correct any
errors or missing information. This is an advanced feature.
</p>
<h3 className="f4 fw7">Why Edit</h3>
<section className="mb2">
<p className="mb3">
Sometimes the result of a Search doesn’t completely match a person’s
true record. This can happen for a few reasons:
</p>
<ol className="mh4">
<li className="mb3 pb1">
The search returns the wrong person’s record: In the rare event
that a different person’s Oregon record shares a birth date and
name with the person of interest, these records may appear in the
search result.
The search returns the wrong person's record: In rare cases,
another person with the same name and birth date may appear in
results.
</li>
<li className="mb3 pb1">
One or more of a person’s cases cannot be found: If the record has
Expand All @@ -65,26 +47,27 @@ export default function EditingGuide() {
If any of these issues appear in the record, they can be corrected
with the Editing feature. You can also use the Editing feature to
build a record from scratch and run an analysis without relying on
OECI at all. Editing is built directly in to the main Search
feature.
OECI at all. Editing is built directly into the main Search feature.
</p>
<h3 className="f4 fw7 mb2">Enable Editing</h3>
<h3 className="f4 fw7">Enable Editing</h3>
<p className="mb3">
Click the Enable Editing button on the Record Search page:
Click the "Enable Editing" button on the Record Search page:
</p>
<button className="inline-flex bg-white f6 fw5 br2 ba b--black-10 mid-gray link hover-blue pv1 ph2 mb3">
<span
className="inline-flex bg-white f6 fw5 br2 ba b--black-10 mid-gray pv1 ph2 mb3"
aria-hidden="true"
>
Enable Editing
</button>
</span>
<p className="mb3">
You can now perform the following actions to change the contents of
the record. These changes exist only temporarily in your session and
will disappear if you run another search. The edits will persist if
you navigate between Search and the Manual or other pages on
recordsponge.com, but they will disappear if you leave the website.
the record. Edits are temporary and persist while you navigate the
site, but will reset if you run a new search or leave
recordsponge.com.
</p>
<h3 className="f4 fw7 mb2">Add Case</h3>
<h3 className="f4 fw7">Add Case</h3>
<p className="mb3">
Click the Add Case button just below the Summary panel:
Click the "Add Case" button just below the Summary panel:
</p>
<div className="ma3">
<AddButton onClick={() => {}} actionName="Add" text="Case" />
Expand All @@ -95,10 +78,10 @@ export default function EditingGuide() {
Balance, and Birth Year, all of which are used to provide complete
analysis of the record.
</p>
<h3 className="f4 fw7 mb2">Edit Case</h3>
<h3 className="f4 fw7">Edit Case</h3>
<p className="mb3">
You can edit any of these fields on an existing or newly created
case by clicking the edit button in the case's header.
case by clicking the "Edit" button in the case's header.
</p>
<div className="ml3 mb3">
<EditButton onClick={() => {}} actionName="Edit Case" />
Expand All @@ -122,8 +105,8 @@ export default function EditingGuide() {
showEditButtons={true}
/>
</div>
<h3 className="f4 fw7 mb2">Add Charge</h3>
<p className="mb2">
<h3 className="f4 fw7">Add Charge</h3>
<p>
Next to the Edit Case{" "}
<EditButton onClick={() => {}} actionName="Edit Case" /> button is
another Add{" "}
Expand All @@ -138,13 +121,13 @@ export default function EditingGuide() {
button. This allows you to add charges to a newly-created or
existing case.
</p>
<p className="mb2">
For an existing charge, you can also the information by clicking the
Edit
<p>
For an existing charge, you can also edit the information by
clicking the Edit
<EditButton onClick={() => {}} actionName="Edit Case" />
button on that charge.
</p>
<p className="mb2">
<p>
Both of these operations open a similar panel to allow creating or
editing the charge:
</p>
Expand All @@ -164,15 +147,19 @@ export default function EditingGuide() {
disposition and a Charge Type. The charge type and disposition
jointly determine the type-eligibility of the charge. See our
complete list of{" "}
<Link className="bb hover-blue" to="/rules#chargetypes">
{" "}
Charge Types{" "}
<Link
className="bb hover-blue"
to="/rules#chargetypes"
target="_blank"
rel="noreferrer noopener"
>
Charge Types
</Link>{" "}
so that you can accurately select a charge type for each new or
edited charge.
</p>
</section>
</div>
</div>
</Accordion>
);
}
18 changes: 13 additions & 5 deletions src/frontend/src/components/Manual/Manual.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ it("renders", () => {
.create(
<BrowserRouter>
<Manual />
</BrowserRouter>
</BrowserRouter>,
)
.toJSON();
});
Expand All @@ -22,16 +22,24 @@ test("the editing guide can be opened and closed", async () => {
render(
<BrowserRouter>
<Manual />
</BrowserRouter>
</BrowserRouter>,
);

const part2 = screen.getByLabelText("Part 2: Search Client Records");
await user.click(part2);

const results = screen.getByLabelText("Results");
await user.click(results);

const summary = screen.getByLabelText("Editing Guide");

expect(screen.queryByText(/why edit/i)).not.toBeVisible();

await user.click(screen.getByRole("button"));
await user.click(summary);

expect(screen.queryByText(/why edit/i)).toBeVisible();
expect(screen.getByText(/why edit/i)).toBeVisible();

await user.click(screen.getByRole("button", { name: /editing guide/i }));
await user.click(summary);

expect(screen.queryByText(/why edit/i)).not.toBeVisible();
});
Loading