God, I hope I'm ready
This commit is contained in:
@@ -3,12 +3,15 @@ from __future__ import annotations
|
||||
import uuid
|
||||
|
||||
import file_widgets
|
||||
from PyQt6.QtWidgets import QLineEdit, QMessageBox, QPushButton, QVBoxLayout, QWidget
|
||||
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):
|
||||
def __init__(
|
||||
self, folder_id: uuid.UUID, file_list: file_widgets.FileListWidget
|
||||
):
|
||||
super().__init__()
|
||||
self.folder_id = folder_id
|
||||
self.file_list = file_list
|
||||
@@ -22,16 +25,24 @@ class CreateFolderWidget(QWidget):
|
||||
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()
|
||||
try:
|
||||
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()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
QMessageBox.critical(self, "HTTP error", str(e))
|
||||
|
||||
+29
-11
@@ -6,6 +6,8 @@ import hashlib
|
||||
import os
|
||||
import uuid
|
||||
|
||||
import dateutil
|
||||
import dateutil.tz
|
||||
import file_widgets
|
||||
import pydantic
|
||||
from PyQt6.QtGui import QIcon
|
||||
@@ -32,12 +34,14 @@ class File(pydantic.BaseModel):
|
||||
|
||||
def details(self, list: file_widgets.FileListWidget) -> QWidget:
|
||||
del list
|
||||
file_size = self._format_bytes(self.file_size)
|
||||
file_size = self._format_size(self.file_size)
|
||||
file_size_text = f"{file_size[0]:.2f} {file_size[1]}"
|
||||
created_at = self._format_date(self.created_at)
|
||||
updated_at = self._format_date(self.updated_at)
|
||||
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}"
|
||||
+ f"created at: {created_at}\nupdated at: {updated_at}"
|
||||
)
|
||||
label = QLabel()
|
||||
label.setWindowTitle("File info")
|
||||
@@ -45,7 +49,7 @@ class File(pydantic.BaseModel):
|
||||
return label
|
||||
|
||||
@staticmethod
|
||||
def _format_bytes(size: int):
|
||||
def _format_size(size: int):
|
||||
power = 2**10
|
||||
n = 0
|
||||
power_labels = {0: "", 1: "kibi", 2: "mebi", 3: "gibi", 4: "tebi"}
|
||||
@@ -54,6 +58,12 @@ class File(pydantic.BaseModel):
|
||||
n += 1
|
||||
return size, power_labels[n] + "bytes"
|
||||
|
||||
@staticmethod
|
||||
def _format_date(date: datetime.datetime) -> str:
|
||||
date = date.replace(tzinfo=dateutil.tz.tzutc())
|
||||
date = date.astimezone(dateutil.tz.tzlocal())
|
||||
return date.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
def icon(self) -> QIcon:
|
||||
return QIcon(resource_path("assets/file.png"))
|
||||
|
||||
@@ -76,6 +86,7 @@ class File(pydantic.BaseModel):
|
||||
|
||||
def create(path: str, parent_id: uuid.UUID):
|
||||
"""Upload the file"""
|
||||
print(path)
|
||||
file_name = os.path.basename(path)
|
||||
try:
|
||||
with open(path, "rb") as f:
|
||||
@@ -94,14 +105,21 @@ class File(pydantic.BaseModel):
|
||||
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)
|
||||
try:
|
||||
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",
|
||||
"Error downloading the file",
|
||||
)
|
||||
return
|
||||
for data in stream.iter_bytes():
|
||||
f.write(data)
|
||||
except Exception as e:
|
||||
QMessageBox.warning(None, "Error downloading the file", str(e))
|
||||
|
||||
def modify(self, path: str):
|
||||
"""Upload the file"""
|
||||
|
||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
import uuid
|
||||
import threading
|
||||
from typing import Protocol, Self
|
||||
|
||||
import create_folder_widget
|
||||
@@ -151,10 +152,14 @@ class FileListWidget(QListWidget):
|
||||
self.upload_file(file_path)
|
||||
|
||||
def upload_file(self, file_path):
|
||||
response = self.current_response()
|
||||
if not isinstance(response, ListResponse):
|
||||
return
|
||||
File.create(file_path, response.folder_id)
|
||||
def _inner():
|
||||
response = self.current_response()
|
||||
if not isinstance(response, ListResponse):
|
||||
return
|
||||
File.create(file_path, response.folder_id)
|
||||
self.update_response()
|
||||
|
||||
threading.Thread(target=_inner).start()
|
||||
|
||||
def add_item(self, item: DisplayProtocol):
|
||||
widget = QListWidgetItem(item.name())
|
||||
@@ -249,7 +254,7 @@ class Sidebar(QWidget):
|
||||
self.folder_widget.show()
|
||||
|
||||
def sync_all(self):
|
||||
sync.SyncData.sync_all()
|
||||
threading.Thread(target=sync.SyncData.sync_all).start()
|
||||
|
||||
|
||||
class MainFileWidget(QWidget):
|
||||
|
||||
+16
-11
@@ -33,7 +33,7 @@ class Folder(pydantic.BaseModel):
|
||||
if not response:
|
||||
QMessageBox.warning(None, "Error deleting folder", response.text)
|
||||
|
||||
def details(self, list: file_widgets.FileListWidget) -> QWidget:
|
||||
def details(self, _: file_widgets.FileListWidget) -> QWidget:
|
||||
import folder_info
|
||||
|
||||
return folder_info.FolderInfoWidget(self)
|
||||
@@ -49,13 +49,18 @@ class Folder(pydantic.BaseModel):
|
||||
|
||||
@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('"'))
|
||||
try:
|
||||
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('"'))
|
||||
except Exception as e:
|
||||
QMessageBox.warning(None, "Error creating the folder", str(e))
|
||||
|
||||
@@ -30,34 +30,40 @@ class Permission(enum.StrEnum):
|
||||
manage = enum.auto()
|
||||
|
||||
def set(self, folder_id: uuid.UUID, user_id: int):
|
||||
response = RequestClient().client.post(
|
||||
"/permissions",
|
||||
json={
|
||||
"folder_id": str(folder_id),
|
||||
"permission_type": str(self),
|
||||
"user_id": int(user_id),
|
||||
},
|
||||
)
|
||||
if not response.is_success:
|
||||
QMessageBox.warning(
|
||||
None, "Error setting permissions", response.text
|
||||
try:
|
||||
response = RequestClient().client.post(
|
||||
"/permissions",
|
||||
json={
|
||||
"folder_id": str(folder_id),
|
||||
"permission_type": str(self),
|
||||
"user_id": int(user_id),
|
||||
},
|
||||
)
|
||||
if not response.is_success:
|
||||
QMessageBox.warning(
|
||||
None, "Error setting permissions", response.text
|
||||
)
|
||||
except Exception as e:
|
||||
QMessageBox.warning(None, "Error setting permissions", str(e))
|
||||
|
||||
@staticmethod
|
||||
def delete(folder_id: uuid.UUID, user_id: int, widget: FolderInfoWidget):
|
||||
response = RequestClient().client.delete(
|
||||
"/permissions",
|
||||
params={
|
||||
"folder_id": folder_id,
|
||||
"user_id": user_id,
|
||||
},
|
||||
)
|
||||
if not response.is_success:
|
||||
QMessageBox.warning(
|
||||
None, "Error deleting permissions", response.text
|
||||
try:
|
||||
response = RequestClient().client.delete(
|
||||
"/permissions",
|
||||
params={
|
||||
"folder_id": folder_id,
|
||||
"user_id": user_id,
|
||||
},
|
||||
)
|
||||
else:
|
||||
widget.redraw()
|
||||
if not response.is_success:
|
||||
QMessageBox.warning(
|
||||
None, "Error deleting permissions", response.text
|
||||
)
|
||||
else:
|
||||
widget.redraw()
|
||||
except Exception as e:
|
||||
QMessageBox.warning(None, "Error deleting permissions", str(e))
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
@@ -66,12 +72,18 @@ class Permissions:
|
||||
|
||||
@staticmethod
|
||||
def get(folder_id: uuid.UUID) -> Permissions:
|
||||
mapping = pydantic.TypeAdapter(dict[str, Permission]).validate_json(
|
||||
RequestClient()
|
||||
.client.get("/permissions", params={"folder_id": folder_id})
|
||||
.text
|
||||
)
|
||||
return Permissions(mapping)
|
||||
try:
|
||||
mapping = pydantic.TypeAdapter(
|
||||
dict[str, Permission]
|
||||
).validate_json(
|
||||
RequestClient()
|
||||
.client.get("/permissions", params={"folder_id": folder_id})
|
||||
.text
|
||||
)
|
||||
return Permissions(mapping)
|
||||
except Exception as e:
|
||||
QMessageBox.warning(None, "Error getting permissions", str(e))
|
||||
return Permissions({})
|
||||
|
||||
|
||||
class FolderInfoWidget(QWidget):
|
||||
|
||||
@@ -10,7 +10,7 @@ class State(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.setWindowTitle("Auth App")
|
||||
self.setWindowTitle("Drive application")
|
||||
|
||||
self.stack = QStackedWidget()
|
||||
self.register_widget = auth.RegisterWidget(self)
|
||||
@@ -39,10 +39,6 @@ class State(QMainWindow):
|
||||
try:
|
||||
keyring.set_password("auth_app", "access_token", token)
|
||||
request_client.RequestClient().set_token(token)
|
||||
sync.SyncData.sync_all(
|
||||
# "/home/stnicolay/backups",
|
||||
# uuid.UUID("0191397f-ae77-7b2a-bed7-9d28ed56a90a"),
|
||||
)
|
||||
self.file_widget = file_widgets.MainFileWidget(self)
|
||||
self.stack.addWidget(self.file_widget)
|
||||
self.stack.setCurrentWidget(self.file_widget)
|
||||
@@ -56,4 +52,5 @@ class State(QMainWindow):
|
||||
def logout(self):
|
||||
keyring.delete_password("auth_app", "access_token")
|
||||
request_client.RequestClient().delete_token()
|
||||
sync.SyncData.delete_all()
|
||||
self.switch_to_login()
|
||||
|
||||
+31
-12
@@ -3,13 +3,15 @@ from __future__ import annotations
|
||||
import datetime
|
||||
import os
|
||||
import uuid
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from concurrent.futures import ProcessPoolExecutor
|
||||
from time import ctime
|
||||
|
||||
from file import File
|
||||
from folder import Folder
|
||||
from request_client import RequestClient
|
||||
from sqlmodel import Field, Session, SQLModel, create_engine, delete, select
|
||||
import multiprocessing
|
||||
from PyQt6.QtWidgets import QMessageBox
|
||||
|
||||
|
||||
class FolderStructure(Folder):
|
||||
@@ -41,20 +43,29 @@ class SyncData(SQLModel, table=True):
|
||||
path: str
|
||||
last_updated: datetime.datetime
|
||||
|
||||
def sync_one(self):
|
||||
try:
|
||||
merge_folder(
|
||||
self.path,
|
||||
FolderStructure.get_structure(self.folder_id),
|
||||
self.last_updated,
|
||||
)
|
||||
except Exception as e:
|
||||
SyncData.delete(self.folder_id)
|
||||
QMessageBox.warning(
|
||||
None,
|
||||
"Error syncing folder",
|
||||
f"Error syncing {self.path!r} folder:\n{e}",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def sync_all():
|
||||
|
||||
with Session(engine) as s:
|
||||
syncs = s.exec(select(SyncData)).fetchall()
|
||||
with ThreadPoolExecutor(3) as pool:
|
||||
map_ = pool.map(
|
||||
lambda sync: merge_folder(
|
||||
sync.path,
|
||||
FolderStructure.get_structure(sync.folder_id),
|
||||
sync.last_updated,
|
||||
),
|
||||
syncs,
|
||||
)
|
||||
tuple(map_)
|
||||
|
||||
with ProcessPoolExecutor(3) as pool:
|
||||
tuple(pool.map(SyncData.sync_one, syncs))
|
||||
|
||||
@staticmethod
|
||||
def new_sync(path: str, folder_id: uuid.UUID):
|
||||
@@ -66,7 +77,9 @@ class SyncData(SQLModel, table=True):
|
||||
)
|
||||
s.add(sync)
|
||||
s.commit()
|
||||
upload_folder(path, folder_id)
|
||||
multiprocessing.Process(
|
||||
target=lambda: upload_folder(path, folder_id)
|
||||
).start()
|
||||
|
||||
@staticmethod
|
||||
def get_for_folder(folder_id: uuid.UUID) -> SyncData | None:
|
||||
@@ -81,6 +94,12 @@ class SyncData(SQLModel, table=True):
|
||||
s.exec(delete(SyncData).where(SyncData.folder_id == folder_id))
|
||||
s.commit()
|
||||
|
||||
@staticmethod
|
||||
def delete_all():
|
||||
with Session(engine) as s:
|
||||
s.exec(delete(SyncData))
|
||||
s.commit()
|
||||
|
||||
|
||||
sqlite_file_name = "database.db"
|
||||
sqlite_url = f"sqlite:///{sqlite_file_name}"
|
||||
|
||||
@@ -29,7 +29,9 @@ class User(pydantic.BaseModel):
|
||||
url = "/users/current"
|
||||
response = RequestClient().client.get(url, params=params)
|
||||
if not response.is_success:
|
||||
QMessageBox.warning(None, "Error getting permissions", response.text)
|
||||
QMessageBox.warning(
|
||||
None, "Error getting permissions", response.text
|
||||
)
|
||||
return
|
||||
return User.model_validate_json(response.text)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user