Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- osc.cli.review: added initial version of the "review" command
- Loading branch information
Showing
3 changed files
with
239 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
"""Provides various functions for the "review" command.""" | ||
|
||
import logging | ||
|
||
from osc.util.xpath import XPathBuilder | ||
from osc.remote import Request | ||
from osc.search import find_request | ||
from osc.cli.util.env import edit_message | ||
from osc.cli.request.request import AbstractRequestController, SHOW_TEMPLATE | ||
|
||
|
||
def logger(): | ||
"""Returns a logging.Logger object.""" | ||
return logging.getLogger(__name__) | ||
|
||
|
||
class ReviewController(AbstractRequestController): | ||
"""Concrete ReviewController.""" | ||
|
||
@classmethod | ||
def list(cls, renderer, tgt_project, tgt_package, info): | ||
"""Lists requests for the given project and package. | ||
project and package might be None. | ||
""" | ||
super(ReviewController, cls).list(renderer, tgt_project, | ||
tgt_package, info) | ||
|
||
@classmethod | ||
def change_review_state(cls, renderer, reqid, method, message, info, | ||
supersede_id=None): | ||
"""Changes the state of a review. | ||
method is the method which is called on the | ||
retrieved request object. | ||
If message is None $EDITOR is opened. | ||
""" | ||
request = Request.find(reqid) | ||
review = cls._find_review(request, info) | ||
if review is not None: | ||
cls._change_request_state(renderer, request, method, message, | ||
info, supersede_id, review) | ||
|
||
@classmethod | ||
def add(cls, renderer, reqid, message, info): | ||
"""Adds a new review to a request.""" | ||
request = Request.find(reqid) | ||
if message is None: | ||
message = edit_message() | ||
kwargs = {'comment': message, 'by_user': info.user, | ||
'by_group': info.group} | ||
if info.package: | ||
kwargs['by_project'] = info.package[0].project | ||
kwargs['by_package'] = info.package[0].package | ||
elif info.project is not None: | ||
kwargs['by_project'] = info.project | ||
request.add_review(**kwargs) | ||
renderer.render(SHOW_TEMPLATE, request=request) | ||
|
||
@classmethod | ||
def _find_review(cls, request, info): | ||
"""Returns a review or None.""" | ||
xpb = XPathBuilder(context_item=True) | ||
_, xp = cls._build_by_predicate(xpb, info, []) | ||
logger().debug(xp.tostring()) | ||
reviews = request.findall(xp.tostring()) | ||
if reviews: | ||
return reviews[0] | ||
return None | ||
|
||
@classmethod | ||
def _build_by_predicate(cls, xpb, info, states): | ||
"""Builds a by_<kind> predicate. | ||
States is a list of states or the empty list. | ||
Returns a two tuple. The first element indicates whether | ||
a at least one by_ predicate was build | ||
""" | ||
by_kind = False | ||
xp = xpb.dummy() | ||
if info.user is not None: | ||
pred = xpb.attr('by_user') == info.user | ||
xp = xp | cls._add_states(xpb, pred, states) | ||
by_kind = True | ||
if info.group is not None: | ||
pred = xpb.attr('by_group') == info.group | ||
xp = xp | cls._add_states(xpb, pred, states) | ||
by_kind = True | ||
if info.project is not None: | ||
pred = xpb.attr('by_project') == info.project | ||
xp = xp | cls._add_states(xpb, pred, states) | ||
by_kind = True | ||
if info.package: | ||
# info.package is a list | ||
pred = ((xpb.attr('by_project') == info.package[0].project) | ||
& (xpb.attr('by_package') == info.package[0].package)) | ||
xp = xp | cls._add_states(xpb, pred, states) | ||
by_kind = True | ||
return by_kind, xp | ||
|
||
@classmethod | ||
def _add_states(cls, xpb, pred, states): | ||
"""Adds states to the existing predicate pred. | ||
states is a list of states or the empty list. | ||
""" | ||
st_pred = xpb.dummy() | ||
for state in states: | ||
st_pred = st_pred | (xpb.attr('state') == state) | ||
return xpb.review[pred & st_pred.parenthesize()] | ||
|
||
@classmethod | ||
def _find_requests(cls, tgt_project, tgt_package, info): | ||
"""Returns a collection of requests.""" | ||
xpb = XPathBuilder() | ||
xp = xpb.dummy() | ||
by_kind, xp = cls._build_by_predicate(xpb, info, info.state) | ||
if not by_kind: | ||
xp = cls._add_states(xpb, xpb.dummy(), info.state) | ||
xp = (xpb.state.attr('name') == 'review') & xp.parenthesize() | ||
if tgt_project is not None: | ||
xp = xp & (xpb.action.target.attr('project') == tgt_project) | ||
if tgt_package is not None: | ||
xp = xp & (xpb.action.target.attr('package') == tgt_package) | ||
logger().debug(xp.tostring()) | ||
res = find_request(xp=xp, apiurl=info.apiurl) | ||
collection = [r for r in res] | ||
return collection |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
"""Defines the review command.""" | ||
|
||
from osc.cli.cli import OscCommand, call | ||
from osc.cli.description import CommandDescription, Option | ||
from osc.cli.review.review import ReviewController | ||
|
||
|
||
class Review(CommandDescription, OscCommand): | ||
"""Show and modify reviews.""" | ||
cmd = 'review' | ||
opt_user = Option('U', 'user', 'use by_user', sub=True) | ||
opt_group = Option('G', 'group', 'use by_group', sub=True) | ||
opt_project = Option('P', 'project', 'use by_project', sub=True) | ||
opt_package = Option('p', 'package', 'use by_package', sub=True, | ||
oargs='project/package', nargs=1, default=[]) | ||
|
||
|
||
class ReviewList(CommandDescription, Review): | ||
"""List reviews. | ||
By default only requests with state review will be listed. | ||
Examples: | ||
osc review list api:// | ||
osc review list api://project | ||
osc review list api://project/package | ||
""" | ||
cmd = 'list' | ||
args = 'api://tgt_project?/tgt_package?' | ||
opt_state = Option('s', 'state', | ||
('list only requests which have a review with state ' | ||
'STATE'), | ||
choices=['new', 'accepted', 'revoked', 'declined'], | ||
action='append', default=['new']) | ||
func = call(ReviewController.list) | ||
|
||
|
||
class ReviewAccept(CommandDescription, Review): | ||
"""Accept a specific review. | ||
If no message is specified $EDITOR is opened. | ||
Example: | ||
osc review accept api://reqid [--message MESSAGE] --user <user> | ||
""" | ||
cmd = 'accept' | ||
args = 'api://reqid' | ||
opt_message = Option('m', 'message', 'specify a message') | ||
mutex_req_group = [Review.opt_user, Review.opt_group, Review.opt_project, | ||
Review.opt_package] | ||
func = call(ReviewController.change_review_state) | ||
func_defaults = {'method': 'accept'} | ||
|
||
|
||
class ReviewDecline(CommandDescription, Review): | ||
"""Decline a specific review. | ||
If no message is specified $EDITOR is opened. | ||
Example: | ||
osc review decline api://reqid [--message MESSAGE] --user <user> | ||
""" | ||
cmd = 'decline' | ||
args = 'api://reqid' | ||
opt_message = Option('m', 'message', 'specify a message') | ||
mutex_req_group = [Review.opt_user, Review.opt_group, Review.opt_project, | ||
Review.opt_package] | ||
func = call(ReviewController.change_review_state) | ||
func_defaults = {'method': 'decline'} | ||
|
||
|
||
class ReviewRevoke(CommandDescription, Review): | ||
"""Revoke a specific review. | ||
If no message is specified $EDITOR is opened. | ||
Example: | ||
osc review revoke api://reqid [--message MESSAGE] --user <user> | ||
""" | ||
cmd = 'revoke' | ||
args = 'api://reqid' | ||
opt_message = Option('m', 'message', 'specify a message') | ||
mutex_req_group = [Review.opt_user, Review.opt_group, Review.opt_project, | ||
Review.opt_package] | ||
func = call(ReviewController.change_review_state) | ||
func_defaults = {'method': 'revoke'} | ||
|
||
|
||
class ReviewAdd(CommandDescription, Review): | ||
"""Add a new review to the request. | ||
If no message is specified $EDITOR is opened. | ||
Example: | ||
osc review add api://reqid [--message MESSAGE] --user <user> | ||
""" | ||
cmd = 'add' | ||
args = 'api://reqid' | ||
opt_message = Option('m', 'message', 'specify a message') | ||
mutex_req_group = [Review.opt_user, Review.opt_group, Review.opt_project, | ||
Review.opt_package] | ||
func = call(ReviewController.add) |