class RipeZombiePicker(PostPicker): def __init__(self): super().__init__() self.cr = CodeReview() @property def url(self): return URL def accept(self, post_id): """ memo: deleted answers are excluded by Stack API memo: deleted questions are excluded by Stack API :param post_id: id of a question :return: messages to send, or falsy to reject """ logging.info('fetching question {}'.format(post_id)) try: question = self.cr.question(post_id) except ValueError as e: logging.error('error when fetching question: '.format(e)) return None if 'closed_date' in question.json: logging.warning('question closed, skip: {}'.format(question.url)) return None score_0_exists = False for answer in question.answers: if answer.score > 0: logging.warning('answer with postitive score exists, skip: {}'.format(answer.url)) return None if answer.is_accepted: logging.warning('accepted answer exists, skip: {}'.format(answer.url)) return None if answer.score == 0: score_0_exists = True if not score_0_exists: logging.warning('no answer with 0 score, skip: {}'.format(question.url)) return None return format_post(DESCRIPTION, question.title, question.url, question.tags)
class CodeOnlyAnswerPicker(PostPicker): def __init__(self): super().__init__() self.cr = CodeReview() @property def url(self): return URL def accept(self, post_id): """ memo: deleted answers are excluded by Stack API memo: deleted questions are excluded by Stack API :param post_id: id of an answer :return: messages to send, or falsy to reject """ logging.info('fetching answer {}'.format(post_id)) try: answer = self.cr.answer(post_id) except ValueError as e: logging.error('error when fetching answer: '.format(e)) return None if answer.is_accepted: logging.info('answer is accepted, skip: {}'.format(answer.id)) return None if answer.score != 0: logging.info('answer has score != 0, skip: {}'.format(answer.id)) return None logging.info('fetching question {}'.format(answer.question_id)) try: question = self.cr.question(answer.question_id) except ValueError as e: logging.error('error when fetching answer: '.format(e)) return None if 'closed_date' in question.json: logging.warning('question closed, skip: {}'.format(answer.url)) return None return [INTRO_MESSAGE, answer.url]
class NarutoPicker(PostPicker): def __init__(self): super().__init__() self.cr = CodeReview() @property def url(self): return URL def accept(self, post_id): """ memo: deleted answers are excluded by Stack API memo: deleted questions are excluded by Stack API :param post_id: id of an answer :return: messages to send, or falsy to reject """ logging.info('fetching answer {}'.format(post_id)) try: answer = self.cr.answer(post_id) except ValueError as e: logging.error('error when fetching answer: '.format(e)) return None if not answer.is_accepted: logging.warning('answer not accepted, skip: {}'.format(answer.url)) return None if answer.score != 0: logging.warning('score not zero, skip: {}'.format(answer.url)) return None question = self.cr.question(answer.question_id) if 'closed_date' in question.json: logging.warning('question closed, skip: {}'.format(answer.url)) return None if question.owner_id == answer.owner_id: logging.warning('answer owner is the same as question owner, skip: {}'.format(answer.url)) return None return format_post(DESCRIPTION, question.title, answer.url, question.tags)
class TumbleweedCandidatePicker(PostPicker): def __init__(self): super().__init__() self.cr = CodeReview() @property def url(self): return URL def accept(self, post_id): """ memo: deleted questions are excluded by Stack API :param post_id: id of a question :return: messages to send, or falsy to reject """ logging.info('fetching question {}'.format(post_id)) try: question = self.cr.question(post_id) except ValueError as e: logging.error('error when fetching question: '.format(e)) return None if 'closed_date' in question.json: logging.warning('question closed, skip: {}'.format(question.url)) return None if question.score != 0: logging.warning('question has non-zero score, skip: {}'.format(question.url)) return None if question.answers: logging.warning('question has answers, skip: {}'.format(question.url)) return None if question.comments.fetch(): logging.warning('question has comments, skip: {}'.format(question.url)) return None if self.recently_awarded_tumbleweed(question.owner_id, question.creation_date): logging.warning('owner has recently received tumbleweed, skip: {}'.format(question.url)) return None return format_post(DESCRIPTION, question.title, question.url, question.tags) def recently_awarded_tumbleweed(self, user_id, date): badge_id = self.cr.badge(name='Tumbleweed').id fromdate = int(date.timestamp()) url = 'https://api.stackexchange.com/2.2/badges/{}/recipients'.format(badge_id) data = { 'fromdate': fromdate, 'site': 'codereview', } response = requests.get(url, data) if not response.ok: return self.has_tumbleweed(user_id) items = json.loads(response.content.decode())['items'] return user_id in [item['user']['user_id'] for item in items] def has_tumbleweed(self, owner_id): user = self.cr.user(owner_id) return any([badge.name == 'Tumbleweed' for badge in user.badges.fetch()])