Skip to content

Commit

Permalink
Merge b960fec into bdc876e
Browse files Browse the repository at this point in the history
  • Loading branch information
mfrodl committed Nov 6, 2016
2 parents bdc876e + b960fec commit 3c0973f
Show file tree
Hide file tree
Showing 2 changed files with 188 additions and 0 deletions.
2 changes: 2 additions & 0 deletions did/plugins/__init__.py
Expand Up @@ -10,6 +10,8 @@
+----------+-----+
| header | 000 |
+----------+-----+
| google | 050 |
+----------+-----+
| nitrate | 100 |
+----------+-----+
| bugzilla | 200 |
Expand Down
186 changes: 186 additions & 0 deletions did/plugins/google.py
@@ -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),
]

0 comments on commit 3c0973f

Please sign in to comment.