From 0fa0063d5b2c7743c4cb733a6aaecfa057148245 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Tue, 29 Sep 2020 11:56:51 +0100 Subject: [PATCH] Fixed queries after github enablement (#24) Makes incoming query work on both supported backends. --- lib/gri/__main__.py | 32 +++++++++++++++----------------- lib/gri/abc.py | 11 +++++++++-- lib/gri/gerrit.py | 24 +++++++++++++++++------- lib/gri/github.py | 32 +++++++++++++++++++++----------- setup.cfg | 1 + 5 files changed, 63 insertions(+), 37 deletions(-) diff --git a/lib/gri/__main__.py b/lib/gri/__main__.py index f605d20..43de00a 100755 --- a/lib/gri/__main__.py +++ b/lib/gri/__main__.py @@ -14,7 +14,7 @@ from rich.markdown import Markdown from rich.table import Table -from gri.abc import Server +from gri.abc import Query, Server from gri.console import TERMINAL_THEME, bootstrap, get_logging_level from gri.constants import RC_CONFIG_ERROR, RC_PARTIAL_RUN from gri.gerrit import GerritServer @@ -66,6 +66,7 @@ def __init__(self, ctx: click.Context) -> None: self.servers: List[Server] = [] self.user = ctx.params["user"] self.errors = 0 # number of errors encountered + self.query_details: List[str] = [] server = ctx.params["server"] try: for srv in ( @@ -96,10 +97,11 @@ def __init__(self, ctx: click.Context) -> None: self.reviews: List[Review] = list() term.print(self.header()) - def run_query(self, query: str) -> int: + def run_query(self, query: Query) -> int: """Performs a query and stores result inside reviews attribute""" errors = 0 self.reviews.clear() + self.query_details = [] for item in self.servers: try: @@ -108,6 +110,8 @@ def run_query(self, query: str) -> int: except (HTTPError, RuntimeError) as exc: LOG.error(exc) errors += 1 + self.query_details.append(item.mk_query(query)) + return errors def header(self) -> str: @@ -116,7 +120,7 @@ def header(self) -> str: def report( self, - query: str = None, + query: Query, title: str = "Reviews", max_score: int = 1, action: Optional[str] = None, @@ -154,8 +158,7 @@ def report( term.print() term.print(table) - extra = f" from: [cyan]{query}[/]" if query else "" - term.print(f"[dim]-- {cnt} changes listed{extra}[/]") + term.print(f"[dim]-- {cnt} changes listed {self.query_details}[/]") def display_config(self) -> None: msg = yaml.dump( @@ -269,19 +272,18 @@ def owned(ctx): """Changes originated from current user (implicit)""" # query = "status:open" # query += f" owner:{ctx.obj.user}" - query = "owned" if ctx.obj.user == "self": title = "Own reviews" else: title = f"Reviews owned by {ctx.obj.user}" - ctx.obj.report(query=query, title=title) + ctx.obj.report(query=Query("owned"), title=title) @cli.command() @click.pass_context def incoming(ctx): """Incoming reviews""" - ctx.obj.report(query="incoming", title=incoming.__doc__) + ctx.obj.report(query=Query("incoming"), title=incoming.__doc__) @cli.command() @@ -293,8 +295,7 @@ def incoming(ctx): ) def merged(ctx, age): """Merged in the last number of days""" - query = f"status:merged -age:{age}d owner:{ctx.obj.user}" - ctx.obj.report(query=query, title=f"Merged Reviews ({age}d)") + ctx.obj.report(query=Query("merged", age=age), title=f"Merged Reviews ({age}d)") # @cli.command() @@ -309,16 +310,14 @@ def merged(ctx, age): @click.pass_context def watched(ctx): """Watched reviews based on server side filters""" - query = f"watchedby:{ctx.obj.user} status:open" - ctx.obj.report(query=query, title=watched.__doc__) + ctx.obj.report(query=Query("watched"), title=watched.__doc__) @cli.command() @click.pass_context def draft(ctx): """Draft reviews or with draft comments.""" - query = "status:open owner:self has:draft OR draftby:self" - ctx.obj.report(query=query, title=draft.__doc__) + ctx.obj.report(query=Query("draft"), title=draft.__doc__) @cli.command() @@ -332,12 +331,11 @@ def draft(ctx): def abandon(ctx, age): """Abandon changes (delete for drafts) when they are >90 days old and with very low score. Requires -f to perform the action.""" - query = f"status:open age:{age}d owner:{ctx.obj.user}" ctx.obj.report( - query=query, + query=Query("abandon", age=age), title=f"Reviews to abandon ({age}d)", - max_score=0.1, + max_score=1.0, action="abandon", ) diff --git a/lib/gri/abc.py b/lib/gri/abc.py index f1456f0..220bcc4 100644 --- a/lib/gri/abc.py +++ b/lib/gri/abc.py @@ -1,19 +1,26 @@ import datetime from abc import ABC +from dataclasses import dataclass from typing import Dict, List from gri.console import link from gri.label import Label +@dataclass +class Query: + name: str + age: int = 0 + + class Server(ABC): # pylint: disable=too-few-public-methods def __init__(self) -> None: self.name = "Unknown" - def query(self, query=None) -> List: + def query(self, query: Query) -> List: raise NotImplementedError() - def mk_query(self, query: str) -> str: + def mk_query(self, query: Query) -> str: raise NotImplementedError() diff --git a/lib/gri/gerrit.py b/lib/gri/gerrit.py index 145c505..a8b9bd6 100644 --- a/lib/gri/gerrit.py +++ b/lib/gri/gerrit.py @@ -7,7 +7,7 @@ import requests from requests.auth import HTTPBasicAuth, HTTPDigestAuth -from gri.abc import Server +from gri.abc import Query, Server from gri.review import ChangeRequest try: @@ -73,7 +73,7 @@ def __init__(self, url: str, name: str = "", ctx=None) -> None: } ) - def query(self, query=None) -> List: + def query(self, query: Query) -> List: gerrit_query = self.mk_query(query) @@ -90,13 +90,23 @@ def query(self, query=None) -> List: for r in self.parsed(self.__session.get(url)) ] - def mk_query(self, query: str) -> str: - if query == "owned": + def mk_query(self, query: Query) -> str: + if query.name == "owned": return f"status:open owner:{self.ctx.obj.user}" - if query == "incoming": + if query.name == "incoming": return f"reviewer:{self.ctx.obj.user} status:open" - - raise NotImplementedError(f"{query} query not implemented by {self.__class__}") + if query.name == "watched": + return f"watchedby:{self.ctx.obj.user} status:open" + if query.name == "abandon": + return f"status:open age:{query.age}d owner:{self.ctx.obj.user}" + if query.name == "draft": + return "status:open owner:self has:draft OR draftby:self" + if query.name == "merged": + return f"status:merged -age:{query.age}d owner:{self.ctx.obj.user}" + + raise NotImplementedError( + f"{query.name} query not implemented by {self.__class__}" + ) @staticmethod def parsed(result) -> dict: diff --git a/lib/gri/github.py b/lib/gri/github.py index c66815b..64c0e79 100644 --- a/lib/gri/github.py +++ b/lib/gri/github.py @@ -1,11 +1,11 @@ -import datetime import logging import os +from datetime import datetime, timedelta from typing import Dict, List import github -from gri.abc import Review, Server +from gri.abc import Query, Review, Server from gri.review import Label try: @@ -25,9 +25,9 @@ def __init__(self, url: str, name: str = "", ctx=None) -> None: token = os.environ.get("HOMEBREW_GITHUB_API_TOKEN") self.github = github.Github(login_or_token=token) - def query(self, query=None) -> List: + def query(self, query: Query) -> List: reviews = [] - limit = 5 + limit = 10 results = self.github.search_issues(self.mk_query(query)) for _, item in zip(range(limit), results): review = PullRequest(data=item.raw_data, server=self) @@ -36,13 +36,25 @@ def query(self, query=None) -> List: # print(mine) return reviews - def mk_query(self, query: str) -> str: # pylint: disable=no-self-use + def mk_query(self, query: Query) -> str: # pylint: disable=no-self-use """Return query string based on """ - if query == "owned": + # https://docs.github.com/en/free-pro-team@latest/github/searching-for-information-on-github/searching-issues-and-pull-requests + if query.name == "owned": return "is:pr is:open author:@me" - if query == "incoming": + if query.name == "incoming": return "is:pr is:open involves:@me -author:@me" - raise NotImplementedError(f"Unable to build query for {query}") + if query.name == "watched": + return "is:pr is:open involves:@me -author:@me" + if query.name == "abandon": + day = (datetime.now() - timedelta(days=query.age)).date().isoformat() + return f"is:pr is:open author:@me updated:<={day}" + if query.name == "draft": + return "is:pr draft:true is:open author:@me" + if query.name == "merged": + day = (datetime.now() - timedelta(days=query.age)).date().isoformat() + return f"is:pr is:merged author:@me updated:>={day}" + + raise NotImplementedError(f"Unable to build query for {query.name}") class PullRequest(Review): # pylint: disable=too-many-instance-attributes @@ -54,9 +66,7 @@ def __init__(self, data: dict, server) -> None: self.data = data # LOG.error(data) self.server = server - self.updated = datetime.datetime.strptime( - self.data["updated_at"], "%Y-%m-%dT%H:%M:%SZ" - ) + self.updated = datetime.strptime(self.data["updated_at"], "%Y-%m-%dT%H:%M:%SZ") self.state = data["state"] path = urlparse(self.url).path.split("/") self.org = path[1] diff --git a/setup.cfg b/setup.cfg index 1a856bd..4dc16f3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -56,6 +56,7 @@ setup_requires = install_requires = click>=7.1.2 click-help-colors>=0.6 + dataclasses; python_version<"3.7" pygithub pyyaml>=5.3.1 requests