Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
188 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
# coding: utf-8 | ||
|
||
""" | ||
Google Apps stats such as sent emails or attended events | ||
Config example:: | ||
[google] | ||
type = google | ||
client_id = <client_id> | ||
client_secret = <client_secret> | ||
To access data via Google API, you will need to create credentials | ||
(``client_id`` and ``client_secret``) first. This can be done at: | ||
https://console.developers.google.com/apis/credentials | ||
""" | ||
|
||
import os | ||
import httplib2 | ||
|
||
from apiclient import discovery | ||
from oauth2client import tools | ||
from oauth2client.file import Storage | ||
from oauth2client.client import OAuth2WebServerFlow | ||
|
||
from did.base import Config, ReportError, CONFIG | ||
from did.utils import log, pretty, listed, split | ||
from did.stats import Stats, StatsGroup | ||
|
||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
# Constants | ||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
SCOPE = "https://www.googleapis.com/auth/calendar.readonly" | ||
REDIRECT_URI = "urn:ietf:wg:oauth:2.0:oob" | ||
|
||
CREDENTIAL_DIR = CONFIG | ||
CREDENTIAL_FILE = "google-api-credentials.json" | ||
CREDENTIAL_PATH = os.path.join(CREDENTIAL_DIR, CREDENTIAL_FILE) | ||
|
||
USER_AGENT = "did" | ||
|
||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
# Google Apps | ||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
class Google(object): | ||
""" Google Apps common code """ | ||
|
||
def __init__(self, client_id, client_secret): | ||
""" | ||
Gets valid user credentials from storage. | ||
If nothing has been stored, or if the stored credentials are invalid, | ||
the OAuth2 flow is completed to obtain the new credentials. | ||
""" | ||
if not os.path.exists(CREDENTIAL_DIR): | ||
os.makedirs(CREDENTIAL_DIR) | ||
|
||
storage = Storage(CREDENTIAL_PATH) | ||
credentials = storage.get() | ||
|
||
if not credentials or credentials.invalid: | ||
flow = OAuth2WebServerFlow( | ||
client_id=client_id, | ||
client_secret=client_secret, | ||
scope=SCOPE, | ||
redirect_uri=REDIRECT_URI) | ||
flow.user_agent = USER_AGENT | ||
|
||
# Do not parse did command-line options by OAuth client | ||
flags = tools.argparser.parse_args(args=[]) | ||
credentials = tools.run_flow(flow, storage, flags) | ||
|
||
self.http = credentials.authorize(httplib2.Http()) | ||
|
||
class GoogleCalendar(Google): | ||
""" Google Calendar functions """ | ||
def __init__(self, client_id, client_secret): | ||
super(GoogleCalendar, self).__init__(client_id, client_secret) | ||
self.service = discovery.build("calendar", "v3", http=self.http) | ||
|
||
def events(self, **kwargs): | ||
""" Fetch events meeting specified criteria """ | ||
events_result = self.service.events().list(**kwargs).execute() | ||
return [Event(event) for event in events_result.get("items", [])] | ||
|
||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
# Event | ||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
class Event(object): | ||
""" Googe Calendar Event """ | ||
def __init__(self, dict): | ||
""" Create Event object from dictionary returned by Google API """ | ||
self.__dict__ = dict | ||
|
||
def __unicode__(self): | ||
""" String representation """ | ||
return self.summary if hasattr(self, "summary") else "(No title)" | ||
|
||
def __getitem__(self, name): | ||
return self.__dict__.get(name, None) | ||
|
||
def created_by(self, email): | ||
""" Check if user created the event """ | ||
return self["creator"]["email"] == email | ||
|
||
def organized_by(self, email): | ||
""" Check if user created the event """ | ||
return self["organizer"]["email"] == email | ||
|
||
def attended_by(self, email): | ||
""" Check if user attended the event """ | ||
for attendee in self["attendees"] or []: | ||
if (attendee["email"] == email | ||
and attendee["responseStatus"] == "accepted"): | ||
return True | ||
return False | ||
|
||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
# Stats | ||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
class GoogleStatsBase(Stats): | ||
""" Base class containing common code """ | ||
def __init__(self, option, name=None, parent=None): | ||
super(GoogleStatsBase, self).__init__( | ||
option=option, name=name, parent=parent) | ||
try: | ||
self.since = self.options.since.datetime.isoformat() + "Z" | ||
self.until = self.options.until.datetime.isoformat() + "Z" | ||
except AttributeError: | ||
log.debug("Failed to initialize time range, skipping") | ||
self._events = None | ||
|
||
@property | ||
def events(self): | ||
""" All events in calendar within specified time range """ | ||
if self._events is None: | ||
self._events = self.parent.calendar.events( | ||
calendarId="primary", singleEvents=True, orderBy="startTime", | ||
timeMin=self.since, timeMax=self.until) | ||
return self._events | ||
|
||
class GoogleEventsOrganized(GoogleStatsBase): | ||
""" Events organized """ | ||
def fetch(self): | ||
log.info(u"Searching for events attended by {0}".format(self.user)) | ||
self.stats = [ | ||
event for event in self.events | ||
if event.organized_by(self.user.email) | ||
] | ||
|
||
class GoogleEventsAttended(GoogleStatsBase): | ||
""" Events attended """ | ||
def fetch(self): | ||
log.info(u"Searching for events attended by {0}".format(self.user)) | ||
self.stats = [ | ||
event for event in self.events | ||
if event.attended_by(self.user.email) | ||
] | ||
|
||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
# Google Stats Group | ||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
class GoogleStatsGroup(StatsGroup): | ||
""" Google stats group """ | ||
|
||
# Default order | ||
order = 50 | ||
|
||
def __init__(self, option, name=None, parent=None, user=None): | ||
StatsGroup.__init__(self, option, name, parent, user) | ||
config = dict(Config().section(option)) | ||
client_id = config["client_id"] | ||
client_secret = config["client_secret"] | ||
self.calendar = GoogleCalendar(client_id, client_secret) | ||
self.stats = [ | ||
GoogleEventsOrganized( | ||
option=option + "-events-organized", parent=self), | ||
GoogleEventsAttended( | ||
option=option + "-events-attended", parent=self), | ||
] |