Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

create autocomplete endpoint #24

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ class GazetteSearchResponse(BaseModel):
gazettes: List[GazetteItem]


class CityAutocompleteItem(BaseModel):
territory_id: str
territory_name: str
state_code: str


class CityAutocompleteResponse(BaseModel):
suggestions: List[CityAutocompleteItem]


def trigger_gazettes_search(
territory_id: str = None,
since: date = None,
Expand Down Expand Up @@ -128,6 +138,32 @@ async def get_gazettes_by_territory_id(
):
return trigger_gazettes_search(territory_id, since, until, keywords, offset, size)

@app.get(
"/cities/autocomplete",
response_model=CityAutocompleteResponse,
name="Autocomplete city",
description="Autocomplete city by term",
response_model_exclude_unset=True,
response_model_exclude_none=True,
)
async def autocomplete_city(
term: str = Query(
0, title="Term", description="Term to autocomplete",
),
size: Optional[int] = Query(
10,
title="Number of item to return",
description="Define the number of item should be returned",
),
):
def convert(item):
return CityAutocompleteItem(
territory_id=item.territory_id,
territory_name=item.territory_name,
state_code=item.state_code)

suggestions = list(map(convert, app.gazettes.autocomplete_city(term, size)))
return CityAutocompleteResponse(suggestions=suggestions)

def configure_api_app(gazettes: GazetteAccessInterface, api_root_path=None):
if not isinstance(gazettes, GazetteAccessInterface):
Expand Down
33 changes: 32 additions & 1 deletion database/elasticsearch.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import elasticsearch

from gazettes import GazetteDataGateway, Gazette
from gazettes import GazetteDataGateway, Gazette, CityAutocomplete


class ElasticSearchDataMapper(GazetteDataGateway):
Expand Down Expand Up @@ -120,6 +120,37 @@ def get_gazettes(
self.create_list_with_gazette_objects(gazettes["hits"]["hits"]),
)

def autocomplete_city(
self,
term=None,
size=10,
):
query = { "query": { "match_phrase_prefix": { "territory_name": { "query": term } } },
"size": 0,
"aggs": {
"territory_and_state": {
"composite": {
"size": size,
"sources": [
{ "territory_id": { "terms": { "field": "territory_id.keyword" } } },
{ "territory_name": { "terms": { "field": "territory_name.keyword" } } },
{ "state_code": { "terms": { "field": "state_code.keyword" } } }
]
}
}
}
}
search_result = self._es.search(body=query, index=self._index)

def convert(document):
return CityAutocomplete(
document["key"]["territory_id"],
document["key"]["territory_name"],
document["key"]["state_code"],
)

return list(map(convert, search_result["aggregations"]["territory_and_state"]["buckets"]))


def create_elasticsearch_data_mapper(
host: str = None, index: str = None
Expand Down
1 change: 1 addition & 0 deletions gazettes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
GazetteRequest,
GazetteDataGateway,
Gazette,
CityAutocomplete,
create_gazettes_interface,
)
45 changes: 45 additions & 0 deletions gazettes/gazette_access.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ def get_gazettes(
Method to get the gazette from storage
"""

@abc.abstractmethod
def autocomplete_city(
self, term=None, size: int = 10
):
"""
Method to autocomplete city name
"""


class GazetteAccessInterface(abc.ABC):
"""
Expand Down Expand Up @@ -75,6 +83,9 @@ def get_gazettes(self, filters: GazetteRequest = None):
)
return (total_number_gazettes, [vars(gazette) for gazette in gazettes])

def autocomplete_city(self, term=None, size: int = 10):
return self._data_gateway.autocomplete_city(term, size)


class Gazette:
"""
Expand Down Expand Up @@ -130,6 +141,40 @@ def __eq__(self, other):
def __repr__(self):
return f"Gazette({self.checksum}, {self.territory_id}, {self.date}, {self.url}, {self.territory_name}, {self.state_code}, {self.edition}, {self.is_extra_edition})"

class CityAutocomplete:
"""
Item to represent a city
"""

def __init__(
self,
territory_id,
territory_name,
state_code,
):
self.territory_id = territory_id
self.territory_name = territory_name
self.state_code = state_code

def __hash__(self):
return hash(
(
self.territory_id,
self.territory_name,
self.state_code,
)
)

def __eq__(self, other):
return (
self.territory_id == other.territory_id
and self.territory_name == other.territory_name
and self.state_code == other.state_code
)

def __repr__(self):
return f"CityAutocomplete({self.territory_id}, {self.territory_name}, {self.state_code})"


def create_gazettes_interface(data_gateway: GazetteDataGateway):
if not isinstance(data_gateway, GazetteDataGateway):
Expand Down