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):
def
validate_date(ctx, _, value):
def
setup_state_code(ctx, _, value):
def
setup_court_code(ctx, _, 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.