xlwings-server 1.1.0__py3-none-any.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.
- xlwings_server/.env.template +145 -0
- xlwings_server/__init__.py +12 -0
- xlwings_server/_version.py +34 -0
- xlwings_server/auth/__init__.py +0 -0
- xlwings_server/auth/custom/__init__.py +26 -0
- xlwings_server/auth/entraid/__init__.py +131 -0
- xlwings_server/auth/entraid/jwks.py +10 -0
- xlwings_server/azure_functions_templates/.funcignore +28 -0
- xlwings_server/azure_functions_templates/function_app.py +28 -0
- xlwings_server/azure_functions_templates/host.json +22 -0
- xlwings_server/azure_functions_templates/local.settings.json +8 -0
- xlwings_server/build_utils/__init__.py +9 -0
- xlwings_server/build_utils/static_file_hasher.py +212 -0
- xlwings_server/cli.py +1592 -0
- xlwings_server/config.py +228 -0
- xlwings_server/custom_functions/__init__.py +8 -0
- xlwings_server/custom_functions/examples.py +177 -0
- xlwings_server/custom_scripts/__init__.py +8 -0
- xlwings_server/custom_scripts/examples.py +94 -0
- xlwings_server/databases.py +19 -0
- xlwings_server/dependencies.py +126 -0
- xlwings_server/docker_templates/.dockerignore +15 -0
- xlwings_server/docker_templates/Dockerfile +60 -0
- xlwings_server/docker_templates/docker-compose.yaml +32 -0
- xlwings_server/hotreload.py +59 -0
- xlwings_server/main.py +242 -0
- xlwings_server/models/__init__.py +14 -0
- xlwings_server/models/user.py +53 -0
- xlwings_server/object_handles.py +142 -0
- xlwings_server/routers/__init__.py +0 -0
- xlwings_server/routers/manifest.py +82 -0
- xlwings_server/routers/root.py +16 -0
- xlwings_server/routers/socketio.py +69 -0
- xlwings_server/routers/taskpane.py +12 -0
- xlwings_server/routers/xlwings.py +197 -0
- xlwings_server/security_headers.json +53 -0
- xlwings_server/serializers/__init__.py +25 -0
- xlwings_server/serializers/default_serializer.py +19 -0
- xlwings_server/serializers/dictionary_serializer.py +25 -0
- xlwings_server/serializers/framework.py +50 -0
- xlwings_server/serializers/numpy_serializer.py +26 -0
- xlwings_server/serializers/pandas_serializer.py +95 -0
- xlwings_server/static/css/core.css +28 -0
- xlwings_server/static/css/style.css +0 -0
- xlwings_server/static/images/favicon.png +0 -0
- xlwings_server/static/images/xlwings-16.png +0 -0
- xlwings_server/static/images/xlwings-32.png +0 -0
- xlwings_server/static/images/xlwings-64.png +0 -0
- xlwings_server/static/images/xlwings-80.png +0 -0
- xlwings_server/static/js/auth.js +13 -0
- xlwings_server/static/js/config.js +4 -0
- xlwings_server/static/js/core/alpinejs-csp-boilerplate.js +11 -0
- xlwings_server/static/js/core/bootstrap-customizations.js +7 -0
- xlwings_server/static/js/core/custom-functions-code.js +296 -0
- xlwings_server/static/js/core/examples.js +62 -0
- xlwings_server/static/js/core/hotreload.js +3 -0
- xlwings_server/static/js/core/htmx-handlers.js +86 -0
- xlwings_server/static/js/core/officejs-history-fix-part1.js +3 -0
- xlwings_server/static/js/core/officejs-history-fix-part2.js +2 -0
- xlwings_server/static/js/core/reload-custom-functions.js +79 -0
- xlwings_server/static/js/core/socketio-handlers.js +34 -0
- xlwings_server/static/js/core/xlwings-alert.js +22 -0
- xlwings_server/static/js/core/xlwingsjs/alert.js +85 -0
- xlwings_server/static/js/core/xlwingsjs/auth.js +63 -0
- xlwings_server/static/js/core/xlwingsjs/sheet-buttons.js +133 -0
- xlwings_server/static/js/core/xlwingsjs/utils.js +119 -0
- xlwings_server/static/js/core/xlwingsjs/wasm.js +131 -0
- xlwings_server/static/js/core/xlwingsjs/xlwings.js +1060 -0
- xlwings_server/static/js/main.js +0 -0
- xlwings_server/static/js/ribbon.js +17 -0
- xlwings_server/static/vendor/@alpinejs/LICENSE +21 -0
- xlwings_server/static/vendor/@alpinejs/csp/dist/cdn.min.js +7 -0
- xlwings_server/static/vendor/@microsoft/office-js/LICENSE.md +76 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/af-za/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/agaveerrorux.js +18 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/agavedefaulticon32x32.png +0 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/agavedefaulticon96x96.png +0 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/businessbarclose_16x16x32.png +0 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/dropdownarrow_16x16x32.png +0 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/ellipsis_16x16x32.png +0 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/miniinfoblue_16x16x32.png +0 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/moe_default_icon.png +0 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/moe_status_icons.png +0 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/office.png +0 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/refresh_16x16x32.png +0 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/index.html +16 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/style/agaveerrorux.css +482 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/am-et/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-ae/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-bh/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-dz/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-eg/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-iq/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-jo/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-kw/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-lb/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-ly/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-ma/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-om/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-qa/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-sa/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-sy/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-tn/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-ye/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ariatelemetry/aria-web-telemetry-2.8.0.min.js +2 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ariatelemetry/aria-web-telemetry-2.9.0.min.js +2 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ariatelemetry/aria-web-telemetry.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/az-latn-az/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/be-by/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/bg-bg/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/bn-in/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/bs-latn-ba/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ca-es/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/cs-cz/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/cy-gb/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/da-dk/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/de-at/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/de-ch/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/de-de/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/de-li/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/de-lu/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/el-gr/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-029/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-au/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-bz/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-ca/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-gb/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-ie/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-in/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-jm/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-my/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-nz/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-ph/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-sg/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-tt/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-us/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-za/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-zw/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-ar/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-bo/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-cl/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-co/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-cr/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-do/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-ec/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-es/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-gt/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-hn/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-mx/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-ni/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-pa/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-pe/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-pr/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-py/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-sv/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-us/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-uy/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-ve/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es6-promise.js +5 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/et-ee/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/eu-es/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excel-15.01.js +11 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excel-15.02.js +11 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excel-15.js +11 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excel-mac-16.00-core.js +11 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excel-mac-16.00.js +25 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excel-web-16.00-core.js +11 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excel-web-16.00.js +25 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excel-win32-16.00.js +19 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excel-win32-16.01-core.js +11 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excel-win32-16.01.js +25 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excel-winrt-16.00.js +25 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excelios-15.js +11 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excelwebapp-15.01.js +11 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excelwebapp-15.02.js +11 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excelwebapp-15.js +11 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/fa-ir/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/fi-fi/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/fil-ph/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/fr-be/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/fr-ca/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/fr-ch/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/fr-fr/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/fr-lu/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/fr-mc/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ga-ie/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/gl-es/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/gu-in/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/he-il/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/hi-in/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/hr-ba/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/hr-hr/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/html2canvas.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/hu-hu/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/hy-am/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/id-id/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/is-is/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/it-ch/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/it-it/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ja-jp/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ka-ge/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/kk-kz/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/km-kh/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/kn-in/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ko-kr/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/lb-lu/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/lo-la/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/lt-lt/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/lv-lv/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/mk-mk/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ml-in/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/mn-mn/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/mr-in/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ms-bn/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ms-my/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/mt-mt/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/nb-no/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ne-np/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/nl-be/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/nl-nl/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/nn-no/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/o15apptofilemappingtable.js +11 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/office-vsdoc.js +28596 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/office.js +84 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/pl-pl/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/pt-br/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/pt-pt/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ro-ro/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ru-ru/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/si-lk/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/sk-sk/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/sl-si/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/sq-al/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/sr-cyrl-cs/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/sr-cyrl-rs/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/sr-latn-cs/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/sr-latn-rs/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/sv-fi/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/sv-se/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/sw-ke/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ta-in/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/te-in/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/telemetry/oteljs.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/telemetry/oteljs_agave.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/th-th/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/tr-tr/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/uk-ua/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ur-pk/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/vi-vn/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/webauth/webauth.browserauth.js +77 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/webauth/webauth.implicit.js +35 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/zh-cn/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/zh-hk/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/zh-mo/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/zh-sg/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/zh-tw/office_strings.js +1 -0
- xlwings_server/static/vendor/axios/dist/axios.min.js +3 -0
- xlwings_server/static/vendor/axios/dist/axios.min.js.map +1 -0
- xlwings_server/static/vendor/bootstrap/LICENSE +21 -0
- xlwings_server/static/vendor/bootstrap/dist/js/bootstrap.bundle.min.js +7 -0
- xlwings_server/static/vendor/bootstrap/dist/js/bootstrap.bundle.min.js.map +1 -0
- xlwings_server/static/vendor/bootstrap-xlwings/dist/bootstrap-xlwings.min.css +12 -0
- xlwings_server/static/vendor/bootstrap-xlwings/dist/bootstrap-xlwings.min.css.map +1 -0
- xlwings_server/static/vendor/htmx-ext-head-support/head-support.js +144 -0
- xlwings_server/static/vendor/htmx-ext-loading-states/loading-states.js +184 -0
- xlwings_server/static/vendor/htmx.org/LICENSE +13 -0
- xlwings_server/static/vendor/htmx.org/dist/htmx.min.js +1 -0
- xlwings_server/static/vendor/socket.io/LICENSE +22 -0
- xlwings_server/static/vendor/socket.io/client-dist/socket.io.min.js +7 -0
- xlwings_server/static/vendor/socket.io/client-dist/socket.io.min.js.map +1 -0
- xlwings_server/templates/_book.html +8 -0
- xlwings_server/templates/alert_base.html +16 -0
- xlwings_server/templates/base.html +117 -0
- xlwings_server/templates/examples/alpine/README.md +26 -0
- xlwings_server/templates/examples/alpine/taskpane.html +47 -0
- xlwings_server/templates/examples/auth/README.md +38 -0
- xlwings_server/templates/examples/auth/protected.html +8 -0
- xlwings_server/templates/examples/auth/public.html +11 -0
- xlwings_server/templates/examples/excel_object_model/README.md +49 -0
- xlwings_server/templates/examples/excel_object_model/add_name_form.html +27 -0
- xlwings_server/templates/examples/hello_world/README.md +9 -0
- xlwings_server/templates/examples/hello_world/taskpane_hello.html +24 -0
- xlwings_server/templates/examples/htmx_form/README.md +44 -0
- xlwings_server/templates/examples/htmx_form/_greeting.html +6 -0
- xlwings_server/templates/examples/htmx_form/taskpane_htmx_form.html +21 -0
- xlwings_server/templates/examples/live_form_validation/README.md +60 -0
- xlwings_server/templates/examples/live_form_validation/add_name_form.html +33 -0
- xlwings_server/templates/examples/multi_app/README.md +34 -0
- xlwings_server/templates/examples/multi_app/taskpane1.html +7 -0
- xlwings_server/templates/examples/multi_app/taskpane2.html +7 -0
- xlwings_server/templates/examples/multi_app/taskpane_loader.html +5 -0
- xlwings_server/templates/examples/navigation/README.md +28 -0
- xlwings_server/templates/examples/navigation/_navigation.html +16 -0
- xlwings_server/templates/examples/navigation/taskpane_one.html +8 -0
- xlwings_server/templates/examples/navigation/taskpane_three.html +8 -0
- xlwings_server/templates/examples/navigation/taskpane_two.html +8 -0
- xlwings_server/templates/examples/pictures/README.md +42 -0
- xlwings_server/templates/examples/pictures/_picture.html +4 -0
- xlwings_server/templates/examples/pictures/taskpane_pictures.html +26 -0
- xlwings_server/templates/manifest.xml +155 -0
- xlwings_server/templates/taskpane.html +1 -0
- xlwings_server/templates/xlwings_alert.html +27 -0
- xlwings_server/templates.py +61 -0
- xlwings_server/utils.py +32 -0
- xlwings_server/wasm/__init__.py +0 -0
- xlwings_server/wasm/config.py +24 -0
- xlwings_server/wasm/main.py +236 -0
- xlwings_server/wasm/requirements.txt +5 -0
- xlwings_server-1.1.0.dist-info/METADATA +61 -0
- xlwings_server-1.1.0.dist-info/RECORD +313 -0
- xlwings_server-1.1.0.dist-info/WHEEL +4 -0
- xlwings_server-1.1.0.dist-info/entry_points.txt +2 -0
- xlwings_server-1.1.0.dist-info/licenses/LICENSE.md +223 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter
|
|
4
|
+
|
|
5
|
+
from ..config import settings
|
|
6
|
+
|
|
7
|
+
router = APIRouter(prefix=settings.app_path)
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Endpoints
|
|
13
|
+
@router.get("/")
|
|
14
|
+
async def root():
|
|
15
|
+
# This endpoint could be used for a health check
|
|
16
|
+
return {"status": "ok"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import socketio
|
|
4
|
+
import xlwings as xw
|
|
5
|
+
|
|
6
|
+
# Try to import custom modules from project directory first (CLI/Azure mode)
|
|
7
|
+
# Fall back to package location (tests/package mode)
|
|
8
|
+
try:
|
|
9
|
+
import custom_functions
|
|
10
|
+
except ModuleNotFoundError:
|
|
11
|
+
import xlwings_server.custom_functions as custom_functions
|
|
12
|
+
|
|
13
|
+
from xlwings_server.config import PROJECT_DIR, settings
|
|
14
|
+
from xlwings_server.dependencies import authenticate
|
|
15
|
+
from xlwings_server.models import CurrentUser
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
if settings.socketio_message_queue_url:
|
|
20
|
+
client_manager = socketio.AsyncRedisManager(
|
|
21
|
+
settings.socketio_message_queue_url, write_only=not settings.socketio_server_app
|
|
22
|
+
)
|
|
23
|
+
else:
|
|
24
|
+
client_manager = None
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
sio = socketio.AsyncServer(
|
|
28
|
+
async_mode="asgi",
|
|
29
|
+
client_manager=client_manager,
|
|
30
|
+
cors_allowed_origins=(
|
|
31
|
+
settings.cors_allow_origins[0]
|
|
32
|
+
if len(settings.cors_allow_origins) == 1
|
|
33
|
+
else settings.cors_allow_origins
|
|
34
|
+
),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@sio.on("connect")
|
|
39
|
+
async def connect(sid, environ, auth):
|
|
40
|
+
if settings.environment == "dev" and settings.enable_hotreload:
|
|
41
|
+
from .. import hotreload
|
|
42
|
+
|
|
43
|
+
logging.getLogger("watchfiles").setLevel(logging.ERROR)
|
|
44
|
+
await hotreload.start_browser_reload_watcher(sio=sio, directory=PROJECT_DIR)
|
|
45
|
+
token_string = auth.get("token")
|
|
46
|
+
provider = auth.get("provider")
|
|
47
|
+
try:
|
|
48
|
+
current_user = await authenticate(token_string, auth_provider=provider)
|
|
49
|
+
await sio.save_session(sid, {"current_user": current_user})
|
|
50
|
+
logger.info(f"Socket.io: connect {sid}")
|
|
51
|
+
logger.info(f"Socket.io: User authenticated {current_user.name}")
|
|
52
|
+
except Exception as e:
|
|
53
|
+
logger.info(f"Socket.io: authentication failed for sid {sid}: {repr(e)}")
|
|
54
|
+
await sio.disconnect(sid)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@sio.on("disconnect")
|
|
58
|
+
async def disconnect(sid):
|
|
59
|
+
await xw.server.sio_disconnect(sid)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@sio.on("xlwings:function-call")
|
|
63
|
+
async def sio_function_call(sid, data):
|
|
64
|
+
session = await sio.get_session(sid)
|
|
65
|
+
current_user = session["current_user"]
|
|
66
|
+
logger.info(f"""Function "{data['func_name']}" called by {current_user.name}""")
|
|
67
|
+
await xw.server.sio_custom_function_call(
|
|
68
|
+
sid, data, custom_functions, current_user, sio, {CurrentUser: current_user}
|
|
69
|
+
)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from fastapi import APIRouter, Request
|
|
2
|
+
|
|
3
|
+
from ..config import settings
|
|
4
|
+
from ..templates import TemplateResponse
|
|
5
|
+
|
|
6
|
+
router = APIRouter(prefix=settings.app_path)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@router.get("/taskpane")
|
|
10
|
+
@router.get("/taskpane.html")
|
|
11
|
+
async def taskpane(request: Request):
|
|
12
|
+
return TemplateResponse(request=request, name=settings.taskpane_html)
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import contextvars
|
|
2
|
+
import inspect
|
|
3
|
+
import logging
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from textwrap import dedent
|
|
6
|
+
|
|
7
|
+
import xlwings as xw
|
|
8
|
+
import xlwings.server
|
|
9
|
+
from fastapi import APIRouter, Body, Header, Request, Response
|
|
10
|
+
|
|
11
|
+
# Try to import custom modules from project directory first (CLI/Azure mode)
|
|
12
|
+
# Fall back to package location (tests/package mode)
|
|
13
|
+
try:
|
|
14
|
+
import custom_functions
|
|
15
|
+
except ModuleNotFoundError:
|
|
16
|
+
import xlwings_server.custom_functions as custom_functions
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
import custom_scripts
|
|
20
|
+
except ModuleNotFoundError:
|
|
21
|
+
import xlwings_server.custom_scripts as custom_scripts
|
|
22
|
+
|
|
23
|
+
from xlwings_server import dependencies as dep
|
|
24
|
+
from xlwings_server.config import PACKAGE_DIR, settings
|
|
25
|
+
from xlwings_server.models import CurrentUser
|
|
26
|
+
from xlwings_server.templates import TemplateResponse
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
router = APIRouter(prefix=f"{settings.app_path}/xlwings")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def sanitize_log_input(input_string: str) -> str:
|
|
34
|
+
"""Replaces newline and carriage return characters to prevent log injection."""
|
|
35
|
+
if not isinstance(input_string, str):
|
|
36
|
+
input_string = str(input_string)
|
|
37
|
+
return input_string.replace("\n", "\\n").replace("\r", "\\r")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@router.get("/alert")
|
|
41
|
+
async def alert(
|
|
42
|
+
request: Request, prompt: str, title: str, buttons: str, mode: str, callback: str
|
|
43
|
+
):
|
|
44
|
+
"""Boilerplate required by book.app.alert() and to show unhandled exceptions"""
|
|
45
|
+
return TemplateResponse(
|
|
46
|
+
request=request,
|
|
47
|
+
name="xlwings_alert.html",
|
|
48
|
+
context={
|
|
49
|
+
"prompt": prompt,
|
|
50
|
+
"title": title,
|
|
51
|
+
"buttons": buttons,
|
|
52
|
+
"mode": mode,
|
|
53
|
+
"callback": callback,
|
|
54
|
+
"settings": settings,
|
|
55
|
+
},
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@router.get("/custom-functions-meta")
|
|
60
|
+
@router.get("/custom-functions-meta.json")
|
|
61
|
+
async def custom_functions_meta():
|
|
62
|
+
return xlwings.server.custom_functions_meta(
|
|
63
|
+
custom_functions, typehinted_params_to_exclude=[CurrentUser]
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@router.get("/custom-functions-code")
|
|
68
|
+
@router.get("/custom-functions-code.js")
|
|
69
|
+
async def custom_functions_code():
|
|
70
|
+
custom_functions_call_path = f"{settings.app_path}/xlwings/custom-functions-call"
|
|
71
|
+
js = (
|
|
72
|
+
PACKAGE_DIR / "static" / "js" / "core" / "custom-functions-code.js"
|
|
73
|
+
).read_text()
|
|
74
|
+
# format string would require to double all curly braces
|
|
75
|
+
js = js.replace("placeholder_xlwings_version", xw.__version__).replace(
|
|
76
|
+
"placeholder_custom_functions_call_path", custom_functions_call_path
|
|
77
|
+
)
|
|
78
|
+
for name, obj in inspect.getmembers(custom_functions):
|
|
79
|
+
if hasattr(obj, "__xlfunc__"):
|
|
80
|
+
xlfunc = obj.__xlfunc__
|
|
81
|
+
func_name = xlfunc["name"]
|
|
82
|
+
streaming = "true" if inspect.isasyncgenfunction(obj) else "false"
|
|
83
|
+
js += dedent(
|
|
84
|
+
f"""\
|
|
85
|
+
async function {func_name}() {{
|
|
86
|
+
let args = ["{func_name}", {streaming}]
|
|
87
|
+
args.push.apply(args, arguments);
|
|
88
|
+
return await base.apply(null, args);
|
|
89
|
+
}}
|
|
90
|
+
CustomFunctions.associate("{func_name.upper()}", {func_name});
|
|
91
|
+
"""
|
|
92
|
+
)
|
|
93
|
+
return Response(
|
|
94
|
+
content=js,
|
|
95
|
+
media_type="text/javascript",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# ContextVars
|
|
100
|
+
socketio_id_context = contextvars.ContextVar("socketio_id_context")
|
|
101
|
+
caller_address_context = contextvars.ContextVar("caller_address_context")
|
|
102
|
+
redis_client_context = contextvars.ContextVar("redis_client_context")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@router.post("/custom-functions-call")
|
|
106
|
+
async def custom_functions_call(
|
|
107
|
+
current_user: dep.User,
|
|
108
|
+
redis_client: dep.RedisClient,
|
|
109
|
+
data: dict = Body,
|
|
110
|
+
sid: str | None = Header(default=None),
|
|
111
|
+
):
|
|
112
|
+
# Replace newline and carriage return characters to prevent log injection
|
|
113
|
+
safe_func_name = sanitize_log_input(data["func_name"])
|
|
114
|
+
safe_user_name = sanitize_log_input(current_user.name)
|
|
115
|
+
logger.info(f"""Function "{safe_func_name}" called by {safe_user_name}""")
|
|
116
|
+
socketio_id_context.set(sid) # For utils.trigger_script()
|
|
117
|
+
caller_address_context.set(data["caller_address"]) # For ObjectCache converter
|
|
118
|
+
redis_client_context.set(redis_client) # For ObjectCache converter
|
|
119
|
+
|
|
120
|
+
rv = await xlwings.server.custom_functions_call(
|
|
121
|
+
data,
|
|
122
|
+
custom_functions,
|
|
123
|
+
current_user,
|
|
124
|
+
typehint_to_value={CurrentUser: current_user},
|
|
125
|
+
)
|
|
126
|
+
return {"result": rv}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@router.post("/custom-scripts-call/{script_name}")
|
|
130
|
+
async def custom_scripts_call(script_name: str, book: dep.Book, current_user: dep.User):
|
|
131
|
+
# Replace newline and carriage return characters to prevent log injection
|
|
132
|
+
safe_script_name = sanitize_log_input(script_name)
|
|
133
|
+
safe_user_name = sanitize_log_input(current_user.name)
|
|
134
|
+
logger.info(f"""Script "{safe_script_name}" called by {safe_user_name}""")
|
|
135
|
+
book = await xlwings.server.custom_scripts_call(
|
|
136
|
+
custom_scripts,
|
|
137
|
+
script_name,
|
|
138
|
+
current_user,
|
|
139
|
+
typehint_to_value={CurrentUser: current_user, xw.Book: book},
|
|
140
|
+
)
|
|
141
|
+
return book.json()
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@router.get("/custom-scripts-meta")
|
|
145
|
+
@router.get("/custom-scripts-meta.json")
|
|
146
|
+
async def custom_scripts_meta():
|
|
147
|
+
return xlwings.server.custom_scripts_meta(custom_scripts)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
if settings.enable_wasm:
|
|
151
|
+
|
|
152
|
+
@router.get("/pyodide.json")
|
|
153
|
+
async def get_pyodide_config():
|
|
154
|
+
# requirements.txt
|
|
155
|
+
packages = (
|
|
156
|
+
Path(settings.project_dir / "wasm" / "requirements.txt")
|
|
157
|
+
.read_text()
|
|
158
|
+
.splitlines()
|
|
159
|
+
)
|
|
160
|
+
packages = [
|
|
161
|
+
pkg.replace("/static", settings.static_url_path).strip()
|
|
162
|
+
for pkg in packages
|
|
163
|
+
if pkg.strip()
|
|
164
|
+
]
|
|
165
|
+
|
|
166
|
+
# Files
|
|
167
|
+
def scan_directory(
|
|
168
|
+
base_dir: Path, dir_name: str, prepend_dir_name: bool = False
|
|
169
|
+
) -> dict:
|
|
170
|
+
dir_path = Path(settings.project_dir / dir_name)
|
|
171
|
+
files = {}
|
|
172
|
+
if dir_path.exists():
|
|
173
|
+
for file_path in dir_path.rglob("*"):
|
|
174
|
+
if (
|
|
175
|
+
file_path.is_file()
|
|
176
|
+
and file_path.suffix != ".pyc"
|
|
177
|
+
and file_path.name != "requirements.txt"
|
|
178
|
+
):
|
|
179
|
+
relative_path = file_path.relative_to(dir_path)
|
|
180
|
+
files[
|
|
181
|
+
f"{settings.static_url_path.replace('static', dir_name)}/{relative_path}"
|
|
182
|
+
] = (
|
|
183
|
+
f"./{dir_name}/{relative_path}"
|
|
184
|
+
if prepend_dir_name
|
|
185
|
+
else f"./{relative_path}"
|
|
186
|
+
)
|
|
187
|
+
return files
|
|
188
|
+
|
|
189
|
+
# Scan all directories
|
|
190
|
+
files = {}
|
|
191
|
+
files.update(scan_directory(settings.project_dir, "wasm"))
|
|
192
|
+
for directory in ["custom_functions", "custom_scripts"]:
|
|
193
|
+
files.update(
|
|
194
|
+
scan_directory(settings.project_dir, directory, prepend_dir_name=True)
|
|
195
|
+
)
|
|
196
|
+
response = {"packages": packages, "files": files}
|
|
197
|
+
return response
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"last_update_utc": "2024-09-02 21:54:45",
|
|
3
|
+
"headers": [
|
|
4
|
+
{
|
|
5
|
+
"name": "Cache-Control",
|
|
6
|
+
"value": "no-store, max-age=0"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"name": "Clear-Site-Data",
|
|
10
|
+
"value": "\"cache\",\"cookies\",\"storage\""
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"name": "Content-Security-Policy",
|
|
14
|
+
"value": "default-src 'self'; form-action 'self'; object-src 'none'; frame-ancestors 'none'; upgrade-insecure-requests; block-all-mixed-content"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"name": "Cross-Origin-Embedder-Policy",
|
|
18
|
+
"value": "require-corp"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "Cross-Origin-Opener-Policy",
|
|
22
|
+
"value": "same-origin"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"name": "Cross-Origin-Resource-Policy",
|
|
26
|
+
"value": "same-origin"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "Permissions-Policy",
|
|
30
|
+
"value": "accelerometer=(), autoplay=(), camera=(), cross-origin-isolated=(), display-capture=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(self), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(), clipboard-write=(), gamepad=(), hid=(), idle-detection=(), interest-cohort=(), serial=(), unload=()"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "Referrer-Policy",
|
|
34
|
+
"value": "no-referrer"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "Strict-Transport-Security",
|
|
38
|
+
"value": "max-age=31536000; includeSubDomains"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"name": "X-Content-Type-Options",
|
|
42
|
+
"value": "nosniff"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"name": "X-Frame-Options",
|
|
46
|
+
"value": "deny"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "X-Permitted-Cross-Domain-Policies",
|
|
50
|
+
"value": "none"
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from . import (
|
|
4
|
+
default_serializer,
|
|
5
|
+
dictionary_serializer,
|
|
6
|
+
numpy_serializer,
|
|
7
|
+
pandas_serializer,
|
|
8
|
+
)
|
|
9
|
+
from .framework import Serializer, custom_encoder, serializers
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def serialize(obj, serializer_name="default"):
|
|
13
|
+
serializer = serializers.get(type(obj), serializers.get(serializer_name))
|
|
14
|
+
if serializer is None:
|
|
15
|
+
raise ValueError(f"No serializer registered for object of type {type(obj)}")
|
|
16
|
+
return json.dumps(serializer.serialize(obj), default=custom_encoder)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def deserialize(payload, serializer_name="default"):
|
|
20
|
+
payload = json.loads(payload)
|
|
21
|
+
serializer_name = payload.get("serializer", serializer_name)
|
|
22
|
+
serializer = serializers.get(serializer_name)
|
|
23
|
+
if serializer is None:
|
|
24
|
+
raise ValueError(f"No serializer registered with name '{serializer_name}'")
|
|
25
|
+
return serializer.deserialize(payload)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from .framework import Serializer, custom_decoder
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class DefaultSerializer(Serializer):
|
|
5
|
+
name = "default"
|
|
6
|
+
|
|
7
|
+
@classmethod
|
|
8
|
+
def serialize(cls, obj):
|
|
9
|
+
return {
|
|
10
|
+
"data": obj,
|
|
11
|
+
"serializer": cls.name,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@classmethod
|
|
15
|
+
def deserialize(cls, payload):
|
|
16
|
+
return custom_decoder(payload["data"])
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
DefaultSerializer.register()
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from .framework import Serializer, custom_decoder
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class DictionarySerializer(Serializer):
|
|
5
|
+
name = "custom_dict_serializer"
|
|
6
|
+
|
|
7
|
+
@classmethod
|
|
8
|
+
def serialize(cls, obj):
|
|
9
|
+
# Convert dictionary with non-string keys (e.g., datetime) to a list of
|
|
10
|
+
# [key, value] pairs
|
|
11
|
+
items = [[k, v] for k, v in obj.items()]
|
|
12
|
+
return {
|
|
13
|
+
"data": items,
|
|
14
|
+
"serializer": cls.name,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def deserialize(cls, payload):
|
|
19
|
+
# Convert back from list of pairs to dictionary
|
|
20
|
+
items = payload["data"]
|
|
21
|
+
return {custom_decoder(k): custom_decoder(v) for k, v in items}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Register the serializer for dict type
|
|
25
|
+
DictionarySerializer.register(dict)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import datetime as dt
|
|
2
|
+
|
|
3
|
+
# Registry, holding type or serializer_name vs. serializer combinations
|
|
4
|
+
serializers = {}
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# Base class
|
|
8
|
+
class Serializer:
|
|
9
|
+
@classmethod
|
|
10
|
+
def serialize(cls, obj):
|
|
11
|
+
raise NotImplementedError()
|
|
12
|
+
|
|
13
|
+
@classmethod
|
|
14
|
+
def deserialize(cls, payload):
|
|
15
|
+
raise NotImplementedError()
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def register(cls, *types):
|
|
19
|
+
serializers[cls.name] = cls
|
|
20
|
+
for type in types:
|
|
21
|
+
serializers[type] = cls
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Custom encoders/decoders
|
|
25
|
+
def custom_encoder(obj):
|
|
26
|
+
if isinstance(obj, dt.datetime):
|
|
27
|
+
return obj.isoformat()
|
|
28
|
+
valid_types = tuple(cls for cls in serializers.keys() if isinstance(cls, type))
|
|
29
|
+
if isinstance(obj, valid_types):
|
|
30
|
+
serializer = serializers.get(type(obj))
|
|
31
|
+
if serializer:
|
|
32
|
+
return serializer.serialize(obj)
|
|
33
|
+
raise TypeError(f"Object of type {type(obj)} is not JSON serializable")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def custom_decoder(obj):
|
|
37
|
+
if isinstance(obj, list):
|
|
38
|
+
return [custom_decoder(item) for item in obj]
|
|
39
|
+
elif isinstance(obj, dict):
|
|
40
|
+
serializer = serializers.get(obj.get("serializer"))
|
|
41
|
+
if serializer:
|
|
42
|
+
return serializer.deserialize(obj)
|
|
43
|
+
return {key: custom_decoder(value) for key, value in obj.items()}
|
|
44
|
+
elif isinstance(obj, str):
|
|
45
|
+
try:
|
|
46
|
+
return dt.datetime.fromisoformat(obj)
|
|
47
|
+
except ValueError:
|
|
48
|
+
return obj
|
|
49
|
+
else:
|
|
50
|
+
return obj
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
try:
|
|
2
|
+
import numpy as np
|
|
3
|
+
except ImportError:
|
|
4
|
+
np = None
|
|
5
|
+
|
|
6
|
+
from .framework import Serializer
|
|
7
|
+
|
|
8
|
+
if np:
|
|
9
|
+
|
|
10
|
+
class NumpyArraySerializer(Serializer):
|
|
11
|
+
name = "np.array"
|
|
12
|
+
|
|
13
|
+
@classmethod
|
|
14
|
+
def serialize(cls, arr: np.ndarray):
|
|
15
|
+
return {
|
|
16
|
+
"serializer": cls.name,
|
|
17
|
+
"data": arr.tolist(),
|
|
18
|
+
"dtype": str(arr.dtype),
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def deserialize(cls, payload):
|
|
23
|
+
arr = np.array(payload["data"], dtype=payload["dtype"])
|
|
24
|
+
return arr
|
|
25
|
+
|
|
26
|
+
NumpyArraySerializer.register(np.array, np.ndarray)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from io import StringIO
|
|
3
|
+
|
|
4
|
+
try:
|
|
5
|
+
import pandas as pd
|
|
6
|
+
except ImportError:
|
|
7
|
+
pd = None
|
|
8
|
+
|
|
9
|
+
from .framework import Serializer
|
|
10
|
+
|
|
11
|
+
if pd:
|
|
12
|
+
|
|
13
|
+
class PandasDataFrameSerializer(Serializer):
|
|
14
|
+
name = "pd.DataFrame"
|
|
15
|
+
|
|
16
|
+
@classmethod
|
|
17
|
+
def serialize(cls, df):
|
|
18
|
+
serialized = {"serializer": cls.name}
|
|
19
|
+
|
|
20
|
+
if isinstance(df.index, pd.MultiIndex):
|
|
21
|
+
df = df.copy()
|
|
22
|
+
index_names = df.index.names
|
|
23
|
+
# Index names are often None
|
|
24
|
+
temp_names = [
|
|
25
|
+
f"_idx_{uuid.uuid4().hex[:8]}" for _ in range(len(df.index.names))
|
|
26
|
+
]
|
|
27
|
+
index_mapping = {
|
|
28
|
+
temp: orig for temp, orig in zip(temp_names, index_names)
|
|
29
|
+
}
|
|
30
|
+
df.index.names = temp_names
|
|
31
|
+
df = df.reset_index()
|
|
32
|
+
serialized.update(
|
|
33
|
+
{
|
|
34
|
+
"is_multi_index": True,
|
|
35
|
+
"index_mapping": index_mapping,
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
serialized.update(
|
|
40
|
+
{
|
|
41
|
+
"serializer": cls.name,
|
|
42
|
+
"data": df.to_json(date_format="iso"),
|
|
43
|
+
"dtypes": {col: str(dtype) for col, dtype in df.dtypes.items()},
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Preserve DatetimeIndex frequency if it exists
|
|
48
|
+
if isinstance(df.index, pd.DatetimeIndex) and df.index.freq is not None:
|
|
49
|
+
serialized["index_freq"] = df.index.freq.freqstr
|
|
50
|
+
|
|
51
|
+
return serialized
|
|
52
|
+
|
|
53
|
+
@classmethod
|
|
54
|
+
def deserialize(cls, payload):
|
|
55
|
+
df = pd.read_json(StringIO(payload["data"]))
|
|
56
|
+
|
|
57
|
+
# Standard column type conversion
|
|
58
|
+
for col, dtype in payload["dtypes"].items():
|
|
59
|
+
df[col] = df[col].astype(dtype)
|
|
60
|
+
|
|
61
|
+
# Handle MultiIndex reconstruction
|
|
62
|
+
if payload.get("is_multi_index"):
|
|
63
|
+
index_mapping = payload["index_mapping"]
|
|
64
|
+
# Convert temporary columns back to index with original names
|
|
65
|
+
index_cols = list(index_mapping.keys())
|
|
66
|
+
df = df.set_index(index_cols)
|
|
67
|
+
# Restore original index names
|
|
68
|
+
df.index.names = list(index_mapping.values())
|
|
69
|
+
|
|
70
|
+
# Restore DatetimeIndex frequency if it was saved
|
|
71
|
+
if isinstance(df.index, pd.DatetimeIndex) and "index_freq" in payload:
|
|
72
|
+
df.index.freq = pd.tseries.frequencies.to_offset(payload["index_freq"])
|
|
73
|
+
|
|
74
|
+
return df
|
|
75
|
+
|
|
76
|
+
PandasDataFrameSerializer.register(pd.DataFrame)
|
|
77
|
+
|
|
78
|
+
class PandasSeriesSerializer(Serializer):
|
|
79
|
+
name = "pd.Series"
|
|
80
|
+
|
|
81
|
+
@classmethod
|
|
82
|
+
def serialize(cls, series):
|
|
83
|
+
return {
|
|
84
|
+
"serializer": cls.name,
|
|
85
|
+
"data": series.to_json(date_format="iso"),
|
|
86
|
+
"dtype": str(series.dtype),
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@classmethod
|
|
90
|
+
def deserialize(cls, payload):
|
|
91
|
+
series = pd.read_json(StringIO(payload["data"]), typ="series")
|
|
92
|
+
series = series.astype(payload["dtype"])
|
|
93
|
+
return series
|
|
94
|
+
|
|
95
|
+
PandasSeriesSerializer.register(pd.Series)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* These are styles required by core functionality of xlwings Server.
|
|
3
|
+
* For your own CSS, use style.css instead.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* htmx IndicatorStyles */
|
|
7
|
+
.htmx-indicator {
|
|
8
|
+
opacity: 0;
|
|
9
|
+
}
|
|
10
|
+
.htmx-request .htmx-indicator {
|
|
11
|
+
opacity: 1;
|
|
12
|
+
transition: opacity 200ms ease-in;
|
|
13
|
+
}
|
|
14
|
+
.htmx-request.htmx-indicator {
|
|
15
|
+
opacity: 1;
|
|
16
|
+
transition: opacity 200ms ease-in;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* Alpine.js */
|
|
20
|
+
[x-cloak] {
|
|
21
|
+
/* https://alpinejs.dev/directives/cloak */
|
|
22
|
+
display: none !important;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* Custom */
|
|
26
|
+
.z-index-1000 {
|
|
27
|
+
z-index: 1000;
|
|
28
|
+
}
|
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const alpineComponents = {};
|
|
2
|
+
|
|
3
|
+
document.addEventListener("alpine:init", () => {
|
|
4
|
+
for (let name in alpineComponents) {
|
|
5
|
+
Alpine.data(name, () => alpineComponents[name]);
|
|
6
|
+
}
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
function registerAlpineComponent(name, obj) {
|
|
10
|
+
alpineComponents[name] = obj;
|
|
11
|
+
}
|