├── streamlit_highcharts ├── __version__.py ├── frontend │ ├── src │ │ ├── react-app-env.d.ts │ │ ├── index.tsx │ │ └── MyComponent.tsx │ ├── .prettierrc │ ├── tsconfig.json │ ├── public │ │ └── index.html │ └── package.json ├── example.py └── __init__.py ├── MANIFEST.in ├── README.md ├── setup.py ├── e2e ├── test_template.py └── e2e_utils.py ├── pyproject.toml ├── .gitignore └── LICENSE /streamlit_highcharts/__version__.py: -------------------------------------------------------------------------------- 1 | __version__ = '1.0.0' 2 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include streamlit_highcharts/frontend/build * 2 | -------------------------------------------------------------------------------- /streamlit_highcharts/frontend/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /streamlit_highcharts/frontend/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "lf", 3 | "semi": false, 4 | "trailingComma": "es5" 5 | } 6 | -------------------------------------------------------------------------------- /streamlit_highcharts/frontend/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom" 3 | import MyComponent from "./MyComponent" 4 | 5 | ReactDOM.render( 6 | 7 | 8 | , 9 | document.getElementById("root") 10 | ) 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # streamlit-highcharts-python 2 | 3 | Streamlit component that allows you to embed Highcharts for Python data visualizations in your Streamlit apps. 4 | 5 | ## Installation instructions 6 | 7 | ```sh 8 | pip install streamlit-highcharts-python 9 | ``` 10 | 11 | ## Usage instructions 12 | 13 | ```python 14 | import streamlit as st 15 | 16 | from streamlit_highcharts import streamlit_highcharts 17 | 18 | value = streamlit_highcharts() 19 | 20 | st.write(value) 21 | ``` -------------------------------------------------------------------------------- /streamlit_highcharts/frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "noEmit": true, 16 | "jsx": "react" 17 | }, 18 | "include": ["src"] 19 | } 20 | -------------------------------------------------------------------------------- /streamlit_highcharts/frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Streamlit Component 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /streamlit_highcharts/frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "streamlit_highcharts", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.13.1", 7 | "react-dom": "^16.13.1", 8 | "streamlit-component-lib": "^2.0.0" 9 | }, 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "react-scripts build", 13 | "test": "react-scripts test", 14 | "eject": "react-scripts eject" 15 | }, 16 | "eslintConfig": { 17 | "extends": "react-app" 18 | }, 19 | "browserslist": { 20 | "production": [ 21 | ">0.2%", 22 | "not dead", 23 | "not op_mini all" 24 | ], 25 | "development": [ 26 | "last 1 chrome version", 27 | "last 1 firefox version", 28 | "last 1 safari version" 29 | ] 30 | }, 31 | "homepage": ".", 32 | "devDependencies": { 33 | "@types/node": "^12.0.0", 34 | "@types/react": "^16.9.0", 35 | "@types/react-dom": "^16.9.0", 36 | "react-scripts": "^5.0.1", 37 | "typescript": "^4.2.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import setuptools 4 | 5 | this_directory = Path(__file__).parent 6 | long_description = (this_directory / "README.md").read_text() 7 | 8 | setuptools.setup( 9 | name="streamlit-highcharts-python", 10 | version="0.0.1", 11 | author="HCP LLC", 12 | author_email="support@highchartspython.com", 13 | description="Streamlit component that allows you to embed Highcharts for Python data visualizations in your Streamlit apps.", 14 | long_description=long_description, 15 | long_description_content_type="text/markdown", 16 | url="", 17 | packages=setuptools.find_packages(), 18 | include_package_data=True, 19 | classifiers=[], 20 | python_requires=">=3.7", 21 | install_requires=[ 22 | # By definition, a Custom Component depends on Streamlit. 23 | # If your component has other Python dependencies, list 24 | # them here. 25 | "streamlit >= 0.63", 26 | ], 27 | extras_require={ 28 | "devel": [ 29 | "wheel", 30 | "pytest==7.4.0", 31 | "playwright==1.39.0", 32 | "requests==2.31.0", 33 | "pytest-playwright-snapshot==1.0", 34 | "pytest-rerunfailures==12.0", 35 | ] 36 | } 37 | ) 38 | -------------------------------------------------------------------------------- /streamlit_highcharts/example.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from streamlit_highcharts import streamlit_highcharts 3 | 4 | # Add some test code to play with the component while it's in development. 5 | # During development, we can run this just as we would any other Streamlit 6 | # app: `$ streamlit run streamlit_highcharts/example.py` 7 | 8 | st.subheader("Component with constant args") 9 | 10 | # Create an instance of our component with a constant `name` arg, and 11 | # print its output value. 12 | num_clicks = streamlit_highcharts("World") 13 | st.markdown("You've clicked %s times!" % int(num_clicks)) 14 | 15 | st.markdown("---") 16 | st.subheader("Component with variable args") 17 | 18 | # Create a second instance of our component whose `name` arg will vary 19 | # based on a text_input widget. 20 | # 21 | # We use the special "key" argument to assign a fixed identity to this 22 | # component instance. By default, when a component's arguments change, 23 | # it is considered a new instance and will be re-mounted on the frontend 24 | # and lose its current state. In this case, we want to vary the component's 25 | # "name" argument without having it get recreated. 26 | name_input = st.text_input("Enter a name", value="Streamlit") 27 | num_clicks = streamlit_highcharts(name_input, key="foo") 28 | st.markdown("You've clicked %s times!" % int(num_clicks)) 29 | -------------------------------------------------------------------------------- /streamlit_highcharts/frontend/src/MyComponent.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Streamlit, 3 | StreamlitComponentBase, 4 | withStreamlitConnection, 5 | } from "streamlit-component-lib" 6 | import React, { ReactNode } from "react" 7 | 8 | interface State { 9 | numClicks: number 10 | isFocused: boolean 11 | } 12 | 13 | /** 14 | * This is a React-based component template. The `render()` function is called 15 | * automatically when your component should be re-rendered. 16 | */ 17 | class MyComponent extends StreamlitComponentBase { 18 | public state = { numClicks: 0, isFocused: false } 19 | 20 | public render = (): ReactNode => { 21 | // Arguments that are passed to the plugin in Python are accessible 22 | // via `this.props.args`. Here, we access the "name" arg. 23 | const name = this.props.args["name"] 24 | 25 | // Streamlit sends us a theme object via props that we can use to ensure 26 | // that our component has visuals that match the active theme in a 27 | // streamlit app. 28 | const { theme } = this.props 29 | const style: React.CSSProperties = {} 30 | 31 | // Maintain compatibility with older versions of Streamlit that don't send 32 | // a theme object. 33 | if (theme) { 34 | // Use the theme object to style our button border. Alternatively, the 35 | // theme style is defined in CSS vars. 36 | const borderStyling = `1px solid ${ 37 | this.state.isFocused ? theme.primaryColor : "gray" 38 | }` 39 | style.border = borderStyling 40 | style.outline = borderStyling 41 | } 42 | 43 | // Show a button and some text. 44 | // When the button is clicked, we'll increment our "numClicks" state 45 | // variable, and send its new value back to Streamlit, where it'll 46 | // be available to the Python program. 47 | return ( 48 | 49 | Hello, {name}!   50 | 59 | 60 | ) 61 | } 62 | 63 | /** Click handler for our "Click Me!" button. */ 64 | private onClicked = (): void => { 65 | // Increment state.numClicks, and pass the new value back to 66 | // Streamlit via `Streamlit.setComponentValue`. 67 | this.setState( 68 | prevState => ({ numClicks: prevState.numClicks + 1 }), 69 | () => Streamlit.setComponentValue(this.state.numClicks) 70 | ) 71 | } 72 | 73 | /** Focus handler for our "Click Me!" button. */ 74 | private _onFocus = (): void => { 75 | this.setState({ isFocused: true }) 76 | } 77 | 78 | /** Blur handler for our "Click Me!" button. */ 79 | private _onBlur = (): void => { 80 | this.setState({ isFocused: false }) 81 | } 82 | } 83 | 84 | // "withStreamlitConnection" is a wrapper function. It bootstraps the 85 | // connection between your component and the Streamlit app, and handles 86 | // passing arguments from Python -> Component. 87 | // 88 | // You don't need to edit withStreamlitConnection (but you're welcome to!). 89 | export default withStreamlitConnection(MyComponent) 90 | -------------------------------------------------------------------------------- /e2e/test_template.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import pytest 4 | 5 | from playwright.sync_api import Page, expect 6 | 7 | from e2e_utils import StreamlitRunner 8 | 9 | ROOT_DIRECTORY = Path(__file__).parent.parent.absolute() 10 | BASIC_EXAMPLE_FILE = ROOT_DIRECTORY / "my_component" / "example.py" 11 | 12 | @pytest.fixture(autouse=True, scope="module") 13 | def streamlit_app(): 14 | with StreamlitRunner(BASIC_EXAMPLE_FILE) as runner: 15 | yield runner 16 | 17 | 18 | @pytest.fixture(autouse=True, scope="function") 19 | def go_to_app(page: Page, streamlit_app: StreamlitRunner): 20 | page.goto(streamlit_app.server_url) 21 | # Wait for app to load 22 | page.get_by_role("img", name="Running...").is_hidden() 23 | 24 | 25 | def test_should_render_template(page: Page): 26 | frame_0 = page.frame_locator( 27 | 'iframe[title="my_component\\.my_component"]' 28 | ).nth(0) 29 | frame_1 = page.frame_locator( 30 | 'iframe[title="my_component\\.my_component"]' 31 | ).nth(1) 32 | 33 | st_markdown_0 = page.get_by_role('paragraph').nth(0) 34 | st_markdown_1 = page.get_by_role('paragraph').nth(1) 35 | 36 | expect(st_markdown_0).to_contain_text("You've clicked 0 times!") 37 | 38 | frame_0.get_by_role("button", name="Click me!").click() 39 | 40 | expect(st_markdown_0).to_contain_text("You've clicked 1 times!") 41 | expect(st_markdown_1).to_contain_text("You've clicked 0 times!") 42 | 43 | frame_1.get_by_role("button", name="Click me!").click() 44 | frame_1.get_by_role("button", name="Click me!").click() 45 | 46 | expect(st_markdown_0).to_contain_text("You've clicked 1 times!") 47 | expect(st_markdown_1).to_contain_text("You've clicked 2 times!") 48 | 49 | page.get_by_label("Enter a name").click() 50 | page.get_by_label("Enter a name").fill("World") 51 | page.get_by_label("Enter a name").press("Enter") 52 | 53 | expect(frame_1.get_by_text("Hello, World!")).to_be_visible() 54 | 55 | frame_1.get_by_role("button", name="Click me!").click() 56 | 57 | expect(st_markdown_0).to_contain_text("You've clicked 1 times!") 58 | expect(st_markdown_1).to_contain_text("You've clicked 3 times!") 59 | 60 | 61 | def test_should_change_iframe_height(page: Page): 62 | frame = page.frame_locator('iframe[title="my_component\\.my_component"]').nth(1) 63 | 64 | expect(frame.get_by_text("Hello, Streamlit!")).to_be_visible() 65 | 66 | locator = page.locator('iframe[title="my_component\\.my_component"]').nth(1) 67 | 68 | page.wait_for_timeout(1000) 69 | init_frame_height = locator.bounding_box()['height'] 70 | assert init_frame_height != 0 71 | 72 | page.get_by_label("Enter a name").click() 73 | 74 | page.get_by_label("Enter a name").fill(35 * "Streamlit ") 75 | page.get_by_label("Enter a name").press("Enter") 76 | 77 | expect(frame.get_by_text("Streamlit Streamlit Streamlit")).to_be_visible() 78 | 79 | page.wait_for_timeout(1000) 80 | frame_height = locator.bounding_box()['height'] 81 | assert frame_height > init_frame_height 82 | 83 | page.set_viewport_size({"width": 150, "height": 150}) 84 | 85 | expect(frame.get_by_text("Streamlit Streamlit Streamlit")).not_to_be_in_viewport() 86 | 87 | page.wait_for_timeout(1000) 88 | frame_height_after_viewport_change = locator.bounding_box()['height'] 89 | assert frame_height_after_viewport_change > frame_height 90 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "hatchling>=1.6", 4 | ] 5 | build-backend = "hatchling.build" 6 | 7 | [project] 8 | name = "streamlit-highcharts" 9 | description = "Streamlit component that for embedding Highcharts for Python data visualizations in your Streamlit apps." 10 | readme = "README.md" 11 | license-files = { paths = ["LICENSE"] } 12 | 13 | dynamic = [ 14 | "version" 15 | ] 16 | 17 | authors = [ 18 | { name = "HCP LLC", email = "support@highchartspython.com" } 19 | ] 20 | 21 | keywords = [ 22 | "highcharts", 23 | "data visualization", 24 | "data viz", 25 | "charts", 26 | "graphing", 27 | "streamlit", 28 | "plotting" 29 | ] 30 | 31 | classifiers = [ 32 | "Development Status :: 5 - Production/Stable", 33 | "Environment :: Web Environment", 34 | "Operating System :: OS Independent", 35 | "Intended Audience :: Developers", 36 | "Intended Audience :: Education", 37 | "Intended Audience :: Financial and Insurance Industry", 38 | "Intended Audience :: Healthcare Industry", 39 | "Intended Audience :: Manufacturing", 40 | "Intended Audience :: Science/Research", 41 | 42 | "Topic :: Software Development :: Libraries", 43 | "Topic :: Software Development :: Code Generators", 44 | "Topic :: Utilities", 45 | "Topic :: Internet :: WWW/HTTP :: Dynamic Content", 46 | "Topic :: Office/Business", 47 | "Topic :: Scientific/Engineering :: Visualization", 48 | "Topic :: Scientific/Engineering :: Information Analysis", 49 | 50 | "License :: Other/Proprietary License" 51 | ] 52 | 53 | requires-python = ">= 3.10" 54 | dependencies = [ 55 | "highcharts-core>=1.9.3", 56 | "streamlit >= 0.63", 57 | ] 58 | 59 | [tool.hatch.version] 60 | path = "streamlit_highcharts/__version__.py" 61 | 62 | [project.urls] 63 | "Homepage" = "https://highchartspython.com" 64 | "Documentation" = "https://streamlit.highchartspython.com/en/latest/" 65 | "Support" = "https://www.highchartspython.com/get-help" 66 | "Source Code" = "https://github.com/highcharts-for-python/streamlit-highcharts-python" 67 | "History" = "https://github.com/highcharts-for-python/streamlit-highcharts-python/blob/main/CHANGES.rst" 68 | "Bug Tracker" = "https://github.com/highcharts-for-python/streamlit-highcharts-python/issues" 69 | "Demo" = "https://github.com/highcharts-for-python/highcharts-for-python-demos" 70 | "Sponsor" = "https://github.com/sponsors/highcharts-for-python" 71 | 72 | [project.optional-dependencies] 73 | dev = [ 74 | "playwright==1.39.0", 75 | "pytest>=7.4.0", 76 | "pytest-cov>=3.0.0", 77 | "pytest-xdist>=2.5.0", 78 | "python-dotenv>=1.0.0", 79 | "pytest-playwright-snapshot==1.0", 80 | "pytest-rerunfailures==12.0", 81 | "requests==2.31.0", 82 | "Sphinx==6.1.3", 83 | "sphinx-rtd-theme==1.2.0", 84 | "sphinx-toolbox==3.4.0", 85 | "sphinx-tabs==3.4.1", 86 | ] 87 | soft = [ 88 | "IPython>=8.10.0", 89 | "pyspark>=3.3.0", 90 | "pandas>=1.3.3", 91 | "orjson>=3.7.7", 92 | "anthropic>=0.3.11", 93 | "dill>=0.3.7", 94 | "openai>=0.28.0" 95 | ] 96 | ai = [ 97 | "anthropic>=0.3.11", 98 | "dill>=0.3.7", 99 | "openai>=0.28.0" 100 | ] 101 | docs = [ 102 | "Sphinx==6.1.3", 103 | "sphinx-rtd-theme==1.2.0", 104 | "sphinx-toolbox==3.4.0", 105 | "sphinx-tabs==3.4.1" 106 | ] -------------------------------------------------------------------------------- /streamlit_highcharts/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import streamlit.components.v1 as components 3 | 4 | # Create a _RELEASE constant. We'll set this to False while we're developing 5 | # the component, and True when we're ready to package and distribute it. 6 | # (This is, of course, optional - there are innumerable ways to manage your 7 | # release process.) 8 | _RELEASE = False 9 | 10 | # Declare a Streamlit component. `declare_component` returns a function 11 | # that is used to create instances of the component. We're naming this 12 | # function "_component_func", with an underscore prefix, because we don't want 13 | # to expose it directly to users. Instead, we will create a custom wrapper 14 | # function, below, that will serve as our component's public API. 15 | 16 | # It's worth noting that this call to `declare_component` is the 17 | # *only thing* you need to do to create the binding between Streamlit and 18 | # your component frontend. Everything else we do in this file is simply a 19 | # best practice. 20 | 21 | if not _RELEASE: 22 | _component_func = components.declare_component( 23 | # We give the component a simple, descriptive name ("streamlit_highcharts" 24 | # does not fit this bill, so please choose something better for your 25 | # own component :) 26 | "streamlit_highcharts", 27 | # Pass `url` here to tell Streamlit that the component will be served 28 | # by the local dev server that you run via `npm run start`. 29 | # (This is useful while your component is in development.) 30 | url="http://localhost:3001", 31 | ) 32 | else: 33 | # When we're distributing a production version of the component, we'll 34 | # replace the `url` param with `path`, and point it to the component's 35 | # build directory: 36 | parent_dir = os.path.dirname(os.path.abspath(__file__)) 37 | build_dir = os.path.join(parent_dir, "frontend/build") 38 | _component_func = components.declare_component("streamlit_highcharts", path=build_dir) 39 | 40 | 41 | # Create a wrapper function for the component. This is an optional 42 | # best practice - we could simply expose the component function returned by 43 | # `declare_component` and call it done. The wrapper allows us to customize 44 | # our component's API: we can pre-process its input args, post-process its 45 | # output value, and add a docstring for users. 46 | def streamlit_highcharts(name, key=None): 47 | """Create a new instance of "streamlit_highcharts". 48 | 49 | Parameters 50 | ---------- 51 | name: str 52 | The name of the thing we're saying hello to. The component will display 53 | the text "Hello, {name}!" 54 | key: str or None 55 | An optional key that uniquely identifies this component. If this is 56 | None, and the component's arguments are changed, the component will 57 | be re-mounted in the Streamlit frontend and lose its current state. 58 | 59 | Returns 60 | ------- 61 | int 62 | The number of times the component's "Click Me" button has been clicked. 63 | (This is the value passed to `Streamlit.setComponentValue` on the 64 | frontend.) 65 | 66 | """ 67 | # Call through to our private component function. Arguments we pass here 68 | # will be sent to the frontend, where they'll be available in an "args" 69 | # dictionary. 70 | # 71 | # "default" is a special argument that specifies the initial return 72 | # value of the component before the user has interacted with it. 73 | component_value = _component_func(name=name, key=key, default=0) 74 | 75 | # We could modify the value returned from the component if we wanted. 76 | # There's no need to do this in our simple example - but it's an option. 77 | return component_value 78 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | streamlit_highcharts/frontend/node_modules/ 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | cover/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | db.sqlite3 63 | db.sqlite3-journal 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | .pybuilder/ 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | # For a library or package, you might want to ignore these files since the code is 88 | # intended to run in multiple environments; otherwise, check them in: 89 | # .python-version 90 | 91 | # pipenv 92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 95 | # install all needed dependencies. 96 | #Pipfile.lock 97 | 98 | # poetry 99 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 100 | # This is especially recommended for binary packages to ensure reproducibility, and is more 101 | # commonly ignored for libraries. 102 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 103 | #poetry.lock 104 | 105 | # pdm 106 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 107 | #pdm.lock 108 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 109 | # in version control. 110 | # https://pdm.fming.dev/#use-with-ide 111 | .pdm.toml 112 | 113 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 114 | __pypackages__/ 115 | 116 | # Celery stuff 117 | celerybeat-schedule 118 | celerybeat.pid 119 | 120 | # SageMath parsed files 121 | *.sage.py 122 | 123 | # Environments 124 | .env 125 | .venv 126 | env/ 127 | venv/ 128 | ENV/ 129 | env.bak/ 130 | venv.bak/ 131 | 132 | # Spyder project settings 133 | .spyderproject 134 | .spyproject 135 | 136 | # Rope project settings 137 | .ropeproject 138 | 139 | # mkdocs documentation 140 | /site 141 | 142 | # mypy 143 | .mypy_cache/ 144 | .dmypy.json 145 | dmypy.json 146 | 147 | # Pyre type checker 148 | .pyre/ 149 | 150 | # pytype static type analyzer 151 | .pytype/ 152 | 153 | # Cython debug symbols 154 | cython_debug/ 155 | 156 | # PyCharm 157 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 158 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 159 | # and can be added to the global gitignore or merged into this file. For a more nuclear 160 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 161 | #.idea/ 162 | -------------------------------------------------------------------------------- /e2e/e2e_utils.py: -------------------------------------------------------------------------------- 1 | import contextlib 2 | import logging 3 | import os 4 | import shlex 5 | import socket 6 | import subprocess 7 | import sys 8 | import time 9 | import typing 10 | from contextlib import closing 11 | from tempfile import TemporaryFile 12 | 13 | import requests 14 | 15 | 16 | LOGGER = logging.getLogger(__file__) 17 | 18 | 19 | def _find_free_port(): 20 | """Find and return a free port on the local machine.""" 21 | with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s: 22 | s.bind(("", 0)) # 0 means that the OS chooses a random port 23 | s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 24 | return int(s.getsockname()[1]) # [1] contains the randomly selected port number 25 | 26 | 27 | class AsyncSubprocess: 28 | """A context manager. Wraps subprocess. Popen to capture output safely.""" 29 | 30 | def __init__(self, args: typing.List[str], cwd: typing.Optional[str] = None, 31 | env: typing.Optional[typing.Dict[str, str]] = None): 32 | """Initialize an AsyncSubprocess instance. 33 | 34 | Args: 35 | args (List[str]): List of command-line arguments. 36 | cwd (str, optional): Current working directory. Defaults to None. 37 | env (dict, optional): Environment variables. Defaults to None. 38 | """ 39 | self.args = args 40 | self.cwd = cwd 41 | self.env = env 42 | self._proc = None 43 | self._stdout_file = None 44 | 45 | def terminate(self) -> typing.Optional[str]: 46 | """Terminate the process and return its stdout/stderr in a string.""" 47 | if self._proc is not None: 48 | self._proc.terminate() 49 | self._proc.wait() 50 | self._proc = None 51 | 52 | # Read the stdout file and close it 53 | stdout = None 54 | if self._stdout_file is not None: 55 | self._stdout_file.seek(0) 56 | stdout = self._stdout_file.read() 57 | self._stdout_file.close() 58 | self._stdout_file = None 59 | 60 | return stdout 61 | 62 | def __enter__(self) -> "AsyncSubprocess": 63 | """Start the subprocess when entering the context.""" 64 | self.start() 65 | return self 66 | 67 | def __exit__(self, exc_type, exc_val, exc_tb): 68 | """Stop the subprocess and close resources when exiting the context.""" 69 | self.stop() 70 | 71 | def start(self): 72 | # Start the process and capture its stdout/stderr output to a temp 73 | # file. We do this instead of using subprocess.PIPE (which causes the 74 | # Popen object to capture the output to its own internal buffer), 75 | # because large amounts of output can cause it to deadlock. 76 | self._stdout_file = TemporaryFile("w+") 77 | LOGGER.info("Running command: %s", shlex.join(self.args)) 78 | self._proc = subprocess.Popen( 79 | self.args, 80 | cwd=self.cwd, 81 | stdout=self._stdout_file, 82 | stderr=subprocess.STDOUT, 83 | text=True, 84 | env={**os.environ.copy(), **self.env} if self.env else None, 85 | ) 86 | 87 | def stop(self): 88 | """Terminate the subprocess and close resources.""" 89 | if self._proc is not None: 90 | self._proc.terminate() 91 | self._proc = None 92 | if self._stdout_file is not None: 93 | self._stdout_file.close() 94 | self._stdout_file = None 95 | 96 | 97 | class StreamlitRunner: 98 | """A context manager for running Streamlit scripts.""" 99 | 100 | def __init__( 101 | self, script_path: os.PathLike, server_port: typing.Optional[int] = None 102 | ): 103 | """Initialize a StreamlitRunner instance. 104 | 105 | Args: 106 | script_path (os.PathLike): Path to the Streamlit script to run. 107 | server_port (int, optional): Port for the Streamlit server. Defaults to None. 108 | """ 109 | self._process = None 110 | self.server_port = server_port 111 | self.script_path = script_path 112 | 113 | def __enter__(self) -> "StreamlitRunner": 114 | """Start the Streamlit server when entering the context.""" 115 | self.start() 116 | return self 117 | 118 | def __exit__(self, type, value, traceback): 119 | """Stop the Streamlit server and close resources when exiting the context.""" 120 | self.stop() 121 | 122 | def start(self): 123 | """Start the Streamlit server using the specified script and options.""" 124 | self.server_port = self.server_port or _find_free_port() 125 | self._process = AsyncSubprocess( 126 | [ 127 | sys.executable, 128 | "-m", 129 | "streamlit", 130 | "run", 131 | str(self.script_path), 132 | f"--server.port={self.server_port}", 133 | "--server.headless=true", 134 | "--browser.gatherUsageStats=false", 135 | "--global.developmentMode=false", 136 | ] 137 | ) 138 | self._process.start() 139 | if not self.is_server_running(): 140 | self._process.stop() 141 | raise RuntimeError("Application failed to start") 142 | 143 | def stop(self): 144 | """Stop the Streamlit server and close resources.""" 145 | self._process.stop() 146 | 147 | def is_server_running(self, timeout: int = 30) -> bool: 148 | """Check if the Streamlit server is running. 149 | 150 | Args: 151 | timeout (int, optional): Maximum time to wait for the server to start. Defaults to 30. 152 | 153 | Returns: 154 | bool: True if the server is running, False otherwise. 155 | """ 156 | with requests.Session() as http_session: 157 | start_time = time.time() 158 | while True: 159 | with contextlib.suppress(requests.RequestException): 160 | response = http_session.get(self.server_url + "/_stcore/health") 161 | if response.text == "ok": 162 | return True 163 | time.sleep(3) 164 | if time.time() - start_time > 60 * timeout: 165 | return False 166 | 167 | @property 168 | def server_url(self) -> str: 169 | """Get the URL of the Streamlit server.""" 170 | if not self.server_port: 171 | raise RuntimeError("Unknown server port") 172 | return f"http://localhost:{self.server_port}" 173 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ####################################################################### 2 | Terms and Conditions for the Highcharts for Python Toolkit License 3 | ####################################################################### 4 | 5 | *********************************************** 6 | 1. Acceptance of the Terms and Conditions 7 | *********************************************** 8 | 9 | These terms and conditions (the “Wrapper T&Cs”) shall apply to Licensee’s license to and use of the Wrapper (as defined below), a complementary product to the Software owned and delivered by Highsoft AS (“Highsoft”). The Wrapper is owned by HCP LLC (“HCP”) and distributed by Highsoft AS or its partners, resellers or distributors, and these Wrapper T&Cs shall apply to the Licensee’s usage of the Wrapper irrespective of which license type(s) to the Software the Licensee has subscribed to or purchased from Highsoft. 10 | 11 | By installing or using the Wrapper or any part thereof, Licensee agrees to be bound by these Wrapper T&Cs, unless Licensee is using the Software and the Wrapper for evaluation purposes authorized by Highsoft, i.e. during a trial period. 12 | 13 | Without prejudice to the limited right to download and test the Wrapper for evaluation prior to the purchase of a Wrapper License, Licensee is not authorized to install or use the Wrapper unless Licensee pays the applicable Wrapper Fee and fully agrees to all terms and conditions set forth herein and to any terms set forth in related licensing agreements or terms from Highsoft to which these Wrapper T&Cs constitute an addendum. 14 | 15 | *********************************************** 16 | 2. Relationship to the Main Agreement 17 | *********************************************** 18 | 19 | A Wrapper License (as defined below) or renewal thereof can only be purchased by a Licensee either already holding a License to the Software, or at the same time as purchasing such a License, as an add-on product to such License, and only by a Licensee holding an active and valid enrolment in the Highcharts Advantage plan. The purchase of, and right to use the Licensed Software shall remain governed by the terms and conditions of the License issued by Highsoft to Licensee, whether this is Highsoft Standard License Agreement, Highsoft Terms and Conditions for Subscription to Annual License to Highsoft Software, or another licensing agreement as agreed between Highsoft and Licensee, as applicable (such terms and conditions between Highsoft and Licensee hereinafter collectively referred to as the “Main Agreement”). 20 | 21 | A Wrapper License (as defined below) can additionally be obtained by holding an active and valid Educational or Personal License to the Software. The right to use the Licensed Software shall remain governed by the terms and conditions of the Authorized User issued by Highsoft to Authorized User, as defined in the Educational and Personal License 1.0, (such terms and conditions between Highsoft and Authorized User hereinafter collectively referred to as the "Main Agreement"). 22 | 23 | These Wrapper T&Cs constitute an addendum to the Main Agreement and an integral part thereof and apply to the use of the Wrapper only. 24 | 25 | In the case of any discrepancy between the Main Agreement and these Wrapper T&Cs related to the licensing of the Wrapper, these Wrapper T&Cs shall prevail. 26 | 27 | *********************************************** 28 | 3. Definitions 29 | *********************************************** 30 | 31 | Unless otherwise defined below or elsewhere in these Wrapper T&Cs, all capitalized terms used but not otherwise defined herein shall have the same definitions as set out in the Main Agreement. 32 | 33 | * **Agreement** shall mean the Main Agreement, these Wrapper T&Cs and the License Statement; 34 | * **Delivery Date** shall mean the date Licensee is invoiced by Highsoft for the Wrapper License, which will 35 | correspond to the Delivery Date for the Software if purchased together, or at a separate time, if purchased as an 36 | add-on to an existing License; 37 | * **HCP** shall mean HCP LLC, the company behind the Wrapper, a US limited liability corporation registered in the 38 | state of Delaware, with its registered office at 867 Boylston Street, 5th Floor #1674, Boston, MA 02116, United 39 | States.Licensee shall mean the legal entity to which the License (to the Licensed Software) and the Wrapper License 40 | has been granted, as expressly stated in the License Statement; 41 | * **Licensors** shall mean Highsoft and/or HCP collectively; 42 | * **Wrapper** shall mean the proprietary software products distributed by Highsoft and owned by HCP, including 43 | Highcharts for Python – the main product included in all licenses – in addition to Highcharts Stock for Python, 44 | Highcharts Maps for Python, Highcharts Gantt for Python, and other optional additional products or libraries which 45 | may be developed by HCP and offered within the Wrapper by Highsoft. 46 | * **Wrapper** Fee shall mean the fee payable by Licensee to Highsoft for the Wrapper License and for the right to 47 | receive the support services provided by HCP under these Wrapper T&Cs ; 48 | * **Wrapper License** shall mean the right to use the Wrapper in accordance with the Agreement and as set out herein, 49 | whether granted as a perpetual license or in the form of a time-limited subscription (subject to renewal), and as 50 | one or more of the license types as defined in the Main Agreement; 51 | * **Wrapper Term** shall have the meaning set out in section 7. 52 | 53 | *********************************************** 54 | 4. Ownership and Copyright 55 | *********************************************** 56 | 57 | The Wrapper is the property of HCP and is protected by copyright law as well as other statutory and non-statutory intellectual property law. All title and copyrights in and to the Wrapper are and shall remain owned fully and solely by HCP. 58 | 59 | Under the Agreement, the Wrapper is licensed to Licensee by Highsoft on behalf of HCP, not sold. 60 | 61 | The Licensors reserve all rights not expressly granted to Licensee under the Agreement. Without limiting the generality of the foregoing, Licensee acknowledges and agrees that: (a) except as specifically set forth herein, the Licensors retains all right, title and interest in and to the Wrapper and the Software, and Licensee does not acquire any right, title or interest to the Wrapper or the Software except as set forth herein; (b) any configuration or deployment of the Wrapper or the Software shall not affect or diminish the Licensor’s rights, title or interest in and to the Wrapper or the Software. The Agreement shall not limit in any way the Licensor’s right to develop, use, license, create derivative works of, or otherwise exploit the Wrapper or the Software, or to permit Third Parties to do so. 62 | 63 | The Licensors acknowledge and agree that (i) Licensee retains all rights, title and interest in and to any Licensee-owned software, and the Licensors do not acquire any right, title, or interest in or to such application; and (ii) any integration of the Wrapper with a Licensee-owned application shall not affect or diminish Licensee’s rights, title, and interest in and to such application. 64 | 65 | *********************************************** 66 | 5. Grant of License 67 | *********************************************** 68 | 69 | Subject to Licensee’s full payment of the Wrapper Fee, Highsoft grants to Licensee a right to use the Wrapper during the Wrapper Term (as defined in Section 7 below), on the same terms and conditions as set forth in the Main Agreement and the License Statement as modified by these Wrapper T&Cs. The Main Agreement shall apply accordingly to the Wrapper License insofar as the terms and conditions therein are applicable, whereas all references to the “Licensed Software” in the Main Agreement shall for the purposes of these Wrapper T&Cs to the extent applicable be deemed to include the Wrapper. 70 | 71 | The License type(s) chosen by Licensee for the Licensed Software shall apply to the Wrapper License, and depending on the purchased License type(s), as stated in the Licensed Statement, the relevant section in the Main Agreement detailing the usage rights and limitations for the License shall apply similarly to the Wrapper License. 72 | 73 | The rights granted to Licensee under the Agreement, is strictly limited to the usage rights granted under the chosen License type and with the scope as stated in the License Statement. The number of authorized Developers included in the Wrapper License is the same as the number of authorized Developers included in the License, as set out in the License Statement. The Wrapper may only be used in such Web Application(s), SaaS Application(s) and/or Licensee Product(s) as expressly identified in the License Statement. 74 | 75 | A Wrapper License shall include the components of the Wrapper which correspond to the Licensed Software to which Licensee holds a valid License, i.e. a Wrapper License purchased for Highcharts Stock shall include the Highcharts for Python software and all component libraries needed to implement the Highcharts Stock for Python library, and a Wrapper License purchased for Highcharts Maps shall include the Highcharts for Python software and all component libraries needed to implement the Highcharts Maps for Python, etc. 76 | 77 | 5.1 General Grants and Limitations 78 | ======================================== 79 | 80 | The Wrapper License includes the support services provided by HCP set forth in Section 6 below. 81 | Irrespective of the chosen License type, the Wrapper License and any support services for the Wrapper shall be subject to renewal, and contingent upon Licensee’s continued enrollment in the Highcharts Advantage plan, as set out in section 7. 82 | 83 | Licensee may obtain the Wrapper source code by downloading the source code from the Highsoft Website or from Github or equivalent source code repository made available by HCP, by downloading and installing the source code from a public repository such as PyPi, and make its own edits, and keep its own repositories with the modified source code; provided, however, any such modifications shall be at Licensee’s own risk and shall void any support obligation of HCP hereunder. 84 | 85 | Licensee shall not modify, delete or obscure any notices of proprietary rights or any Wrapper identification or restrictions on or in the Wrapper found in the source code. 86 | 87 | *********************************************** 88 | 6. Wrapper Support 89 | *********************************************** 90 | 91 | A Wrapper License entitles Licensee to the support services and access to new Releases of the Wrapper as set out herein. While support for the Wrapper is contingent upon Licensee’s valid enrollment in the Highcharts Advantage plan and the annual number of hours of support available for support of the Licensed Software for each successive twelve month term during the period that Licensee is enrolled in Highcharts Advantage (each, a “Support Year”) are inclusive of the number of hours of support offered during such Support Year for the Wrapper, support of the Wrapper is not covered under Licensee’s enrollment in Highcharts Advantage, but is offered by Highsoft for a separate fee and provided separately, directly and independently by HCP. In the event that the Licensee is not enrolled in the Highcharts Advantage Plan, for example if the Main Agreement grants the Licensee a Personal or Educational License, then the Licensee shall not be entitled to the support services outlined herein. 92 | 93 | All support inquiries related to the Wrapper shall be sent to support@highchartspython.com or filed at https://www.highchartspython.com. 94 | 95 | Under a valid and effective Wrapper License, Licensee shall be entitled to receive from HCP: 96 | 97 | i. All new releases or updates of the Wrapper released during the applicable Advantage Period; 98 | 99 | Under a valid and effective Wrapper License, contingent upon the Licensee’s valid enrollment in the Highcharts Advantage Plan and payment of applicable fees, Licensee shall be entitled to receive from HCP: 100 | 101 | ii. Up to ten (10) hours of the personalized technical support for the Wrapper and/or the Licensed Software 102 | (combined) per Developer per Support Year based on the number of Developers stated in the License Statement for 103 | the License. Licensee may freely distribute its included total of ten (10) hours of personalized technical 104 | support between support of the Wrapper and support of the Licensed Software; 105 | iii. Technical support by e-mail; 106 | iv. Priority response; 107 | v. Access to 2nd line support for the Wrapper by core developers; 108 | vi. Online text chat with 1st line support Wrapper engineers; 109 | vii. Investigation of any claimed bug/error/malfunction/nonfunctioning of the Wrapper, and when possible, 110 | suggestions as to corrective or work-around solutions to the problems; 111 | viii. Supply of emergency hot fixes to the Wrapper; 112 | ix. Guidance and advice on implementing the Wrapper with the Software, as well as with any third-party systems and 113 | platforms to the extent such implementation is authorized by Highsoft. The guidance and advice shall include 114 | advice on best practices, limited code review, and guidance on parts of the code that are directly related to 115 | using the Wrapper with the Software; 116 | x. Any bug and error fixing, malfunctioning of the Wrapper is to be delivered outside the personalized technical 117 | support hours. 118 | 119 | Licensee is responsible for downloading and installing major version releases and updates of the Wrapper during the applicable Advantage Period. During each Advantage Period in which Licensee is validly enrolled in Highcharts Advantage, HCP will provide support for the current version and last major version releases of the Wrapper. For the avoidance of doubt, HCP shall have no obligation to provide support for any version of the Wrapper released prior to the major version release which immediately preceded the then current major version release of the Wrapper. 120 | 121 | The support services as set forth in this section (i) do not cover issues arising in connection with implementation of the Wrapper or Licensed Software in/to Licensee Products or Licensee’s own applications or to the Wrapper as modified by Licensee, and (ii) shall not extend to any Third Parties to which Licensee distributes Licensee Products, SaaS Application(s) or Web Application(s) containing the Wrapper, Licensed Software or any part thereof. Support to any Licensee customers shall hence be Licensee’s full and sole responsibility. The Licensors may, at its sole discretion, at any time choose to discontinue the supply of new Releases of the Wrapper. 122 | 123 | *********************************************** 124 | 7. Term and Renewal 125 | *********************************************** 126 | 127 | The term of the Wrapper License (the ”Wrapper Term”) shall correspond to the Initial Term of the License as indicated in the License Statement issued by Highsoft, and the provisions of the Main Agreement pertaining to the terms and conditions on the Term and Renewal of the License shall apply similarly to the Wrapper License; provided, however, in all cases, the support services set forth in Section 6 above are co-terminus with and contingent upon the Licensee’s enrollment in the Highsoft Advantage Plan as set forth in the applicable Main Agreement, including any renewal thereof, and payment of the corresponding Wrapper Fee. 128 | 129 | During the term of the Wrapper License, the Wrapper shall be made available by the Licensors and Licensee shall be authorized to download the Wrapper from the Highsoft Website, from Github or comparable source code repository maintained by HCP, or from a public Python library repository such as PyPi. 130 | 131 | *********************************************** 132 | 8. Termination 133 | *********************************************** 134 | 135 | The termination and expiration provisions in the Main Agreement shall apply similarly to the Agreement, and a termination or expiration of the Main Agreement, however occasioned, shall be construed as, and entail a termination of this Agreement. 136 | 137 | *********************************************************** 138 | 9. Annual License Fee, Renewal Fee, and Payment Terms 139 | *********************************************************** 140 | 141 | Licensee shall upon purchase of the Wrapper License pay the applicable Wrapper License Fee as determined by Highsoft, subject to the provisions on payment of the License Fee and Highcharts Advantage Fee as set out in the Main Agreement. 142 | For avoidance of doubt, the Wrapper License and accompanying rights including any subsequent renewals is granted to Licensee on the condition that all the due fees are paid to Highsoft in full and on time. 143 | 144 | *********************************************************** 145 | 10. Warranties and Representations 146 | *********************************************************** 147 | 148 | 10.1 Scope 149 | ================= 150 | 151 | All warranties and representations given herein are provided by HCP, and HCP’s warranties and representations in this section 10 are limited to the Wrapper provided to Licensee under the Agreement. 152 | 153 | 10.2 HCP’s Warranties and Representations 154 | =============================================== 155 | 156 | HCP warrants and represents that: 157 | 158 | i. For a period of ninety (90) days following its Delivery Date, the Wrapper will perform substantially in 159 | accordance with HCP’s written specifications, provided that it has been used in accordance with all documentation 160 | and specifications made available on Highsoft's Website and not modified by Licensee, 161 | ii. HCP will perform its obligations under the Wrapper License and these Wrapper T&Cs in accordance with all 162 | applicable laws and regulations, 163 | iii. HCP has the full and unconditional ownership of the Wrapper, 164 | iv. The Wrapper does not infringe intellectual property rights of any Third Party, 165 | v. When installed in accordance with HCP’s written specifications, Third Party Dependencies, defined as software on 166 | which the Wrapper relies that has been developed and made available by Third Parties, shall be installed. HCP 167 | warrants that at the time of the Wrapper’s Release, such software was available for public distribution in 168 | accordance with its applicable licenses and its bundling with the Wrapper is fully compliant with the licenses of 169 | any and all such Third Party Dependencies. The Licensee can review the details of all such Third Party 170 | Dependencies, including their relevant licensing provisions, by reviewing the Wrapper documentation made 171 | available on Highsoft’s Website. 172 | vi. HCP has the requisite knowledge, personnel, resources and know-how to fully perform and deliver the Wrapper and 173 | associated services as stipulated by these Wrapper T&Cs in a professional manner, 174 | vii. HCP has not intentionally placed and will use its best efforts to avoid the placement of any Harmful Codes into 175 | the Wrapper provided under the Wrapper License. For the purpose of this section 10.2, "Harmful Codes" is 176 | defined as any program that infects, damages and/or impairs another program or data, disables hardware or 177 | software, or permits or assists in the breach of data. 178 | 179 | 10.3 Licensee’s Remedies 180 | ============================= 181 | 182 | In the event of a breach, or alleged breach of any of the warranties in section 10.2, Licensee shall promptly notify either HCP or Highsoft and delete the Wrapper. Licensee’s sole remedy in such an event shall be that HCP shall re-supply or correct the Wrapper so that it operates according to the warranties set out in section 10.2. The warranties shall not apply if Licensee has modified, or used the Wrapper improperly, or on an operating environment not approved by HCP. Improper use and unapproved operating environments will be as set forth in the documentation provided to Licensee on or prior to Delivery Date. 183 | 184 | *********************************************************** 185 | 11. Limitation of Liability 186 | *********************************************************** 187 | 188 | 11.1 Highsoft 189 | =================== 190 | 191 | The Licensee understands and accepts that the Wrapper is provided by Highsoft as an Official Wrapper, and are hence not covered by the Warranties and Representations included in the Main Agreement, and is provided “as is” by Highsoft and may have errors and omissions. Highsoft disclaims any and all liability for the Wrapper or Licensee’s usage of or reliance on the Wrapper, and makes no warranties, express or implied, including but not limited to, warranties of merchantability, fitness for purpose, performance, accuracy, or non-infringing nature. 192 | 193 | 11.2 HCP 194 | ================== 195 | 196 | The Wrapper and all related support services supplied by HCP are provided ‘as is’ and may have errors and omissions. Thus, remedies are only available to Licensee in the event of any breach of the warranties set out in section 10. 197 | 198 | UNDER NO CIRCUMSTANCES, AND EVEN IF INFORMED THEREOF BY LICENSEE OR ANY OTHER PARTY, WILL THE LICENSORS BE LIABLE UNDER OR IN CONNECTION WITH A CLAIM RELATING TO THESE WRAPPER T&CS OR ITS SUBJECT MATTER FOR (i) LOSS OF, OR DAMAGE TO, DATA; (ii) SPECIAL, INCIDENTAL, CONSEQUENTIAL OR INDIRECT DAMAGES; OR (iii) LOST PROFITS, BUSINESS, REVENUE, GOODWILL, OR ANTICIPATED SAVINGS. 199 | 200 | Incorporation of the Wrapper into any application as further described in the Main Agreement shall not in any manner expand HCP’s liabilities under the Annual Wrapper License. Thus, HCP shall not under any circumstance be neither responsible nor liable for any aspects of such Licensee application(s), including but not limited to its reliability, uptime/downtime, functioning or fitness for purpose. Any obligations, liabilities or warranties undertaken by Licensee towards its customers with respect to such application(s) shall apply only between mentioned parties, and Licensee hereby undertakes to indemnify and hold the Licensors harmless from and against any and all losses, clams and damages related to such application(s). 201 | 202 | IN NO EVENT WILL THE LIABILITY OF THE LICENSORS UNDER OR IN CONNECTION WITH THESE WRAPPER T&CS OR ITS SUBJECT MATTER, UNDER ANY LEGAL OR EQUITABLE THEORY, INCLUDING BREACH OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY, AND OTHERWISE, EXCEED THE TOTAL WRAPPER FEE PAID BY THE LICENSEE DURING THE LAST TWELVE (12) MONTHS PRIOR TO OCCURRENCE GIVING CAUSE TO SUCH LIABILITY. 203 | 204 | *********************************************************** 205 | 12. Confidentiality 206 | *********************************************************** 207 | 208 | As a trusted partner of Highsoft, HCP is bound by the confidentiality provisions of the Main Agreement, which shall apply similarly for the Wrapper T&Cs, and Licensee hereby grants to Highsoft the explicit right to share Confidential Information with HCP as needed. 209 | 210 | *********************************************************** 211 | 13. Applicable Law and Venue 212 | *********************************************************** 213 | 214 | The construction, validity and operation of the Wrapper License and these Wrapper T&Cs, and the performance of all obligations hereunder, shall be governed by and construed in accordance with the laws of the Commonwealth of Massachusetts, United States of America, without regard to conflict of law principles that would result in the application of any law other than the law of the Commonwealth of Massachusetts. 215 | 216 | In the event of a dispute, controversy or claim between Licensee and HCP arising out of or relating to the Wrapper License and these Wrapper T&Cs, or the breach, termination, or invalidity thereof the Parties shall meet in an effort to resolve such dispute, controversy or claim amicably through negotiation. If the Parties do not reach an amicable solution within two (2) weeks of such efforts being initiated, either Party may initiate legal proceedings in the United States federal courts and state courts located in the Commonwealth of Massachusetts, which courts shall have sole and exclusive jurisdiction and venue to adjudicate any such dispute, controversy or claim. The Parties consent to the exclusive jurisdiction of the courts specified above, and expressly waive any objection to the jurisdiction or convenience of such courts. 217 | 218 | Any dispute, controversy or claim between Licensee and Highsoft, shall be resolved in accordance with the provisions of the Main Agreement. 219 | 220 | *********************************************************** 221 | 14. Processing of Personal Data 222 | *********************************************************** 223 | 224 | To the extent the purchase of the Wrapper License involves processing by either Highsoft and/or HCP of personal data about the Licensee or Licensee’s customers or personnel related to the purchase of the Wrapper License, Highsoft and HCP shall be acting as joint and independent data controllers. The provisions of the Main Agreement pertaining to Highsoft’s processing of personal data shall apply similarly to any processing by Highsoft of personal data in relation to the Agreement. 225 | 226 | To the extent the support services performed by HCP under this Agreement involves processing by HCP of personal data about the Licensee or Licensee’s customers or personnel, HCP shall be acting as a data controller. 227 | 228 | *********************************************************** 229 | 15. Miscellaneous 230 | *********************************************************** 231 | 232 | The provisions of the Main Agreement entitled “Miscellaneous” shall apply similarly to these Wrapper T&Cs, as applicable. --------------------------------------------------------------------------------