diff --git a/assets/agenda/components/AgendaFilters.tsx b/assets/agenda/components/AgendaFilters.tsx index 8ec98d689..73db0d52b 100644 --- a/assets/agenda/components/AgendaFilters.tsx +++ b/assets/agenda/components/AgendaFilters.tsx @@ -48,6 +48,7 @@ const renderFilter = { key="location" activeFilter={props.activeFilter} toggleFilter={props.toggleFilter} + locationEnabledOptions = {props.locationEnabledOptions} /> ) ), @@ -154,6 +155,7 @@ const mapStateToProps = (state: IAgendaState) => ({ locators: state.locators?.items || [], itemTypeFilterConfig: state.uiConfig.subnav?.item_type || {}, subnavConfig: state, + locationEnabledOptions: state.locationsFiltersOptions || {}, }); type StateProps = ReturnType; diff --git a/assets/agenda/components/LocationFilter.tsx b/assets/agenda/components/LocationFilter.tsx index 2dbf25529..15dbdf084 100644 --- a/assets/agenda/components/LocationFilter.tsx +++ b/assets/agenda/components/LocationFilter.tsx @@ -349,7 +349,7 @@ export class LocationFilter extends React.Component { )} onClick={() => this.onChange(item)} > - {gettext('{{ name }} (State, {{ country }})', { + {gettext('{{ name }} (State/Province, {{ country }})', { name: item.name, country: item.country, })} @@ -432,7 +432,7 @@ export class LocationFilter extends React.Component { render() { const activeFilter = get(this.props, 'activeFilter.location') || {}; const isActive = activeFilter.type != null; - + const isPlaceEnabled = this.props.locationEnabledOptions?.place; return (
{ )} -
{gettext('Places')}
- {this.state.results.places.length > 0 ? ( - this.state.results.places.map(this.renderRegionSearchResult) - ) : ( - + {isPlaceEnabled && ( + <> +
{gettext('Places')}
+ {this.state.results.places.length > 0 ? ( + this.state.results.places.map(this.renderRegionSearchResult) + ) : ( + + )} + )} )} @@ -547,4 +551,5 @@ export class LocationFilter extends React.Component { LocationFilter.propTypes = { activeFilter: PropTypes.object, toggleFilter: PropTypes.func, + locationEnabledOptions: PropTypes.object, }; diff --git a/assets/agenda/reducers.ts b/assets/agenda/reducers.ts index 685c7e43e..f4a484434 100644 --- a/assets/agenda/reducers.ts +++ b/assets/agenda/reducers.ts @@ -336,7 +336,8 @@ export default function agendaReducer(state: IAgendaState = initialState, action hasAgendaFeaturedItems: action.agendaData.has_agenda_featured_items || false, userFolders: action.agendaData.user_folders, companyFolders: action.agendaData.company_folders, - dateFilters: action.agendaData.date_filters || [] + dateFilters: action.agendaData.date_filters || [], + locationsFiltersOptions: action.agendaData.location_filters_options || {}, }; } diff --git a/assets/interfaces/agenda.ts b/assets/interfaces/agenda.ts index b5eb5cabc..225ee1f9b 100644 --- a/assets/interfaces/agenda.ts +++ b/assets/interfaces/agenda.ts @@ -314,6 +314,14 @@ export interface IAgendaState { errors?: {[field: string]: Array}; loadingAggregations?: boolean; dateFilters?: IDateFilters; + locationsFiltersOptions?:ILocationFilterOptions; +} + +export interface ILocationFilterOptions { + city?: boolean; + state?: boolean; + country?: boolean; + place?: boolean; } export type AgendaGetState = () => IAgendaState; diff --git a/newsroom/agenda/views.py b/newsroom/agenda/views.py index fa92ba063..2b6f2a954 100644 --- a/newsroom/agenda/views.py +++ b/newsroom/agenda/views.py @@ -143,6 +143,7 @@ def get_view_data() -> Dict: "user_folders": get_user_folders(user, "agenda") if user else [], "company_folders": get_company_folders(company, "agenda") if company else [], "date_filters": app.config.get("AGENDA_TIME_FILTERS", []), + "location_filters_options": app.config.get("CALENDAR_LOCATIONS_FILTER_OPTIONS", {}), } @@ -315,6 +316,7 @@ def related_wire_items(wire_id): @blueprint.route("/agenda/search_locations") @login_required def search_locations(): + location_filter_options = app.config.get("CALENDAR_LOCATIONS_FILTER_OPTIONS", {}) query = request.args.get("q") or "" apply_filters = len(query) > 0 @@ -341,37 +343,44 @@ def gen_agg_terms(field: str): "size": 1000, } - es_query = { - "size": 0, - "aggs": { - "city_search_country": { - "terms": gen_agg_terms("address.country"), - "aggs": { - "city_search_state": { - "terms": gen_agg_terms("address.state"), - "aggs": { - "cities": { - "terms": gen_agg_terms("address.city"), - }, + # Start with an empty aggregation structure + es_query = {"size": 0, "aggs": {}} + + # Conditionally add aggregations based on configuration + if location_filter_options.get("city", True): + es_query["aggs"]["city_search_country"] = { + "terms": gen_agg_terms("address.country"), + "aggs": { + "city_search_state": { + "terms": gen_agg_terms("address.state"), + "aggs": { + "cities": { + "terms": gen_agg_terms("address.city"), }, }, }, }, - "state_search_country": { - "terms": gen_agg_terms("address.country"), - "aggs": { - "states": { - "terms": gen_agg_terms("address.state"), - }, + } + + if location_filter_options.get("state", True): + es_query["aggs"]["state_search_country"] = { + "terms": gen_agg_terms("address.country"), + "aggs": { + "states": { + "terms": gen_agg_terms("address.state"), }, }, - "countries": { - "terms": gen_agg_terms("address.country"), - }, - "places": {"terms": gen_agg_terms("name")}, - }, - } + } + if location_filter_options.get("country", True): + es_query["aggs"]["countries"] = { + "terms": gen_agg_terms("address.country"), + } + + if location_filter_options.get("place", True): + es_query["aggs"]["places"] = {"terms": gen_agg_terms("name")} + + # Add a query filter if one is provided if apply_filters: es_query["query"] = { "bool": { @@ -391,62 +400,84 @@ def gen_agg_terms(field: str): }, } - es_query["aggs"]["city_search"] = { - "filter": gen_agg_filter("address.city"), - "aggs": {"city_search_country": es_query["aggs"].pop("city_search_country")}, - } - es_query["aggs"]["state_search"] = { - "filter": gen_agg_filter("address.state"), - "aggs": {"state_search_country": es_query["aggs"].pop("state_search_country")}, - } - es_query["aggs"]["country_search"] = { - "filter": gen_agg_filter("address.country"), - "aggs": {"countries": es_query["aggs"].pop("countries")}, - } - es_query["aggs"]["place_search"] = { - "filter": gen_agg_filter("name"), - "aggs": {"places": es_query["aggs"].pop("places")}, - } + # Conditionally add filtered aggregations based on enabled options + if location_filter_options.get("city", True): + es_query["aggs"]["city_search"] = { + "filter": gen_agg_filter("address.city"), + "aggs": {"city_search_country": es_query["aggs"].pop("city_search_country")}, + } + + if location_filter_options.get("state", True): + es_query["aggs"]["state_search"] = { + "filter": gen_agg_filter("address.state"), + "aggs": {"state_search_country": es_query["aggs"].pop("state_search_country")}, + } + + if location_filter_options.get("country", True): + es_query["aggs"]["country_search"] = { + "filter": gen_agg_filter("address.country"), + "aggs": {"countries": es_query["aggs"].pop("countries")}, + } + + if location_filter_options.get("place", True): + es_query["aggs"]["place_search"] = { + "filter": gen_agg_filter("name"), + "aggs": {"places": es_query["aggs"].pop("places")}, + } + # Execute the query req = ParsedRequest() req.args = {"source": json.dumps(es_query)} service = get_resource_service("agenda") cursor = service.internal_get(req, {}) aggs = cursor.hits.get("aggregations") or {} + # Process results based on the aggregations enabled in config regions = [] - for country_bucket in (aggs.get("city_search_country") or aggs["city_search"]["city_search_country"])["buckets"]: - country_name = country_bucket["key"] - for state_bucket in country_bucket["city_search_state"]["buckets"]: - state_name = state_bucket["key"] - for city_bucket in state_bucket["cities"]["buckets"]: + + if location_filter_options.get("city", True): + for country_bucket in (aggs.get("city_search_country") or aggs["city_search"]["city_search_country"])[ + "buckets" + ]: + country_name = country_bucket["key"] + for state_bucket in country_bucket["city_search_state"]["buckets"]: + state_name = state_bucket["key"] + for city_bucket in state_bucket["cities"]["buckets"]: + regions.append( + {"name": city_bucket["key"], "country": country_name, "state": state_name, "type": "city"} + ) + + if location_filter_options.get("state", True): + for country_bucket in (aggs.get("state_search_country") or aggs["state_search"]["state_search_country"])[ + "buckets" + ]: + country_name = country_bucket["key"] + for state_bucket in country_bucket["states"]["buckets"]: regions.append( - {"name": city_bucket["key"], "country": country_name, "state": state_name, "type": "city"} + { + "name": state_bucket["key"], + "country": country_name, + "type": "state", + } ) - for country_bucket in (aggs.get("state_search_country") or aggs["state_search"]["state_search_country"])["buckets"]: - country_name = country_bucket["key"] - for state_bucket in country_bucket["states"]["buckets"]: + if location_filter_options.get("country", True): + for country_bucket in (aggs.get("countries") or aggs["country_search"]["countries"])["buckets"]: regions.append( { - "name": state_bucket["key"], - "country": country_name, - "type": "state", + "name": country_bucket["key"], + "type": "country", } ) - for country_bucket in (aggs.get("countries") or aggs["country_search"]["countries"])["buckets"]: - regions.append( - { - "name": country_bucket["key"], - "type": "country", - } - ) + places = [] + if location_filter_options.get("place", True): + places = [bucket["key"] for bucket in (aggs.get("places") or aggs["place_search"]["places"])["buckets"]] return ( { "regions": regions, - "places": [bucket["key"] for bucket in (aggs.get("places") or aggs["place_search"]["places"])["buckets"]], + "places": places, }, 200, ) diff --git a/newsroom/web/default_settings.py b/newsroom/web/default_settings.py index 9092c011a..044361447 100644 --- a/newsroom/web/default_settings.py +++ b/newsroom/web/default_settings.py @@ -857,3 +857,15 @@ #: .. versionadded:: 2.8 #: COVERAGE_REQUEST_EMAIL_CC_CURRENT_USER = False + +#: Calendar Location Filter options config +#: +#: .. versionadded: 2.9 +#: + +CALENDAR_LOCATIONS_FILTER_OPTIONS = { + "city": True, + "state": True, + "country": True, + "place": True, +}