File indexing completed on 2024-12-01 08:16:20

0001 """
0002 Module containing issues command
0003 """
0004 
0005 # SPDX-FileCopyrightText: 2020 Benjamin Port <benjamin.port@kde.org>
0006 #
0007 # SPDX-License-Identifier: GPL-2.0-or-later
0008 
0009 import argparse
0010 import os
0011 import sys
0012 from typing import List, Dict
0013 
0014 from gitlab.v4.objects import ProjectIssue
0015 from gitlab.exceptions import GitlabGetError
0016 
0017 from lab.repositoryconnection import RepositoryConnection
0018 from lab.utils import TextFormatting, Utils, LogType
0019 from lab.table import Table
0020 
0021 
0022 def parser(
0023     subparsers: argparse._SubParsersAction,  # pylint: disable=protected-access
0024 ) -> argparse.ArgumentParser:
0025     """
0026     Subparser for issues command
0027     :param subparsers: subparsers object from global parser
0028     :return: issues subparser
0029     """
0030 
0031     issues_parser: argparse.ArgumentParser = subparsers.add_parser("issues", help="Gitlab issues")
0032 
0033     issues_parser.add_argument(
0034         "--opened",
0035         help="Show opened issues",
0036         action="store_true",
0037     )
0038     issues_parser.add_argument(
0039         "--closed",
0040         help="Show closed issues",
0041         action="store_true",
0042     )
0043     group = issues_parser.add_mutually_exclusive_group()
0044     group.add_argument(
0045         "--assigned",
0046         help="Show only issues assigned to me",
0047         action="store_true",
0048     )
0049     group.add_argument(
0050         "--project",
0051         help="Show all project issues and not only the one you authored",
0052         action="store_true",
0053     )
0054     issues_parser.add_argument(
0055         "issue_id", help="Show issue by id if provided", metavar="issue_id", type=int, nargs="?"
0056     )
0057     issues_parser.add_argument("--web", help="open on web browser", action="store_true")
0058     return issues_parser
0059 
0060 
0061 def run(args: argparse.Namespace) -> None:
0062     """
0063     run merge request list command
0064     :param args: parsed arguments
0065     """
0066     if args.issue_id is not None:
0067         issue: IssuesShow = IssuesShow(args.issue_id)
0068         if args.web:
0069             issue.open_web()
0070         else:
0071             print(issue)
0072     else:
0073         lister: IssuesList = IssuesList(args.opened, args.closed, args.assigned, args.project)
0074         if args.web:
0075             lister.open_web()
0076         else:
0077             lister.print_formatted_list()
0078 
0079 
0080 class IssuesList(RepositoryConnection):
0081     """
0082     Lists all merge requests of the current repository
0083     """
0084 
0085     opened: bool = True
0086     closed: bool = True
0087     assigned: bool = False
0088     project: bool = False
0089 
0090     def __init__(self, opened: bool, closed: bool, assigned: bool, for_project: bool) -> None:
0091         RepositoryConnection.__init__(self)
0092         self.opened = opened
0093         self.closed = closed
0094         self.assigned = assigned
0095         self.for_project = for_project
0096 
0097     def print_formatted_list(self) -> None:
0098         """
0099         prints the list of issues to the terminal formatted as a table
0100         """
0101         table = Table()
0102         args: Dict[str, str] = {}
0103 
0104         # compute filters
0105         state: str = "all"
0106         if self.opened and not self.closed:
0107             state = "opened"
0108         elif self.closed and not self.opened:
0109             state = "closed"
0110         args["state"] = state
0111 
0112         issues: List[ProjectIssue] = []
0113         if not self.for_project and self.assigned:
0114             # List issues all over the instance assigned to me
0115             args["scope"] = "assigned_to_me"
0116             issues = self._connection.issues.list(**args)
0117         elif not self.for_project and not self.assigned:
0118             # Request both created and assigned issues on the whole instance
0119             args["scope"] = "created_by_me"
0120             issues = self._connection.issues.list(**args)
0121             args["scope"] = "assigned_to_me"
0122             issues += self._connection.issues.list(**args)
0123         elif self.for_project and not self.assigned:
0124             # Request all issues on the current project
0125             args["scope"] = "all"
0126             issues = self._remote_project.issues.list(**args)
0127 
0128         for issue in issues:
0129             formatting = TextFormatting.GREEN if issue.state == "opened" else TextFormatting.RED
0130             row: List[str] = [
0131                 TextFormatting.BOLD + issue.references["full"] + TextFormatting.END,
0132                 issue.title,
0133                 formatting + issue.state + TextFormatting.END,
0134             ]
0135 
0136             table.add_row(row)
0137 
0138         table.print()
0139 
0140     def open_web(self) -> None:
0141         """
0142         Open issue with xdg-open
0143         """
0144         if self._remote_project.issues_enabled:
0145             Utils.xdg_open(f"{self._remote_project.web_url}/-/issues")
0146         else:
0147             Utils.log(LogType.ERROR, "Issue are disabled for this project")
0148 
0149 
0150 class IssuesShow(RepositoryConnection):
0151     """
0152     Show issue
0153     """
0154 
0155     def __init__(self, issue_id: int):
0156         RepositoryConnection.__init__(self)
0157         try:
0158             self.issue: ProjectIssue = self._remote_project.issues.get(issue_id, lazy=False)
0159         except GitlabGetError:
0160             Utils.log(LogType.WARNING, f"No issue with ID {issue_id}")
0161             sys.exit(1)
0162 
0163     def open_web(self) -> None:
0164         """
0165         Open issue with xdg-open
0166         """
0167         Utils.xdg_open(self.issue.web_url)
0168 
0169     def __str__(self) -> str:
0170         formatting = TextFormatting.GREEN if self.issue.state == "opened" else TextFormatting.RED
0171         textbuffer: str = ""
0172         textbuffer += (
0173             TextFormatting.BOLD
0174             + self.issue.title
0175             + TextFormatting.END
0176             + f" (#{self.issue.iid}) "
0177             + formatting
0178             + self.issue.state
0179             + TextFormatting.END
0180             + os.linesep
0181         )
0182         textbuffer += self.issue.description
0183         return textbuffer