mirror of
https://github.com/subutux/rmapy.git
synced 2025-12-08 22:53:25 +00:00
Added documentation
This commit is contained in:
@@ -32,7 +32,10 @@ release = '0.1'
|
|||||||
# ones.
|
# ones.
|
||||||
|
|
||||||
|
|
||||||
extensions = ['sphinx.ext.napoleon']
|
extensions = [
|
||||||
|
'sphinx.ext.napoleon',
|
||||||
|
'sphinx_autodoc_typehints'
|
||||||
|
]
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ['_templates']
|
templates_path = ['_templates']
|
||||||
@@ -54,3 +57,16 @@ html_theme = 'alabaster'
|
|||||||
# relative to this directory. They are copied after the builtin static files,
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
html_static_path = ['_static']
|
html_static_path = ['_static']
|
||||||
|
|
||||||
|
|
||||||
|
napoleon_google_docstring = True
|
||||||
|
napoleon_numpy_docstring = True
|
||||||
|
napoleon_include_init_with_doc = False
|
||||||
|
napoleon_include_private_with_doc = False
|
||||||
|
napoleon_include_special_with_doc = True
|
||||||
|
napoleon_use_admonition_for_examples = False
|
||||||
|
napoleon_use_admonition_for_notes = False
|
||||||
|
napoleon_use_admonition_for_references = False
|
||||||
|
napoleon_use_ivar = False
|
||||||
|
napoleon_use_param = True
|
||||||
|
napoleon_use_rtype = True
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ Welcome to rmapi's documentation!
|
|||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
:caption: Contents:
|
|
||||||
|
rmapi
|
||||||
|
|
||||||
.. automodule:: rmapi
|
.. automodule:: rmapi
|
||||||
|
|
||||||
|
|||||||
@@ -297,12 +297,9 @@ class Client(object):
|
|||||||
|
|
||||||
This needs to be done in 3 steps:
|
This needs to be done in 3 steps:
|
||||||
|
|
||||||
1. Create an upload request for a new CollectionType meta object.
|
#. Create an upload request for a new CollectionType meta object.
|
||||||
|
#. Upload a zipfile with a *.content file containing an empty object.
|
||||||
2. Upload a zipfile with a *.content file containing
|
#. Update the meta object with the new name.
|
||||||
an empty object.
|
|
||||||
|
|
||||||
3. Update the meta object with the new name.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
folder: A folder instance.
|
folder: A folder instance.
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
from .document import Document
|
from .document import Document
|
||||||
from .folder import Folder
|
from .folder import Folder
|
||||||
from typing import NoReturn, TypeVar
|
from typing import NoReturn, TypeVar, List
|
||||||
|
from .exceptions import FolderNotFound
|
||||||
|
|
||||||
DocOrFolder = TypeVar('DocumentOrFolder', Document, Folder)
|
DocOrFolder = TypeVar('DocumentOrFolder', 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.
|
||||||
|
Attributes:
|
||||||
|
items: A list containing the items.
|
||||||
"""
|
"""
|
||||||
items = []
|
items = []
|
||||||
|
|
||||||
@@ -16,18 +20,36 @@ class Collection(object):
|
|||||||
self.items.append(i)
|
self.items.append(i)
|
||||||
|
|
||||||
def add(self, docdict: dict) -> NoReturn:
|
def add(self, docdict: dict) -> NoReturn:
|
||||||
|
"""Add an item to the collection.
|
||||||
|
It wraps it in the correct class based on the Type parameter of the
|
||||||
|
dict.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
docdict: A dict representing a document or folder.
|
||||||
|
"""
|
||||||
|
|
||||||
if docdict.get("Type", None) == "DocumentType":
|
if docdict.get("Type", None) == "DocumentType":
|
||||||
return self.addDocument(docdict)
|
return self.add_document(docdict)
|
||||||
elif docdict.get("Type", None) == "CollectionType":
|
elif docdict.get("Type", None) == "CollectionType":
|
||||||
return self.addFolder(docdict)
|
return self.add_folder(docdict)
|
||||||
else:
|
else:
|
||||||
raise TypeError("Unsupported type: {_type}"
|
raise TypeError("Unsupported type: {_type}"
|
||||||
.format(_type=docdict.get("Type", None)))
|
.format(_type=docdict.get("Type", None)))
|
||||||
|
|
||||||
def addDocument(self, docdict: dict) -> NoReturn:
|
def add_document(self, docdict: dict) -> NoReturn:
|
||||||
|
"""Add a document to the collection
|
||||||
|
|
||||||
|
Args:
|
||||||
|
docdict: A dict respresenting a document.
|
||||||
|
"""
|
||||||
self.items.append(Document(**docdict))
|
self.items.append(Document(**docdict))
|
||||||
|
|
||||||
def addFolder(self, dirdict: dict) -> NoReturn:
|
def add_folder(self, dirdict: dict) -> NoReturn:
|
||||||
|
"""Add a document to the collection
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dirdict: A dict respresenting a folder.
|
||||||
|
"""
|
||||||
self.items.append(Folder(**dirdict))
|
self.items.append(Folder(**dirdict))
|
||||||
|
|
||||||
def parent(self, docorfolder: DocOrFolder) -> Folder:
|
def parent(self, docorfolder: DocOrFolder) -> Folder:
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ from yaml import dump as yml_dump
|
|||||||
|
|
||||||
|
|
||||||
def load() -> dict:
|
def load() -> dict:
|
||||||
"""
|
"""Load the .rmapi config file
|
||||||
Load the .rmapi config file
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
config_file_path = Path.joinpath(Path.home(), ".rmapi")
|
config_file_path = Path.joinpath(Path.home(), ".rmapi")
|
||||||
@@ -18,8 +17,11 @@ def load() -> dict:
|
|||||||
|
|
||||||
|
|
||||||
def dump(config: dict) -> True:
|
def dump(config: dict) -> True:
|
||||||
"""
|
"""Dump config to the .rmapi config file
|
||||||
Dump config to the .rmapi config file
|
|
||||||
|
Args:
|
||||||
|
config: A dict containing data to dump to the .rmapi
|
||||||
|
config file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
config_file_path = Path.joinpath(Path.home(), ".rmapi")
|
config_file_path = Path.joinpath(Path.home(), ".rmapi")
|
||||||
|
|||||||
@@ -3,13 +3,36 @@ 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
|
from typing import NoReturn, TypeVar
|
||||||
from requests import Response
|
from requests import Response
|
||||||
|
|
||||||
|
BytesOrString = TypeVar("BytesOrString", BytesIO, str)
|
||||||
|
|
||||||
|
|
||||||
class Document(object):
|
class Document(object):
|
||||||
""" Document represents a real object expected in most
|
""" Document represents a real object expected in most
|
||||||
calls by the remarkable API"""
|
calls by the remarkable API
|
||||||
|
|
||||||
|
This contains the metadata from a document.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
ID: Id of the meta object.
|
||||||
|
Version: The version of this object.
|
||||||
|
Success: If the last API Call was a succes.
|
||||||
|
BlobURLGet: The url to get the data blob from. Can be empty.
|
||||||
|
BlobURLGetExpires: The expiration date of the Get url.
|
||||||
|
BlobURLPut: The url to upload the data blob to. Can be empty.
|
||||||
|
BlobURLPutExpires: The expiration date of the Put url.
|
||||||
|
ModifiedClient: When the last change was by the client.
|
||||||
|
Type: Currently there are only 2 known types: DocumentType &
|
||||||
|
CollectionType.
|
||||||
|
VissibleName: The human name of the object.
|
||||||
|
CurrentPage: The current selected page of the object.
|
||||||
|
Bookmarked: If the object is bookmarked.
|
||||||
|
Parent: If empty, this object is is the root folder. This can be an ID
|
||||||
|
of a CollectionType.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
ID = ""
|
ID = ""
|
||||||
Version = 0
|
Version = 0
|
||||||
@@ -31,7 +54,15 @@ class Document(object):
|
|||||||
for k in kkeys:
|
for k in kkeys:
|
||||||
setattr(self, k, kwargs.get(k, getattr(self, k)))
|
setattr(self, k, kwargs.get(k, getattr(self, k)))
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self) -> dict:
|
||||||
|
"""Return a dict representation of this object.
|
||||||
|
|
||||||
|
Used for API Calls.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
a dict of the current object.
|
||||||
|
"""
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"ID": self.ID,
|
"ID": self.ID,
|
||||||
"Version": self.Version,
|
"Version": self.Version,
|
||||||
@@ -50,20 +81,23 @@ class Document(object):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
"""String representation of this object"""
|
||||||
return f"<rmapi.document.Document {self.ID}>"
|
return f"<rmapi.document.Document {self.ID}>"
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
"""String representation of this object"""
|
||||||
return self.__str__()
|
return self.__str__()
|
||||||
|
|
||||||
|
|
||||||
class ZipDocument(object):
|
class ZipDocument(object):
|
||||||
"""
|
"""
|
||||||
Here is the content of an archive retried on the tablet as example:
|
Here is the content of an archive retried on the tablet as example:
|
||||||
384327f5-133e-49c8-82ff-30aa19f3cfa40.content
|
|
||||||
384327f5-133e-49c8-82ff-30aa19f3cfa40-metadata.json
|
* 384327f5-133e-49c8-82ff-30aa19f3cfa40.content
|
||||||
384327f5-133e-49c8-82ff-30aa19f3cfa40.rm
|
* 384327f5-133e-49c8-82ff-30aa19f3cfa40-metadata.json
|
||||||
384327f5-133e-49c8-82ff-30aa19f3cfa40.pagedata
|
* 384326f5-133e-49c8-82ff-30aa19f3cfa40.rm
|
||||||
384327f5-133e-49c8-82ff-30aa19f3cfa40.thumbnails/0.jpg
|
* 384327f5-133e-49c8-82ff-30aa19f3cfa40.pagedata
|
||||||
|
* 384327f5-133e-49c8-82ff-30aa19f3cfa40.thumbnails/0.jpg
|
||||||
|
|
||||||
As the .zip file from remarkable is simply a normal .zip file
|
As the .zip file from remarkable is simply a normal .zip file
|
||||||
containing specific file formats, this package is a helper to
|
containing specific file formats, this package is a helper to
|
||||||
@@ -76,6 +110,16 @@ class ZipDocument(object):
|
|||||||
|
|
||||||
You can find some help about the format at the following URL:
|
You can find some help about the format at the following URL:
|
||||||
https://remarkablewiki.com/tech/filesystem
|
https://remarkablewiki.com/tech/filesystem
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
content: Sane defaults for the .content file in the zip.
|
||||||
|
metadata: parameters describing this blob.
|
||||||
|
pagedata: the content of the .pagedata file.
|
||||||
|
zipfile: The raw zipfile in memory.
|
||||||
|
pdf: the raw pdf file if there is one.
|
||||||
|
epub: the raw epub file if there is one.
|
||||||
|
rm: A list of :class:rmapi.document.RmPage in this zip.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
content = {
|
content = {
|
||||||
"ExtraMetadata": {
|
"ExtraMetadata": {
|
||||||
@@ -114,6 +158,7 @@ class ZipDocument(object):
|
|||||||
"M33": 1,
|
"M33": 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata = {
|
metadata = {
|
||||||
"deleted": False,
|
"deleted": False,
|
||||||
"lastModified": "1568368808000",
|
"lastModified": "1568368808000",
|
||||||
@@ -136,6 +181,13 @@ class ZipDocument(object):
|
|||||||
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
|
||||||
|
|
||||||
|
Args:
|
||||||
|
ID: Can be left empty to generate one
|
||||||
|
doc: a raw pdf, epub or rm (.lines) file.
|
||||||
|
file: a zipfile to convert from
|
||||||
|
"""
|
||||||
if not ID:
|
if not ID:
|
||||||
ID = str(uuid4())
|
ID = str(uuid4())
|
||||||
self.ID = ID
|
self.ID = ID
|
||||||
@@ -160,15 +212,22 @@ class ZipDocument(object):
|
|||||||
if file:
|
if file:
|
||||||
self.load(file)
|
self.load(file)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
|
"""string representation of this class"""
|
||||||
return f"<rmapi.document.ZipDocument {self.ID}>"
|
return f"<rmapi.document.ZipDocument {self.ID}>"
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
|
"""string representation of this class"""
|
||||||
return self.__str__()
|
return self.__str__()
|
||||||
|
|
||||||
def dump(self, file):
|
def dump(self, file: str) -> NoReturn:
|
||||||
"""
|
"""Dump the contents of ZipDocument back to a zip file.
|
||||||
Dump the contents of ZipDocument back to a zip file
|
|
||||||
|
This builds a zipfile to upload back to the Remarkable Cloud.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file: Where to save the zipfile
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with ZipFile(f"{file}.zip", "w", ZIP_DEFLATED) as zf:
|
with ZipFile(f"{file}.zip", "w", ZIP_DEFLATED) as zf:
|
||||||
@@ -198,10 +257,16 @@ class ZipDocument(object):
|
|||||||
zf.writestr(f"{self.ID}.thumbnails/{page.order}.jpg",
|
zf.writestr(f"{self.ID}.thumbnails/{page.order}.jpg",
|
||||||
page.thumbnail.read())
|
page.thumbnail.read())
|
||||||
|
|
||||||
def load(self, file) -> NoReturn:
|
def load(self, file: BytesOrString) -> NoReturn:
|
||||||
"""
|
"""Load a zipfile into this class.
|
||||||
Fill in the defaults from the given ZIP
|
|
||||||
|
Extracts the zipfile and reads in the contents.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file: A string of a file location or a BytesIO instance of a raw
|
||||||
|
zipfile
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.zipfile = BytesIO()
|
self.zipfile = BytesIO()
|
||||||
self.zipfile.seek(0)
|
self.zipfile.seek(0)
|
||||||
if isinstance(file, str):
|
if isinstance(file, str):
|
||||||
@@ -265,7 +330,11 @@ class ZipDocument(object):
|
|||||||
|
|
||||||
|
|
||||||
class RmPage(object):
|
class RmPage(object):
|
||||||
"""A Remarkable Page"""
|
"""A Remarkable Page
|
||||||
|
|
||||||
|
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:
|
||||||
@@ -281,24 +350,42 @@ class RmPage(object):
|
|||||||
else:
|
else:
|
||||||
self.ID = str(uuid4())
|
self.ID = str(uuid4())
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
|
"""String representation of this object"""
|
||||||
return f"<rmapi.document.RmPage {self.order} for {self.ID}>"
|
return f"<rmapi.document.RmPage {self.order} for {self.ID}>"
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
|
"""String representation of this object"""
|
||||||
return self.__str__()
|
return self.__str__()
|
||||||
|
|
||||||
|
|
||||||
def from_zip(ID: str, file: str) -> ZipDocument:
|
def from_zip(ID: str, file: str) -> ZipDocument:
|
||||||
|
"""Return A ZipDocument from a zipfile.
|
||||||
|
|
||||||
|
Create a ZipDocument instance from a zipfile.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
ID: The object ID this zipfile represents.
|
||||||
|
file: the filename of the zipfile.
|
||||||
|
Returns:
|
||||||
|
An instance of the supplied zipfile.
|
||||||
"""
|
"""
|
||||||
Return A ZipDocument from a 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.
|
||||||
|
|
||||||
|
This is used with the BlobGETUrl from a :class:`rmapi.document.Document`.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
ID: The object ID this zipfile represents.
|
||||||
|
stream: a stream containing the zipfile.
|
||||||
|
Returns:
|
||||||
|
the object of the downloaded zipfile.
|
||||||
"""
|
"""
|
||||||
Return a ZipDocument from a request stream containing a zipfile.
|
|
||||||
"""
|
|
||||||
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)
|
||||||
|
|||||||
Reference in New Issue
Block a user