Source code for spinchimp

# -*- coding: utf-8 -*-

import re
import urllib
import urllib2

from spinchimp import exceptions as ex


[docs]class SpinChimp(object): """A class representing the Spin Chimp API (http://spinchimp.com/api). All articles must be in UTF-8 encoding! """ URL = 'http://api.spinchimp.com/{method}?' """URL for invoking the API""" TIMEOUT = 10 DEFAULT_PARAMS_SPIN = { 'quality': '4', 'posmatch': '3', 'protectedterms': '', 'rewrite': '0', 'phraseignorequality': '0', 'spinwithinspin': '0', 'spinwithinhtml': '0', 'applyinstantunique': '0', 'fullcharset': '0', 'spintidy': '0', 'tagprotect': '', 'maxspindepth': '0', } def __init__(self, email, apikey, aid=''): """AID is Application ID or application name""" self._email = email self._apikey = apikey self._aid = aid
[docs] def _get_param_value(self, param_name, params, def_params=DEFAULT_PARAMS_SPIN): """ Returns parameter value or use default. """ if param_name in params: return params[param_name] elif param_name in def_params: return def_params[param_name] else: raise ex.WrongParameterName(param_name)
[docs] def _value_has(self, param, values, params): """ Raise WrongParameterVal if value of param is not in values. """ val = self._get_param_value(param, params) if val not in values: raise ex.WrongParameterVal(param, val)
[docs] def _value_is_int(self, param, params): """ Raise WrongParameterVal if value of param is not integer. """ val = self._get_param_value(param, params) try: int(val) except ValueError: raise ex.WrongParameterVal(param, val)
[docs] def _validate(self, params): """ Checks every single parameter and raise error on wrong key or value. """ # remove entries with None value for i, j in params.iteritems(): if j is None: del(i) self._value_has('quality', ['1', '2', '3', '4', '5'], params) self._value_has('posmatch', ['0', '1', '2', '3', '4'], params) self._value_has('rewrite', ['0', '1'], params) self._value_has('phraseignorequality', ['0', '1'], params) self._value_has('spinwithinspin', ['0', '1'], params) self._value_has('spinwithinhtml', ['0', '1'], params) self._value_has('applyinstantunique', ['0', '1'], params) self._value_has('fullcharset', ['0', '1'], params) self._value_has('spintidy', ['0', '1'], params) self._value_has('maxspindepth', ['0', '1'], params) return True
[docs] def unspun(self, text, dontincludeoriginal=0, reorderparagraphs=0): """ Generates an unspun doc from one with spintax. Optionally reorders paragraphs and removes original word. :param text: text in spintax format :type text: string :param dontincludeoriginal: 0 (False) or 1 (True) :type dontincludeoriginal: integer :param reorderparagraphs: 0 (False) or 1 (True) :type reorderparagraphs: integer :return: unique text :rtype: dictionary """ params = { 'dontincludeoriginal': str(dontincludeoriginal), 'reorderparagraphs': str(reorderparagraphs) } self._value_has('dontincludeoriginal', ['0', '1'], params) self._value_has('reorderparagraphs', ['0', '1'], params) response = self._send_request( method='GenerateSpin', text=text, params=params ) return response
[docs] def word_density(self, text, minlength=3): """ Calculates the word densities of words and phrases in the article. :param text: original text :type text: string :param minlength: minimum length :type minlength: integer :return: words as keys in dictionary and percents as values :rtype: dictionary """ params = {'minlength': str(minlength)} self._value_is_int('minlength', params) response = self._send_request( method='CalcWordDensity', text=text, params=params ) return dict([atr.split(',') for atr in response.split('|')])
@staticmethod
[docs] def test_connection(): """ Static method that checks server status. The server returns 'OK' on successful connection. """ urldata = SpinChimp.URL.format(method='TestConnection') req = urllib2.Request(urldata, data='') try: response = urllib2.urlopen(req, timeout=SpinChimp.TIMEOUT) except urllib2.URLError as e: raise ex.NetworkError(str(e)) return response.read()
[docs] def quota_all(self): """ The server returns: daily limit, remaining daily limit, extended quota and bulk quota in dictionary for this account. """ response = self._send_request( method='QueryStats', text='', params={'simple': '0'} ) return dict([atr.split(',') for atr in response.split('|')])
[docs] def quota_left_total(self): """ The server returns remaining query times of this account. """ return self._send_request( method='QueryStats', text='', params={'simple': '1'} )
[docs] def text_with_spintax(self, text, params=None): """ Return processed spun text with spintax. :param text: original text that needs to be changed :type text: string :param params: parameters to pass along with the request :type params: dictionary :return: processed text in spintax format :rtype: string """ if not params: params = self.DEFAULT_PARAMS_SPIN.copy() else: self._validate(params) params['rewrite'] = '0' return self._send_request( method='GlobalSpin', text=text, params=params )
[docs] def unique_variation(self, text, params=None): """ Return a unique variation of the given text. :param text: original text that needs to be changed :type text: string :param params: parameters to pass along with the request :type params: dictionary :return: processed text :rtype: string """ if not params: params = self.DEFAULT_PARAMS_SPIN.copy() else: self._validate(params) params['rewrite'] = '1' return self._send_request( method='GlobalSpin', text=text, params=params )
[docs] def _send_request(self, method, text, params): """ Invoke Spin Chimp API with given parameters and return its response. :param params: parameters to pass along with the request :type params: dictionary :return: API's response (article) :rtype: string """ for k, v in params.items(): params[k] = v.encode("utf-8") params['email'] = self._email params['apikey'] = self._apikey params['aid'] = self._aid url = self.URL.format(method=method) + urllib.urlencode(params) textdata = text.encode('utf-8') try: response = urllib2.urlopen(url, data=textdata, timeout=self.TIMEOUT) except urllib2.URLError as e: raise ex.NetworkError(str(e)) result = response.read().decode("utf-8") if result.lower().startswith('failed:'): self._raise_error(result[7:]) return result
def _raise_error(self, errormsg): if ( re.search(r"Credentials check result:InvalidEmail", errormsg, re.IGNORECASE ) or re.search(r"Credentials check result:SubscriptionExpired", errormsg, re.IGNORECASE ) or re.search(r"Credentials check result:InvalidAPIKey", errormsg, re.IGNORECASE ) or re.search(r"Credentials check result:NotAPIRegistered", errormsg, re.IGNORECASE ) or re.search(r"No Email specified", errormsg, re.IGNORECASE ) or re.search(r"No API Key specified", errormsg, re.IGNORECASE ) or re.search(r"No Application ID (aid) Specified", errormsg, re.IGNORECASE ) ): raise ex.AuthenticationError(errormsg) elif ( re.search(r"Credentials check result:MaxQueriesReached", errormsg, re.IGNORECASE )): raise ex.QuotaLimitError(errormsg) elif (re.search(r"Credentials check result:DatabaseFailure", errormsg, re.IGNORECASE )): raise ex.InternalError(errormsg) elif (re.search(r"There are no words in your article!", errormsg, re.IGNORECASE ) or re.search(r"Article too long (\d words). Max is \d.", errormsg, re.IGNORECASE )): raise ex.ArticleError(errormsg) else: raise ex.UnknownError(errormsg)