From 80b3ecd88dcf1c5bbd3197f85b8903f95f506731 Mon Sep 17 00:00:00 2001 From: Nick Haynes Date: Tue, 23 May 2017 15:37:32 -0400 Subject: [PATCH 01/22] pep8-ify --- wordsmith/wordsmith.py | 87 ++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 38 deletions(-) diff --git a/wordsmith/wordsmith.py b/wordsmith/wordsmith.py index 3f417bb..32365cd 100644 --- a/wordsmith/wordsmith.py +++ b/wordsmith/wordsmith.py @@ -12,47 +12,58 @@ from .project import Project from .exceptions import ProjectSlugError + class Wordsmith(object): - """ - Constructs a :class:`Wordsmith ` object. - - :param api_key: API key from Wordsmith. - :param base_url: (optional) String representing the base URL for the Wordsmith API per documentation at http://wordsmith.readme.io/v1/docs - :param user_agent: (optional) String representing the user agent that should be sent with each API request - """ - - def __init__(self, api_key, **kwargs): - self.projects = [] - self.config = Configuration(api_key) - if 'base_url' in kwargs: - self.config.base_url = kwargs['base_url'] - if 'user_agent' in kwargs: - self.config.user_agent = kwargs['user_agent'] - response = requests.get(self.config.base_url + '/projects', headers=self.config.get_headers()) - if response.status_code == 200: - for project_data in json.loads(response.text)['data']: - self.projects.append(Project(project_data['name'], project_data['slug'], project_data['schema'], project_data['templates'], self.config)) - - def project(self, slug): """ - Get a Wordsmith project by slug + Constructs a :class:`Wordsmith ` object. - :param slug: String representing the slug of the Wordmsith project - :return: :class:`Wordsmith ` - :rtype: wordsmith.Project + :param api_key: API key from Wordsmith. + :param base_url: (optional) String representing the base URL for the + Wordsmith API per documentation at + http://wordsmith.readme.io/v1/docs + :param user_agent: (optional) String representing the user agent that + should be sent with each API request """ - matches = [project for project in self.projects if project.slug == slug] - if len(matches) == 1: - return matches[0] - else: - raise ProjectSlugError('{} is not a valid project slug.'.format(slug)) - def find_project(self, name): - """ - Find Wordsmith projects by project name + def __init__(self, api_key, **kwargs): + self.projects = [] + self.config = Configuration(api_key) + if 'base_url' in kwargs: + self.config.base_url = kwargs['base_url'] + if 'user_agent' in kwargs: + self.config.user_agent = kwargs['user_agent'] + response = requests.get(self.config.base_url + '/projects', + headers=self.config.get_headers()) + if response.status_code == 200: + for project_data in json.loads(response.text)['data']: + self.projects.append(Project(project_data['name'], + project_data['slug'], + project_data['schema'], + project_data['templates'], + self.config)) - :param name: String representing the name of the Wordsmith project - :return: :class:`list` - :rtype: list - """ - return [project for project in self.projects if project.name == name] + def project(self, slug): + """ + Get a Wordsmith project by slug + + :param slug: String representing the slug of the Wordmsith project + :return: :class:`Wordsmith ` + :rtype: wordsmith.Project + """ + matches = [project for project in self.projects + if project.slug == slug] + if len(matches) == 1: + return matches[0] + else: + raise ProjectSlugError( + '{} is not a valid project slug.'.format(slug)) + + def find_project(self, name): + """ + Find Wordsmith projects by project name + + :param name: String representing the name of the Wordsmith project + :return: :class:`list` + :rtype: list + """ + return [project for project in self.projects if project.name == name] From f5388008d1b6533788b5abcdf5be4b238b3f5a64 Mon Sep 17 00:00:00 2001 From: Nick Haynes Date: Tue, 23 May 2017 15:49:50 -0400 Subject: [PATCH 02/22] pep8-ify the rest --- wordsmith/__init__.py | 14 ++-- wordsmith/configuration.py | 47 +++++++------ wordsmith/exceptions.py | 55 ++++++++------- wordsmith/narrative.py | 141 +++++++++++++++++++++---------------- wordsmith/project.py | 71 ++++++++++--------- wordsmith/template.py | 71 +++++++++++-------- 6 files changed, 225 insertions(+), 174 deletions(-) diff --git a/wordsmith/__init__.py b/wordsmith/__init__.py index 5499376..ce8469d 100644 --- a/wordsmith/__init__.py +++ b/wordsmith/__init__.py @@ -1,11 +1,13 @@ -__title__ = 'wordsmith' -__version__ = '0.5' -__author__ = 'John Hegele' -__copyright__ = 'Copyright 2016 Automated Insights' - from .wordsmith import Wordsmith from .configuration import Configuration from .project import Project from .template import Template from .narrative import Narrative, Batch -from .exceptions import (ProjectSlugError, TemplateSlugError, NarrativeGenerateError) +from .exceptions import (ProjectSlugError, + TemplateSlugError, + NarrativeGenerateError) + +__title__ = 'wordsmith' +__version__ = '0.5' +__author__ = 'John Hegele' +__copyright__ = 'Copyright 2016 Automated Insights' diff --git a/wordsmith/configuration.py b/wordsmith/configuration.py index 91414e1..a4274f5 100644 --- a/wordsmith/configuration.py +++ b/wordsmith/configuration.py @@ -6,28 +6,35 @@ """ from . import __version__ -class Configuration(object): - """ - Constructs a :class:`Wordsmith ` object. - :param api_key: API key from Wordsmith. - :param base_url: (optional) String representing the base URL for the Wordsmith API per documentation at http://wordsmith.readme.io/v1/docs - :param user_agent: (optional) String representing the user agent that should be sent with each API request +class Configuration(object): """ + Constructs a :class:`Wordsmith ` object. - DEFAULT_URL = 'https://api.automatedinsights.com/v1' - DEFAULT_USER_AGENT = 'PythonSDK/' + __version__ + :param api_key: API key from Wordsmith. + :param base_url: (optional) String representing the base URL for the + Wordsmith API per documentation at + http://wordsmith.readme.io/v1/docs + :param user_agent: (optional) String representing the user agent that + should be sent with each API request + """ - def __init__(self, api_key, **kwargs): - self.api_key = api_key - self.base_url = kwargs['base_url'] if 'base_url' in kwargs else self.DEFAULT_URL - self.user_agent = kwargs['user_agent'] if 'user_agent' in kwargs else self.DEFAULT_USER_AGENT + DEFAULT_URL = 'https://api.automatedinsights.com/v1' + DEFAULT_USER_AGENT = 'PythonSDK/' + __version__ - def get_headers(self): - """ - Format user agent and auth values as a dictionary for use in GET/POST requests to the API - """ - return { - 'User-Agent' : self.user_agent, - 'Authorization' : 'Bearer ' + self.api_key - } + def __init__(self, api_key, **kwargs): + self.api_key = api_key + self.base_url = kwargs['base_url'] if 'base_url' in kwargs\ + else self.DEFAULT_URL + self.user_agent = kwargs['user_agent'] if 'user_agent' in kwargs\ + else self.DEFAULT_USER_AGENT + + def get_headers(self): + """ + Format user agent and auth values as a dictionary for use in GET/POST + requests to the API + """ + return { + 'User-Agent': self.user_agent, + 'Authorization': 'Bearer ' + self.api_key + } diff --git a/wordsmith/exceptions.py b/wordsmith/exceptions.py index 40937f2..30ae05b 100644 --- a/wordsmith/exceptions.py +++ b/wordsmith/exceptions.py @@ -5,34 +5,41 @@ This module contains Wordsmith's custom exceptions. """ + class ProjectSlugError(ValueError): - """An invalid project slug was passed.""" + """An invalid project slug was passed.""" + + def __init__(self, msg): + self.msg = msg - def __init__(self, msg): - self.msg = msg class TemplateSlugError(ValueError): - """An invalid template slug was passed.""" + """An invalid template slug was passed.""" + + def __init__(self, msg): + self.msg = msg - def __init__(self, msg): - self.msg = msg class NarrativeGenerateError(Exception): - """The Wordsmith platform responded with an error code when attempting to generate narrative.""" - - def __init__(self, response, data): - """Initialize with the HTTP response object""" - self.http_status_code = response.status_code - self.http_reason = response.reason - self.data = data - try: - self.details = [str(e['detail']) for e in response.json()['errors']] - self._details_reported = True - except KeyError: - self.details = ['Wordsmith reported an error but no details were provided.'] - self._details_reported = False - self.msg = '\nError generating narrative.' + \ - '\nHTTP Status Code: {}'.format(self.http_status_code) + \ - '\nHTTP Reason: {}'.format(self.http_reason) + \ - '\nNumber of errors reported by Wordsmith: {}'.format(len(self.details) if self._details_reported else 0) - super(NarrativeGenerateError, self).__init__(self.msg) + """The Wordsmith platform responded with an error code when attempting to + generate narrative.""" + + def __init__(self, response, data): + """Initialize with the HTTP response object""" + self.http_status_code = response.status_code + self.http_reason = response.reason + self.data = data + try: + self.details = [str(e['detail']) + for e in response.json()['errors']] + self._details_reported = True + except KeyError: + self.details =\ + ['Wordsmith reported an error but no details were provided.'] + self._details_reported = False + self.msg = '\nError generating narrative.' + \ + '\nHTTP Status Code: {}'.format(self.http_status_code) + \ + '\nHTTP Reason: {}'.format(self.http_reason) + \ + '\nNumber of errors reported by Wordsmith: {}'.\ + format(len(self.details) if self._details_reported else 0) + super(NarrativeGenerateError, self).__init__(self.msg) diff --git a/wordsmith/narrative.py b/wordsmith/narrative.py index 9a954fb..671c8d7 100644 --- a/wordsmith/narrative.py +++ b/wordsmith/narrative.py @@ -11,74 +11,93 @@ from multiprocessing.dummy import Pool from .exceptions import NarrativeGenerateError + class Narrative(object): - """ - Constructs a :class:`Wordsmith ` object. + """ + Constructs a :class:`Wordsmith ` object. + + :param project_slug: String representing the slug of the parent project of + this narrative + :param template_slug: String representing the slug of the parent template + of this narrative + :param data: Dictionary representation of the row data to be passed to the + Wordsmith platform + :param config: wordsmith.Configuration object containing configuration + details + """ - :param project_slug: String representing the slug of the parent project of this narrative - :param template_slug: String representing the slug of the parent template of this narrative - :param data: Dictionary representation of the row data to be passed to the Wordsmith platform - :param config: wordsmith.Configuration object containing configuration details - """ + def __init__(self, project_slug, template_slug, data, config): + self.project_slug = project_slug + self.template_slug = template_slug + self.data = data + self._config = config + self.post_url = '{}/projects/{}/templates/{}/outputs'\ + .format(self._config.base_url, + self.project_slug, + self.template_slug) + headers = self._config.get_headers() + headers['Content-Type'] = 'application/json' + for header, value in six.iteritems(data): + if value is None: + data[header] = '' + ws_data = { + 'data': data + } + response = requests.post(self.post_url, + data=json.dumps(ws_data), + headers=headers) + if response.status_code == 200: + self.text = json.loads(response.text)['data']['content'] + else: + self.text = None + raise NarrativeGenerateError(response, data) - def __init__(self, project_slug, template_slug, data, config): - self.project_slug = project_slug - self.template_slug = template_slug - self.data = data - self._config = config - self.post_url = '{}/projects/{}/templates/{}/outputs'.format(self._config.base_url, self.project_slug, self.template_slug) - headers = self._config.get_headers() - headers['Content-Type'] = 'application/json' - for header, value in six.iteritems(data): - if value is None: - data[header] = '' - ws_data = { - 'data' : data - } - response = requests.post(self.post_url, data=json.dumps(ws_data), headers=headers) - if response.status_code == 200: - self.text = json.loads(response.text)['data']['content'] - else: - self.text = None - raise NarrativeGenerateError(response, data) + def __str__(self): + return self.text - def __str__(self): - return self.text class Batch(object): - """ - Constructs a :class:`Wordsmith ` object. + """ + Constructs a :class:`Wordsmith ` object. - :param project_slug: String representing the slug of the parent project of this narrative - :param template_slug: String representing the slug of the parent template of this narrative - :param data_list: List of dictionary representations of the row data to be passed to the Wordsmith platform - :param config: wordsmith.Configuration object containing configuration details - """ + :param project_slug: String representing the slug of the parent project of + this narrative + :param template_slug: String representing the slug of the parent template + of this narrative + :param data_list: List of dictionary representations of the row data to be + passed to the Wordsmith platform + :param config: wordsmith.Configuration object containing configuration + details + """ - def __init__(self, project_slug, template_slug, data_list, config): - self.project_slug = project_slug - self.template_slug = template_slug - self.data_list = data_list - self._config = config - self.break_on_error = False - self.pool_size = 8 - self.narratives = [] - self.errors = [] + def __init__(self, project_slug, template_slug, data_list, config): + self.project_slug = project_slug + self.template_slug = template_slug + self.data_list = data_list + self._config = config + self.break_on_error = False + self.pool_size = 8 + self.narratives = [] + self.errors = [] - def _generate_narrative(self, data): - narrative = None - try: - narrative = Narrative(self.project_slug, self.template_slug, data, self._config) - except NarrativeGenerateError as e: - if self.break_on_error: - raise e - else: - self.errors.append(e) - return narrative + def _generate_narrative(self, data): + narrative = None + try: + narrative = Narrative(self.project_slug, + self.template_slug, + data, + self._config) + except NarrativeGenerateError as e: + if self.break_on_error: + raise e + else: + self.errors.append(e) + return narrative - def generate(self): - self._index = 0 - thread_pool = Pool(self.pool_size) - self.narratives = thread_pool.map(self._generate_narrative, self.data_list) - thread_pool.close() - thread_pool.join() + def generate(self): + self._index = 0 + thread_pool = Pool(self.pool_size) + self.narratives = thread_pool.map(self._generate_narrative, + self.data_list) + thread_pool.close() + thread_pool.join() diff --git a/wordsmith/project.py b/wordsmith/project.py index 937396c..85ae271 100644 --- a/wordsmith/project.py +++ b/wordsmith/project.py @@ -7,37 +7,44 @@ from .template import Template from .exceptions import TemplateSlugError + class Project(object): - def __init__(self, name, slug, schema, templates, config): - self._config = config - self.templates = [] - self.name = name - self.slug = slug - self.schema = schema - for template_data in templates: - self.templates.append(Template(self.slug, template_data['name'], template_data['slug'], self._config)) - - def template(self, slug): - """ - Get a Wordsmith template by slug - - :param slug: String representing the slug of the Wordmsith template - :return: :class:`Wordsmith