Edit on GitHub

ecourts.cli

  1import click
  2import csv
  3import sys
  4from entities import Court
  5from ecourt import ECourt, RetryException
  6from sys import stdout
  7from datetime import datetime
  8from storage import Storage
  9from tabulate import tabulate
 10import click
 11from functools import wraps
 12from parsers.cases import parse_cases
 13
 14def validate_year(ctx, _, value):
 15    if value and (value < 1990 or value > 2025):
 16        raise click.BadParameter("Year must be in YYYY format")
 17    return value
 18
 19
 20def validate_date(ctx, _, value):
 21    if value:
 22        try:
 23            datetime.strptime(value, "%Y-%m-%d")
 24        except ValueError:
 25            raise click.BadParameter("Date must be in YYYY-MM-DD format")
 26    return value
 27
 28
 29def setup_state_code(ctx, _, value):
 30    ctx.obj["state_code"] = value
 31
 32
 33def setup_court_code(ctx, _, value):
 34    ctx.obj["court"] = Court(state_code=ctx.obj["state_code"], court_code=value)
 35
 36
 37def common_options(f):
 38    @click.option("--case-number", help="Case number")
 39    @click.option("--party-name", help="Party name")
 40    @click.option("--case-type", type=int, help="Case type")
 41    @click.option("--year", callback=validate_year, type=int, help="Year in YYYY format")
 42    @click.option(
 43        "--state-code",
 44        callback=setup_state_code,
 45        help="State code of the Court",
 46        required=True,
 47    )
 48    @click.option(
 49        "--court-code", callback=setup_court_code, help="Court code of the Court"
 50    )
 51    @click.pass_context
 52    @wraps(f)
 53    def wrapper(ctx, *args, **kwargs):
 54        ctx.ensure_object(dict)
 55        for option in ["case_number", "party_name", "case_type", "year"]:
 56            if option in kwargs:
 57                ctx.obj[option] = kwargs[option]
 58
 59        if not "court" in ctx.obj:
 60            raise click.BadParameter("A valid state-code, court-code is required")
 61        return f(*args, **kwargs)
 62
 63    return wrapper
 64
 65
 66@click.group()
 67@click.pass_context
 68def ecourts(ctx):
 69    """eCourts application for retrieving case information."""
 70    ctx.ensure_object(dict)
 71
 72
 73@ecourts.command()
 74@common_options
 75@click.option("--fir-number", help="FIR number")
 76@click.option("--filing-number", help="Filing number")
 77@click.option(
 78    "--status", type=click.Choice(["Pending", "Disposed"]), help="Case status"
 79)
 80@click.option("--police-station-id", help="Police station ID")
 81@click.option("--act-type", help="ACT Type (number)")
 82@click.option("--save", help="Save cases to database", is_flag=True)
 83@click.pass_context
 84def get_cases(
 85    ctx,
 86    case_number,
 87    party_name,
 88    case_type,
 89    year,
 90    state_code,
 91    court_code,
 92    fir_number,
 93    filing_number,
 94    status,
 95    police_station_id,
 96    act_type,
 97    save
 98):
 99    court: Court = ctx.obj["court"]
100    ecourt = ECourt(court)
101
102    if case_number and case_type and year:
103        ecourt.search_by_case_number(case_type, case_number, year)
104    elif police_station_id:
105        ecourt.search_by_fir_number(police_station_id, fir_number, year, status)
106    elif party_name:
107        ecourt.search_by_party_name(party_name, year, status)
108    elif filing_number and year:
109        ecourt.search_by_filing_number(filing_number, year)
110    elif act_type != None and status != None:
111        assert status != "Both"
112        extra_fields = {
113            "status": status,
114            "act_type": act_type
115        }
116        cases = list(ecourt.ActType(act_type, status))
117    elif case_type != None and status != None:
118        assert status != "Both"
119        extra_fields = {
120            "status": status,
121            "case_type_int": case_type
122        }
123        cases = list(ecourt.CaseType(case_type, status, year))
124    else:
125        click.echo(
126            "Invalid combination of arguments. Please provide a valid set of options."
127        )
128        sys.exit(1)
129
130    idx = 0
131    for case in cases:
132        res = {}
133        fields = ['registration_number', 'cnr_number', 'name']
134        for field in fields:
135            value = getattr(case, field)
136            if value:
137                res[field] = value
138
139        writer = csv.DictWriter(stdout, fieldnames=fields)
140        if idx == 0:
141            writer.writeheader()
142        writer.writerow(res)
143        idx += 1
144
145    if save:
146        print(court)
147        print(cases)
148        print(extra_fields)
149        Storage().addCases(court, cases, extra_fields)
150
151
152@ecourts.command()
153@common_options
154@click.option("--filing-number", help="Filing number")
155@click.option(
156    "--from-date", callback=validate_date, help="From date in YYYY-MM-DD format"
157)
158@click.option("--to-date", callback=validate_date, help="To date in YYYY-MM-DD format")
159@click.pass_context
160def get_orders(
161    ctx,
162    case_number,
163    party_name,
164    case_type,
165    year,
166    state_code,
167    court_code,
168    filing_number,
169    from_date,
170    to_date,
171):
172    """Get order information based on various search criteria."""
173    if case_number and case_type and year:
174        orders_case_number(state_code, court_code, case_type, case_number, year)
175    elif filing_number and year:
176        orders_filling_number(state_code, court_code, filing_number, year)
177    elif party_name and year:
178        orders_party_name(state_code, court_code, party_name, year)
179    elif from_date and to_date:
180        orders_order_date(state_code, court_code, from_date, to_date)
181    else:
182        click.echo(
183            "Invalid combination of arguments. Please provide a valid set of options."
184        )
185
186
187@ecourts.command()
188@click.option("--state-code", help="State code of the Court")
189@click.option("--court-code", help="Court code of the Court")
190@click.option("--save", help="Save data in database", is_flag=True)
191@click.pass_context
192def get_case_types(ctx, state_code, court_code, save):
193    if state_code == None and court_code == None:
194        courts = Court.enumerate()
195    else:
196        courts = [Court(state_code=state_code, court_code=court_code)]
197
198    for court in courts:
199        ecourt = ECourt(court)
200        types = ecourt.getCaseTypes()
201        if save:
202            Storage().addCaseTypes(types)
203
204@ecourts.command()
205@click.argument("query")
206@click.pass_context
207def find_case_types(ctx, query: str):
208    query = query.lower()
209    print(query)
210    s = Storage()
211    types = [ct for ct in s.getCaseTypes() if query in ct.description.lower()]
212    print(tabulate([
213        {"State": cl.court.state_code, "Court": cl.court.court_code, "Code": cl.code, "Description": cl.description}
214        for cl in types
215    ], headers={"code": "Code", "description": "Description"}, tablefmt="presto"))
216
217
218@ecourts.command()
219@click.option("--state-code", help="State code of the Court")
220@click.option("--court-code", help="Court code of the Court")
221@click.option("--save", help="Save data in database", is_flag=True)
222@click.pass_context
223def get_act_types(ctx, state_code, court_code, save):
224    if state_code == None and court_code == None:
225        courts = Court.enumerate()
226    else:
227        courts = [Court(state_code=state_code, court_code=court_code)]
228
229    for court in courts:
230        ecourt = ECourt(court)
231        types = ecourt.getActTypes()
232        if save:
233            Storage().addActTypes(types)
234
235
236@ecourts.command()
237@click.option("--state-code", help="State code of the Court")
238@click.option("--court-code", help="Court code of the Court")
239@click.option("--date", help="Cause List Date", type=click.DateTime(formats=["%Y-%m-%d"]),
240              default=str(datetime.today().date()))
241@click.option("--save", help="Save data in database", is_flag=True)
242@click.option("--max-attempts", help="Maximum attempts to make per court, in case of any errors", type=int, default=15)
243@click.pass_context
244def get_cause_lists(ctx, state_code, court_code, date: click.DateTime, save, max_attempts):
245
246    if state_code == None and court_code == None:
247        courts = list(Court.enumerate())
248    else:
249        courts = [Court(state_code=state_code, court_code=court_code)]
250
251    for court in courts:
252        ecourt = ECourt(court)
253        ecourt.set_max_attempts(max_attempts)
254        try:
255            data = list(ecourt.getCauseLists(date.date()))
256            if len(data) > 0:
257                print(tabulate([
258                    {"State": court.state_code, "Court": court.court_code} | cl.printable_dict()
259                    for cl in data
260                ], headers={"bench": "Bench", "type": "Type"}, tablefmt="presto"))
261        except RetryException:
262            if len(courts) > 1:
263                print(f"Error in court {court}. Ignoring")
264                continue
265            else:
266                raise
267
268
269@ecourts.command()
270@click.option("--cnr", help="Case CNR", required=False, type=str)
271@click.option("--download-orders", help="Download Orders", required=False, is_flag=True, default=False)
272def enrich_cases(cnr, download_orders):
273    s = Storage()
274    for case_data in s.getCases():
275        if cnr:
276            if case_data['cnr_number'] != cnr:
277                continue
278        # cnr given = automatically force an update
279        if case_data['case_status'] != None and cnr == None:
280            continue
281        
282        court = Court(state_code=case_data['state_code'], court_code=case_data['court_code'])
283
284        if 'case_type_int' not in case_data:
285            case_data['case_type_int'] = s.findCaseType(court, case_data['case_type'])
286            if case_data['case_type_int'] == None:
287                print("Case Type not found for " + case_data['cnr_number'])
288                continue
289
290        ecourt = ECourt(court)
291        registration_number = case_data['registration_number']
292        # Search using case_type_int for now, we can move to number search, but that is heuristic really.
293        cases = list(parse_cases(ecourt.searchSingleCase(registration_number, case_data['case_type_int'])))
294        if len(cases) != 1:
295            print("Error in parsing case " + case_data['cnr_number'] + " Got " + str(len(cases)) + " cases while searching")
296        else:
297            single_case = cases[0]
298            new_case = ecourt.expand_case(single_case)
299            if new_case.registration_number != single_case.registration_number or registration_number != single_case.registration_number:
300                print("Case Details Mismatch" + case_data['cnr_number'])
301            print(f"{new_case.cnr_number},{new_case.filing_number},{new_case.registration_number} {len(new_case.hearings)} Hearings, {len(new_case.orders)} Orders")
302            s.addCases(ecourt.court, [new_case])
303
304            if download_orders:
305                if new_case.orders:
306                    for order in new_case.orders:
307                        fn = urllib.parse.quote_plus(order.filename)
308                        if os.path.exists(f"orders/{fn}.pdf"):
309                            continue
310                        ecourt.downloadOrder(order, new_case, f"orders/{fn}.pdf")
311
312
313@ecourts.command()
314def stats():
315    """Print statistics about the database."""
316    stats = Storage().stats()
317    for k, v in stats.items():
318        click.echo(f"{k}: {v}")
319
320
321if __name__ == "__main__":
322    ecourts()
def validate_year(ctx, _, value):
15def validate_year(ctx, _, value):
16    if value and (value < 1990 or value > 2025):
17        raise click.BadParameter("Year must be in YYYY format")
18    return value
def validate_date(ctx, _, value):
21def validate_date(ctx, _, value):
22    if value:
23        try:
24            datetime.strptime(value, "%Y-%m-%d")
25        except ValueError:
26            raise click.BadParameter("Date must be in YYYY-MM-DD format")
27    return value
def setup_state_code(ctx, _, value):
30def setup_state_code(ctx, _, value):
31    ctx.obj["state_code"] = value
def setup_court_code(ctx, _, value):
34def setup_court_code(ctx, _, value):
35    ctx.obj["court"] = Court(state_code=ctx.obj["state_code"], court_code=value)
def common_options(f):
38def common_options(f):
39    @click.option("--case-number", help="Case number")
40    @click.option("--party-name", help="Party name")
41    @click.option("--case-type", type=int, help="Case type")
42    @click.option("--year", callback=validate_year, type=int, help="Year in YYYY format")
43    @click.option(
44        "--state-code",
45        callback=setup_state_code,
46        help="State code of the Court",
47        required=True,
48    )
49    @click.option(
50        "--court-code", callback=setup_court_code, help="Court code of the Court"
51    )
52    @click.pass_context
53    @wraps(f)
54    def wrapper(ctx, *args, **kwargs):
55        ctx.ensure_object(dict)
56        for option in ["case_number", "party_name", "case_type", "year"]:
57            if option in kwargs:
58                ctx.obj[option] = kwargs[option]
59
60        if not "court" in ctx.obj:
61            raise click.BadParameter("A valid state-code, court-code is required")
62        return f(*args, **kwargs)
63
64    return wrapper
ecourts = <Group ecourts>

eCourts application for retrieving case information.

get_orders = <Command get-orders>

Get order information based on various search criteria.

stats = <Command stats>

Print statistics about the database.