149 lines
4.3 KiB
Python
149 lines
4.3 KiB
Python
from __future__ import annotations
|
|
|
|
import datetime
|
|
import os
|
|
import uuid
|
|
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):
|
|
files: list[File]
|
|
folders: list[FolderStructure]
|
|
|
|
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 | None:
|
|
for file in self.files:
|
|
if file.file_name == name:
|
|
return file
|
|
|
|
@staticmethod
|
|
def get_structure(folder_id: uuid.UUID) -> FolderStructure:
|
|
return FolderStructure.model_validate_json(
|
|
RequestClient()
|
|
.client.get("/folders/structure", params={"folder_id": folder_id})
|
|
.text
|
|
)
|
|
|
|
|
|
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
|
|
|
|
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 ProcessPoolExecutor(3) as pool:
|
|
tuple(pool.map(SyncData.sync_one, syncs))
|
|
|
|
@staticmethod
|
|
def new_sync(path: str, folder_id: uuid.UUID):
|
|
with Session(engine) as s:
|
|
sync = SyncData(
|
|
path=path,
|
|
last_updated=datetime.datetime.now(),
|
|
folder_id=folder_id,
|
|
)
|
|
s.add(sync)
|
|
s.commit()
|
|
multiprocessing.Process(
|
|
target=lambda: upload_folder(path, folder_id)
|
|
).start()
|
|
|
|
@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()
|
|
|
|
@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}"
|
|
|
|
engine = create_engine(sqlite_url, echo=True)
|
|
SQLModel.metadata.create_all(engine)
|
|
|
|
|
|
def upload_folder(path: str, folder_id: uuid.UUID):
|
|
items = os.listdir(path)
|
|
for item in items:
|
|
item_path = os.path.join(path, item)
|
|
if os.path.isfile(item_path):
|
|
File.create(item_path, folder_id)
|
|
elif os.path.isdir(item_path):
|
|
id = Folder.create(item, folder_id)
|
|
upload_folder(item_path, id)
|
|
|
|
|
|
def merge_folder(
|
|
path: str,
|
|
structure: FolderStructure,
|
|
time: datetime.datetime,
|
|
):
|
|
for item in os.listdir(path):
|
|
item_path = os.path.join(path, item)
|
|
if os.path.isfile(item_path):
|
|
file = structure.find_file(item)
|
|
if file is None:
|
|
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"
|
|
)
|
|
print(item, mtime, time)
|
|
if mtime > time:
|
|
file.modify(item_path)
|
|
else:
|
|
file.download(item_path)
|
|
elif os.path.isdir(item_path):
|
|
folder = structure.find_folder(item)
|
|
if folder is None:
|
|
folder_id = Folder.create(item, structure.folder_id)
|
|
upload_folder(item_path, folder_id)
|
|
else:
|
|
merge_folder(item_path, folder, time)
|