text: from __future__ import (
      absolute_import,
      division,
      print_function,
      unicode_literals,
  )
  
  import re
  from datetime import datetime
  from typing import List, Optional
  
  import six
  
  from courts_db.text_utils import strip_punc
  
  from .utils import gather_regexes, load_courts_db, make_court_dictionary
  
  
  __all__ = [
      # lazy-loaded data structures:
      "courts",
      "court_dict",
      "regexes",
      # helper functions:
      "find_court_ids_by_name",
      "filter_courts_by_date",
      "filter_courts_by_bankruptcy",
      "find_court_by_id",
      "find_court",
  ]
  
  
  def __getattr__(name):
      """Lazy load data structures from loaders module."""
      if name == "courts":
          value = load_courts_db()
      elif name == "court_dict":
          from . import courts
  
          value = make_court_dictionary(courts)
      elif name == "regexes":
          from . import courts
  
          value = gather_regexes(courts)
      else:
          raise AttributeError(f"module {__name__} has no attribute {name}")
      globals()[name] = value
      return value
  
  
  def find_court_ids_by_name(
      court_str: str, bankruptcy: Optional[bool], location: Optional[str]
  ) -> List[str]:
      """Find court IDs with our courts-db regex list
  
      :param court_str: test string
      :param bankruptcy: Are we searhing for a bankruptcy court
      :return: List of Court IDs matched
      """
      from . import regexes
  
      assert (
          type(court_str) == six.text_type
      ), "court_str is not a text type, it's of type %s" % type(court_str)
  
      court_matches = set()
      matches = []
      for regex, court_id, court_name, court_type, court_location in regexes:
          # If location provided - check if overlapped.
          if location and court_location != location:
              continue
          # Filter gathered regexes by bankruptcy flag.
          if bankruptcy is True:
              if court_type != "bankruptcy":
                  continue
          elif bankruptcy is False:
              if court_type == "bankruptcy":
                  continue
          match = re.search(regex, court_str)
          if match:
              m = (match.group(0), court_id)
              matches.append(m)
      # If no matches found - check against - Court Name - not regex patterns.
      if not matches:
          for court in courts:
              if strip_punc(court_str) == strip_punc(court["name"]):
                  matches.append((court_str, court["id"]))
  
      matched_strings = [m[0] for m in matches]
      filtered_list = filter(
          lambda x: [x for i in matched_strings if x in i and x != i] == [],
          matched_strings,
      )
      for item in list(filtered_list):
          for mat in matches:
              if item == mat[0]:
                  court_matches.add(mat[1])
      return list(court_matches)
  
  
  def filter_courts_by_date(matches, date_found, strict_dates=False):
      """Filter IDs by date found.
  
      Strict dates should be more useful as dates are filled in.
      :param matches:
      :param date_found: datetime object
      :param strict_dates: Boolean that helps tell the sytsem how to handle
      null dates in courts-db
      :return: List of court IDs matched
      """
      from . import courts
  
      assert (
          type(date_found) is datetime
      ), "date_found is not a date object, it's of type %s" % type(date_found)
  
      results = [court for court in courts if court["id"] in matches]
      filtered_results = []
      for result in results:
          for date_object in result["dates"]:
              date_start = date_object["start"]
              date_end = date_object["end"]
              if strict_dates == False:
                  if date_start == None:
                      date_start = "1600-01-01"
                  if date_end == None:
                      date_end = "2100-01-01"
              if strict_dates:
                  if date_start == None:
                      continue
                  if date_end == None:
                      date_end = "2100-01-01"
  
              date_start = datetime.strptime(date_start, "%Y-%m-%d")
              date_end = datetime.strptime(date_end, "%Y-%m-%d")
  
              if date_start <= date_found <= date_end:
                  filtered_results.append(result["id"])
  
      return filtered_results
  
  
  def filter_courts_by_bankruptcy(matches, bankruptcy):
      from . import courts
  
      results = [court for court in courts if court["id"] in matches]
      if bankruptcy:
          return [
              court["id"] for court in results if court["type"] == "bankruptcy"
          ]
      return [court["id"] for court in results if court["type"] != "bankruptcy"]
  
  
  def find_court_by_id(court_id):
      """Find court dictionary using court id code.
  
      :param court_id: Court code used by Courtlistener.com
      :return: Return dictionary court object from db
      """
      from . import courts
  
      return [court for court in courts if court["id"] == court_id]
  
  
  def find_court(
      court_str: str,
      bankruptcy: Optional[bool] = None,
      date_found: Optional[datetime] = None,
      strict_dates: Optional[bool] = False,
      location: Optional[str] = None,
  ):
      """Finds a list of court ID for a given string and parameters
  
      :param court_str: The unicode string we are testing
      :param bankruptcy: Tells function to exclude or include bankruptcy cases
      :param date_found: Date object
      :param strict_dates: Boolean that helps tell the sytsem how to handle
      :param location: Where the court is located.
      null dates in courts-db
      :return: List of court IDs if any
      """
      court_str = strip_punc(court_str)
      matches = find_court_ids_by_name(court_str, bankruptcy, location)
      if bankruptcy is not None:
          matches = filter_courts_by_bankruptcy(
              matches=matches, bankruptcy=bankruptcy
          )
      if date_found:
          matches = filter_courts_by_date(
              matches=matches, date_found=date_found, strict_dates=strict_dates
          )
  
      return matches
  