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.
Files changed (101) hide show
  1. tdrpa/_tdxlwings/__init__.py +193 -0
  2. tdrpa/_tdxlwings/__pycache__/__init__.cpython-311.pyc +0 -0
  3. tdrpa/_tdxlwings/__pycache__/__init__.cpython-38.pyc +0 -0
  4. tdrpa/_tdxlwings/__pycache__/_win32patch.cpython-311.pyc +0 -0
  5. tdrpa/_tdxlwings/__pycache__/_win32patch.cpython-38.pyc +0 -0
  6. tdrpa/_tdxlwings/__pycache__/_xlwindows.cpython-311.pyc +0 -0
  7. tdrpa/_tdxlwings/__pycache__/_xlwindows.cpython-38.pyc +0 -0
  8. tdrpa/_tdxlwings/__pycache__/apps.cpython-311.pyc +0 -0
  9. tdrpa/_tdxlwings/__pycache__/apps.cpython-38.pyc +0 -0
  10. tdrpa/_tdxlwings/__pycache__/base_classes.cpython-311.pyc +0 -0
  11. tdrpa/_tdxlwings/__pycache__/base_classes.cpython-38.pyc +0 -0
  12. tdrpa/_tdxlwings/__pycache__/com_server.cpython-311.pyc +0 -0
  13. tdrpa/_tdxlwings/__pycache__/com_server.cpython-38.pyc +0 -0
  14. tdrpa/_tdxlwings/__pycache__/constants.cpython-311.pyc +0 -0
  15. tdrpa/_tdxlwings/__pycache__/constants.cpython-38.pyc +0 -0
  16. tdrpa/_tdxlwings/__pycache__/expansion.cpython-311.pyc +0 -0
  17. tdrpa/_tdxlwings/__pycache__/expansion.cpython-38.pyc +0 -0
  18. tdrpa/_tdxlwings/__pycache__/main.cpython-311.pyc +0 -0
  19. tdrpa/_tdxlwings/__pycache__/main.cpython-38.pyc +0 -0
  20. tdrpa/_tdxlwings/__pycache__/udfs.cpython-311.pyc +0 -0
  21. tdrpa/_tdxlwings/__pycache__/udfs.cpython-38.pyc +0 -0
  22. tdrpa/_tdxlwings/__pycache__/utils.cpython-311.pyc +0 -0
  23. tdrpa/_tdxlwings/__pycache__/utils.cpython-38.pyc +0 -0
  24. tdrpa/_tdxlwings/_win32patch.py +90 -0
  25. tdrpa/_tdxlwings/_xlmac.py +2240 -0
  26. tdrpa/_tdxlwings/_xlwindows.py +2518 -0
  27. tdrpa/_tdxlwings/addin/Dictionary.cls +474 -0
  28. tdrpa/_tdxlwings/addin/IWebAuthenticator.cls +71 -0
  29. tdrpa/_tdxlwings/addin/WebClient.cls +772 -0
  30. tdrpa/_tdxlwings/addin/WebHelpers.bas +3203 -0
  31. tdrpa/_tdxlwings/addin/WebRequest.cls +875 -0
  32. tdrpa/_tdxlwings/addin/WebResponse.cls +453 -0
  33. tdrpa/_tdxlwings/addin/xlwings.xlam +0 -0
  34. tdrpa/_tdxlwings/apps.py +35 -0
  35. tdrpa/_tdxlwings/base_classes.py +1092 -0
  36. tdrpa/_tdxlwings/cli.py +1306 -0
  37. tdrpa/_tdxlwings/com_server.py +385 -0
  38. tdrpa/_tdxlwings/constants.py +3080 -0
  39. tdrpa/_tdxlwings/conversion/__init__.py +103 -0
  40. tdrpa/_tdxlwings/conversion/framework.py +147 -0
  41. tdrpa/_tdxlwings/conversion/numpy_conv.py +34 -0
  42. tdrpa/_tdxlwings/conversion/pandas_conv.py +184 -0
  43. tdrpa/_tdxlwings/conversion/standard.py +321 -0
  44. tdrpa/_tdxlwings/expansion.py +83 -0
  45. tdrpa/_tdxlwings/ext/__init__.py +3 -0
  46. tdrpa/_tdxlwings/ext/sql.py +73 -0
  47. tdrpa/_tdxlwings/html/xlwings-alert.html +71 -0
  48. tdrpa/_tdxlwings/js/xlwings.js +577 -0
  49. tdrpa/_tdxlwings/js/xlwings.ts +729 -0
  50. tdrpa/_tdxlwings/mac_dict.py +6399 -0
  51. tdrpa/_tdxlwings/main.py +5205 -0
  52. tdrpa/_tdxlwings/mistune/__init__.py +63 -0
  53. tdrpa/_tdxlwings/mistune/block_parser.py +366 -0
  54. tdrpa/_tdxlwings/mistune/inline_parser.py +216 -0
  55. tdrpa/_tdxlwings/mistune/markdown.py +84 -0
  56. tdrpa/_tdxlwings/mistune/renderers.py +220 -0
  57. tdrpa/_tdxlwings/mistune/scanner.py +121 -0
  58. tdrpa/_tdxlwings/mistune/util.py +41 -0
  59. tdrpa/_tdxlwings/pro/__init__.py +40 -0
  60. tdrpa/_tdxlwings/pro/_xlcalamine.py +536 -0
  61. tdrpa/_tdxlwings/pro/_xlofficejs.py +146 -0
  62. tdrpa/_tdxlwings/pro/_xlremote.py +1293 -0
  63. tdrpa/_tdxlwings/pro/custom_functions_code.js +150 -0
  64. tdrpa/_tdxlwings/pro/embedded_code.py +60 -0
  65. tdrpa/_tdxlwings/pro/udfs_officejs.py +549 -0
  66. tdrpa/_tdxlwings/pro/utils.py +199 -0
  67. tdrpa/_tdxlwings/quickstart.xlsm +0 -0
  68. tdrpa/_tdxlwings/quickstart_addin.xlam +0 -0
  69. tdrpa/_tdxlwings/quickstart_addin_ribbon.xlam +0 -0
  70. tdrpa/_tdxlwings/quickstart_fastapi/main.py +47 -0
  71. tdrpa/_tdxlwings/quickstart_fastapi/requirements.txt +3 -0
  72. tdrpa/_tdxlwings/quickstart_standalone.xlsm +0 -0
  73. tdrpa/_tdxlwings/reports.py +12 -0
  74. tdrpa/_tdxlwings/rest/__init__.py +1 -0
  75. tdrpa/_tdxlwings/rest/api.py +368 -0
  76. tdrpa/_tdxlwings/rest/serializers.py +103 -0
  77. tdrpa/_tdxlwings/server.py +14 -0
  78. tdrpa/_tdxlwings/udfs.py +775 -0
  79. tdrpa/_tdxlwings/utils.py +777 -0
  80. tdrpa/_tdxlwings/xlwings-0.31.6.applescript +30 -0
  81. tdrpa/_tdxlwings/xlwings.bas +2061 -0
  82. tdrpa/_tdxlwings/xlwings_custom_addin.bas +2042 -0
  83. tdrpa/_tdxlwings/xlwingslib.cp38-win_amd64.pyd +0 -0
  84. tdrpa/tdworker/__init__.pyi +12 -0
  85. tdrpa/tdworker/_clip.pyi +50 -0
  86. tdrpa/tdworker/_excel.pyi +743 -0
  87. tdrpa/tdworker/_file.pyi +77 -0
  88. tdrpa/tdworker/_img.pyi +226 -0
  89. tdrpa/tdworker/_network.pyi +94 -0
  90. tdrpa/tdworker/_os.pyi +47 -0
  91. tdrpa/tdworker/_sp.pyi +21 -0
  92. tdrpa/tdworker/_w.pyi +129 -0
  93. tdrpa/tdworker/_web.pyi +995 -0
  94. tdrpa/tdworker/_winE.pyi +228 -0
  95. tdrpa/tdworker/_winK.pyi +74 -0
  96. tdrpa/tdworker/_winM.pyi +117 -0
  97. tdrpa/tdworker.cp312-win_amd64.pyd +0 -0
  98. tdrpa_tdworker-1.2.13.2.dist-info/METADATA +38 -0
  99. tdrpa_tdworker-1.2.13.2.dist-info/RECORD +101 -0
  100. tdrpa_tdworker-1.2.13.2.dist-info/WHEEL +5 -0
  101. 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
@@ -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)
@@ -0,0 +1,3 @@
1
+ fastapi
2
+ uvicorn[standard]
3
+ xlwings
@@ -0,0 +1,12 @@
1
+ # Allows you to use xlwings.reports instead of xlwings.pro.reports
2
+
3
+ from .pro.reports import * # noqa: F401,F403
4
+
5
+ __all__ = ( # noqa: F405
6
+ "render_template",
7
+ "create_report",
8
+ "Image",
9
+ "Markdown",
10
+ "MarkdownStyle",
11
+ "formatter",
12
+ )
@@ -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)