summaryrefslogtreecommitdiff
path: root/kodereviewer/models/file.py
diff options
context:
space:
mode:
Diffstat (limited to 'kodereviewer/models/file.py')
-rw-r--r--kodereviewer/models/file.py180
1 files changed, 178 insertions, 2 deletions
diff --git a/kodereviewer/models/file.py b/kodereviewer/models/file.py
index 246bd5b..4f0d139 100644
--- a/kodereviewer/models/file.py
+++ b/kodereviewer/models/file.py
@@ -1,8 +1,14 @@
+import logging
from copy import copy
+from dataclasses import dataclass, field
from enum import IntEnum, auto
-from typing import Any, Optional
+from pathlib import Path
+from typing import Any, Optional, Self
-from PySide6.QtCore import QAbstractListModel, QByteArray, QModelIndex, QObject, QPersistentModelIndex, QStandardPaths, QUrl, Qt, Signal, Slot, Property
+from PySide6.QtCore import (
+ QAbstractListModel, QAbstractItemModel, QByteArray, QModelIndex, QObject,
+ QPersistentModelIndex, QStandardPaths, QUrl, Qt, Signal, Slot, Property
+)
from PySide6.QtQml import QmlElement
from kodereviewer.project import Project
@@ -11,6 +17,176 @@ from kodereviewer.data import ChangedFile, Label, PullRequest
QML_IMPORT_NAME = "org.deprecated.kodereviewer"
QML_IMPORT_MAJOR_VERSION = 1
+logger = logging.getLogger(__name__)
+
+
+@dataclass
+class FileItem:
+ filename: str
+ parent: Self | None
+ children: list[Self] = field(default_factory=list)
+
+ def append(self, child: Self):
+ self.children.append(child)
+
+ def child(self, row: int) -> Self | None:
+ if row >= 0 and row < len(self.children):
+ return self.children[row]
+ return None
+
+ def row(self) -> int:
+ """Reports the item's location within it's parent."""
+ if self.parent is None:
+ return 0
+ for i, child in enumerate(self.parent.children):
+ if self is child:
+ return i
+
+ raise Exception('Should not happen!')
+
+ def column_count(self) -> int:
+ """Only the filename."""
+ return 1
+
+ def __str__(self) -> str:
+ return f'{self.filename} | {id(self.parent)} | {[x.filename for x in self.children]}'
+
+
+@QmlElement
+class TreeFileModel(QAbstractItemModel):
+
+ filenames: list[str] = field(default_factory=list)
+ dir_mapping: dict[str, FileItem] = field(default_factory=dict)
+ root_node: FileItem
+ _pull_request: Optional[PullRequest]
+
+ def __init__(self):
+ super().__init__()
+ self.root_node = FileItem('./', None)
+ self._pull_request = None
+
+ def load_files(self, filenames: list[str]):
+ logger.info(f'Loading {filenames}')
+ self.filenames = filenames
+ self.dir_mapping: dict[str, FileItem] = {}
+ root_node = FileItem('./', None)
+ for file in self.filenames:
+ p = Path(file)
+ directories = p.parts[:-1]
+ fname = p.name
+ current_path = Path('')
+ parent: FileItem = root_node
+ for dir in directories:
+ current_path = current_path / dir
+ if str(current_path) not in self.dir_mapping:
+ logger.info(f'Creating {current_path}')
+ self.dir_mapping[str(current_path)] = FileItem(
+ str(current_path), parent
+ )
+ logger.info(f'Appending to {parent}')
+ parent.append(self.dir_mapping[str(current_path)])
+
+ parent = self.dir_mapping[str(current_path)]
+ file_item = FileItem(fname, parent)
+ parent.append(file_item)
+ self.root_node = root_node
+ logger.info(self.root_node)
+
+ pullRequestChanged = Signal()
+
+ 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
+
+ self.beginResetModel()
+ self._pull_request = pull_request
+ self._pull_request.filesLoaded.connect(self._reset_model)
+ self.endResetModel()
+ self.pullRequestChanged.emit()
+
+ pullRequest = Property(PullRequest, fget=get_pull_request, fset=set_pull_request,
+ notify=pullRequestChanged)
+
+ def _reset_model(self) -> None:
+ logger.info('reseting model')
+ self.beginResetModel()
+ filenames = [f.filename for f in self._pull_request.files]
+ self.load_files(filenames)
+ self.endResetModel()
+
+ def index(self, row: int, column: int,
+ index: QModelIndex = QModelIndex()) -> QModelIndex:
+ """Returns the index of the item in the model specified by the given row, column and parent index."""
+ if not self.hasIndex(row, column, index):
+ return QModelIndex()
+ item: FileItem
+ if not index.isValid():
+ item = self.root_node
+ else:
+ item = index.internalPointer()
+
+ if item.child(row):
+ return self.createIndex(row, column, item.child(row))
+ return QModelIndex()
+
+ def parent(self, index: QModelIndex) -> QModelIndex:
+ """Returns the parent of the model item with the given index
+
+ If the item has no parent, an invalid QModelIndex is returned.
+ """
+ if not index.isValid():
+ return QModelIndex()
+
+ item: FileItem = index.internalPointer()
+ parent: FileItem = item.parent
+ if parent == self.root_node:
+ return QModelIndex()
+ return self.createIndex(parent.row(), 0, parent)
+
+ def rowCount(self, index: QModelIndex = QModelIndex()) -> int:
+ """Returns the number of rows under the given parent
+
+ When the parent is valid it means that rowCount is returning
+ the number of children of parent. """
+
+ parent: FileItem
+ if index.isValid():
+ parent = index.internalPointer()
+ else:
+ parent = self.root_node
+
+ return len(parent.children)
+
+ def columnCount(self, parent: QModelIndex = QModelIndex()) -> int:
+ return 1
+
+ FilenameRole = Qt.ItemDataRole.UserRole + 1
+
+ def data(self, index: QModelIndex, role: int) -> Any:
+ if not index.isValid():
+ return ''
+ filename = index.internalPointer().filename
+ name = Path(filename).name
+ logger.info(index)
+ logger.info(f'Role: {role} | {self.FilenameRole}')
+ if role == Qt.ItemDataRole.DisplayRole:
+ return name
+ if role == self.FilenameRole:
+ logger.info(name)
+ return name
+ return None
+
+ def roleNames(self) -> dict[int, QByteArray]:
+ return {
+ self.FilenameRole: QByteArray(b'filename'),
+ }
+
@QmlElement
class FileModel(QAbstractListModel):