11"""Provides form rendering and validation for the advanced search feature."""
22
3+ import calendar
4+ import re
35from datetime import date , datetime
46from typing import Callable , Optional , List , Any
57
1113from wtforms import widgets
1214
1315from arxiv import taxonomy
14-
15- from search .controllers .util import doesNotStartWithWildcard , stripWhiteSpace
16+ from search .domain import DateRange
17+ from search .controllers .util import does_not_start_with_wildcard , \
18+ strip_white_space , has_balanced_quotes
1619
1720
1821class MultiFormatDateField (DateField ):
@@ -21,10 +24,12 @@ class MultiFormatDateField(DateField):
2124 def __init__ (self , label : Optional [str ] = None ,
2225 validators : Optional [List [Callable ]] = None ,
2326 formats : List [str ] = ['%Y-%m-%d %H:%M:%S' ],
27+ default_upper_bound : bool = False ,
2428 ** kwargs : Any ) -> None :
2529 """Override to change ``format: str`` to ``formats: List[str]``."""
2630 super (DateField , self ).__init__ (label , validators , ** kwargs )
2731 self .formats = formats
32+ self .default_upper_bound = default_upper_bound
2833
2934 def _value (self ) -> str :
3035 if self .raw_data :
@@ -39,7 +44,17 @@ def process_formdata(self, valuelist: List[str]) -> None:
3944 self .data : Optional [date ]
4045 for fmt in self .formats :
4146 try :
42- self .data = datetime .strptime (date_str , fmt ).date ()
47+ adj_date = datetime .strptime (date_str , fmt ).date ()
48+ if self .default_upper_bound :
49+ if not re .search (r'%[Bbm]' , fmt ):
50+ # when month does not appear in matching format
51+ adj_date = adj_date .replace (month = 12 , day = 31 )
52+ elif not re .search ('%d' , fmt ):
53+ # when day does not appear in matching format
54+ last_day = calendar .monthrange (adj_date .year ,
55+ adj_date .month )[1 ]
56+ adj_date = adj_date .replace (day = last_day )
57+ self .data = adj_date
4358 return
4459 except ValueError :
4560 continue
@@ -52,8 +67,9 @@ class FieldForm(Form):
5267
5368 # pylint: disable=too-few-public-methods
5469
55- term = StringField ("Search term..." , filters = [stripWhiteSpace ],
56- validators = [doesNotStartWithWildcard ])
70+ term = StringField ("Search term..." , filters = [strip_white_space ],
71+ validators = [does_not_start_with_wildcard ,
72+ has_balanced_quotes ])
5773 operator = SelectField ("Operator" , choices = [
5874 ('AND' , 'AND' ), ('OR' , 'OR' ), ('NOT' , 'NOT' )
5975 ], default = 'AND' )
@@ -115,7 +131,8 @@ def yearInBounds(form: Form, field: DateField) -> None:
115131 return None
116132
117133 start_of_time = date (year = 1991 , month = 1 , day = 1 )
118- if field .data < start_of_time or field .data > date .today ():
134+ upper_limit = date .today ().replace (year = date .today ().year + 1 )
135+ if field .data < start_of_time or field .data > upper_limit :
119136 raise ValidationError ('Not a valid publication year' )
120137
121138
@@ -146,9 +163,24 @@ class DateForm(Form):
146163 to_date = MultiFormatDateField (
147164 'to' ,
148165 validators = [validators .Optional (), yearInBounds ],
149- formats = ['%Y-%m-%d' , '%Y-%m' , '%Y' ]
166+ formats = ['%Y-%m-%d' , '%Y-%m' , '%Y' ],
167+ default_upper_bound = True
150168 )
151169
170+ SUBMITTED_ORIGINAL = DateRange .SUBMITTED_ORIGINAL
171+ SUBMITTED_CURRENT = DateRange .SUBMITTED_CURRENT
172+ ANNOUNCED = DateRange .ANNOUNCED
173+ DATE_TYPE_CHOICES = [
174+ (SUBMITTED_CURRENT , 'Submission date (most recent)' ),
175+ (SUBMITTED_ORIGINAL , 'Submission date (original)' ),
176+ (ANNOUNCED , 'Announcement date' ),
177+ ]
178+ date_type = RadioField ('Apply to' , choices = DATE_TYPE_CHOICES ,
179+ default = SUBMITTED_CURRENT ,
180+ description = "You may filter on either submission"
181+ " date or announcement date. Note that announcement"
182+ " date supports only year and month granularity." )
183+
152184 def validate_filter_by (self , field : RadioField ) -> None :
153185 """Ensure that related fields are filled."""
154186 if field .data == 'specific_year' and not self .data .get ('year' ):
@@ -175,6 +207,7 @@ class AdvancedSearchForm(Form):
175207 classification = FormField (ClassificationForm )
176208 date = FormField (DateForm )
177209 size = SelectField ('results per page' , default = 50 , choices = [
210+ ('25' , '25' ),
178211 ('50' , '50' ),
179212 ('100' , '100' ),
180213 ('200' , '200' )
@@ -187,3 +220,11 @@ class AdvancedSearchForm(Form):
187220 ('' , 'Relevance' )
188221 ], validators = [validators .Optional ()], default = '-announced_date_first' )
189222 include_older_versions = BooleanField ('Include older versions of papers' )
223+
224+ HIDE_ABSTRACTS = 'hide'
225+ SHOW_ABSTRACTS = 'show'
226+
227+ abstracts = RadioField ('Abstracts' , choices = [
228+ (SHOW_ABSTRACTS , 'Show abstracts' ),
229+ (HIDE_ABSTRACTS , 'Hide abstracts' )
230+ ], default = SHOW_ABSTRACTS )
0 commit comments