From 68863d101ecc7a772c8104f7ced152218156cb4e Mon Sep 17 00:00:00 2001 From: Rohan Patel Date: Fri, 28 Aug 2015 10:38:44 +0530 Subject: [MOV] website_twitter: restructured module files --- addons/website_twitter/__openerp__.py | 6 +- addons/website_twitter/data/twitter_data.xml | 16 --- .../website_twitter/data/website_twitter_data.xml | 16 +++ addons/website_twitter/models/__init__.py | 4 +- addons/website_twitter/models/twitter.py | 117 --------------------- addons/website_twitter/models/twitter_config.py | 77 -------------- addons/website_twitter/models/website_twitter.py | 117 +++++++++++++++++++++ .../models/website_twitter_config.py | 77 ++++++++++++++ addons/website_twitter/views/twitter_snippet.xml | 37 ------- addons/website_twitter/views/twitter_view.xml | 56 ---------- .../views/website_twitter_settings_views.xml | 56 ++++++++++ .../views/website_twitter_snippet_templates.xml | 37 +++++++ 12 files changed, 308 insertions(+), 308 deletions(-) delete mode 100644 addons/website_twitter/data/twitter_data.xml create mode 100644 addons/website_twitter/data/website_twitter_data.xml delete mode 100644 addons/website_twitter/models/twitter.py delete mode 100644 addons/website_twitter/models/twitter_config.py create mode 100644 addons/website_twitter/models/website_twitter.py create mode 100644 addons/website_twitter/models/website_twitter_config.py delete mode 100644 addons/website_twitter/views/twitter_snippet.xml delete mode 100644 addons/website_twitter/views/twitter_view.xml create mode 100644 addons/website_twitter/views/website_twitter_settings_views.xml create mode 100644 addons/website_twitter/views/website_twitter_snippet_templates.xml diff --git a/addons/website_twitter/__openerp__.py b/addons/website_twitter/__openerp__.py index dab7463d0df..188f1da237c 100644 --- a/addons/website_twitter/__openerp__.py +++ b/addons/website_twitter/__openerp__.py @@ -12,9 +12,9 @@ Display best tweets 'depends': ['website'], 'data': [ 'security/ir.model.access.csv', - 'data/twitter_data.xml', - 'views/twitter_view.xml', - 'views/twitter_snippet.xml' + 'data/website_twitter_data.xml', + 'views/website_twitter_settings_views.xml', + 'views/website_twitter_snippet_templates.xml' ], 'demo': [], 'qweb': [], diff --git a/addons/website_twitter/data/twitter_data.xml b/addons/website_twitter/data/twitter_data.xml deleted file mode 100644 index b7ca4281504..00000000000 --- a/addons/website_twitter/data/twitter_data.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - Fetch new Twitter favorites - 2 - hours - -1 - - website - _refresh_favorite_tweets - () - - - - diff --git a/addons/website_twitter/data/website_twitter_data.xml b/addons/website_twitter/data/website_twitter_data.xml new file mode 100644 index 00000000000..b7ca4281504 --- /dev/null +++ b/addons/website_twitter/data/website_twitter_data.xml @@ -0,0 +1,16 @@ + + + + + Fetch new Twitter favorites + 2 + hours + -1 + + website + _refresh_favorite_tweets + () + + + + diff --git a/addons/website_twitter/models/__init__.py b/addons/website_twitter/models/__init__.py index 0556d68f235..a5916ba9a3b 100644 --- a/addons/website_twitter/models/__init__.py +++ b/addons/website_twitter/models/__init__.py @@ -1,2 +1,2 @@ -import twitter -import twitter_config +import website_twitter +import website_twitter_config diff --git a/addons/website_twitter/models/twitter.py b/addons/website_twitter/models/twitter.py deleted file mode 100644 index 9b24ed2aa85..00000000000 --- a/addons/website_twitter/models/twitter.py +++ /dev/null @@ -1,117 +0,0 @@ -# -*- coding: utf-8 -*- - -from urllib2 import urlopen, Request, HTTPError - -import base64 -import json -import logging -import werkzeug - -from openerp.osv import fields, osv - -API_ENDPOINT = 'https://api.twitter.com' -API_VERSION = '1.1' -REQUEST_TOKEN_URL = '%s/oauth2/token' % API_ENDPOINT -REQUEST_FAVORITE_LIST_URL = '%s/%s/favorites/list.json' % (API_ENDPOINT, API_VERSION) -URLOPEN_TIMEOUT = 10 - -_logger = logging.getLogger(__name__) - -class TwitterClient(osv.osv): - _inherit = "website" - - _columns = { - 'twitter_api_key': fields.char('Twitter API key', help="Twitter API Key"), - 'twitter_api_secret': fields.char('Twitter API secret', help="Twitter API Secret"), - 'twitter_screen_name': fields.char('Get favorites from this screen name'), - } - - def _request(self, website, url, params=None): - """Send an authenticated request to the Twitter API.""" - access_token = self._get_access_token(website) - if params: - params = werkzeug.url_encode(params) - url = url + '?' + params - try: - request = Request(url) - request.add_header('Authorization', 'Bearer %s' % access_token) - return json.load(urlopen(request, timeout=URLOPEN_TIMEOUT)) - except HTTPError, e: - _logger.debug("Twitter API request failed with code: %r, msg: %r, content: %r", - e.code, e.msg, e.fp.read()) - raise - - def _refresh_favorite_tweets(self, cr, uid, context=None): - ''' called by cron job ''' - website = self.pool['website'] - ids = self.pool['website'].search(cr, uid, [('twitter_api_key', '!=', False), - ('twitter_api_secret', '!=', False), - ('twitter_screen_name', '!=', False)], - context=context) - _logger.debug("Refreshing tweets for website IDs: %r", ids) - website.fetch_favorite_tweets(cr, uid, ids, context=context) - - def fetch_favorite_tweets(self, cr, uid, ids, context=None): - website_tweets = self.pool['website.twitter.tweet'] - tweet_ids = [] - for website in self.browse(cr, uid, ids, context=context): - if not all((website.twitter_api_key, website.twitter_api_secret, - website.twitter_screen_name)): - _logger.debug("Skip fetching favorite tweets for unconfigured website %s", - website) - continue - params = {'screen_name': website.twitter_screen_name} - last_tweet = website_tweets.search_read( - cr, uid, [('website_id', '=', website.id), - ('screen_name', '=', website.twitter_screen_name)], - ['tweet_id'], - limit=1, order='tweet_id desc', context=context) - if last_tweet: - params['since_id'] = int(last_tweet[0]['tweet_id']) - _logger.debug("Fetching favorite tweets using params %r", params) - response = self._request(website, REQUEST_FAVORITE_LIST_URL, params=params) - for tweet_dict in response: - tweet_id = tweet_dict['id'] # unsigned 64-bit snowflake ID - tweet_ids = website_tweets.search(cr, uid, [('tweet_id', '=', tweet_id)]) - if not tweet_ids: - new_tweet = website_tweets.create( - cr, uid, - { - 'website_id': website.id, - 'tweet': json.dumps(tweet_dict), - 'tweet_id': tweet_id, # stored in NUMERIC PG field - 'screen_name': website.twitter_screen_name, - }, - context=context) - _logger.debug("Found new favorite: %r, %r", tweet_id, tweet_dict) - tweet_ids.append(new_tweet) - return tweet_ids - - def _get_access_token(self, website): - """Obtain a bearer token.""" - bearer_token_cred = '%s:%s' % (website.twitter_api_key, website.twitter_api_secret) - encoded_cred = base64.b64encode(bearer_token_cred) - request = Request(REQUEST_TOKEN_URL) - request.add_header('Content-Type', - 'application/x-www-form-urlencoded;charset=UTF-8') - request.add_header('Authorization', - 'Basic %s' % encoded_cred) - request.add_data('grant_type=client_credentials') - data = json.load(urlopen(request, timeout=URLOPEN_TIMEOUT)) - access_token = data['access_token'] - return access_token - -class WebsiteTwitterTweet(osv.osv): - _name = "website.twitter.tweet" - _description = "Twitter Tweets" - _columns = { - 'website_id': fields.many2one('website', string="Website"), - 'screen_name': fields.char("Screen Name"), - 'tweet': fields.text('Tweets'), - - # Twitter IDs are 64-bit unsigned ints, so we need to store them in - # unlimited precision NUMERIC columns, which can be done with a - # float field. Used digits=(0,0) to indicate unlimited. - # Using VARCHAR would work too but would have sorting problems. - 'tweet_id': fields.float("Tweet ID", digits=(0,0)), # Twitter - } diff --git a/addons/website_twitter/models/twitter_config.py b/addons/website_twitter/models/twitter_config.py deleted file mode 100644 index 115eda5cd57..00000000000 --- a/addons/website_twitter/models/twitter_config.py +++ /dev/null @@ -1,77 +0,0 @@ -# -*- coding: utf-8 -*- - -import logging -from urllib2 import URLError, HTTPError - -from openerp.osv import fields, osv -from openerp.tools.translate import _ -from openerp.exceptions import UserError - -_logger = logging.getLogger(__name__) - - -class twitter_config_settings(osv.TransientModel): - _inherit = 'website.config.settings' - - _columns = { - 'twitter_api_key': fields.related( - 'website_id', 'twitter_api_key', type="char", - string='Twitter API Key', - help="Twitter API key you can get it from https://apps.twitter.com/app/new"), - 'twitter_api_secret': fields.related( - 'website_id', 'twitter_api_secret', type="char", - string='Twitter API secret', - help="Twitter API secret you can get it from https://apps.twitter.com/app/new"), - 'twitter_tutorial': fields.dummy( - type="boolean", string="Show me how to obtain the Twitter API Key and Secret"), - 'twitter_screen_name': fields.related( - 'website_id', 'twitter_screen_name', - type="char", string='Get favorites from this screen name', - help="Screen Name of the Twitter Account from which you want to load favorites." - "It does not have to match the API Key/Secret."), - } - - def _get_twitter_exception_message(self, error_code, context=None): - TWITTER_EXCEPTION = { - 304: _('There was no new data to return.'), - 400: _('The request was invalid or cannot be otherwise served. Requests without authentication are considered invalid and will yield this response.'), - 401: _('Authentication credentials were missing or incorrect. Maybe screen name tweets are protected.'), - 403: _('The request is understood, but it has been refused or access is not allowed. Please check your Twitter API Key and Secret.'), - 429: _('Request cannot be served due to the applications rate limit having been exhausted for the resource.'), - 500: _('Twitter seems broken. Please retry later. You may consider posting an issue on Twitter forums to get help.'), - 502: _('Twitter is down or being upgraded.'), - 503: _('The Twitter servers are up, but overloaded with requests. Try again later.'), - 504: _('The Twitter servers are up, but the request could not be serviced due to some failure within our stack. Try again later.') - } - if error_code in TWITTER_EXCEPTION: - return TWITTER_EXCEPTION[error_code] - else: - return _('HTTP Error: Something is misconfigured') - - def _check_twitter_authorization(self, cr, uid, config_id, context=None): - website_obj = self.pool['website'] - website_config = self.browse(cr, uid, config_id, context=context) - try: - website_obj.fetch_favorite_tweets(cr, uid, [website_config.website_id.id], context=context) - - except HTTPError, e: - _logger.info("%s - %s" % (e.code, e.reason), exc_info=True) - raise UserError("%s - %s" % (e.code, e.reason) + ':' + self._get_twitter_exception_message(e.code, context)) - except URLError, e: - _logger.info(_('We failed to reach a twitter server.'), exc_info=True) - raise UserError(_('Internet connection refused') + ' ' + _('We failed to reach a twitter server.')) - except Exception, e: - _logger.info(_('Please double-check your Twitter API Key and Secret!'), exc_info=True) - raise UserError(_('Twitter authorization error!') + ' ' + _('Please double-check your Twitter API Key and Secret!')) - - def create(self, cr, uid, vals, context=None): - res_id = super(twitter_config_settings, self).create(cr, uid, vals, context=context) - if vals.get('twitter_api_key') or vals.get('twitter_api_secret') or vals.get('twitter_screen_name'): - self._check_twitter_authorization(cr, uid, res_id, context=context) - return res_id - - def write(self, cr, uid, ids, vals, context=None): - res_id = super(twitter_config_settings, self).write(cr, uid, ids, vals, context=context) - if vals.get('twitter_api_key') or vals.get('twitter_api_secret') or vals.get('twitter_screen_name'): - self._check_twitter_authorization(cr, uid, ids, context=context) - return res_id diff --git a/addons/website_twitter/models/website_twitter.py b/addons/website_twitter/models/website_twitter.py new file mode 100644 index 00000000000..9b24ed2aa85 --- /dev/null +++ b/addons/website_twitter/models/website_twitter.py @@ -0,0 +1,117 @@ +# -*- coding: utf-8 -*- + +from urllib2 import urlopen, Request, HTTPError + +import base64 +import json +import logging +import werkzeug + +from openerp.osv import fields, osv + +API_ENDPOINT = 'https://api.twitter.com' +API_VERSION = '1.1' +REQUEST_TOKEN_URL = '%s/oauth2/token' % API_ENDPOINT +REQUEST_FAVORITE_LIST_URL = '%s/%s/favorites/list.json' % (API_ENDPOINT, API_VERSION) +URLOPEN_TIMEOUT = 10 + +_logger = logging.getLogger(__name__) + +class TwitterClient(osv.osv): + _inherit = "website" + + _columns = { + 'twitter_api_key': fields.char('Twitter API key', help="Twitter API Key"), + 'twitter_api_secret': fields.char('Twitter API secret', help="Twitter API Secret"), + 'twitter_screen_name': fields.char('Get favorites from this screen name'), + } + + def _request(self, website, url, params=None): + """Send an authenticated request to the Twitter API.""" + access_token = self._get_access_token(website) + if params: + params = werkzeug.url_encode(params) + url = url + '?' + params + try: + request = Request(url) + request.add_header('Authorization', 'Bearer %s' % access_token) + return json.load(urlopen(request, timeout=URLOPEN_TIMEOUT)) + except HTTPError, e: + _logger.debug("Twitter API request failed with code: %r, msg: %r, content: %r", + e.code, e.msg, e.fp.read()) + raise + + def _refresh_favorite_tweets(self, cr, uid, context=None): + ''' called by cron job ''' + website = self.pool['website'] + ids = self.pool['website'].search(cr, uid, [('twitter_api_key', '!=', False), + ('twitter_api_secret', '!=', False), + ('twitter_screen_name', '!=', False)], + context=context) + _logger.debug("Refreshing tweets for website IDs: %r", ids) + website.fetch_favorite_tweets(cr, uid, ids, context=context) + + def fetch_favorite_tweets(self, cr, uid, ids, context=None): + website_tweets = self.pool['website.twitter.tweet'] + tweet_ids = [] + for website in self.browse(cr, uid, ids, context=context): + if not all((website.twitter_api_key, website.twitter_api_secret, + website.twitter_screen_name)): + _logger.debug("Skip fetching favorite tweets for unconfigured website %s", + website) + continue + params = {'screen_name': website.twitter_screen_name} + last_tweet = website_tweets.search_read( + cr, uid, [('website_id', '=', website.id), + ('screen_name', '=', website.twitter_screen_name)], + ['tweet_id'], + limit=1, order='tweet_id desc', context=context) + if last_tweet: + params['since_id'] = int(last_tweet[0]['tweet_id']) + _logger.debug("Fetching favorite tweets using params %r", params) + response = self._request(website, REQUEST_FAVORITE_LIST_URL, params=params) + for tweet_dict in response: + tweet_id = tweet_dict['id'] # unsigned 64-bit snowflake ID + tweet_ids = website_tweets.search(cr, uid, [('tweet_id', '=', tweet_id)]) + if not tweet_ids: + new_tweet = website_tweets.create( + cr, uid, + { + 'website_id': website.id, + 'tweet': json.dumps(tweet_dict), + 'tweet_id': tweet_id, # stored in NUMERIC PG field + 'screen_name': website.twitter_screen_name, + }, + context=context) + _logger.debug("Found new favorite: %r, %r", tweet_id, tweet_dict) + tweet_ids.append(new_tweet) + return tweet_ids + + def _get_access_token(self, website): + """Obtain a bearer token.""" + bearer_token_cred = '%s:%s' % (website.twitter_api_key, website.twitter_api_secret) + encoded_cred = base64.b64encode(bearer_token_cred) + request = Request(REQUEST_TOKEN_URL) + request.add_header('Content-Type', + 'application/x-www-form-urlencoded;charset=UTF-8') + request.add_header('Authorization', + 'Basic %s' % encoded_cred) + request.add_data('grant_type=client_credentials') + data = json.load(urlopen(request, timeout=URLOPEN_TIMEOUT)) + access_token = data['access_token'] + return access_token + +class WebsiteTwitterTweet(osv.osv): + _name = "website.twitter.tweet" + _description = "Twitter Tweets" + _columns = { + 'website_id': fields.many2one('website', string="Website"), + 'screen_name': fields.char("Screen Name"), + 'tweet': fields.text('Tweets'), + + # Twitter IDs are 64-bit unsigned ints, so we need to store them in + # unlimited precision NUMERIC columns, which can be done with a + # float field. Used digits=(0,0) to indicate unlimited. + # Using VARCHAR would work too but would have sorting problems. + 'tweet_id': fields.float("Tweet ID", digits=(0,0)), # Twitter + } diff --git a/addons/website_twitter/models/website_twitter_config.py b/addons/website_twitter/models/website_twitter_config.py new file mode 100644 index 00000000000..115eda5cd57 --- /dev/null +++ b/addons/website_twitter/models/website_twitter_config.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- + +import logging +from urllib2 import URLError, HTTPError + +from openerp.osv import fields, osv +from openerp.tools.translate import _ +from openerp.exceptions import UserError + +_logger = logging.getLogger(__name__) + + +class twitter_config_settings(osv.TransientModel): + _inherit = 'website.config.settings' + + _columns = { + 'twitter_api_key': fields.related( + 'website_id', 'twitter_api_key', type="char", + string='Twitter API Key', + help="Twitter API key you can get it from https://apps.twitter.com/app/new"), + 'twitter_api_secret': fields.related( + 'website_id', 'twitter_api_secret', type="char", + string='Twitter API secret', + help="Twitter API secret you can get it from https://apps.twitter.com/app/new"), + 'twitter_tutorial': fields.dummy( + type="boolean", string="Show me how to obtain the Twitter API Key and Secret"), + 'twitter_screen_name': fields.related( + 'website_id', 'twitter_screen_name', + type="char", string='Get favorites from this screen name', + help="Screen Name of the Twitter Account from which you want to load favorites." + "It does not have to match the API Key/Secret."), + } + + def _get_twitter_exception_message(self, error_code, context=None): + TWITTER_EXCEPTION = { + 304: _('There was no new data to return.'), + 400: _('The request was invalid or cannot be otherwise served. Requests without authentication are considered invalid and will yield this response.'), + 401: _('Authentication credentials were missing or incorrect. Maybe screen name tweets are protected.'), + 403: _('The request is understood, but it has been refused or access is not allowed. Please check your Twitter API Key and Secret.'), + 429: _('Request cannot be served due to the applications rate limit having been exhausted for the resource.'), + 500: _('Twitter seems broken. Please retry later. You may consider posting an issue on Twitter forums to get help.'), + 502: _('Twitter is down or being upgraded.'), + 503: _('The Twitter servers are up, but overloaded with requests. Try again later.'), + 504: _('The Twitter servers are up, but the request could not be serviced due to some failure within our stack. Try again later.') + } + if error_code in TWITTER_EXCEPTION: + return TWITTER_EXCEPTION[error_code] + else: + return _('HTTP Error: Something is misconfigured') + + def _check_twitter_authorization(self, cr, uid, config_id, context=None): + website_obj = self.pool['website'] + website_config = self.browse(cr, uid, config_id, context=context) + try: + website_obj.fetch_favorite_tweets(cr, uid, [website_config.website_id.id], context=context) + + except HTTPError, e: + _logger.info("%s - %s" % (e.code, e.reason), exc_info=True) + raise UserError("%s - %s" % (e.code, e.reason) + ':' + self._get_twitter_exception_message(e.code, context)) + except URLError, e: + _logger.info(_('We failed to reach a twitter server.'), exc_info=True) + raise UserError(_('Internet connection refused') + ' ' + _('We failed to reach a twitter server.')) + except Exception, e: + _logger.info(_('Please double-check your Twitter API Key and Secret!'), exc_info=True) + raise UserError(_('Twitter authorization error!') + ' ' + _('Please double-check your Twitter API Key and Secret!')) + + def create(self, cr, uid, vals, context=None): + res_id = super(twitter_config_settings, self).create(cr, uid, vals, context=context) + if vals.get('twitter_api_key') or vals.get('twitter_api_secret') or vals.get('twitter_screen_name'): + self._check_twitter_authorization(cr, uid, res_id, context=context) + return res_id + + def write(self, cr, uid, ids, vals, context=None): + res_id = super(twitter_config_settings, self).write(cr, uid, ids, vals, context=context) + if vals.get('twitter_api_key') or vals.get('twitter_api_secret') or vals.get('twitter_screen_name'): + self._check_twitter_authorization(cr, uid, ids, context=context) + return res_id diff --git a/addons/website_twitter/views/twitter_snippet.xml b/addons/website_twitter/views/twitter_snippet.xml deleted file mode 100644 index 544cc95f6a7..00000000000 --- a/addons/website_twitter/views/twitter_snippet.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - -