From 442c0bbffc5c372c7ec3510914968f75ab6e4a4f Mon Sep 17 00:00:00 2001 From: Terry Truong Date: Thu, 5 Jan 2023 17:13:03 +1100 Subject: Add partially-complete search modal For now, use placeholder code for jumping to a search result. Add db index for case-insensitive event title searching. Make type=info requests accept title instead of ID (for looking up a searched-for title). Make EventInfo contain an Event field (for showing info in search suggestions). Add titleToEvent map in App, for use by SearchModal to look up searched-for titles. Add keyboard shortcuts to open/close search and info modals. --- backend/hist_data/gen_events_data.py | 1 + backend/histplorer.py | 47 ++++++++++++++++++++---------------- backend/tests/test_histplorer.py | 14 ++++++++--- 3 files changed, 37 insertions(+), 25 deletions(-) (limited to 'backend') diff --git a/backend/hist_data/gen_events_data.py b/backend/hist_data/gen_events_data.py index ee2fdc1..118b40c 100755 --- a/backend/hist_data/gen_events_data.py +++ b/backend/hist_data/gen_events_data.py @@ -178,6 +178,7 @@ def genData(wikidataFile: str, offsetsFile: str, dbFile: str, nProcs: int) -> No dbCur.execute('CREATE TABLE events (id INT PRIMARY KEY, title TEXT UNIQUE, ' \ 'start INT, start_upper INT, end INT, end_upper INT, fmt INT, ctg TEXT)') dbCur.execute('CREATE INDEX events_id_start_idx ON events(id, start)') + dbCur.execute('CREATE INDEX events_title_nocase_idx ON events(title COLLATE NOCASE)') if nProcs == 1: with bz2.open(wikidataFile, mode='rb') as file: for lineNum, line in enumerate(file, 1): diff --git a/backend/histplorer.py b/backend/histplorer.py index 86d0e2a..c20116e 100755 --- a/backend/histplorer.py +++ b/backend/histplorer.py @@ -13,7 +13,7 @@ Expected HTTP query parameters: range=-13000. means '13000 BC onwards' - scale: With type=events, specifies a date scale - incl: With type=events, specifies an event to include, as an event ID -- event: With type=info, specifies the event ID to get info for +- event: With type=info, specifies the event title to get info for - input: With type=sugg, specifies a search string to suggest for - limit: With type=events or type=sugg, specifies the max number of results - ctg: With type=events or type=sugg, specifies an event category to restrict results to @@ -91,14 +91,15 @@ class ImgInfo: return str(self.__dict__) class EventInfo: """ Used when responding to type=info requests """ - def __init__(self, desc: str, wikiId: int, imgInfo: ImgInfo): + def __init__(self, event: Event, desc: str, wikiId: int, imgInfo: ImgInfo): + self.event = event self.desc = desc self.wikiId = wikiId self.imgInfo = imgInfo # Used in unit testing def __eq__(self, other): return isinstance(other, EventInfo) and \ - (self.desc, self.wikiId, self.imgInfo) == (other.desc, other.wikiId, other.imgInfo) + (self.event, self.desc, self.wikiId, self.imgInfo) == (other.event, other.desc, other.wikiId, other.imgInfo) def __repr__(self): return str(self.__dict__) class SuggResponse: @@ -109,7 +110,7 @@ class SuggResponse: # Used in unit testing def __eq__(self, other): return isinstance(other, SuggResponse) and \ - (set(self.suggs), self.hasMore) == (set(other.suggs), other.hasMore) + (self.suggs, self.hasMore) == (other.suggs, other.hasMore) def __repr__(self): return str(self.__dict__) @@ -268,8 +269,8 @@ def eventEntryToResults( if n is not None: newDates[i] = dbDateToHistDate(n, fmt, i < 2) # - return Event(eventId, title, newDates[0], newDates[1], newDates[2], newDates[3], ctg, 0, pop) #return Event(eventId, title, newDates[0], newDates[1], newDates[2], newDates[3], ctg, imageId, pop) + return Event(eventId, title, newDates[0], newDates[1], newDates[2], newDates[3], ctg, 0, pop) def lookupUnitCounts( start: HistDate | None, end: HistDate | None, scale: int, dbCur: sqlite3.Cursor) -> dict[int, int] | None: # Build query @@ -294,23 +295,26 @@ def handleInfoReq(params: dict[str, str], dbCur: sqlite3.Cursor): if 'event' not in params: print('INFO: No \'event\' parameter for type=info request', file=sys.stderr) return None - try: - eventId = int(params['event']) - except ValueError: - print('INFO: Invalid value for \'event\' parameter', file=sys.stderr) - return None - return lookupEventInfo(eventId, dbCur) -def lookupEventInfo(eventId: int, dbCur: sqlite3.Cursor) -> EventInfo | None: - """ Look up an event with given ID, and return a descriptive EventInfo """ - return EventInfo(f'DESC {eventId}', 1, ImgInfo(f'http://example.org/{eventId}', 'license', 'artist', 'credit')) - #query = 'SELECT desc, wiki_id, url, license, artist, credit FROM events' \ + return lookupEventInfo(params['event'], dbCur) +def lookupEventInfo(eventTitle: str, dbCur: sqlite3.Cursor) -> EventInfo | None: + """ Look up an event with given title, and return a descriptive EventInfo """ + return EventInfo( + Event(1, eventTitle, HistDate(True, 2000, 10, 1), None, None, None, 'event', 10, 100), + f'DESC for {eventTitle}', 1, ImgInfo(f'http://example.org/{eventTitle}', 'license', 'artist', 'credit')) + #query = \ + # 'SELECT events.id, title, start, start_upper, end, end_upper, fmt, ctg, images.id, pop.pop, ' \ + # ' descs.desc, descs.wiki_id, ' \ + # ' images.url, images.license, images.artist, images.credit FROM events' \ + # ' INNER JOIN pop ON events.id = pop.id' \ # ' INNER JOIN descs ON events.id = descs.id' \ - # ' INNER JOIN event_imgs ON events.id = event_imgs.id INNER JOIN images ON event_imgs.img_id = images.id' \ - # ' WHERE events.id = ?' - #row = dbCur.execute(query, (eventId,)).fetchone() + # ' INNER JOIN event_imgs ON events.id = event_imgs.id' \ + # ' INNER JOIN images ON event_imgs.img_id = images.id' \ + # ' WHERE events.title = ? COLLATE NOCASE' + #row = dbCur.execute(query, (eventTitle,)).fetchone() #if row is not None: - # desc, wikiId, url, license, artist, credit = row - # return EventInfo(desc, wikiId, ImgInfo(url, license, artist, credit)) + # event = eventEntryToResults(row[:10]) + # desc, wikiId, url, license, artist, credit = row[10:] + # return EventInfo(event, desc, wikiId, ImgInfo(url, license, artist, credit)) #else: # return None @@ -350,12 +354,13 @@ def lookupSuggs(searchStr: str, resultLimit: int, ctg: str | None, dbCur: sqlite for (title,) in dbCur.execute(query, params): suggs.append(title) # If insufficient results, try substring search - existing = set(suggs) if len(suggs) < tempLimit: + existing = set(suggs) params = ['%' + searchStr + '%'] + ([ctg] if ctg is not None else []) for (title,) in dbCur.execute(query, params): if title not in existing: suggs.append(title) if len(suggs) == tempLimit: break + # return SuggResponse(suggs[:resultLimit], len(suggs) > resultLimit) diff --git a/backend/tests/test_histplorer.py b/backend/tests/test_histplorer.py index b3f7cb4..fcaafb5 100644 --- a/backend/tests/test_histplorer.py +++ b/backend/tests/test_histplorer.py @@ -124,12 +124,18 @@ class TestHandleReq(unittest.TestCase): ]) self.assertEqual(response.unitCounts, {-2000: 1, 1900: 2, 1990: 1}) def test_info_req(self): - response = handleReq(self.dbFile, {'QUERY_STRING': 'type=info&event=3'}) + response = handleReq(self.dbFile, {'QUERY_STRING': 'type=info&event=event%20three'}) self.assertEqual(response, - EventInfo('desc three', 300, ImgInfo('example.com/3', 'cc-by-sa 3.0', 'artist three', 'credits three'))) - response = handleReq(self.dbFile, {'QUERY_STRING': 'type=info&event=4'}) + EventInfo( + Event(3, 'event three', HistDate(True, 1990, 10, 10), HistDate(True, 2000, 10, 10), None, None, + 'discovery', 30, 0), + 'desc three', 300, ImgInfo('example.com/3', 'cc-by-sa 3.0', 'artist three', 'credits three'))) + response = handleReq(self.dbFile, {'QUERY_STRING': 'type=info&event=event%20four'}) self.assertEqual(response, - EventInfo('desc four', 400, ImgInfo('example.com/2', 'cc-by', 'artist two', 'credits two'))) + EventInfo( + Event(4, 'event four', HistDate(False, -2000, 10, 10), None, HistDate(False, 1, 10, 10), None, + 'event', 20, 1000), + 'desc four', 400, ImgInfo('example.com/2', 'cc-by', 'artist two', 'credits two'))) def test_sugg_req(self): response = handleReq(self.dbFile, {'QUERY_STRING': 'type=sugg&input=event t'}) self.assertEqual(response, SuggResponse(['event two', 'event three'], False)) -- cgit v1.2.3