law_doc 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.rspec +3 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/README.org +82 -0
- data/Rakefile +6 -0
- data/bin/console +7 -0
- data/bin/setup +8 -0
- data/law_doc.gemspec +34 -0
- data/lib/blankable_date.rb +239 -0
- data/lib/law_doc/address.rb +49 -0
- data/lib/law_doc/case.rb +318 -0
- data/lib/law_doc/core_ext/array.rb +51 -0
- data/lib/law_doc/core_ext/nil.rb +9 -0
- data/lib/law_doc/core_ext/numeric.rb +34 -0
- data/lib/law_doc/core_ext/string.rb +3 -0
- data/lib/law_doc/core_ext.rb +4 -0
- data/lib/law_doc/court.rb +48 -0
- data/lib/law_doc/document.rb +452 -0
- data/lib/law_doc/error.rb +2 -0
- data/lib/law_doc/init_types.rb +182 -0
- data/lib/law_doc/judge.rb +28 -0
- data/lib/law_doc/lawyer.rb +111 -0
- data/lib/law_doc/party.rb +40 -0
- data/lib/law_doc/person.rb +291 -0
- data/lib/law_doc/phone.rb +62 -0
- data/lib/law_doc/version.rb +3 -0
- data/lib/law_doc.rb +73 -0
- metadata +173 -0
data/lib/law_doc/case.rb
ADDED
@@ -0,0 +1,318 @@
|
|
1
|
+
module LawDoc
|
2
|
+
class Case
|
3
|
+
STYLES = %i[versus inre].freeze
|
4
|
+
|
5
|
+
attr_reader :number, :complaint_date, :style, :court,
|
6
|
+
:judge, :magistrate, :parties
|
7
|
+
|
8
|
+
include InitTypes
|
9
|
+
|
10
|
+
def initialize(number: nil,
|
11
|
+
complaint_date: nil,
|
12
|
+
style: :versus,
|
13
|
+
court: nil,
|
14
|
+
judge: nil,
|
15
|
+
magistrate: nil,
|
16
|
+
parties: [])
|
17
|
+
@number = number.to_s
|
18
|
+
@complaint_date = init_date(complaint_date, field: 'complaint_date')
|
19
|
+
@court = init_court(court)
|
20
|
+
@judge = init_judge(judge)
|
21
|
+
@magistrate = init_judge(magistrate, field: 'magistrate') if magistrate
|
22
|
+
if style
|
23
|
+
@style = style.to_s.to_sym
|
24
|
+
raise "invalid Case style: '#{@style}'" unless STYLES.include?(@style)
|
25
|
+
end
|
26
|
+
@parties = init_parties(parties)
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
number
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_h
|
34
|
+
{ number: number, complaint_date: complaint_date,
|
35
|
+
style: style, court: court.to_h, judge: judge.to_h,
|
36
|
+
magistrate: magistrate.to_h,
|
37
|
+
parties: parties }
|
38
|
+
end
|
39
|
+
|
40
|
+
include Comparable
|
41
|
+
|
42
|
+
def <=>(other)
|
43
|
+
to_h <=> other.to_h
|
44
|
+
end
|
45
|
+
|
46
|
+
# Return an array of parties that are plaintiffs. Any party whose role
|
47
|
+
# contains 'plaintiff' counts.
|
48
|
+
def plaintiffs
|
49
|
+
result = parties_of_role('plaintiff')
|
50
|
+
parties_matching_role(/plaintiff/i).each do |pty|
|
51
|
+
result << pty unless result.include?(pty)
|
52
|
+
end
|
53
|
+
result
|
54
|
+
end
|
55
|
+
|
56
|
+
# Return the first party that is a plaintiff.
|
57
|
+
def plaintiff
|
58
|
+
plaintiffs.first
|
59
|
+
end
|
60
|
+
|
61
|
+
# Return an array of parties that are defendants. Any party whose role
|
62
|
+
# contains 'defendant' counts.
|
63
|
+
def defendants
|
64
|
+
result = parties_of_role('defendant')
|
65
|
+
parties_matching_role(/defendant/i).each do |pty|
|
66
|
+
result << pty unless result.include?(pty)
|
67
|
+
end
|
68
|
+
result
|
69
|
+
end
|
70
|
+
|
71
|
+
# Return the first party that is a defendant.
|
72
|
+
def defendant
|
73
|
+
defendants.first
|
74
|
+
end
|
75
|
+
|
76
|
+
# Return an array of parties that are neither plaintiffs nor defendants.
|
77
|
+
def other_parties
|
78
|
+
result = Set.new(parties)
|
79
|
+
result -= plaintiffs
|
80
|
+
result -= defendants
|
81
|
+
result.to_a
|
82
|
+
end
|
83
|
+
|
84
|
+
def parties_of_lawyer(lwy)
|
85
|
+
parties.select { |pty| pty.lawyers.include?(lwy) }
|
86
|
+
end
|
87
|
+
|
88
|
+
def lawyer_parties_names(lwy)
|
89
|
+
parties_of_lawyer(lwy).map(&:name).join_and
|
90
|
+
end
|
91
|
+
|
92
|
+
def lawyer_roles(lwy)
|
93
|
+
ptys = parties_of_lawyer(lwy)
|
94
|
+
role = ptys.first.role
|
95
|
+
if ptys.size == 1
|
96
|
+
role.singularize
|
97
|
+
else
|
98
|
+
role.pluralize
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Return an array of the parties for this case whose role attribute exactly
|
103
|
+
# equals the parameter role.
|
104
|
+
def parties_of_role(role)
|
105
|
+
role = role.to_s.singularize.downcase
|
106
|
+
parties.select { |p| p.role.downcase == role }
|
107
|
+
end
|
108
|
+
|
109
|
+
# Return an array of the parties for this case whose role attribute does
|
110
|
+
# /not/ equal the parameter role.
|
111
|
+
def parties_not_role(role)
|
112
|
+
role = role.to_s.singularize.downcase
|
113
|
+
parties.reject { |p| p.role.downcase == role }
|
114
|
+
end
|
115
|
+
|
116
|
+
# Return an array of the parties for this case whose role matches the regexp
|
117
|
+
# given by role, either as a literal Regexp or as a String converted to a
|
118
|
+
# Regexp. In the latter case, unless the string starts and ends with '/'
|
119
|
+
# any Regexp meta-characters in the string are quoted. Matches are done
|
120
|
+
# case insensitively against each party's 'role' attribute. Since each
|
121
|
+
# party's role will be expressed in the singular form, we singularize before
|
122
|
+
# matching against individual parties. Thus, 'plaintiffs' will match all
|
123
|
+
# parties with role 'Plaintiff'.
|
124
|
+
def parties_matching_role(role)
|
125
|
+
match_role =
|
126
|
+
case role
|
127
|
+
when Regexp
|
128
|
+
role
|
129
|
+
when String
|
130
|
+
if role =~ %r{\A/([^/]*)/\z}
|
131
|
+
Regexp.new($1.singularize)
|
132
|
+
else
|
133
|
+
Regexp.new(Regexp.quote(role.singularize), true)
|
134
|
+
end
|
135
|
+
else
|
136
|
+
raise ArgumentError, 'role parameter must be Regexp or String'
|
137
|
+
end
|
138
|
+
parties.select { |p| p.role.downcase =~ match_role }
|
139
|
+
end
|
140
|
+
|
141
|
+
# Return an array of the parties for this case whose role does /not/ match
|
142
|
+
# the regexp given by role, either as a literal Regexp or as a String
|
143
|
+
# converted to a Regexp. In the latter case, unless the string starts and
|
144
|
+
# ends with '/' any Regexp meta-characters in the string are quoted. Matches
|
145
|
+
# are done case insensitively against each party's 'role' attribute.
|
146
|
+
def parties_not_matching_role(role)
|
147
|
+
role =
|
148
|
+
case role
|
149
|
+
when Regexp
|
150
|
+
role
|
151
|
+
when String
|
152
|
+
role =
|
153
|
+
if role =~ %r{\A/([^/]*)/\z}
|
154
|
+
Regexp.new($1)
|
155
|
+
else
|
156
|
+
Regexp.new(Regexp.quote(role), true)
|
157
|
+
end
|
158
|
+
else
|
159
|
+
raise ArgumentError, 'role parameter must be Regexp or String'
|
160
|
+
end
|
161
|
+
parties.reject { |p| p.role.downcase =~ role }
|
162
|
+
end
|
163
|
+
|
164
|
+
# Return an array of all roles for parties in this case.
|
165
|
+
def roles
|
166
|
+
roles = Set.new
|
167
|
+
parties.each do |pty|
|
168
|
+
roles << pty.role
|
169
|
+
end
|
170
|
+
roles.to_a
|
171
|
+
end
|
172
|
+
|
173
|
+
# Return a string with the roles of all parties ptys in this kase joined
|
174
|
+
# with and.
|
175
|
+
def party_roles(ptys)
|
176
|
+
# Sort roles by number of parties in each role
|
177
|
+
roles = ptys.group_by(&:role).transform_values(&:count).sort
|
178
|
+
# Pluralize by count
|
179
|
+
role_names = []
|
180
|
+
roles.each do |role, count|
|
181
|
+
role_names << role.pluralize(count)
|
182
|
+
end
|
183
|
+
role_names.join_and
|
184
|
+
end
|
185
|
+
|
186
|
+
def tex_lawdoc_caption_inre
|
187
|
+
pps = parties_of_role(roles.first)
|
188
|
+
body = ''
|
189
|
+
unless pps.empty?
|
190
|
+
body = '\\textbf{In the Matter of:}\\\\[12pt]'
|
191
|
+
pps.each_with_flags do |pty, first, _last|
|
192
|
+
body += "\\versusand\n" unless first
|
193
|
+
body += "\\aparty{#{pty.name.tq},}\\\\\n"
|
194
|
+
end
|
195
|
+
body += "\\role{#{roles.first.tq + (pps.size > 1 ? 's' : '')}}"
|
196
|
+
end
|
197
|
+
# Same body for both styles
|
198
|
+
<<~CAPTION
|
199
|
+
\\LeftFullCaptionBlock{%\n
|
200
|
+
#{body}
|
201
|
+
}\n\n
|
202
|
+
\\LeftCaptionBlock}{%\n
|
203
|
+
#{body}
|
204
|
+
}\n\n
|
205
|
+
CAPTION
|
206
|
+
end
|
207
|
+
|
208
|
+
# Compute FullLeftCaptionBlock
|
209
|
+
def tex_lawdoc_full_caption_versus
|
210
|
+
result = "\\LeftFullCaptionBlock{%\n"
|
211
|
+
unless plaintiffs.empty?
|
212
|
+
pgroups = plaintiffs.group_by(&:role)
|
213
|
+
first_group = true
|
214
|
+
pgroups.each_value do |ps|
|
215
|
+
if first_group
|
216
|
+
first_group = false
|
217
|
+
else
|
218
|
+
result += " \\versusand\n"
|
219
|
+
end
|
220
|
+
ps.each do |p|
|
221
|
+
result += " \\aparty{#{p.name.tq}}\\\\\n"
|
222
|
+
end
|
223
|
+
role_str = ps.length > 1 ? ps.first.role.pluralize : ps.first.role
|
224
|
+
result += " \\role{#{role_str.tq}}\n"
|
225
|
+
end
|
226
|
+
result += " \\versus\n"
|
227
|
+
end
|
228
|
+
unless defendants.empty?
|
229
|
+
dgroups = defendants.group_by(&:role)
|
230
|
+
first_group = true
|
231
|
+
dgroups.each_value do |ds|
|
232
|
+
if first_group
|
233
|
+
first_group = false
|
234
|
+
else
|
235
|
+
result += " \\versusand\n"
|
236
|
+
end
|
237
|
+
ds.each do |d|
|
238
|
+
result += " \\aparty{#{d.name.tq}}\\\\\n"
|
239
|
+
end
|
240
|
+
role_str = ds.length > 1 ? ds.first.role.pluralize : ds.first.role
|
241
|
+
result += " \\role{#{role_str.tq}}\n"
|
242
|
+
end
|
243
|
+
end
|
244
|
+
unless other_parties.empty?
|
245
|
+
ogroups = other_parties.group_by(&:role)
|
246
|
+
first_group = true
|
247
|
+
ogroups.each_value do |os|
|
248
|
+
if first_group
|
249
|
+
first_group = false
|
250
|
+
else
|
251
|
+
result += " \\versusand\n"
|
252
|
+
end
|
253
|
+
os.each do |d|
|
254
|
+
result += " \\aparty{#{d.name.tq}}\\\\\n"
|
255
|
+
end
|
256
|
+
role_str = os.length > 1 ? os.first.role.pluralize : os.first.role
|
257
|
+
result += " \\role{#{role_str.tq}}\n"
|
258
|
+
end
|
259
|
+
end
|
260
|
+
result += "}\n"
|
261
|
+
result
|
262
|
+
end
|
263
|
+
|
264
|
+
# Compute LeftCaptionBlock
|
265
|
+
def tex_lawdoc_caption_versus
|
266
|
+
result = "\\LeftCaptionBlock{%\n"
|
267
|
+
unless plaintiffs.empty?
|
268
|
+
p = plaintiffs.first
|
269
|
+
et_al = plaintiffs.length > 1 ? ', \textit{et al.}' : ''
|
270
|
+
role_str = plaintiffs.length > 1 ? p.role.pluralize : p.role
|
271
|
+
result += " \\party{#{p.name.tq}#{et_al}}{#{role_str.tq}}\\\\\n"
|
272
|
+
result += " \\versus\n"
|
273
|
+
end
|
274
|
+
unless defendants.empty?
|
275
|
+
d = defendants.first
|
276
|
+
et_al = defendants.length > 1 ? ', \textit{et al.}' : ''
|
277
|
+
role_str = defendants.length > 1 ? d.role.pluralize : d.role
|
278
|
+
result += " \\party{#{d.name.tq}#{et_al}}{#{role_str.tq}}\\\\\n"
|
279
|
+
end
|
280
|
+
result += "}\n"
|
281
|
+
result
|
282
|
+
end
|
283
|
+
|
284
|
+
def tex_lawdoc_caption
|
285
|
+
if style == :inre
|
286
|
+
tex_lawdoc_caption_inre
|
287
|
+
else
|
288
|
+
tex_lawdoc_caption_versus
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def tex_lawdoc_full_caption
|
293
|
+
if style == :inre
|
294
|
+
tex_lawdoc_full_caption_inre
|
295
|
+
else
|
296
|
+
tex_lawdoc_full_caption_versus
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def tex_lawdoc_macros
|
301
|
+
result = ''
|
302
|
+
result += "\\CaseNumber{#{number.tq}}\n"
|
303
|
+
if judge
|
304
|
+
result += "\\JudgeInitials{#{judge.initials.tq}}\n"
|
305
|
+
result += "\\JudgeLast{#{judge.last.tq}}\n"
|
306
|
+
result += "\\JudgeName{#{judge.name.tq}}\n"
|
307
|
+
end
|
308
|
+
if magistrate
|
309
|
+
result += "\\MagistrateInitials{#{magistrate.initials.tq}}\n"
|
310
|
+
result += "\\MagistrateLast{#{magistrate.last.tq}}\n"
|
311
|
+
result += "\\MagistrateName{#{magistrate.name.tq}}\n"
|
312
|
+
end
|
313
|
+
result += tex_lawdoc_caption
|
314
|
+
result += tex_lawdoc_full_caption
|
315
|
+
result
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'active_support/core_ext/object/blank'
|
2
|
+
|
3
|
+
class Array
|
4
|
+
def non_blank
|
5
|
+
select { |x| !x.blank? }
|
6
|
+
end
|
7
|
+
|
8
|
+
def join_nonblank(sep = ' ')
|
9
|
+
non_blank.join(sep)
|
10
|
+
end
|
11
|
+
|
12
|
+
def join_serial(sep = ', ', last_sep = ', ')
|
13
|
+
result = ''
|
14
|
+
non_blank.each_with_flags do |itm, first, last|
|
15
|
+
next if itm.blank?
|
16
|
+
result +=
|
17
|
+
if first
|
18
|
+
itm.to_s
|
19
|
+
elsif last
|
20
|
+
"#{last_sep}#{itm}"
|
21
|
+
else
|
22
|
+
"#{sep}#{itm}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
result
|
26
|
+
end
|
27
|
+
|
28
|
+
def join_and
|
29
|
+
items = non_blank
|
30
|
+
case items.size
|
31
|
+
when 1
|
32
|
+
items.first.to_s
|
33
|
+
when 2
|
34
|
+
items.join(' and ')
|
35
|
+
else
|
36
|
+
join_serial(', ', ', and ')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def join_or
|
41
|
+
items = non_blank
|
42
|
+
case items.size
|
43
|
+
when 1
|
44
|
+
items.first.to_s
|
45
|
+
when 2
|
46
|
+
items.join(' and ')
|
47
|
+
else
|
48
|
+
join_serial(', ', ', or ')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class Numeric
|
2
|
+
def ordinal
|
3
|
+
if abs < 10
|
4
|
+
# Single digit
|
5
|
+
if abs == 1
|
6
|
+
"#{self}st"
|
7
|
+
elsif self.abs == 2
|
8
|
+
"#{self}nd"
|
9
|
+
elsif self.abs == 3
|
10
|
+
"#{self}rd"
|
11
|
+
else
|
12
|
+
"#{self}th"
|
13
|
+
end
|
14
|
+
elsif [0, 4, 5, 6, 7, 8, 9].include?(abs % 10) ||
|
15
|
+
[11, 12, 13].include?(abs % 100)
|
16
|
+
"#{self}th"
|
17
|
+
elsif abs % 10 == 1
|
18
|
+
"#{self}st"
|
19
|
+
elsif abs % 10 == 2
|
20
|
+
"#{self}nd"
|
21
|
+
elsif abs % 10 == 3
|
22
|
+
"#{self}rd"
|
23
|
+
else
|
24
|
+
raise "Numeric#ordinal unhandled case: #{self}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def month_name
|
29
|
+
index = (abs % 12) - 1
|
30
|
+
months = %w[January February March April May June July August
|
31
|
+
September October November December ]
|
32
|
+
months[index]
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module LawDoc
|
2
|
+
class Court < Person
|
3
|
+
attr_reader :division, :district, :state
|
4
|
+
|
5
|
+
def initialize(division: nil,
|
6
|
+
district: nil,
|
7
|
+
state: nil,
|
8
|
+
**other)
|
9
|
+
other[:sex] = 'entity'
|
10
|
+
super(**other)
|
11
|
+
@district = district
|
12
|
+
@division = division
|
13
|
+
@state = state
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_h
|
17
|
+
super.merge({ division: division,
|
18
|
+
district: district,
|
19
|
+
state: state })
|
20
|
+
end
|
21
|
+
|
22
|
+
include Comparable
|
23
|
+
|
24
|
+
def <=>(other)
|
25
|
+
to_h <=> other.to_h
|
26
|
+
end
|
27
|
+
|
28
|
+
def tex_lawdoc_forum
|
29
|
+
components = []
|
30
|
+
components << '\\begin{center}'
|
31
|
+
components << "#{name.tq}\\\\"
|
32
|
+
components << "For the #{district.tq}\\\\" if district
|
33
|
+
components << "(#{division.tq} Division)\\\\" if division
|
34
|
+
components << '\\end{center}'
|
35
|
+
components.join_nonblank("\n")
|
36
|
+
end
|
37
|
+
|
38
|
+
def tex_lawdoc_macros
|
39
|
+
<<~ENDM
|
40
|
+
\\renewcommand{\\Forum}{#{tex_lawdoc_forum}}
|
41
|
+
\\renewcommand{\\CourtName}{#{name.tq}}
|
42
|
+
\\renewcommand{\\CourtDistrict}{#{district.tq}}
|
43
|
+
\\renewcommand{\\CourtDivision}{#{division.tq}}
|
44
|
+
\\renewcommand{\\CourtState}{#{state.tq}}
|
45
|
+
ENDM
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|