Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
219858e0b6
|
|||
a281a97b3e
|
|||
64f64b82dc
|
|||
9284346153
|
|||
292ca1df27
|
|||
f10b42a1f2
|
|||
ac34a26b02
|
|||
79f1b708cb
|
|||
905577fe74
|
15
.cspell.json
Normal file
15
.cspell.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2",
|
||||||
|
"language": "en,en-gb",
|
||||||
|
"ignoreWords": ["getframe", "pycache", "pydantic", "UNLCK"],
|
||||||
|
"words": [
|
||||||
|
"chacl",
|
||||||
|
"kikan",
|
||||||
|
"mdrs",
|
||||||
|
"mdrsclient",
|
||||||
|
"neurodata",
|
||||||
|
"Neuroinformatics",
|
||||||
|
"RIKEN"
|
||||||
|
],
|
||||||
|
"ignorePaths": [".env", "__pycache__"]
|
||||||
|
}
|
12
.editorconfig
Normal file
12
.editorconfig
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# editorconfig.org
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
19
.vscode/settings.json
vendored
19
.vscode/settings.json
vendored
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.organizeImports": true
|
"source.organizeImports": "explicit"
|
||||||
},
|
},
|
||||||
"[python]": {
|
"[python]": {
|
||||||
"editor.defaultFormatter": "ms-python.black-formatter"
|
"editor.defaultFormatter": "ms-python.black-formatter"
|
||||||
@ -15,9 +15,12 @@
|
|||||||
"[jsonc]": {
|
"[jsonc]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
},
|
},
|
||||||
|
// Extensions - Black Formatter
|
||||||
|
"black-formatter.args": ["--line-length=120"],
|
||||||
// Extensions - Code Spell Checker
|
// Extensions - Code Spell Checker
|
||||||
"cSpell.ignoreWords": ["getframe", "pydantic", "UNLCK"],
|
// - see: .cspell.json
|
||||||
"cSpell.words": ["chacl", "mdrs", "mdrsclient", "neurodata", "Neuroinformatics", "RIKEN"],
|
// Extensions - Flake8
|
||||||
|
"flake8.args": ["--max-line-length=120"],
|
||||||
// Extensions - isort
|
// Extensions - isort
|
||||||
"isort.args": ["--profile=black"],
|
"isort.args": ["--profile=black"],
|
||||||
// Extensions - Prettier
|
// Extensions - Prettier
|
||||||
@ -28,16 +31,6 @@
|
|||||||
"prettier.trailingComma": "all",
|
"prettier.trailingComma": "all",
|
||||||
// Extensions - Pylance
|
// Extensions - Pylance
|
||||||
"python.analysis.typeCheckingMode": "strict",
|
"python.analysis.typeCheckingMode": "strict",
|
||||||
"python.analysis.exclude": ["api/migrations/[0-9]*.py"],
|
|
||||||
// Extensions - Python:black
|
|
||||||
"python.formatting.blackArgs": ["--line-length=120"],
|
|
||||||
"python.formatting.provider": "black",
|
|
||||||
// Extensions - Python:Flake8
|
|
||||||
"python.linting.enabled": true,
|
|
||||||
"python.linting.flake8Enabled": true,
|
|
||||||
"python.linting.flake8Args": ["--max-line-length=120"],
|
|
||||||
"python.linting.ignorePatterns": ["**/site-packages/**/*.py", ".vscode/*.py"],
|
|
||||||
"python.linting.lintOnSave": true,
|
|
||||||
// Extensions - Python Docstring Generator configuration
|
// Extensions - Python Docstring Generator configuration
|
||||||
"autoDocstring.docstringFormat": "google"
|
"autoDocstring.docstringFormat": "google"
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ The mdrs-client-python is python library and a command-line client for up- and d
|
|||||||
## Installing
|
## Installing
|
||||||
|
|
||||||
```
|
```
|
||||||
pip install -e .
|
poetry install
|
||||||
```
|
```
|
||||||
|
|
||||||
## Example Usage
|
## Example Usage
|
||||||
|
@ -1 +1 @@
|
|||||||
1.1.1
|
1.3.0
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
from mdrsclient.api.file import FileApi
|
from mdrsclient.api.files import FilesApi
|
||||||
from mdrsclient.api.folder import FolderApi
|
from mdrsclient.api.folders import FoldersApi
|
||||||
from mdrsclient.api.laboratory import LaboratoryApi
|
from mdrsclient.api.laboratories import LaboratoriesApi
|
||||||
from mdrsclient.api.user import UserApi
|
from mdrsclient.api.users import UsersApi
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"FileApi",
|
"FilesApi",
|
||||||
"FolderApi",
|
"FoldersApi",
|
||||||
"LaboratoryApi",
|
"LaboratoriesApi",
|
||||||
"UserApi",
|
"UsersApi",
|
||||||
]
|
]
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import os
|
||||||
from typing import Any, Final
|
from typing import Any, Final
|
||||||
|
|
||||||
from pydantic import TypeAdapter
|
from pydantic import TypeAdapter
|
||||||
@ -10,12 +11,12 @@ from mdrsclient.models import File
|
|||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class FileCreateResponse:
|
class FilesApiCreateResponse:
|
||||||
id: str
|
id: str
|
||||||
|
|
||||||
|
|
||||||
class FileApi(BaseApi):
|
class FilesApi(BaseApi):
|
||||||
ENTRYPOINT: Final[str] = "v2/file/"
|
ENTRYPOINT: Final[str] = "v3/files/"
|
||||||
|
|
||||||
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)
|
||||||
@ -31,10 +32,10 @@ class FileApi(BaseApi):
|
|||||||
token_check(self.connection)
|
token_check(self.connection)
|
||||||
data: dict[str, str | int] = {"folder_id": folder_id}
|
data: dict[str, str | int] = {"folder_id": folder_id}
|
||||||
try:
|
try:
|
||||||
with open(path, mode="rb") as fp:
|
with open(os.path.relpath(path), mode="rb") as fp:
|
||||||
response = self.connection.post(url, data=data, files={"file": fp})
|
response = self.connection.post(url, data=data, files={"file": fp})
|
||||||
self._raise_response_error(response)
|
self._raise_response_error(response)
|
||||||
ret = TypeAdapter(FileCreateResponse).validate_python(response.json())
|
ret = TypeAdapter(FilesApiCreateResponse).validate_python(response.json())
|
||||||
except OSError:
|
except OSError:
|
||||||
raise UnexpectedException(f"Could not open `{path}` file.")
|
raise UnexpectedException(f"Could not open `{path}` file.")
|
||||||
return ret.id
|
return ret.id
|
||||||
@ -46,7 +47,7 @@ class FileApi(BaseApi):
|
|||||||
if path is not None:
|
if path is not None:
|
||||||
# update file body
|
# update file body
|
||||||
try:
|
try:
|
||||||
with open(path, mode="rb") as fp:
|
with open(os.path.relpath(path), mode="rb") as fp:
|
||||||
response = self.connection.put(url, files={"file": fp})
|
response = self.connection.put(url, files={"file": fp})
|
||||||
except OSError:
|
except OSError:
|
||||||
raise UnexpectedException(f"Could not open `{path}` file.")
|
raise UnexpectedException(f"Could not open `{path}` file.")
|
||||||
@ -93,7 +94,7 @@ class FileApi(BaseApi):
|
|||||||
|
|
||||||
def download(self, file: File, path: str) -> bool:
|
def download(self, file: File, path: str) -> bool:
|
||||||
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
|
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
|
||||||
url = "v2/" + file.download_url
|
url = file.download_url
|
||||||
response = self.connection.get(url, stream=True)
|
response = self.connection.get(url, stream=True)
|
||||||
self._raise_response_error(response)
|
self._raise_response_error(response)
|
||||||
try:
|
try:
|
@ -11,12 +11,12 @@ from mdrsclient.models import Folder, FolderSimple
|
|||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class FolderCreateResponse:
|
class FoldersApiCreateResponse:
|
||||||
id: str
|
id: str
|
||||||
|
|
||||||
|
|
||||||
class FolderApi(BaseApi):
|
class FoldersApi(BaseApi):
|
||||||
ENTRYPOINT: Final[str] = "v2/folder/"
|
ENTRYPOINT: Final[str] = "v3/folders/"
|
||||||
|
|
||||||
def list(self, laboratory_id: int, path: str) -> list[FolderSimple]:
|
def list(self, laboratory_id: int, path: str) -> list[FolderSimple]:
|
||||||
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
|
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
|
||||||
@ -46,7 +46,7 @@ class FolderApi(BaseApi):
|
|||||||
token_check(self.connection)
|
token_check(self.connection)
|
||||||
response = self.connection.post(url, data=data)
|
response = self.connection.post(url, data=data)
|
||||||
self._raise_response_error(response)
|
self._raise_response_error(response)
|
||||||
ret = TypeAdapter(FolderCreateResponse).validate_python(response.json())
|
ret = TypeAdapter(FoldersApiCreateResponse).validate_python(response.json())
|
||||||
return ret.id
|
return ret.id
|
||||||
|
|
||||||
def update(self, folder: FolderSimple) -> bool:
|
def update(self, folder: FolderSimple) -> bool:
|
@ -7,8 +7,8 @@ from mdrsclient.api.utils import token_check
|
|||||||
from mdrsclient.models import Laboratories, Laboratory
|
from mdrsclient.models import Laboratories, Laboratory
|
||||||
|
|
||||||
|
|
||||||
class LaboratoryApi(BaseApi):
|
class LaboratoriesApi(BaseApi):
|
||||||
ENTRYPOINT: Final[str] = "v2/laboratory/"
|
ENTRYPOINT: Final[str] = "v3/laboratories/"
|
||||||
|
|
||||||
def list(self) -> Laboratories:
|
def list(self) -> Laboratories:
|
||||||
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
|
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
|
@ -1,46 +0,0 @@
|
|||||||
from typing import Final
|
|
||||||
|
|
||||||
import requests
|
|
||||||
from pydantic import TypeAdapter
|
|
||||||
from pydantic.dataclasses import dataclass
|
|
||||||
|
|
||||||
from mdrsclient.api.base import BaseApi
|
|
||||||
from mdrsclient.exceptions import UnauthorizedException
|
|
||||||
from mdrsclient.models import Token, User
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class UserAuthResponse(Token):
|
|
||||||
is_reviewer: bool | None = None
|
|
||||||
laboratory: str | None = None
|
|
||||||
lab_id: int | None = None
|
|
||||||
|
|
||||||
|
|
||||||
class UserApi(BaseApi):
|
|
||||||
ENTRYPOINT: Final[str] = "v2/"
|
|
||||||
|
|
||||||
def auth(self, username: str, password: str) -> tuple[User, Token]:
|
|
||||||
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
|
|
||||||
url = self.ENTRYPOINT + "auth/"
|
|
||||||
data: dict[str, str | int] = {"username": username, "password": password}
|
|
||||||
response = self.connection.post(url, data=data)
|
|
||||||
if response.status_code == requests.codes.unauthorized:
|
|
||||||
raise UnauthorizedException("Invalid username or password.")
|
|
||||||
self._raise_response_error(response)
|
|
||||||
obj = TypeAdapter(UserAuthResponse).validate_python(response.json())
|
|
||||||
token = Token(access=obj.access, refresh=obj.refresh)
|
|
||||||
laboratory_ids = [obj.lab_id] if obj.lab_id is not None else []
|
|
||||||
is_reviewer = obj.is_reviewer if obj.is_reviewer is not None else False
|
|
||||||
user = User(id=token.user_id, username=username, laboratory_ids=laboratory_ids, is_reviewer=is_reviewer)
|
|
||||||
return (user, token)
|
|
||||||
|
|
||||||
def refresh(self, token: Token) -> Token:
|
|
||||||
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
|
|
||||||
url = self.ENTRYPOINT + "refresh/"
|
|
||||||
data: dict[str, str | int] = {"refresh": token.refresh}
|
|
||||||
response = self.connection.post(url, data=data)
|
|
||||||
if response.status_code == requests.codes.unauthorized:
|
|
||||||
raise UnauthorizedException("Token is invalid or expired.")
|
|
||||||
self._raise_response_error(response)
|
|
||||||
token = TypeAdapter(Token).validate_python(response.json())
|
|
||||||
return token
|
|
67
mdrsclient/api/users.py
Normal file
67
mdrsclient/api/users.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
from typing import Final
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from pydantic import TypeAdapter
|
||||||
|
from pydantic.dataclasses import dataclass
|
||||||
|
|
||||||
|
from mdrsclient.api.base import BaseApi
|
||||||
|
from mdrsclient.exceptions import UnauthorizedException
|
||||||
|
from mdrsclient.models import Token, User
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class UsersCurrentResponseLaboratory:
|
||||||
|
id: int
|
||||||
|
name: str
|
||||||
|
role: int
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class UsersApiCurrentResponse:
|
||||||
|
id: int
|
||||||
|
username: str
|
||||||
|
full_name: str
|
||||||
|
email: str
|
||||||
|
laboratories: list[UsersCurrentResponseLaboratory]
|
||||||
|
is_staff: bool
|
||||||
|
is_active: bool
|
||||||
|
is_superuser: bool
|
||||||
|
is_reviewer: bool
|
||||||
|
last_login: str # ISO8601
|
||||||
|
date_joined: str # ISO8601
|
||||||
|
|
||||||
|
|
||||||
|
class UsersApi(BaseApi):
|
||||||
|
ENTRYPOINT: Final[str] = "v3/users/"
|
||||||
|
|
||||||
|
def current(self) -> User:
|
||||||
|
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
|
||||||
|
url = self.ENTRYPOINT + "current/"
|
||||||
|
response = self.connection.get(url)
|
||||||
|
self._raise_response_error(response)
|
||||||
|
obj = TypeAdapter(UsersApiCurrentResponse).validate_python(response.json())
|
||||||
|
laboratory_ids = list(map(lambda x: x.id, obj.laboratories))
|
||||||
|
user = User(id=obj.id, username=obj.username, laboratory_ids=laboratory_ids, is_reviewer=obj.is_reviewer)
|
||||||
|
return user
|
||||||
|
|
||||||
|
def token(self, username: str, password: str) -> Token:
|
||||||
|
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
|
||||||
|
url = self.ENTRYPOINT + "token/"
|
||||||
|
data: dict[str, str | int] = {"username": username, "password": password}
|
||||||
|
response = self.connection.post(url, data=data)
|
||||||
|
if response.status_code == requests.codes.unauthorized:
|
||||||
|
raise UnauthorizedException("Invalid username or password.")
|
||||||
|
self._raise_response_error(response)
|
||||||
|
token = TypeAdapter(Token).validate_python(response.json())
|
||||||
|
return token
|
||||||
|
|
||||||
|
def tokenRefresh(self, token: Token) -> Token:
|
||||||
|
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
|
||||||
|
url = self.ENTRYPOINT + "token/refresh/"
|
||||||
|
data: dict[str, str | int] = {"refresh": token.refresh}
|
||||||
|
response = self.connection.post(url, data=data)
|
||||||
|
if response.status_code == requests.codes.unauthorized:
|
||||||
|
raise UnauthorizedException("Token is invalid or expired.")
|
||||||
|
self._raise_response_error(response)
|
||||||
|
token = TypeAdapter(Token).validate_python(response.json())
|
||||||
|
return token
|
@ -1,4 +1,4 @@
|
|||||||
from mdrsclient.api.user import UserApi
|
from mdrsclient.api.users import UsersApi
|
||||||
from mdrsclient.connection import MDRSConnection
|
from mdrsclient.connection import MDRSConnection
|
||||||
from mdrsclient.exceptions import UnauthorizedException
|
from mdrsclient.exceptions import UnauthorizedException
|
||||||
|
|
||||||
@ -8,9 +8,9 @@ def token_check(connection: MDRSConnection) -> None:
|
|||||||
connection.lock.acquire()
|
connection.lock.acquire()
|
||||||
if connection.token is not None:
|
if connection.token is not None:
|
||||||
if connection.token.is_refresh_required:
|
if connection.token.is_refresh_required:
|
||||||
user_api = UserApi(connection)
|
user_api = UsersApi(connection)
|
||||||
try:
|
try:
|
||||||
connection.token = user_api.refresh(connection.token)
|
connection.token = user_api.tokenRefresh(connection.token)
|
||||||
except UnauthorizedException:
|
except UnauthorizedException:
|
||||||
connection.logout()
|
connection.logout()
|
||||||
elif connection.token.is_expired:
|
elif connection.token.is_expired:
|
||||||
|
@ -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 FolderApi, LaboratoryApi
|
from mdrsclient.api import 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 (
|
||||||
@ -31,7 +31,7 @@ class BaseCommand(ABC):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def _find_laboratory(cls, connection: MDRSConnection, name: str) -> Laboratory:
|
def _find_laboratory(cls, connection: MDRSConnection, name: str) -> Laboratory:
|
||||||
if connection.laboratories.empty() or connection.token is not None and connection.token.is_expired:
|
if connection.laboratories.empty() or connection.token is not None and connection.token.is_expired:
|
||||||
laboratory_api = LaboratoryApi(connection)
|
laboratory_api = LaboratoriesApi(connection)
|
||||||
connection.laboratories = laboratory_api.list()
|
connection.laboratories = laboratory_api.list()
|
||||||
laboratory = connection.laboratories.find_by_name(name)
|
laboratory = connection.laboratories.find_by_name(name)
|
||||||
if laboratory is None:
|
if laboratory is None:
|
||||||
@ -42,7 +42,7 @@ class BaseCommand(ABC):
|
|||||||
def _find_folder(
|
def _find_folder(
|
||||||
cls, connection: MDRSConnection, laboratory: Laboratory, path: str, password: str | None = None
|
cls, connection: MDRSConnection, laboratory: Laboratory, path: str, password: str | None = None
|
||||||
) -> Folder:
|
) -> Folder:
|
||||||
folder_api = FolderApi(connection)
|
folder_api = FoldersApi(connection)
|
||||||
folders = folder_api.list(laboratory.id, normalize("NFC", path))
|
folders = folder_api.list(laboratory.id, normalize("NFC", path))
|
||||||
if len(folders) != 1:
|
if len(folders) != 1:
|
||||||
raise UnexpectedException(f"Folder `{path}` not found.")
|
raise UnexpectedException(f"Folder `{path}` not found.")
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from argparse import Namespace
|
from argparse import Namespace
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from mdrsclient.api import FolderApi
|
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 import FolderAccessLevel
|
from mdrsclient.models import FolderAccessLevel
|
||||||
@ -36,5 +36,5 @@ class ChaclCommand(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)
|
||||||
folder_api = FolderApi(connection)
|
folder_api = FoldersApi(connection)
|
||||||
folder_api.acl(folder.id, access_level, is_recursive, password)
|
folder_api.acl(folder.id, access_level, is_recursive, password)
|
||||||
|
@ -3,7 +3,7 @@ from argparse import Namespace
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
from unicodedata import normalize
|
from unicodedata import normalize
|
||||||
|
|
||||||
from mdrsclient.api import FileApi, FolderApi
|
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
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ class CpCommand(BaseCommand):
|
|||||||
d_sub_folder = d_parent_folder.find_sub_folder(d_basename)
|
d_sub_folder = d_parent_folder.find_sub_folder(d_basename)
|
||||||
if d_sub_folder is not None:
|
if d_sub_folder is not None:
|
||||||
raise IllegalArgumentException(f"Cannot overwrite non-folder `{d_basename}` with folder `{d_path}`.")
|
raise IllegalArgumentException(f"Cannot overwrite non-folder `{d_basename}` with folder `{d_path}`.")
|
||||||
file_api = FileApi(connection)
|
file_api = FilesApi(connection)
|
||||||
if s_parent_folder.id != d_parent_folder.id or d_basename != s_basename:
|
if s_parent_folder.id != d_parent_folder.id or d_basename != s_basename:
|
||||||
file_api.copy(s_file, d_parent_folder.id, normalize("NFC", d_basename))
|
file_api.copy(s_file, d_parent_folder.id, normalize("NFC", d_basename))
|
||||||
else:
|
else:
|
||||||
@ -73,6 +73,6 @@ class CpCommand(BaseCommand):
|
|||||||
if d_folder.id == s_folder.id:
|
if d_folder.id == s_folder.id:
|
||||||
raise IllegalArgumentException(f"`{s_path}` and `{s_path}` are the same folder.")
|
raise IllegalArgumentException(f"`{s_path}` and `{s_path}` are the same folder.")
|
||||||
raise IllegalArgumentException(f"Cannot move `{s_path}` to `{d_path}`: Folder not empty.")
|
raise IllegalArgumentException(f"Cannot move `{s_path}` to `{d_path}`: Folder not empty.")
|
||||||
folder_api = FolderApi(connection)
|
folder_api = FoldersApi(connection)
|
||||||
if s_parent_folder.id != d_parent_folder.id or s_basename != d_basename:
|
if s_parent_folder.id != d_parent_folder.id or s_basename != d_basename:
|
||||||
folder_api.copy(s_folder, d_parent_folder.id, normalize("NFC", d_basename))
|
folder_api.copy(s_folder, d_parent_folder.id, normalize("NFC", d_basename))
|
||||||
|
@ -5,7 +5,7 @@ from typing import Any
|
|||||||
|
|
||||||
from pydantic.dataclasses import dataclass
|
from pydantic.dataclasses import dataclass
|
||||||
|
|
||||||
from mdrsclient.api import FileApi, FolderApi
|
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
|
from mdrsclient.exceptions import IllegalArgumentException
|
||||||
@ -62,13 +62,13 @@ class DownloadCommand(BaseCommand):
|
|||||||
raise IllegalArgumentException(f"File or folder `{r_path}` not found.")
|
raise IllegalArgumentException(f"File or folder `{r_path}` not found.")
|
||||||
if not is_recursive:
|
if not is_recursive:
|
||||||
raise IllegalArgumentException(f"Cannot download `{r_path}`: Is a folder.")
|
raise IllegalArgumentException(f"Cannot download `{r_path}`: Is a folder.")
|
||||||
folder_api = FolderApi(connection)
|
folder_api = FoldersApi(connection)
|
||||||
cls.__multiple_download_pickup_recursive_files(folder_api, download_files, folder.id, l_dirname)
|
cls.__multiple_download_pickup_recursive_files(folder_api, download_files, folder.id, l_dirname)
|
||||||
cls.__multiple_download(connection, download_files)
|
cls.__multiple_download(connection, download_files)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def __multiple_download_pickup_recursive_files(
|
def __multiple_download_pickup_recursive_files(
|
||||||
cls, folder_api: FolderApi, infolist: list[DownloadFileInfo], folder_id: str, basedir: str
|
cls, folder_api: FoldersApi, infolist: list[DownloadFileInfo], folder_id: str, basedir: str
|
||||||
) -> None:
|
) -> None:
|
||||||
folder = folder_api.retrieve(folder_id)
|
folder = folder_api.retrieve(folder_id)
|
||||||
dirname = os.path.join(basedir, folder.name)
|
dirname = os.path.join(basedir, folder.name)
|
||||||
@ -83,11 +83,11 @@ class DownloadCommand(BaseCommand):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def __multiple_download(cls, connection: MDRSConnection, infolist: list[DownloadFileInfo]) -> None:
|
def __multiple_download(cls, connection: MDRSConnection, infolist: list[DownloadFileInfo]) -> None:
|
||||||
file_api = FileApi(connection)
|
file_api = FilesApi(connection)
|
||||||
with ThreadPoolExecutor(max_workers=CONCURRENT) as pool:
|
with ThreadPoolExecutor(max_workers=CONCURRENT) as pool:
|
||||||
pool.map(lambda x: cls.__multiple_download_worker(file_api, x), infolist)
|
pool.map(lambda x: cls.__multiple_download_worker(file_api, x), infolist)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def __multiple_download_worker(cls, file_api: FileApi, info: DownloadFileInfo) -> None:
|
def __multiple_download_worker(cls, file_api: FilesApi, info: DownloadFileInfo) -> None:
|
||||||
file_api.download(info.file, info.path)
|
file_api.download(info.file, info.path)
|
||||||
print(info.path)
|
print(info.path)
|
||||||
|
@ -3,7 +3,7 @@ import os
|
|||||||
from argparse import Namespace
|
from argparse import Namespace
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from mdrsclient.api import FileApi
|
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
|
||||||
|
|
||||||
@ -34,6 +34,6 @@ class FileMetadataCommand(BaseCommand):
|
|||||||
file = folder.find_file(r_basename)
|
file = folder.find_file(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 = FileApi(connection)
|
file_api = FilesApi(connection)
|
||||||
metadata = file_api.metadata(file)
|
metadata = file_api.metadata(file)
|
||||||
print(json.dumps(metadata, ensure_ascii=False))
|
print(json.dumps(metadata, ensure_ascii=False))
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from argparse import Namespace
|
from argparse import Namespace
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from mdrsclient.api import LaboratoryApi
|
from mdrsclient.api import LaboratoriesApi
|
||||||
from mdrsclient.commands.base import BaseCommand
|
from mdrsclient.commands.base import BaseCommand
|
||||||
|
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ class LabsCommand(BaseCommand):
|
|||||||
def labs(cls, remote: str) -> None:
|
def labs(cls, remote: str) -> None:
|
||||||
remote = cls._parse_remote_host(remote)
|
remote = cls._parse_remote_host(remote)
|
||||||
connection = cls._create_connection(remote)
|
connection = cls._create_connection(remote)
|
||||||
laboratory_api = LaboratoryApi(connection)
|
laboratory_api = LaboratoriesApi(connection)
|
||||||
laboratories = laboratory_api.list()
|
laboratories = laboratory_api.list()
|
||||||
connection.laboratories = laboratories
|
connection.laboratories = laboratories
|
||||||
label = {"id": "ID", "name": "Name", "pi_name": "PI", "full_name": "Laboratory"}
|
label = {"id": "ID", "name": "Name", "pi_name": "PI", "full_name": "Laboratory"}
|
||||||
|
@ -2,7 +2,7 @@ import getpass
|
|||||||
from argparse import Namespace
|
from argparse import Namespace
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from mdrsclient.api import UserApi
|
from mdrsclient.api import UsersApi
|
||||||
from mdrsclient.commands.base import BaseCommand
|
from mdrsclient.commands.base import BaseCommand
|
||||||
from mdrsclient.config import ConfigFile
|
from mdrsclient.config import ConfigFile
|
||||||
from mdrsclient.connection import MDRSConnection
|
from mdrsclient.connection import MDRSConnection
|
||||||
@ -30,8 +30,9 @@ class LoginCommand(BaseCommand):
|
|||||||
if config.url is None:
|
if config.url is None:
|
||||||
raise MissingConfigurationException(f"Remote host `{remote}` is not found.")
|
raise MissingConfigurationException(f"Remote host `{remote}` is not found.")
|
||||||
connection = MDRSConnection(config.remote, config.url)
|
connection = MDRSConnection(config.remote, config.url)
|
||||||
user_api = UserApi(connection)
|
user_api = UsersApi(connection)
|
||||||
(user, token) = user_api.auth(username, password)
|
token = user_api.token(username, password)
|
||||||
print("Login Successful")
|
|
||||||
connection.user = user
|
|
||||||
connection.token = token
|
connection.token = token
|
||||||
|
user = user_api.current()
|
||||||
|
connection.user = user
|
||||||
|
print("Login Successful")
|
||||||
|
@ -4,7 +4,7 @@ from typing import Any
|
|||||||
|
|
||||||
from pydantic.dataclasses import dataclass
|
from pydantic.dataclasses import dataclass
|
||||||
|
|
||||||
from mdrsclient.api import FolderApi
|
from mdrsclient.api import 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
|
||||||
@ -82,7 +82,7 @@ class LsCommand(BaseCommand):
|
|||||||
"type": "Type",
|
"type": "Type",
|
||||||
"acl": "Access",
|
"acl": "Access",
|
||||||
"laboratory": "Laboratory",
|
"laboratory": "Laboratory",
|
||||||
"size": "Lock/Size",
|
"size": "Size",
|
||||||
"date": "Date",
|
"date": "Date",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
}
|
}
|
||||||
@ -90,11 +90,11 @@ class LsCommand(BaseCommand):
|
|||||||
for key in label.keys():
|
for key in label.keys():
|
||||||
length[key] = len(label[key]) if not context.is_quick else 0
|
length[key] = len(label[key]) if not context.is_quick else 0
|
||||||
for sub_folder in folder.sub_folders:
|
for sub_folder in folder.sub_folders:
|
||||||
sub_laboratory = context.connection.laboratories.find_by_id(sub_folder.lab_id)
|
sub_laboratory = context.connection.laboratories.find_by_id(sub_folder.laboratory_id)
|
||||||
sub_laboratory_name = sub_laboratory.name if sub_laboratory is not None else "(invalid)"
|
sub_laboratory_name = sub_laboratory.name if sub_laboratory is not None else "(invalid)"
|
||||||
length["acl"] = max(length["acl"], len(sub_folder.access_level_name))
|
length["acl"] = max(length["acl"], len(sub_folder.access_level_name))
|
||||||
length["laboratory"] = max(length["laboratory"], len(sub_laboratory_name))
|
length["laboratory"] = max(length["laboratory"], len(sub_laboratory_name))
|
||||||
length["size"] = max(length["size"], len(sub_folder.lock_name))
|
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 folder.files:
|
||||||
@ -118,10 +118,11 @@ class LsCommand(BaseCommand):
|
|||||||
print("-" * len(header.expandtabs()))
|
print("-" * len(header.expandtabs()))
|
||||||
|
|
||||||
for sub_folder in sorted(folder.sub_folders, key=lambda x: x.name):
|
for sub_folder in sorted(folder.sub_folders, key=lambda x: x.name):
|
||||||
sub_laboratory_name = cls._laboratory_name(context, sub_folder.lab_id)
|
sub_laboratory_name = cls._laboratory_name(context, sub_folder.laboratory_id)
|
||||||
|
sub_folder_type = "[d]" if sub_folder.lock is False else "[l]"
|
||||||
print(
|
print(
|
||||||
f"{'[d]':{length['type']}}\t{sub_folder.access_level_name:{length['acl']}}\t"
|
f"{sub_folder_type:{length['type']}}\t{sub_folder.access_level_name:{length['acl']}}\t"
|
||||||
f"{sub_laboratory_name:{length['laboratory']}}\t{sub_folder.lock_name:{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(folder.files, key=lambda x: x.name):
|
||||||
@ -134,7 +135,7 @@ class LsCommand(BaseCommand):
|
|||||||
if context.is_recursive:
|
if context.is_recursive:
|
||||||
print("")
|
print("")
|
||||||
for sub_folder in sorted(folder.sub_folders, key=lambda x: x.name):
|
for sub_folder in sorted(folder.sub_folders, key=lambda x: x.name):
|
||||||
folder_api = FolderApi(context.connection)
|
folder_api = FoldersApi(context.connection)
|
||||||
try:
|
try:
|
||||||
if sub_folder.lock:
|
if sub_folder.lock:
|
||||||
folder_api.auth(sub_folder.id, context.password)
|
folder_api.auth(sub_folder.id, context.password)
|
||||||
@ -149,15 +150,16 @@ class LsCommand(BaseCommand):
|
|||||||
"id": folder.id,
|
"id": folder.id,
|
||||||
"pid": folder.pid,
|
"pid": folder.pid,
|
||||||
"name": folder.name,
|
"name": folder.name,
|
||||||
|
"size": folder.size,
|
||||||
"access_level": folder.access_level_name,
|
"access_level": folder.access_level_name,
|
||||||
"lock": folder.lock,
|
"lock": folder.lock,
|
||||||
"laboratory": cls._laboratory_name(context, folder.lab_id),
|
"laboratory": cls._laboratory_name(context, folder.laboratory_id),
|
||||||
"description": folder.description,
|
"description": folder.description,
|
||||||
"created_at": folder.created_at,
|
"created_at": folder.created_at,
|
||||||
"updated_at": folder.updated_at,
|
"updated_at": folder.updated_at,
|
||||||
}
|
}
|
||||||
if isinstance(folder, Folder):
|
if isinstance(folder, Folder):
|
||||||
folder_api = FolderApi(context.connection)
|
folder_api = FoldersApi(context.connection)
|
||||||
data["metadata"] = folder_api.metadata(folder.id)
|
data["metadata"] = folder_api.metadata(folder.id)
|
||||||
if context.is_recursive:
|
if context.is_recursive:
|
||||||
sub_folders: list[dict[str, Any]] = []
|
sub_folders: list[dict[str, Any]] = []
|
||||||
@ -187,7 +189,7 @@ class LsCommand(BaseCommand):
|
|||||||
# "thumbnail": file.thumbnail,
|
# "thumbnail": file.thumbnail,
|
||||||
"description": file.description,
|
"description": file.description,
|
||||||
"metadata": file.metadata,
|
"metadata": file.metadata,
|
||||||
"download_url": f"{context.connection.url}/v2/{file.download_url}",
|
"download_url": f"{context.connection.url}/{file.download_url}",
|
||||||
"created_at": file.created_at,
|
"created_at": file.created_at,
|
||||||
"updated_at": file.updated_at,
|
"updated_at": file.updated_at,
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import json
|
|||||||
from argparse import Namespace
|
from argparse import Namespace
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from mdrsclient.api import FolderApi
|
from mdrsclient.api import FoldersApi
|
||||||
from mdrsclient.commands.base import BaseCommand
|
from mdrsclient.commands.base import BaseCommand
|
||||||
|
|
||||||
|
|
||||||
@ -26,6 +26,6 @@ class MetadataCommand(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, password)
|
folder = cls._find_folder(connection, laboratory, r_path, password)
|
||||||
folder_api = FolderApi(connection)
|
folder_api = FoldersApi(connection)
|
||||||
metadata = folder_api.metadata(folder.id)
|
metadata = folder_api.metadata(folder.id)
|
||||||
print(json.dumps(metadata, ensure_ascii=False))
|
print(json.dumps(metadata, ensure_ascii=False))
|
||||||
|
@ -3,7 +3,7 @@ from argparse import Namespace
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
from unicodedata import normalize
|
from unicodedata import normalize
|
||||||
|
|
||||||
from mdrsclient.api import FolderApi
|
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
|
||||||
|
|
||||||
@ -31,5 +31,5 @@ class MkdirCommand(BaseCommand):
|
|||||||
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:
|
if parent_folder.find_sub_folder(r_basename) is not None or parent_folder.find_file(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 = FolderApi(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)
|
||||||
|
@ -3,7 +3,7 @@ from argparse import Namespace
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
from unicodedata import normalize
|
from unicodedata import normalize
|
||||||
|
|
||||||
from mdrsclient.api import FileApi, FolderApi
|
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
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ class MvCommand(BaseCommand):
|
|||||||
d_sub_folder = d_parent_folder.find_sub_folder(d_basename)
|
d_sub_folder = d_parent_folder.find_sub_folder(d_basename)
|
||||||
if d_sub_folder is not None:
|
if d_sub_folder is not None:
|
||||||
raise IllegalArgumentException(f"Cannot overwrite non-folder `{d_basename}` with folder `{d_path}`.")
|
raise IllegalArgumentException(f"Cannot overwrite non-folder `{d_basename}` with folder `{d_path}`.")
|
||||||
file_api = FileApi(connection)
|
file_api = FilesApi(connection)
|
||||||
if s_parent_folder.id != d_parent_folder.id or d_basename != s_basename:
|
if s_parent_folder.id != d_parent_folder.id or d_basename != s_basename:
|
||||||
file_api.move(s_file, d_parent_folder.id, normalize("NFC", d_basename))
|
file_api.move(s_file, d_parent_folder.id, normalize("NFC", d_basename))
|
||||||
else:
|
else:
|
||||||
@ -67,6 +67,6 @@ class MvCommand(BaseCommand):
|
|||||||
if d_folder.id == s_folder.id:
|
if d_folder.id == s_folder.id:
|
||||||
raise IllegalArgumentException(f"`{s_path}` and `{s_path}` are the same folder.")
|
raise IllegalArgumentException(f"`{s_path}` and `{s_path}` are the same folder.")
|
||||||
raise IllegalArgumentException(f"Cannot move `{s_path}` to `{d_path}`: Folder not empty.")
|
raise IllegalArgumentException(f"Cannot move `{s_path}` to `{d_path}`: Folder not empty.")
|
||||||
folder_api = FolderApi(connection)
|
folder_api = FoldersApi(connection)
|
||||||
if s_parent_folder.id != d_parent_folder.id or d_basename != s_basename:
|
if s_parent_folder.id != d_parent_folder.id or d_basename != s_basename:
|
||||||
folder_api.move(s_folder, d_parent_folder.id, normalize("NFC", d_basename))
|
folder_api.move(s_folder, d_parent_folder.id, normalize("NFC", d_basename))
|
||||||
|
@ -2,7 +2,7 @@ import os
|
|||||||
from argparse import Namespace
|
from argparse import Namespace
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from mdrsclient.api import FileApi, FolderApi
|
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
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ class RmCommand(BaseCommand):
|
|||||||
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)
|
file = parent_folder.find_file(r_basename)
|
||||||
if file is not None:
|
if file is not None:
|
||||||
file_api = FileApi(connection)
|
file_api = FilesApi(connection)
|
||||||
file_api.destroy(file)
|
file_api.destroy(file)
|
||||||
else:
|
else:
|
||||||
folder = parent_folder.find_sub_folder(r_basename)
|
folder = parent_folder.find_sub_folder(r_basename)
|
||||||
@ -42,5 +42,5 @@ class RmCommand(BaseCommand):
|
|||||||
raise IllegalArgumentException(f"Cannot remove `{r_path}`: No such file or folder.")
|
raise IllegalArgumentException(f"Cannot remove `{r_path}`: No such file or folder.")
|
||||||
if not is_recursive:
|
if not is_recursive:
|
||||||
raise IllegalArgumentException(f"Cannot remove `{r_path}`: Is a folder.")
|
raise IllegalArgumentException(f"Cannot remove `{r_path}`: Is a folder.")
|
||||||
folder_api = FolderApi(connection)
|
folder_api = FoldersApi(connection)
|
||||||
folder_api.destroy(folder.id, True)
|
folder_api.destroy(folder.id, True)
|
||||||
|
@ -5,7 +5,7 @@ from typing import Any
|
|||||||
|
|
||||||
from pydantic.dataclasses import dataclass
|
from pydantic.dataclasses import dataclass
|
||||||
|
|
||||||
from mdrsclient.api import FileApi, FolderApi
|
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
|
||||||
@ -40,7 +40,7 @@ class UploadCommand(BaseCommand):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def upload(cls, local_path: str, remote_path: str, is_recursive: bool) -> None:
|
def upload(cls, local_path: str, remote_path: str, is_recursive: bool) -> None:
|
||||||
(remote, laboratory_name, r_path) = cls._parse_remote_host_with_path(remote_path)
|
(remote, laboratory_name, r_path) = cls._parse_remote_host_with_path(remote_path)
|
||||||
l_path = os.path.realpath(local_path)
|
l_path = local_path
|
||||||
if not os.path.exists(l_path):
|
if not os.path.exists(l_path):
|
||||||
raise IllegalArgumentException(f"File or directory `{local_path}` not found.")
|
raise IllegalArgumentException(f"File or directory `{local_path}` not found.")
|
||||||
connection = cls._create_connection(remote)
|
connection = cls._create_connection(remote)
|
||||||
@ -50,13 +50,12 @@ class UploadCommand(BaseCommand):
|
|||||||
if os.path.isdir(l_path):
|
if os.path.isdir(l_path):
|
||||||
if not is_recursive:
|
if not is_recursive:
|
||||||
raise IllegalArgumentException(f"Cannot upload `{local_path}`: Is a directory.")
|
raise IllegalArgumentException(f"Cannot upload `{local_path}`: Is a directory.")
|
||||||
folder_api = FolderApi(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
|
||||||
l_basename = os.path.basename(l_path)
|
l_basename = os.path.basename(l_path)
|
||||||
for dirpath, _, filenames in os.walk(l_path):
|
for dirpath, _, filenames in os.walk(l_path):
|
||||||
sub = l_basename if dirpath == l_path else os.path.join(l_basename, os.path.relpath(dirpath, l_path))
|
d_dirname = os.path.join(r_path, l_basename)
|
||||||
d_dirname = os.path.join(r_path, sub)
|
|
||||||
d_basename = os.path.basename(d_dirname)
|
d_basename = os.path.basename(d_dirname)
|
||||||
# prepare destination parent path
|
# prepare destination parent path
|
||||||
d_parent_dirname = os.path.dirname(d_dirname)
|
d_parent_dirname = os.path.dirname(d_dirname)
|
||||||
@ -82,12 +81,12 @@ class UploadCommand(BaseCommand):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def __multiple_upload(cls, connection: MDRSConnection, infos: list[UploadFileInfo]) -> None:
|
def __multiple_upload(cls, connection: MDRSConnection, infos: list[UploadFileInfo]) -> None:
|
||||||
file_api = FileApi(connection)
|
file_api = FilesApi(connection)
|
||||||
with ThreadPoolExecutor(max_workers=CONCURRENT) as pool:
|
with ThreadPoolExecutor(max_workers=CONCURRENT) as pool:
|
||||||
pool.map(lambda x: cls.__multiple_upload_worker(file_api, x), infos)
|
pool.map(lambda x: cls.__multiple_upload_worker(file_api, x), infos)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def __multiple_upload_worker(cls, file_api: FileApi, info: UploadFileInfo) -> None:
|
def __multiple_upload_worker(cls, file_api: FilesApi, info: UploadFileInfo) -> None:
|
||||||
basename = os.path.basename(info.path)
|
basename = os.path.basename(info.path)
|
||||||
file = info.folder.find_file(basename)
|
file = info.folder.find_file(basename)
|
||||||
try:
|
try:
|
||||||
|
@ -8,28 +8,37 @@ from mdrsclient.models.utils import iso8601_to_user_friendly
|
|||||||
|
|
||||||
|
|
||||||
class FolderAccessLevelItem(NamedTuple):
|
class FolderAccessLevelItem(NamedTuple):
|
||||||
id: int
|
mask: int
|
||||||
key: str
|
key: str
|
||||||
label: str
|
label: str
|
||||||
|
|
||||||
|
|
||||||
class FolderAccessLevel:
|
class FolderAccessLevel:
|
||||||
|
# Bit Mask
|
||||||
|
# - bit 0: Is Private
|
||||||
|
# - bit 1: Is Public
|
||||||
|
# - bit 2: With Password
|
||||||
|
# - bit 3-7: (Reserved)
|
||||||
|
# - bit 8-15: Restricted Open
|
||||||
ACCESS_LEVELS: Final[list[FolderAccessLevelItem]] = [
|
ACCESS_LEVELS: Final[list[FolderAccessLevelItem]] = [
|
||||||
FolderAccessLevelItem(-1, "storage", "Storage"),
|
FolderAccessLevelItem(0x0204, "5kikan_or_pw_open", "5Kikan or PW Open"),
|
||||||
FolderAccessLevelItem(0, "private", "Private"),
|
FolderAccessLevelItem(0x0104, "cbs_or_pw_open", "CBS or PW Open"),
|
||||||
FolderAccessLevelItem(1, "cbs_open", "CBS Open"),
|
FolderAccessLevelItem(0x0200, "5kikan_open", "5Kikan Open"),
|
||||||
FolderAccessLevelItem(2, "pw_open", "PW Open"),
|
FolderAccessLevelItem(0x0100, "cbs_open", "CBS Open"),
|
||||||
FolderAccessLevelItem(3, "public", "Public"),
|
FolderAccessLevelItem(0x0004, "pw_open", "PW Open"),
|
||||||
|
FolderAccessLevelItem(0x0002, "public", "Public"),
|
||||||
|
FolderAccessLevelItem(0x0001, "private", "Private"),
|
||||||
|
FolderAccessLevelItem(0x0000, "storage", "Storage"),
|
||||||
]
|
]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def key2id(key: str) -> int | None:
|
def key2id(key: str) -> int | None:
|
||||||
acl = next((x for x in FolderAccessLevel.ACCESS_LEVELS if x.key == key), None)
|
acl = next((x for x in FolderAccessLevel.ACCESS_LEVELS if x.key == key), None)
|
||||||
return acl.id if acl is not None else None
|
return acl.mask if acl is not None else None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def id2label(id: int) -> str | None:
|
def id2label(id: int) -> str | None:
|
||||||
acl = next((x for x in FolderAccessLevel.ACCESS_LEVELS if x.id == id), None)
|
acl = next((x for x in FolderAccessLevel.ACCESS_LEVELS if (x.mask & id) == x.mask), None)
|
||||||
return acl.label if acl is not None else None
|
return acl.label if acl is not None else None
|
||||||
|
|
||||||
|
|
||||||
@ -40,7 +49,8 @@ class FolderSimple:
|
|||||||
name: str
|
name: str
|
||||||
access_level: int
|
access_level: int
|
||||||
lock: bool
|
lock: bool
|
||||||
lab_id: int
|
size: int
|
||||||
|
laboratory_id: int
|
||||||
description: str
|
description: str
|
||||||
created_at: str
|
created_at: str
|
||||||
updated_at: str
|
updated_at: str
|
||||||
|
74
pyproject.toml
Normal file
74
pyproject.toml
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
[tool.poetry]
|
||||||
|
name = "mdrs-client-python"
|
||||||
|
version = "1.3.0"
|
||||||
|
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>"]
|
||||||
|
license = "MIT"
|
||||||
|
readme = "README.md"
|
||||||
|
classifiers=[
|
||||||
|
"Development Status :: 3 - Alpha",
|
||||||
|
"Environment :: Console",
|
||||||
|
"Intended Audience :: Developers",
|
||||||
|
"Intended Audience :: Science/Research",
|
||||||
|
"Programming Language :: Python :: 3.10",
|
||||||
|
"Programming Language :: Python :: 3.11",
|
||||||
|
"Programming Language :: Python :: 3.12",
|
||||||
|
"OSI Approved :: MIT License",
|
||||||
|
"Topic :: Utilities",
|
||||||
|
]
|
||||||
|
packages = [
|
||||||
|
{ include = "mdrsclient" }
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
python = "^3.10"
|
||||||
|
requests = "^2.31.0"
|
||||||
|
python-dotenv = "^1.0.0"
|
||||||
|
pydantic = "^2.5.2"
|
||||||
|
pydantic-settings = "^2.1.0"
|
||||||
|
PyJWT = "^2.8.0"
|
||||||
|
validators = "^0.22.0"
|
||||||
|
|
||||||
|
[tool.poetry.group.dev.dependencies]
|
||||||
|
black = "^23.12.0"
|
||||||
|
flake8 = "^6.1.0"
|
||||||
|
Flake8-pyproject = "^1.2.3"
|
||||||
|
isort = "^5.13.0"
|
||||||
|
pyright = "^1.1.339"
|
||||||
|
|
||||||
|
[tool.poetry.scripts]
|
||||||
|
mdrs = 'mdrsclient.__main__:main'
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["poetry-core"]
|
||||||
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
|
||||||
|
[tool.black]
|
||||||
|
line-length = 120
|
||||||
|
target-version = ['py310']
|
||||||
|
include = '\.pyi?$'
|
||||||
|
exclude = '''
|
||||||
|
/(
|
||||||
|
\.git
|
||||||
|
| \.venv
|
||||||
|
| __pycache__
|
||||||
|
| dist
|
||||||
|
)/
|
||||||
|
'''
|
||||||
|
|
||||||
|
[tool.flake8]
|
||||||
|
exclude = ".git, .venv, __pycache__, dist"
|
||||||
|
max-complexity = 10
|
||||||
|
max-line-length = 120
|
||||||
|
|
||||||
|
[tool.isort]
|
||||||
|
profile = "black"
|
||||||
|
line_length = 120
|
||||||
|
|
||||||
|
[tool.mypy]
|
||||||
|
|
||||||
|
[tool.pyright]
|
||||||
|
typeCheckingMode = "basic"
|
||||||
|
exclude = ["**/__pycache__", "**/.*", "dist"]
|
||||||
|
#reportUnknownMemberType = "warning"
|
||||||
|
#reportUnknownVariableType = "warning"
|
@ -1,6 +0,0 @@
|
|||||||
requests
|
|
||||||
python-dotenv
|
|
||||||
pydantic
|
|
||||||
pydantic-settings
|
|
||||||
PyJWT
|
|
||||||
validators
|
|
40
setup.py
40
setup.py
@ -1,40 +0,0 @@
|
|||||||
import os
|
|
||||||
from typing import Final
|
|
||||||
|
|
||||||
from setuptools import find_packages, setup
|
|
||||||
|
|
||||||
from mdrsclient import __version__
|
|
||||||
|
|
||||||
BASE_DIR: Final[str] = os.path.realpath(os.path.dirname(__file__))
|
|
||||||
|
|
||||||
with open(os.path.join(BASE_DIR, "requirements.txt")) as f:
|
|
||||||
__requirements__ = f.read().splitlines()
|
|
||||||
|
|
||||||
with open(os.path.join(BASE_DIR, "README.md")) as f:
|
|
||||||
__readme__ = f.read()
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name="mdrsclient",
|
|
||||||
version=__version__,
|
|
||||||
description="A MDRS command-line tool",
|
|
||||||
long_description=__readme__,
|
|
||||||
author="Neuroinformatics Unit, RIKEN CBS",
|
|
||||||
license="MIT",
|
|
||||||
classifiers=[
|
|
||||||
"Development Status :: 3 - Alpha",
|
|
||||||
"Environment :: Console",
|
|
||||||
"Intended Audience :: Developers",
|
|
||||||
"Intended Audience :: Science/Research",
|
|
||||||
"Programming Language :: Python :: 3.10",
|
|
||||||
"Programming Language :: Python :: 3.11",
|
|
||||||
"OSI Approved :: MIT License",
|
|
||||||
"Topic :: Utilities",
|
|
||||||
],
|
|
||||||
packages=find_packages(),
|
|
||||||
include_package_data=True,
|
|
||||||
package_data={
|
|
||||||
"mdrsclient": ["VERSION"],
|
|
||||||
},
|
|
||||||
install_requires=__requirements__,
|
|
||||||
entry_points={"console_scripts": ["mdrs=mdrsclient.__main__:main"]},
|
|
||||||
)
|
|
Reference in New Issue
Block a user