From c651ae6d7a11c77a607543a1afae863b20b6d174 Mon Sep 17 00:00:00 2001 From: Matias Linares Date: Sun, 22 Sep 2024 15:37:36 -0300 Subject: Pull request description and comments working --- kodereviewer/models/__init__.py | 9 ++++ kodereviewer/models/comments.py | 89 +++++++++++++++++++++++++++++++++++ kodereviewer/models/project.py | 92 +++++++++++++++++++++++++++++++++++++ kodereviewer/models/pull_request.py | 75 ++++++++++++++++++++++++++++++ 4 files changed, 265 insertions(+) create mode 100644 kodereviewer/models/__init__.py create mode 100644 kodereviewer/models/comments.py create mode 100644 kodereviewer/models/project.py create mode 100644 kodereviewer/models/pull_request.py (limited to 'kodereviewer/models') diff --git a/kodereviewer/models/__init__.py b/kodereviewer/models/__init__.py new file mode 100644 index 0000000..853b618 --- /dev/null +++ b/kodereviewer/models/__init__.py @@ -0,0 +1,9 @@ +from kodereviewer.models.comments import CommentModel +from kodereviewer.models.project import ProjectModel +from kodereviewer.models.pull_request import PullRequestModel + +__all__ = [ + 'CommentModel', + 'ProjectModel', + 'PullRequestModel', +] diff --git a/kodereviewer/models/comments.py b/kodereviewer/models/comments.py new file mode 100644 index 0000000..06de3d7 --- /dev/null +++ b/kodereviewer/models/comments.py @@ -0,0 +1,89 @@ +from enum import auto, IntEnum +from typing import Optional + +from PySide6.QtCore import QAbstractListModel, QByteArray, QModelIndex, QObject, QPersistentModelIndex, QStandardPaths, QUrl, Qt, Signal, Slot, Property +from PySide6.QtQml import QmlElement + +from kodereviewer.data import Comment, PullRequest + +QML_IMPORT_NAME = "org.deprecated.kodereviewer" +QML_IMPORT_MAJOR_VERSION = 1 + + +@QmlElement +class CommentModel(QAbstractListModel): + + _pull_request: Optional[PullRequest] + + class Roles(IntEnum): + Body = Qt.ItemDataRole.UserRole + 1 + CreatedAt = auto() + UpdatedAt = auto() + Username = auto() + AvatarUrl = auto() + + + def __init__(self): + super().__init__() + self._pull_request = None + + def get_pull_request(self) -> Optional[PullRequest]: + return self._pull_request + + def set_pull_request(self, pull_request: Optional[PullRequest]) -> None: + if pull_request is None: + return + + if self._pull_request is not None and self._pull_request == pull_request: + return + + print(f"Setting up pull request to {pull_request}") + self.beginResetModel() + self._pull_request = pull_request + self._pull_request.commentsLoaded.connect(self._reset_model) + self.endResetModel() + self.pullRequestChanged.emit() + + pullRequestChanged = Signal() + pullRequest = Property(PullRequest, fget=get_pull_request, fset=set_pull_request, + notify=pullRequestChanged) + + def _reset_model(self) -> None: + self.beginResetModel() + self.endResetModel() + + def data(self, + index: QModelIndex | QPersistentModelIndex, + role: int = Qt.ItemDataRole.DisplayRole) -> object: + if self._pull_request is None: + return None + + comment: Comment = self._pull_request._comments[index.row()] + + if role == self.Roles.Body: + return comment.body + if role == self.Roles.CreatedAt: + return comment.created_at.strftime("%Y-%M-%d %H:%m") + if role == self.Roles.UpdatedAt: + return comment.updated_at.strftime("%Y-%M-%d %H:%m") + if role == self.Roles.Username: + return comment.user.username + if role == self.Roles.AvatarUrl: + return comment.user.avatar_url + if role == Qt.ItemDataRole.DisplayRole: + return comment.body + return None + + def rowCount(self, parent: QModelIndex | QPersistentModelIndex = QModelIndex()) -> int: + if self._pull_request is None: + return 0 + return len(self._pull_request._comments) + + def roleNames(self) -> dict[int, QByteArray]: + return { + self.Roles.Body: QByteArray(b'body'), + self.Roles.CreatedAt: QByteArray(b'createdAt'), + self.Roles.UpdatedAt: QByteArray(b'updatedAt'), + self.Roles.Username: QByteArray(b'username'), + self.Roles.AvatarUrl: QByteArray(b'avatarUrl'), + } diff --git a/kodereviewer/models/project.py b/kodereviewer/models/project.py new file mode 100644 index 0000000..c676157 --- /dev/null +++ b/kodereviewer/models/project.py @@ -0,0 +1,92 @@ +import json +from os import path +from typing import Any + +from PySide6.QtCore import QAbstractListModel, QByteArray, QModelIndex, QObject, QPersistentModelIndex, QStandardPaths, QUrl, Qt, Signal, Slot, Property +from PySide6.QtQml import QmlElement + +from kodereviewer.project import Project + +QML_IMPORT_NAME = "org.deprecated.kodereviewer" +QML_IMPORT_MAJOR_VERSION = 1 + + +@QmlElement +class ProjectModel(QAbstractListModel): + """Projects list!""" + + projects: list[Project] + + NameRole = Qt.ItemDataRole.UserRole + 1 + OwnerRole = NameRole + 1 + UrlRole = OwnerRole + 1 + + def __init__(self): + super().__init__() + self.projects = [] + + project_config = self._project_file() + try: + with open(project_config) as fp: + data = json.load(fp) + if isinstance(data, list): + self._load_projects(data) + except OSError as e: + pass + + def _project_file(self) -> str: + app_data = QStandardPaths.writableLocation(QStandardPaths.AppDataLocation) + project_config = path.join(app_data, 'projects.json') + return project_config + + def data(self, + index: QModelIndex | QPersistentModelIndex, + role: int = Qt.ItemDataRole.DisplayRole) -> object: + project = self.projects[index.row()] + + if role == Qt.ItemDataRole.DisplayRole: + return project.name + if role == self.NameRole: + return project.name + if role == self.OwnerRole: + return project.owner + if role == self.UrlRole: + return project.url + + return None + + def rowCount(self, parent: QModelIndex | QPersistentModelIndex = QModelIndex()) -> int: + return len(self.projects) + + def roleNames(self) -> dict[int, QByteArray]: + return { + self.NameRole: QByteArray(b"name"), + self.OwnerRole: QByteArray(b"owner"), + self.UrlRole: QByteArray(b"url") + } + + @Slot(int, result=Project) + def get(self, index: int) -> Project: + return self.projects[index] + + def _load_projects(self, data: list[dict[str, Any]]) -> None: + for project in data: + self.projects.append(Project( + project['name'], + project['owner'], + project['url'] + )) + + def _save_projects(self): + project_config = self._project_file() + data = [{'name': project.name, 'owner': project.owner, 'url': project.url} for project in self.projects] + + with open(self._project_file(), 'w') as fp: + json.dump(data, fp) + + @Slot(str, str, str) + def add(self, name: str, owner: str, url: str) -> None: + self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount()) + self.projects.append(Project(name, owner, url)) + self._save_projects() + self.endInsertRows() diff --git a/kodereviewer/models/pull_request.py b/kodereviewer/models/pull_request.py new file mode 100644 index 0000000..7d08efa --- /dev/null +++ b/kodereviewer/models/pull_request.py @@ -0,0 +1,75 @@ +import json +from os import path +from typing import Any, Optional + +from PySide6.QtCore import QAbstractListModel, QByteArray, QModelIndex, QObject, QPersistentModelIndex, QStandardPaths, QUrl, Qt, Signal, Slot, Property +from PySide6.QtQml import QmlElement + +from kodereviewer.project import Project +from kodereviewer.data import PullRequest + +QML_IMPORT_NAME = "org.deprecated.kodereviewer" +QML_IMPORT_MAJOR_VERSION = 1 + +@QmlElement +class PullRequestModel(QAbstractListModel): + + _project: Optional[Project] + + NumberRole = Qt.ItemDataRole.UserRole + 1 + TitleRole = NumberRole + 1 + + def __init__(self): + super().__init__() + + def data(self, + index: QModelIndex | QPersistentModelIndex, + role: int = Qt.ItemDataRole.DisplayRole) -> object: + if self._project is None: + return None + + pull_request = self._project.pullRequests[index.row()] + if role == self.NumberRole: + return pull_request.number + if role == self.TitleRole: + return pull_request.title + if role == Qt.ItemDataRole.DisplayRole: + return f'{pull_request.number} - {pull_request.title}' + return None + + def rowCount(self, parent: QModelIndex | QPersistentModelIndex = QModelIndex()) -> int: + if self._project is not None: + return len(self._project.pullRequests) + return 0 + + def roleNames(self) -> dict[int, QByteArray]: + return { + self.NumberRole: QByteArray(b"number"), + self.TitleRole: QByteArray(b"title"), + } + + def get_project(self) -> Optional[Project]: + return self._project + + def set_project(self, project: Optional[Project])-> None: + if project is None: + return + + self._project = project + assert self._project is not None + + self._project.pullRequestsChanged.connect(self._reset_model) + + project = Property(Project, fget=get_project, fset=set_project) + + @Slot(int, result=PullRequest) + def get(self, index: int) -> Optional[PullRequest]: + if self._project is not None: + return self._project.pullRequests[index] + return None + + + def _reset_model(self) -> None: + print("Reseting pull request model") + self.beginResetModel() + self.endResetModel() -- cgit v1.2.3-70-g09d2