mirror of
https://github.com/subutux/rmapy.git
synced 2025-12-08 22:53:25 +00:00
Code cleanup & Refactoring
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
quickstart
|
quick start
|
||||||
==========
|
==========
|
||||||
|
|
||||||
If you previously used the go package `rmapi`_ ,the keys for authorization
|
If you previously used the go package `rmapi`_ ,the keys for authorization
|
||||||
@@ -12,7 +12,7 @@ If not, you'll need to register the client as a new device on `my remarkable`_.
|
|||||||
.. _rmapi: https://github.com/juruen/rmapi
|
.. _rmapi: https://github.com/juruen/rmapi
|
||||||
|
|
||||||
|
|
||||||
Registering the API CLient
|
Registering the API Client
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Registering the device is easy. Go to `my remarkable`_ to register a new device
|
Registering the device is easy. Go to `my remarkable`_ to register a new device
|
||||||
@@ -25,16 +25,16 @@ and use the code you see on the webpage
|
|||||||
from rmapy.api import Client
|
from rmapy.api import Client
|
||||||
|
|
||||||
rmapy = Client()
|
rmapy = Client()
|
||||||
# Shoud return False
|
# Should return False
|
||||||
rmapy.is_authenticated()
|
rmapy.is_authenticated()
|
||||||
# This registers the client as a new device. The received device token is
|
# This registers the client as a new device. The received device token is
|
||||||
# stored in the users directory in the file ~/.rmapi, the same as with the
|
# stored in the users directory in the file ~/.rmapi, the same as with the
|
||||||
# go rmapi client.
|
# go rmapi client.
|
||||||
rmapy.register_device("fkgzzklrs")
|
rmapy.register_device("fkgzzklrs")
|
||||||
# It's always a good idea to refresh the user token everytime you start
|
# It's always a good idea to refresh the user token every time you start
|
||||||
# a new session.
|
# a new session.
|
||||||
rmapy.refresh_token()
|
rmapy.refresh_token()
|
||||||
# Shoud return True
|
# Should return True
|
||||||
rmapy.is_authenticated()
|
rmapy.is_authenticated()
|
||||||
|
|
||||||
Working with items
|
Working with items
|
||||||
|
|||||||
61
rmapy/api.py
61
rmapy/api.py
@@ -26,12 +26,8 @@ DocumentOrFolder = Union[Document, Folder]
|
|||||||
class Client(object):
|
class Client(object):
|
||||||
"""API Client for Remarkable Cloud
|
"""API Client for Remarkable Cloud
|
||||||
|
|
||||||
This allows you to authenticate & communiticate with the Remarkable Cloud
|
This allows you to authenticate & communicate with the Remarkable Cloud
|
||||||
and does all the heavy lifting for you.
|
and does all the heavy lifting for you.
|
||||||
|
|
||||||
Attributes:
|
|
||||||
token_set: the authentication tokens
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
token_set = {
|
token_set = {
|
||||||
@@ -48,7 +44,7 @@ class Client(object):
|
|||||||
|
|
||||||
def request(self, method: str, path: str,
|
def request(self, method: str, path: str,
|
||||||
data=None,
|
data=None,
|
||||||
body=None, headers={},
|
body=None, headers=None,
|
||||||
params=None, stream=False) -> requests.Response:
|
params=None, stream=False) -> requests.Response:
|
||||||
"""Creates a request against the Remarkable Cloud API
|
"""Creates a request against the Remarkable Cloud API
|
||||||
|
|
||||||
@@ -62,12 +58,14 @@ class Client(object):
|
|||||||
body: the body to request with. This will be converted to json.
|
body: the body to request with. This will be converted to json.
|
||||||
headers: a dict of additional headers to add to the request.
|
headers: a dict of additional headers to add to the request.
|
||||||
params: Query params to append to the request.
|
params: Query params to append to the request.
|
||||||
steam: Should the response be a stream?
|
stream: Should the response be a stream?
|
||||||
Returns:
|
Returns:
|
||||||
A Response instance containing most likely the response from
|
A Response instance containing most likely the response from
|
||||||
the server.
|
the server.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if headers is None:
|
||||||
|
headers = {}
|
||||||
if not path.startswith("http"):
|
if not path.startswith("http"):
|
||||||
if not path.startswith('/'):
|
if not path.startswith('/'):
|
||||||
path = '/' + path
|
path = '/' + path
|
||||||
@@ -184,13 +182,13 @@ class Client(object):
|
|||||||
|
|
||||||
return collection
|
return collection
|
||||||
|
|
||||||
def get_doc(self, ID: str) -> Optional[DocumentOrFolder]:
|
def get_doc(self, _id: str) -> Optional[DocumentOrFolder]:
|
||||||
"""Get a meta item by ID
|
"""Get a meta item by ID
|
||||||
|
|
||||||
Fetch a meta item from the Remarkable Cloud by ID.
|
Fetch a meta item from the Remarkable Cloud by ID.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
ID: The id of the meta item.
|
_id: The id of the meta item.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A Document or Folder instance of the requested ID.
|
A Document or Folder instance of the requested ID.
|
||||||
@@ -198,10 +196,10 @@ class Client(object):
|
|||||||
DocumentNotFound: When a document cannot be found.
|
DocumentNotFound: When a document cannot be found.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
log.debug(f"GETTING DOC {ID}")
|
log.debug(f"GETTING DOC {_id}")
|
||||||
response = self.request("GET", "/document-storage/json/2/docs",
|
response = self.request("GET", "/document-storage/json/2/docs",
|
||||||
params={
|
params={
|
||||||
"doc": ID,
|
"doc": _id,
|
||||||
"withBlob": True
|
"withBlob": True
|
||||||
})
|
})
|
||||||
log.debug(response.url)
|
log.debug(response.url)
|
||||||
@@ -214,7 +212,7 @@ class Client(object):
|
|||||||
elif data_response[0]["Type"] == "DocumentType":
|
elif data_response[0]["Type"] == "DocumentType":
|
||||||
return Document(**data_response[0])
|
return Document(**data_response[0])
|
||||||
else:
|
else:
|
||||||
raise DocumentNotFound(f"Cound not find document {ID}")
|
raise DocumentNotFound(f"Could not find document {_id}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def download(self, document: Document) -> ZipDocument:
|
def download(self, document: Document) -> ZipDocument:
|
||||||
@@ -250,7 +248,7 @@ class Client(object):
|
|||||||
Args:
|
Args:
|
||||||
doc: A Document or folder to delete.
|
doc: A Document or folder to delete.
|
||||||
Raises:
|
Raises:
|
||||||
ApiError: an error occured while uploading the document.
|
ApiError: an error occurred while uploading the document.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
response = self.request("PUT", "/document-storage/json/2/delete",
|
response = self.request("PUT", "/document-storage/json/2/delete",
|
||||||
@@ -259,28 +257,29 @@ class Client(object):
|
|||||||
"Version": doc.Version
|
"Version": doc.Version
|
||||||
}])
|
}])
|
||||||
|
|
||||||
return self.check_reponse(response)
|
return self.check_response(response)
|
||||||
|
|
||||||
def upload(self, zipDoc: ZipDocument, to: Folder = Folder(ID="")):
|
def upload(self, zip_doc: ZipDocument, to: Folder = Folder(ID="")):
|
||||||
"""Upload a document to the cloud.
|
"""Upload a document to the cloud.
|
||||||
|
|
||||||
Add a new document to the Remarkable Cloud.
|
Add a new document to the Remarkable Cloud.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
zipDoc: A ZipDocument instance containing the data of a Document.
|
zip_doc: A ZipDocument instance containing the data of a Document.
|
||||||
|
to: the parent of the document. (Default root)
|
||||||
Raises:
|
Raises:
|
||||||
ApiError: an error occured while uploading the document.
|
ApiError: an error occurred while uploading the document.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
BlobURLPut = self._upload_request(zipDoc)
|
blob_url_put = self._upload_request(zip_doc)
|
||||||
zipDoc.dump(zipDoc.zipfile)
|
zip_doc.dump(zip_doc.zipfile)
|
||||||
response = self.request("PUT", BlobURLPut, data=zipDoc.zipfile.read())
|
response = self.request("PUT", blob_url_put, data=zip_doc.zipfile.read())
|
||||||
# Reset seek
|
# Reset seek
|
||||||
zipDoc.zipfile.seek(0)
|
zip_doc.zipfile.seek(0)
|
||||||
if response.ok:
|
if response.ok:
|
||||||
doc = Document(**zipDoc.metadata)
|
doc = Document(**zip_doc.metadata)
|
||||||
doc.ID = zipDoc.ID
|
doc.ID = zip_doc.ID
|
||||||
doc.Parent = to.ID
|
doc.Parent = to.ID
|
||||||
return self.update_metadata(doc)
|
return self.update_metadata(doc)
|
||||||
else:
|
else:
|
||||||
@@ -304,7 +303,7 @@ class Client(object):
|
|||||||
"/document-storage/json/2/upload/update-status",
|
"/document-storage/json/2/upload/update-status",
|
||||||
body=[req])
|
body=[req])
|
||||||
|
|
||||||
return self.check_reponse(res)
|
return self.check_response(res)
|
||||||
|
|
||||||
def get_current_version(self, docorfolder: DocumentOrFolder) -> int:
|
def get_current_version(self, docorfolder: DocumentOrFolder) -> int:
|
||||||
"""Get the latest version info from a Document or Folder
|
"""Get the latest version info from a Document or Folder
|
||||||
@@ -318,7 +317,7 @@ class Client(object):
|
|||||||
the version information.
|
the version information.
|
||||||
Raises:
|
Raises:
|
||||||
DocumentNotFound: cannot find the requested Document or Folder.
|
DocumentNotFound: cannot find the requested Document or Folder.
|
||||||
ApiError: An error occured while processing the request.
|
ApiError: An error occurred while processing the request.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -329,8 +328,8 @@ class Client(object):
|
|||||||
return 0
|
return 0
|
||||||
return int(d.Version)
|
return int(d.Version)
|
||||||
|
|
||||||
def _upload_request(self, zdoc: ZipDocument) -> dict:
|
def _upload_request(self, zip_doc: ZipDocument) -> str:
|
||||||
zipFile, req = zdoc.create_request()
|
zip_file, req = zip_doc.create_request()
|
||||||
res = self.request("PUT", "/document-storage/json/2/upload/request",
|
res = self.request("PUT", "/document-storage/json/2/upload/request",
|
||||||
body=[req])
|
body=[req])
|
||||||
if not res.ok:
|
if not res.ok:
|
||||||
@@ -362,7 +361,7 @@ class Client(object):
|
|||||||
True if the folder is created.
|
True if the folder is created.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
zipFolder, req = folder.create_request()
|
zip_folder, req = folder.create_request()
|
||||||
res = self.request("PUT", "/document-storage/json/2/upload/request",
|
res = self.request("PUT", "/document-storage/json/2/upload/request",
|
||||||
body=[req])
|
body=[req])
|
||||||
if not res.ok:
|
if not res.ok:
|
||||||
@@ -373,7 +372,7 @@ class Client(object):
|
|||||||
if len(response) > 0:
|
if len(response) > 0:
|
||||||
dest = response[0].get("BlobURLPut", None)
|
dest = response[0].get("BlobURLPut", None)
|
||||||
if dest:
|
if dest:
|
||||||
res = self.request("PUT", dest, data=zipFolder.read())
|
res = self.request("PUT", dest, data=zip_folder.read())
|
||||||
else:
|
else:
|
||||||
raise ApiError(
|
raise ApiError(
|
||||||
"Cannot create a folder. because BlobURLPut is not set",
|
"Cannot create a folder. because BlobURLPut is not set",
|
||||||
@@ -382,7 +381,8 @@ class Client(object):
|
|||||||
self.update_metadata(folder)
|
self.update_metadata(folder)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def check_reponse(self, response: requests.Response):
|
@staticmethod
|
||||||
|
def check_response(response: requests.Response):
|
||||||
"""Check the response from an API Call
|
"""Check the response from an API Call
|
||||||
|
|
||||||
Does some sanity checking on the Response
|
Does some sanity checking on the Response
|
||||||
@@ -416,4 +416,3 @@ class Client(object):
|
|||||||
raise ApiError(
|
raise ApiError(
|
||||||
f"Got An invalid HTTP Response: {response.status_code}",
|
f"Got An invalid HTTP Response: {response.status_code}",
|
||||||
response=response)
|
response=response)
|
||||||
return True
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ DocumentOrFolder = Union[Document, Folder]
|
|||||||
class Collection(object):
|
class Collection(object):
|
||||||
"""A collection of meta items
|
"""A collection of meta items
|
||||||
|
|
||||||
This is basicly the content of the Remarkable Cloud.
|
This is basically the content of the Remarkable Cloud.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
items: A list containing the items.
|
items: A list containing the items.
|
||||||
@@ -21,59 +21,59 @@ class Collection(object):
|
|||||||
for i in items:
|
for i in items:
|
||||||
self.items.append(i)
|
self.items.append(i)
|
||||||
|
|
||||||
def add(self, docdict: dict) -> None:
|
def add(self, doc_dict: dict) -> None:
|
||||||
"""Add an item to the collection.
|
"""Add an item to the collection.
|
||||||
It wraps it in the correct class based on the Type parameter of the
|
It wraps it in the correct class based on the Type parameter of the
|
||||||
dict.
|
dict.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
docdict: A dict representing a document or folder.
|
doc_dict: A dict representing a document or folder.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if docdict.get("Type", None) == "DocumentType":
|
if doc_dict.get("Type", None) == "DocumentType":
|
||||||
self.add_document(docdict)
|
self.add_document(doc_dict)
|
||||||
elif docdict.get("Type", None) == "CollectionType":
|
elif doc_dict.get("Type", None) == "CollectionType":
|
||||||
self.add_folder(docdict)
|
self.add_folder(doc_dict)
|
||||||
else:
|
else:
|
||||||
raise TypeError("Unsupported type: {_type}"
|
raise TypeError("Unsupported type: {_type}"
|
||||||
.format(_type=docdict.get("Type", None)))
|
.format(_type=doc_dict.get("Type", None)))
|
||||||
|
|
||||||
def add_document(self, docdict: dict) -> None:
|
def add_document(self, doc_dict: dict) -> None:
|
||||||
"""Add a document to the collection
|
"""Add a document to the collection
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
docdict: A dict respresenting a document.
|
doc_dict: A dict representing a document.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.items.append(Document(**docdict))
|
self.items.append(Document(**doc_dict))
|
||||||
|
|
||||||
def add_folder(self, dirdict: dict) -> None:
|
def add_folder(self, dir_dict: dict) -> None:
|
||||||
"""Add a document to the collection
|
"""Add a document to the collection
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
dirdict: A dict respresenting a folder.
|
dir_dict: A dict representing a folder.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.items.append(Folder(**dirdict))
|
self.items.append(Folder(**dir_dict))
|
||||||
|
|
||||||
def parent(self, docorfolder: DocumentOrFolder) -> Folder:
|
def parent(self, doc_or_folder: DocumentOrFolder) -> Folder:
|
||||||
"""Returns the paren of a Document or Folder
|
"""Returns the paren of a Document or Folder
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
docorfolder: A document or folder to get the parent from
|
doc_or_folder: A document or folder to get the parent from
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The parent folder.
|
The parent folder.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
results = [i for i in self.items if i.ID == docorfolder.ID]
|
results = [i for i in self.items if i.ID == doc_or_folder.ID]
|
||||||
if len(results) > 0 and isinstance(results[0], Folder):
|
if len(results) > 0 and isinstance(results[0], Folder):
|
||||||
return results[0]
|
return results[0]
|
||||||
else:
|
else:
|
||||||
raise FolderNotFound("Could not found the parent of the document.")
|
raise FolderNotFound("Could not found the parent of the document.")
|
||||||
|
|
||||||
def children(self, folder: Folder = None) -> List[DocumentOrFolder]:
|
def children(self, folder: Folder = None) -> List[DocumentOrFolder]:
|
||||||
"""Get all the childern from a folder
|
"""Get all the children from a folder
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
folder: A folder where to get the children from. If None, this will
|
folder: A folder where to get the children from. If None, this will
|
||||||
|
|||||||
@@ -4,12 +4,11 @@ from zipfile import ZipFile, ZIP_DEFLATED
|
|||||||
import shutil
|
import shutil
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
import json
|
import json
|
||||||
from typing import NoReturn, TypeVar, List, Tuple
|
from typing import TypeVar, List, Tuple
|
||||||
from requests import Response
|
from requests import Response
|
||||||
from .meta import Meta
|
from .meta import Meta
|
||||||
|
|
||||||
BytesOrString = TypeVar("BytesOrString", BytesIO, str)
|
BytesOrString = TypeVar("BytesOrString", BytesIO, str)
|
||||||
BytesOrNone = TypeVar("BytesOrNone", BytesIO, None)
|
|
||||||
|
|
||||||
|
|
||||||
class RmPage(object):
|
class RmPage(object):
|
||||||
@@ -18,7 +17,7 @@ class RmPage(object):
|
|||||||
Contains the metadata, the page itself & thumbnail.
|
Contains the metadata, the page itself & thumbnail.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, page, metadata=None, order=0, thumbnail=None, ID=None):
|
def __init__(self, page, metadata=None, order=0, thumbnail=None, _id=None):
|
||||||
self.page = page
|
self.page = page
|
||||||
if metadata:
|
if metadata:
|
||||||
self.metadata = metadata
|
self.metadata = metadata
|
||||||
@@ -28,8 +27,8 @@ class RmPage(object):
|
|||||||
self.order = order
|
self.order = order
|
||||||
if thumbnail:
|
if thumbnail:
|
||||||
self.thumbnail = thumbnail
|
self.thumbnail = thumbnail
|
||||||
if ID:
|
if _id:
|
||||||
self.ID = ID
|
self.ID = _id
|
||||||
else:
|
else:
|
||||||
self.ID = str(uuid4())
|
self.ID = str(uuid4())
|
||||||
|
|
||||||
@@ -51,7 +50,7 @@ class Document(Meta):
|
|||||||
Attributes:
|
Attributes:
|
||||||
ID: Id of the meta object.
|
ID: Id of the meta object.
|
||||||
Version: The version of this object.
|
Version: The version of this object.
|
||||||
Success: If the last API Call was a succes.
|
Success: If the last API Call was a success.
|
||||||
BlobURLGet: The url to get the data blob from. Can be empty.
|
BlobURLGet: The url to get the data blob from. Can be empty.
|
||||||
BlobURLGetExpires: The expiration date of the Get url.
|
BlobURLGetExpires: The expiration date of the Get url.
|
||||||
BlobURLPut: The url to upload the data blob to. Can be empty.
|
BlobURLPut: The url to upload the data blob to. Can be empty.
|
||||||
@@ -179,17 +178,17 @@ class ZipDocument(object):
|
|||||||
rm: List[RmPage] = []
|
rm: List[RmPage] = []
|
||||||
ID = None
|
ID = None
|
||||||
|
|
||||||
def __init__(self, ID=None, doc=None, file=None):
|
def __init__(self, _id=None, doc=None, file=None):
|
||||||
"""Create a new instance of a ZipDocument
|
"""Create a new instance of a ZipDocument
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
ID: Can be left empty to generate one
|
_id: Can be left empty to generate one
|
||||||
doc: a raw pdf, epub or rm (.lines) file.
|
doc: a raw pdf, epub or rm (.lines) file.
|
||||||
file: a zipfile to convert from
|
file: a zipfile to convert from
|
||||||
"""
|
"""
|
||||||
if not ID:
|
if not _id:
|
||||||
ID = str(uuid4())
|
_id = str(uuid4())
|
||||||
self.ID = ID
|
self.ID = _id
|
||||||
if doc:
|
if doc:
|
||||||
ext = doc[-4:]
|
ext = doc[-4:]
|
||||||
if ext.endswith("pdf"):
|
if ext.endswith("pdf"):
|
||||||
@@ -207,7 +206,7 @@ class ZipDocument(object):
|
|||||||
elif ext.endswith("rm"):
|
elif ext.endswith("rm"):
|
||||||
self.content["fileType"] = "notebook"
|
self.content["fileType"] = "notebook"
|
||||||
with open(doc, 'rb') as fb:
|
with open(doc, 'rb') as fb:
|
||||||
self.rm.append(RmPage(page=BytesIO(doc.read())))
|
self.rm.append(RmPage(page=BytesIO(fb.read())))
|
||||||
name = os.path.splitext(os.path.basename(doc))[0]
|
name = os.path.splitext(os.path.basename(doc))[0]
|
||||||
self.metadata["VissibleName"] = name
|
self.metadata["VissibleName"] = name
|
||||||
|
|
||||||
@@ -316,10 +315,8 @@ class ZipDocument(object):
|
|||||||
pages = [x for x in zf.namelist()
|
pages = [x for x in zf.namelist()
|
||||||
if x.startswith(f"{self.ID}/") and x.endswith('.rm')]
|
if x.startswith(f"{self.ID}/") and x.endswith('.rm')]
|
||||||
for p in pages:
|
for p in pages:
|
||||||
pagenumber = int(p.replace(f"{self.ID}/", "")
|
page_number = int(p.replace(f"{self.ID}/", "")
|
||||||
.replace(".rm", ""))
|
.replace(".rm", ""))
|
||||||
page = BytesIO()
|
|
||||||
thumbnail = BytesIO()
|
|
||||||
with zf.open(p, 'r') as rm:
|
with zf.open(p, 'r') as rm:
|
||||||
page = BytesIO(rm.read())
|
page = BytesIO(rm.read())
|
||||||
page.seek(0)
|
page.seek(0)
|
||||||
@@ -331,34 +328,34 @@ class ZipDocument(object):
|
|||||||
thumbnail = BytesIO(tn.read())
|
thumbnail = BytesIO(tn.read())
|
||||||
thumbnail.seek(0)
|
thumbnail.seek(0)
|
||||||
|
|
||||||
self.rm.append(RmPage(page, metadata, pagenumber, thumbnail,
|
self.rm.append(RmPage(page, metadata, page_number, thumbnail,
|
||||||
self.ID))
|
self.ID))
|
||||||
|
|
||||||
self.zipfile.seek(0)
|
self.zipfile.seek(0)
|
||||||
|
|
||||||
|
|
||||||
def from_zip(ID: str, file: str) -> ZipDocument:
|
def from_zip(_id: str, file: str) -> ZipDocument:
|
||||||
"""Return A ZipDocument from a zipfile.
|
"""Return A ZipDocument from a zipfile.
|
||||||
|
|
||||||
Create a ZipDocument instance from a zipfile.
|
Create a ZipDocument instance from a zipfile.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
ID: The object ID this zipfile represents.
|
_id: The object ID this zipfile represents.
|
||||||
file: the filename of the zipfile.
|
file: the filename of the zipfile.
|
||||||
Returns:
|
Returns:
|
||||||
An instance of the supplied zipfile.
|
An instance of the supplied zipfile.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return ZipDocument(ID, file=file)
|
return ZipDocument(_id, file=file)
|
||||||
|
|
||||||
|
|
||||||
def from_request_stream(ID: str, stream: Response) -> ZipDocument:
|
def from_request_stream(_id: str, stream: Response) -> ZipDocument:
|
||||||
"""Return a ZipDocument from a request stream containing a zipfile.
|
"""Return a ZipDocument from a request stream containing a zipfile.
|
||||||
|
|
||||||
This is used with the BlobGETUrl from a :class:`rmapy.document.Document`.
|
This is used with the BlobGETUrl from a :class:`rmapy.document.Document`.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
ID: The object ID this zipfile represents.
|
_id: The object ID this zipfile represents.
|
||||||
stream: a stream containing the zipfile.
|
stream: a stream containing the zipfile.
|
||||||
Returns:
|
Returns:
|
||||||
the object of the downloaded zipfile.
|
the object of the downloaded zipfile.
|
||||||
@@ -367,6 +364,6 @@ def from_request_stream(ID: str, stream: Response) -> ZipDocument:
|
|||||||
tmp = BytesIO()
|
tmp = BytesIO()
|
||||||
for chunk in stream.iter_content(chunk_size=8192):
|
for chunk in stream.iter_content(chunk_size=8192):
|
||||||
tmp.write(chunk)
|
tmp.write(chunk)
|
||||||
zd = ZipDocument(ID=ID)
|
zd = ZipDocument(_id=_id)
|
||||||
zd.load(tmp)
|
zd.load(tmp)
|
||||||
return zd
|
return zd
|
||||||
|
|||||||
@@ -27,4 +27,3 @@ class ApiError(Exception):
|
|||||||
def __init__(self, msg, response=None):
|
def __init__(self, msg, response=None):
|
||||||
self.response = response
|
self.response = response
|
||||||
super(ApiError, self).__init__(msg)
|
super(ApiError, self).__init__(msg)
|
||||||
|
|
||||||
|
|||||||
@@ -6,20 +6,21 @@ from zipfile import ZipFile, ZIP_DEFLATED
|
|||||||
from .const import RFC3339Nano
|
from .const import RFC3339Nano
|
||||||
from typing import Tuple, Optional
|
from typing import Tuple, Optional
|
||||||
|
|
||||||
|
|
||||||
class ZipFolder(object):
|
class ZipFolder(object):
|
||||||
"""A dummy zipfile to create a folder
|
"""A dummy zipfile to create a folder
|
||||||
|
|
||||||
This is needed to create a folder on the Remarkable Cloud
|
This is needed to create a folder on the Remarkable Cloud
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, ID: str):
|
def __init__(self, _id: str):
|
||||||
"""Creates a zipfile in memory
|
"""Creates a zipfile in memory
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
ID: the ID to create a zipFolder for
|
_id: the ID to create a zipFolder for
|
||||||
"""
|
"""
|
||||||
super(ZipFolder, self).__init__()
|
super(ZipFolder, self).__init__()
|
||||||
self.ID = ID
|
self.ID = _id
|
||||||
self.file = BytesIO()
|
self.file = BytesIO()
|
||||||
self.Version = 1
|
self.Version = 1
|
||||||
with ZipFile(self.file, 'w', ZIP_DEFLATED) as zf:
|
with ZipFile(self.file, 'w', ZIP_DEFLATED) as zf:
|
||||||
@@ -37,7 +38,7 @@ class Folder(Meta):
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
name: An optional name for this folder. In the end, a name is
|
name: An optional name for this folder. In the end, a name is
|
||||||
really needed, but can be ommitted to set a later time.
|
really needed, but can be omitted to set a later time.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
super(Folder, self).__init__(**kwargs)
|
super(Folder, self).__init__(**kwargs)
|
||||||
@@ -48,9 +49,9 @@ class Folder(Meta):
|
|||||||
self.ID = str(uuid4())
|
self.ID = str(uuid4())
|
||||||
|
|
||||||
def create_request(self) -> Tuple[BytesIO, dict]:
|
def create_request(self) -> Tuple[BytesIO, dict]:
|
||||||
"""Prepares the nessesary parameters to create this folder.
|
"""Prepares the necessary parameters to create this folder.
|
||||||
|
|
||||||
This creates a ZipFolder & the nessesary json body to
|
This creates a ZipFolder & the necessary json body to
|
||||||
create an upload request.
|
create an upload request.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -61,9 +62,9 @@ class Folder(Meta):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def update_request(self) -> dict:
|
def update_request(self) -> dict:
|
||||||
"""Perpares the nessesary parameters to update a folder.
|
"""Prepares the necessary parameters to update a folder.
|
||||||
|
|
||||||
This sets some parameters in the datastructure to submit to the API.
|
This sets some parameters in the data structure to submit to the API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
data = self.to_dict()
|
data = self.to_dict()
|
||||||
@@ -76,6 +77,3 @@ class Folder(Meta):
|
|||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return self.__str__()
|
return self.__str__()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ class Meta(object):
|
|||||||
Attributes:
|
Attributes:
|
||||||
ID: Id of the meta object.
|
ID: Id of the meta object.
|
||||||
Version: The version of this object.
|
Version: The version of this object.
|
||||||
Success: If the last API Call was a succes.
|
Success: If the last API Call was a success.
|
||||||
BlobURLGet: The url to get the data blob from. Can be empty.
|
BlobURLGet: The url to get the data blob from. Can be empty.
|
||||||
BlobURLGetExpires: The expiration date of the Get url.
|
BlobURLGetExpires: The expiration date of the Get url.
|
||||||
BlobURLPut: The url to upload the data blob to. Can be empty.
|
BlobURLPut: The url to upload the data blob to. Can be empty.
|
||||||
@@ -26,7 +26,7 @@ class Meta(object):
|
|||||||
ID = ""
|
ID = ""
|
||||||
Version = 0
|
Version = 0
|
||||||
Message = ""
|
Message = ""
|
||||||
Succes = True
|
Success = True
|
||||||
BlobURLGet = ""
|
BlobURLGet = ""
|
||||||
BlobURLGetExpires = ""
|
BlobURLGetExpires = ""
|
||||||
BlobURLPut = ""
|
BlobURLPut = ""
|
||||||
@@ -39,8 +39,8 @@ class Meta(object):
|
|||||||
Parent = ""
|
Parent = ""
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
kkeys = self.to_dict().keys()
|
k_keys = self.to_dict().keys()
|
||||||
for k in kkeys:
|
for k in k_keys:
|
||||||
setattr(self, k, kwargs.get(k, getattr(self, k)))
|
setattr(self, k, kwargs.get(k, getattr(self, k)))
|
||||||
|
|
||||||
def to_dict(self) -> dict:
|
def to_dict(self) -> dict:
|
||||||
@@ -56,7 +56,7 @@ class Meta(object):
|
|||||||
"ID": self.ID,
|
"ID": self.ID,
|
||||||
"Version": self.Version,
|
"Version": self.Version,
|
||||||
"Message": self.Message,
|
"Message": self.Message,
|
||||||
"Succes": self.Succes,
|
"Succes": self.Success,
|
||||||
"BlobURLGet": self.BlobURLGet,
|
"BlobURLGet": self.BlobURLGet,
|
||||||
"BlobURLGetExpires": self.BlobURLGetExpires,
|
"BlobURLGetExpires": self.BlobURLGetExpires,
|
||||||
"BlobURLPut": self.BlobURLPut,
|
"BlobURLPut": self.BlobURLPut,
|
||||||
|
|||||||
10
setup.py
10
setup.py
@@ -22,7 +22,7 @@ setup(
|
|||||||
# package, this name will be registered for you. It will determine how
|
# package, this name will be registered for you. It will determine how
|
||||||
# users can install this project, e.g.:
|
# users can install this project, e.g.:
|
||||||
#
|
#
|
||||||
# $ pip install sampleproject
|
# $ pip install sampleprojectk
|
||||||
#
|
#
|
||||||
# And where it will live on PyPI: https://pypi.org/project/sampleproject/
|
# And where it will live on PyPI: https://pypi.org/project/sampleproject/
|
||||||
#
|
#
|
||||||
@@ -37,7 +37,7 @@ setup(
|
|||||||
# For a discussion on single-sourcing the version across setup.py and the
|
# For a discussion on single-sourcing the version across setup.py and the
|
||||||
# project code, see
|
# project code, see
|
||||||
# https://packaging.python.org/en/latest/single_source_version.html
|
# https://packaging.python.org/en/latest/single_source_version.html
|
||||||
version='0.2.1', # Required
|
version='0.2.2', # Required
|
||||||
|
|
||||||
# This is a one-line description or tagline of what your project does. This
|
# This is a one-line description or tagline of what your project does. This
|
||||||
# corresponds to the "Summary" metadata field:
|
# corresponds to the "Summary" metadata field:
|
||||||
@@ -88,8 +88,8 @@ setup(
|
|||||||
# 3 - Alpha
|
# 3 - Alpha
|
||||||
# 4 - Beta
|
# 4 - Beta
|
||||||
# 5 - Production/Stable
|
# 5 - Production/Stable
|
||||||
'Development Status :: 3 - Alpha',
|
#'Development Status :: 3 - Alpha',
|
||||||
#'Development Status :: 4 - Beta',
|
'Development Status :: 4 - Beta',
|
||||||
#'Development Status :: 5 - Production/Stable'
|
#'Development Status :: 5 - Production/Stable'
|
||||||
#'Development Status :: 6 - Mature'
|
#'Development Status :: 6 - Mature'
|
||||||
# Indicate who your project is intended for
|
# Indicate who your project is intended for
|
||||||
@@ -180,7 +180,7 @@ setup(
|
|||||||
#
|
#
|
||||||
# For example, the following would provide a command called `sample` which
|
# For example, the following would provide a command called `sample` which
|
||||||
# executes the function `main` from this package when invoked:
|
# executes the function `main` from this package when invoked:
|
||||||
# TODO
|
# TODO Create a command line tool
|
||||||
# entry_points={ # Optional
|
# entry_points={ # Optional
|
||||||
# 'console_scripts': [
|
# 'console_scripts': [
|
||||||
# 'sample=sample:main',
|
# 'sample=sample:main',
|
||||||
|
|||||||
Reference in New Issue
Block a user