diff --git a/desktop_client/file_widgets.py b/desktop_client/file_widgets.py index 63d18cc..664e84e 100644 --- a/desktop_client/file_widgets.py +++ b/desktop_client/file_widgets.py @@ -1,5 +1,7 @@ from __future__ import annotations +import dataclasses +import user import datetime import os import uuid @@ -17,7 +19,9 @@ from PyQt6.QtGui import ( QIcon, ) from PyQt6.QtWidgets import ( + QFileDialog, QHBoxLayout, + QLabel, QListWidget, QListWidgetItem, QMenu, @@ -25,8 +29,6 @@ from PyQt6.QtWidgets import ( QPushButton, QVBoxLayout, QWidget, - QLabel, - QFileDialog, ) from request_client import RequestClient @@ -273,26 +275,67 @@ class Sidebar(QWidget): super().__init__() self.state = state self.file_list = file_list + self.user_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), + ] + 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 + ... + + +@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() class MainFileWidget(QWidget): diff --git a/desktop_client/request_client.py b/desktop_client/request_client.py index b2f990a..7e59321 100644 --- a/desktop_client/request_client.py +++ b/desktop_client/request_client.py @@ -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 diff --git a/desktop_client/state.py b/desktop_client/state.py index 8da02f1..9bd5c10 100644 --- a/desktop_client/state.py +++ b/desktop_client/state.py @@ -27,8 +27,8 @@ class State(QMainWindow): password = keyring.get_credential("auth_app", "access_token") if password is None: self.switch_to_login() # Start with the login widget - - self.login(password.password) + else: + self.login(password.password) def switch_to_login(self): self.stack.setCurrentWidget(self.login_widget) @@ -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() diff --git a/desktop_client/user.py b/desktop_client/user.py new file mode 100644 index 0000000..64200e6 --- /dev/null +++ b/desktop_client/user.py @@ -0,0 +1,97 @@ +from __future__ import annotations + +import pydantic +import state +from PyQt6.QtWidgets import ( + QWidget, + QLineEdit, + QLabel, + QVBoxLayout, + QPushButton, + QHBoxLayout, + QMessageBox, +) +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()