Skip to content

Commit

Permalink
Loop through the API and improve search/sort.
Browse files Browse the repository at this point in the history
Now, a request is almost guaranteed to fill to maxResults, if
there is also a nextPageToken.
  • Loading branch information
AlexanderOtavka committed Jan 5, 2016
1 parent 004c9c3 commit 3a2d1ab
Show file tree
Hide file tree
Showing 12 changed files with 96 additions and 58 deletions.
2 changes: 2 additions & 0 deletions api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""The endpoints server."""

from __future__ import division, print_function

import endpoints

from calendarsapi import CalendarsAPI
Expand Down
2 changes: 2 additions & 0 deletions api/authutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
subject to copyright by Google.
"""

from __future__ import division, print_function

import os
import json

Expand Down
2 changes: 2 additions & 0 deletions api/calendarsapi.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""API for managing calendars."""

from __future__ import division, print_function

import logging

import endpoints
Expand Down
44 changes: 29 additions & 15 deletions api/eventsapi.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""API for managing events."""

from __future__ import division, print_function

import logging

import endpoints
Expand Down Expand Up @@ -112,13 +114,6 @@ def filter_and_update_events(unfiltered_events, starred_event_ids,
chosen.append(event)
return chosen

@staticmethod
def sort_and_search(events, search):
if search:
return searchutils.event_keyword_chron_search(events, search)
else:
return searchutils.event_chron_sort(events)

@endpoints.method(messages.EVENT_SEARCH_RESOURCE, messages.EventCollection,
http_method="GET", path="/calendars/{calendarId}/events")
def list(self, request):
Expand Down Expand Up @@ -184,25 +179,44 @@ def list(self, request):
else:
extra_starred_ids = []

# Initial sort and search, before checking the length
events = self.sort_and_search(starred_events, request.search)
events = starred_events[:]
""":type: list[messages.EventProperties]"""

if request.search:
# Initial search, before checking the length
events = searchutils.event_keyword_search(events, request.search)

events += cached_events

# TODO: make this a for loop to 10, and last add search/sort to it
if len(events) < request.maxResults:
# TODO: handle excluded recurring events better
for _ in range(10):
if len(events) >= request.maxResults:
break

# Get event list from the google api
api_events, gapi_next_page_token = gapiutils.get_events(
service, request.calendarId, request.timeZone,
gapi_next_page_token, request.maxResults)

events += self.filter_and_update_events(
api_events = self.filter_and_update_events(
api_events, starred_event_ids, calendar_key, request.hidden)

# Sort and search again after adding api events
events = self.sort_and_search(events, request.search)
# TODO: if gapi_next_page_token is None: break
if request.search:
# Search again after adding more api events
api_events = searchutils.event_keyword_search(api_events,
request.search)

events += api_events

if gapi_next_page_token is None:
break

# Do a final sort after adding all api events
if request.search:
events = searchutils.event_keyword_chron_sort(events,
request.search)
else:
events = searchutils.event_chron_sort(events)

if len(events) >= request.maxResults:
# Save extra for later
Expand Down
2 changes: 2 additions & 0 deletions api/gapiutils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Tools for getting data from the Google Calendar API."""

from __future__ import division, print_function

import httplib
from datetime import datetime, tzinfo

Expand Down
2 changes: 2 additions & 0 deletions api/garbagecollect.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Clear out old or unbound datastore entities."""

from __future__ import division, print_function

import logging

from google.appengine.ext import ndb
Expand Down
2 changes: 2 additions & 0 deletions api/messages.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Endpoints messages."""

from __future__ import division, print_function

from datetime import datetime

from protorpc import messages, message_types
Expand Down
2 changes: 2 additions & 0 deletions api/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Datastore models."""

from __future__ import division, print_function

import hashlib

from google.appengine.ext import ndb
Expand Down
10 changes: 7 additions & 3 deletions api/publicapi.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""API for accessing public calendars and events."""

from __future__ import division, print_function

import endpoints
from protorpc import remote
from oauth2client.appengine import AppAssertionCredentials
Expand Down Expand Up @@ -64,13 +66,15 @@ def events_list(self, request):
authutils.CALENDAR_API_VERSION,
AppAssertionCredentials(authutils.SERVICE_ACCOUNT_SCOPES)
)
events = gapiutils.get_events(service, request.calendarId,
request.timeZone, request.pageToken)
events, next_page_token = gapiutils.get_events(
service, request.calendarId, request.timeZone,
request.pageToken, request.maxResults)

# Sort and search
search = request.search
if search:
events = searchutils.event_keyword_chron_search(events, search)
events = searchutils.event_keyword_search(events, search)
events = searchutils.event_keyword_chron_sort(events, search)
else:
events = searchutils.event_chron_sort(events)

Expand Down
2 changes: 2 additions & 0 deletions api/redirect.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""The endpoints server."""

from __future__ import division, print_function

import webapp2

__author__ = "Alexander Otavka"
Expand Down
82 changes: 42 additions & 40 deletions api/searchutils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Tools to help with searching and sorting API data."""

from __future__ import division, print_function

from messages import EventProperties, CalendarProperties

__author__ = "Alexander Otavka"
Expand All @@ -12,31 +14,31 @@ def __init__(self):
"Insufficient matches found for data item.")


def _get_event_kw_score(event, keywords, narrow=False):
def _get_event_kw_score(event, keywords, narrow):
"""
Get a relevance score for an event based on keyword matches.
:param EventProperties event: Event to be scored.
:param str keywords: Search terms separated by spaces.
:param bool narrow:
If true, throw NullSearchError for insufficient keyword matches.
:type event: EventProperties
:type keywords: str
:param bool narrow: If true, throw NullSearchError for insufficient keyword
matches.
:rtype: float
:raise NullSearchError:
If narrow=True and insufficient keyword matches are found.
:raise NullSearchError: If narrow is True and insufficient keyword matches
are found.
"""
event_string_data = event.name
# TODO: make search algorithm less bad
event_string_data = event.name.lower()
search_set = set(keywords.split())
matches = 0.0
for keyword in search_set:
if keyword in event_string_data:
if keyword.lower() in event_string_data:
matches += 1.0
if narrow and matches < len(search_set) // 2:
if narrow and (not matches or matches < len(search_set) // 2):
raise NullSearchError()
return matches


def _get_calendar_kw_score(calendar, keywords, narrow=False):
def _get_calendar_kw_score(calendar, keywords, narrow):
"""
Get a relevance score for a calendar based on keyword matches.
Expand Down Expand Up @@ -98,24 +100,6 @@ def calendar_id_score(c):
return c.calendarId


def event_chronological_order():
return [event_starred, event_start_date, event_alpha_score, event_id_score]


def event_kw_chron_order(kw, narrow):
return [event_starred, event_kw_score(kw, narrow), event_start_date,
event_alpha_score, event_id_score]


def calendar_kw_alpha_order(kw, narrow):
return [calendar_kw_score(kw, narrow), calendar_alpha_score,
calendar_id_score]


def calendar_alpha_order():
return [calendar_alpha_score, calendar_id_score]


def search(search_list, order):
"""
Search and sort search_list based on tuple of order functions.
Expand All @@ -134,46 +118,64 @@ def search(search_list, order):
except NullSearchError:
continue
sorted_list = sorted(sorted_list)
return list(zip(*sorted_list)[-1])
if sorted_list:
return list(zip(*sorted_list)[-1])
else:
return []


def event_keyword_search(event_list, keywords):
"""
Search exclusively by keyword order, and narrow results.
:type event_list: list[EventProperties]
:type keywords: str
:rtype: list[EventProperties]
"""
return search(event_list, [event_kw_score(keywords, True)])


def event_keyword_chron_search(event_list, keywords):
def event_keyword_chron_sort(event_list, keywords):
"""
Convenience function, search with event_kw_chron_order.
Sort by keyword matches, then by start date, putting starred first.
:type event_list: list[EventProperties]
:type keywords: str
:rtype: list[EventProperties]
"""
return search(event_list, event_kw_chron_order(keywords, True))
return search(event_list, [event_starred, event_kw_score(keywords, False),
event_start_date, event_alpha_score,
event_id_score])


def event_chron_sort(event_list):
"""
Convenience function, search with event_chronological_order.
Sort events in chronological order, starred first.
:type event_list: list[EventProperties]
:rtype: list[EventProperties]
"""
return search(event_list, event_chronological_order())
return search(event_list, [event_starred, event_start_date,
event_alpha_score, event_id_score])


def calendar_keyword_alpha_search(calendar_list, keywords):
"""
Convenience function, search with calendar_kw_alpha_order.
Search and narrow by keyword matches, then alphabetical order.
:type calendar_list: list[CalendarProperties]
:type keywords: str
:rtype: list[CalendarProperties]
"""
return search(calendar_list, calendar_kw_alpha_order(keywords, True))
return search(calendar_list, [calendar_kw_score(keywords, True),
calendar_alpha_score, calendar_id_score])


def calendar_alpha_sort(calendar_list):
"""
Convenience function, search with calendar_alpha_order.
Sort calendars in alphabetical order.
:type calendar_list: list[CalendarProperties]
:rtype: list[CalendarProperties]
"""
return search(calendar_list, calendar_alpha_order())
return search(calendar_list, [calendar_alpha_score, calendar_id_score])
2 changes: 2 additions & 0 deletions api/ticktockapi.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""TickTock API definition."""

from __future__ import division, print_function

import endpoints

__author__ = "Alexander Otavka"
Expand Down

0 comments on commit 3a2d1ab

Please sign in to comment.