tdrpa.tdworker 1.2.13.2__py312-none-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tdrpa/_tdxlwings/__init__.py +193 -0
- tdrpa/_tdxlwings/__pycache__/__init__.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/__init__.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/_win32patch.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/_win32patch.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/_xlwindows.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/_xlwindows.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/apps.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/apps.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/base_classes.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/base_classes.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/com_server.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/com_server.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/constants.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/constants.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/expansion.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/expansion.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/main.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/main.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/udfs.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/udfs.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/utils.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/utils.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/_win32patch.py +90 -0
- tdrpa/_tdxlwings/_xlmac.py +2240 -0
- tdrpa/_tdxlwings/_xlwindows.py +2518 -0
- tdrpa/_tdxlwings/addin/Dictionary.cls +474 -0
- tdrpa/_tdxlwings/addin/IWebAuthenticator.cls +71 -0
- tdrpa/_tdxlwings/addin/WebClient.cls +772 -0
- tdrpa/_tdxlwings/addin/WebHelpers.bas +3203 -0
- tdrpa/_tdxlwings/addin/WebRequest.cls +875 -0
- tdrpa/_tdxlwings/addin/WebResponse.cls +453 -0
- tdrpa/_tdxlwings/addin/xlwings.xlam +0 -0
- tdrpa/_tdxlwings/apps.py +35 -0
- tdrpa/_tdxlwings/base_classes.py +1092 -0
- tdrpa/_tdxlwings/cli.py +1306 -0
- tdrpa/_tdxlwings/com_server.py +385 -0
- tdrpa/_tdxlwings/constants.py +3080 -0
- tdrpa/_tdxlwings/conversion/__init__.py +103 -0
- tdrpa/_tdxlwings/conversion/framework.py +147 -0
- tdrpa/_tdxlwings/conversion/numpy_conv.py +34 -0
- tdrpa/_tdxlwings/conversion/pandas_conv.py +184 -0
- tdrpa/_tdxlwings/conversion/standard.py +321 -0
- tdrpa/_tdxlwings/expansion.py +83 -0
- tdrpa/_tdxlwings/ext/__init__.py +3 -0
- tdrpa/_tdxlwings/ext/sql.py +73 -0
- tdrpa/_tdxlwings/html/xlwings-alert.html +71 -0
- tdrpa/_tdxlwings/js/xlwings.js +577 -0
- tdrpa/_tdxlwings/js/xlwings.ts +729 -0
- tdrpa/_tdxlwings/mac_dict.py +6399 -0
- tdrpa/_tdxlwings/main.py +5205 -0
- tdrpa/_tdxlwings/mistune/__init__.py +63 -0
- tdrpa/_tdxlwings/mistune/block_parser.py +366 -0
- tdrpa/_tdxlwings/mistune/inline_parser.py +216 -0
- tdrpa/_tdxlwings/mistune/markdown.py +84 -0
- tdrpa/_tdxlwings/mistune/renderers.py +220 -0
- tdrpa/_tdxlwings/mistune/scanner.py +121 -0
- tdrpa/_tdxlwings/mistune/util.py +41 -0
- tdrpa/_tdxlwings/pro/__init__.py +40 -0
- tdrpa/_tdxlwings/pro/_xlcalamine.py +536 -0
- tdrpa/_tdxlwings/pro/_xlofficejs.py +146 -0
- tdrpa/_tdxlwings/pro/_xlremote.py +1293 -0
- tdrpa/_tdxlwings/pro/custom_functions_code.js +150 -0
- tdrpa/_tdxlwings/pro/embedded_code.py +60 -0
- tdrpa/_tdxlwings/pro/udfs_officejs.py +549 -0
- tdrpa/_tdxlwings/pro/utils.py +199 -0
- tdrpa/_tdxlwings/quickstart.xlsm +0 -0
- tdrpa/_tdxlwings/quickstart_addin.xlam +0 -0
- tdrpa/_tdxlwings/quickstart_addin_ribbon.xlam +0 -0
- tdrpa/_tdxlwings/quickstart_fastapi/main.py +47 -0
- tdrpa/_tdxlwings/quickstart_fastapi/requirements.txt +3 -0
- tdrpa/_tdxlwings/quickstart_standalone.xlsm +0 -0
- tdrpa/_tdxlwings/reports.py +12 -0
- tdrpa/_tdxlwings/rest/__init__.py +1 -0
- tdrpa/_tdxlwings/rest/api.py +368 -0
- tdrpa/_tdxlwings/rest/serializers.py +103 -0
- tdrpa/_tdxlwings/server.py +14 -0
- tdrpa/_tdxlwings/udfs.py +775 -0
- tdrpa/_tdxlwings/utils.py +777 -0
- tdrpa/_tdxlwings/xlwings-0.31.6.applescript +30 -0
- tdrpa/_tdxlwings/xlwings.bas +2061 -0
- tdrpa/_tdxlwings/xlwings_custom_addin.bas +2042 -0
- tdrpa/_tdxlwings/xlwingslib.cp38-win_amd64.pyd +0 -0
- tdrpa/tdworker/__init__.pyi +12 -0
- tdrpa/tdworker/_clip.pyi +50 -0
- tdrpa/tdworker/_excel.pyi +743 -0
- tdrpa/tdworker/_file.pyi +77 -0
- tdrpa/tdworker/_img.pyi +226 -0
- tdrpa/tdworker/_network.pyi +94 -0
- tdrpa/tdworker/_os.pyi +47 -0
- tdrpa/tdworker/_sp.pyi +21 -0
- tdrpa/tdworker/_w.pyi +129 -0
- tdrpa/tdworker/_web.pyi +995 -0
- tdrpa/tdworker/_winE.pyi +228 -0
- tdrpa/tdworker/_winK.pyi +74 -0
- tdrpa/tdworker/_winM.pyi +117 -0
- tdrpa/tdworker.cp312-win_amd64.pyd +0 -0
- tdrpa_tdworker-1.2.13.2.dist-info/METADATA +38 -0
- tdrpa_tdworker-1.2.13.2.dist-info/RECORD +101 -0
- tdrpa_tdworker-1.2.13.2.dist-info/WHEEL +5 -0
- tdrpa_tdworker-1.2.13.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,199 @@
|
|
1
|
+
"""
|
2
|
+
Required Notice: Copyright (C) Zoomer Analytics GmbH.
|
3
|
+
|
4
|
+
xlwings PRO is dual-licensed under one of the following licenses:
|
5
|
+
|
6
|
+
* PolyForm Noncommercial License 1.0.0 (for noncommercial use):
|
7
|
+
https://polyformproject.org/licenses/noncommercial/1.0.0
|
8
|
+
* xlwings PRO License (for commercial use):
|
9
|
+
https://github.com/xlwings/xlwings/blob/main/LICENSE_PRO.txt
|
10
|
+
|
11
|
+
Commercial licenses can be purchased at https://www.xlwings.org
|
12
|
+
"""
|
13
|
+
|
14
|
+
import atexit
|
15
|
+
import base64
|
16
|
+
import binascii
|
17
|
+
import datetime as dt
|
18
|
+
import glob
|
19
|
+
import hashlib
|
20
|
+
import hmac
|
21
|
+
import json
|
22
|
+
import os
|
23
|
+
import shutil
|
24
|
+
import tempfile
|
25
|
+
import time
|
26
|
+
import warnings
|
27
|
+
from functools import lru_cache
|
28
|
+
|
29
|
+
import tdrpa._tdxlwings as xlwings
|
30
|
+
|
31
|
+
from ..utils import read_config_sheet
|
32
|
+
|
33
|
+
try:
|
34
|
+
from cryptography.fernet import Fernet, InvalidToken
|
35
|
+
except ImportError:
|
36
|
+
Fernet = None
|
37
|
+
InvalidToken = None
|
38
|
+
|
39
|
+
|
40
|
+
class LicenseHandler:
|
41
|
+
@staticmethod
|
42
|
+
def get_cipher():
|
43
|
+
try:
|
44
|
+
return Fernet('kS2PJbJuXbNXC-sY9XFGg01OmEKuCnKAH0zyMNWWm8c=')
|
45
|
+
except (TypeError, ValueError):
|
46
|
+
raise xlwings.LicenseError(
|
47
|
+
"Couldn't validate xlwings license key."
|
48
|
+
) from None
|
49
|
+
|
50
|
+
@staticmethod
|
51
|
+
def get_license():
|
52
|
+
# Sheet config (only used by RunPython, UDFs use env var)
|
53
|
+
try:
|
54
|
+
sheet_license_key = read_config_sheet(xlwings.Book.caller()).get(
|
55
|
+
"LICENSE_KEY"
|
56
|
+
)
|
57
|
+
if sheet_license_key:
|
58
|
+
return sheet_license_key
|
59
|
+
except: # noqa: E722
|
60
|
+
pass
|
61
|
+
# User config file
|
62
|
+
config_file = xlwings.USER_CONFIG_FILE
|
63
|
+
if os.path.exists(config_file):
|
64
|
+
with open(config_file, "r") as f:
|
65
|
+
config = f.readlines()
|
66
|
+
key = None
|
67
|
+
for line in config:
|
68
|
+
if line.split(",")[0] == '"LICENSE_KEY"':
|
69
|
+
key = line.split(",")[1].strip()[1:-1]
|
70
|
+
if key:
|
71
|
+
return key
|
72
|
+
# Env Var - also used if LICENSE_KEY is in config sheet and called via UDF
|
73
|
+
if os.getenv("XLWINGS_LICENSE_KEY"):
|
74
|
+
return os.environ["XLWINGS_LICENSE_KEY"]
|
75
|
+
raise xlwings.LicenseError("Couldn't find an xlwings license key.")
|
76
|
+
|
77
|
+
@staticmethod
|
78
|
+
@lru_cache()
|
79
|
+
def validate_license(product, license_type=None):
|
80
|
+
key = LicenseHandler.get_license()
|
81
|
+
if key == "noncommercial":
|
82
|
+
return {"license_type": "noncommercial"}
|
83
|
+
if key.startswith("gA") and not Fernet:
|
84
|
+
# Legacy up to 0.27.12
|
85
|
+
raise ImportError(
|
86
|
+
"You are using a legacy xlwings license key that requires the "
|
87
|
+
"'cryptography' package. Either install it via 'pip install "
|
88
|
+
"cryptography' or contact us for a new license key that doesn't depend "
|
89
|
+
"on cryptography."
|
90
|
+
) from None
|
91
|
+
elif key.startswith("gA"):
|
92
|
+
cipher_suite = LicenseHandler.get_cipher()
|
93
|
+
try:
|
94
|
+
license_info = json.loads(cipher_suite.decrypt(key.encode()).decode())
|
95
|
+
except (binascii.Error, InvalidToken):
|
96
|
+
raise xlwings.LicenseError("Invalid xlwings license key.") from None
|
97
|
+
else:
|
98
|
+
signature = hmac.new(
|
99
|
+
'kS2PJbJuXbNXC-sY9XFGg01OmEKuCnKAH0zyMNWWm8c='.encode(),
|
100
|
+
key[:-5].encode(),
|
101
|
+
hashlib.sha256,
|
102
|
+
).hexdigest()
|
103
|
+
if signature[:5] != key[-5:]:
|
104
|
+
raise xlwings.LicenseError("Invalid xlwings license key.") from None
|
105
|
+
else:
|
106
|
+
try:
|
107
|
+
license_info = json.loads(
|
108
|
+
base64.urlsafe_b64decode(key[:-5]).decode()
|
109
|
+
)
|
110
|
+
except: # noqa: E722
|
111
|
+
raise xlwings.LicenseError("Invalid xlwings license key.") from None
|
112
|
+
try:
|
113
|
+
if (
|
114
|
+
license_type == "developer"
|
115
|
+
and license_info["license_type"] != "developer"
|
116
|
+
):
|
117
|
+
raise xlwings.LicenseError(
|
118
|
+
"You need a paid xlwings license key for this action."
|
119
|
+
)
|
120
|
+
except KeyError:
|
121
|
+
raise xlwings.LicenseError(
|
122
|
+
"You need a paid xlwings license key for this action."
|
123
|
+
) from None
|
124
|
+
if (
|
125
|
+
"valid_until" not in license_info.keys()
|
126
|
+
or "products" not in license_info.keys()
|
127
|
+
):
|
128
|
+
raise xlwings.LicenseError("Invalid xlwings license key format.") from None
|
129
|
+
license_valid_until = dt.datetime.strptime(
|
130
|
+
license_info["valid_until"], "%Y-%m-%d"
|
131
|
+
).date()
|
132
|
+
if dt.date.today() > license_valid_until:
|
133
|
+
raise xlwings.LicenseError(
|
134
|
+
"Your xlwings license expired on {}.".format(
|
135
|
+
license_valid_until.strftime("%Y-%m-%d")
|
136
|
+
)
|
137
|
+
) from None
|
138
|
+
if product not in license_info["products"]:
|
139
|
+
raise xlwings.LicenseError(
|
140
|
+
f"Your xlwings license key isn't valid for the '{product}' "
|
141
|
+
"functionality."
|
142
|
+
) from None
|
143
|
+
if (
|
144
|
+
"version" in license_info.keys()
|
145
|
+
and license_info["version"] != xlwings.__version__
|
146
|
+
):
|
147
|
+
raise xlwings.LicenseError(
|
148
|
+
"Your xlwings deploy key is only valid for v{0}. To use a different "
|
149
|
+
"version of xlwings, re-release your tool or generate a new deploy key "
|
150
|
+
"via 'xlwings license deploy'.".format(license_info["version"])
|
151
|
+
) from None
|
152
|
+
if (license_valid_until - dt.date.today()) < dt.timedelta(days=30):
|
153
|
+
warnings.warn(
|
154
|
+
f"Your xlwings license key expires in "
|
155
|
+
f"{(license_valid_until - dt.date.today()).days} days."
|
156
|
+
)
|
157
|
+
return license_info
|
158
|
+
|
159
|
+
@staticmethod
|
160
|
+
def create_deploy_key():
|
161
|
+
license_info = LicenseHandler.validate_license("pro", license_type="developer")
|
162
|
+
if license_info["license_type"] == "noncommercial":
|
163
|
+
return "noncommercial"
|
164
|
+
license_dict = json.dumps(
|
165
|
+
{
|
166
|
+
"version": xlwings.__version__,
|
167
|
+
"products": license_info["products"],
|
168
|
+
"valid_until": "2999-12-31",
|
169
|
+
"license_type": "deploy_key",
|
170
|
+
}
|
171
|
+
).encode()
|
172
|
+
if LicenseHandler.get_license().startswith("gA"):
|
173
|
+
# Legacy
|
174
|
+
cipher_suite = LicenseHandler.get_cipher()
|
175
|
+
return cipher_suite.encrypt(license_dict).decode()
|
176
|
+
else:
|
177
|
+
body = base64.urlsafe_b64encode(license_dict)
|
178
|
+
signature = hmac.new(
|
179
|
+
'kS2PJbJuXbNXC-sY9XFGg01OmEKuCnKAH0zyMNWWm8c='.encode(), body, hashlib.sha256
|
180
|
+
).hexdigest()
|
181
|
+
return f"{body.decode()}{signature[:5]}"
|
182
|
+
|
183
|
+
|
184
|
+
@lru_cache()
|
185
|
+
def get_embedded_code_temp_dir():
|
186
|
+
tmp_base_path = os.path.join(tempfile.gettempdir(), "xlwings")
|
187
|
+
os.makedirs(tmp_base_path, exist_ok=True)
|
188
|
+
try:
|
189
|
+
# HACK: Clean up directories that are older than 30 days
|
190
|
+
# This should be done in the C++ part when the Python process is killed
|
191
|
+
for subdir in glob.glob(tmp_base_path + "/*/"):
|
192
|
+
if os.path.getmtime(subdir) < time.time() - 30 * 86400:
|
193
|
+
shutil.rmtree(subdir, ignore_errors=True)
|
194
|
+
except Exception:
|
195
|
+
pass # we don't care if it fails
|
196
|
+
tempdir = tempfile.mkdtemp(dir=tmp_base_path)
|
197
|
+
# This only works for RunPython calls running outside the COM server
|
198
|
+
atexit.register(shutil.rmtree, tempdir)
|
199
|
+
return tempdir
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,47 @@
|
|
1
|
+
from fastapi import Body, FastAPI, status
|
2
|
+
from fastapi.middleware.cors import CORSMiddleware
|
3
|
+
from fastapi.responses import PlainTextResponse
|
4
|
+
|
5
|
+
import tdrpa._tdxlwings as xw
|
6
|
+
|
7
|
+
app = FastAPI()
|
8
|
+
|
9
|
+
|
10
|
+
@app.post("/hello")
|
11
|
+
def hello(data: dict = Body):
|
12
|
+
# Instantiate a Book object with the deserialized request body
|
13
|
+
with xw.Book(json=data) as book:
|
14
|
+
# Use xlwings as usual
|
15
|
+
sheet = book.sheets[0]
|
16
|
+
cell = sheet["A1"]
|
17
|
+
if cell.value == "Hello xlwings!":
|
18
|
+
cell.value = "Bye xlwings!"
|
19
|
+
else:
|
20
|
+
cell.value = "Hello xlwings!"
|
21
|
+
|
22
|
+
# Pass the following back as the response
|
23
|
+
return book.json()
|
24
|
+
|
25
|
+
|
26
|
+
@app.exception_handler(Exception)
|
27
|
+
async def exception_handler(request, exception):
|
28
|
+
# This handles all exceptions, so you may want to make this more restrictive
|
29
|
+
return PlainTextResponse(
|
30
|
+
str(exception), status_code=status.HTTP_500_INTERNAL_SERVER_ERROR
|
31
|
+
)
|
32
|
+
|
33
|
+
|
34
|
+
# Office Scripts and custom functions in Excel on the web require CORS
|
35
|
+
cors_app = CORSMiddleware(
|
36
|
+
app=app,
|
37
|
+
allow_origins="*",
|
38
|
+
allow_methods=["POST"],
|
39
|
+
allow_headers=["*"],
|
40
|
+
allow_credentials=True,
|
41
|
+
)
|
42
|
+
|
43
|
+
|
44
|
+
# if __name__ == "__main__":
|
45
|
+
# import uvicorn
|
46
|
+
#
|
47
|
+
# uvicorn.run("main:cors_app", host="127.0.0.1", port=8000, reload=True)
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
from .api import api, run # noqa: F401
|
@@ -0,0 +1,368 @@
|
|
1
|
+
# See also:
|
2
|
+
# https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/resources/excel
|
3
|
+
# TODO: proper exception handling in xlwings base package so it can be used here
|
4
|
+
import logging
|
5
|
+
import sys
|
6
|
+
|
7
|
+
from werkzeug.routing import PathConverter
|
8
|
+
|
9
|
+
import tdrpa._tdxlwings as xw
|
10
|
+
from tdrpa._tdxlwings.rest.serializers import (
|
11
|
+
serialize_app,
|
12
|
+
serialize_book,
|
13
|
+
serialize_chart,
|
14
|
+
serialize_name,
|
15
|
+
serialize_picture,
|
16
|
+
serialize_range,
|
17
|
+
serialize_shape,
|
18
|
+
serialize_sheet,
|
19
|
+
)
|
20
|
+
|
21
|
+
try:
|
22
|
+
from flask import Flask, abort, jsonify, request
|
23
|
+
except ImportError:
|
24
|
+
raise Exception(
|
25
|
+
"To use the xlwings REST API server, you need Flask>=1.0.0 installed."
|
26
|
+
)
|
27
|
+
|
28
|
+
|
29
|
+
api = Flask(__name__)
|
30
|
+
logger = logging.getLogger(__name__)
|
31
|
+
|
32
|
+
|
33
|
+
class EverythingConverter(PathConverter):
|
34
|
+
regex = ".*?"
|
35
|
+
|
36
|
+
|
37
|
+
if sys.platform.startswith("darwin"):
|
38
|
+
# Hack to allow leading slashes on Mac
|
39
|
+
api.url_map.converters["path"] = EverythingConverter
|
40
|
+
|
41
|
+
|
42
|
+
def get_book_object(fullname=None, name_or_ix=None, app_ix=None):
|
43
|
+
assert fullname is None or name_or_ix is None
|
44
|
+
if fullname:
|
45
|
+
try:
|
46
|
+
return xw.Book(fullname)
|
47
|
+
except Exception as e:
|
48
|
+
logger.exception(str(e))
|
49
|
+
abort(500, str(e))
|
50
|
+
elif name_or_ix:
|
51
|
+
if name_or_ix.isdigit():
|
52
|
+
name_or_ix = int(name_or_ix)
|
53
|
+
app = xw.apps[int(app_ix)] if app_ix else xw.apps.active
|
54
|
+
try:
|
55
|
+
return app.books[name_or_ix]
|
56
|
+
except KeyError as e:
|
57
|
+
logger.exception(str(e))
|
58
|
+
abort(500, "Couldn't find Book: " + str(e))
|
59
|
+
except Exception as e:
|
60
|
+
logger.exception(str(e))
|
61
|
+
abort(500, str(e))
|
62
|
+
|
63
|
+
|
64
|
+
def get_sheet_object(book, name_or_id):
|
65
|
+
if name_or_id.isdigit():
|
66
|
+
name_or_id = int(name_or_id)
|
67
|
+
return book.sheets[name_or_id]
|
68
|
+
|
69
|
+
|
70
|
+
@api.route("/apps", methods=["GET"])
|
71
|
+
def get_apps():
|
72
|
+
return jsonify(apps=[serialize_app(app) for app in xw.apps])
|
73
|
+
|
74
|
+
|
75
|
+
@api.route("/apps/<pid>", methods=["GET"])
|
76
|
+
def get_app(pid):
|
77
|
+
return jsonify(serialize_app(xw.apps[int(pid)]))
|
78
|
+
|
79
|
+
|
80
|
+
@api.route("/apps/<pid>/books", methods=["GET"])
|
81
|
+
@api.route("/books", methods=["GET"])
|
82
|
+
def get_books(pid=None):
|
83
|
+
books = xw.apps[int(pid)].books if pid else xw.books
|
84
|
+
return jsonify(books=[serialize_book(book) for book in books])
|
85
|
+
|
86
|
+
|
87
|
+
@api.route("/apps/<pid>/books/<book_name_or_ix>", methods=["GET"])
|
88
|
+
@api.route("/books/<book_name_or_ix>", methods=["GET"])
|
89
|
+
@api.route("/book/<path:fullname_or_name>", methods=["GET"])
|
90
|
+
def get_book(book_name_or_ix=None, fullname_or_name=None, pid=None):
|
91
|
+
book = get_book_object(fullname_or_name, book_name_or_ix, pid)
|
92
|
+
return jsonify(serialize_book(book))
|
93
|
+
|
94
|
+
|
95
|
+
@api.route("/apps/<pid>/books/<book_name_or_ix>/sheets", methods=["GET"])
|
96
|
+
@api.route("/books/<book_name_or_ix>/sheets", methods=["GET"])
|
97
|
+
@api.route("/book/<path:fullname_or_name>/sheets", methods=["GET"])
|
98
|
+
def get_sheets(book_name_or_ix=None, fullname_or_name=None, pid=None):
|
99
|
+
book = get_book_object(fullname_or_name, book_name_or_ix, pid)
|
100
|
+
return jsonify(sheets=[serialize_sheet(sheet) for sheet in book.sheets])
|
101
|
+
|
102
|
+
|
103
|
+
@api.route(
|
104
|
+
"/apps/<pid>/books/<book_name_or_ix>/sheets/<sheet_name_or_ix>", methods=["GET"]
|
105
|
+
)
|
106
|
+
@api.route("/books/<book_name_or_ix>/sheets/<sheet_name_or_ix>", methods=["GET"])
|
107
|
+
@api.route("/book/<path:fullname_or_name>/sheets/<sheet_name_or_ix>", methods=["GET"])
|
108
|
+
def get_sheet(sheet_name_or_ix, book_name_or_ix=None, fullname_or_name=None, pid=None):
|
109
|
+
book = get_book_object(fullname_or_name, book_name_or_ix, pid)
|
110
|
+
sheet = get_sheet_object(book, sheet_name_or_ix)
|
111
|
+
return jsonify(serialize_sheet(sheet))
|
112
|
+
|
113
|
+
|
114
|
+
@api.route(
|
115
|
+
"/apps/<pid>/books/<book_name_or_ix>/sheets/<sheet_name_or_ix>/range",
|
116
|
+
methods=["GET"],
|
117
|
+
)
|
118
|
+
@api.route("/books/<book_name_or_ix>/sheets/<sheet_name_or_ix>/range", methods=["GET"])
|
119
|
+
@api.route(
|
120
|
+
"/book/<path:fullname_or_name>/sheets/<sheet_name_or_ix>/range", methods=["GET"]
|
121
|
+
)
|
122
|
+
def get_range(sheet_name_or_ix, book_name_or_ix=None, fullname_or_name=None, pid=None):
|
123
|
+
book = get_book_object(fullname_or_name, book_name_or_ix, pid)
|
124
|
+
sheet = get_sheet_object(book, sheet_name_or_ix)
|
125
|
+
return jsonify(serialize_range(sheet.used_range))
|
126
|
+
|
127
|
+
|
128
|
+
@api.route(
|
129
|
+
"/apps/<pid>/books/<book_name_or_ix>/sheets/<sheet_name_or_ix>/range/<address>",
|
130
|
+
methods=["GET"],
|
131
|
+
)
|
132
|
+
@api.route(
|
133
|
+
"/books/<book_name_or_ix>/sheets/<sheet_name_or_ix>/range/<address>",
|
134
|
+
methods=["GET"],
|
135
|
+
)
|
136
|
+
@api.route(
|
137
|
+
"/book/<path:fullname_or_name>/sheets/<sheet_name_or_ix>/range/<address>",
|
138
|
+
methods=["GET"],
|
139
|
+
)
|
140
|
+
def get_range_address(
|
141
|
+
address, sheet_name_or_ix, book_name_or_ix=None, fullname_or_name=None, pid=None
|
142
|
+
):
|
143
|
+
book = get_book_object(fullname_or_name, book_name_or_ix, pid)
|
144
|
+
sheet = get_sheet_object(book, sheet_name_or_ix)
|
145
|
+
options = {k: v for k, v in request.args.items()}
|
146
|
+
return jsonify(serialize_range(sheet.range(address).options(**options)))
|
147
|
+
|
148
|
+
|
149
|
+
@api.route("/apps/<pid>/books/<book_name_or_ix>/names", methods=["GET"])
|
150
|
+
@api.route("/books/<book_name_or_ix>/names", methods=["GET"])
|
151
|
+
@api.route("/book/<path:fullname_or_name>/names", methods=["GET"])
|
152
|
+
def get_book_names(book_name_or_ix=None, fullname_or_name=None, pid=None):
|
153
|
+
book = get_book_object(fullname_or_name, book_name_or_ix, pid)
|
154
|
+
return jsonify(names=[serialize_name(name) for name in book.names])
|
155
|
+
|
156
|
+
|
157
|
+
@api.route("/apps/<pid>/books/<book_name_or_ix>/names/<name>", methods=["GET"])
|
158
|
+
@api.route("/books/<book_name_or_ix>/names/<name>", methods=["GET"])
|
159
|
+
@api.route("/book/<path:fullname_or_name>/names/<name>", methods=["GET"])
|
160
|
+
def get_book_name(name, book_name_or_ix=None, fullname_or_name=None, pid=None):
|
161
|
+
book = get_book_object(fullname_or_name, book_name_or_ix, pid)
|
162
|
+
return jsonify(serialize_name(book.names[name]))
|
163
|
+
|
164
|
+
|
165
|
+
@api.route("/apps/<pid>/books/<book_name_or_ix>/names/<name>/range", methods=["GET"])
|
166
|
+
@api.route("/books/<book_name_or_ix>/names/<name>/range", methods=["GET"])
|
167
|
+
@api.route("/book/<path:fullname_or_name>/names/<name>/range", methods=["GET"])
|
168
|
+
def get_book_name_range(name, book_name_or_ix=None, fullname_or_name=None, pid=None):
|
169
|
+
book = get_book_object(fullname_or_name, book_name_or_ix, pid)
|
170
|
+
return jsonify(serialize_range(book.names[name].refers_to_range))
|
171
|
+
|
172
|
+
|
173
|
+
@api.route(
|
174
|
+
"/apps/<pid>/books/<book_name_or_ix>/sheets/<sheet_name_or_ix>/names",
|
175
|
+
methods=["GET"],
|
176
|
+
)
|
177
|
+
@api.route("/books/<book_name_or_ix>/sheets/<sheet_name_or_ix>/names", methods=["GET"])
|
178
|
+
@api.route(
|
179
|
+
"/book/<path:fullname_or_name>/sheets/<sheet_name_or_ix>/names", methods=["GET"]
|
180
|
+
)
|
181
|
+
def get_sheet_names(
|
182
|
+
sheet_name_or_ix, book_name_or_ix=None, fullname_or_name=None, pid=None
|
183
|
+
):
|
184
|
+
book = get_book_object(fullname_or_name, book_name_or_ix, pid)
|
185
|
+
sheet = get_sheet_object(book, sheet_name_or_ix)
|
186
|
+
return jsonify(names=[serialize_name(name) for name in sheet.names])
|
187
|
+
|
188
|
+
|
189
|
+
@api.route(
|
190
|
+
"/apps/<pid>/books/<book_name_or_ix>/sheets/<sheet_name_or_ix>/names/<sheet_scope_name>",
|
191
|
+
methods=["GET"],
|
192
|
+
)
|
193
|
+
@api.route(
|
194
|
+
"/books/<book_name_or_ix>/sheets/<sheet_name_or_ix>/names/<sheet_scope_name>",
|
195
|
+
methods=["GET"],
|
196
|
+
)
|
197
|
+
@api.route(
|
198
|
+
"/book/<path:fullname_or_name>/sheets/<sheet_name_or_ix>/names/<sheet_scope_name>",
|
199
|
+
methods=["GET"],
|
200
|
+
)
|
201
|
+
def get_sheet_name(
|
202
|
+
sheet_name_or_ix,
|
203
|
+
sheet_scope_name,
|
204
|
+
book_name_or_ix=None,
|
205
|
+
fullname_or_name=None,
|
206
|
+
pid=None,
|
207
|
+
):
|
208
|
+
book = get_book_object(fullname_or_name, book_name_or_ix, pid)
|
209
|
+
sheet = get_sheet_object(book, sheet_name_or_ix)
|
210
|
+
return jsonify(serialize_name(sheet.names[sheet_scope_name]))
|
211
|
+
|
212
|
+
|
213
|
+
@api.route(
|
214
|
+
"/apps/<pid>/books/<book_name_or_ix>/sheets/<sheet_name_or_ix>/names/<sheet_scope_name>/range",
|
215
|
+
methods=["GET"],
|
216
|
+
)
|
217
|
+
@api.route(
|
218
|
+
"/books/<book_name_or_ix>/sheets/<sheet_name_or_ix>/names/<sheet_scope_name>/range",
|
219
|
+
methods=["GET"],
|
220
|
+
)
|
221
|
+
@api.route(
|
222
|
+
"/book/<path:fullname_or_name>/sheets/<sheet_name_or_ix>/names/<sheet_scope_name>/range",
|
223
|
+
methods=["GET"],
|
224
|
+
)
|
225
|
+
def get_sheet_name_range(
|
226
|
+
sheet_name_or_ix,
|
227
|
+
sheet_scope_name,
|
228
|
+
book_name_or_ix=None,
|
229
|
+
fullname_or_name=None,
|
230
|
+
pid=None,
|
231
|
+
):
|
232
|
+
book = get_book_object(fullname_or_name, book_name_or_ix, pid)
|
233
|
+
sheet = get_sheet_object(book, sheet_name_or_ix)
|
234
|
+
return jsonify(serialize_range(sheet.names[sheet_scope_name].refers_to_range))
|
235
|
+
|
236
|
+
|
237
|
+
@api.route(
|
238
|
+
"/apps/<pid>/books/<book_name_or_ix>/sheets/<sheet_name_or_ix>/charts",
|
239
|
+
methods=["GET"],
|
240
|
+
)
|
241
|
+
@api.route("/books/<book_name_or_ix>/sheets/<sheet_name_or_ix>/charts", methods=["GET"])
|
242
|
+
@api.route(
|
243
|
+
"/book/<path:fullname_or_name>/sheets/<sheet_name_or_ix>/charts", methods=["GET"]
|
244
|
+
)
|
245
|
+
def get_charts(sheet_name_or_ix, book_name_or_ix=None, fullname_or_name=None, pid=None):
|
246
|
+
book = get_book_object(fullname_or_name, book_name_or_ix, pid)
|
247
|
+
sheet = get_sheet_object(book, sheet_name_or_ix)
|
248
|
+
return jsonify(charts=[serialize_chart(chart) for chart in sheet.charts])
|
249
|
+
|
250
|
+
|
251
|
+
@api.route(
|
252
|
+
"/apps/<pid>/books/<book_name_or_ix>/sheets/<sheet_name_or_ix>/charts/<chart_name_or_ix>",
|
253
|
+
methods=["GET"],
|
254
|
+
)
|
255
|
+
@api.route(
|
256
|
+
"/books/<book_name_or_ix>/sheets/<sheet_name_or_ix>/charts/<chart_name_or_ix>",
|
257
|
+
methods=["GET"],
|
258
|
+
)
|
259
|
+
@api.route(
|
260
|
+
"/book/<path:fullname_or_name>/sheets/<sheet_name_or_ix>/charts/<chart_name_or_ix>",
|
261
|
+
methods=["GET"],
|
262
|
+
)
|
263
|
+
def get_chart(
|
264
|
+
sheet_name_or_ix,
|
265
|
+
chart_name_or_ix,
|
266
|
+
book_name_or_ix=None,
|
267
|
+
fullname_or_name=None,
|
268
|
+
pid=None,
|
269
|
+
):
|
270
|
+
book = get_book_object(fullname_or_name, book_name_or_ix, pid)
|
271
|
+
sheet = get_sheet_object(book, sheet_name_or_ix)
|
272
|
+
chart = int(chart_name_or_ix) if chart_name_or_ix.isdigit() else chart_name_or_ix
|
273
|
+
return jsonify(serialize_chart(sheet.charts[chart]))
|
274
|
+
|
275
|
+
|
276
|
+
@api.route(
|
277
|
+
"/apps/<pid>/books/<book_name_or_ix>/sheets/<sheet_name_or_ix>/shapes",
|
278
|
+
methods=["GET"],
|
279
|
+
)
|
280
|
+
@api.route("/books/<book_name_or_ix>/sheets/<sheet_name_or_ix>/shapes", methods=["GET"])
|
281
|
+
@api.route(
|
282
|
+
"/book/<path:fullname_or_name>/sheets/<sheet_name_or_ix>/shapes", methods=["GET"]
|
283
|
+
)
|
284
|
+
def get_shapes(sheet_name_or_ix, book_name_or_ix=None, fullname_or_name=None, pid=None):
|
285
|
+
book = get_book_object(fullname_or_name, book_name_or_ix, pid)
|
286
|
+
sheet = get_sheet_object(book, sheet_name_or_ix)
|
287
|
+
return jsonify(shapes=[serialize_shape(shp) for shp in sheet.shapes])
|
288
|
+
|
289
|
+
|
290
|
+
@api.route(
|
291
|
+
"/apps/<pid>/books/<book_name_or_ix>/sheets/<sheet_name_or_ix>/shapes/<shape_name_or_ix>",
|
292
|
+
methods=["GET"],
|
293
|
+
)
|
294
|
+
@api.route(
|
295
|
+
"/books/<book_name_or_ix>/sheets/<sheet_name_or_ix>/shapes/<shape_name_or_ix>",
|
296
|
+
methods=["GET"],
|
297
|
+
)
|
298
|
+
@api.route(
|
299
|
+
"/book/<path:fullname_or_name>/sheets/<sheet_name_or_ix>/shapes/<shape_name_or_ix>",
|
300
|
+
methods=["GET"],
|
301
|
+
)
|
302
|
+
def get_shape(
|
303
|
+
sheet_name_or_ix,
|
304
|
+
shape_name_or_ix,
|
305
|
+
book_name_or_ix=None,
|
306
|
+
fullname_or_name=None,
|
307
|
+
pid=None,
|
308
|
+
):
|
309
|
+
book = get_book_object(fullname_or_name, book_name_or_ix, pid)
|
310
|
+
sheet = get_sheet_object(book, sheet_name_or_ix)
|
311
|
+
shape = int(shape_name_or_ix) if shape_name_or_ix.isdigit() else shape_name_or_ix
|
312
|
+
return jsonify(serialize_shape(sheet.shapes[shape]))
|
313
|
+
|
314
|
+
|
315
|
+
@api.route(
|
316
|
+
"/apps/<pid>/books/<book_name_or_ix>/sheets/<sheet_name_or_ix>/pictures",
|
317
|
+
methods=["GET"],
|
318
|
+
)
|
319
|
+
@api.route(
|
320
|
+
"/books/<book_name_or_ix>/sheets/<sheet_name_or_ix>/pictures", methods=["GET"]
|
321
|
+
)
|
322
|
+
@api.route(
|
323
|
+
"/book/<path:fullname_or_name>/sheets/<sheet_name_or_ix>/pictures", methods=["GET"]
|
324
|
+
)
|
325
|
+
def get_pictures(
|
326
|
+
sheet_name_or_ix, book_name_or_ix=None, fullname_or_name=None, pid=None
|
327
|
+
):
|
328
|
+
book = get_book_object(fullname_or_name, book_name_or_ix, pid)
|
329
|
+
sheet = get_sheet_object(book, sheet_name_or_ix)
|
330
|
+
return jsonify(pictures=[serialize_picture(pic) for pic in sheet.pictures])
|
331
|
+
|
332
|
+
|
333
|
+
@api.route(
|
334
|
+
"/apps/<pid>/books/<book_name_or_ix>/sheets/<sheet_name_or_ix>/pictures/<picture_name_or_ix>",
|
335
|
+
methods=["GET"],
|
336
|
+
)
|
337
|
+
@api.route(
|
338
|
+
"/books/<book_name_or_ix>/sheets/<sheet_name_or_ix>/pictures/<picture_name_or_ix>",
|
339
|
+
methods=["GET"],
|
340
|
+
)
|
341
|
+
@api.route(
|
342
|
+
"/book/<path:fullname_or_name>/sheets/<sheet_name_or_ix>/pictures/<picture_name_or_ix>",
|
343
|
+
methods=["GET"],
|
344
|
+
)
|
345
|
+
def get_picture(
|
346
|
+
sheet_name_or_ix,
|
347
|
+
picture_name_or_ix,
|
348
|
+
book_name_or_ix=None,
|
349
|
+
fullname_or_name=None,
|
350
|
+
pid=None,
|
351
|
+
):
|
352
|
+
book = get_book_object(fullname_or_name, book_name_or_ix, pid)
|
353
|
+
sheet = get_sheet_object(book, sheet_name_or_ix)
|
354
|
+
pic = (
|
355
|
+
int(picture_name_or_ix) if picture_name_or_ix.isdigit() else picture_name_or_ix
|
356
|
+
)
|
357
|
+
return jsonify(serialize_picture(sheet.pictures[pic]))
|
358
|
+
|
359
|
+
|
360
|
+
def run(host=None, port=None, debug=None, **options):
|
361
|
+
"""
|
362
|
+
Run Flask development server
|
363
|
+
"""
|
364
|
+
api.run(host=host, port=port, debug=debug, **options)
|
365
|
+
|
366
|
+
|
367
|
+
if __name__ == "__main__":
|
368
|
+
run(debug=True)
|