"""This module provides the RatingsAndReviewsAPI class for talking to the ratings and reviews API, plus a few helper classes. """ from piston_mini_client import ( PistonAPI, returns, returns_json, returns_list_of, ) from piston_mini_client.validators import validate_pattern, validate from piston_mini_client.failhandlers import APIError # These are factored out as constants for if you need to work against a # server that doesn't support both schemes (like http-only dev servers) PUBLIC_API_SCHEME = 'http' AUTHENTICATED_API_SCHEME = 'https' from rnrclient_pristine import ReviewRequest, ReviewsStats, ReviewDetails from softwarecenter.backend.fake_review_settings import ( FakeReviewSettings, network_delay, ) import json import random import time class RatingsAndReviewsAPI(PistonAPI): """A fake client pretending to be RAtingsAndReviewsAPI from rnrclient_pristine. Uses settings from test.fake_review_settings.FakeReviewSettings to provide predictable responses to methods that try to use the RatingsAndReviewsAPI for testing purposes (i.e. without network activity). To use this, instead of importing from rnrclient_pristine, you can import from rnrclient_fake instead. """ default_service_root = 'http://localhost:8000/reviews/api/1.0' default_content_type = 'application/x-www-form-urlencoded' _exception_msg = 'Fake RatingsAndReviewsAPI raising fake exception' _PACKAGE_NAMES = ['armagetronad', 'compizconfig-settings-manager', 'file-roller', 'aisleriot', 'p7zip-full', 'compiz-core', 'banshee', 'gconf-editor', 'nanny', '3depict', 'apturl', 'jockey-gtk', 'alex4', 'bzr-explorer', 'aqualung'] _USERS = ["Joe Doll", "John Foo", "Cat Lala", "Foo Grumpf", "Bar Tender", "Baz Lightyear"] _SUMMARIES = ["Cool", "Medium", "Bad", "Too difficult"] _TEXT = ["Review text number 1", "Review text number 2", "Review text number 3", "Review text number 4"] _fake_settings = FakeReviewSettings() @returns_json @network_delay def server_status(self): if self._fake_settings.get_setting('server_response_error'): raise APIError(self._exception_msg) return json.dumps('ok') @validate_pattern('origin', r'[0-9a-z+-.:/]+', required=False) @validate_pattern('distroseries', r'\w+', required=False) @validate('days', int, required=False) @returns_list_of(ReviewsStats) @network_delay def review_stats(self, origin='any', distroseries='any', days=None, valid_days=(1, 3, 7)): if self._fake_settings.get_setting('review_stats_error'): raise APIError(self._exception_msg) if self._fake_settings.get_setting('packages_returned') > 15: quantity = 15 else: quantity = self._fake_settings.get_setting('packages_returned') stats = [] for i in range(0, quantity): s = {'app_name': '', 'package_name': self._PACKAGE_NAMES[i], 'ratings_total': str(random.randrange(1, 200)), 'ratings_average': str(random.randrange(0, 5)), 'histogram': None } stats.append(s) return json.dumps(stats) @validate_pattern('language', r'\w+', required=False) @validate_pattern('origin', r'[0-9a-z+-.:/]+', required=False) @validate_pattern('distroseries', r'\w+', required=False) @validate_pattern('version', r'[-\w+.:~]+', required=False) @validate_pattern('packagename', r'[a-z0-9.+-]+') @validate('appname', str, required=False) @validate('page', int, required=False) @validate('sort', str, required=False) @returns_list_of(ReviewDetails) @network_delay def get_reviews(self, packagename, language='any', origin='any', distroseries='any', version='any', appname='', page=1, sort='helpful'): # work out how many reviews to return for pagination if page <= self._fake_settings.get_setting('review_pages'): num_reviews = 10 elif page == self._fake_settings.get_setting('review_pages') + 1: num_reviews = self._fake_settings.get_setting('reviews_returned') else: num_reviews = 0 if self._fake_settings.get_setting('get_reviews_error'): raise APIError(self._exception_msg) reviews = self._make_fake_reviews(packagename, num_reviews) return json.dumps(reviews) @validate('review_id', int) @returns(ReviewDetails) @network_delay def get_review(self, review_id): if self._fake_settings.get_setting('get_review_error'): raise APIError(self._exception_msg) review = self._make_fake_reviews(single_id=review_id) return json.dumps(review) @validate('review', ReviewRequest) @returns(ReviewDetails) @network_delay def submit_review(self, review): if self._fake_settings.get_setting('submit_review_error'): raise APIError(self._exception_msg) user = self._fake_settings.get_setting( 'reviewer_username') or random.choice(self._USERS) review_id = self._fake_settings.get_setting( 'submit_review_id') or random.randint(1, 10000) r = { "origin": review.origin, "rating": review.rating, "hide": False, "app_name": review.app_name, "language": review.language, "reviewer_username": user, "usefulness_total": 0, "usefulness_favorable": 0, "review_text": review.review_text, "date_deleted": None, "summary": review.summary, "version": review.version, "id": review_id, "date_created": time.strftime("%Y-%m-%d %H:%M:%S"), "reviewer_displayname": "Fake User", "package_name": review.package_name, "distroseries": review.distroseries } return json.dumps(r) @validate('review_id', int) @validate_pattern('reason', r'[^\n]+') @validate_pattern('text', r'[^\n]+') @returns_json @network_delay def flag_review(self, review_id, reason, text): if self._fake_settings.get_setting('flag_review_error'): raise APIError(self._exception_msg) mod_id = random.randint(1, 500) pkg = self._fake_settings.get_setting( 'flag_package_name') or random.choice(self._PACKAGE_NAMES) username = self._fake_settings.get_setting( 'flagger_username') or random.choice(self._USERS) f = { "user_id": random.randint(1, 500), "description": text, "review_moderation_id": mod_id, "_user_cache": self._make_user_cache(username), "summary": reason, "_review_moderation_cache": { "status": 0, "review_id": review_id, "_review_cache": self._make_fake_reviews(packagename=pkg, single_id=review_id), "moderation_text": text, "date_moderated": None, "moderator_id": None, "date_created": time.strftime("%Y-%m-%d %H:%M:%S"), "id": mod_id }, "date_created": time.strftime("%Y-%m-%d %H:%M:%S"), "id": mod_id } return json.dumps(f) @validate('review_id', int) @validate_pattern('useful', 'True|False') @returns_json @network_delay def submit_usefulness(self, review_id, useful): if self._fake_settings.get_setting('submit_usefulness_error'): raise APIError(self._exception_msg) return json.dumps(self._fake_settings.get_setting( 'usefulness_response_string')) @validate('review_id', int, required=False) @validate_pattern('username', r'[^\n]+', required=False) @returns_json @network_delay def get_usefulness(self, review_id=None, username=None): if not username and not review_id: return None if self._fake_settings.get_setting('get_usefulness_error'): raise APIError(self._exception_msg) #just return a single fake item if the review_id was supplied if review_id: if username: response_user = username else: response_user = random.choice(self._USERS) response = { 'username': response_user, 'useful': random.choice(['True', 'False']), 'review_id': review_id } return json.dumps([response]) #set up review ids to honour requested and also add randoms quantity = self._fake_settings.get_setting('votes_returned') id_list = self._fake_settings.get_setting('required_review_ids') id_quantity = len(id_list) #figure out if we need to accommodate requested review ids if id_quantity == 0: rand_id_start = 0 else: rand_id_start = max(id_list) votes = [] for i in range(0, quantity): #assign review ids requested if any still exist try: id = id_list[i] except IndexError: id = random.randint(rand_id_start, 10000) u = { 'username': username, 'useful': random.choice(['True', 'False']), 'review_id': id } votes.append(u) return json.dumps(votes) @validate('review_id', int) @returns_json def delete_review(self, review_id): """Delete a review""" return json.dumps(True) @validate('review_id', int) @validate('rating', int) @validate_pattern('summary', r'[^\n]+') @validate_pattern('review_text', r'[^\n]+') @returns(ReviewDetails) def modify_review(self, review_id, rating, summary, review_text): """Modify an existing review""" return json.dumps(self._make_fake_reviews()[0]) def _make_fake_reviews(self, packagename='compiz-core', quantity=1, single_id=None): """Make and return a requested quantity of fake reviews""" reviews = [] for i in range(0, quantity): if quantity == 1 and single_id: id = single_id else: id = i * 3 r = { "origin": "ubuntu", "rating": random.randint(1, 5), "hide": False, "app_name": "", "language": "en", "reviewer_username": random.choice(self._USERS), "usefulness_total": random.randint(3, 6), "usefulness_favorable": random.randint(1, 3), "review_text": random.choice(self._TEXT), "date_deleted": None, "summary": random.choice(self._SUMMARIES), "version": "1:0.9.4", "id": id, "date_created": time.strftime("%Y-%m-%d %H:%M:%S"), "reviewer_displayname": "Fake Person", "package_name": packagename, "distroseries": "natty" } reviews.append(r) #get_review wants a dict but get_reviews wants a list of dicts if single_id: return r else: return reviews def _make_user_cache(self, username): return { "username": username, "first_name": "Fake", "last_name": "User", "is_active": True, "email": "fakeuser@email.com", "is_superuser": False, "is_staff": False, "last_login": time.strftime("%Y-%m-%d %H:%M:%S"), "password": "!", "id": random.randint(1, 500), "date_joined": time.strftime("%Y-%m-%d %H:%M:%S") }