3 Commits

15 changed files with 115 additions and 46 deletions

View File

@ -1 +1 @@
1.3.11 1.3.12

View File

@ -17,10 +17,26 @@ class FilesApiCreateResponse:
id: str id: str
@dataclass(frozen=True)
class FilesApiListResponse:
count: int
next: str | None
previous: str | None
results: list[File]
class FilesApi(BaseApi): class FilesApi(BaseApi):
ENTRYPOINT: Final[str] = "v3/files/" ENTRYPOINT: Final[str] = "v3/files/"
FALLBACK_MIMETYPE: Final[str] = "application/octet-stream" FALLBACK_MIMETYPE: Final[str] = "application/octet-stream"
def list(self, folder_id: str, page_num: int) -> FilesApiListResponse:
url = self.ENTRYPOINT
token_check(self.connection)
params: dict[str, str | int] = {"folder_id": folder_id, "page": page_num}
response = self.connection.get(url, params=params)
self._raise_response_error(response)
return TypeAdapter(FilesApiListResponse).validate_python(response.json())
def retrieve(self, id: str) -> File: def retrieve(self, id: str) -> File:
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name) # print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
url = self.ENTRYPOINT + id + "/" url = self.ENTRYPOINT + id + "/"

View File

@ -3,7 +3,7 @@ from abc import ABC, abstractmethod
from typing import Any from typing import Any
from unicodedata import normalize from unicodedata import normalize
from mdrsclient.api import FoldersApi, LaboratoriesApi from mdrsclient.api import FilesApi, FoldersApi, LaboratoriesApi
from mdrsclient.config import ConfigFile from mdrsclient.config import ConfigFile
from mdrsclient.connection import MDRSConnection from mdrsclient.connection import MDRSConnection
from mdrsclient.exceptions import ( from mdrsclient.exceptions import (
@ -12,7 +12,8 @@ from mdrsclient.exceptions import (
UnauthorizedException, UnauthorizedException,
UnexpectedException, UnexpectedException,
) )
from mdrsclient.models import Folder, Laboratory from mdrsclient.models import File, Folder, Laboratory
from mdrsclient.utils import page_num_from_url
class BaseCommand(ABC): class BaseCommand(ABC):
@ -52,6 +53,19 @@ class BaseCommand(ABC):
folder_api.auth(folders[0].id, password) folder_api.auth(folders[0].id, password)
return folder_api.retrieve(folders[0].id) return folder_api.retrieve(folders[0].id)
@classmethod
def _find_files(cls, connection: MDRSConnection, folder_id: str) -> list[File]:
files_api = FilesApi(connection)
page = 1
results_file = []
while page:
result = files_api.list(folder_id, page)
results_file.extend(result.results)
page = 0
if result.next:
page = page_num_from_url(result.next)
return results_file
@classmethod @classmethod
def _parse_remote_host(cls, path: str) -> str: def _parse_remote_host(cls, path: str) -> str:
path_array = path.split(":") path_array = path.split(":")

View File

@ -6,6 +6,7 @@ from unicodedata import normalize
from mdrsclient.api import FilesApi, FoldersApi from mdrsclient.api import FilesApi, FoldersApi
from mdrsclient.commands.base import BaseCommand from mdrsclient.commands.base import BaseCommand
from mdrsclient.exceptions import IllegalArgumentException from mdrsclient.exceptions import IllegalArgumentException
from mdrsclient.models.file import find_file
class CpCommand(BaseCommand): class CpCommand(BaseCommand):
@ -46,11 +47,13 @@ class CpCommand(BaseCommand):
connection = cls._create_connection(s_remote) connection = cls._create_connection(s_remote)
laboratory = cls._find_laboratory(connection, s_laboratory_name) laboratory = cls._find_laboratory(connection, s_laboratory_name)
s_parent_folder = cls._find_folder(connection, laboratory, s_dirname) s_parent_folder = cls._find_folder(connection, laboratory, s_dirname)
s_parent_files = cls._find_files(connection, s_parent_folder.id)
d_parent_folder = cls._find_folder(connection, laboratory, d_dirname) d_parent_folder = cls._find_folder(connection, laboratory, d_dirname)
s_file = s_parent_folder.find_file(s_basename) d_parent_files = cls._find_files(connection, d_parent_folder.id)
s_file = find_file(s_parent_files, s_basename)
if s_file is not None: if s_file is not None:
# source is file # source is file
d_file = d_parent_folder.find_file(d_basename) d_file = find_file(d_parent_files, d_basename)
if d_file is not None: if d_file is not None:
raise IllegalArgumentException(f"File `{d_basename}` already exists.") raise IllegalArgumentException(f"File `{d_basename}` already exists.")
d_sub_folder = d_parent_folder.find_sub_folder(d_basename) d_sub_folder = d_parent_folder.find_sub_folder(d_basename)
@ -66,7 +69,7 @@ class CpCommand(BaseCommand):
# source is folder # source is folder
if not is_recursive: if not is_recursive:
raise IllegalArgumentException(f"Cannot copy `{s_path}`: Is a folder.") raise IllegalArgumentException(f"Cannot copy `{s_path}`: Is a folder.")
if d_parent_folder.find_file(d_basename) is not None: if find_file(d_parent_files, d_basename) is not None:
raise IllegalArgumentException(f"Cannot overwrite non-folder `{d_basename}` with folder `{s_path}`.") raise IllegalArgumentException(f"Cannot overwrite non-folder `{d_basename}` with folder `{s_path}`.")
d_folder = d_parent_folder.find_sub_folder(d_basename) d_folder = d_parent_folder.find_sub_folder(d_basename)
if d_folder is not None: if d_folder is not None:

View File

@ -10,6 +10,7 @@ from mdrsclient.commands.base import BaseCommand
from mdrsclient.connection import MDRSConnection from mdrsclient.connection import MDRSConnection
from mdrsclient.exceptions import IllegalArgumentException, UnexpectedException from mdrsclient.exceptions import IllegalArgumentException, UnexpectedException
from mdrsclient.models import File, Folder, Laboratory from mdrsclient.models import File, Folder, Laboratory
from mdrsclient.models.file import find_file
from mdrsclient.settings import CONCURRENT from mdrsclient.settings import CONCURRENT
@ -77,7 +78,8 @@ class DownloadCommand(BaseCommand):
raise IllegalArgumentException(f"Local directory `{local_path}` not found.") raise IllegalArgumentException(f"Local directory `{local_path}` not found.")
laboratory = cls._find_laboratory(connection, laboratory_name) laboratory = cls._find_laboratory(connection, laboratory_name)
r_parent_folder = cls._find_folder(connection, laboratory, r_dirname, password) r_parent_folder = cls._find_folder(connection, laboratory, r_dirname, password)
file = r_parent_folder.find_file(r_basename) r_parent_files = cls._find_files(connection, r_parent_folder.id)
file = find_file(r_parent_files, r_basename)
if file is not None: if file is not None:
if cls.__check_excludes(excludes, laboratory, r_parent_folder, file): if cls.__check_excludes(excludes, laboratory, r_parent_folder, file):
return return
@ -109,13 +111,14 @@ class DownloadCommand(BaseCommand):
) -> None: ) -> None:
context = DownloadContext(False, is_skip_if_exists, []) context = DownloadContext(False, is_skip_if_exists, [])
folder = folder_api.retrieve(folder_id) folder = folder_api.retrieve(folder_id)
files = cls._find_files(connection, folder.id)
dirname = os.path.join(basedir, folder.name) dirname = os.path.join(basedir, folder.name)
if cls.__check_excludes(excludes, laboratory, folder, None): if cls.__check_excludes(excludes, laboratory, folder, None):
return return
if not os.path.exists(dirname): if not os.path.exists(dirname):
os.makedirs(dirname) os.makedirs(dirname)
print(dirname) print(dirname)
for file in folder.files: for file in files:
if cls.__check_excludes(excludes, laboratory, folder, file): if cls.__check_excludes(excludes, laboratory, folder, file):
continue continue
path = os.path.join(dirname, file.name) path = os.path.join(dirname, file.name)
@ -141,7 +144,7 @@ class DownloadCommand(BaseCommand):
@classmethod @classmethod
def __multiple_download_worker(cls, file_api: FilesApi, info: DownloadFileInfo, is_skip_if_exists: bool) -> bool: def __multiple_download_worker(cls, file_api: FilesApi, info: DownloadFileInfo, is_skip_if_exists: bool) -> bool:
if not is_skip_if_exists or os.path.exists(info.path) and info.file.size != os.path.getsize(info.path): if not is_skip_if_exists or not os.path.exists(info.path) or info.file.size != os.path.getsize(info.path):
try: try:
file_api.download(info.file, info.path) file_api.download(info.file, info.path)
except Exception: except Exception:

View File

@ -6,6 +6,7 @@ from typing import Any
from mdrsclient.api import FilesApi from mdrsclient.api import FilesApi
from mdrsclient.commands.base import BaseCommand from mdrsclient.commands.base import BaseCommand
from mdrsclient.exceptions import IllegalArgumentException from mdrsclient.exceptions import IllegalArgumentException
from mdrsclient.models.file import find_file
class FileMetadataCommand(BaseCommand): class FileMetadataCommand(BaseCommand):
@ -31,7 +32,8 @@ class FileMetadataCommand(BaseCommand):
connection = cls._create_connection(remote) connection = cls._create_connection(remote)
laboratory = cls._find_laboratory(connection, laboratory_name) laboratory = cls._find_laboratory(connection, laboratory_name)
folder = cls._find_folder(connection, laboratory, r_dirname, password) folder = cls._find_folder(connection, laboratory, r_dirname, password)
file = folder.find_file(r_basename) files = cls._find_files(connection, folder.id)
file = find_file(files, r_basename)
if file is None: if file is None:
raise IllegalArgumentException(f"File `{r_basename}` not found.") raise IllegalArgumentException(f"File `{r_basename}` not found.")
file_api = FilesApi(connection) file_api = FilesApi(connection)

View File

@ -4,7 +4,7 @@ from typing import Any
from pydantic.dataclasses import dataclass from pydantic.dataclasses import dataclass
from mdrsclient.api import FoldersApi from mdrsclient.api import FilesApi, FoldersApi
from mdrsclient.commands.base import BaseCommand from mdrsclient.commands.base import BaseCommand
from mdrsclient.connection import MDRSConnection from mdrsclient.connection import MDRSConnection
from mdrsclient.exceptions import UnauthorizedException from mdrsclient.exceptions import UnauthorizedException
@ -67,17 +67,18 @@ class LsCommand(BaseCommand):
is_recursive, is_recursive,
) )
folder = cls._find_folder(connection, laboratory, r_path, password) folder = cls._find_folder(connection, laboratory, r_path, password)
files = cls._find_files(connection, folder.id)
if context.is_json: if context.is_json:
cls._ls_json(context, folder) cls._ls_json(context, folder, files)
else: else:
cls._ls_plain(context, folder) cls._ls_plain(context, folder, files)
@classmethod @classmethod
def _ls_json(cls, context: LsCommandContext, folder: Folder) -> None: def _ls_json(cls, context: LsCommandContext, folder: Folder, files: list[File]) -> None:
print(json.dumps(cls._folder2dict(context, folder), ensure_ascii=False)) print(json.dumps(cls._folder2dict(context, folder, files), ensure_ascii=False))
@classmethod @classmethod
def _ls_plain(cls, context: LsCommandContext, folder: Folder) -> None: def _ls_plain(cls, context: LsCommandContext, folder: Folder, files: list[File]) -> None:
label = { label = {
"type": "Type", "type": "Type",
"acl": "Access", "acl": "Access",
@ -97,7 +98,7 @@ class LsCommand(BaseCommand):
length["size"] = max(length["size"], len(str(folder.size))) length["size"] = max(length["size"], len(str(folder.size)))
length["date"] = max(length["date"], len(sub_folder.updated_at_name)) length["date"] = max(length["date"], len(sub_folder.updated_at_name))
length["name"] = max(length["name"], len(sub_folder.name)) length["name"] = max(length["name"], len(sub_folder.name))
for file in folder.files: for file in files:
length["size"] = max(length["size"], len(str(file.size))) length["size"] = max(length["size"], len(str(file.size)))
length["date"] = max(length["date"], len(file.updated_at_name)) length["date"] = max(length["date"], len(file.updated_at_name))
length["name"] = max(length["name"], len(file.name)) length["name"] = max(length["name"], len(file.name))
@ -111,7 +112,7 @@ class LsCommand(BaseCommand):
if context.is_recursive: if context.is_recursive:
print(f"{context.prefix}{folder.path}:") print(f"{context.prefix}{folder.path}:")
print(f"total {sum(f.size for f in folder.files)}") print(f"total {sum(f.size for f in files)}")
if not context.is_quick: if not context.is_quick:
print(header) print(header)
@ -125,7 +126,7 @@ class LsCommand(BaseCommand):
f"{sub_laboratory_name:{length['laboratory']}}\t{sub_folder.size:{length['size']}}\t" f"{sub_laboratory_name:{length['laboratory']}}\t{sub_folder.size:{length['size']}}\t"
f"{sub_folder.updated_at_name:{length['date']}}\t{sub_folder.name:{length['name']}}" f"{sub_folder.updated_at_name:{length['date']}}\t{sub_folder.name:{length['name']}}"
) )
for file in sorted(folder.files, key=lambda x: x.name): for file in sorted(files, key=lambda x: x.name):
print( print(
f"{'[f]':{length['type']}}\t{folder.access_level_name:{length['acl']}}\t" f"{'[f]':{length['type']}}\t{folder.access_level_name:{length['acl']}}\t"
f"{context.laboratory.name:{length['laboratory']}}\t{file.size:{length['size']}}\t" f"{context.laboratory.name:{length['laboratory']}}\t{file.size:{length['size']}}\t"
@ -140,12 +141,15 @@ class LsCommand(BaseCommand):
if sub_folder.lock: if sub_folder.lock:
folder_api.auth(sub_folder.id, context.password) folder_api.auth(sub_folder.id, context.password)
folder = folder_api.retrieve(sub_folder.id) folder = folder_api.retrieve(sub_folder.id)
cls._ls_plain(context, folder) files = cls._find_files(context.connection, sub_folder.id)
cls._ls_plain(context, folder, files)
except UnauthorizedException: except UnauthorizedException:
pass pass
@classmethod @classmethod
def _folder2dict(cls, context: LsCommandContext, folder: Folder | FolderSimple) -> dict[str, Any]: def _folder2dict(
cls, context: LsCommandContext, folder: Folder | FolderSimple, files: list[File]
) -> dict[str, Any]:
data: dict[str, Any] = { data: dict[str, Any] = {
"id": folder.id, "id": folder.id,
"pid": folder.pid, "pid": folder.pid,
@ -168,15 +172,16 @@ class LsCommand(BaseCommand):
if sub_folder.lock: if sub_folder.lock:
folder_api.auth(sub_folder.id, context.password) folder_api.auth(sub_folder.id, context.password)
folder2 = folder_api.retrieve(sub_folder.id) folder2 = folder_api.retrieve(sub_folder.id)
sub_folders.append(cls._folder2dict(context, folder2)) files2 = cls._find_files(context.connection, sub_folder.id)
sub_folders.append(cls._folder2dict(context, folder2, files2))
except UnauthorizedException: except UnauthorizedException:
pass pass
data["sub_folders"] = sub_folders data["sub_folders"] = sub_folders
else: else:
data["sub_folders"] = list( data["sub_folders"] = list(
map(lambda x: cls._folder2dict(context, x), sorted(folder.sub_folders, key=lambda x: x.name)) map(lambda x: cls._folder2dict(context, x, []), sorted(folder.sub_folders, key=lambda x: x.name))
) )
data["files"] = list(map(lambda x: cls._file2dict(context, x), sorted(folder.files, key=lambda x: x.name))) data["files"] = list(map(lambda x: cls._file2dict(context, x), sorted(files, key=lambda x: x.name)))
return data return data
@classmethod @classmethod

View File

@ -6,6 +6,7 @@ from unicodedata import normalize
from mdrsclient.api import FoldersApi from mdrsclient.api import FoldersApi
from mdrsclient.commands.base import BaseCommand from mdrsclient.commands.base import BaseCommand
from mdrsclient.exceptions import IllegalArgumentException from mdrsclient.exceptions import IllegalArgumentException
from mdrsclient.models.file import find_file
class MkdirCommand(BaseCommand): class MkdirCommand(BaseCommand):
@ -29,7 +30,8 @@ class MkdirCommand(BaseCommand):
connection = cls._create_connection(remote) connection = cls._create_connection(remote)
laboratory = cls._find_laboratory(connection, laboratory_name) laboratory = cls._find_laboratory(connection, laboratory_name)
parent_folder = cls._find_folder(connection, laboratory, r_dirname) parent_folder = cls._find_folder(connection, laboratory, r_dirname)
if parent_folder.find_sub_folder(r_basename) is not None or parent_folder.find_file(r_basename) is not None: files = cls._find_files(connection, parent_folder.id)
if parent_folder.find_sub_folder(r_basename) is not None or find_file(files, r_basename) is not None:
raise IllegalArgumentException(f"Cannot create folder `{r_path}`: File exists.") raise IllegalArgumentException(f"Cannot create folder `{r_path}`: File exists.")
folder_api = FoldersApi(connection) folder_api = FoldersApi(connection)
folder_api.create(normalize("NFC", r_basename), parent_folder.id) folder_api.create(normalize("NFC", r_basename), parent_folder.id)

View File

@ -6,6 +6,7 @@ from unicodedata import normalize
from mdrsclient.api import FilesApi, FoldersApi from mdrsclient.api import FilesApi, FoldersApi
from mdrsclient.commands.base import BaseCommand from mdrsclient.commands.base import BaseCommand
from mdrsclient.exceptions import IllegalArgumentException from mdrsclient.exceptions import IllegalArgumentException
from mdrsclient.models.file import find_file
class MvCommand(BaseCommand): class MvCommand(BaseCommand):
@ -42,11 +43,13 @@ class MvCommand(BaseCommand):
connection = cls._create_connection(s_remote) connection = cls._create_connection(s_remote)
laboratory = cls._find_laboratory(connection, s_laboratory_name) laboratory = cls._find_laboratory(connection, s_laboratory_name)
s_parent_folder = cls._find_folder(connection, laboratory, s_dirname) s_parent_folder = cls._find_folder(connection, laboratory, s_dirname)
s_parent_files = cls._find_files(connection, s_parent_folder.id)
d_parent_folder = cls._find_folder(connection, laboratory, d_dirname) d_parent_folder = cls._find_folder(connection, laboratory, d_dirname)
s_file = s_parent_folder.find_file(s_basename) d_parent_files = cls._find_files(connection, d_parent_folder.id)
s_file = find_file(s_parent_files, s_basename)
if s_file is not None: if s_file is not None:
# source is file # source is file
d_file = d_parent_folder.find_file(d_basename) d_file = find_file(d_parent_files, d_basename)
if d_file is not None: if d_file is not None:
raise IllegalArgumentException(f"File `{d_basename}` already exists.") raise IllegalArgumentException(f"File `{d_basename}` already exists.")
d_sub_folder = d_parent_folder.find_sub_folder(d_basename) d_sub_folder = d_parent_folder.find_sub_folder(d_basename)
@ -60,7 +63,7 @@ class MvCommand(BaseCommand):
if s_folder is None: if s_folder is None:
raise IllegalArgumentException(f"File or folder `{s_basename}` not found.") raise IllegalArgumentException(f"File or folder `{s_basename}` not found.")
# source is folder # source is folder
if d_parent_folder.find_file(d_basename) is not None: if find_file(d_parent_files, d_basename) is not None:
raise IllegalArgumentException(f"Cannot overwrite non-folder `{d_basename}` with folder `{s_path}`.") raise IllegalArgumentException(f"Cannot overwrite non-folder `{d_basename}` with folder `{s_path}`.")
d_folder = d_parent_folder.find_sub_folder(d_basename) d_folder = d_parent_folder.find_sub_folder(d_basename)
if d_folder is not None: if d_folder is not None:

View File

@ -5,6 +5,7 @@ from typing import Any
from mdrsclient.api import FilesApi, FoldersApi from mdrsclient.api import FilesApi, FoldersApi
from mdrsclient.commands.base import BaseCommand from mdrsclient.commands.base import BaseCommand
from mdrsclient.exceptions import IllegalArgumentException from mdrsclient.exceptions import IllegalArgumentException
from mdrsclient.models.file import find_file
class RmCommand(BaseCommand): class RmCommand(BaseCommand):
@ -32,7 +33,8 @@ class RmCommand(BaseCommand):
connection = cls._create_connection(remote) connection = cls._create_connection(remote)
laboratory = cls._find_laboratory(connection, laboratory_name) laboratory = cls._find_laboratory(connection, laboratory_name)
parent_folder = cls._find_folder(connection, laboratory, r_dirname) parent_folder = cls._find_folder(connection, laboratory, r_dirname)
file = parent_folder.find_file(r_basename) parent_files = cls._find_files(connection, parent_folder.id)
file = find_file(parent_files, r_basename)
if file is not None: if file is not None:
file_api = FilesApi(connection) file_api = FilesApi(connection)
file_api.destroy(file) file_api.destroy(file)

View File

@ -9,13 +9,15 @@ from mdrsclient.api import FilesApi, FoldersApi
from mdrsclient.commands.base import BaseCommand from mdrsclient.commands.base import BaseCommand
from mdrsclient.connection import MDRSConnection from mdrsclient.connection import MDRSConnection
from mdrsclient.exceptions import IllegalArgumentException, MDRSException from mdrsclient.exceptions import IllegalArgumentException, MDRSException
from mdrsclient.models import Folder from mdrsclient.models import File, Folder
from mdrsclient.models.file import find_file
from mdrsclient.settings import CONCURRENT from mdrsclient.settings import CONCURRENT
@dataclass(frozen=True) @dataclass(frozen=True)
class UploadFileInfo: class UploadFileInfo:
folder: Folder folder: Folder
files: list[File]
path: str path: str
@ -53,6 +55,7 @@ class UploadCommand(BaseCommand):
connection = cls._create_connection(remote) connection = cls._create_connection(remote)
laboratory = cls._find_laboratory(connection, laboratory_name) laboratory = cls._find_laboratory(connection, laboratory_name)
folder = cls._find_folder(connection, laboratory, r_path) folder = cls._find_folder(connection, laboratory, r_path)
files = cls._find_files(connection, folder.id)
infos: list[UploadFileInfo] = [] infos: list[UploadFileInfo] = []
if os.path.isdir(l_path): if os.path.isdir(l_path):
if not is_recursive: if not is_recursive:
@ -60,6 +63,8 @@ class UploadCommand(BaseCommand):
folder_api = FoldersApi(connection) folder_api = FoldersApi(connection)
folder_map: dict[str, Folder] = {} folder_map: dict[str, Folder] = {}
folder_map[r_path] = folder folder_map[r_path] = folder
files_map: dict[str, list[File]] = {}
files_map[r_path] = files
l_basename = os.path.basename(l_path) l_basename = os.path.basename(l_path)
for dirpath, _, filenames in os.walk(l_path, followlinks=True): for dirpath, _, filenames in os.walk(l_path, followlinks=True):
sub = l_basename if dirpath == l_path else os.path.join(l_basename, os.path.relpath(dirpath, l_path)) sub = l_basename if dirpath == l_path else os.path.join(l_basename, os.path.relpath(dirpath, l_path))
@ -68,7 +73,10 @@ class UploadCommand(BaseCommand):
# prepare destination parent path # prepare destination parent path
d_parent_dirname = os.path.dirname(d_dirname) d_parent_dirname = os.path.dirname(d_dirname)
if folder_map.get(d_parent_dirname) is None: if folder_map.get(d_parent_dirname) is None:
folder_map[d_parent_dirname] = cls._find_folder(connection, laboratory, d_parent_dirname) parent_folder = cls._find_folder(connection, laboratory, d_parent_dirname)
folder_map[d_parent_dirname] = parent_folder
parent_files = cls._find_files(connection, parent_folder.id)
files_map[d_parent_dirname] = parent_files
# prepare destination path # prepare destination path
if folder_map.get(d_dirname) is None: if folder_map.get(d_dirname) is None:
d_folder = folder_map[d_parent_dirname].find_sub_folder(d_basename) d_folder = folder_map[d_parent_dirname].find_sub_folder(d_basename)
@ -78,13 +86,16 @@ class UploadCommand(BaseCommand):
d_folder_id = d_folder.id d_folder_id = d_folder.id
print(d_dirname) print(d_dirname)
folder_map[d_dirname] = folder_api.retrieve(d_folder_id) folder_map[d_dirname] = folder_api.retrieve(d_folder_id)
files_map[d_dirname] = cls._find_files(connection, d_folder_id)
if d_folder is None: if d_folder is None:
folder_map[d_parent_dirname].sub_folders.append(folder_map[d_dirname]) folder_map[d_parent_dirname].sub_folders.append(folder_map[d_dirname])
# register upload file list # register upload file list
for filename in filenames: for filename in filenames:
infos.append(UploadFileInfo(folder_map[d_dirname], os.path.join(dirpath, filename))) infos.append(
UploadFileInfo(folder_map[d_dirname], files_map[d_dirname], os.path.join(dirpath, filename))
)
else: else:
infos.append(UploadFileInfo(folder, l_path)) infos.append(UploadFileInfo(folder, files, l_path))
cls.__multiple_upload(connection, infos, is_skip_if_exists) cls.__multiple_upload(connection, infos, is_skip_if_exists)
@classmethod @classmethod
@ -98,7 +109,7 @@ class UploadCommand(BaseCommand):
@classmethod @classmethod
def __multiple_upload_worker(cls, file_api: FilesApi, info: UploadFileInfo, is_skip_if_exists: bool) -> None: def __multiple_upload_worker(cls, file_api: FilesApi, info: UploadFileInfo, is_skip_if_exists: bool) -> None:
basename = os.path.basename(info.path) basename = os.path.basename(info.path)
file = info.folder.find_file(basename) file = find_file(info.files, basename)
try: try:
if file is None: if file is None:
file_api.create(info.folder.id, info.path) file_api.create(info.folder.id, info.path)

View File

@ -1,4 +1,5 @@
from typing import Any from typing import Any
from unicodedata import normalize
from pydantic.dataclasses import dataclass from pydantic.dataclasses import dataclass
@ -25,3 +26,8 @@ class File:
@property @property
def updated_at_name(self) -> str: def updated_at_name(self) -> str:
return iso8601_to_user_friendly(self.updated_at) return iso8601_to_user_friendly(self.updated_at)
def find_file(files: list[File], name: str) -> File | None:
_name = normalize("NFC", name).lower()
return next((x for x in files if x.name.lower() == _name), None)

View File

@ -3,7 +3,6 @@ from unicodedata import normalize
from pydantic.dataclasses import dataclass from pydantic.dataclasses import dataclass
from mdrsclient.models.file import File
from mdrsclient.models.utils import iso8601_to_user_friendly from mdrsclient.models.utils import iso8601_to_user_friendly
@ -78,13 +77,8 @@ class FolderSimple:
class Folder(FolderSimple): class Folder(FolderSimple):
metadata: list[dict[str, Any]] metadata: list[dict[str, Any]]
sub_folders: list[FolderSimple] sub_folders: list[FolderSimple]
files: list[File]
path: str path: str
def find_sub_folder(self, name: str) -> FolderSimple | None: def find_sub_folder(self, name: str) -> FolderSimple | None:
_name = normalize("NFC", name).lower() _name = normalize("NFC", name).lower()
return next((x for x in self.sub_folders if x.name.lower() == _name), None) return next((x for x in self.sub_folders if x.name.lower() == _name), None)
def find_file(self, name: str) -> File | None:
_name = normalize("NFC", name).lower()
return next((x for x in self.files if x.name.lower() == _name), None)

View File

@ -1,5 +1,6 @@
import os import os
from typing import IO, Any from typing import IO, Any
from urllib.parse import parse_qs, urlparse
if os.name == "nt": if os.name == "nt":
import msvcrt import msvcrt
@ -21,3 +22,10 @@ class FileLock:
msvcrt.locking(file.fileno(), msvcrt.LK_UNLCK, 1) msvcrt.locking(file.fileno(), msvcrt.LK_UNLCK, 1)
elif os.name == "posix": elif os.name == "posix":
fcntl.flock(file.fileno(), fcntl.LOCK_UN) fcntl.flock(file.fileno(), fcntl.LOCK_UN)
def page_num_from_url(url: str) -> int | None:
parsed_url = urlparse(url)
params = parse_qs(parsed_url.query)
page = params.get("page", [None])[0]
return int(page) if page is not None else None

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "mdrs-client-python" name = "mdrs-client-python"
version = "1.3.11" version = "1.3.12"
description = "The mdrs-client-python is python library and a command-line client for up- and downloading files to and from MDRS based repository." description = "The mdrs-client-python is python library and a command-line client for up- and downloading files to and from MDRS based repository."
authors = ["Yoshihiro OKUMURA <yoshihiro.okumura@riken.jp>"] authors = ["Yoshihiro OKUMURA <yoshihiro.okumura@riken.jp>"]
license = "MIT" license = "MIT"
@ -25,18 +25,18 @@ packages = [
python = "^3.10" python = "^3.10"
requests = "^2.32.3" requests = "^2.32.3"
requests-toolbelt = "^1.0.0" requests-toolbelt = "^1.0.0"
python-dotenv = "^1.0.1" python-dotenv = "^1.1.0"
pydantic = "^2.10.5" pydantic = "^2.11.4"
pydantic-settings = "^2.7.1" pydantic-settings = "^2.9.1"
PyJWT = "^2.10.1" PyJWT = "^2.10.1"
validators = "^0.34.0" validators = "^0.34.0"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
black = "^24.10.0" black = "^24.10.0"
flake8 = "^7.1.1" flake8 = "^7.2.0"
Flake8-pyproject = "^1.2.3" Flake8-pyproject = "^1.2.3"
isort = "^5.13.2" isort = "^5.13.2"
pyright = "^1.1.391" pyright = "^1.1.400"
[tool.poetry.scripts] [tool.poetry.scripts]
mdrs = 'mdrsclient.__main__:main' mdrs = 'mdrsclient.__main__:main'