Sandy Carter (http://www.savoirfairelinux.com) has proposed merging lp:~savoirfairelinux-openerp/lp-community-utils/nag_refactor into lp:lp-community-utils.
Requested reviews: OpenERP Community Reviewer/Maintainer (openerp-community-reviewer) For more details, see: https://code.launchpad.net/~savoirfairelinux-openerp/lp-community-utils/nag_refactor/+merge/205257 Cleaned up code of nag * Put branch analysis code in nag_lib * Changed Nag to a class * Removed unused parameters * Fixed sign/sort issue with age * General readability improvements This will allow for other scripts that the general nag script to use some shared functionality -- https://code.launchpad.net/~savoirfairelinux-openerp/lp-community-utils/nag_refactor/+merge/205257 Your team Savoir-faire Linux' OpenERP is subscribed to branch lp:~savoirfairelinux-openerp/lp-community-utils/nag_refactor.
=== added file 'nag_lib.py' --- nag_lib.py 1970-01-01 00:00:00 +0000 +++ nag_lib.py 2014-02-06 20:34:22 +0000 @@ -0,0 +1,147 @@ +# Copyright 2012 Canonical Ltd. +# Written by: +# Zygmunt Krynicki <[email protected]> +# Hacked for the OpenERP Community Reviewers version by: +# Guewen Baconnier <[email protected]> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from __future__ import absolute_import, print_function, unicode_literals + +import logging +import datetime + + +class DefaultPolicy: + + def __init__(self): + pass + + mps_need_commit_message = False + min_review_age = 0 + max_bug_new_age = 7 + + +class UTC(datetime.tzinfo): + + def dst(self, foo): + return datetime.timedelta(0, 0) + + def utcoffset(self, foo): + return datetime.timedelta(0, 0) + + +# Nag: +# Indication that we want to nag the 'person'... +# ...with the specified 'action' +# ...about a particular 'subject' +class Nag(): + def __init__(self, person, action, subject, sort_class, sort_priority, + age, project_name, proposal): + self.person = person + self.action = action + self.subject = subject + self.sort_class = sort_class + self.sort_priority = sort_priority + self.age = age + self.project_name = project_name + self.proposal = proposal + +UTC = UTC() +SORT_CLASS_BUG, SORT_CLASS_MERGE = range(2) + + +def gen_merge_proposal_nags(policy, project): + # XXX: cannnot use utcnow as launchpad returns tz-aware objects here + now = datetime.datetime.now(tz=UTC) + logging.debug("Looking for merge proposals in project %r", project) + # NOTE: Workaround for project.getMergeProposals() crashing on timeouts + try: + merge_proposals = project.getMergeProposals(status='Needs review') + except AttributeError: + logging.warning("The project %s has no code to look at", project) + return + for proposal in merge_proposals: + logging.debug("Looking at merge proposal %r", proposal) + # Skip everything that is still not requested for review + if proposal.date_review_requested is None: + continue + # Skip everything that is already merged + if proposal.date_merged is not None: + continue + review_age = now - proposal.date_review_requested + # Nag about missing commit message on merge requests + if policy.mps_need_commit_message and proposal.commit_message is None: + yield Nag( + person=proposal.registrant.display_name, + action="set a commit message on merge request", + subject=proposal.web_link, + sort_class=SORT_CLASS_MERGE, + sort_priority=None, # TODO: get from max(linked bugs) + age=review_age.days, + project_name=project.name, + proposal=proposal, + ) + # Nag about aging merge requests + if review_age.days >= policy.min_review_age: + yield Nag( + person='Someone', + action="review the merge request", + subject=proposal.web_link, + sort_class=SORT_CLASS_MERGE, + sort_priority=None, # TODO: get from max(linked bugs) + age=review_age.days, + project_name=project.name, + proposal=proposal, + ) + + +def gen_project_nags(lp, policy, project_name): + # TODO: Detect project groups and redirect the nag to all projects + # underneath that project + # Access the project that we care about + logging.debug("Accessing project %r", project_name) + project = lp.projects[project_name] + # Re-yield all the merge proposal nags + # NOTE: change to yield from in py3k3 + for nag in gen_merge_proposal_nags(policy, project): + yield nag + + +def gen_bug_nags(policy, project): + now = datetime.datetime.now(tz=UTC) + new_threshold = now - datetime.timedelta(days=policy.max_bug_new_age) + # Nag about bugs that are "New" for too long + logging.debug("Looking at 'new' bugs that are 8 days or older") + aging_new_bugs = project.searchTasks(status='New', + created_before=new_threshold) + for bug_task in aging_new_bugs: + yield Nag( + person='everyone', + action='triage the bug', + subject=bug_task.web_link, + sort_class=SORT_CLASS_BUG, + sort_priority=None, # TODO: convert from importance name + age=None, + project_name=project.name, + proposal=None) + + +def parse_projects_file(filename): + """ Parse a file containing name of the projects + A line per project + """ + with open(filename, mode='rU') as project_file: + content = project_file.read() + + return [name.strip() for name in content.splitlines()] === modified file 'openerp-nag' --- openerp-nag 2013-09-22 14:36:16 +0000 +++ openerp-nag 2014-02-06 20:34:22 +0000 @@ -42,26 +42,18 @@ from __future__ import unicode_literals, print_function import argparse -import collections -import datetime import logging from launchpadlib.launchpad import Launchpad - +from nag_lib import ( + parse_projects_file, + gen_project_nags, + DefaultPolicy, +) consumer_name = 'OpenERP Community Reviewers Nagging Scripts' -# Nag: -# Indication that we want to nag the 'person'... -# ...with the specified 'action' -# ...about a particular 'subject' -Nag = collections.namedtuple( - "Nag", "person action subject sort_class sort_priority sort_age project_name") - -SORT_CLASS_BUG, SORT_CLASS_MERGE = range(2) - - def show_lp_object(obj): print(obj) print("attributes:", obj.lp_attributes) @@ -75,109 +67,7 @@ print("-" * 80) -class DefaultPolicy: - - mps_need_commit_message = False - - max_review_age = 0 - - max_bug_new_age = 7 - - -class UTC(datetime.tzinfo): - - def dst(self, foo): - return datetime.timedelta(0, 0) - - def utcoffset(self, foo): - return datetime.timedelta(0, 0) - - -UTC = UTC() - - -def gen_project_nags(lp, policy, project_name): - # TODO: Detect project groups and redirect the nag to all projects - # underneath that project - # Access the project that we care about - logging.debug("Accessing project %r", project_name) - project = lp.projects[project_name] - # Re-yield all the merge proposal nags - # NOTE: change to yield from in py3k3 - for nag in gen_merge_proposal_nags(lp, policy, project): - yield nag - - -def gen_merge_proposal_nags(lp, policy, project): - # XXX: cannnot use utcnow as launchpad returns tz-aware objects here - now = datetime.datetime.now(tz=UTC) - logging.debug("Looking for merge proposals in project %r", project) - # NOTE: Workaround for project.getMergeProposals() crashing on timeouts - try: - merge_proposals = project.getMergeProposals(status='Needs review') - except AttributeError: - logging.warning("The project %s has no code to look at", project) - return - for proposal in merge_proposals: - logging.debug("Looking at merge proposal %r", proposal) - # Skip everything that is still not requested for review - if proposal.date_review_requested is None: - continue - # Skip everything that is already merged - if proposal.date_merged is not None: - continue - # Nag about missing commit message on merge requests - if policy.mps_need_commit_message and proposal.commit_message is None: - yield Nag( - person=proposal.registrant.display_name, - action="set a commit message on merge request", - subject=proposal.web_link, - sort_class=SORT_CLASS_MERGE, - sort_priority=None, # TODO: get from max(linked bugs) - sort_age=(proposal.date_review_requested - now).days, - project_name=project.name - ) - # Nag about aging merge requests - if (now - proposal.date_review_requested).days >= policy.max_review_age: - yield Nag( - person='Someone', - action="review the merge request", - subject=proposal.web_link, - sort_class=SORT_CLASS_MERGE, - sort_priority=None, # TODO: get from max(linked bugs) - sort_age=(proposal.date_review_requested - now).days, - project_name=project.name - ) - - -def gen_bug_nags(lp, policy, project): - now = datetime.datetime.now(tz=UTC) - new_threshold = now - datetime.timedelta(days=policy.max_bug_new_age) - # Nag about bugs that are "New" for too long - logging.debug("Looking at 'new' bugs that are 8 days or older") - aging_new_bugs = project.searchTasks(status='New', - created_before=new_threshold) - for bug_task in aging_new_bugs: - yield Nag( - person='everyone', - action='triage the bug', - subject=bug_task.web_link, - sort_class=SORT_CLASS_BUG, - sort_priority=None, # TODO: convert from importance name - sort_age=None) - - -def parse_projects_file(filename): - """ Parse a file containing name of the projects - A line per project - """ - with open(filename, mode='rU') as project_file: - content = project_file.read() - - return [name.strip() for name in content.splitlines()] - - -def main(): +def parse_args(): parser = argparse.ArgumentParser() group = parser.add_argument_group(title="debugging") group.add_argument( @@ -189,15 +79,13 @@ group.add_argument( '-f', '--projects-file', dest='projects_file', - help="File containing a list of Launchpad projects to track.\n" - "One project name per line") + help="""\ +File containing a list of Launchpad projects to track. +One project name per line""") group.add_argument( '--anonymous', action='store_true', help="Access Launchpad anonymously") group.add_argument( - '--authenticated', action='store_false', dest='anonymous', - help="Access launchpad as youself") - group.add_argument( '--staging', action='store_const', const='staging', dest='service_root', help="Use staging launchpad instance") group.add_argument( @@ -205,6 +93,16 @@ dest='service_root', help="Use production launchpad instance") parser.set_defaults(anonymous=True, service_root='production') args = parser.parse_args() + if not args.project and not args.projects_file: + parser.print_usage() + parser.exit(2, """\ +No project specified using option --project or --projects-file +""") + return args + + +def main(): + args = parse_args() # Get the policy policy = DefaultPolicy # Reconfigure logging @@ -216,11 +114,6 @@ else: lp = Launchpad.login_with(consumer_name, args.service_root) - if not args.project and not args.projects_file: - parser.print_usage() - parser.exit(2, "No project specified using option " - "--project or --projects-file\n") - project_names = args.project if args.projects_file: project_names.extend(parse_projects_file(args.projects_file)) @@ -231,19 +124,20 @@ logging.info("Looking for things to nag about under %s", project_name) nags.extend(gen_project_nags(lp, policy, project_name)) nags.sort(key=lambda nag: (nag.sort_class, nag.sort_priority, - nag.sort_age)) + -nag.age)) print("=" * 80) print("Done thinking, here's the nag list".center(80)) print("=" * 80) for index1, nag in enumerate(nags, 1): print("{index1:-2}: [age {age}] {person} should {action} {subject} " "on the project {project}".format( - index1=index1, age=(nag.sort_age and -nag.sort_age), person=nag.person, - action=nag.action, subject=nag.subject, project=nag.project_name)) + index1=index1, age=nag.age, person=nag.person, + action=nag.action, subject=nag.subject, + project=nag.project_name)) if __name__ == "__main__": try: - main() + exit(main()) except KeyboardInterrupt: - pass + exit()
-- Mailing list: https://launchpad.net/~savoirfairelinux-openerp Post to : [email protected] Unsubscribe : https://launchpad.net/~savoirfairelinux-openerp More help : https://help.launchpad.net/ListHelp

