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.
@@ -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,9 @@
1
+ class NilClass
2
+ def tex_block
3
+ ''
4
+ end
5
+
6
+ def tq
7
+ ''
8
+ end
9
+ 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,3 @@
1
+ class String
2
+ alias tq tex_quote
3
+ end
@@ -0,0 +1,4 @@
1
+ require 'law_doc/core_ext/array'
2
+ require 'law_doc/core_ext/numeric'
3
+ require 'law_doc/core_ext/string'
4
+ require 'law_doc/core_ext/nil'
@@ -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