Sync widget
This commit is contained in:
		
							
								
								
									
										124
									
								
								desktop_client/file.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								desktop_client/file.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,124 @@
 | 
			
		||||
from __future__ import annotations
 | 
			
		||||
 | 
			
		||||
import base64
 | 
			
		||||
import datetime
 | 
			
		||||
import hashlib
 | 
			
		||||
import os
 | 
			
		||||
import uuid
 | 
			
		||||
 | 
			
		||||
import file_widgets
 | 
			
		||||
import pydantic
 | 
			
		||||
from PyQt6.QtGui import QIcon
 | 
			
		||||
from PyQt6.QtWidgets import QFileDialog, QLabel, QMessageBox, QWidget
 | 
			
		||||
from request_client import RequestClient
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class File(pydantic.BaseModel):
 | 
			
		||||
    file_id: uuid.UUID
 | 
			
		||||
    file_name: str
 | 
			
		||||
    file_size: int
 | 
			
		||||
    sha512: str
 | 
			
		||||
    created_at: datetime.datetime
 | 
			
		||||
    updated_at: datetime.datetime
 | 
			
		||||
 | 
			
		||||
    def name(self) -> str:
 | 
			
		||||
        return self.file_name
 | 
			
		||||
 | 
			
		||||
    def delete(self) -> None:
 | 
			
		||||
        RequestClient().client.delete(
 | 
			
		||||
            "/files", params={"file_id": self.file_id}
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def details(self, list: file_widgets.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: {file_size_text}\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: "kibi", 2: "mebi", 3: "gibi", 4: "tebi"}
 | 
			
		||||
        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: file_widgets.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)
 | 
			
		||||
 | 
			
		||||
    def create(path: str, parent_id: uuid.UUID):
 | 
			
		||||
        """Upload the file"""
 | 
			
		||||
        file_name = os.path.basename(path)
 | 
			
		||||
        try:
 | 
			
		||||
            with open(path, "rb") as f:
 | 
			
		||||
                files = {"file": (file_name, f)}
 | 
			
		||||
                response = RequestClient().client.post(
 | 
			
		||||
                    "/files",
 | 
			
		||||
                    files=files,
 | 
			
		||||
                    params={"parent_folder": parent_id},
 | 
			
		||||
                )
 | 
			
		||||
                if not response.is_success:
 | 
			
		||||
                    QMessageBox.warning(
 | 
			
		||||
                        None, "Error", f"Upload failed: {response.text}"
 | 
			
		||||
                    )
 | 
			
		||||
                print(response.text)
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            QMessageBox.critical(None, "HTTP Error", str(e))
 | 
			
		||||
 | 
			
		||||
    def download(self, path: str):
 | 
			
		||||
        with open(path, "wb") as f, RequestClient().client.stream(
 | 
			
		||||
            "GET", "/files", params={"file_id": self.file_id}
 | 
			
		||||
        ) as stream:
 | 
			
		||||
            if not stream.is_success:
 | 
			
		||||
                QMessageBox.warning(None, "Error downloading the file")
 | 
			
		||||
                return
 | 
			
		||||
            for data in stream.iter_bytes():
 | 
			
		||||
                f.write(data)
 | 
			
		||||
 | 
			
		||||
    def modify(self, path: str):
 | 
			
		||||
        """Upload the file"""
 | 
			
		||||
        file_name = os.path.basename(path)
 | 
			
		||||
        hash = hashlib.file_digest(open(path, "rb"), "sha512").digest()
 | 
			
		||||
        if base64.b64decode(self.sha512) == hash:
 | 
			
		||||
            return
 | 
			
		||||
        try:
 | 
			
		||||
            with open(path, "rb") as f:
 | 
			
		||||
                files = {"file": (file_name, f)}
 | 
			
		||||
                response = RequestClient().client.patch(
 | 
			
		||||
                    "/files",
 | 
			
		||||
                    files=files,
 | 
			
		||||
                    params={"file_id": self.file_id},
 | 
			
		||||
                )
 | 
			
		||||
                if not response.is_success:
 | 
			
		||||
                    QMessageBox.warning(
 | 
			
		||||
                        None, "Error", f"Upload failed: {response.text}"
 | 
			
		||||
                    )
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            QMessageBox.critical(None, "HTTP Error", str(e))
 | 
			
		||||
@@ -1,25 +1,28 @@
 | 
			
		||||
from __future__ import annotations
 | 
			
		||||
 | 
			
		||||
import base64
 | 
			
		||||
import dataclasses
 | 
			
		||||
import datetime
 | 
			
		||||
import hashlib
 | 
			
		||||
import os
 | 
			
		||||
import uuid
 | 
			
		||||
from typing import Protocol, Self
 | 
			
		||||
 | 
			
		||||
import create_folder_widget
 | 
			
		||||
import folder_info
 | 
			
		||||
import sync
 | 
			
		||||
import httpx
 | 
			
		||||
import pydantic
 | 
			
		||||
import state
 | 
			
		||||
import user
 | 
			
		||||
from file import File
 | 
			
		||||
from folder import Folder
 | 
			
		||||
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,
 | 
			
		||||
@@ -48,155 +51,6 @@ class DisplayProtocol(Protocol):
 | 
			
		||||
        raise NotImplementedError
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class File(pydantic.BaseModel):
 | 
			
		||||
    file_id: uuid.UUID
 | 
			
		||||
    file_name: str
 | 
			
		||||
    file_size: int
 | 
			
		||||
    sha512: str
 | 
			
		||||
    created_at: datetime.datetime
 | 
			
		||||
    updated_at: datetime.datetime
 | 
			
		||||
 | 
			
		||||
    def name(self) -> str:
 | 
			
		||||
        return self.file_name
 | 
			
		||||
 | 
			
		||||
    def delete(self) -> None:
 | 
			
		||||
        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: {file_size_text}\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: "kibi", 2: "mebi", 3: "gibi", 4: "tebi"}
 | 
			
		||||
        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)
 | 
			
		||||
 | 
			
		||||
    def create(path: str, parent_id: uuid.UUID):
 | 
			
		||||
        """Upload the file"""
 | 
			
		||||
        file_name = os.path.basename(path)
 | 
			
		||||
        try:
 | 
			
		||||
            with open(path, "rb") as f:
 | 
			
		||||
                files = {"file": (file_name, f)}
 | 
			
		||||
                response = RequestClient().client.post(
 | 
			
		||||
                    "/files",
 | 
			
		||||
                    files=files,
 | 
			
		||||
                    params={"parent_folder": parent_id},
 | 
			
		||||
                )
 | 
			
		||||
                if not response.is_success:
 | 
			
		||||
                    QMessageBox.warning(
 | 
			
		||||
                        None, "Error", f"Upload failed: {response.text}"
 | 
			
		||||
                    )
 | 
			
		||||
                print(response.text)
 | 
			
		||||
        except httpx.HTTPError as e:
 | 
			
		||||
            QMessageBox.critical(None, "HTTP Error", str(e))
 | 
			
		||||
 | 
			
		||||
    def download(self, path: str):
 | 
			
		||||
        with open(path, "wb") as f, RequestClient().client.stream(
 | 
			
		||||
            "GET", "/files", params={"file_id": self.file_id}
 | 
			
		||||
        ) as stream:
 | 
			
		||||
            if not stream.is_success:
 | 
			
		||||
                QMessageBox.warning(None, "Error downloading the file")
 | 
			
		||||
                return
 | 
			
		||||
            for data in stream.iter_bytes():
 | 
			
		||||
                f.write(data)
 | 
			
		||||
 | 
			
		||||
    def modify(self, path: str):
 | 
			
		||||
        """Upload the file"""
 | 
			
		||||
        file_name = os.path.basename(path)
 | 
			
		||||
        hash = hashlib.file_digest(open(path, "rb"), "sha512").digest()
 | 
			
		||||
        if base64.b64decode(self.sha512) == hash:
 | 
			
		||||
            return
 | 
			
		||||
        try:
 | 
			
		||||
            with open(path, "rb") as f:
 | 
			
		||||
                files = {"file": (file_name, f)}
 | 
			
		||||
                response = RequestClient().client.patch(
 | 
			
		||||
                    "/files",
 | 
			
		||||
                    files=files,
 | 
			
		||||
                    params={"file_id": self.file_id},
 | 
			
		||||
                )
 | 
			
		||||
                if not response.is_success:
 | 
			
		||||
                    QMessageBox.warning(
 | 
			
		||||
                        None, "Error", f"Upload failed: {response.text}"
 | 
			
		||||
                    )
 | 
			
		||||
        except httpx.HTTPError as e:
 | 
			
		||||
            QMessageBox.critical(None, "HTTP Error", str(e))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Folder(pydantic.BaseModel):
 | 
			
		||||
    folder_id: uuid.UUID
 | 
			
		||||
    owner_id: int
 | 
			
		||||
    folder_name: str
 | 
			
		||||
    created_at: datetime.datetime
 | 
			
		||||
 | 
			
		||||
    def name(self) -> str:
 | 
			
		||||
        return self.folder_name
 | 
			
		||||
 | 
			
		||||
    def delete(self) -> None:
 | 
			
		||||
        response = RequestClient().client.delete(
 | 
			
		||||
            "/folders", params={"folder_id": self.folder_id}
 | 
			
		||||
        )
 | 
			
		||||
        if not response:
 | 
			
		||||
            QMessageBox.warning(None, "Error deleting folder", response.text)
 | 
			
		||||
 | 
			
		||||
    def details(self, list: FileListWidget) -> QWidget:
 | 
			
		||||
        return folder_info.FolderInfoWidget(self)
 | 
			
		||||
 | 
			
		||||
    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()
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def create(name: str, parent_id: uuid.UUID) -> uuid.UUID:
 | 
			
		||||
        response = RequestClient().client.post(
 | 
			
		||||
            "/folders",
 | 
			
		||||
            json={
 | 
			
		||||
                "folder_name": name,
 | 
			
		||||
                "parent_folder_id": str(parent_id),
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
        if not response.is_success:
 | 
			
		||||
            QMessageBox.warning(None, "Error creating folder", response.text)
 | 
			
		||||
        return uuid.UUID(response.text.strip('"'))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ResponseProtocol(Protocol):
 | 
			
		||||
    def items(self) -> list[DisplayProtocol]:
 | 
			
		||||
        raise NotImplementedError
 | 
			
		||||
@@ -229,7 +83,9 @@ class ListResponse(pydantic.BaseModel):
 | 
			
		||||
        return self.get(self.folder_id)
 | 
			
		||||
 | 
			
		||||
    def create_folder(self, file_list: FileListWidget):
 | 
			
		||||
        return create_folder_widget.CreateFolderWidget(self.folder_id, file_list)
 | 
			
		||||
        return create_folder_widget.CreateFolderWidget(
 | 
			
		||||
            self.folder_id, file_list
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@dataclasses.dataclass(slots=True)
 | 
			
		||||
@@ -305,7 +161,9 @@ 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(
 | 
			
		||||
@@ -370,8 +228,9 @@ class Sidebar(QWidget):
 | 
			
		||||
            ("Go back", self.go_back),
 | 
			
		||||
            ("Go root", self.go_root),
 | 
			
		||||
            ("Get permitted", self.get_tlp),
 | 
			
		||||
            ("Sync", self.sync),
 | 
			
		||||
            ("Refresh", self.refresh),
 | 
			
		||||
            ("Create folder", self.create_folder),
 | 
			
		||||
            ("Sync all", self.sync_all),
 | 
			
		||||
        ]
 | 
			
		||||
        for text, func in buttons:
 | 
			
		||||
            button = QPushButton(text)
 | 
			
		||||
@@ -400,9 +259,8 @@ class Sidebar(QWidget):
 | 
			
		||||
        self.file_list.responses.append(TlpResponse.get())
 | 
			
		||||
        self.file_list.update()
 | 
			
		||||
 | 
			
		||||
    def sync(self):
 | 
			
		||||
        # TODO
 | 
			
		||||
        ...
 | 
			
		||||
    def refresh(self):
 | 
			
		||||
        self.file_list.update_response()
 | 
			
		||||
 | 
			
		||||
    def create_folder(self):
 | 
			
		||||
        self.folder_widget = self.file_list.current_response().create_folder(
 | 
			
		||||
@@ -411,6 +269,9 @@ class Sidebar(QWidget):
 | 
			
		||||
        if self.folder_widget is not None:
 | 
			
		||||
            self.folder_widget.show()
 | 
			
		||||
 | 
			
		||||
    def sync_all(self):
 | 
			
		||||
        sync.SyncData.sync_all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MainFileWidget(QWidget):
 | 
			
		||||
    def __init__(self, state: state.State):
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										60
									
								
								desktop_client/folder.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								desktop_client/folder.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
			
		||||
from __future__ import annotations
 | 
			
		||||
 | 
			
		||||
import datetime
 | 
			
		||||
import uuid
 | 
			
		||||
import typing
 | 
			
		||||
 | 
			
		||||
if typing.TYPE_CHECKING:
 | 
			
		||||
    import file_widgets
 | 
			
		||||
 | 
			
		||||
import pydantic
 | 
			
		||||
from PyQt6.QtGui import QIcon
 | 
			
		||||
from PyQt6.QtWidgets import QMessageBox, QWidget
 | 
			
		||||
from request_client import RequestClient
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Folder(pydantic.BaseModel):
 | 
			
		||||
    folder_id: uuid.UUID
 | 
			
		||||
    owner_id: int
 | 
			
		||||
    folder_name: str
 | 
			
		||||
    created_at: datetime.datetime
 | 
			
		||||
 | 
			
		||||
    def name(self) -> str:
 | 
			
		||||
        return self.folder_name
 | 
			
		||||
 | 
			
		||||
    def delete(self) -> None:
 | 
			
		||||
        import sync
 | 
			
		||||
 | 
			
		||||
        sync.SyncData.delete(self.folder_id)
 | 
			
		||||
        response = RequestClient().client.delete(
 | 
			
		||||
            "/folders", params={"folder_id": self.folder_id}
 | 
			
		||||
        )
 | 
			
		||||
        if not response:
 | 
			
		||||
            QMessageBox.warning(None, "Error deleting folder", response.text)
 | 
			
		||||
 | 
			
		||||
    def details(self, list: file_widgets.FileListWidget) -> QWidget:
 | 
			
		||||
        import folder_info
 | 
			
		||||
 | 
			
		||||
        return folder_info.FolderInfoWidget(self)
 | 
			
		||||
 | 
			
		||||
    def icon(self) -> QIcon:
 | 
			
		||||
        return QIcon("assets/folder.png")
 | 
			
		||||
 | 
			
		||||
    def double_click(self, list: file_widgets.FileListWidget) -> None:
 | 
			
		||||
        import file_widgets
 | 
			
		||||
 | 
			
		||||
        list.responses.append(file_widgets.ListResponse.get(self.folder_id))
 | 
			
		||||
        list.update()
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def create(name: str, parent_id: uuid.UUID) -> uuid.UUID:
 | 
			
		||||
        response = RequestClient().client.post(
 | 
			
		||||
            "/folders",
 | 
			
		||||
            json={
 | 
			
		||||
                "folder_name": name,
 | 
			
		||||
                "parent_folder_id": str(parent_id),
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
        if not response.is_success:
 | 
			
		||||
            QMessageBox.warning(None, "Error creating folder", response.text)
 | 
			
		||||
        return uuid.UUID(response.text.strip('"'))
 | 
			
		||||
@@ -7,14 +7,17 @@ from functools import cache, partial
 | 
			
		||||
 | 
			
		||||
import file_widgets
 | 
			
		||||
import pydantic
 | 
			
		||||
import sync
 | 
			
		||||
import user
 | 
			
		||||
from PyQt6.QtWidgets import (
 | 
			
		||||
    QComboBox,
 | 
			
		||||
    QFileDialog,
 | 
			
		||||
    QHBoxLayout,
 | 
			
		||||
    QLabel,
 | 
			
		||||
    QLineEdit,
 | 
			
		||||
    QMessageBox,
 | 
			
		||||
    QPushButton,
 | 
			
		||||
    QSpacerItem,
 | 
			
		||||
    QVBoxLayout,
 | 
			
		||||
    QWidget,
 | 
			
		||||
)
 | 
			
		||||
@@ -36,7 +39,9 @@ class Permission(enum.StrEnum):
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
        if not response.is_success:
 | 
			
		||||
            QMessageBox.warning(None, "Error setting permissions", response.text)
 | 
			
		||||
            QMessageBox.warning(
 | 
			
		||||
                None, "Error setting permissions", response.text
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def delete(folder_id: uuid.UUID, user_id: int, widget: FolderInfoWidget):
 | 
			
		||||
@@ -48,7 +53,9 @@ class Permission(enum.StrEnum):
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
        if not response.is_success:
 | 
			
		||||
            QMessageBox.warning(None, "Error deleting permissions", response.text)
 | 
			
		||||
            QMessageBox.warning(
 | 
			
		||||
                None, "Error deleting permissions", response.text
 | 
			
		||||
            )
 | 
			
		||||
        else:
 | 
			
		||||
            widget.redraw()
 | 
			
		||||
 | 
			
		||||
@@ -94,10 +101,14 @@ class FolderInfoWidget(QWidget):
 | 
			
		||||
            combo = QComboBox()
 | 
			
		||||
            combo.addItems(map(str, Permission))
 | 
			
		||||
            combo.setCurrentText(str(permissions))
 | 
			
		||||
            combo.currentTextChanged.connect(partial(self.change, user_id=user_id))
 | 
			
		||||
            combo.currentTextChanged.connect(
 | 
			
		||||
                partial(self.change, user_id=user_id)
 | 
			
		||||
            )
 | 
			
		||||
            delete = QPushButton("Delete")
 | 
			
		||||
            delete.clicked.connect(
 | 
			
		||||
                partial(Permission.delete, self.folder.folder_id, user_id, self)
 | 
			
		||||
                partial(
 | 
			
		||||
                    Permission.delete, self.folder.folder_id, user_id, self
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
            layout.addWidget(name)
 | 
			
		||||
            layout.addWidget(combo)
 | 
			
		||||
@@ -123,6 +134,25 @@ class FolderInfoWidget(QWidget):
 | 
			
		||||
        layout.addWidget(button)
 | 
			
		||||
        main_layout.addLayout(layout)
 | 
			
		||||
 | 
			
		||||
        main_layout.addSpacerItem(QSpacerItem(0, 50))
 | 
			
		||||
 | 
			
		||||
        synced_to = sync.SyncData.get_for_folder(self.folder.folder_id)
 | 
			
		||||
        if synced_to is not None:
 | 
			
		||||
            synced_to_text = f"Synched to {synced_to.path}"
 | 
			
		||||
        else:
 | 
			
		||||
            synced_to_text = "Not synced"
 | 
			
		||||
        layout = QHBoxLayout()
 | 
			
		||||
        label = QLabel(synced_to_text)
 | 
			
		||||
        layout.addWidget(label)
 | 
			
		||||
        if synced_to is not None:
 | 
			
		||||
            button = QPushButton("Delete")
 | 
			
		||||
            button.clicked.connect(self.remove_sync)
 | 
			
		||||
        else:
 | 
			
		||||
            button = QPushButton("Add sync")
 | 
			
		||||
            button.clicked.connect(self.add_sync)
 | 
			
		||||
        layout.addWidget(button)
 | 
			
		||||
        main_layout.addLayout(layout)
 | 
			
		||||
 | 
			
		||||
        if self.layout():
 | 
			
		||||
            QWidget().setLayout(self.layout())
 | 
			
		||||
        self.setLayout(main_layout)
 | 
			
		||||
@@ -140,6 +170,19 @@ class FolderInfoWidget(QWidget):
 | 
			
		||||
 | 
			
		||||
    def search_save(self):
 | 
			
		||||
        permission = Permission(self.perm_combo.currentText())
 | 
			
		||||
        search = self.search_combo.currentText()
 | 
			
		||||
        if not search:
 | 
			
		||||
            return
 | 
			
		||||
        user_id = self.user_mapping[self.search_combo.currentText()]
 | 
			
		||||
        permission.set(self.folder.folder_id, user_id)
 | 
			
		||||
        self.redraw()
 | 
			
		||||
 | 
			
		||||
    def add_sync(self):
 | 
			
		||||
        path = QFileDialog.getExistingDirectory()
 | 
			
		||||
        if path:
 | 
			
		||||
            sync.SyncData.new_sync(path, self.folder.folder_id)
 | 
			
		||||
            self.redraw()
 | 
			
		||||
 | 
			
		||||
    def remove_sync(self):
 | 
			
		||||
        sync.SyncData.delete(self.folder.folder_id)
 | 
			
		||||
        self.redraw()
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
import uuid
 | 
			
		||||
 | 
			
		||||
import auth
 | 
			
		||||
import file_widgets
 | 
			
		||||
import httpx
 | 
			
		||||
 
 | 
			
		||||
@@ -6,21 +6,22 @@ import uuid
 | 
			
		||||
from concurrent.futures import ThreadPoolExecutor
 | 
			
		||||
from time import ctime
 | 
			
		||||
 | 
			
		||||
import file_widgets
 | 
			
		||||
from file import File
 | 
			
		||||
from folder import Folder
 | 
			
		||||
from request_client import RequestClient
 | 
			
		||||
from sqlmodel import Field, Session, SQLModel, create_engine, select
 | 
			
		||||
from sqlmodel import Field, Session, SQLModel, create_engine, delete, select
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FolderStructure(file_widgets.Folder):
 | 
			
		||||
    files: list[file_widgets.File]
 | 
			
		||||
class FolderStructure(Folder):
 | 
			
		||||
    files: list[File]
 | 
			
		||||
    folders: list[FolderStructure]
 | 
			
		||||
 | 
			
		||||
    def find_folder(self, name: str) -> file_widgets.File | None:
 | 
			
		||||
    def find_folder(self, name: str) -> File | None:
 | 
			
		||||
        for file in self.folders:
 | 
			
		||||
            if file.folder_name == name:
 | 
			
		||||
                return file
 | 
			
		||||
 | 
			
		||||
    def find_file(self, name: str) -> file_widgets.File | None:
 | 
			
		||||
    def find_file(self, name: str) -> File | None:
 | 
			
		||||
        for file in self.files:
 | 
			
		||||
            if file.file_name == name:
 | 
			
		||||
                return file
 | 
			
		||||
@@ -36,9 +37,9 @@ class FolderStructure(file_widgets.Folder):
 | 
			
		||||
 | 
			
		||||
class SyncData(SQLModel, table=True):
 | 
			
		||||
    id: int | None = Field(default=None, primary_key=True)
 | 
			
		||||
    folder_id: uuid.UUID = Field(unique=True, index=True)
 | 
			
		||||
    path: str
 | 
			
		||||
    last_updated: datetime.datetime
 | 
			
		||||
    folder_id: uuid.UUID
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def sync_all():
 | 
			
		||||
@@ -56,9 +57,7 @@ class SyncData(SQLModel, table=True):
 | 
			
		||||
            tuple(map_)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def new_sync(path: str, parent_id):
 | 
			
		||||
        name = os.path.basename(path)
 | 
			
		||||
        folder_id = file_widgets.Folder.create(name, parent_id)
 | 
			
		||||
    def new_sync(path: str, folder_id: uuid.UUID):
 | 
			
		||||
        with Session(engine) as s:
 | 
			
		||||
            sync = SyncData(
 | 
			
		||||
                path=path,
 | 
			
		||||
@@ -69,6 +68,19 @@ class SyncData(SQLModel, table=True):
 | 
			
		||||
            s.commit()
 | 
			
		||||
        upload_folder(path, folder_id)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_for_folder(folder_id: uuid.UUID) -> SyncData | None:
 | 
			
		||||
        with Session(engine) as s:
 | 
			
		||||
            return s.exec(
 | 
			
		||||
                select(SyncData).where(SyncData.folder_id == folder_id)
 | 
			
		||||
            ).one_or_none()
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def delete(folder_id: uuid.UUID):
 | 
			
		||||
        with Session(engine) as s:
 | 
			
		||||
            s.exec(delete(SyncData).where(SyncData.folder_id == folder_id))
 | 
			
		||||
            s.commit()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
sqlite_file_name = "database.db"
 | 
			
		||||
sqlite_url = f"sqlite:///{sqlite_file_name}"
 | 
			
		||||
@@ -82,9 +94,9 @@ def upload_folder(path: str, folder_id: uuid.UUID):
 | 
			
		||||
    for item in items:
 | 
			
		||||
        item_path = os.path.join(path, item)
 | 
			
		||||
        if os.path.isfile(item_path):
 | 
			
		||||
            file_widgets.File.create(item_path, folder_id)
 | 
			
		||||
            File.create(item_path, folder_id)
 | 
			
		||||
        elif os.path.isdir(item_path):
 | 
			
		||||
            id = file_widgets.Folder.create(item, folder_id)
 | 
			
		||||
            id = Folder.create(item, folder_id)
 | 
			
		||||
            upload_folder(item_path, id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -98,7 +110,7 @@ def merge_folder(
 | 
			
		||||
        if os.path.isfile(item_path):
 | 
			
		||||
            file = structure.find_file(item)
 | 
			
		||||
            if file is None:
 | 
			
		||||
                file_widgets.File.create(item_path, structure.folder_id)
 | 
			
		||||
                File.create(item_path, structure.folder_id)
 | 
			
		||||
                continue
 | 
			
		||||
            mtime = datetime.datetime.strptime(
 | 
			
		||||
                ctime(os.path.getmtime(item_path)), "%a %b %d %H:%M:%S %Y"
 | 
			
		||||
@@ -111,7 +123,7 @@ def merge_folder(
 | 
			
		||||
        elif os.path.isdir(item_path):
 | 
			
		||||
            folder = structure.find_folder(item)
 | 
			
		||||
            if folder is None:
 | 
			
		||||
                folder_id = file_widgets.Folder.create(item, structure.folder_id)
 | 
			
		||||
                folder_id = Folder.create(item, structure.folder_id)
 | 
			
		||||
                upload_folder(item_path, folder_id)
 | 
			
		||||
            else:
 | 
			
		||||
                merge_folder(item_path, folder, time)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user