2024-07-04 12:33:57 +09:00
|
|
|
import mimetypes
|
2023-12-18 16:23:46 +09:00
|
|
|
import os
|
2023-07-19 21:47:47 +09:00
|
|
|
from typing import Any, Final
|
2023-05-01 20:00:32 +09:00
|
|
|
|
2023-07-19 14:43:16 +09:00
|
|
|
from pydantic import TypeAdapter
|
2023-05-01 20:00:32 +09:00
|
|
|
from pydantic.dataclasses import dataclass
|
2024-07-04 12:33:57 +09:00
|
|
|
from requests_toolbelt.multipart.encoder import MultipartEncoder
|
2023-05-01 20:00:32 +09:00
|
|
|
|
|
|
|
from mdrsclient.api.base import BaseApi
|
|
|
|
from mdrsclient.api.utils import token_check
|
|
|
|
from mdrsclient.exceptions import UnexpectedException
|
|
|
|
from mdrsclient.models import File
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass(frozen=True)
|
2023-12-12 20:05:46 +09:00
|
|
|
class FilesApiCreateResponse:
|
2023-05-01 20:00:32 +09:00
|
|
|
id: str
|
|
|
|
|
|
|
|
|
2023-12-12 20:05:46 +09:00
|
|
|
class FilesApi(BaseApi):
|
|
|
|
ENTRYPOINT: Final[str] = "v3/files/"
|
2024-07-04 12:33:57 +09:00
|
|
|
FALLBACK_MIMETYPE: Final[str] = "application/octet-stream"
|
2023-05-01 20:00:32 +09:00
|
|
|
|
|
|
|
def retrieve(self, id: str) -> File:
|
2023-05-09 19:45:03 +09:00
|
|
|
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
|
2023-05-01 20:00:32 +09:00
|
|
|
url = self.ENTRYPOINT + id + "/"
|
2023-05-09 13:08:50 +09:00
|
|
|
token_check(self.connection)
|
2023-05-10 14:46:08 +09:00
|
|
|
response = self.connection.get(url)
|
2023-05-01 20:00:32 +09:00
|
|
|
self._raise_response_error(response)
|
2023-07-19 14:43:16 +09:00
|
|
|
return TypeAdapter(File).validate_python(response.json())
|
2023-05-01 20:00:32 +09:00
|
|
|
|
|
|
|
def create(self, folder_id: str, path: str) -> str:
|
2023-05-09 19:45:03 +09:00
|
|
|
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
|
2023-05-01 20:00:32 +09:00
|
|
|
url = self.ENTRYPOINT
|
2023-05-09 13:08:50 +09:00
|
|
|
token_check(self.connection)
|
2024-07-04 12:33:57 +09:00
|
|
|
data: dict[str, str | int] | MultipartEncoder = {}
|
2023-05-01 20:00:32 +09:00
|
|
|
try:
|
2023-12-20 19:42:06 +09:00
|
|
|
with open(os.path.realpath(path), mode="rb") as fp:
|
2024-07-04 12:33:57 +09:00
|
|
|
data = MultipartEncoder(
|
|
|
|
fields={"folder_id": folder_id, "file": (os.path.basename(path), fp, self._get_mime_type(path))}
|
|
|
|
)
|
|
|
|
response = self.connection.post(url, data=data, headers={"Content-Type": data.content_type})
|
2023-05-01 20:00:32 +09:00
|
|
|
self._raise_response_error(response)
|
2023-12-12 20:05:46 +09:00
|
|
|
ret = TypeAdapter(FilesApiCreateResponse).validate_python(response.json())
|
2023-05-01 20:00:32 +09:00
|
|
|
except OSError:
|
|
|
|
raise UnexpectedException(f"Could not open `{path}` file.")
|
2024-07-04 12:33:57 +09:00
|
|
|
except MemoryError:
|
|
|
|
raise UnexpectedException("Out of memory.")
|
|
|
|
except Exception as e:
|
2024-07-04 15:21:45 +09:00
|
|
|
raise UnexpectedException("Unspecified error.") from e
|
2023-05-01 20:00:32 +09:00
|
|
|
return ret.id
|
|
|
|
|
|
|
|
def update(self, file: File, path: str | None) -> bool:
|
2023-05-09 19:45:03 +09:00
|
|
|
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
|
2023-05-01 20:00:32 +09:00
|
|
|
url = self.ENTRYPOINT + file.id + "/"
|
2023-05-09 13:08:50 +09:00
|
|
|
token_check(self.connection)
|
2024-07-04 12:33:57 +09:00
|
|
|
data: dict[str, str | int] | MultipartEncoder = {}
|
2023-05-01 20:00:32 +09:00
|
|
|
if path is not None:
|
2023-05-09 18:38:58 +09:00
|
|
|
# update file body
|
2023-05-01 20:00:32 +09:00
|
|
|
try:
|
2023-12-20 19:42:06 +09:00
|
|
|
with open(os.path.realpath(path), mode="rb") as fp:
|
2024-07-04 12:33:57 +09:00
|
|
|
data = MultipartEncoder(fields={"file": (os.path.basename(path), fp, self._get_mime_type(path))})
|
|
|
|
response = self.connection.put(url, data=data, headers={"Content-Type": data.content_type})
|
2023-05-01 20:00:32 +09:00
|
|
|
except OSError:
|
|
|
|
raise UnexpectedException(f"Could not open `{path}` file.")
|
2024-07-04 12:33:57 +09:00
|
|
|
except MemoryError:
|
|
|
|
raise UnexpectedException("Out of memory.")
|
2024-07-04 15:21:45 +09:00
|
|
|
except Exception as e:
|
|
|
|
raise UnexpectedException("Unspecified error.") from e
|
2023-05-01 20:00:32 +09:00
|
|
|
else:
|
2023-05-09 18:38:58 +09:00
|
|
|
# update metadata
|
2024-07-04 12:33:57 +09:00
|
|
|
data = {"name": file.name, "description": file.description}
|
2023-05-10 14:46:08 +09:00
|
|
|
response = self.connection.put(url, data=data)
|
2023-05-01 20:00:32 +09:00
|
|
|
self._raise_response_error(response)
|
|
|
|
return True
|
|
|
|
|
|
|
|
def destroy(self, file: File) -> bool:
|
2023-05-09 19:45:03 +09:00
|
|
|
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
|
2023-05-01 20:00:32 +09:00
|
|
|
url = self.ENTRYPOINT + file.id + "/"
|
2023-05-09 13:08:50 +09:00
|
|
|
token_check(self.connection)
|
2023-05-10 14:46:08 +09:00
|
|
|
response = self.connection.delete(url)
|
2023-05-01 20:00:32 +09:00
|
|
|
self._raise_response_error(response)
|
|
|
|
return True
|
|
|
|
|
2023-07-20 11:43:07 +09:00
|
|
|
def move(self, file: File, folder_id: str, name: str) -> bool:
|
2023-05-09 19:45:03 +09:00
|
|
|
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
|
2023-05-01 20:00:32 +09:00
|
|
|
url = self.ENTRYPOINT + file.id + "/move/"
|
2023-07-20 11:43:07 +09:00
|
|
|
data: dict[str, str | int] = {"folder": folder_id, "name": name}
|
|
|
|
token_check(self.connection)
|
|
|
|
response = self.connection.post(url, data=data)
|
|
|
|
self._raise_response_error(response)
|
|
|
|
return True
|
|
|
|
|
|
|
|
def copy(self, file: File, folder_id: str, name: str) -> bool:
|
|
|
|
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
|
|
|
|
url = self.ENTRYPOINT + file.id + "/copy/"
|
|
|
|
data: dict[str, str | int] = {"folder": folder_id, "name": name}
|
2023-05-09 13:08:50 +09:00
|
|
|
token_check(self.connection)
|
2023-05-10 14:46:08 +09:00
|
|
|
response = self.connection.post(url, data=data)
|
2023-05-01 20:00:32 +09:00
|
|
|
self._raise_response_error(response)
|
|
|
|
return True
|
|
|
|
|
2023-07-19 21:47:47 +09:00
|
|
|
def metadata(self, file: File) -> dict[str, Any]:
|
2023-05-09 19:45:03 +09:00
|
|
|
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
|
2023-05-01 20:00:32 +09:00
|
|
|
url = self.ENTRYPOINT + file.id + "/metadata/"
|
2023-05-09 13:08:50 +09:00
|
|
|
token_check(self.connection)
|
2023-05-10 14:46:08 +09:00
|
|
|
response = self.connection.get(url)
|
2023-05-01 20:00:32 +09:00
|
|
|
self._raise_response_error(response)
|
|
|
|
return response.json()
|
2023-05-09 14:38:13 +09:00
|
|
|
|
|
|
|
def download(self, file: File, path: str) -> bool:
|
2023-05-09 19:45:03 +09:00
|
|
|
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
|
2023-12-12 20:05:46 +09:00
|
|
|
url = file.download_url
|
2024-07-08 18:07:26 +09:00
|
|
|
token_check(self.connection)
|
2023-05-10 14:46:08 +09:00
|
|
|
response = self.connection.get(url, stream=True)
|
2023-05-09 18:38:58 +09:00
|
|
|
self._raise_response_error(response)
|
2023-05-09 14:38:13 +09:00
|
|
|
try:
|
|
|
|
with open(path, "wb") as f:
|
2023-05-09 18:38:58 +09:00
|
|
|
for chunk in response.iter_content(chunk_size=4096):
|
2023-05-09 14:38:13 +09:00
|
|
|
if chunk:
|
|
|
|
f.write(chunk)
|
|
|
|
f.flush()
|
|
|
|
except PermissionError:
|
|
|
|
print(f"Cannot create file `{path}`: Permission denied.")
|
|
|
|
return True
|
2024-07-04 12:33:57 +09:00
|
|
|
|
|
|
|
def _get_mime_type(self, path: str) -> str:
|
|
|
|
mt = mimetypes.guess_type(path)
|
|
|
|
if mt:
|
|
|
|
return mt[0] or self.FALLBACK_MIMETYPE
|
|
|
|
return self.FALLBACK_MIMETYPE
|