patternpythonMinor
Small Python script using Google and Slack APIs
Viewed 0 times
scriptapisgooglesmallusingpythonandslack
Problem
I've recently been working on a small Python script that I was tasked to do at my new job. It will basically handle the rotation and notification of the on call engineer by populating a Google Calendar with the schedule as well as pinging a Slack channel I created. The script will be run weekly as a cron job and will pull the list from a Google Sheet. This is my first time using both Google and Slack's APIs, so please let me know of any ways in which I could improve my code. Thanks!
```
# pylint: disable=E1101
"""
Tracks and rotates the on call engineer at REDACTED.
Uses Slack and Google's API to obtain access to the services.
"""
from os import mkdir, path, environ
from os.path import join, dirname
from datetime import datetime, timedelta
import argparse
from httplib2 import Http
from apiclient import discovery
from oauth2client.file import Storage
from oauth2client import client
from oauth2client import tools
from dotenv import load_dotenv
from slackclient import SlackClient
# try getting flags (if any, not sure what this does)
FLAGS = None
# scopes and google stuff
SCOPES = 'https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/spreadsheets'
CLIENT_SECRET_FILE = 'client_secrets.json'
APPLICATION_NAME = 'On Call Tracker'
SHEET_ID = 'REDACTED'
CALENDAR_ID = 'REDACTED'
# load variables from .env file
DOTENV_PATH = join(dirname(__file__), '.env')
load_dotenv(DOTENV_PATH)
# set slack authentication constants
SLACK_TOKEN = environ.get("SLACK_TOKEN")
SLACK_CLIENT = SlackClient(SLACK_TOKEN)
def get_credentials():
"""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.
Returns:
Credentials, the obtained credential.
"""
home_dir = path.expanduser('~')
credential_dir = path.join(home_dir, '.credentials')
if not path.exists(credential_dir):
mkdir(credential_dir)
credential_pa
```
# pylint: disable=E1101
"""
Tracks and rotates the on call engineer at REDACTED.
Uses Slack and Google's API to obtain access to the services.
"""
from os import mkdir, path, environ
from os.path import join, dirname
from datetime import datetime, timedelta
import argparse
from httplib2 import Http
from apiclient import discovery
from oauth2client.file import Storage
from oauth2client import client
from oauth2client import tools
from dotenv import load_dotenv
from slackclient import SlackClient
# try getting flags (if any, not sure what this does)
FLAGS = None
# scopes and google stuff
SCOPES = 'https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/spreadsheets'
CLIENT_SECRET_FILE = 'client_secrets.json'
APPLICATION_NAME = 'On Call Tracker'
SHEET_ID = 'REDACTED'
CALENDAR_ID = 'REDACTED'
# load variables from .env file
DOTENV_PATH = join(dirname(__file__), '.env')
load_dotenv(DOTENV_PATH)
# set slack authentication constants
SLACK_TOKEN = environ.get("SLACK_TOKEN")
SLACK_CLIENT = SlackClient(SLACK_TOKEN)
def get_credentials():
"""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.
Returns:
Credentials, the obtained credential.
"""
home_dir = path.expanduser('~')
credential_dir = path.join(home_dir, '.credentials')
if not path.exists(credential_dir):
mkdir(credential_dir)
credential_pa
Solution
- Compability with Python 3 is a good idea, in particular the
print
statement should be a function call instead.
path.joincan also be directly done on the path to save a line.
- If variables are only used once, maybe just inline them
(e.g.
DOTENV_PATH).- Some of the file names and other strings are hardcoded, some are in
constants at the top - why the distinction? Worse, e.g.
'A2:C'isn't a constant but was written out twice!
return Noneat the end of a function is the same as not having a
return statement.-
In
ping_slack first splitting, then zipping the values is morework than just having a single dictionary comprehension, e.g.:
channels_dict = {chan['name']: chan['id'] for chan in chan_list}However given that only a single entry is used anyway this does more
work than just saving that single value, but looks perhaps more
elegant.
Lastly the docstring is wrong, the
chan_list argument is a list,not a
string.More general things:
- Some of the function names, like
del_all_eventsare shortened for no
real reason, whereas
rotate_names_in_sheet is long and descriptive.I'd choose either style and stick with it.
- In
send_message, theuserargument should be a bit more
descriptive, perhaps
as_user to signify that it's basically a flag,not a "user" object. Oh right, that's also that the Slack API uses,
so why not adhere to that.
- The error checking and the way that different functionality is split
up between components could be improved. E.g.
rotate_names_in_sheetuses a default value (
[]) for the values value, but in the mainfunction there'll be an index out of range exception if that case
happens at any time - it'd be better if that situation is handled
earlier, at the point that it occurs, i.e. if value is missing an
exception should be raised (or alternatively the
main function fixedsuch that it can cope with the missing name).
- Similarly a couple of expressions, e.g.
service.events, come up more
than once, perhaps some refactoring such that the code around that is
more succinct would be good.
- The date calculations are somewhat opaque. Using a better API would
make it probably more readable, but I don't have a suggestion as to
which library that could be done with.
Code Snippets
channels_dict = {chan['name']: chan['id'] for chan in chan_list}Context
StackExchange Code Review Q#152952, answer score: 3
Revisions (0)
No revisions yet.