Example #1
0
class DocketEntryFilterForm(forms.Form):
    ASCENDING = "asc"
    DESCENDING = "desc"
    DOCKET_ORDER_BY_CHOICES = (
        (ASCENDING, "Ascending"),
        (DESCENDING, "Descending"),
    )
    entry_gte = forms.IntegerField(
        required=False,
        min_value=0,
        widget=forms.TextInput(attrs={
            "class": "form-control",
            "autocomplete": "off"
        }),
    )
    entry_lte = forms.IntegerField(
        required=False,
        min_value=0,
        widget=forms.TextInput(attrs={
            "class": "form-control",
            "autocomplete": "off"
        }),
    )
    filed_after = FloorDateField(
        required=False,
        label="Filed After",
        widget=forms.TextInput(
            attrs={
                "placeholder": "MM/DD/YYYY",
                "class": "form-control datepicker",
                "autocomplete": "off",
            }),
    )
    filed_before = CeilingDateField(
        required=False,
        label="Filed Before",
        widget=forms.TextInput(
            attrs={
                "placeholder": "MM/DD/YYYY",
                "class": "form-control datepicker",
                "autocomplete": "off",
            }),
    )
    order_by = forms.ChoiceField(
        choices=DOCKET_ORDER_BY_CHOICES,
        required=False,
        label="Ordering",
        initial=ASCENDING,
        widget=forms.Select(),
    )
Example #2
0
class DocketEntryFilterForm(forms.Form):
    ASCENDING = 'asc'
    DESCENDING = 'desc'
    DOCKET_ORDER_BY_CHOICES = (
        (ASCENDING, 'Ascending'),
        (DESCENDING, 'Descending'),
    )
    entry_gte = forms.IntegerField(
        required=False,
        min_value=0,
        widget=forms.TextInput(attrs={
            'class': 'form-control',
            'autocomplete': 'off'
        })
    )
    entry_lte = forms.IntegerField(
        required=False,
        min_value=0,
        widget=forms.TextInput(attrs={
            'class': 'form-control',
            'autocomplete': 'off'
        })
    )
    filed_after = FloorDateField(
        required=False,
        label='Filed After',
        widget=forms.TextInput(attrs={
            'placeholder': 'YYYY-MM-DD',
            'class': 'form-control',
            'autocomplete': 'off'
        })
    )
    filed_before = CeilingDateField(
        required=False,
        label='Filed Before',
        widget=forms.TextInput(attrs={
            'placeholder': 'YYYY-MM-DD',
            'class': 'form-control',
            'autocomplete': 'off'
        })
    )
    order_by = forms.ChoiceField(
        choices=DOCKET_ORDER_BY_CHOICES,
        required=False,
        label='Ordering',
        initial=ASCENDING,
        widget=forms.Select()
    )
Example #3
0
class SearchForm(forms.Form):
    #
    # Blended fields
    #
    type = forms.ChoiceField(
        choices=TYPE_CHOICES,
        required=False,
        initial='o',
        widget=forms.RadioSelect(
            attrs={'class': 'external-input form-control'}
        )
    )
    q = forms.CharField(
        required=False
    )
    court = forms.CharField(
        required=False,
        widget=forms.HiddenInput()
    )
    order_by = forms.ChoiceField(
        choices=OPINION_ORDER_BY_CHOICES,
        required=False,
        label='Result Ordering',
        initial='score desc',
        widget=forms.Select(
            attrs={'class': 'external-input form-control'}
        )
    )

    #
    # Oral argument and Opinion shared fields
    #
    case_name = forms.CharField(
        required=False,
        label='Case Name',
        initial='',
        widget=forms.TextInput(
            attrs={'class': 'external-input form-control',
                   'autocomplete': 'off'}
        )
    )
    judge = forms.CharField(
        required=False,
        initial='',
        widget=forms.TextInput(
            attrs={'class': 'external-input form-control',
                   'autocomplete': 'off'}
        )
    )
    docket_number = forms.CharField(
        required=False,
        label='Docket Number',
        widget=forms.TextInput(
            attrs={'class': 'external-input form-control',
                   'autocomplete': 'off'}
        )
    )

    #
    # RECAP fields
    #
    available_only = forms.BooleanField(
        label="Exclude items not in RECAP",
        label_suffix='',
        required=False,
        widget=forms.CheckboxInput(attrs={
            'class': 'external-input form-control left',
        }),
    )
    description = forms.CharField(
        required=False,
        label="Document Description",
        widget=forms.TextInput(
            attrs={'class': 'external-input form-control',
                   'autocomplete': 'off'}
        )
    )
    nature_of_suit = forms.CharField(
        required=False,
        label="Nature of Suit",
        widget=forms.TextInput(
            attrs={'class': 'external-input form-control',
                   'autocomplete': 'off'}
        )
    )
    cause = forms.CharField(
        required=False,
        label="Cause",
        widget=forms.TextInput(
            attrs={'class': 'external-input form-control',
                   'autocomplete': 'off'}
        )
    )
    assigned_to = forms.CharField(
        required=False,
        label="Assigned To Judge",
        widget=forms.TextInput(
            attrs={'class': 'external-input form-control',
                   'autocomplete': 'off'}
        )
    )
    referred_to = forms.CharField(
        required=False,
        label="Referred To Judge",
        widget=forms.TextInput(
            attrs={'class': 'external-input form-control',
                   'autocomplete': 'off'}
        )
    )
    document_number = forms.IntegerField(
        required=False,
        label="Document #",
        widget=forms.TextInput(
            attrs={'class': 'external-input form-control',
                   'autocomplete': 'off'}
        )
    )
    attachment_number = forms.IntegerField(
        required=False,
        label="Attachment #",
        widget=forms.TextInput(
            attrs={'class': 'external-input form-control',
                   'autocomplete': 'off'}
        )
    )

    #
    # Oral argument fields
    #
    argued_after = FloorDateField(
        required=False,
        label="Argued After",
        widget=forms.TextInput(
            attrs={'placeholder': 'YYYY-MM-DD',
                   'class': 'external-input form-control',
                   'autocomplete': 'off'}
        )
    )
    argued_before = CeilingDateField(
        required=False,
        label="Argued Before",
        widget=forms.TextInput(
            attrs={'placeholder': 'YYYY-MM-DD',
                   'class': 'external-input form-control',
                   'autocomplete': 'off'}
        )
    )

    #
    # Opinion fields
    #
    filed_after = FloorDateField(
        required=False,
        label='Filed After',
        widget=forms.TextInput(
            attrs={'placeholder': 'YYYY-MM-DD',
                   'class': 'external-input form-control',
                   'autocomplete': 'off'}
        )
    )
    filed_before = CeilingDateField(
        required=False,
        label='Filed Before',
        widget=forms.TextInput(
            attrs={'placeholder': 'YYYY-MM-DD',
                   'class': 'external-input form-control',
                   'autocomplete': 'off'}
        )
    )
    citation = forms.CharField(
        required=False,
        widget=forms.TextInput(
            attrs={'class': 'external-input form-control',
                   'autocomplete': 'off'}
        )
    )
    neutral_cite = forms.CharField(
        required=False,
        label='Neutral Citation',
        widget=forms.TextInput(
            attrs={'class': 'external-input form-control',
                   'autocomplete': 'off'}
        )
    )
    cited_gt = forms.CharField(
        required=False,
        label='Min Cites',
        initial=0,
        widget=forms.TextInput(
            attrs={'class': 'external-input form-control',
                   'autocomplete': 'off'}
        )
    )
    cited_lt = forms.CharField(
        required=False,
        label='Max Cites',
        initial=60000,
        widget=forms.TextInput(
            attrs={'class': 'external-input form-control',
                   'autocomplete': 'off'}
        )
    )

    #
    # Judge fields
    #
    name = forms.CharField(
        required=False,
        label='Name',
        widget=forms.TextInput(
            attrs={'class': 'external-input form-control',
                   'autocomplete': 'off'}
        )
    )
    born_after = FloorDateField(
        required=False,
        label="Born After",
        widget=forms.TextInput(
            attrs={'placeholder': 'YYYY-MM-DD',
                   'class': 'external-input form-control',
                   'autocomplete': 'off'}
        )
    )
    born_before = CeilingDateField(
        required=False,
        label="Born Before",
        widget=forms.TextInput(
            attrs={'placeholder': 'YYYY-MM-DD',
                   'class': 'external-input form-control',
                   'autocomplete': 'off'}
        )
    )
    dob_city = forms.CharField(
        required=False,
        label='Birth City',
        widget=forms.TextInput(
                attrs={'class': 'external-input form-control',
                       'autocomplete': 'off'}
        )
    )
    dob_state = forms.ChoiceField(
        choices=[('', '---------')] + list(STATE_CHOICES),
        required=False,
        label='Birth State',
        widget=forms.Select(
            attrs={'class': 'external-input form-control'}
        )
    )
    school = forms.CharField(
        required=False,
        label='School Attended',
        widget=forms.TextInput(
            attrs={'class': 'external-input form-control',
                   'autocomplete': 'off'}
        )
    )
    appointer = forms.CharField(
        required=False,
        label='Appointed By',
        widget=forms.TextInput(
            attrs={'class': 'external-input form-control',
                   'autocomplete': 'off'}
        )
    )
    selection_method = forms.ChoiceField(
        choices=[('', '---------')] + list(Position.SELECTION_METHODS),
        required=False,
        label='Selection Method',
        initial='None',
        widget=forms.Select(
            attrs={'class': 'external-input form-control'}
        )
    )
    political_affiliation = forms.ChoiceField(
        choices=[('', '---------')] + list(PoliticalAffiliation.POLITICAL_PARTIES),
        required=False,
        label='Political Affiliation',
        initial='None',
        widget=forms.Select(
            attrs={'class': 'external-input form-control'}
        )
    )

    def get_date_field_names(self):
        return {f_name.split('_')[0] for f_name, f in self.fields.items()
                if isinstance(f, DateField)}

    def __init__(self, *args, **kwargs):
        super(SearchForm, self).__init__(*args, **kwargs)
        """
        Normally we wouldn't need to use __init__ in a form object like
        this, however, since we are generating checkbox fields with dynamic
        names coming from the database, we need to interact directly with the
        fields dict.
        """
        courts = Court.objects.filter(in_use=True)
        for court in courts:
            self.fields['court_' + court.pk] = forms.BooleanField(
                label=court.short_name,
                required=False,
                initial=True,
                widget=forms.CheckboxInput(attrs={'checked': 'checked'})
            )

        for status in DOCUMENT_STATUSES:
            attrs = {}
            if status[1] == 'Precedential':
                initial = True
                attrs.update({'checked': 'checked'})
            else:
                initial = False
            self.fields['stat_' + status[1]] = forms.BooleanField(
                label=status[1],
                required=False,
                initial=initial,
                widget=forms.CheckboxInput(attrs=attrs)
            )

    # This is a particularly nasty area of the code due to several factors:
    #  1. Django doesn't have a good method of setting default values for
    #     bound forms. As a result, we set them here as part of a cleanup
    #     routine. This way a user can do a query for page=2, and still have
    #     all the correct defaults.
    #  2. In our search form, part of what we do is clean up the GET requests
    #     that the user sent. This is completed in clean_form(). This allows a
    #     user to be taught what better queries look like. To do this, we have
    #     to make a temporary variable in _clean_form() and assign it
    #     the values of the cleaned_data. The upshot of this is that most
    #     changes made here will also need to be made in _clean_form().
    #     Failure to do that will result in the query being processed correctly
    #     (search results are all good), but the form on the UI won't be
    #     cleaned up for the user, making things rather confusing.
    #  3. We do some cleanup work in search_utils.make_stats_variable(). The
    #     work that's done there is used to check or un-check the boxes in the
    #     sidebar, so if you tweak how they work you'll need to tweak this
    #     function.
    # In short: This is a nasty area. Comments this long are a bad sign for
    # the intrepid developer.
    def clean_q(self):
        """
        Cleans up various problems with the query:
         - lowercase --> camelCase
         - '|' --> ' OR '
        """
        q = self.cleaned_data['q']

        # Fix fields to work in all lowercase
        sub_pairs = (
            # Blended
            ('casename', 'caseName'),
            ('case_name', 'caseName'),
            ('docketnumber', 'docketNumber'),
            ('datefiled', 'dateFiled'),
            ('suitnature', 'suitNature'),

            # Opinions
            ('lexiscite', 'lexisCite'),
            ('neutralcite', 'neutralCite'),
            ('citecount', 'citeCount'),

            # Oral Args
            ('dateargued', 'dateArgued'),
            ('datereargued', 'dateReargued'),

            # People
            ('DOD', 'dod'),
            ('DOB', 'dob'),

            # RECAP
            ('dateterminated', 'dateTerminated'),
            ('jurydemand', 'juryDemand'),
        )
        for bad, good in sub_pairs:
            q = re.sub(bad, good, q)

        # Make pipes work
        q = re.sub('\|', ' OR ', q)

        return q

    def clean_order_by(self):
        """Sets the default order_by value if one isn't provided by the user."""
        if self.cleaned_data['type'] == 'o' or not self.cleaned_data['type']:
            if not self.cleaned_data['order_by']:
                return self.fields['order_by'].initial
        elif self.cleaned_data['type'] == 'oa':
            if not self.cleaned_data['order_by']:
                return 'dateArgued desc'
        elif self.cleaned_data['type'] == 'p':
            if not self.cleaned_data['order_by']:
                return 'name_reverse asc'
        return self.cleaned_data['order_by']

    def clean_type(self):
        """Make sure that type has an initial value."""
        if not self.cleaned_data['type']:
            return self.fields['type'].initial
        return self.cleaned_data['type']

    def clean(self):
        """
        Handles validation fixes that need to be performed across fields.
        """
        cleaned_data = self.cleaned_data

        # 1. Make sure that the dates do this |--> <--| rather than <--| |-->
        for field_name in self.get_date_field_names():
            before = cleaned_data.get('%s_before' % field_name)
            after = cleaned_data.get('%s_after' % field_name)
            if before and after and (before < after):
                # The user is requesting dates like this: <--b  a-->. Switch
                # the dates so their query is like this: a-->   <--b
                cleaned_data['%s_before' % field_name] = after
                cleaned_data['%s_after' % field_name] = before

        # 2. Convert the value in the court field to the various court_* fields
        court_str = cleaned_data.get('court')
        if court_str:
            if ' ' in court_str:
                court_ids = court_str.split(' ')
            elif ',' in court_str:
                court_ids = court_str.split(',')
            else:
                court_ids = [court_str]
            for court_id in court_ids:
                cleaned_data['court_%s' % court_id] = True

        # 3. Make sure that the user has selected at least one facet for each
        #    taxonomy. Note that this logic must be paralleled in
        #    search_utils.make_facet_variable
        court_bools = [v for k, v in cleaned_data.items()
                       if k.startswith('court_')]
        if not any(court_bools):
            # Set all facets to True
            for key in cleaned_data.keys():
                if key.startswith('court_'):
                    cleaned_data[key] = True

        stat_bools = [v for k, v in cleaned_data.items()
                      if k.startswith('stat_')]
        if not any(stat_bools):
            # Set everything to False...
            for key in cleaned_data.keys():
                if key.startswith('stat_'):
                    cleaned_data[key] = False
            # ...except precedential
            cleaned_data['stat_Precedential'] = True

        # 4. Strip any whitespace, otherwise it crashes Solr.
        for k, v in cleaned_data.items():
            if isinstance(v, basestring):
                cleaned_data[k] = v.strip()

        return cleaned_data
Example #4
0
class SearchForm(forms.Form):
    #
    # Blended fields
    #
    type = forms.ChoiceField(
        choices=SEARCH_TYPES.NAMES,
        required=False,
        initial=SEARCH_TYPES.OPINION,
        widget=forms.RadioSelect(
            attrs={"class": "external-input form-control"}
        ),
    )
    type.as_str_types = []
    q = forms.CharField(required=False, label="Query",)
    q.as_str_types = SEARCH_TYPES.ALL_TYPES
    court = forms.CharField(required=False, widget=forms.HiddenInput())
    court.as_str_types = []
    order_by = RandomChoiceField(
        choices=OPINION_ORDER_BY_CHOICES,
        required=False,
        label="Result Ordering",
        initial="score desc",
        widget=forms.Select(attrs={"class": "external-input form-control"}),
    )
    order_by.as_str_types = []

    #
    # Oral argument and Opinion shared fields
    #
    judge = forms.CharField(
        required=False,
        initial="",
        label="Judge",
        widget=forms.TextInput(
            attrs={
                "class": "external-input form-control",
                "autocomplete": "off",
            }
        ),
    )
    judge.as_str_types = [SEARCH_TYPES.OPINION, SEARCH_TYPES.ORAL_ARGUMENT]

    # Oral arg, opinion, and RECAP
    case_name = forms.CharField(
        required=False,
        label="Case Name",
        initial="",
        widget=forms.TextInput(
            attrs={
                "class": "external-input form-control",
                "autocomplete": "off",
            }
        ),
    )
    case_name.as_str_types = [
        SEARCH_TYPES.OPINION,
        SEARCH_TYPES.RECAP,
        SEARCH_TYPES.ORAL_ARGUMENT,
    ]
    docket_number = forms.CharField(
        required=False,
        label="Docket Number",
        widget=forms.TextInput(
            attrs={
                "class": "external-input form-control",
                "autocomplete": "off",
            }
        ),
    )
    docket_number.as_str_types = [
        SEARCH_TYPES.OPINION,
        SEARCH_TYPES.RECAP,
        SEARCH_TYPES.ORAL_ARGUMENT,
    ]

    #
    # RECAP fields
    #
    available_only = forms.BooleanField(
        label="Only show results with PDFs",
        label_suffix="",
        required=False,
        widget=forms.CheckboxInput(
            attrs={"class": "external-input form-control left",}
        ),
    )
    available_only.as_str_types = [SEARCH_TYPES.RECAP]
    description = forms.CharField(
        required=False,
        label="Document Description",
        widget=forms.TextInput(
            attrs={
                "class": "external-input form-control",
                "autocomplete": "off",
            }
        ),
    )
    description.as_str_types = [SEARCH_TYPES.RECAP]
    nature_of_suit = forms.CharField(
        required=False,
        label="Nature of Suit",
        widget=forms.TextInput(
            attrs={
                "class": "external-input form-control",
                "autocomplete": "off",
            }
        ),
    )
    nature_of_suit.as_str_types = [SEARCH_TYPES.RECAP]
    cause = forms.CharField(
        required=False,
        label="Cause",
        widget=forms.TextInput(
            attrs={
                "class": "external-input form-control",
                "autocomplete": "off",
            }
        ),
    )
    cause.as_str_types = [SEARCH_TYPES.RECAP]
    assigned_to = forms.CharField(
        required=False,
        label="Assigned To Judge",
        widget=forms.TextInput(
            attrs={
                "class": "external-input form-control",
                "autocomplete": "off",
            }
        ),
    )
    assigned_to.as_str_types = [SEARCH_TYPES.RECAP]
    referred_to = forms.CharField(
        required=False,
        label="Referred To Judge",
        widget=forms.TextInput(
            attrs={
                "class": "external-input form-control",
                "autocomplete": "off",
            }
        ),
    )
    referred_to.as_str_types = [SEARCH_TYPES.RECAP]
    document_number = forms.CharField(
        required=False,
        label="Document #",
        widget=forms.TextInput(
            attrs={
                "class": "external-input form-control",
                "autocomplete": "off",
            }
        ),
    )
    document_number.as_str_types = [SEARCH_TYPES.RECAP]
    attachment_number = forms.CharField(
        required=False,
        label="Attachment #",
        widget=forms.TextInput(
            attrs={
                "class": "external-input form-control",
                "autocomplete": "off",
            }
        ),
    )
    attachment_number.as_str_types = [SEARCH_TYPES.RECAP]
    party_name = forms.CharField(
        required=False,
        label="Party Name",
        widget=forms.TextInput(
            attrs={
                "class": "external-input form-control",
                "autocomplete": "off",
            },
        ),
    )
    party_name.as_str_types = [SEARCH_TYPES.RECAP]
    atty_name = forms.CharField(
        required=False,
        label="Attorney Name",
        widget=forms.TextInput(
            attrs={
                "class": "external-input form-control",
                "autocomplete": "off",
            },
        ),
    )
    atty_name.as_str_types = [SEARCH_TYPES.RECAP]

    #
    # Oral argument fields
    #
    argued_after = FloorDateField(
        required=False,
        label="Argued After",
        widget=forms.TextInput(
            attrs={
                "placeholder": "MM/DD/YYYY",
                "class": "external-input form-control datepicker",
                "autocomplete": "off",
            }
        ),
    )
    argued_after.as_str_types = [SEARCH_TYPES.ORAL_ARGUMENT]
    argued_before = CeilingDateField(
        required=False,
        label="Argued Before",
        widget=forms.TextInput(
            attrs={
                "placeholder": "MM/DD/YYYY",
                "class": "external-input form-control datepicker",
                "autocomplete": "off",
            }
        ),
    )
    argued_before.as_str_types = [SEARCH_TYPES.ORAL_ARGUMENT]

    #
    # Opinion fields
    #
    filed_after = FloorDateField(
        required=False,
        label="Filed After",
        widget=forms.TextInput(
            attrs={
                "placeholder": "MM/DD/YYYY",
                "class": "external-input form-control datepicker",
                "autocomplete": "off",
            }
        ),
    )
    filed_after.as_str_types = [SEARCH_TYPES.OPINION, SEARCH_TYPES.RECAP]
    filed_before = CeilingDateField(
        required=False,
        label="Filed Before",
        widget=forms.TextInput(
            attrs={
                "placeholder": "MM/DD/YYYY",
                "class": "external-input form-control datepicker",
                "autocomplete": "off",
            }
        ),
    )
    filed_before.as_str_types = [SEARCH_TYPES.OPINION, SEARCH_TYPES.RECAP]
    citation = forms.CharField(
        required=False,
        label="Citation",
        widget=forms.TextInput(
            attrs={
                "class": "external-input form-control",
                "autocomplete": "off",
            }
        ),
    )
    citation.as_str_types = [SEARCH_TYPES.OPINION]
    neutral_cite = forms.CharField(
        required=False,
        label="Neutral Citation",
        widget=forms.TextInput(
            attrs={
                "class": "external-input form-control",
                "autocomplete": "off",
            }
        ),
    )
    neutral_cite.as_str_types = [SEARCH_TYPES.OPINION]
    cited_gt = forms.IntegerField(
        required=False,
        label="Min Cites",
        initial=0,
        widget=forms.TextInput(
            attrs={
                "class": "external-input form-control",
                "autocomplete": "off",
            }
        ),
    )
    cited_gt.as_str_types = [SEARCH_TYPES.OPINION]
    cited_lt = forms.IntegerField(
        required=False,
        label="Max Cites",
        initial=100000,
        widget=forms.TextInput(
            attrs={
                "class": "external-input form-control",
                "autocomplete": "off",
            }
        ),
    )
    cited_lt.as_str_types = [SEARCH_TYPES.OPINION]

    #
    # Judge fields
    #
    name = forms.CharField(
        required=False,
        label="Name",
        widget=forms.TextInput(
            attrs={
                "class": "external-input form-control",
                "autocomplete": "off",
            }
        ),
    )
    name.as_str_types = [SEARCH_TYPES.PEOPLE]
    born_after = FloorDateField(
        required=False,
        label="Born After",
        widget=forms.TextInput(
            attrs={
                "placeholder": "MM/DD/YYYY",
                "class": "external-input form-control datepicker",
                "autocomplete": "off",
            }
        ),
    )
    born_after.as_str_types = [SEARCH_TYPES.PEOPLE]
    born_before = CeilingDateField(
        required=False,
        label="Born Before",
        widget=forms.TextInput(
            attrs={
                "placeholder": "MM/DD/YYYY",
                "class": "external-input form-control datepicker",
                "autocomplete": "off",
            }
        ),
    )
    born_before.as_str_types = [SEARCH_TYPES.PEOPLE]
    dob_city = forms.CharField(
        required=False,
        label="Birth City",
        widget=forms.TextInput(
            attrs={
                "class": "external-input form-control",
                "autocomplete": "off",
            }
        ),
    )
    dob_city.as_str_types = [SEARCH_TYPES.PEOPLE]
    dob_state = forms.ChoiceField(
        choices=[("", "---------")] + list(STATE_CHOICES),
        required=False,
        label="Birth State",
        widget=forms.Select(attrs={"class": "external-input form-control"}),
    )
    dob_state.as_str_types = [SEARCH_TYPES.PEOPLE]
    school = forms.CharField(
        required=False,
        label="School Attended",
        widget=forms.TextInput(
            attrs={
                "class": "external-input form-control",
                "autocomplete": "off",
            }
        ),
    )
    school.as_str_types = [SEARCH_TYPES.PEOPLE]
    appointer = forms.CharField(
        required=False,
        label="Appointed By",
        widget=forms.TextInput(
            attrs={
                "class": "external-input form-control",
                "autocomplete": "off",
            }
        ),
    )
    appointer.as_str_types = [SEARCH_TYPES.PEOPLE]
    selection_method = forms.ChoiceField(
        choices=[("", "---------")] + list(Position.SELECTION_METHODS),
        required=False,
        label="Selection Method",
        initial="None",
        widget=forms.Select(attrs={"class": "external-input form-control"}),
    )
    selection_method.as_str_types = [SEARCH_TYPES.PEOPLE]
    political_affiliation = forms.ChoiceField(
        choices=[("", "---------")]
        + list(PoliticalAffiliation.POLITICAL_PARTIES),
        required=False,
        label="Political Affiliation",
        initial="None",
        widget=forms.Select(attrs={"class": "external-input form-control"}),
    )
    political_affiliation.as_str_types = [SEARCH_TYPES.PEOPLE]

    def get_date_field_names(self):
        return {
            f_name.split("_")[0]
            for f_name, f in self.fields.items()
            if isinstance(f, DateField)
        }

    def __init__(self, *args, **kwargs):
        super(SearchForm, self).__init__(*args, **kwargs)
        """
        Normally we wouldn't need to use __init__ in a form object like
        this, however, since we are generating checkbox fields with dynamic
        names coming from the database, we need to interact directly with the
        fields dict.
        """
        courts = Court.objects.filter(in_use=True)
        for court in courts:
            self.fields["court_" + court.pk] = forms.BooleanField(
                label=court.short_name,
                required=False,
                initial=True,
                widget=forms.CheckboxInput(attrs={"checked": "checked"}),
            )

        for status in DOCUMENT_STATUSES:
            attrs = {}
            if status[1] == "Precedential":
                initial = True
                attrs.update({"checked": "checked"})
            else:
                initial = False
            new_field = forms.BooleanField(
                label=status[1],
                required=False,
                initial=initial,
                widget=forms.CheckboxInput(attrs=attrs),
            )
            new_field.as_str_types = [SEARCH_TYPES.OPINION]
            self.fields["stat_" + status[1]] = new_field

    # This is a particularly nasty area of the code due to several factors:
    #  1. Django doesn't have a good method of setting default values for
    #     bound forms. As a result, we set them here as part of a cleanup
    #     routine. This way a user can do a query for page=2, and still have
    #     all the correct defaults.
    #  2. In our search form, part of what we do is clean up the GET requests
    #     that the user sent. This is completed in clean_form(). This allows a
    #     user to be taught what better queries look like. To do this, we have
    #     to make a temporary variable in _clean_form() and assign it
    #     the values of the cleaned_data. The upshot of this is that most
    #     changes made here will also need to be made in _clean_form().
    #     Failure to do that will result in the query being processed correctly
    #     (search results are all good), but the form on the UI won't be
    #     cleaned up for the user, making things rather confusing.
    #  3. We do some cleanup work in search_utils.make_stats_variable(). The
    #     work that's done there is used to check or un-check the boxes in the
    #     sidebar, so if you tweak how they work you'll need to tweak this
    #     function.
    # In short: This is a nasty area. Comments this long are a bad sign for
    # the intrepid developer.
    def clean_q(self):
        """
        Cleans up various problems with the query:
         - lowercase --> camelCase
         - '|' --> ' OR '
        """
        q = self.cleaned_data["q"]

        # Fix fields to work in all lowercase
        sub_pairs = (
            # Blended
            ("casename", "caseName"),
            ("case_name", "caseName"),
            ("docketnumber", "docketNumber"),
            ("datefiled", "dateFiled"),
            ("suitnature", "suitNature"),
            # Opinions
            ("lexiscite", "lexisCite"),
            ("neutralcite", "neutralCite"),
            ("citecount", "citeCount"),
            # Oral Args
            ("dateargued", "dateArgued"),
            ("datereargued", "dateReargued"),
            # People
            ("DOD", "dod"),
            ("DOB", "dob"),
            # RECAP
            ("dateterminated", "dateTerminated"),
            ("jurydemand", "juryDemand"),
        )
        for bad, good in sub_pairs:
            q = re.sub(bad, good, q)

        # Make pipes work
        q = re.sub("\|", " OR ", q)

        return q

    def clean_order_by(self):
        """Sets the default order_by value if one isn't provided by the user."""
        if (
            self.cleaned_data["type"] == SEARCH_TYPES.OPINION
            or not self.cleaned_data["type"]
        ):
            if not self.cleaned_data["order_by"]:
                return self.fields["order_by"].initial
        elif self.cleaned_data["type"] == SEARCH_TYPES.ORAL_ARGUMENT:
            if not self.cleaned_data["order_by"]:
                return "dateArgued desc"
        elif self.cleaned_data["type"] == SEARCH_TYPES.PEOPLE:
            if not self.cleaned_data["order_by"]:
                return "name_reverse asc"
        return self.cleaned_data["order_by"]

    def clean_type(self):
        """Make sure that type has an initial value."""
        if not self.cleaned_data["type"]:
            return self.fields["type"].initial
        return self.cleaned_data["type"]

    def clean(self):
        """
        Handles validation fixes that need to be performed across fields.
        """
        cleaned_data = self.cleaned_data

        # 1. Make sure that the dates do this |--> <--| rather than <--| |-->
        for field_name in self.get_date_field_names():
            before = cleaned_data.get("%s_before" % field_name)
            after = cleaned_data.get("%s_after" % field_name)
            if before and after and (before < after):
                # The user is requesting dates like this: <--b  a-->. Switch
                # the dates so their query is like this: a-->   <--b
                cleaned_data["%s_before" % field_name] = after
                cleaned_data["%s_after" % field_name] = before

        # 2. Convert the value in the court field to the various court_* fields
        court_str = cleaned_data.get("court")
        if court_str:
            if " " in court_str:
                court_ids = court_str.split(" ")
            elif "," in court_str:
                court_ids = court_str.split(",")
            else:
                court_ids = [court_str]
            for court_id in court_ids:
                cleaned_data["court_%s" % court_id] = True

        # 3. Make sure that the user has selected at least one facet for each
        #    taxonomy. Note that this logic must be paralleled in
        #    search_utils.make_facet_variable
        court_bools = [
            v for k, v in cleaned_data.items() if k.startswith("court_")
        ]
        if not any(court_bools):
            # Set all facets to True
            for key in cleaned_data.keys():
                if key.startswith("court_"):
                    cleaned_data[key] = True

        stat_bools = [
            v for k, v in cleaned_data.items() if k.startswith("stat_")
        ]
        if not any(stat_bools):
            # Set everything to False...
            for key in cleaned_data.keys():
                if key.startswith("stat_"):
                    cleaned_data[key] = False
            # ...except precedential
            cleaned_data["stat_Precedential"] = True

        cleaned_data["_court_count"] = len(court_bools)
        cleaned_data["_stat_count"] = len(stat_bools)

        # 4. Strip any whitespace, otherwise it crashes Solr.
        for k, v in cleaned_data.items():
            if isinstance(v, basestring):
                cleaned_data[k] = v.strip()

        return cleaned_data

    def as_display_dict(self, court_count_human):
        """Generate a displayable dictionary of the search form

        This can be useful for displaying on the front end, or converting into
        a useful string. The dictionary looks like:

            {
              'Case name': 'Foo',
              'Query': 'bar',
            }

        :param court_count_human: The number of courts being queried or "All",
        if all courts are being queried.
        :returns A dictionary of the data
        """
        display_dict = OrderedDict({"Courts": court_count_human})
        search_type = self.data["type"]
        for field_name, field in self.fields.items():
            if not hasattr(field, "as_str_types"):
                continue
            if search_type in field.as_str_types:
                value = self.cleaned_data.get(field_name)
                if value:
                    if isinstance(field, ChoiceField):
                        choices = flatten_choices(self.fields[field_name])
                        value = dict(choices)[value]
                    display_dict[field.label] = value

        return display_dict

    def as_text(self, court_count_human):
        """Create a human-readable string representation of the search form"""
        crumbs = []
        for label, value in self.as_display_dict(court_count_human).items():
            crumbs.append(u"%s: %s" % (label, value))
        return u" › ".join(crumbs)
Example #5
0
class SearchForm(forms.Form):
    #
    # Blended fields
    #
    type = forms.ChoiceField(
        choices=TYPE_CHOICES,
        required=False,
        initial='o',
        widget=forms.RadioSelect(
            attrs={'class': 'external-input form-control'}))
    q = forms.CharField(required=False)
    case_name = forms.CharField(required=False,
                                label='Case Name',
                                initial='',
                                widget=forms.TextInput(
                                    attrs={
                                        'class': 'external-input form-control',
                                        'autocomplete': 'off',
                                        'tabindex': '202'
                                    }))
    judge = forms.CharField(required=False,
                            initial='',
                            widget=forms.TextInput(
                                attrs={
                                    'class': 'external-input form-control',
                                    'autocomplete': 'off',
                                    'tabindex': '203'
                                }))
    court = forms.CharField(required=False, widget=forms.HiddenInput())
    docket_number = forms.CharField(
        required=False,
        label='Docket Number',
        widget=forms.TextInput(
            attrs={
                'class': 'external-input form-control',
                'autocomplete': 'off',
                'tabindex': '226'
            }))

    #
    # Oral argument fields
    #
    argued_after = FloorDateField(
        required=False,
        label="Argued After",
        widget=forms.TextInput(
            attrs={
                'placeholder': 'YYYY-MM-DD',
                'class': 'external-input form-control',
                'autocomplete': 'off'
            }))
    argued_before = CeilingDateField(
        required=False,
        label="Argued Before",
        widget=forms.TextInput(
            attrs={
                'placeholder': 'YYYY-MM-DD',
                'class': 'external-input form-control',
                'autocomplete': 'off'
            }))

    #
    # Opinion fields
    #
    order_by = forms.ChoiceField(
        choices=OPINION_ORDER_BY_CHOICES,
        required=False,
        label='Result Ordering',
        initial='score desc',
        widget=forms.Select(attrs={
            'class': 'external-input form-control',
            'tabindex': '201'
        }))
    filed_after = FloorDateField(
        required=False,
        label='Filed After',
        widget=forms.TextInput(
            attrs={
                'placeholder': 'YYYY-MM-DD',
                'class': 'external-input form-control',
                'autocomplete': 'off',
                'tabindex': '220'
            }))
    filed_before = CeilingDateField(
        required=False,
        label='Filed Before',
        widget=forms.TextInput(
            attrs={
                'placeholder': 'YYYY-MM-DD',
                'class': 'external-input form-control',
                'autocomplete': 'off',
                'tabindex': '221'
            }))
    citation = forms.CharField(required=False,
                               widget=forms.TextInput(
                                   attrs={
                                       'class': 'external-input form-control',
                                       'autocomplete': 'off',
                                       'tabindex': '224'
                                   }))
    neutral_cite = forms.CharField(
        required=False,
        label='Neutral Citation',
        widget=forms.TextInput(
            attrs={
                'class': 'external-input form-control',
                'autocomplete': 'off',
                'tabindex': '225'
            }))
    cited_gt = forms.CharField(required=False,
                               label='Min Cites',
                               initial=0,
                               widget=forms.TextInput(
                                   attrs={
                                       'class': 'external-input form-control',
                                       'autocomplete': 'off',
                                       'tabindex': '222'
                                   }))
    cited_lt = forms.CharField(required=False,
                               label='Max Cites',
                               initial=20000,
                               widget=forms.TextInput(
                                   attrs={
                                       'class': 'external-input form-control',
                                       'autocomplete': 'off',
                                       'tabindex': '223'
                                   }))

    def __init__(self, *args, **kwargs):
        super(SearchForm, self).__init__(*args, **kwargs)
        """
        Normally we wouldn't need to use __init__ in a form object like
        this, however, since we are generating checkbox fields with dynamic
        names coming from the database, we need to interact directly with the
        fields dict.
        """
        courts = Court.objects.filter(in_use=True).values(
            'pk', 'short_name', 'jurisdiction', 'has_oral_argument_scraper')
        for court in courts:
            self.fields['court_' + court['pk']] = forms.BooleanField(
                label=court['short_name'],
                required=False,
                initial=True,
                widget=forms.CheckboxInput(attrs={'checked': 'checked'}))

        tabindex_i = 204
        for status in DOCUMENT_STATUSES:
            attrs = {'tabindex': str(tabindex_i)}
            if status[1] == 'Precedential':
                initial = True
                attrs.update({'checked': 'checked'})
            else:
                initial = False
            self.fields['stat_' + status[1]] = forms.BooleanField(
                label=status[1],
                required=False,
                initial=initial,
                widget=forms.CheckboxInput(attrs=attrs))
            tabindex_i += 1

    # This is a particularly nasty area of the code due to several factors:
    #  1. Django doesn't have a good method of setting default values for
    #     bound forms. As a result, we set them here as part of a cleanup
    #     routine. This way a user can do a query for page=2, and still have
    #     all the correct defaults.
    #  2. In our search form, part of what we do is clean up the GET requests
    #     that the user sent. This is completed in clean_form(). This allows a
    #     user to be taught what better queries look like. To do this, we have
    #     to make a temporary variable in _clean_opinion_form() and assign it
    #     the values of the cleaned_data. The upshot of this is that most
    #     changes made here will also need to be made in _clean_opinion_form().
    #     Failure to do that will result in the query being processed correctly
    #     (search results are all good), but the form on the UI won't be
    #     cleaned up for the user, making things rather confusing.
    #  3. We do some cleanup work in search_utils.make_stats_variable(). The
    #     work that's done there is used to check or un-check the boxes in the
    #     sidebar, so if you tweak how they work you'll need to tweak this
    #     function.
    # In short: This is a nasty area. Comments this long are a bad sign for
    # the intrepid developer.
    def clean_q(self):
        """
        Cleans up various problems with the query:
         - lowercase --> camelCase
         - '|' --> ' OR '
        """
        q = self.cleaned_data['q']

        # Fix fields to work in all lowercase
        sub_pairs = (
            ('casename', 'caseName'),
            ('lexiscite', 'lexisCite'),
            ('docketnumber', 'docketNumber'),
            ('neutralcite', 'neutralCite'),
            ('citecount', 'citeCount'),
            ('datefiled', 'dateFiled'),
            ('dateargued', 'dateArgued'),
        )
        for bad, good in sub_pairs:
            q = re.sub(bad, good, q)

        # Make pipes work
        q = re.sub('\|', ' OR ', q)

        return q

    def clean_order_by(self):
        """Sets the default order_by value if one isn't provided by the user."""
        if self.cleaned_data['type'] == 'o' or not \
                self.cleaned_data['type']:
            if not self.cleaned_data['order_by']:
                return self.fields['order_by'].initial
        elif self.cleaned_data['type'] == 'oa':
            if not self.cleaned_data['order_by']:
                return 'dateArgued desc'
        return self.cleaned_data['order_by']

    def clean_type(self):
        """Make sure that type has an initial value."""
        if not self.cleaned_data['type']:
            return self.fields['type'].initial
        return self.cleaned_data['type']

    def clean(self):
        """
        Handles validation fixes that need to be performed across fields.
        """
        cleaned_data = self.cleaned_data

        # 1. Make sure that the dates do this |--> <--| rather than <--| |-->
        before = cleaned_data.get('filed_before')
        after = cleaned_data.get('filed_after')
        if before and after:
            # Only do something if both fields are valid so far.
            if before < after:
                # The user is requesting dates like this: <--b  a-->. Switch
                # the dates so their query is like this: a-->   <--b
                cleaned_data['filed_before'] = after
                cleaned_data['filed_after'] = before

        # 2. Convert the value in the court field to the various court_* fields
        court_str = cleaned_data.get('court')
        if court_str:
            if ' ' in court_str:
                court_ids = court_str.split(' ')
            elif ',' in court_str:
                court_ids = court_str.split(',')
            else:
                court_ids = [court_str]
            for court_id in court_ids:
                cleaned_data['court_%s' % court_id] = True

        # 3. Make sure that the user has selected at least one facet for each
        #    taxonomy. Note that this logic must be paralleled in
        #    search_utils.make_facet_variable
        court_bools = [
            v for k, v in cleaned_data.items() if k.startswith('court_')
        ]
        if not any(court_bools):
            # Set all facets to True
            for key in cleaned_data.keys():
                if key.startswith('court_'):
                    cleaned_data[key] = True

        # 4. Strip any whitespace, otherwise it crashes Solr.
        for k, v in cleaned_data.items():
            if isinstance(v, basestring):
                cleaned_data[k] = v.strip()

        # Here we reset the defaults.
        stat_bools = [
            v for k, v in cleaned_data.items() if k.startswith('stat_')
        ]
        if not any(stat_bools):
            # Set everything to False...
            for key in cleaned_data.keys():
                if key.startswith('stat_'):
                    cleaned_data[key] = False
            # ...except precedential
            cleaned_data['stat_Precedential'] = True

        return cleaned_data