Compare commits
No commits in common. "27a09a8b543a3a713950a99e7075e342d250c7a1" and "d6ecae08bd33fb067825ff64cffd32401f49b7d0" have entirely different histories.
27a09a8b54
...
d6ecae08bd
@ -3,15 +3,12 @@ from __future__ import annotations
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import file_widgets
|
import file_widgets
|
||||||
from PyQt6.QtWidgets import (QLineEdit, QMessageBox, QPushButton, QVBoxLayout,
|
from PyQt6.QtWidgets import QLineEdit, QMessageBox, QPushButton, QVBoxLayout, QWidget
|
||||||
QWidget)
|
|
||||||
from request_client import RequestClient
|
from request_client import RequestClient
|
||||||
|
|
||||||
|
|
||||||
class CreateFolderWidget(QWidget):
|
class CreateFolderWidget(QWidget):
|
||||||
def __init__(
|
def __init__(self, folder_id: uuid.UUID, file_list: file_widgets.FileListWidget):
|
||||||
self, folder_id: uuid.UUID, file_list: file_widgets.FileListWidget
|
|
||||||
):
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.folder_id = folder_id
|
self.folder_id = folder_id
|
||||||
self.file_list = file_list
|
self.file_list = file_list
|
||||||
@ -25,7 +22,6 @@ class CreateFolderWidget(QWidget):
|
|||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
|
|
||||||
def submit(self):
|
def submit(self):
|
||||||
try:
|
|
||||||
response = RequestClient().client.post(
|
response = RequestClient().client.post(
|
||||||
"/folders",
|
"/folders",
|
||||||
json={
|
json={
|
||||||
@ -34,15 +30,8 @@ class CreateFolderWidget(QWidget):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
if not response.is_success:
|
if not response.is_success:
|
||||||
QMessageBox.warning(
|
QMessageBox.warning(None, "Error creating folder", response.text)
|
||||||
None, "Error creating folder", response.text
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
QMessageBox.information(
|
QMessageBox.information(None, "Folder created", "Folder created")
|
||||||
None, "Folder created", "Folder created"
|
|
||||||
)
|
|
||||||
self.file_list.update_response()
|
self.file_list.update_response()
|
||||||
self.close()
|
self.close()
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
QMessageBox.critical(self, "HTTP error", str(e))
|
|
||||||
|
@ -6,14 +6,11 @@ import hashlib
|
|||||||
import os
|
import os
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import dateutil
|
|
||||||
import dateutil.tz
|
|
||||||
import file_widgets
|
import file_widgets
|
||||||
import pydantic
|
import pydantic
|
||||||
from PyQt6.QtGui import QIcon
|
from PyQt6.QtGui import QIcon
|
||||||
from PyQt6.QtWidgets import QFileDialog, QLabel, QMessageBox, QWidget
|
from PyQt6.QtWidgets import QFileDialog, QLabel, QMessageBox, QWidget
|
||||||
from request_client import RequestClient
|
from request_client import RequestClient
|
||||||
from utils import resource_path
|
|
||||||
|
|
||||||
|
|
||||||
class File(pydantic.BaseModel):
|
class File(pydantic.BaseModel):
|
||||||
@ -34,14 +31,12 @@ class File(pydantic.BaseModel):
|
|||||||
|
|
||||||
def details(self, list: file_widgets.FileListWidget) -> QWidget:
|
def details(self, list: file_widgets.FileListWidget) -> QWidget:
|
||||||
del list
|
del list
|
||||||
file_size = self._format_size(self.file_size)
|
file_size = self._format_bytes(self.file_size)
|
||||||
file_size_text = f"{file_size[0]:.2f} {file_size[1]}"
|
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 = (
|
details = (
|
||||||
f"file id: {self.file_id}\nfile_name: {self.file_name}\n"
|
f"file id: {self.file_id}\nfile_name: {self.file_name}\n"
|
||||||
+ f"file_size: {file_size_text}\n"
|
+ f"file_size: {file_size_text}\n"
|
||||||
+ f"created at: {created_at}\nupdated at: {updated_at}"
|
+ f"created at: {self.created_at}\nupdated at: {self.updated_at}"
|
||||||
)
|
)
|
||||||
label = QLabel()
|
label = QLabel()
|
||||||
label.setWindowTitle("File info")
|
label.setWindowTitle("File info")
|
||||||
@ -49,7 +44,7 @@ class File(pydantic.BaseModel):
|
|||||||
return label
|
return label
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _format_size(size: int):
|
def _format_bytes(size: int):
|
||||||
power = 2**10
|
power = 2**10
|
||||||
n = 0
|
n = 0
|
||||||
power_labels = {0: "", 1: "kibi", 2: "mebi", 3: "gibi", 4: "tebi"}
|
power_labels = {0: "", 1: "kibi", 2: "mebi", 3: "gibi", 4: "tebi"}
|
||||||
@ -58,14 +53,8 @@ class File(pydantic.BaseModel):
|
|||||||
n += 1
|
n += 1
|
||||||
return size, power_labels[n] + "bytes"
|
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:
|
def icon(self) -> QIcon:
|
||||||
return QIcon(resource_path("assets/file.png"))
|
return QIcon("assets/file.png")
|
||||||
|
|
||||||
def double_click(self, list: file_widgets.FileListWidget) -> None:
|
def double_click(self, list: file_widgets.FileListWidget) -> None:
|
||||||
location = QFileDialog.getExistingDirectory(
|
location = QFileDialog.getExistingDirectory(
|
||||||
@ -86,7 +75,6 @@ class File(pydantic.BaseModel):
|
|||||||
|
|
||||||
def create(path: str, parent_id: uuid.UUID):
|
def create(path: str, parent_id: uuid.UUID):
|
||||||
"""Upload the file"""
|
"""Upload the file"""
|
||||||
print(path)
|
|
||||||
file_name = os.path.basename(path)
|
file_name = os.path.basename(path)
|
||||||
try:
|
try:
|
||||||
with open(path, "rb") as f:
|
with open(path, "rb") as f:
|
||||||
@ -105,21 +93,14 @@ class File(pydantic.BaseModel):
|
|||||||
QMessageBox.critical(None, "HTTP Error", str(e))
|
QMessageBox.critical(None, "HTTP Error", str(e))
|
||||||
|
|
||||||
def download(self, path: str):
|
def download(self, path: str):
|
||||||
try:
|
|
||||||
with open(path, "wb") as f, RequestClient().client.stream(
|
with open(path, "wb") as f, RequestClient().client.stream(
|
||||||
"GET", "/files", params={"file_id": self.file_id}
|
"GET", "/files", params={"file_id": self.file_id}
|
||||||
) as stream:
|
) as stream:
|
||||||
if not stream.is_success:
|
if not stream.is_success:
|
||||||
QMessageBox.warning(
|
QMessageBox.warning(None, "Error downloading the file")
|
||||||
None,
|
|
||||||
"Error downloading the file",
|
|
||||||
"Error downloading the file",
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
for data in stream.iter_bytes():
|
for data in stream.iter_bytes():
|
||||||
f.write(data)
|
f.write(data)
|
||||||
except Exception as e:
|
|
||||||
QMessageBox.warning(None, "Error downloading the file", str(e))
|
|
||||||
|
|
||||||
def modify(self, path: str):
|
def modify(self, path: str):
|
||||||
"""Upload the file"""
|
"""Upload the file"""
|
||||||
|
@ -2,13 +2,12 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import dataclasses
|
import dataclasses
|
||||||
import uuid
|
import uuid
|
||||||
import threading
|
|
||||||
from typing import Protocol, Self
|
from typing import Protocol, Self
|
||||||
|
|
||||||
import create_folder_widget
|
import create_folder_widget
|
||||||
|
import sync
|
||||||
import pydantic
|
import pydantic
|
||||||
import state
|
import state
|
||||||
import sync
|
|
||||||
import user
|
import user
|
||||||
from file import File
|
from file import File
|
||||||
from folder import Folder
|
from folder import Folder
|
||||||
@ -152,14 +151,10 @@ class FileListWidget(QListWidget):
|
|||||||
self.upload_file(file_path)
|
self.upload_file(file_path)
|
||||||
|
|
||||||
def upload_file(self, file_path):
|
def upload_file(self, file_path):
|
||||||
def _inner():
|
|
||||||
response = self.current_response()
|
response = self.current_response()
|
||||||
if not isinstance(response, ListResponse):
|
if not isinstance(response, ListResponse):
|
||||||
return
|
return
|
||||||
File.create(file_path, response.folder_id)
|
File.create(file_path, response.folder_id)
|
||||||
self.update_response()
|
|
||||||
|
|
||||||
threading.Thread(target=_inner).start()
|
|
||||||
|
|
||||||
def add_item(self, item: DisplayProtocol):
|
def add_item(self, item: DisplayProtocol):
|
||||||
widget = QListWidgetItem(item.name())
|
widget = QListWidgetItem(item.name())
|
||||||
@ -254,7 +249,7 @@ class Sidebar(QWidget):
|
|||||||
self.folder_widget.show()
|
self.folder_widget.show()
|
||||||
|
|
||||||
def sync_all(self):
|
def sync_all(self):
|
||||||
threading.Thread(target=sync.SyncData.sync_all).start()
|
sync.SyncData.sync_all()
|
||||||
|
|
||||||
|
|
||||||
class MainFileWidget(QWidget):
|
class MainFileWidget(QWidget):
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import typing
|
|
||||||
import uuid
|
import uuid
|
||||||
|
import typing
|
||||||
|
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
import file_widgets
|
import file_widgets
|
||||||
@ -11,7 +11,6 @@ import pydantic
|
|||||||
from PyQt6.QtGui import QIcon
|
from PyQt6.QtGui import QIcon
|
||||||
from PyQt6.QtWidgets import QMessageBox, QWidget
|
from PyQt6.QtWidgets import QMessageBox, QWidget
|
||||||
from request_client import RequestClient
|
from request_client import RequestClient
|
||||||
from utils import resource_path
|
|
||||||
|
|
||||||
|
|
||||||
class Folder(pydantic.BaseModel):
|
class Folder(pydantic.BaseModel):
|
||||||
@ -33,13 +32,13 @@ class Folder(pydantic.BaseModel):
|
|||||||
if not response:
|
if not response:
|
||||||
QMessageBox.warning(None, "Error deleting folder", response.text)
|
QMessageBox.warning(None, "Error deleting folder", response.text)
|
||||||
|
|
||||||
def details(self, _: file_widgets.FileListWidget) -> QWidget:
|
def details(self, list: file_widgets.FileListWidget) -> QWidget:
|
||||||
import folder_info
|
import folder_info
|
||||||
|
|
||||||
return folder_info.FolderInfoWidget(self)
|
return folder_info.FolderInfoWidget(self)
|
||||||
|
|
||||||
def icon(self) -> QIcon:
|
def icon(self) -> QIcon:
|
||||||
return QIcon(resource_path("assets/folder.png"))
|
return QIcon("assets/folder.png")
|
||||||
|
|
||||||
def double_click(self, list: file_widgets.FileListWidget) -> None:
|
def double_click(self, list: file_widgets.FileListWidget) -> None:
|
||||||
import file_widgets
|
import file_widgets
|
||||||
@ -49,7 +48,6 @@ class Folder(pydantic.BaseModel):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create(name: str, parent_id: uuid.UUID) -> uuid.UUID:
|
def create(name: str, parent_id: uuid.UUID) -> uuid.UUID:
|
||||||
try:
|
|
||||||
response = RequestClient().client.post(
|
response = RequestClient().client.post(
|
||||||
"/folders",
|
"/folders",
|
||||||
json={
|
json={
|
||||||
@ -58,9 +56,5 @@ class Folder(pydantic.BaseModel):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
if not response.is_success:
|
if not response.is_success:
|
||||||
QMessageBox.warning(
|
QMessageBox.warning(None, "Error creating folder", response.text)
|
||||||
None, "Error creating folder", response.text
|
|
||||||
)
|
|
||||||
return uuid.UUID(response.text.strip('"'))
|
return uuid.UUID(response.text.strip('"'))
|
||||||
except Exception as e:
|
|
||||||
QMessageBox.warning(None, "Error creating the folder", str(e))
|
|
||||||
|
@ -30,7 +30,6 @@ class Permission(enum.StrEnum):
|
|||||||
manage = enum.auto()
|
manage = enum.auto()
|
||||||
|
|
||||||
def set(self, folder_id: uuid.UUID, user_id: int):
|
def set(self, folder_id: uuid.UUID, user_id: int):
|
||||||
try:
|
|
||||||
response = RequestClient().client.post(
|
response = RequestClient().client.post(
|
||||||
"/permissions",
|
"/permissions",
|
||||||
json={
|
json={
|
||||||
@ -43,12 +42,9 @@ class Permission(enum.StrEnum):
|
|||||||
QMessageBox.warning(
|
QMessageBox.warning(
|
||||||
None, "Error setting permissions", response.text
|
None, "Error setting permissions", response.text
|
||||||
)
|
)
|
||||||
except Exception as e:
|
|
||||||
QMessageBox.warning(None, "Error setting permissions", str(e))
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def delete(folder_id: uuid.UUID, user_id: int, widget: FolderInfoWidget):
|
def delete(folder_id: uuid.UUID, user_id: int, widget: FolderInfoWidget):
|
||||||
try:
|
|
||||||
response = RequestClient().client.delete(
|
response = RequestClient().client.delete(
|
||||||
"/permissions",
|
"/permissions",
|
||||||
params={
|
params={
|
||||||
@ -62,8 +58,6 @@ class Permission(enum.StrEnum):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
widget.redraw()
|
widget.redraw()
|
||||||
except Exception as e:
|
|
||||||
QMessageBox.warning(None, "Error deleting permissions", str(e))
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(slots=True)
|
@dataclass(slots=True)
|
||||||
@ -72,18 +66,12 @@ class Permissions:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get(folder_id: uuid.UUID) -> Permissions:
|
def get(folder_id: uuid.UUID) -> Permissions:
|
||||||
try:
|
mapping = pydantic.TypeAdapter(dict[str, Permission]).validate_json(
|
||||||
mapping = pydantic.TypeAdapter(
|
|
||||||
dict[str, Permission]
|
|
||||||
).validate_json(
|
|
||||||
RequestClient()
|
RequestClient()
|
||||||
.client.get("/permissions", params={"folder_id": folder_id})
|
.client.get("/permissions", params={"folder_id": folder_id})
|
||||||
.text
|
.text
|
||||||
)
|
)
|
||||||
return Permissions(mapping)
|
return Permissions(mapping)
|
||||||
except Exception as e:
|
|
||||||
QMessageBox.warning(None, "Error getting permissions", str(e))
|
|
||||||
return Permissions({})
|
|
||||||
|
|
||||||
|
|
||||||
class FolderInfoWidget(QWidget):
|
class FolderInfoWidget(QWidget):
|
||||||
|
@ -3,7 +3,7 @@ import file_widgets
|
|||||||
import keyring
|
import keyring
|
||||||
import request_client
|
import request_client
|
||||||
import sync
|
import sync
|
||||||
from PyQt6.QtWidgets import QMainWindow, QMessageBox, QStackedWidget
|
from PyQt6.QtWidgets import QMainWindow, QStackedWidget, QMessageBox
|
||||||
|
|
||||||
|
|
||||||
class State(QMainWindow):
|
class State(QMainWindow):
|
||||||
@ -39,6 +39,10 @@ class State(QMainWindow):
|
|||||||
try:
|
try:
|
||||||
keyring.set_password("auth_app", "access_token", token)
|
keyring.set_password("auth_app", "access_token", token)
|
||||||
request_client.RequestClient().set_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.file_widget = file_widgets.MainFileWidget(self)
|
||||||
self.stack.addWidget(self.file_widget)
|
self.stack.addWidget(self.file_widget)
|
||||||
self.stack.setCurrentWidget(self.file_widget)
|
self.stack.setCurrentWidget(self.file_widget)
|
||||||
@ -52,5 +56,4 @@ class State(QMainWindow):
|
|||||||
def logout(self):
|
def logout(self):
|
||||||
keyring.delete_password("auth_app", "access_token")
|
keyring.delete_password("auth_app", "access_token")
|
||||||
request_client.RequestClient().delete_token()
|
request_client.RequestClient().delete_token()
|
||||||
sync.SyncData.delete_all()
|
|
||||||
self.switch_to_login()
|
self.switch_to_login()
|
||||||
|
@ -3,15 +3,13 @@ from __future__ import annotations
|
|||||||
import datetime
|
import datetime
|
||||||
import os
|
import os
|
||||||
import uuid
|
import uuid
|
||||||
from concurrent.futures import ProcessPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
from time import ctime
|
from time import ctime
|
||||||
|
|
||||||
from file import File
|
from file import File
|
||||||
from folder import Folder
|
from folder import Folder
|
||||||
from request_client import RequestClient
|
from request_client import RequestClient
|
||||||
from sqlmodel import Field, Session, SQLModel, create_engine, delete, select
|
from sqlmodel import Field, Session, SQLModel, create_engine, delete, select
|
||||||
import multiprocessing
|
|
||||||
from PyQt6.QtWidgets import QMessageBox
|
|
||||||
|
|
||||||
|
|
||||||
class FolderStructure(Folder):
|
class FolderStructure(Folder):
|
||||||
@ -43,29 +41,20 @@ class SyncData(SQLModel, table=True):
|
|||||||
path: str
|
path: str
|
||||||
last_updated: datetime.datetime
|
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
|
@staticmethod
|
||||||
def sync_all():
|
def sync_all():
|
||||||
|
|
||||||
with Session(engine) as s:
|
with Session(engine) as s:
|
||||||
syncs = s.exec(select(SyncData)).fetchall()
|
syncs = s.exec(select(SyncData)).fetchall()
|
||||||
|
with ThreadPoolExecutor(3) as pool:
|
||||||
with ProcessPoolExecutor(3) as pool:
|
map_ = pool.map(
|
||||||
tuple(pool.map(SyncData.sync_one, syncs))
|
lambda sync: merge_folder(
|
||||||
|
sync.path,
|
||||||
|
FolderStructure.get_structure(sync.folder_id),
|
||||||
|
sync.last_updated,
|
||||||
|
),
|
||||||
|
syncs,
|
||||||
|
)
|
||||||
|
tuple(map_)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def new_sync(path: str, folder_id: uuid.UUID):
|
def new_sync(path: str, folder_id: uuid.UUID):
|
||||||
@ -77,9 +66,7 @@ class SyncData(SQLModel, table=True):
|
|||||||
)
|
)
|
||||||
s.add(sync)
|
s.add(sync)
|
||||||
s.commit()
|
s.commit()
|
||||||
multiprocessing.Process(
|
upload_folder(path, folder_id)
|
||||||
target=lambda: upload_folder(path, folder_id)
|
|
||||||
).start()
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_for_folder(folder_id: uuid.UUID) -> SyncData | None:
|
def get_for_folder(folder_id: uuid.UUID) -> SyncData | None:
|
||||||
@ -94,12 +81,6 @@ class SyncData(SQLModel, table=True):
|
|||||||
s.exec(delete(SyncData).where(SyncData.folder_id == folder_id))
|
s.exec(delete(SyncData).where(SyncData.folder_id == folder_id))
|
||||||
s.commit()
|
s.commit()
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def delete_all():
|
|
||||||
with Session(engine) as s:
|
|
||||||
s.exec(delete(SyncData))
|
|
||||||
s.commit()
|
|
||||||
|
|
||||||
|
|
||||||
sqlite_file_name = "database.db"
|
sqlite_file_name = "database.db"
|
||||||
sqlite_url = f"sqlite:///{sqlite_file_name}"
|
sqlite_url = f"sqlite:///{sqlite_file_name}"
|
||||||
|
@ -29,9 +29,7 @@ class User(pydantic.BaseModel):
|
|||||||
url = "/users/current"
|
url = "/users/current"
|
||||||
response = RequestClient().client.get(url, params=params)
|
response = RequestClient().client.get(url, params=params)
|
||||||
if not response.is_success:
|
if not response.is_success:
|
||||||
QMessageBox.warning(
|
QMessageBox.warning(None, "Error getting permissions", response.text)
|
||||||
None, "Error getting permissions", response.text
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
return User.model_validate_json(response.text)
|
return User.model_validate_json(response.text)
|
||||||
|
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
import os
|
|
||||||
import sys
|
|
||||||
from functools import cache
|
|
||||||
|
|
||||||
_DEFAULT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
||||||
|
|
||||||
|
|
||||||
@cache
|
|
||||||
def resource_path(relative_path: str) -> str:
|
|
||||||
"""Get absolute path to resource, works for dev and for PyInstaller"""
|
|
||||||
base_path = getattr(sys, "_MEIPASS", _DEFAULT)
|
|
||||||
return os.path.join(base_path, relative_path)
|
|
43
poetry.lock
generated
43
poetry.lock
generated
@ -427,18 +427,18 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-ena
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jaraco-context"
|
name = "jaraco-context"
|
||||||
version = "6.0.1"
|
version = "5.3.0"
|
||||||
description = "Useful decorators and context managers"
|
description = "Useful decorators and context managers"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "jaraco.context-6.0.1-py3-none-any.whl", hash = "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4"},
|
{file = "jaraco.context-5.3.0-py3-none-any.whl", hash = "sha256:3e16388f7da43d384a1a7cd3452e72e14732ac9fe459678773a3608a812bf266"},
|
||||||
{file = "jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3"},
|
{file = "jaraco.context-5.3.0.tar.gz", hash = "sha256:c2f67165ce1f9be20f32f650f25d8edfc1646a8aeee48ae06fb35f90763576d2"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
||||||
test = ["portend", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
|
testing = ["portend", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jaraco-functools"
|
name = "jaraco-functools"
|
||||||
@ -788,20 +788,6 @@ files = [
|
|||||||
{file = "PyQt6_sip-13.8.0.tar.gz", hash = "sha256:2f74cf3d6d9cab5152bd9f49d570b2dfb87553ebb5c4919abfde27f5b9fd69d4"},
|
{file = "PyQt6_sip-13.8.0.tar.gz", hash = "sha256:2f74cf3d6d9cab5152bd9f49d570b2dfb87553ebb5c4919abfde27f5b9fd69d4"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "python-dateutil"
|
|
||||||
version = "2.9.0.post0"
|
|
||||||
description = "Extensions to the standard Python datetime module"
|
|
||||||
optional = false
|
|
||||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
|
|
||||||
files = [
|
|
||||||
{file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
|
|
||||||
{file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
six = ">=1.5"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "python-dotenv"
|
name = "python-dotenv"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@ -818,13 +804,13 @@ cli = ["click (>=5.0)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pywin32-ctypes"
|
name = "pywin32-ctypes"
|
||||||
version = "0.2.3"
|
version = "0.2.2"
|
||||||
description = "A (partial) reimplementation of pywin32 using ctypes/cffi"
|
description = "A (partial) reimplementation of pywin32 using ctypes/cffi"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.6"
|
||||||
files = [
|
files = [
|
||||||
{file = "pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755"},
|
{file = "pywin32-ctypes-0.2.2.tar.gz", hash = "sha256:3426e063bdd5fd4df74a14fa3cf80a0b42845a87e1d1e81f6549f9daec593a60"},
|
||||||
{file = "pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8"},
|
{file = "pywin32_ctypes-0.2.2-py3-none-any.whl", hash = "sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -842,17 +828,6 @@ files = [
|
|||||||
cryptography = ">=2.0"
|
cryptography = ">=2.0"
|
||||||
jeepney = ">=0.6"
|
jeepney = ">=0.6"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "six"
|
|
||||||
version = "1.16.0"
|
|
||||||
description = "Python 2 and 3 compatibility utilities"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
|
||||||
files = [
|
|
||||||
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
|
||||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sniffio"
|
name = "sniffio"
|
||||||
version = "1.3.1"
|
version = "1.3.1"
|
||||||
@ -980,4 +955,4 @@ files = [
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.12"
|
python-versions = "^3.12"
|
||||||
content-hash = "fd70e86969d9632a562a6755e8ff2f7e9d38e795da2f3c5c04eabe77d5723fdb"
|
content-hash = "6e84cef360a53faa7bba2d9bb57f01093ff0291c28c02aff4727b6c21fd6d715"
|
||||||
|
@ -13,7 +13,6 @@ pyqt6 = "^6.7.1"
|
|||||||
keyring = "^25.3.0"
|
keyring = "^25.3.0"
|
||||||
python-dotenv = "^1.0.1"
|
python-dotenv = "^1.0.1"
|
||||||
sqlmodel = "^0.0.21"
|
sqlmodel = "^0.0.21"
|
||||||
python-dateutil = "^2.9.0.post0"
|
|
||||||
|
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
|
Reference in New Issue
Block a user