diff --git a/lazy_github/lib/github/issues.py b/lazy_github/lib/github/issues.py index c9a27a7..e99d7c0 100644 --- a/lazy_github/lib/github/issues.py +++ b/lazy_github/lib/github/issues.py @@ -2,7 +2,7 @@ from typing import Literal from lazy_github.lib.github.client import GithubClient -from lazy_github.models.github import Issue, PullRequest, Repository, User +from lazy_github.models.github import Issue, PartialPullRequest, Repository, User IssueStateFilter = Literal["open"] | Literal["closed"] | Literal["all"] @@ -16,9 +16,9 @@ async def _list(client: GithubClient, repo: Repository, state: IssueStateFilter) result: list[Issue] = [] for issue in response.json(): if "draft" in issue: - result.append(PullRequest(**issue)) + result.append(PartialPullRequest(**issue, repo=repo)) else: - result.append(Issue(**issue)) + result.append(Issue(**issue, repo=repo)) return result @@ -29,6 +29,11 @@ async def _list(client: GithubClient, repo: Repository, state: IssueStateFilter) if __name__ == "__main__": import asyncio + import logging + + logging.basicConfig( + format="%(levelname)s [%(asctime)s] %(name)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S", level=logging.DEBUG + ) from lazy_github.lib.config import Config from lazy_github.lib.github.auth import token @@ -40,11 +45,11 @@ async def _list(client: GithubClient, repo: Repository, state: IssueStateFilter) default_branch="main", private=False, archived=False, - owner=User(login="gizmo385", id=1), + owner=User(login="gizmo385", id=1, html_url="wat"), ) issues = asyncio.run(_list(client, repo, "all")) for issue in issues: - if isinstance(issue, PullRequest): + if isinstance(issue, PartialPullRequest): print(f"Pull Request #{issue.number}: '{issue.title}' by {issue.user.login}") else: print(f"Issue #{issue.number}: {issue.title}") diff --git a/lazy_github/lib/github/pull_requests.py b/lazy_github/lib/github/pull_requests.py index 33b4c9d..1f3c877 100644 --- a/lazy_github/lib/github/pull_requests.py +++ b/lazy_github/lib/github/pull_requests.py @@ -1,8 +1,16 @@ from lazy_github.lib.github.client import GithubClient from lazy_github.lib.github.issues import list_all_issues -from lazy_github.models.github import PullRequest, Repository +from lazy_github.models.github import FullPullRequest, PartialPullRequest, Repository -async def list_for_repo(client: GithubClient, repo: Repository) -> list[PullRequest]: +async def list_for_repo(client: GithubClient, repo: Repository) -> list[PartialPullRequest]: issues = await list_all_issues(client, repo) - return [i for i in issues if isinstance(i, PullRequest)] + return [i for i in issues if isinstance(i, PartialPullRequest)] + + +async def get_full_pull_request(client: GithubClient, partial_pr: PartialPullRequest) -> FullPullRequest: + user = await client.user() + url = f"/repos/{user.login}/{partial_pr.repo.name}/pulls/{partial_pr.number}" + response = await client.get(url, headers=client.headers_with_auth_accept()) + response.raise_for_status() + return FullPullRequest(**response.json(), repo=partial_pr.repo) diff --git a/lazy_github/lib/messages.py b/lazy_github/lib/messages.py index 7b423e3..efc6902 100644 --- a/lazy_github/lib/messages.py +++ b/lazy_github/lib/messages.py @@ -2,7 +2,7 @@ from textual.message import Message -from lazy_github.models.github import Issue, PullRequest, Repository +from lazy_github.models.github import Issue, PartialPullRequest, Repository class RepoSelected(Message): @@ -23,7 +23,7 @@ class PullRequestSelected(Message): A message indicating that the user is looking for additional information on a particular pull request. """ - def __init__(self, pr: PullRequest) -> None: + def __init__(self, pr: PartialPullRequest) -> None: self.pr = pr super().__init__() @@ -39,13 +39,13 @@ def __init__(self, issues_and_pull_requests: list[Issue]) -> None: super().__init__() @cached_property - def pull_requests(self) -> list[PullRequest]: - return [pr for pr in self.issues_and_pull_requests if isinstance(pr, PullRequest)] + def pull_requests(self) -> list[PartialPullRequest]: + return [pr for pr in self.issues_and_pull_requests if isinstance(pr, PartialPullRequest)] @cached_property def issues(self) -> list[Issue]: return [ issue for issue in self.issues_and_pull_requests - if isinstance(issue, Issue) and not isinstance(issue, PullRequest) + if isinstance(issue, Issue) and not isinstance(issue, PartialPullRequest) ] diff --git a/lazy_github/models/github.py b/lazy_github/models/github.py index 8405dc1..8cb3b4a 100644 --- a/lazy_github/models/github.py +++ b/lazy_github/models/github.py @@ -1,3 +1,4 @@ +from datetime import datetime from enum import StrEnum from pydantic import BaseModel @@ -7,6 +8,7 @@ class User(BaseModel): login: str id: int name: str | None = None + html_url: str followers: int | None = None following: int | None = None @@ -44,10 +46,36 @@ class Issue(BaseModel): title: str body: str | None = None user: User + created_at: datetime + updated_at: datetime + closed_at: datetime | None = None closed_by: User | None = None assignee: User | None = None assignees: list[User] | None + repo: Repository -class PullRequest(Issue): +class Ref(BaseModel): + user: User + ref: str + + +class PartialPullRequest(Issue): + """ + A pull request that may be included in the response to a list issues API call and is missing some information + """ + draft: bool + + +class FullPullRequest(PartialPullRequest): + """More comprehensive details on a pull request from the API""" + + additions: int + deletions: int + changed_files: int + commits: int + head: Ref + base: Ref + html_url: str + merged_at: datetime | None diff --git a/lazy_github/ui/screens/primary.py b/lazy_github/ui/screens/primary.py index 0fe05fe..cf5a0ed 100644 --- a/lazy_github/ui/screens/primary.py +++ b/lazy_github/ui/screens/primary.py @@ -8,6 +8,7 @@ from lazy_github.lib.github.client import GithubClient from lazy_github.lib.github.issues import list_all_issues +from lazy_github.lib.github.pull_requests import get_full_pull_request from lazy_github.lib.messages import IssuesAndPullRequestsFetched, PullRequestSelected, RepoSelected from lazy_github.ui.widgets.actions import ActionsContainer from lazy_github.ui.widgets.command_log import CommandLogSection @@ -149,13 +150,15 @@ def compose(self) -> ComposeResult: yield SelectionsPane(self.client) yield SelectionDetailsPane(self.client) - def on_pull_request_selected(self, message: PullRequestSelected) -> None: + async def on_pull_request_selected(self, message: PullRequestSelected) -> None: log(f"PR = {message.pr}") + full_pr = await get_full_pull_request(self.client, message.pr) + log(f"Full PR = {full_pr}") tabbed_content = self.query_one("#selection_detail_tabs", TabbedContent) tabbed_content.clear_panes() - tabbed_content.add_pane(PrOverviewTabPane(message.pr)) - tabbed_content.add_pane(PrDiffTabPane(message.pr)) - tabbed_content.add_pane(PrConversationTabPane(message.pr)) + tabbed_content.add_pane(PrOverviewTabPane(full_pr)) + tabbed_content.add_pane(PrDiffTabPane(full_pr)) + tabbed_content.add_pane(PrConversationTabPane(full_pr)) tabbed_content.focus() diff --git a/lazy_github/ui/widgets/pull_requests.py b/lazy_github/ui/widgets/pull_requests.py index ac8f233..8cf7360 100644 --- a/lazy_github/ui/widgets/pull_requests.py +++ b/lazy_github/ui/widgets/pull_requests.py @@ -6,11 +6,10 @@ from textual.coordinate import Coordinate from textual.widgets import Label, ListView, Markdown, RichLog, Rule, TabPane -import lazy_github.lib.github.pull_requests as pr_api from lazy_github.lib.github.client import GithubClient from lazy_github.lib.messages import IssuesAndPullRequestsFetched, PullRequestSelected from lazy_github.lib.string_utils import bold, link, pluralize -from lazy_github.models.github import PullRequest +from lazy_github.models.github import FullPullRequest, PartialPullRequest from lazy_github.ui.widgets.command_log import log_event from lazy_github.ui.widgets.common import LazyGithubContainer, LazyGithubDataTable @@ -22,7 +21,7 @@ class PullRequestsContainer(LazyGithubContainer): def __init__(self, client: GithubClient, *args, **kwargs) -> None: self.client = client - self.pull_requests: Dict[int, PullRequest] = {} + self.pull_requests: Dict[int, PartialPullRequest] = {} self.status_column_index = -1 self.number_column_index = -1 self.title_column_index = -1 @@ -58,9 +57,10 @@ async def on_issues_and_pull_requests_fetched(self, message: IssuesAndPullReques rows.append((pr.state, pr.number, pr.user.login, pr.title)) self.table.add_rows(rows) - async def get_selected_pr(self) -> PullRequest: + async def get_selected_pr(self) -> PartialPullRequest: pr_number_coord = Coordinate(self.table.cursor_row, self.number_column_index) number = self.table.get_cell_at(pr_number_coord) + # full_pr = pr_api.get_pull_request(self.client, number) return self.pull_requests[number] @on(LazyGithubDataTable.RowSelected, "#pull_requests_table") @@ -77,15 +77,19 @@ class PrOverviewTabPane(TabPane): } """ - def __init__(self, pr: PullRequest) -> None: + def __init__(self, pr: FullPullRequest) -> None: super().__init__("Overview", id="overview_pane") self.pr = pr - def _old_compose(self) -> ComposeResult: + def compose(self) -> ComposeResult: pr_link = link(f"(#{self.pr.number})", self.pr.html_url) user_link = link(self.pr.user.login, self.pr.user.html_url) - merge_from = bold(f"{self.pr.head.user.login}:{self.pr.head.ref}") - merge_to = bold(f"{self.pr.base.user.login}:{self.pr.base.ref}") + merge_from = None + if self.pr.head: + merge_from = bold(f"{self.pr.head.user.login}:{self.pr.head.ref}") + merge_to = None + if self.pr.base: + merge_to = bold(f"{self.pr.base.user.login}:{self.pr.base.ref}") change_summary = " • ".join( [ @@ -117,11 +121,11 @@ def _old_compose(self) -> ComposeResult: class PrDiffTabPane(TabPane): - def __init__(self, pr: PullRequest) -> None: + def __init__(self, pr: FullPullRequest) -> None: super().__init__("Diff", id="diff_pane") self.pr = pr - def _old_compose(self) -> ComposeResult: + def compose(self) -> ComposeResult: with ScrollableContainer(): yield RichLog(id="diff_contents", highlight=True) @@ -140,7 +144,7 @@ def on_mount(self) -> None: class PrConversationTabPane(TabPane): - def __init__(self, pr: PullRequest) -> None: + def __init__(self, pr: FullPullRequest) -> None: super().__init__("Conversation", id="conversation_pane") self.pr = pr