Expanshion

This commit is contained in:
StNicolay 2024-08-09 20:33:39 +03:00
parent ff629b8903
commit 654a1e7191
Signed by: StNicolay
GPG Key ID: 9693D04DCD962B0D
3 changed files with 130 additions and 32 deletions

BIN
assets/file.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 KiB

BIN
assets/folder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -3,13 +3,19 @@ from __future__ import annotations
import datetime import datetime
import os import os
import uuid import uuid
from typing import Protocol from typing import Protocol, Self
import httpx import httpx
import pydantic import pydantic
import state import state
from PyQt6.QtCore import QPoint, Qt from PyQt6.QtCore import QPoint, Qt
from PyQt6.QtGui import QAction, QDragEnterEvent, QDragMoveEvent, QDropEvent, QIcon from PyQt6.QtGui import (
QAction,
QDragEnterEvent,
QDragMoveEvent,
QDropEvent,
QIcon,
)
from PyQt6.QtWidgets import ( from PyQt6.QtWidgets import (
QHBoxLayout, QHBoxLayout,
QListWidget, QListWidget,
@ -19,6 +25,8 @@ from PyQt6.QtWidgets import (
QPushButton, QPushButton,
QVBoxLayout, QVBoxLayout,
QWidget, QWidget,
QLabel,
QFileDialog,
) )
from request_client import RequestClient from request_client import RequestClient
@ -30,7 +38,13 @@ class DisplayProtocol(Protocol):
def delete(self) -> None: def delete(self) -> None:
raise NotImplementedError raise NotImplementedError
def details(self) -> QWidget: def details(self, list: FileListWidget) -> QWidget:
raise NotImplementedError
def icon(self) -> QIcon:
raise NotImplementedError
def double_click(self, list: FileListWidget) -> None:
raise NotImplementedError raise NotImplementedError
@ -46,11 +60,51 @@ class File(pydantic.BaseModel):
return self.file_name return self.file_name
def delete(self) -> None: def delete(self) -> None:
RequestClient().client.delete("/files", params={"file_id": self.file_id}) RequestClient().client.delete(
"/files", params={"file_id": self.file_id}
)
def details_button(self) -> QPushButton: def details(self, list: FileListWidget) -> QWidget:
# TODO del list
raise NotImplementedError details = (
f"file id: {self.file_id}\nfile_name: {self.file_name}\n"
+ f"file_size: {self._format_bytes(self.file_size)}\n"
+ f"created at: {self.created_at}\nupdated at: {self.updated_at}"
)
label = QLabel()
label.setWindowTitle("File info")
label.setText(details)
return label
@staticmethod
def _format_bytes(size: int):
power = 2**10
n = 0
power_labels = {0: "", 1: "kilo", 2: "mega", 3: "giga", 4: "tera"}
while size > power and n < 4:
size /= power
n += 1
return size, power_labels[n] + "bytes"
def icon(self) -> QIcon:
return QIcon("assets/file.png")
def double_click(self, list: FileListWidget) -> None:
location = QFileDialog.getExistingDirectory(
list, caption="Select save location"
)
if not location:
return
with open(
os.path.join(location, self.file_name), "wb"
) as f, RequestClient().client.stream(
"GET", "/files", params={"file_id": self.file_id}
) as stream:
if not stream.is_success:
QMessageBox.warning(list, "Error downloading the file")
return
for data in stream.iter_bytes():
f.write(data)
class Folder(pydantic.BaseModel): class Folder(pydantic.BaseModel):
@ -63,12 +117,29 @@ class Folder(pydantic.BaseModel):
return self.folder_name return self.folder_name
def delete(self) -> None: def delete(self) -> None:
RequestClient().client.delete("/folders", params={"folder_id": self.folder_id}) RequestClient().client.delete(
"/folders", params={"folder_id": self.folder_id}
)
def details_button(self) -> QPushButton: def details(self, list: FileListWidget) -> QWidget:
# TODO # TODO
raise NotImplementedError raise NotImplementedError
def icon(self) -> QIcon:
return QIcon("assets/folder.png")
def double_click(self, list: FileListWidget) -> None:
list.responses.append(ListResponse.get(self.folder_id))
list.update()
class ResponseProtocol(Protocol):
def items(self) -> list[DisplayProtocol]:
raise NotImplementedError
def update(self) -> ResponseProtocol:
raise NotImplementedError
class ListResponse(pydantic.BaseModel): class ListResponse(pydantic.BaseModel):
folder_id: uuid.UUID folder_id: uuid.UUID
@ -78,6 +149,18 @@ class ListResponse(pydantic.BaseModel):
def items(self) -> list[DisplayProtocol]: def items(self) -> list[DisplayProtocol]:
return self.files + self.folders return self.files + self.folders
@classmethod
def get(cls, folder_id: uuid.UUID | None = None) -> Self:
params = {}
if folder_id:
params["folder_id"] = folder_id
return ListResponse.model_validate_json(
RequestClient().client.get("/folders", params=params).text
)
def update(self) -> ResponseProtocol:
return self.get(self.folder_id)
class FileListWidget(QListWidget): class FileListWidget(QListWidget):
def __init__(self, state: state.State): def __init__(self, state: state.State):
@ -86,9 +169,16 @@ class FileListWidget(QListWidget):
self.setAcceptDrops(True) self.setAcceptDrops(True)
self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.customContextMenuRequested.connect(self.show_context_menu) self.customContextMenuRequested.connect(self.show_context_menu)
self.itemDoubleClicked.connect(self.show_item)
self.details_widget = None
self.responses: list[ListResponse] = [] self.responses: list[ResponseProtocol] = [ListResponse.get()]
self.update_response() self.update()
def show_item(self, qitem: QListWidgetItem) -> None:
row = self.row(qitem)
item = self.current_response().items()[row]
item.double_click(self)
def current_response(self) -> ListResponse: def current_response(self) -> ListResponse:
if not self.responses: if not self.responses:
@ -96,17 +186,7 @@ class FileListWidget(QListWidget):
return self.responses[-1] return self.responses[-1]
def update_response(self): def update_response(self):
if not self.responses: self.responses[-1] = self.responses[-1].update()
response = ListResponse.model_validate_json(
RequestClient().client.get("/folders").text
)
self.responses.append(response)
print(response.files)
self.responses[-1] = ListResponse.model_validate_json(
RequestClient()
.client.get("/folders", params={"folder_id": self.responses[-1].folder_id})
.text
)
self.update() self.update()
def dragEnterEvent(self, event: QDragEnterEvent): def dragEnterEvent(self, event: QDragEnterEvent):
@ -133,7 +213,9 @@ class FileListWidget(QListWidget):
response = RequestClient().client.post( response = RequestClient().client.post(
"http://localhost:3000/files", "http://localhost:3000/files",
files=files, files=files,
params={"parent_folder": self.current_response().folder_id}, params={
"parent_folder": self.current_response().folder_id
},
) )
if response.is_success: if response.is_success:
QMessageBox.information( QMessageBox.information(
@ -147,10 +229,10 @@ class FileListWidget(QListWidget):
except httpx.HTTPError as e: except httpx.HTTPError as e:
QMessageBox.critical(self, "HTTP Error", str(e)) QMessageBox.critical(self, "HTTP Error", str(e))
def add_file_item(self, file_name): def add_item(self, item: DisplayProtocol):
item = QListWidgetItem(file_name) widget = QListWidgetItem(item.name())
item.setIcon(QIcon.fromTheme("text-x-generic")) # File icon widget.setIcon(item.icon())
self.addItem(item) self.addItem(widget)
def show_context_menu(self, pos: QPoint): def show_context_menu(self, pos: QPoint):
item = self.itemAt(pos) item = self.itemAt(pos)
@ -165,8 +247,10 @@ class FileListWidget(QListWidget):
menu.exec(self.mapToGlobal(pos)) menu.exec(self.mapToGlobal(pos))
def show_details(self, item): def show_details(self, item):
file_name = item.text() row = self.row(item)
QMessageBox.information(self, "Details", f"Details for {file_name}") item = self.current_response().items()[row]
self.details_widget = item.details(self)
self.details_widget.show()
def delete_item(self, item): def delete_item(self, item):
row = self.row(item) row = self.row(item)
@ -179,13 +263,14 @@ class FileListWidget(QListWidget):
self.clear() self.clear()
last = self.responses[-1] last = self.responses[-1]
for item in last.items(): for item in last.items():
self.add_file_item(item.name()) self.add_item(item)
class Sidebar(QWidget): class Sidebar(QWidget):
def __init__(self, state: state.State): def __init__(self, state: state.State, file_list: FileListWidget):
super().__init__() super().__init__()
self.state = state self.state = state
self.file_list = file_list
layout = QVBoxLayout() layout = QVBoxLayout()
self.setLayout(layout) self.setLayout(layout)
# Add your sidebar buttons here # Add your sidebar buttons here
@ -194,14 +279,27 @@ class Sidebar(QWidget):
layout.addWidget(btn) layout.addWidget(btn)
layout.addStretch() layout.addStretch()
def get_user(self): ...
def logout(self): ...
def go_back(self): ...
def go_root(self): ...
def get_tlp(self):
"""Get top level permitted folders"""
def sync(self): ...
class MainFileWidget(QWidget): class MainFileWidget(QWidget):
def __init__(self, state: state.State): def __init__(self, state: state.State):
super().__init__() super().__init__()
self.state = state self.state = state
layout = QHBoxLayout() layout = QHBoxLayout()
self.sidebar = Sidebar(state)
self.file_list = FileListWidget(state) self.file_list = FileListWidget(state)
self.sidebar = Sidebar(state, self.file_list)
layout.addWidget(self.sidebar) layout.addWidget(self.sidebar)
layout.addWidget(self.file_list) layout.addWidget(self.file_list)
self.setLayout(layout) self.setLayout(layout)