Compare commits

...

3 Commits

Author SHA1 Message Date
37940d633a
Folder creation 2024-08-09 23:14:31 +03:00
b0f20680b4
User info widgets 2024-08-09 22:50:20 +03:00
cb3b5a3c27
File size fixes 2024-08-09 20:44:44 +03:00
5 changed files with 229 additions and 30 deletions

View File

@ -0,0 +1,37 @@
from __future__ import annotations
import uuid
import file_widgets
from PyQt6.QtWidgets import QLineEdit, QMessageBox, QPushButton, QVBoxLayout, QWidget
from request_client import RequestClient
class CreateFolderWidget(QWidget):
def __init__(self, folder_id: uuid.UUID, file_list: file_widgets.FileListWidget):
super().__init__()
self.folder_id = folder_id
self.file_list = file_list
self.setWindowTitle("Folder creation")
layout = QVBoxLayout()
self.edit = QLineEdit()
layout.addWidget(self.edit)
button = QPushButton("Submit")
button.clicked.connect(self.submit)
layout.addWidget(button)
self.setLayout(layout)
def submit(self):
response = RequestClient().client.post(
"/folders",
json={
"folder_name": self.edit.text(),
"parent_folder_id": str(self.folder_id),
},
)
if not response.is_success:
QMessageBox.warning(None, "Error creating folder", response.text)
else:
QMessageBox.information(None, "Folder created", "Folder created")
self.file_list.update_response()
self.close()

View File

@ -1,23 +1,22 @@
from __future__ import annotations
import dataclasses
import datetime
import os
import uuid
from typing import Protocol, Self
import create_folder_widget
import httpx
import pydantic
import state
import user
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 (
QFileDialog,
QHBoxLayout,
QLabel,
QListWidget,
QListWidgetItem,
QMenu,
@ -25,8 +24,6 @@ from PyQt6.QtWidgets import (
QPushButton,
QVBoxLayout,
QWidget,
QLabel,
QFileDialog,
)
from request_client import RequestClient
@ -60,15 +57,15 @@ class File(pydantic.BaseModel):
return self.file_name
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(self, list: FileListWidget) -> QWidget:
del list
file_size = self._format_bytes(self.file_size)
file_size_text = f"{file_size[0]:.2f} {file_size[1]}"
details = (
f"file id: {self.file_id}\nfile_name: {self.file_name}\n"
+ f"file_size: {self._format_bytes(self.file_size)}\n"
+ f"file_size: {file_size_text}\n"
+ f"created at: {self.created_at}\nupdated at: {self.updated_at}"
)
label = QLabel()
@ -80,7 +77,7 @@ class File(pydantic.BaseModel):
def _format_bytes(size: int):
power = 2**10
n = 0
power_labels = {0: "", 1: "kilo", 2: "mega", 3: "giga", 4: "tera"}
power_labels = {0: "", 1: "kibi", 2: "mebi", 3: "gibi", 4: "tebi"}
while size > power and n < 4:
size /= power
n += 1
@ -117,8 +114,10 @@ class Folder(pydantic.BaseModel):
return self.folder_name
def delete(self) -> None:
RequestClient().client.delete(
"/folders", params={"folder_id": self.folder_id}
print(
RequestClient()
.client.delete("/folders", params={"folder_id": self.folder_id})
.text
)
def details(self, list: FileListWidget) -> QWidget:
@ -140,6 +139,9 @@ class ResponseProtocol(Protocol):
def update(self) -> ResponseProtocol:
raise NotImplementedError
def create_folder(self) -> QWidget | None:
raise NotImplementedError
class ListResponse(pydantic.BaseModel):
folder_id: uuid.UUID
@ -161,6 +163,32 @@ class ListResponse(pydantic.BaseModel):
def update(self) -> ResponseProtocol:
return self.get(self.folder_id)
def create_folder(self, file_list: FileListWidget):
return create_folder_widget.CreateFolderWidget(self.folder_id, file_list)
@dataclasses.dataclass(slots=True)
class TlpResponse:
folders: list[Folder]
@staticmethod
def get() -> Self:
url = "/permissions/get_top_level_permitted_folders"
return TlpResponse(
pydantic.TypeAdapter(list[Folder]).validate_json(
RequestClient().client.get(url).text
)
)
def items(self) -> list[DisplayProtocol]:
return self.folders
def update(self) -> ResponseProtocol:
return self.get()
def create_folder(self):
return # Not much to do
class FileListWidget(QListWidget):
def __init__(self, state: state.State):
@ -213,9 +241,7 @@ class FileListWidget(QListWidget):
response = RequestClient().client.post(
"http://localhost:3000/files",
files=files,
params={
"parent_folder": self.current_response().folder_id
},
params={"parent_folder": self.current_response().folder_id},
)
if response.is_success:
QMessageBox.information(
@ -271,26 +297,56 @@ class Sidebar(QWidget):
super().__init__()
self.state = state
self.file_list = file_list
self.user_widget = None
self.folder_widget = None
layout = QVBoxLayout()
self.setLayout(layout)
# Add your sidebar buttons here
for i in range(5): # Example buttons
btn = QPushButton(f"Button {i+1}")
layout.addWidget(btn)
buttons = [
("User info", self.get_user),
("Log out", self.logout),
("Go back", self.go_back),
("Go root", self.go_root),
("Get permitted", self.get_tlp),
("Sync", self.sync),
("Create folder", self.create_folder),
]
for text, func in buttons:
button = QPushButton(text)
button.clicked.connect(func)
layout.addWidget(button)
layout.addStretch()
def get_user(self): ...
def get_user(self):
self.user_widget = user.UserWidget(self.state)
self.user_widget.show()
def logout(self): ...
def logout(self):
self.state.logout()
def go_back(self): ...
def go_back(self):
if len(self.file_list.responses) >= 2:
self.file_list.responses.pop()
self.file_list.update()
def go_root(self): ...
def go_root(self):
self.file_list.responses.append(ListResponse.get())
self.file_list.update()
def get_tlp(self):
"""Get top level permitted folders"""
self.file_list.responses.append(TlpResponse.get())
self.file_list.update()
def sync(self): ...
def sync(self):
# TODO
...
def create_folder(self):
self.folder_widget = self.file_list.current_response().create_folder(
self.file_list
)
if self.folder_widget is not None:
self.folder_widget.show()
class MainFileWidget(QWidget):

View File

@ -19,6 +19,10 @@ class RequestClient:
def set_token(cls, token: str):
cls._client.headers = {"Authorization": f"Bearer {token}"}
@classmethod
def delete_token(cls):
cls._client.headers = {}
@property
def client(self) -> httpx.Client:
return self._client

View File

@ -27,7 +27,7 @@ class State(QMainWindow):
password = keyring.get_credential("auth_app", "access_token")
if password is None:
self.switch_to_login() # Start with the login widget
else:
self.login(password.password)
def switch_to_login(self):
@ -42,3 +42,8 @@ class State(QMainWindow):
self.file_widget = file_widgets.MainFileWidget(self)
self.stack.addWidget(self.file_widget)
self.stack.setCurrentWidget(self.file_widget)
def logout(self):
keyring.delete_password("auth_app", "access_token")
request_client.RequestClient().delete_token()
self.switch_to_login()

97
desktop_client/user.py Normal file
View File

@ -0,0 +1,97 @@
from __future__ import annotations
import pydantic
import state
from PyQt6.QtWidgets import (
QHBoxLayout,
QLabel,
QLineEdit,
QMessageBox,
QPushButton,
QVBoxLayout,
QWidget,
)
from request_client import RequestClient
class User(pydantic.BaseModel):
user_id: int | None = None
username: str
email: str
@staticmethod
def get(user_id: int | None = None) -> User:
params = {}
if user_id:
url = "/users"
params["user_id"] = user_id
else:
url = "/users/current"
return User.model_validate_json(
RequestClient().client.get(url, params=params).text
)
@staticmethod
def delete():
if not RequestClient().client.delete("/users").is_success:
raise Exception("Error deleting user")
def put(self):
response = RequestClient().client.put(
"/users", json={"username": self.username, "email": self.email}
)
if not response.is_success:
QMessageBox.warning(None, "Error updating user", response.text)
return
QMessageBox.information(None, "User updated", "User updated")
class UserSearch(User):
similarity: float
@staticmethod
def search(search_str: str) -> list[UserSearch]:
return pydantic.TypeAdapter(list[UserSearch]).validate_json(
RequestClient()
.client.get("/users/search", params={"search_string": search_str})
.text
)
class UserWidget(QWidget):
def __init__(self, state: state.State):
super().__init__()
self.state = state
self.setWindowTitle("User information")
self.user = User.get()
main_layout = QVBoxLayout()
lines = [("username", self.user.username), ("email", self.user.email)]
edits = []
for line in lines:
layout = QHBoxLayout()
label = QLabel(line[0])
edit = QLineEdit(line[1])
layout.addWidget(label)
layout.addWidget(edit)
main_layout.addLayout(layout)
edits.append(edit)
self.username: QLineEdit = edits[0]
self.email: QLineEdit = edits[1]
button_layout = QHBoxLayout()
buttons = [("Update", self.update_user), ("Delete", self.delete_user)]
for text, func in buttons:
button = QPushButton(text)
button.clicked.connect(func)
button_layout.addWidget(button)
main_layout.addLayout(button_layout)
self.setLayout(main_layout)
def delete_user(self):
User.delete()
self.close()
self.state.logout()
def update_user(self):
self.user.username = self.username.text()
self.user.email = self.email.text()
self.user.put()