diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f7ac3a..c639df7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +# 2.0.0 + +- Various bug fixes and performance improvements, including major backend refactor. This breaks some graphic config jsom layout rules, so bumping major version +- Includes documentation in-line in wizard +- Allows upload of multiple files simultaneously +- Added search box to wizard to better find graph config options, and hid more options in dropdowns by default +- Added datetime parsing functionality to numerical filters +- Includes Seaborn graphics plotting functionality, for static figures that use Python API rules. Selectors still work for filtering, but no JS tooltips and such. This does not include all Seaborn plots- some catplot-type multi figure plots not yet supported +- Added Cytoscape graphics plotting for network graph visualization + + # 1.0.0 - Removes local csv handler option, tests, and documentation diff --git a/README.md b/README.md index fbd51e5..a454108 100644 --- a/README.md +++ b/README.md @@ -31,15 +31,12 @@ Yes. Escalation has a few advantages: ## What do you need for the app to work? Note, because of Docker issues with Windows, there may be substantial hiccups running on that operating system. -The instructions below have been tested on Mac and Linux. +The instructions below have been tested on Mac and Linux. Linux users will need sudo permissions to run the following setup commands. - [Configure the app](#2.-Configuring-the-app) - Escalation uses configuration files (json) to build the dashboard organizational structure, link the data in visualizations, and construct the visualizations themselves. - These configuration files can be built by hand, using the Configuration Wizard, or any combination of the two - [Data](#2.2-Load-your-data) - - When setting up Escalation, you choose to use either a CSV or SQL backend. - - Depending on the backend, you'll either link the app to a database (new or existing) or a file system path containing your data files. - A file backend may be easier for those unfamiliar with SQL, but SQL is more performant, and storing data in a database offers advantages beyond the database's use in Escalation - Escalation includes tooling to ingest CSV files into SQL, automatically building the necessary SQL data tables and the code necessary to integrate them with Escalation. - ToDo: Data Migration helpers- what happens when the format of your data changes over time? - [Python environment to run the app](#3.-Running-the-configured-app) @@ -57,9 +54,13 @@ From the root level of the code repository, run: We recognize that Docker is less common in academic settings, but highly recommend using it. Here are [instructions](https://docs.docker.com/get-started/) on getting started using Docker. +Additionally, we use Docker Compose for running our multi-container application, which does depend on the Docker Engine. +Once you have Docker successfully installed, your next step is to install Docker Compose. +Here are [instructions](https://docs.docker.com/compose/) for the installation process. We use the Docker containers to run our configuration wizard, as well as the scripts to ingest csv data into a SQL database. Once we set up a configuration and your data, we'll also use these containers to run the web app. + ## 2. Configuring the app ### 2.1 Run the Configuration Wizard @@ -75,24 +76,8 @@ Refresh your browser to update the contents to match your saved configuration. Some notes on [creating your first config files with the UI wizard](documentation/wizard_guide/creating_first_graphic_with_wizard.md). -This includes a gallery of various chart types and how they're configured. - -![config_gallery_thumbnail](documentation/gallery/images/gallery_thumbnail.png) - - -#### Build a config from scratch (advanced, optional) -Run `python build_app_config_json_template.py` to build a base config file. -Everything blank or in `<>` should be changed. - -#### Debugging config files manually (advanced, optional) - -An example of a [main config file](documentation/main_config_example/main_config_example.md). -Examples of [different plots and graphic config files](documentation/plotly_examples/plotly_config_info.md). -Examples of [different selectors](documentation/selector_examples/selector_config_info.md). - ### 2.2 Load your data - -#### SQL database backend (recommended) +#### SQL database backend Escalation provides functionality to parse csv data files, determine the relevant sql schema, create tables in the SQL database from your file, and create the necessary `models.py` file for the app to interact with the database. @@ -104,7 +89,7 @@ Use this web form to upload each file you'd like to use for your visualizations Note, it may take a little while to run. If you'd like to add more than one csv to the same table, you have two options: -combine them before uploading, or submit the additional CSVs using the "Append to existing table" option. +Load them at the same time (by holding shift and selecting multiple files), or submit the additional CSVs using the "Append to existing table" option. Todo: If you have an existing SQL database, how do you copy it into Escalation? We require each table to have an `upload_id` column, and a key that is unique within an `upload_id` value @@ -195,22 +180,30 @@ ToDo: More detailed instructions on virtual env setup, requirements install, an ### Developing for Escalation +- optional, but recommended: run in a virtual environment of your choice. If you use conda and not pyenv or similar, use the appropriate package installer in the next step - `pip install -r requirements-dev.txt` - `pre-commit install` sets up the pre-commit hooks to auto-format the code. This is optional, the repo is formatted with Flake and Black. - +- From the `escalation/` directory (at the same level as `app,py`, run `export FLASK_ENV=development && python -m flask run` ### How to add a new type of plot -Development for Escalation has focused on Plotly, but the codebase should be compatible with other libraries or custom graphics. If you want to use something other than Plotly, your code should: -* Needs to inherit from graphic_class.py +Development for Escalation has focused on Plotly, Cytoscape, and Seaborn, but the codebase should be compatible with other libraries or custom graphics. If you want to use something other than the provided libraries, your code should: +* Inherit from graphic_class.py * Be added to available_graphics.py * Include an html file with javascript code required to plot - +* In addition, if you would like to use the wizard with the new graphic, you should define a schema with a class that inherits from graphic_schema.py. The schema should follow [JSON Schema](https://json-schema.org/). ### How to add a new option feature * add it to available_selectors.py * create a html document input elements need name "\|\|" -* add to create_data_subselect_info and reformat_filter_form_dict in controller.py -* build in functionality graphics_class or data_storer class +* add to `create_data_subselect_info`, `modify_graphic_dict_based_on_addendum_dict`, and `add_operations_to_the_data_from_addendum` as appropriate in graphic_class.py or one of its inheritors. +* build in functionality in graphic_class or data_storer inheritor +# Using and citing Escalation + + +Code submitted to [Zenodo](https://zenodo.org/) for DOI registration and version tracking. +Bibtex and similar citation formatting available here: + +[![DOI](https://zenodo.org/badge/267139951.svg)](https://zenodo.org/badge/latestdoi/267139951) # License diff --git a/documentation/droplet_directions.md b/documentation/droplet_directions.md index d01b892..4c636e2 100644 --- a/documentation/droplet_directions.md +++ b/documentation/droplet_directions.md @@ -32,5 +32,24 @@ - At this point you can log out of the ssh session and the app will continue to run - If you would like to stop the app, run `docker-compose down` in the ssh terminal to your droplet +# Pointing a domain to Escalation (Nginx) +One method to point a domain or subdomain to a port: +- Follow [the directions for setting up a server block](https://www.digitalocean.com/community/tutorials/how-to-set-up-nginx-server-blocks-virtual-hosts-on-ubuntu-16-04) +- In the directions, replace the file /etc/nginx/sites-available/example.com with + + +``` +server { + listen 80; + listen [::]:80; + + server_name your_domain.com; + + location / { + proxy_pass http://127.0.0.1:8000; + } +} +``` + todo: set Flask SECRET_KEY on server, not in code, set postgres db password, set password for admin actions \ No newline at end of file diff --git a/documentation/wizard_guide/creating_first_graphic_with_wizard.md b/documentation/wizard_guide/creating_first_graphic_with_wizard.md index 092e6c2..243f98a 100644 --- a/documentation/wizard_guide/creating_first_graphic_with_wizard.md +++ b/documentation/wizard_guide/creating_first_graphic_with_wizard.md @@ -4,31 +4,35 @@ Before starting this tutorial, you should have your wizard app launched. Ready? Let's begin. -- First open the url where wizard app is being hosted (e.g. http://localhost:8000/). Click on "Configuration Wizard" on the navbar at the top. You should see +- First, open the URL where the wizard app is being hosted (e.g. http://localhost:8000/). Click on `Configuration Wizard` on the navbar at the top. You should see ![](images/first_look.png) -- First, fill out the title, a description for your dashboard, select the data backend you're using (SQL recommended) and click save. -- Next, to add data to the app, click on "Upload" on the navigation bar on the top. Name your data table (Data File Type), - pick a csv file to upload (the csv file must have a header with column names), give a username for the uploader, and click submit. +- First, fill out the title, a description for your dashboard, and click save. +- Next, to add data to the app, click on `Upload` on the navigation bar on the top. Name your data table (Data File Type), + pick a CSV file to upload (the CSV file must have a header with column names), give a username for the uploader, and click submit. ![](images/add_csv.png) -- Click on "Configuration Wizard". +- Click on `Configuration Wizard`. - Now let's add our first page. -Enter a name for the new dashboard panel page and click "Add a Dashboard Panel." +Enter a name for the new dashboard panel page and click `Add a Dashboard Panel`. ![](images/add_page.png) -- Now let's add a graphic to the page. Click "Add a Graphic" - +- Now let's add a graphic to the page. Click `Add a Graphic`. It will take you to the graphic config landing page. +![](images/landing_page.png) +- Choose what uploaded data you would like to use and what kind of graphic you would like then click `Make Graph`. ![](images/graphic_config.png) -- Fill out the form. The html example here - [gallery/index.html](../gallery/index.html) (best opened and viewed in a browser) contains graphics and the forms that generated them. Once you are done hit submit. -Now you will have a graphic in your page. - -![](images/add_graphic.png) +- Fill out a title and a description. +- Then specify the data on the axis (axes) and other customization options. +- To access advanced customization options click the properties buttons. +- You can use the search bar on the top right side to search for options in the config. The box below the search bar will give you the path in the config to options with matching words in the title or description. Clicking on the path will describe the option in the second box. +- You can click `Preview Graph` to view the current graph as long as the schema is valid. +- At the end bottom you can choose data selectors to allow users to interact with the graphs on a page. +- Once you are done, hit submit. +- Now you will have a graphic on your page. -- You can see the graphic by clicking on "Dashboards" then on the title of your page. +![](images/navbar_newpage.png) -![](images/navbar_newpage.png) \ No newline at end of file +- You can see the graphic by clicking on `Dashboards` then on the title of your page. \ No newline at end of file diff --git a/documentation/wizard_guide/images/add_csv.png b/documentation/wizard_guide/images/add_csv.png index ee8d4d7..4649dfc 100644 Binary files a/documentation/wizard_guide/images/add_csv.png and b/documentation/wizard_guide/images/add_csv.png differ diff --git a/documentation/wizard_guide/images/add_graphic.png b/documentation/wizard_guide/images/add_graphic.png deleted file mode 100644 index 8051a7e..0000000 Binary files a/documentation/wizard_guide/images/add_graphic.png and /dev/null differ diff --git a/documentation/wizard_guide/images/add_page.png b/documentation/wizard_guide/images/add_page.png index 6d56976..10cfd04 100644 Binary files a/documentation/wizard_guide/images/add_page.png and b/documentation/wizard_guide/images/add_page.png differ diff --git a/documentation/wizard_guide/images/first_look.png b/documentation/wizard_guide/images/first_look.png index a47efa2..3077e7a 100644 Binary files a/documentation/wizard_guide/images/first_look.png and b/documentation/wizard_guide/images/first_look.png differ diff --git a/documentation/wizard_guide/images/graphic_config.png b/documentation/wizard_guide/images/graphic_config.png index 09c88f2..2c04f79 100644 Binary files a/documentation/wizard_guide/images/graphic_config.png and b/documentation/wizard_guide/images/graphic_config.png differ diff --git a/documentation/wizard_guide/images/landing_page.png b/documentation/wizard_guide/images/landing_page.png new file mode 100644 index 0000000..986afb2 Binary files /dev/null and b/documentation/wizard_guide/images/landing_page.png differ diff --git a/documentation/wizard_guide/images/navbar_newpage.png b/documentation/wizard_guide/images/navbar_newpage.png index c37b160..8ae0fc5 100644 Binary files a/documentation/wizard_guide/images/navbar_newpage.png and b/documentation/wizard_guide/images/navbar_newpage.png differ diff --git a/escalation/app.py b/escalation/app.py index f146a5d..6a4488b 100644 --- a/escalation/app.py +++ b/escalation/app.py @@ -7,6 +7,8 @@ from app_setup import create_app, configure_app from utility.constants import APP_DEPLOY_DATA +# from utility.constants import TEST_APP_DEPLOY_DATA as APP_DEPLOY_DATA + if os.environ.get("CONFIG_FILE_PATH"): config_file_path = os.environ["CONFIG_FILE_PATH"] else: @@ -19,8 +21,7 @@ config_file_path = os.path.join("app_deploy_data", "main_config.json") -# config_file_path = "test_app_deploy_data/test_sql_app_config.json" -# config_file_path = "test_app_deploy_data/test_app_local_config.json" +# config_file_path = "test_app_deploy_data/main_config.json" with open(config_file_path, "r") as config_file: config_dict = json.load(config_file) diff --git a/escalation/app_deploy_data/app_settings.py b/escalation/app_deploy_data/app_settings.py index a694e5e..0e91ac0 100644 --- a/escalation/app_deploy_data/app_settings.py +++ b/escalation/app_deploy_data/app_settings.py @@ -26,7 +26,6 @@ "database": url.database, } - # password required for endpoints that use the @auth.login_required decorator # NB: real versions of these passwords should be passed in as environment variables to the app # Don't check in secrets to version control! diff --git a/escalation/app_setup.py b/escalation/app_setup.py index e830889..30d7ac0 100644 --- a/escalation/app_setup.py +++ b/escalation/app_setup.py @@ -89,14 +89,12 @@ def configure_app(app, config_dict, config_file_folder): return app -def configure_backend(app, models_path="app_deploy_data.models"): +def configure_backend(app): # setup steps unique to SQL-backended apps from database.sql_handler import SqlHandler, SqlDataInventory app.db = SQLAlchemy() - engine = create_engine( - app.config[SQLALCHEMY_DATABASE_URI], convert_unicode=True - ) + engine = create_engine(app.config[SQLALCHEMY_DATABASE_URI], convert_unicode=True) app.db_session = scoped_session( sessionmaker(autocommit=False, autoflush=False, bind=engine) ) @@ -104,7 +102,8 @@ def configure_backend(app, models_path="app_deploy_data.models"): data_backend_class = SqlHandler data_backend_writer = SqlDataInventory - models_imports = importlib.import_module(models_path) + models_file_path = ".".join([app.config[CONFIG_FILE_FOLDER], "models"]) + models_imports = importlib.import_module(models_file_path) app.Base = getattr(models_imports, "Base") @app.teardown_appcontext diff --git a/escalation/build_app_config_json_template.py b/escalation/build_app_config_json_template.py deleted file mode 100644 index fc56108..0000000 --- a/escalation/build_app_config_json_template.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright [2020] [Two Six Labs, LLC] -# Licensed under the Apache License, Version 2.0 - -import json - -from graphics.plotly_plot import ( - LAYOUT, - AGGREGATIONS, - AGGREGATE, - HOVER_DATA, - GROUPBY, -) -from utility.constants import * - - -def build_config_json_template(): - config_dict = { - SITE_TITLE: "", - SITE_DESC: "", - DATA_BACKEND: "", - AVAILABLE_PAGES: { - "": { - WEBPAGE_LABEL: "", - GRAPHICS: { - "": { - PLOT_MANAGER: "plotly", - DATA_SOURCES: [{DATA_SOURCE_TYPE: ""}], - GRAPHIC_TITLE: "", - GRAPHIC_DESC: "", - DATA: { - POINTS_NUM.format(0): {"": "", "": "",} - }, - PLOT_SPECIFIC_INFO: { - DATA: [{"type": ""}], - LAYOUT: {}, - }, - VISUALIZATION_OPTIONS: [ - {OPTION_TYPE: HOVER_DATA, COLUMN_NAME: ["", "",],}, - { - OPTION_TYPE: GROUPBY, - COLUMN_NAME: [""], - SELECT_OPTION: {"styles": {},}, - }, - { - OPTION_TYPE: AGGREGATE, - COLUMN_NAME: [""], - SELECT_OPTION: { - AGGREGATIONS: {"": "",} - }, - }, - ], - SELECTABLE_DATA_DICT: [ - { - OPTION_TYPE: "select", - COLUMN_NAME: "", - SELECT_OPTION: {"multiple": False}, - }, - {OPTION_TYPE: NUMERICAL_FILTER, COLUMN_NAME: "",}, - ], - }, - }, - } - }, - } - return config_dict - - -if __name__ == "__main__": - config_dict = build_config_json_template() - with open("config_template.json", "w") as fout: - json.dump(config_dict, fout, indent=4) diff --git a/escalation/controller.py b/escalation/controller.py index b0434af..afff2ac 100644 --- a/escalation/controller.py +++ b/escalation/controller.py @@ -9,9 +9,6 @@ from database.data_handler import DataHandler from graphics.utils.available_graphics import AVAILABLE_GRAPHICS -from utility.available_selectors import AVAILABLE_SELECTORS -from utility.reformatting_functions import add_instructions_to_config_dict -from database.utils import OPERATIONS_FOR_NUMERICAL_FILTERS from utility.constants import * @@ -24,15 +21,39 @@ def get_data_for_page(single_page_config_dict: dict, addendum_dict=None) -> list """ single_page_config_dict = copy.deepcopy(single_page_config_dict) - single_page_config_dict = add_instructions_to_config_dict( + graphic_object_dict = make_dict_of_graphic_objects( single_page_config_dict, addendum_dict ) - plot_specs = assemble_html_with_graphs_from_page_config(single_page_config_dict) + plot_specs = assemble_html_with_graphs_from_page_config(graphic_object_dict) return plot_specs -def assemble_html_with_graphs_from_page_config(single_page_config_dict: dict) -> list: +def make_dict_of_graphic_objects( + single_page_graphic_config_dict: dict, addendum_dict: dict = None +) -> dict: + """ + We build a page based on 2 dictonaries, what is in the config and what is submitted in the HTML form. + :param single_page_graphic_config_dict: Copy of part of the original config dict. + :param addendum_dict: e.g ImmutableMultiDict([('graphic_name', 'graphic_0'), ('selection_0', 'SHOW_ALL_ROW'), + ('selection_2_upper_operation', '<='), ('selection_2_upper_value', '4'))]) + Should not pass an empty ImmutableMultiDict + :return: dictionary of Graphic classes + """ + if addendum_dict is None: + addendum_dict = {} + graphic_object_dict = {} + for graphic_name, graphic_dict in single_page_graphic_config_dict.items(): + graphic_class = AVAILABLE_GRAPHICS[graphic_dict[PLOT_MANAGER]][OBJECT] + graphic_object = graphic_class( + graphic_dict, addendum_dict.get(graphic_name, {}) + ) + graphic_object.add_instructions_to_config_dict() + graphic_object_dict[graphic_name] = graphic_object + return graphic_object_dict + + +def assemble_html_with_graphs_from_page_config(graphic_object_dict: dict) -> list: """ creates dictionary to be read in by the html file to plot the graphics and selectors :param plot_list: @@ -42,95 +63,42 @@ def assemble_html_with_graphs_from_page_config(single_page_config_dict: dict) -> """ plot_specs = [] - for plot_key, plot_specification in single_page_config_dict.items(): + for plot_key, graphic_object in graphic_object_dict.items(): plot_data_handler = current_app.config.data_handler( - plot_specification[DATA_SOURCES] + graphic_object.graphic_dict[DATA_SOURCES] ) - (plot_directions_dict, graph_html_template) = assemble_plot_from_instructions( - plot_specification, plot_data_handler + data_filters = graphic_object.graphic_dict.get(DATA_FILTERS, []) + plot_data = plot_data_handler.get_column_data( + graphic_object.get_data_columns(), data_filters, ) + # makes a json file as required by js plotting documentation + graphic_object.data = plot_data + graphic_object.make_dict_for_html_plot() - select_info = [] - # checks to see if this plot has selectors - if SELECTABLE_DATA_DICT in plot_specification: - select_info = create_data_subselect_info_for_plot( - plot_specification, plot_data_handler - ) + unique_entry_dict = plot_data_handler.get_column_unique_entries( + graphic_object.get_columns_that_need_unique_entries(), filters=data_filters + ) + graphic_object.unique_entry_dict = unique_entry_dict + graphic_object.create_data_subselect_info_for_plot() html_dict = { - JINJA_GRAPH_HTML_FILE: graph_html_template, - JINJA_SELECT_INFO: select_info, - GRAPHIC_TITLE: plot_specification[GRAPHIC_TITLE], - GRAPHIC_DESC: plot_specification[GRAPHIC_DESC], - JINJA_PLOT_INFO: plot_directions_dict, + JINJA_GRAPH_HTML_FILE: graphic_object.get_graph_html_template(), + JINJA_SELECT_INFO: graphic_object.select_info, + GRAPHIC_TITLE: graphic_object.graphic_dict.get(GRAPHIC_TITLE, plot_key), + GRAPHIC_DESC: graphic_object.graphic_dict.get(GRAPHIC_DESC, ""), + JINJA_PLOT_INFO: graphic_object.graph_json_str, PLOT_ID: plot_key, } plot_specs.append(html_dict) return plot_specs -def assemble_plot_from_instructions(plot_specification, plot_data_handler): - """ - assembles the dictionary needed to render the graphic - a string with the html file that use the aforementioned dictionary - The dashboard options - :param plot_specification: - :param plot_data_handler: - :return: - """ - - visualization_options = plot_specification.get(VISUALIZATION_OPTIONS, {}) - data_filters = [] - if DATA_FILTERS in plot_specification: - data_filters = plot_specification[DATA_FILTERS] - - # Checks to see if it is a valid graphic - # TO DO what if it is not a valid graphic - graphic_data = AVAILABLE_GRAPHICS[plot_specification[PLOT_MANAGER]] - graphic_to_plot = graphic_data[OBJECT] - plot_data = plot_data_handler.get_column_data( - get_unique_set_of_columns_needed( - graphic_to_plot.get_data_columns(plot_specification[PLOT_SPECIFIC_INFO]), - visualization_options, - ), - data_filters, - ) - - # makes a json file as required by js plotting documentation - plot_directions_dict = graphic_to_plot.make_dict_for_html_plot( - plot_data, plot_specification[PLOT_SPECIFIC_INFO], visualization_options, - ) - - return plot_directions_dict, graphic_data[GRAPH_HTML_TEMPLATE] - - -def get_unique_set_of_columns_needed( - set_of_column_names: set, dict_of_plot_metadata: dict = None -) -> list: - """ - Returns the unique columns of the data we need to get - TO DO throw an error if contains column names not in data - - :param data_dict_to_be_plotted: - :param list_of_plot_metadata: - :return: - """ - if dict_of_plot_metadata is not None: - set_of_column_names.update( - { - col_name - for visualization in dict_of_plot_metadata.values() - for col_name in visualization[OPTION_COL] - } - ) - return list(set_of_column_names) - - def create_labels_for_available_pages(available_pages_list: list) -> list: """ - :param available_pages_dict: - :return: + Reformats a list of dashboard pages from the main app config json for template + :param available_pages_list: + :return: list of dicts defining hyperlink text and url for navbar """ labels = [] for available_page in available_pages_list: @@ -143,84 +111,6 @@ def create_labels_for_available_pages(available_pages_list: list) -> list: return labels -def create_data_subselect_info_for_plot( - plot_specification, data_handler: DataHandler, -) -> list: - """ - puts selector data in form to be read by html file - Broken into two major parts read in info from selection_option_dict_for_plot and then populate - select_info elements - :param plot_specification: - :param data_handler: - :return: - """ - - select_info = [] - selectable_data_dict = plot_specification[SELECTABLE_DATA_DICT] - - axis_list = selectable_data_dict.get(AXIS, []) - for index, axis_dict in enumerate(axis_list): - selector_entries = axis_dict[ENTRIES] - selector_entries.sort() - select_info.append(make_filter_dict(AXIS, axis_dict, index, selector_entries)) - - if GROUPBY in selectable_data_dict: - group_by_dict = selectable_data_dict[GROUPBY] - selector_entries = group_by_dict[ENTRIES] - selector_entries.sort() - # append no_group_by to the front of the list - selector_entries.insert(0, NO_GROUP_BY) - select_info.append( - make_filter_dict(GROUPBY, group_by_dict, "", selector_entries) - ) - - filter_list = selectable_data_dict.get(FILTER, []) - for index, filter_dict in enumerate(filter_list): - column = filter_dict[OPTION_COL] - selector_entries = data_handler.get_column_unique_entries( - [column], filters=plot_specification.get(DATA_FILTERS) - ) - selector_entries = selector_entries[column] - selector_entries.sort() - # append show_all_rows to the front of the list - selector_entries.insert(0, SHOW_ALL_ROW) - select_info.append( - make_filter_dict(FILTER, filter_dict, index, selector_entries) - ) - - numerical_filter_list = selectable_data_dict.get(NUMERICAL_FILTER, []) - for index, numerical_filter_dict in enumerate(numerical_filter_list): - select_info.append( - make_filter_dict(NUMERICAL_FILTER, numerical_filter_dict, index) - ) - - return select_info - - -def make_filter_dict(selector_type, select_dict, index, selector_entries=None): - """ - Reformats convenient Python data structures into a dict easily parsed in Jinja - - See tests/test_controller test cases for example formats of the inputs and outputs of this function - - :param selector_type: one of AXIS, FILTER, GROUPBY, NUMERICAL_FILTER (see the explicit iteration in create_data_subselect_info_for_plot) - :param select_dict: - :param index: form index corresponds to ordering of selectors on the web interface - :param selector_entries: A list of options to include in UI dropdown - :return: dict structured for read by Jinja - """ - html_filter_dict = {SELECTOR_TYPE: selector_type} - selector_attributes = AVAILABLE_SELECTORS[selector_type] - column = select_dict.get(OPTION_COL, "") - html_filter_dict[JINJA_SELECT_HTML_FILE] = selector_attributes[SELECT_HTML_TEMPLATE] - html_filter_dict[SELECTOR_NAME] = selector_attributes[SELECTOR_NAME].format(index) - html_filter_dict[TEXT] = selector_attributes[TEXT].format(column) - html_filter_dict[ACTIVE_SELECTORS] = select_dict[ACTIVE_SELECTORS] - html_filter_dict[MULTIPLE] = select_dict.get(MULTIPLE, False) - html_filter_dict[ENTRIES] = selector_entries - return html_filter_dict - - def make_pages_dict(available_pages_list, config_file_folder) -> dict: """ Pulls in all the config files referenced in the main config to make a large dictionary. diff --git a/escalation/database/data_handler.py b/escalation/database/data_handler.py index e5a4434..697e616 100644 --- a/escalation/database/data_handler.py +++ b/escalation/database/data_handler.py @@ -10,9 +10,9 @@ def __init__(self, data_sources, only_use_active: bool = True): pass @abstractmethod - def get_column_data(self, cols: list, filters: dict) -> list: + def get_column_data(self, cols: set, filters: dict) -> list: """ - :param cols: list of column names, including all columns for which data should be returned + :param cols: set of column names, including all columns for which data should be returned :param filters: a dict keyed by column name and valued with the filters to be applied # todo: document the filtering allowed: equality, presence in list, inequality? :return: a dict keyed by column name and valued with lists of row datapoints for the column @@ -20,10 +20,10 @@ def get_column_data(self, cols: list, filters: dict) -> list: pass @abstractmethod - def get_column_unique_entries(self, cols: list, filters: []) -> dict: + def get_column_unique_entries(self, cols: set, filters: []) -> dict: """ - :param cols: list of column names + :param cols: set of column names :param filters: Optional list specifying how to filter contents of the the requested columns based on the row values :return: A dict keyed by column names and valued with the unique values in that column """ diff --git a/escalation/database/sql_handler.py b/escalation/database/sql_handler.py index 18d03ff..aa0cc13 100644 --- a/escalation/database/sql_handler.py +++ b/escalation/database/sql_handler.py @@ -37,7 +37,7 @@ OPTION_TYPE, FILTER, SELECTED, - UNFILTERED_SELECTOR, + FILTERED_SELECTOR, COLUMN_NAME, MAIN_DATA_SOURCE, ADDITIONAL_DATA_SOURCES, @@ -250,16 +250,18 @@ def get_schema_for_data_source(self): for c in column_object.columns } - def get_column_data(self, columns: list, filters: [] = None) -> dict: + def get_column_data(self, columns: set, filters: [] = None) -> dict: """ - :param columns: A complete list of the columns to be returned + :param columns: A complete set of the columns to be returned :param filters: Optional list specifying how to filter the requested columns based on the row values :return: a dict keyed by column name and valued with lists of row datapoints for the column """ if filters is None: filters = [] - cols_for_filters = [filter_dict[OPTION_COL] for filter_dict in filters] - all_to_include_cols = list(set(columns + list(cols_for_filters))) + all_to_include_cols = columns.union( + [filter_dict[OPTION_COL] for filter_dict in filters] + ) + all_column_rename_dict = { self.sanitize_column_name(c): c for c in all_to_include_cols } @@ -339,11 +341,14 @@ def get_column_unique_entries( if filter_active_data: active_data_filters = self.build_filters_from_active_data_source() query = self.apply_filters_to_query(query, active_data_filters) - # if the current column matches one in the filter list marked as unfiltered, - # skip this and don't apply the filters before looking for unique values - if not any( + # if the current column matches one in the filter list marked as filtered, + # apply the filters before looking for unique values + if any( [ - (filter_[COLUMN_NAME] == col and filter_.get(UNFILTERED_SELECTOR)) + ( + filter_[COLUMN_NAME] == col + and filter_.get(FILTERED_SELECTOR, False) + ) for filter_ in filters ] ): @@ -473,9 +478,10 @@ def update_data_upload_metadata_active(cls, data_source_name, active_data_dict): """ data_upload_metadata = cls.get_sqlalchemy_model_class_for_data_upload_metadata() for upload_id, active_status in active_data_dict.items(): + # upload_id are of form f"id_{upload_id}" row = ( current_app.db_session.query(data_upload_metadata) - .filter_by(table_name=data_source_name, upload_id=upload_id) + .filter_by(table_name=data_source_name, upload_id=upload_id[3:]) .first() ) active_boolean = active_status == ACTIVE @@ -609,7 +615,15 @@ def get_data_from_csv(csv_data_file_path): :param csv_data_file_path: :return: pandas dataframe """ - return pd.read_csv(csv_data_file_path, encoding="utf-8", comment="#") + df = pd.read_csv(csv_data_file_path, encoding="utf-8", comment="#") + # convert datetime columns from the strings loaded in csv to datetime + for col in df.columns: + if df[col].dtype == "object": + try: + df[col] = pd.to_datetime(df[col]) + except ValueError: + pass + return df def create_new_table( self, table_name, schema, key_columns, if_exists, diff --git a/escalation/graphics/build_cytoscape_schema.py b/escalation/graphics/build_cytoscape_schema.py new file mode 100644 index 0000000..2027e67 --- /dev/null +++ b/escalation/graphics/build_cytoscape_schema.py @@ -0,0 +1,791 @@ +from graphics.graphic_schema import GraphicsConfigInterfaceBuilder +from utility.constants import * + +COLOR_INFO = ( + "Colours may be specified by name (e.g. red), hex (e.g. #ff0000 or #f00), RGB (e.g. rgb(255, 0, 0))," + " or HSL (e.g. hsl(0, 100%, 50%))." +) +ARROW_TYPES = [ + "triangle", + "triangle-tee", + "circle-triangle", + "triangle-cross", + "triangle-backcurve", + "vee", + "tee", + "square", + "circle", + "diamond", + "chevron", + "none", +] + + +def build_cytoscape_schema(column_names): + schema = { + "$schema": "http://json-schema.org/draft/2019-09/schema#", + "title": "Cytoscape Graph Config", + DESCRIPTION: "Dictionary that follows https://js.cytoscape.org/", + TYPE: OBJECT, + REQUIRED: [SOURCE, TARGET], + OPTIONS: {DISABLE_COLLAPSE: True, REMOVE_EMPTY_PROPERTIES: True}, + DEFAULTPROPERTIES: [SOURCE, TARGET], + PROPERTIES: { + NODE_ID: { + TYPE: STRING, + ENUM: column_names, + DESCRIPTION: "Node identifiers. If empty will infer from source and target data", + }, + SOURCE: { + TYPE: STRING, + ENUM: column_names, + DESCRIPTION: "Source nodes for edges.", + }, + TARGET: { + TYPE: STRING, + ENUM: column_names, + DESCRIPTION: "Target nodes for edges.", + }, + EDGE_ID: {TYPE: STRING, ENUM: column_names, DESCRIPTION: "Edge identifies"}, + ELEMENT_PROPERTIES: { + TYPE: ARRAY_STRING, + DESCRIPTION: "specify properties for each element from column of data. Matches the id of the elements " + "to the column of property," + " see https://js.cytoscape.org/#notation/elements-json", + ITEMS: { + TYPE: OBJECT, + REQUIRED: [PROPERTY_NAME, GROUP, COLUMN_NAME], + PROPERTIES: { + PROPERTY_NAME: { + TYPE: STRING, + DESCRIPTION: "Name of the property: locked - boolean, node's position is immutable. " + "grabbable - boolean, whether the node can be grabbed and moved by the user. " + "classes - a space separated string of class names that the element has. " + "position_x and position_x - the model position of the node when layout = *preset*.", + ENUM: [ + "locked", + "grabbable", + "classes", + "position_x", + "position_y", + ], + }, + GROUP: { + TYPE: STRING, + ENUM: [NODES, EDGES], + DESCRIPTION: "Apply to nodes or edges, column_name should reference a column that" + " has data same number and order as node or edge ids", + DEFAULT: NODES, + }, + COLUMN_NAME: { + TYPE: STRING, + ENUM: column_names, + DESCRIPTION: "Column that contain the property for each element.", + }, + }, + }, + }, + LAYOUT: { + TYPE: OBJECT, + DESCRIPTION: "Placement of the nodes see https://js.cytoscape.org/#layouts", + REQUIRED: ["name"], + PROPERTIES: { + "name": { + TYPE: STRING, + DEFAULT: "random", + ENUM: [ + "null", + "random", + "preset", + "grid", + "circle", + "concentric", + "breadthfirst", + "cose", + ], + DESCRIPTION: "Type of layout", + }, + "fit": { + TYPE: BOOLEAN, + DEFAULT: True, + DESCRIPTION: "whether to fit to viewport", + }, + "padding": {TYPE: NUMBER, DEFAULT: 30, DESCRIPTION: "fit padding"}, + "boundingBox": { + TYPE: OBJECT, + DESCRIPTION: "constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }", + }, + "animate": { + TYPE: BOOLEAN, + DESCRIPTION: "whether to transition the node positions", + }, + "animationDuration": { + TYPE: NUMBER, + DESCRIPTION: "duration of animation in ms if enabled", + DEFAULT: 500, + }, + "positions": { + TYPE: OBJECT, + DESCRIPTION: "layout = preset, map of (node id) => (position obj);" + " or function(node){ return somPos; }", + }, + "avoidOverlap": { + TYPE: BOOLEAN, + DESCRIPTION: "When layout is grid, circle, concentric, breathfirst. prevents node overlap, may" + " overflow boundingBox if not enough space", + DEFAULT: True, + }, + "avoidOverlapPadding": { + TYPE: NUMBER, + DESCRIPTION: "When layout is grid. Extra spacing around nodes when avoidOverlap: true", + DEFAULT: 10, + }, + "nodeDimensionsIncludeLabels": { + TYPE: BOOLEAN, + DESCRIPTION: "Excludes the label when calculating" + " node bounding boxes for the layout algorithm", + DEFAULT: False, + }, + "spacingFactor": { + TYPE: NUMBER, + DESCRIPTION: "When layout is grid, circle, breadthfirst. " + "grid, circle: Applies a multiplicative factor (>0) to expand or" + " compress the overall area that the nodes take up." + "breathfirst: positive spacing factor, larger => more space between nodes" + " (N.B. n/a if causes overlap)", + DEFAULT: 1, + MINIMUM: 0, + }, + "condense": { + TYPE: BOOLEAN, + DESCRIPTION: "When layout is grid. uses all available space on false," + " uses minimal space on true", + DEFAULT: False, + }, + "rows": { + TYPE: INTEGER, + DESCRIPTION: "When layout is grid. force num of rows in the grid", + }, + "cols": { + TYPE: INTEGER, + DESCRIPTION: "When layout is grid. force num of columns in the grid", + }, + "radius": { + TYPE: NUMBER, + DESCRIPTION: "When layout is circle. the radius of the circle", + }, + "startAngle": { + TYPE: NUMBER, + DESCRIPTION: "When layout is circle, concentric. where nodes start in radians", + DEFAULT: 4.712, + }, + "sweep": { + TYPE: NUMBER, + DESCRIPTION: "When layout is circle, concentric. how many radians should be between" + " the first and" + " last node (defaults to full circle)", + }, + "clockwise": { + TYPE: BOOLEAN, + DESCRIPTION: "When layout is circle, concentric. " + "whether the layout should go clockwise (true) or counterclockwise/anticlockwise" + " (false)", + }, + "equidistant": { + TYPE: BOOLEAN, + DESCRIPTION: "When layout is concentric. " + "whether levels have an equal radial distance betwen them, " + "may cause bounding box overflow", + }, + "minNodeSpacing": { + TYPE: NUMBER, + DESCRIPTION: "When layout is concentric. min spacing between outside of nodes" + " (used for radius adjustment)", + }, + "directed": { + TYPE: BOOLEAN, + DESCRIPTION: "When layout is breadthfirst. whether the tree is directed downwards" + " (or edges can point in any direction if false)", + DEFAULT: False, + }, + "circle": { + TYPE: BOOLEAN, + DESCRIPTION: "When layout is breadthfirst. put depths in concentric circles if true, put" + " depths top down if false", + DEFAULT: False, + }, + "grid": { + TYPE: BOOLEAN, + DESCRIPTION: "When layout is breadthfirst. whether to create an even grid into which the DAG" + " is placed (circle:false only)", + DEFAULT: False, + }, + "roots": { + TYPE: STRING, + DESCRIPTION: "When layout is breadthfirst. Format of selector, the roots of the trees", + }, + "maximal": { + TYPE: BOOLEAN, + DESCRIPTION: "When layout is breadthfirst. whether to shift nodes down their natural BFS depths" + " in order to avoid upwards edges (DAGS only)", + DEFAULT: False, + }, + "randomize": { + TYPE: BOOLEAN, + DESCRIPTION: "When layout is cose. Randomize the initial positions of the nodes (true) or" + " use existing positions (false)", + DEFAULT: False, + }, + "componentSpacing": { + TYPE: NUMBER, + DESCRIPTION: "When layout is cose. Extra spacing between components in non-compound graphs", + DEFAULT: 40, + }, + "nodeOverlap": { + TYPE: NUMBER, + DESCRIPTION: "When layout is cose. Node repulsion (overlapping) multiplier", + DEFAULT: 4, + }, + "nestingFactor": { + TYPE: NUMBER, + DESCRIPTION: "When layout is cose. Nesting factor (multiplier) to compute ideal edge length" + " for nested edges", + DEFAULT: 1.2, + }, + "gravity": { + TYPE: NUMBER, + DESCRIPTION: "When layout is cose. Gravity force (constant)", + DEFAULT: 1, + }, + "numIter": { + TYPE: NUMBER, + DESCRIPTION: "When layout is cose. Maximum number of iterations to perform", + DEFAULT: 1000, + }, + "initialTemp": { + TYPE: NUMBER, + DESCRIPTION: "When layout is cose. Initial temperature (maximum node displacement)", + DEFAULT: 1000, + }, + "coolingFactor": { + TYPE: NUMBER, + DESCRIPTION: "When layout is cose. Cooling factor (how the temperature is " + "reduced between consecutive iterations)", + DEFAULT: 0.99, + }, + "minTemp": { + TYPE: NUMBER, + DESCRIPTION: "When layout is cose. Lower temperature threshold (below this" + " point the layout will end)", + DEFAULT: 1.0, + }, + }, + }, + STYLE: { + TYPE: ARRAY_STRING, + DESCRIPTION: "Style applied to a group, e.g. all nodes, all edges, nodes in a certain class" + " or a single node, etc. for style to individual nodes or edges based on a column, " + "add to todo. Each element is CSS-like, See https://js.cytoscape.org/#style", + ITEMS: { + TYPE: OBJECT, + REQUIRED: [SELECTOR, STYLE], + PROPERTIES: { + SELECTOR: { + TYPE: STRING, + DESCRIPTION: "Where to apply the style element, common inputs are *node* or *edge*," + " Also takes in css selector, e.g. *.foo* to apply to all edges and nodes of" + ' class *foo*, #foo (or [id="foo"]) for node or edge of with id foo.' + " See https://js.cytoscape.org/#selectors/notes-amp-caveats", + }, + STYLE: { + TYPE: OBJECT, + DESCRIPTION: "Specify the styles", + PROPERTIES: { + "width": { + TYPE: NUMBER, + DESCRIPTION: "The width of the node’s body or the width of an edge’s line.", + }, + "height": { + TYPE: NUMBER, + DESCRIPTION: "The height of the node’s body", + }, + "shape": { + TYPE: STRING, + DESCRIPTION: "The shape of the node’s body. Note that each shape fits within the" + " specified width and height, and so you may have to adjust width and" + " height if you desire an equilateral shape" + " (i.e. width !== height for several equilateral shapes)", + ENUM: [ + "ellipse", + "triangle", + "round-triangle", + "rectangle", + "round-rectangle", + "bottom-round-rectangle", + "cut-rectangle", + "barrel", + "rhomboid", + "diamond", + "round-diamond", + "pentagon", + "round-pentagon", + "hexagon", + "round-hexagon", + "concave-hexagon", + "heptagon", + "round-heptagon", + "octagon", + "round-octagon", + "star", + "tag", + "round-tag", + "vee", + ], + }, + "background-color": { + TYPE: STRING, + DESCRIPTION: "The colour of the node’s body. " + + COLOR_INFO, + }, + "background-blacken": { + TYPE: NUMBER, + DESCRIPTION: "Blackens the node’s body for values from 0 to 1;" + " whitens the node’s body for values from 0 to -1.", + MAXIMUM: 1, + MINIMUM: -1, + }, + "background-opacity": { + TYPE: NUMBER, + DESCRIPTION: "The opacity level of the node’s background colour", + MAXIMUM: 1, + MINIMUM: 0, + }, + "border-width": { + TYPE: NUMBER, + DESCRIPTION: "The size of the node’s border.", + MINIMUM: 0, + }, + "border-style": { + TYPE: STRING, + DESCRIPTION: "The style of the node’s border", + ENUM: ["solid", "dotted", "dashed", "double"], + }, + "border-color": { + TYPE: STRING, + DESCRIPTION: "The colour of the node’s border. " + + COLOR_INFO, + }, + "border-opacity": { + TYPE: NUMBER, + DESCRIPTION: "The opacity of the node’s border", + MINIMUM: 0, + MAXIMUM: 1, + }, + "padding": { + TYPE: NUMBER, + DESCRIPTION: "The amount of padding around all sides of the node.", + MINIMUM: 0, + }, + "curve-style": { + TYPE: STRING, + DESCRIPTION: "The curving method used to separate two or more edges between two" + " nodes; may be haystack (very fast, bundled straight edges" + " for which loops and compounds are unsupported), straight (straight" + " edges with all arrows supported), bezier (bundled curved edges)," + " unbundled-bezier (curved edges for use with manual control points)," + " segments (a series of straight lines), taxi (right-angled lines," + " hierarchically bundled). Note that haystack edges work best with" + " ellipse, rectangle, or similar nodes. Smaller node shapes, like" + " triangle, will not be as aesthetically pleasing. Also note that" + " edge endpoint arrows are unsupported for haystack edges.", + DEFAULT: "straight", + ENUM: [ + "straight", + "haystack", + "bezier", + "unbundled-bezier", + "segments", + "taxi", + ], + }, + "line-color": { + TYPE: STRING, + DESCRIPTION: "The colour of the edge’s line. " + + COLOR_INFO, + }, + "line-style": { + TYPE: STRING, + DESCRIPTION: "The style of the edge’s line.", + ENUM: ["solid", "dotted", "dashed"], + }, + "line-cap": { + TYPE: STRING, + DESCRIPTION: "The cap style of the edge’s line; may be butt (default), round, or" + " square. The cap may or may not be visible, depending on the shape" + " of the node and the relative size of the node and edge. Caps other" + " than butt extend beyond the specified endpoint of the edge.", + ENUM: ["butt", "round", "square"], + DEFAULT: "butt", + }, + "line-opacity": { + TYPE: NUMBER, + MINIMUM: 0, + MAXIMUM: 1, + DEFAULT: 1, + DESCRIPTION: "The opacity of the edge’s line and arrow. Useful if you wish to have" + " a separate opacity for the edge label versus the edge line. Note" + " that the opacity value of the edge element affects the effective" + " opacity of its line and label subcomponents.", + }, + "target-arrow-color": { + TYPE: STRING, + DESCRIPTION: "The colour of the edge’s source arrow. " + + COLOR_INFO, + }, + "target-arrow-shape": { + TYPE: STRING, + DESCRIPTION: "The shape of the edge’s source arrow", + ENUM: ARROW_TYPES, + }, + "target-arrow-fill": { + TYPE: STRING, + DESCRIPTION: "The fill state of the edge’s source arrow", + ENUM: ["filled", "hollow"], + }, + "mid-target-arrow-color": { + TYPE: STRING, + DESCRIPTION: "The colour of the edge’s source arrow. " + + COLOR_INFO, + }, + "mid-target-arrow-shape": { + TYPE: STRING, + DESCRIPTION: "The shape of the edge’s source arrow", + ENUM: ARROW_TYPES, + }, + "mid-target-arrow-fill": { + TYPE: STRING, + DESCRIPTION: "The fill state of the edge’s source arrow", + ENUM: ["filled", "hollow"], + }, + "arrow-scale": { + TYPE: NUMBER, + DESCRIPTION: "Scaling for the arrow size.", + MINIMUM: 0, + }, + "opacity": { + TYPE: NUMBER, + DESCRIPTION: "The opacity of the element." + " See https://js.cytoscape.org/#style/visibility", + MINIMUM: 0, + MAXIMUM: 1, + }, + "z-index": { + TYPE: INTEGER, + DESCRIPTION: "An integer value that affects the relative draw order of elements." + " In general, an element with a higher z-index will be drawn on top" + " of an element with a lower z-index. Note that edges are under nodes" + " despite z-index.", + }, + "label": { + TYPE: STRING, + DESCRIPTION: "The text to display for an element’s label. Can give a path, e.g. " + "data(id) will label with the elements id", + }, + "source-label": { + TYPE: STRING, + DESCRIPTION: "The text to display for an edge’s source label. Can give a path, e.g. " + "data(id) will label with the elements id", + }, + "target-label": { + TYPE: STRING, + DESCRIPTION: "The text to display for an edge’s target label. Can give a path, e.g. " + "data(id) will label with the elements id", + }, + "color": { + TYPE: STRING, + DESCRIPTION: "The color of the element's label. " + + COLOR_INFO, + }, + "text-opacity": { + TYPE: NUMBER, + DESCRIPTION: "The opacity of the label text, including its outline.", + MINIMUM: 0, + MAXIMUM: 1, + }, + "font-family": { + TYPE: STRING, + DESCRIPTION: "A comma-separated list of font names to use on the label text.", + }, + "font-size": { + TYPE: STRING, + DESCRIPTION: "The size of the label text.", + }, + "font-style": { + TYPE: STRING, + DESCRIPTION: "A CSS font style to be applied to the label text.", + }, + "font-weight": { + TYPE: STRING, + DESCRIPTION: "A CSS font weight to be applied to the label text.", + }, + "text-transform": { + TYPE: STRING, + DESCRIPTION: "A transformation to apply to the label text", + ENUM: ["none", "uppercase", "lowercase"], + }, + "text-halign": { + TYPE: STRING, + DESCRIPTION: "The horizontal alignment of a node’s label", + ENUM: ["left", "center", "right"], + }, + "text-valign": { + TYPE: STRING, + DESCRIPTION: "The vertical alignment of a node’s label", + ENUM: ["top", "center", "bottom"], + }, + "ghost": { + TYPE: STRING, + DESCRIPTION: "Whether to use the ghost effect, a semitransparent duplicate of" + " the element drawn at an offset.", + DEFAULT: "no", + ENUM: ["yes", "no"], + }, + "active-bg-color": { + TYPE: STRING, + DESCRIPTION: "The colour of the indicator shown when the background is grabbed" + " by the user. Selector needs to be *core*. " + + COLOR_INFO, + }, + "active-bg-opacity": { + TYPE: STRING, + DESCRIPTION: " The opacity of the active background indicator." + " Selector needs to be *core*.", + }, + "active-bg-size": { + TYPE: STRING, + DESCRIPTION: " The opacity of the active background indicator." + " Selector needs to be *core*.", + }, + "selection-box-color": { + TYPE: STRING, + DESCRIPTION: "The background colour of the selection box used for" + " drag selection. Selector needs to be *core*. " + + COLOR_INFO, + }, + "selection-box-border-width": { + TYPE: NUMBER, + DESCRIPTION: "The size of the border on the selection box." + " Selector needs to be *core*", + }, + "selection-box-opacity": { + TYPE: NUMBER, + DESCRIPTION: "The opacity of the selection box. Selector needs to be *core*", + MINIMUM: 0, + MAXIMUM: 1, + }, + "outside-texture-bg-color": { + TYPE: STRING, + DESCRIPTION: "The colour of the area outside the viewport texture when" + " initOptions.textureOnViewport === true. Selector needs to be *core*" + ". " + COLOR_INFO, + }, + "outside-texture-bg-opacity": { + TYPE: NUMBER, + DESCRIPTION: "The opacity of the area outside the viewport texture." + " Selector needs to be *core*", + MINIMUM: 0, + MAXIMUM: 1, + }, + }, + }, + }, + }, + }, + "zoom": { + TYPE: NUMBER, + DESCRIPTION: "The initial zoom level of the graph. Make sure to disable viewport" + " manipulation options, such as fit, in your layout so that it is not overridden" + " when the layout is applied. You can set options.minZoom and" + " options.maxZoom to set restrictions on the zoom level", + DEFAULT: "1", + }, + "pan": { + TYPE: OBJECT, + DESCRIPTION: "The initial panning position of the graph. Make sure to disable viewport" + " manipulation options, such as fit, in your layout so that it is not" + " overridden when the layout is applied.", + PROPERTIES: { + "x": {TYPE: NUMBER, DEFAULT: 0}, + "y": {TYPE: NUMBER, DEFAULT: 0}, + }, + ADDITIONAL_PROPERTIES: False, + }, + "minZoom": { + TYPE: NUMBER, + DESCRIPTION: "A minimum bound on the zoom level of the graph. The viewport cannot be" + " scaled smaller than this zoom level.", + }, + "maxZoom": { + TYPE: NUMBER, + DESCRIPTION: "A maximum bound on the zoom level of the graph. The viewport cannot" + " be scaled larger than this zoom level.", + }, + "zoomingEnabled": { + TYPE: BOOLEAN, + DESCRIPTION: "Whether zooming the graph is enabled, both by user events" + " and programmatically.", + DEFAULT: True, + }, + "userZoomingEnabled": { + TYPE: BOOLEAN, + DESCRIPTION: "Whether user events (e.g. mouse wheel, pinch-to-zoom) are allowed to zoom" + " the graph. Programmatic changes to zoom are unaffected by this option.", + DEFAULT: True, + }, + "panningEnabled": { + TYPE: BOOLEAN, + DESCRIPTION: "Whether panning the graph is enabled, both by user" + " events and programmatically.", + DEFAULT: True, + }, + "userPanningEnabled": { + TYPE: BOOLEAN, + DESCRIPTION: "Whether user events (e.g. dragging the graph background) are allowed" + " to pan the graph. Programmatic changes to pan are unaffected by this option.", + DEFAULT: True, + }, + "boxSelectionEnabled": { + TYPE: BOOLEAN, + DESCRIPTION: "Whether box selection (i.e. drag a box overlay around, and release it to" + " select) is enabled. If enabled while panning is also enabled, the user must" + " use a modifier key (shift, alt, control, or command) to use box selection.", + DEFAULT: True, + }, + "selectionType": { + TYPE: STRING, + DESCRIPTION: "A string indicating the selection behaviour from user input. For 'additive'," + " a new selection made by the user adds to the set of currently selected" + " elements. For 'single', a new selection made by the user becomes the entire" + " set of currently selected elements (i.e. the previous elements are" + " unselected).", + ENUM: ["single", "additive"], + DEFAULT: "single", + }, + "touchTapThreshold": { + TYPE: NUMBER, + DESCRIPTION: "A non-negative integer that indicates the maximum allowable distance that" + " a user may move during a tap gesture on touch devices. This makes tapping" + " easier for users. These values have sane defaults, so it is not advised to" + " change these options unless you have very good reason for doing so." + " Large values will almost certainly have undesirable consequences.", + DEFAULT: 8, + }, + "desktopTapThreshold": { + TYPE: NUMBER, + DESCRIPTION: "A non-negative integer that indicates the maximum allowable distance that" + " a user may move during a tap gesture on desktop devices. This makes tapping" + " easier for users. These values have sane defaults, so it is not advised to" + " change these options unless you have very good reason for doing so." + " Large values will almost certainly have undesirable consequences.", + DEFAULT: 4, + }, + "autoungrabify": { + TYPE: BOOLEAN, + DESCRIPTION: "Whether nodes should be ungrabified (not grabbable by user) by default" + " (if true, overrides individual node state)", + DEFAULT: False, + }, + "autolock": { + TYPE: BOOLEAN, + DESCRIPTION: "Whether nodes should be locked (not draggable at all) by default (if true," + " overrides individual node state).", + DEFAULT: False, + }, + "autounselectify": { + TYPE: BOOLEAN, + DESCRIPTION: "Whether nodes should be unselectified (immutable selection state) by default" + " (if true, overrides individual element state).", + DEFAULT: False, + }, + "headless": { + TYPE: BOOLEAN, + DESCRIPTION: "A convenience option that initialises the instance to run headlessly." + " You do not need to set this in environments that are implicitly headless" + " (e.g. Node.js). However, it is handy to set headless: true if you want a" + " headless instance in a browser.", + DEFAULT: False, + }, + "styleEnabled": { + TYPE: BOOLEAN, + DESCRIPTION: "A boolean that indicates whether styling should be used. For headless " + "(i.e. outside the browser) environments, display is not necessary and so" + " neither is styling necessary — thereby speeding up your code. You can" + " manually enable styling in headless environments if you require it for a" + " special case. Note that it does not make sense to disable style if you plan" + " on rendering the graph. Also note that cy.destroy() must be called to clean" + " up a style-enabled, headless instance.", + DEFAULT: False, + }, + "wheelSensitivity": { + TYPE: NUMBER, + DEFAULT: 1, + DESCRIPTION: "Changes the scroll wheel sensitivity when zooming. This is a multiplicative" + " modifier. So, a value between 0 and 1 reduces the sensitivity (zooms slower)," + " and a value greater than 1 increases the sensitivity (zooms faster). This" + " option is set to a sane value that works well for mainstream mice (Apple," + " Logitech, Microsoft) on Linux, Mac, and Windows. If the default value seems" + " too fast or too slow on your particular system, you may have non-default" + " mouse settings in your OS or a niche mouse. You should not change this value" + " unless your app is meant to work only on specific hardware. Otherwise, you" + " risk making zooming too slow or too fast for most users.", + }, + }, + } + + individual_element = { + TYPE: ARRAY_STRING, + TITLE: "Individual Elements Style", + DESCRIPTION: "Apply a style based on a data column to each node or edge", + ITEMS: { + TYPE: OBJECT, + REQUIRED: [GROUP, COLUMN_NAME, STYLE_NAME], + "additionalProperties": False, + PROPERTIES: { + STYLE_NAME: { + TYPE: STRING, + DESCRIPTION: "Name of the style property, see style dictionary for description of each", + ENUM: list( + schema[PROPERTIES][STYLE][ITEMS][PROPERTIES][STYLE][ + PROPERTIES + ].keys() + ), + }, + GROUP: { + TYPE: STRING, + ENUM: [NODES, EDGES], + DESCRIPTION: "Apply to nodes or edges, column_name should reference a column that has data same " + "number and order as node or edge ids", + DEFAULT: NODES, + }, + COLUMN_NAME: { + TYPE: STRING, + ENUM: column_names, + DESCRIPTION: "Column that contain the individual style properties", + }, + }, + }, + } + schema[PROPERTIES][ELEMENT_STYLE] = individual_element + return schema + + +class CytoscapeGraphicSchema(GraphicsConfigInterfaceBuilder): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def build_individual_plot_type_schema(self, plot_type): + return build_cytoscape_schema(self.possible_column_names) + + @staticmethod + def get_available_plots(): + return [{GRAPHIC_NAME: "Graph Visualisation", VALUE: "graphvis"}] diff --git a/escalation/graphics/build_plotly_schema.py b/escalation/graphics/build_plotly_schema.py new file mode 100644 index 0000000..e3e76d4 --- /dev/null +++ b/escalation/graphics/build_plotly_schema.py @@ -0,0 +1,546 @@ +# Copyright [2020] [Two Six Labs, LLC] +# Licensed under the Apache License, Version 2.0 +import copy +import json + +from graphics.graphic_schema import GraphicsConfigInterfaceBuilder +from utility.constants import * + +MODE = "mode" + + +COLORS = [ + "#1f77b4", + "#ff7f0e", + "#2ca02c", + "#d62728", + "#9467bd", + "#8c564b", + "#e377c2", + "#7f7f7f", + "#bcbd22", + "#17becf", +] + +COLOR_NAMES = [ + "blue", + "orange", + "green", + "red", + "purple", + "brown", + "pink", + "gray", + "yellow-green", + "blue-teal", +] + +SUPPORTED_PLOTS = [ + {GRAPHIC_NAME: "Scatter or Line Plot", VALUE: SCATTER}, + {GRAPHIC_NAME: "Bar Plot", VALUE: BAR}, + # {GRAPHIC_NAME: "Heat Map", VALUE: HEATMAP}, + # {GRAPHIC_NAME: "Contour Plot", VALUE: CONTOUR}, + {GRAPHIC_NAME: "Box Plot", VALUE: BOX}, + {GRAPHIC_NAME: "Violin Plot", VALUE: VIOLIN}, + {GRAPHIC_NAME: "Histogram", VALUE: HISTOGRAM}, + {GRAPHIC_NAME: "2D Histogram", VALUE: HISTOGRAM2D}, + {GRAPHIC_NAME: "3D Scatter or Line Plot", VALUE: SCATTER3D}, + {GRAPHIC_NAME: "3D Mesh Plot", VALUE: MESH3D}, +] + + +class PlotlyGraphicSchema(GraphicsConfigInterfaceBuilder): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def build_generic_plotly_plot_schema_template(self): + schema = { + "$schema": "http://json-schema.org/draft/2019-09/schema#", + "title": "Plotly Graph Config", + "description": "dictionary that follows https://plotly.com/javascript/reference/", + "type": "object", + "required": [DATA], + OPTIONS: {DISABLE_COLLAPSE: True, REMOVE_EMPTY_PROPERTIES: True}, + "defaultProperties": [DATA, LAYOUT], + "properties": { + DATA: { + "type": "array", + "description": "list of graphs to be plotted on a single plot", + TITLE: "Data", + MIN_ITEMS: 1, + OPTIONS: {DISABLE_COLLAPSE: True}, + ITEMS: { + TYPE: "object", + "title": "Data Dictionary", + OPTIONS: {DISABLE_COLLAPSE: True}, + "required": ["type"], + PROPERTIES: { + TYPE: { + "type": "string", + TITLE: "Render Mode", + "enum": [ + "scatter", + "scattergl", + "bar", + "pie", + "heatmap", + "heatmapgl", + "image", + "contour", + "table", + "box", + "violin", + "histogram", + "histogram2d", + "histogram2dcontour", + "scatter3d", + "surface", + "mesh3d", + ], + OPTIONS: {HIDDEN: True}, + }, + X: { + "type": "string", + TITLE: "Data on X Axis", + "enum": self.possible_column_names, + }, + Y: { + "type": "string", + TITLE: "Data on Y Axis", + "enum": self.possible_column_names, + }, + Z: { + TYPE: "string", + TITLE: "Data on Z Axis", + ENUM: self.possible_column_names, + }, + ERROR_X: { + TYPE: "object", + "properties": { + ARRAY_STRING: { + "type": "string", + TITLE: "Data column to use for error bars", + ENUM: self.possible_column_names, + }, + }, + TITLE: "Symmetric error bars in the X axis", + }, + ERROR_Y: { + TYPE: "object", + "properties": { + ARRAY_STRING: { + "type": "string", + TITLE: "Data column to use for error bars", + ENUM: self.possible_column_names, + }, + }, + TITLE: "Symmetric error bars in the Y axis", + }, + ERROR_Z: { + TYPE: "object", + "properties": { + ARRAY_STRING: { + "type": "string", + TITLE: "Data column to use for error bars", + ENUM: self.possible_column_names, + }, + }, + TITLE: "Symmetric error bars in the Z axis", + }, + HOVERTEXT: { + DESCRIPTION: "Differs from the plotly documentation." + " Takes in list of column names, where the data in each columns is shown for " + "the data point on hover. (a default hovertemplate is set so the user does not has to" + " set one)." + " To be seen, trace `hoverinfo` must contain a *text* flag (which it does by default).", + TYPE: "array", + OPTIONS: {DISABLE_COLLAPSE: True}, + "items": { + "type": "string", + TITLE: "Column Name", + "enum": self.possible_column_names, + }, + }, + MODE: { + TYPE: "string", + TITLE: "Graph Style", + DESCRIPTION: "Determines the drawing mode for this scatter trace. If the selected option " + "includes *text* then the `text` elements appear at the coordinates." + " Otherwise, the `text` elements appear on hover", + ENUM: [ + "markers", + "lines", + "text", + "lines+markers", + "markers+text", + "lines+text", + "lines+markers+text", + "none", + ], + }, + TRANSFORMS: { + DESCRIPTION: "Transforms on the data. This differs from plotly's schema a little bit." + "Here, transforms is a dictionary with keys 'groupby' and 'aggreations'" + " instead" + " of a list dictionaries where the transform is specified by the type key." + " For filters use the data selector dictionary.", + TYPE: OBJECT, + OPTIONS: { + DISABLE_COLLAPSE: True, + REMOVE_EMPTY_PROPERTIES: True, + }, + PROPERTIES: { + GROUPBY: { + "type": OBJECT, + "description": "Group by transform of the data", + "id": "groupby_id", + "required": [GROUPS], + PROPERTIES: { + "enabled": { + TYPE: BOOLEAN, + DEFAULT: True, + DESCRIPTION: "Determines whether the transform is enabled or disabled.", + }, + "nameformat": { + TYPE: STRING, + DESCRIPTION: "Pattern by which grouped traces are named." + " If only one trace is" + ' present, defaults to the group name (`"%{group}"`), otherwise' + " defaults to the group name with trace name " + '(`"%{group} (%{trace})"`). Available escape sequences are' + " `%{group}`, which inserts the group name, and `%{trace}`, which" + " inserts the trace name. If grouping GDP data by country when more" + " than one trace is present, for example, the default" + ' "%{group} (%{trace})" would return "Monaco (GDP per capita)".', + }, + GROUPS: { + TYPE: "array", + TITLE: "Groups", + DESCRIPTION: "Sets the groups in which the trace data will be split." + " For example, with `x` set to *[1, 2, 3, 4]* and `groups` set to" + " *['a', 'b', 'a', 'b']*," + " the groupby transform with split in one trace with `x` [1, 3] and one" + " trace with `x` [2, 4]." + " Multiple data columns means the group will be the concatenation" + " of all the rows.", + OPTIONS: {DISABLE_COLLAPSE: True}, + "items": { + "type": "string", + TITLE: "Column Name", + "enum": self.possible_column_names, + }, + }, + "Styles": { + TYPE: "array", + TITLE: "Styles", + DESCRIPTION: "Sets each group styles. For example, with `groups` set " + "to *['a', 'b', 'a', 'b']* and `styles` set to " + "*[{target: 'a', value: { marker: { color: 'red' } }}]* marker points" + " in group *'a'* will be drawn in red. " + "Only use if specifying a single column to group by.", + ITEMS: { + TITLE: "Style Dictionary", + TYPE: OBJECT, + PROPERTIES: { + "target": { + TYPE: "string", + DESCRIPTION: "Value of data in column", + # watch will call the functions in enumSource (default_selected_filter, + # identity_callback; defined in the JS) whenever COLUMN_NAME is changed, + # it will also store the value of COLUMN_NAME to a variable called COLUMN_NAME + # to be used by the JS + "watch": { + GROUPS: ".".join( + [ + "groupby_id", + GROUPS, + ] + ), + }, + "enumSource": [ + { + "source": self.unique_entry_values_list, + "filter": COLUMN_VALUE_FILTER, + "title": CALLBACK, + "value": CALLBACK, + } + ], + }, + "value": { + TYPE: "object", + PROPERTIES: { + "marker": { + TYPE: "object", + PROPERTIES: { + "color": { + TYPE: "string" + } + }, + } + }, + }, + }, + }, + }, + }, + }, + AGGREGATE: { + TYPE: ARRAY_STRING, + DESCRIPTION: "Performs aggregations on the data", + ITEMS: { + TYPE: OBJECT, + REQUIRED: [GROUPS, AGGREGATIONS], + PROPERTIES: { + "enabled": { + TYPE: BOOLEAN, + DEFAULT: True, + DESCRIPTION: "Determines whether the transform is enabled or disabled.", + }, + GROUPS: { + TYPE: "array", + TITLE: "Groups", + DESCRIPTION: "Sets the grouping target to which the aggregation is" + " applied. Data points with matching group values will be coalesced" + " into one point, using the supplied aggregation functions to reduce" + " data in other data arrays. If a string, `groups` is assumed" + " to be a reference to a data array in the parent trace" + " object. To aggregate by nested variables, use *.* to" + " access them. For example, set `groups` to *marker.color* to aggregate" + " about the marker color array. If an array, `groups` is itself the " + "data array by which we aggregate." + " Multiple data columns means the group will be the " + "concatenation of all the rows.", + OPTIONS: {DISABLE_COLLAPSE: True}, + "items": { + "type": "string", + TITLE: "Column Name", + "enum": self.possible_column_names, + }, + }, + AGGREGATIONS: { + "type": ARRAY_STRING, + TITLE: "Aggregations", + OPTIONS: {DISABLE_COLLAPSE: True}, + ITEMS: { + TYPE: OBJECT, + TITLE: "Aggregation", + REQUIRED: [TARGET, "func"], + PROPERTIES: { + TARGET: { + TYPE: STRING, + DESCRIPTION: "A reference to the data array in the parent trace to" + " aggregate. To aggregate by nested variables, use *.*" + " to access them. For example, set `groups` to" + " *marker.color* to aggregate over the marker color array." + " The referenced array must already exist, unless `func`" + " is *count*, and each array may only be referenced once.", + }, + "func": { + TYPE: STRING, + DESCRIPTION: "Sets the aggregation function. All values from the linked" + " `target`, corresponding to the same value in the" + " `groups` array, are collected and reduced by " + "this function. " + "*count* is simply the number of values in the `groups` " + "array, so does not even require the linked array " + "to exist." + " *first* (*last*) is just the first (last) linked value. " + "Invalid values are ignored, so for example in *avg* they" + " do not contribute to either the numerator or the" + " denominator. Any data type (numeric, date, category)" + " may be aggregated with any function, even though in" + " certain cases it is unlikely to make sense, for example" + " a sum of dates or average of categories. *median* will" + " return the average of the two central values if there" + " is an even count. *mode* will return the first value" + " to reach the maximum count, in case of a tie." + " *change* will return the difference between the first" + " and last linked values. *range* will return the" + " difference between the min and max linked values.", + ENUM: [ + "avg", + "sum", + "min", + "max", + "mode", + "median", + "count", + "stddev", + "first", + "last", + ], + }, + "funcmode": { + TYPE: STRING, + ENUM: [ + "sample", + "population", + ], + DEFAULT: "sample", + DESCRIPTION: "*stddev* supports two formula variants: *sample*" + " (normalize by N-1) and *population* (normalize by N).", + }, + "enabled": { + TYPE: BOOLEAN, + "valType": "boolean", + DEFAULT: True, + "editType": "calc", + DESCRIPTION: "Determines whether this aggregation function" + " is enabled or disabled.", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + LAYOUT: { + "title": "Graph Layout", + DESCRIPTION: "Determines how the graph looks (optional)", + "type": "object", + OPTIONS: {DISABLE_COLLAPSE: True, REMOVE_EMPTY_PROPERTIES: True}, + }, + }, + } + return schema + + def build_individual_plot_type_schema(self, plot_type): + """ + Seperate schemas for each plot + :return: dict_of_schemas, schema_to_type + """ + # If the element is not in required we will delete it + programmed_data_options = [TYPE, X, Y, Z, MODE] + + directions_for_building_schemas = { + SCATTER: { + ENUM: [SCATTERGL, SCATTER], + REQUIRED: [TYPE, X, Y, MODE], + DESCRIPTION: { + TYPE: "scattergl uses uses WebGL which is faster for lots of points" + }, + }, + BAR: {ENUM: [BAR], REQUIRED: [TYPE, X, Y],}, + BOX: { + ENUM: [BOX], + REQUIRED: [TYPE, X, Y], + DESCRIPTION: { + TYPE: "To show more than one box in the plot, " + 'set "group by" in visualization options below', + }, + }, + VIOLIN: { + ENUM: [VIOLIN], + REQUIRED: [TYPE, Y], + DESCRIPTION: { + TYPE: "To show more than one violin in the plot, " + 'set "group by" in visualization options below', + }, + }, + HISTOGRAM: {ENUM: [HISTOGRAM], REQUIRED: [TYPE, X],}, + CONTOUR: {ENUM: [CONTOUR], REQUIRED: [TYPE, X, Y, Z],}, + HISTOGRAM2D: { + ENUM: [HISTOGRAM2D, HISTOGRAM2DCONTOUR], + REQUIRED: [TYPE, X, Y], + }, + MESH3D: {ENUM: [MESH3D], REQUIRED: [TYPE, X, Y, Z],}, + HEATMAP: { + ENUM: [HEATMAPGL, HEATMAP], + REQUIRED: [TYPE, X, Y, Z], + DESCRIPTION: { + TYPE: "heatmapgl uses WebGL which may be faster for lots of points" + }, + }, + SCATTER3D: {ENUM: [SCATTER3D], REQUIRED: [TYPE, X, Y, Z, MODE],}, + } + + plot_schema = self.build_generic_plotly_plot_schema_template() + + # populate the layout dictionary with the plotly api + with open("plotly_api/layout_plotly_schema.json", "r") as file: + plotly_layout_schema = json.load(file) + plot_schema[PROPERTIES][LAYOUT][PROPERTIES] = plotly_layout_schema[PROPERTIES] + directions = directions_for_building_schemas[plot_type] + plot_schema[PROPERTIES][DATA][ITEMS][PROPERTIES][TYPE][ENUM] = directions[ENUM] + if len(directions[ENUM]) > 1: + plot_schema[PROPERTIES][DATA][ITEMS][PROPERTIES][TYPE][OPTIONS][ + HIDDEN + ] = False + plot_schema[PROPERTIES][DATA][ITEMS][REQUIRED] = directions[REQUIRED] + # removing unnecessary elements from the general Plotly schema + # that are not needed for this plot type + elements_to_delete = [ + x for x in programmed_data_options if x not in directions[REQUIRED] + ] + for element in elements_to_delete: + del plot_schema[PROPERTIES][DATA][ITEMS][PROPERTIES][element] + description_dict = directions.get(DESCRIPTION, {}) + for key_in_schema, description in description_dict.items(): + plot_schema[PROPERTIES][DATA][ITEMS][PROPERTIES][key_in_schema][ + DESCRIPTION + ] = description + + plot_schema = merge_with_plotly_api(plot_schema, plot_type) + + return plot_schema + + def build_data_filter_schema(self): + selectable_data_schema = super().build_data_filter_schema() + selectable_data_schema[PROPERTIES][GROUPBY] = { + "type": "object", + "title": "Group By Selector", + "required": [ENTRIES], + OPTIONS: {COLLAPSED: self.collapse_dict[GROUPBY_SELECTOR]}, + "additionalProperties": False, + PROPERTIES: { + ENTRIES: { + "type": "array", + OPTIONS: {DISABLE_COLLAPSE: True}, + TITLE: "Entries", + "items": {"type": "string", "enum": self.filter_column_names,}, + }, + "multiple": {"type": "boolean"}, + DEFAULT_SELECTED: { + "type": "array", + TITLE: "Default Selected", + "description": "optional, default filter, list of column values", + "items": {"type": "string", "enum": self.filter_column_names,}, + }, + }, + } + return selectable_data_schema + + @staticmethod + def get_available_plots(): + return SUPPORTED_PLOTS + + +def merge_with_plotly_api(schema_dict: dict, plot_type: str) -> dict: + """ + Take properties from the plotly api and merge them into the hand-defined schema. We + manually specify some things we want to show in our wizard, but populate the rest + by default + :param schema_dict: + :param plot_type: + :return: + """ + with open(f"plotly_api/{plot_type}_plotly_schema.json", "r") as file: + plotly_schema = json.load(file) + + schema_property_dict = schema_dict[PROPERTIES][DATA][ITEMS][PROPERTIES] + for property_key, property_info in plotly_schema[ATTRIBUTES].items(): + if property_key in schema_property_dict: + continue + schema_property_dict[property_key] = property_info + schema_dict[PROPERTIES][DATA][ITEMS][PROPERTIES] = schema_property_dict + # check for special layout properties with this type of plot + if LAYOUT_ATTRIBUTES in plotly_schema: + schema_dict[PROPERTIES][LAYOUT][PROPERTIES].update( + plotly_schema[LAYOUT_ATTRIBUTES] + ) + return schema_dict diff --git a/escalation/graphics/build_seaborn_schema.py b/escalation/graphics/build_seaborn_schema.py new file mode 100644 index 0000000..f383a5c --- /dev/null +++ b/escalation/graphics/build_seaborn_schema.py @@ -0,0 +1,427 @@ +# Copyright [2021] [Two Six Labs, LLC], +# Licensed under the Apache License, Version 2.0, + +""" +There is no schema for seaborn available. +Build one by parsing the Seaborn code docstrings using NumpyDocString (which covers the Seaborn documentation formatting) +""" + +from collections import defaultdict +import inspect +import re +import seaborn as sns +from numpydoc.docscrape import NumpyDocString + +from utility.constants import * +from graphics.graphic_schema import GraphicsConfigInterfaceBuilder + +X = "x" +Y = "y" +Z = "z" + +# these are the plot types that don't require function calls on the data, and don't have +# dot-separated function names (which we haven't checked yet +SUPPORTED_PLOTS = [ + {GRAPHIC_NAME: "Scatter Plot", VALUE: "scatterplot"}, + {GRAPHIC_NAME: "Line Plot", VALUE: "lineplot"}, + # {GRAPHIC_NAME: "Relational Plot", VALUE: "relplot"}, + {GRAPHIC_NAME: "Histogram Plot", VALUE: "histplot"}, + {GRAPHIC_NAME: "Kernel Density Estimate Plot", VALUE: "kdeplot"}, + { + GRAPHIC_NAME: "Empirical Cumulative Distribution Function Plot", + VALUE: "ecdfplot", + }, + # {GRAPHIC_NAME: "Distribution Plot", VALUE: "displot"}, + # {GRAPHIC_NAME: "Rug Plot", VALUE: "rugplot"}, + {GRAPHIC_NAME: "Strip Plot", VALUE: "stripplot"}, + {GRAPHIC_NAME: "Beeswarm Plot", VALUE: "swarmplot"}, + {GRAPHIC_NAME: "Box Plot", VALUE: "boxplot"}, + {GRAPHIC_NAME: "Violin Plot", VALUE: "violinplot"}, + {GRAPHIC_NAME: "Enhanced Box Plot", VALUE: "boxenplot"}, + {GRAPHIC_NAME: "Point Plot", VALUE: "pointplot"}, + {GRAPHIC_NAME: "Bar Plot", VALUE: "barplot"}, + {GRAPHIC_NAME: "Count Plot", VALUE: "countplot"}, + # {GRAPHIC_NAME: "Categorical Plot", VALUE: "catplot"}, + # {GRAPHIC_NAME: "Data and Regression Model Fit Plot (lmplot)", VALUE: "lmplot"}, + # {GRAPHIC_NAME: "Data and a Linear Regression Model Fit Plot", VALUE: "regplot"}, + # {GRAPHIC_NAME: "Residuals of a Linear Regression Plot", VALUE: "residplot"}, + # {GRAPHIC_NAME: "Heat Map", VALUE: "heatmap"}, + # {GRAPHIC_NAME: "Hierarchically-Clustered Heat Map Plot", VALUE: "clustermap"}, + # {GRAPHIC_NAME: "Pairwise Relationships Plot", VALUE: "pairplot"}, + # {GRAPHIC_NAME: "Joint Distribution Plot", VALUE: "jointplot"}, +] + +# these are implemented in seaborn as classes, and need slightly different treatment +SUPPORTED_PLOT_CLASSES = [ + "FacetGrid", # i + "PairGrid", + "JointGrid", +] + +WIZARD_CUSTOMIZATION = { + "scatterplot": {REQUIRED: ["x", "y"]}, + "lineplot": {REQUIRED: ["x", "y"]}, + # "relplot": { + # REQUIRED: ["x", "y", "kind", "col"], + # ENUM: {"kind": ["scatter", "line"]}, + # }, + "histplot": {REQUIRED: ["x"]}, + "kdeplot": {REQUIRED: ["x"]}, + "ecdfplot": {REQUIRED: ["x"]}, + # "displot": { + # REQUIRED: ["x", "kind", "col"], + # ENUM: {"kind": ["hist", "kde", "ecdf"]}, + # }, + "stripplot": {REQUIRED: ["x", "y"]}, + "swarmplot": {REQUIRED: ["x", "y"]}, + "boxplot": {REQUIRED: ["y"]}, + "violinplot": {REQUIRED: ["y"]}, + "boxenplot": {REQUIRED: ["y"]}, + "pointplot": {REQUIRED: ["x", "y"]}, + "barplot": {REQUIRED: ["x", "y"]}, + "countplot": {REQUIRED: ["x"]}, + # "catplot": { + # REQUIRED: ["x", "y", "kind", "col"], + # ENUM: { + # "kind": [ + # "strip", + # "swarm", + # "box", + # "violin", + # "boxen", + # "point", + # "bar", + # "count", + # ] + # }, + # }, + # "regplot": {REQUIRED: ["x", "y"]}, + # "residplot": {REQUIRED: ["x", "y"]}, + # "heatmap": {REQUIRED: []}, + # "clustermap": {REQUIRED: []}, +} + + +class SeabornGraphicSchema(GraphicsConfigInterfaceBuilder): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def build_individual_plot_type_schema(self, plot_name): + if self.possible_column_names: + self.possible_column_names.sort() + plot_function = getattr(sns, plot_name) + plot_function_signature = inspect.signature(plot_function).parameters + doc_object = NumpyDocString(plot_function.__doc__) + doc = build_docstring_with_separated_keys(doc_object) + # with open(os.path.join("graphics", "seaborn_schemas", f"{plot_name}_seaborn_schema.json"), 'w') as fout: + template_schema = get_seaborn_single_plot_schema_template(plot_name) + for param in plot_function_signature.keys(): + configurable_param_type = configurable_params.get(param) + # not whitelisted in schema for inclusion in configuration + if not configurable_param_type: + continue + param_dict = {} + if isinstance(configurable_param_type, list): + # all of our enumerated types are strings + param_dict[TYPE] = STRING + param_dict[ENUM] = configurable_param_type + elif ( + isinstance(configurable_param_type, str) + and configurable_param_type == COLUMN_NAMES + ): + param_dict[TYPE] = STRING + param_dict[ENUM] = self.possible_column_names + else: + param_dict[TYPE] = configurable_param_type + try: + param_dict[DESCRIPTION] = doc[param] + except: + # print(f"{param} not found in docstring for {plot_name}, not including") + continue + param_dict[TITLE] = param + # todo: check docstring if optional and collapse + # param_dict[DESCRIPTION][OPTIONS] = {HIDDEN: True}, + template_schema[PROPERTIES][DATA][ITEMS][PROPERTIES][param] = param_dict + plot_specific_customization = WIZARD_CUSTOMIZATION[plot_name] + enum_dict = plot_specific_customization.get(ENUM, {}) + for param, enum_elements in enum_dict.items(): + template_schema[PROPERTIES][DATA][ITEMS][PROPERTIES][param][ + ENUM + ] = enum_elements + template_schema[PROPERTIES][DATA][ITEMS][ + REQUIRED + ] += plot_specific_customization[REQUIRED] + return template_schema + + @staticmethod + def get_available_plots(): + return SUPPORTED_PLOTS + + +def get_seaborn_single_plot_schema_template(plot_name): + schema = { + "$schema": "http://json-schema.org/draft/2019-09/schema#", + "title": "Seaborn Graph Config", + "description": "Assembles graphs according to Seaborn definitions", + "type": "object", + "required": [DATA], + OPTIONS: {DISABLE_COLLAPSE: True, REMOVE_EMPTY_PROPERTIES: True}, + "defaultProperties": [DATA, LAYOUT], + "properties": { + DATA: { + "type": "array", + "description": "list of graphs to be plotted on a single plot", + TITLE: "Data", + MIN_ITEMS: 1, + MAX_ITEMS: 1, + OPTIONS: {DISABLE_COLLAPSE: True}, + "items": { + TYPE: "object", + TITLE: "Data Dictionary", + OPTIONS: {DISABLE_COLLAPSE: True}, + REQUIRED: ["type"], + PROPERTIES: { + "type": { + "type": "string", + TITLE: "Render Mode", + "enum": [plot_name], + OPTIONS: {HIDDEN: True}, + }, + }, + }, + }, + LAYOUT: { + "title": "Graph Layout", + DESCRIPTION: "Determines how the graph looks (optional)", + "type": "object", + OPTIONS: {DISABLE_COLLAPSE: True, REMOVE_EMPTY_PROPERTIES: True}, + PROPERTIES: { + FIGSIZE: { + TYPE: ARRAY_STRING, + TITLE: "Figure Size", + DESCRIPTION: "Width, height in inches. " + "Determines the size of the image when downloaded" + " and the aspect ratio when shown in the browser.", + MIN_ITEMS: 2, + MAX_ITEMS: 2, + ITEMS: [ + { + TYPE: NUMBER, + TITLE: "Width, coord", + MINIMUM: 0, + DEFAULT: 8, + }, + { + TYPE: NUMBER, + TITLE: "Height, coord", + MINIMUM: 0, + DEFAULT: 6, + }, + ], + } + }, + }, + }, + } + return schema + + +configurable_params = dict( + [ + ("x", COLUMN_NAMES), # column_names + ("y", COLUMN_NAMES), # column_names + ("hue", COLUMN_NAMES), # column_names + ("palette", STRING), + ("hue_order", ARRAY_STRING), # ARRAY_STRING of string values in a column + ("color", STRING), + ("order", ARRAY_STRING), # ARRAY_STRING of string values in a column + ("legend", ["auto", "brief", "full", False]), + ("hue_norm", ARRAY_STRING), + ("orient", ["v", "h"]), + ("units", COLUMN_NAMES), # column_names + ("dodge", BOOLEAN), + ("size", COLUMN_NAMES), # column_names + ("height", NUMBER), + ("ci", INTEGER), + ("n_boot", INTEGER), + ("markers", STRING), + ("seed", INTEGER), + ("kind", STRING), # todo: enum? + ("aspect", NUMBER), + # ("estimator", 5), + ("linewidth", NUMBER), + ("saturation", NUMBER), + ("row", COLUMN_NAMES), + ("col", COLUMN_NAMES), + ("col_wrap", INTEGER), + ("row_order", ARRAY_STRING), # ARRAY_STRING of string values in a column + ("col_order", ARRAY_STRING), # ARRAY_STRING of string values in a column + ("weights", COLUMN_NAMES), # column_names + ( + "log_scale", + BOOLEAN, + ), # can also be a number defining log scale, or a pair for both axes + # ("line_kws", 4), + # ("cbar_kws", 4), + ("robust", BOOLEAN), + ("dropna", BOOLEAN), + ("style", STRING), + # ("sizes", 3), ("size_order", 3), ("size_norm", 3), ("style_order", 3), used when specifying ARRAYs corresponding to values + # ("facet_kws", 3), + ("x_bins", INTEGER), + ("x_jitter", NUMBER), + ("y_jitter", NUMBER), + ("cbar", BOOLEAN), + # ("cbar_ax", 3), # check: this should be applied to current graph ax only + ("width", NUMBER), + ("scale", [None, "area", "count", "width"]), + ("lowess", BOOLEAN), + ("x_partial", STRING), + ("y_partial", STRING), + # ("scatter_kws", 3), + ("dashes", BOOLEAN), + ("stat", ["count", "frequency", "density", "probability"]), + ("cumulative", BOOLEAN), + ("common_norm", BOOLEAN), + ("multiple", ["layer", "dodge", "stack", "fill"]), + ("fill", BOOLEAN), + ("thresh", NUMBER), + # ("bw", [‘scott’, ‘silverman’, NUMBER]), + ("gridsize", INTEGER), + ("cut", NUMBER), + ("legend_out", BOOLEAN), + ("sharex", BOOLEAN), + ("sharey", BOOLEAN), + ("edgecolor", STRING), + ("errwidth", NUMBER), + ("capsize", NUMBER), + # ("x_estimator", 2), + # ("x_ci", 2), + ("scatter", BOOLEAN), + ("fit_reg", BOOLEAN), + ("logistic", BOOLEAN), + ("logx", BOOLEAN), + ("truncate", BOOLEAN), + ("label", STRING), + # ("mask", 2), + ("y_bins", INTEGER), + ("alpha", NUMBER), + ("sort", None), + ("err_style", None), + # ("err_kws", None), + ("rug", None), + # ("rug_kws", None), + ("bins", None), + ("binwidth", None), + ("binrange", None), + ("discrete", None), + ("common_bins", BOOLEAN), + ("element", None), + ("shrink", None), + ("kde", None), + # ("kde_kws", None), + ("pthresh", None), + ("pmax", None), + ("shade", None), + ("vertical", None), + ("kernel", None), + ("clip", None), + ("shade_lowest", None), + ("common_grid", None), + ("levels", None), + ("bw_method", None), + ("bw_adjust", None), + ("data2", None), + ("complementary", None), + ("axis", None), + ("expand_margins", None), + ("a", None), + ("margin_titles", None), + ("jitter", None), + ("fliersize", None), + ("whis", None), + ("scale_hue", None), + ("inner", None), + ("split", None), + ("k_depth", None), + ("outlier_prop", None), + ("trust_alpha", None), + ("showfliers", None), + ("linestyles", None), + ("join", None), + ("errcolor", None), + ("marker", None), + ("vmin", None), + ("vmax", None), + ("cmap", None), + ("center", None), + ("annot", None), + ("fmt", None), + # ("annot_kws", None), + ("linewidths", NUMBER), + ("linecolor", STRING), # enum matplotlib color codes with optional user entry + ("square", None), + ("xticklabels", None), + ("yticklabels", None), + # ("pivot_kws", None), + ("method", None), + ("metric", None), + ("z_score", None), + ("standard_scale", None), + ("figsize", None), + ("row_cluster", None), + ("col_cluster", None), + ("row_linkage", None), + ("col_linkage", None), + ("row_colors", None), + ("col_colors", None), + ("dendrogram_ratio", None), + ("colors_ratio", None), + ("cbar_pos", None), + # ("tree_kws", None), + ("vars", None), + ("x_vars", None), + ("y_vars", None), + ("diag_kind", None), + ("corner", None), + # ("plot_kws", None), + # ("diag_kws", None), + # ("grid_kws", None), + ("ratio", None), + ("space", None), + ("xlim", ARRAY_STRING), + ("ylim", ARRAY_STRING), + ("marginal_ticks", None), + # ("joint_kws", None), + # ("marginal_kws", None), + ] +) + + +def build_docstring_with_separated_keys(numpy_doc): + """ + the docstrings are written with some params combined. break them up + :param numpy_doc: NumpyDocString instance + :return: dict + """ + docs_dict = {} + expandable_variable_regex = r"\{(.+)\}(.+)" + for param in numpy_doc["Parameters"]: + groups = re.match(expandable_variable_regex, param.name) + if groups: + # make params in form {x, y}_bins into x_bins and y_bins params + split_params = [x + groups[2] for x in groups[1]] + else: + split_params = [x.strip() for x in param.name.split(",")] + for param_name in split_params: + docs_dict[param_name] = param.desc + return docs_dict + + +# todo: plot classes in schema +# for plot_name in SUPPORTED_PLOT_CLASSES: +# print(f"\n{plot_name}") +# plot_class = getattr(sns, plot_name) +# plot_function = plot_class.__init__ +# doc = NumpyDocString(plot_function.__doc__) +# print(doc["Parameters"][0]) diff --git a/escalation/graphics/cytoscape.py b/escalation/graphics/cytoscape.py new file mode 100644 index 0000000..a429bd5 --- /dev/null +++ b/escalation/graphics/cytoscape.py @@ -0,0 +1,204 @@ +import json + +from graphics.graphic_class import Graphic +from utility.constants import ( + DATA, + NODE_ID, + GROUP, + NODES, + ID, + SOURCE, + TARGET, + EDGE_ID, + EDGES, + ELEMENTS, + ELEMENT_PROPERTIES, + COLUMN_NAME, + STYLE, + LAYOUT, + PROPERTY_NAME, + PROPERTIES, + STYLE_NAME, + ELEMENT_STYLE, + SELECTOR, + PLOT_SPECIFIC_INFO, +) + +keys_transferred_as_is = [ + LAYOUT, + "data", + "zoom", + "pan", + "minZoom", + "maxZoom", + "zoomingEnabled", + "userZoomingEnabled", + "panningEnabled", + "userPanningEnabled", + "boxSelectionEnabled", + "selectionType", + "touchTapThreshold", + "desktopTapThreshold", + "autoungrabify", + "autolock", + "autounselectify", + "headless", + "styleEnabled", + "hideEdgesOnViewport", + "textureOnViewport", + "motionBlur", + "motionBlurOpacity", + "wheelSensitivity", + "pixelRatio", +] + + +class Cytoscape(Graphic): + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) + self.id_dict = {} + self.node_dict = {} + self.edge_dict = {} + + def data_to_element_array(self, instruction_array: list, is_property: bool = True): + """ + Uses the instructions in the element array to put the data into the node and edge dict + :param instruction_array: + :param is_property: property or style + """ + position_name_lookup = {"position_x": "x", "position_y": "y"} + if is_property: + name = PROPERTY_NAME + intermediate_name = PROPERTIES + else: + name = STYLE_NAME + intermediate_name = STYLE + + for instruction_dict in instruction_array: + property_name = instruction_dict[name] + element_type = instruction_dict[GROUP] + if element_type == NODES: + element_dict = self.node_dict # both sides should have same reference + elif element_type == EDGES: + element_dict = self.edge_dict # both sides should have same reference + for element_id, value in zip( + self.id_dict[element_type], self.data[instruction_dict[COLUMN_NAME]] + ): + + if is_property and property_name in ["position_y", "position_x"]: + position = position_name_lookup[property_name] + position_dict_temp = element_dict[ID][intermediate_name].get( + "position", {} + ) + position_dict_temp[position] = value + element_dict[element_id][intermediate_name][ + "position" + ] = position_dict_temp + else: + element_dict[element_id][intermediate_name][property_name] = value + + def make_arrays_for_cytoscape(self, style_array_for_cytoscape) -> [list, list]: + """ + creates element and adds to style array from node and edge dictionaries for Cytoscape dictionary + :param style_array_for_cytoscape: current array with style options from plot_options + :return: + """ + + elements = [] + for element_dict, group in zip( + [self.node_dict, self.edge_dict], [NODES, EDGES] + ): + for element_id, properties in element_dict.items(): + temp_dict = properties.get(PROPERTIES, {}) + temp_dict[GROUP] = group + temp_dict[DATA] = {ID: element_id} + if group == EDGES: + temp_dict[DATA][SOURCE] = properties[SOURCE] + temp_dict[DATA][TARGET] = properties[TARGET] + elements.append(temp_dict) + element_style_dict = properties[STYLE] + if element_style_dict: + style_array_for_cytoscape.append( + {SELECTOR: f"#{element_id}", STYLE: element_style_dict} + ) + return elements, style_array_for_cytoscape + + def make_dict_for_html_plot(self) -> str: + """ + Takes in the data as well user specified json and makes a json according to the specification of Cytoscape + """ + plot_options = self.graphic_dict[PLOT_SPECIFIC_INFO] + + cytoscape_dict = { + key: value + for key, value in plot_options.items() + if key in keys_transferred_as_is + } + + sources = self.data[plot_options[SOURCE]] + targets = self.data[plot_options[TARGET]] + + nodes = ( + self.data[plot_options[NODE_ID]] + if NODE_ID in plot_options + else set(sources).union(set(targets)) + ) + edges = ( + self.data[plot_options[EDGE_ID]] + if EDGE_ID in plot_options + else [f"{source}_to_{target}" for source, target in zip(sources, targets)] + ) + + id_dict = {NODES: nodes, EDGES: edges} + + # For simplicity, we add an extra step. we take the element level properties put them into dictionaries + # then put them into the cytoscape json. The major reason being that cytoscape takes an array of dictionaries + # with a key:value pair identifying the element + + # create element level dictionaries. maybe combined the two dictionaries. + node_dict = {node: {PROPERTIES: {}, STYLE: {}} for node in nodes} + edge_dict = {edge: {PROPERTIES: {}, STYLE: {}} for edge in edges} + for source, target, edge in zip(sources, targets, edges): + edge_dict[edge][SOURCE] = source + edge_dict[edge][TARGET] = target + + self.id_dict = id_dict + self.node_dict = node_dict + self.edge_dict = edge_dict + # Fill up the element level dictionaries + # plot_option[ELEMENT_PROPERTIES] are data that goes into the cytoscape element array on a per element basis + self.data_to_element_array(plot_options.get(ELEMENT_PROPERTIES, []), True) + # plot_option[ELEMENT_STYLE] are data that goes into the cytoscape style array on a per element basis + self.data_to_element_array(plot_options.get(ELEMENT_STYLE, []), False) + + # create json for cytoscape + (elements, style_array_for_cytoscape,) = self.make_arrays_for_cytoscape( + plot_options.get(STYLE, []) + ) + + cytoscape_dict[ELEMENTS] = elements + cytoscape_dict[STYLE] = style_array_for_cytoscape + + self.graph_json_str = json.dumps(cytoscape_dict) + + @staticmethod + def get_graph_html_template() -> str: + return "cytoscape.html" + + def get_data_columns(self) -> set: + """ + extracts what columns of data are needed from the plot_options + :param plot_options: + :return: + """ + plot_options = self.graphic_dict[PLOT_SPECIFIC_INFO] + possible_keys = [NODE_ID, SOURCE, TARGET, EDGE_ID] + set_of_column_names = set() + for key_name in possible_keys: + if key_name in plot_options: + set_of_column_names.add(plot_options[key_name]) + for key_name in [ELEMENT_PROPERTIES, ELEMENT_STYLE]: + if key_name in plot_options: + for option_dict in plot_options[key_name]: + set_of_column_names.add(option_dict[COLUMN_NAME]) + return set_of_column_names diff --git a/escalation/graphics/graphic_class.py b/escalation/graphics/graphic_class.py index 713eb69..58b30a8 100644 --- a/escalation/graphics/graphic_class.py +++ b/escalation/graphics/graphic_class.py @@ -3,32 +3,326 @@ from abc import ABC, abstractmethod +from collections import defaultdict + +from utility.available_selectors import AVAILABLE_SELECTORS +from parser import ParserError + +from dateutil.parser import parse as datetime_parse +from utility.constants import * + +NUMERICAL_FILTER_DICT = {MAX: "<=", MIN: ">="} class Graphic(ABC): - def __init__(self): - pass + def __init__(self, graphic_dict: dict, addendum_dict: dict = None): + + """ + :param graphic_dict: Copy of part of the original config dict. + Outside of getting the data, only functions in reformatting_functions.py should modifies the copy of config dict. + :param addendum_dict: e.g ImmutableMultiDict([('graphic_name', 'graphic_0'), ('selection_0', 'SHOW_ALL_ROW'), + ('selection_2_upper_operation', '<='), ('selection_2_upper_value', '4'))]) + Should not pass an empty ImmutableMultiDict + """ + if addendum_dict is None: + addendum_dict = {} + self.graphic_dict = graphic_dict + self.addendum_dict = addendum_dict + self.select_info = [] + self.data = {} + self.unique_entry_dict = {} + self.graph_json_str = "{}" @staticmethod @abstractmethod - def make_dict_for_html_plot(self, data, data_to_struct, plot_options, hover_data): + def get_graph_html_template() -> str: """ + returns the graph html template the graphic library uses + :param plot_options: + :return: + """ + raise NotImplementedError - - :param data: dictionary of data - :param data_to_struct: instructions of some kind how to put the data into plot_options - :param plot_options: dictionary that contains all the information to make a graphic minus the data - :param hover_data: - :return: instructions that will be passed to the html that will plot the object. + @abstractmethod + def make_dict_for_html_plot(self, data): + """ + creates the json that is passed to the html template """ raise NotImplementedError - @staticmethod @abstractmethod - def get_data_columns(self, plot_options) -> set: + def get_data_columns(self) -> set: """ - extracts what columns of data are needed from the plot_options + extracts what columns of data are needed from the self.graphic_dict[PLOT_SPECIFIC_INFO] :param plot_options: :return: """ raise NotImplementedError + + def set_unique_entry_dict(self, unique_entry_dict): + self.unique_entry_dict = unique_entry_dict + + def add_instructions_to_config_dict(self): + if SELECTABLE_DATA_DICT in self.graphic_dict: + self.add_active_selectors_to_selectable_data_list() + if self.addendum_dict: + self.add_operations_to_the_data_from_addendum() + else: + self.add_operations_to_the_data_from_defaults() + self.modify_graphic_dict_based_on_addendum_dict() + + def add_active_selectors_to_selectable_data_list(self): + """ + Sets which selectors are active based on user choices. + If none have been selected sets reasonable defaults + :return: + """ + selectable_data_dict = self.graphic_dict[SELECTABLE_DATA_DICT] + filter_list = selectable_data_dict.get(FILTER, []) + for index, filter_dict in enumerate(filter_list): + + selected_filters = self.addendum_dict.get( + get_key_for_form(FILTER, index), [] + ) + if SHOW_ALL_ROW in selected_filters: + filter_dict[ACTIVE_SELECTORS] = [SHOW_ALL_ROW] + else: + filter_dict[ACTIVE_SELECTORS] = selected_filters or filter_dict.get( + DEFAULT_SELECTED, [SHOW_ALL_ROW] + ) + + numerical_filter_list = selectable_data_dict.get(NUMERICAL_FILTER, []) + for index, numerical_filter_dict in enumerate(numerical_filter_list): + extrema = [MAX, MIN] + active_numerical_filter_dict = defaultdict(dict) + for extremum in extrema: + # pull the relevant filter info from the submitted form + # all values in addendum_dict are lists + numerical_filter_value = self.addendum_dict.get( + NUMERICAL_FILTER_NUM_LOC_TYPE.format(index, extremum, VALUE) + ) + active_numerical_filter_dict[extremum][VALUE] = ( + numerical_filter_value[0] + if numerical_filter_value + else numerical_filter_dict.get(extremum, "") + ) + + numerical_filter_dict[ACTIVE_SELECTORS] = active_numerical_filter_dict + + def add_operations_to_the_data_from_addendum(self): + """ + Adds operations to be passed to the data handlers for the data + Broken into two major parts read in info from selection_dict and addendum_dict and then + output a filter dict or change visualization_info_list or data_info_dict depending on the kind of filter + :selectable_data_dict: each element of the dictionary on is how to build the selector on the webpage + :addendum_dict: User selections form the webpage + :return: + """ + selectable_data_dict = self.graphic_dict[SELECTABLE_DATA_DICT] + operation_list = [] + + # creates an operations where only the values selected along a column will be shown in the plot + filter_list = selectable_data_dict.get(FILTER, []) + for index, filter_dict in enumerate(filter_list): + base_info_dict_for_selector = get_base_info_for_selector( + filter_dict, FILTER + ) + selection = self.addendum_dict.get(get_key_for_form(FILTER, index)) + if len(selection) == 0 or SHOW_ALL_ROW in selection: + continue + base_info_dict_for_selector[SELECTED] = selection + base_info_dict_for_selector[FILTERED_SELECTOR] = filter_dict.get( + FILTERED_SELECTOR, False + ) + operation_list.append(base_info_dict_for_selector) + + # creates an operations where only the values following an (in)equality + # along a column will be shown in the plot + numerical_filter_list = selectable_data_dict.get(NUMERICAL_FILTER, []) + for index, numerical_filter_dict in enumerate(numerical_filter_list): + base_info_dict_for_selector = get_base_info_for_selector( + numerical_filter_dict, NUMERICAL_FILTER + ) + # the numerical filter contains two filters so add them separately + for extremum in [MAX, MIN]: + # get the value submitted in the web form by using its name + # format specified in numeric_filter.html + # the value is a list of length one + numerical_value = self.addendum_dict[ + NUMERICAL_FILTER_NUM_LOC_TYPE.format(index, extremum, VALUE) + ][0] + if numerical_value == "": + continue + + numerical_filter_info = { + VALUE: convert_string_for_numerical_filter(numerical_value), + OPERATION: NUMERICAL_FILTER_DICT[extremum], + } + operation_list.append( + {**base_info_dict_for_selector, **numerical_filter_info} + ) + self.graphic_dict[DATA_FILTERS] = operation_list + + def add_operations_to_the_data_from_defaults(self): + """ + Adds operations to be passed to the data handlers for the data + Broken into two major parts read in info from selection_dict and then output a filter dict + :selectable_data_dict: each filter element of the dict is a list of dictionary on how to build + the selector on the webpage + :return: + """ + selectable_data_dict = self.graphic_dict[SELECTABLE_DATA_DICT] + operation_list = [] + filter_list = selectable_data_dict.get(FILTER, []) + for index, filter_dict in enumerate(filter_list): + if DEFAULT_SELECTED in filter_dict: + base_info_dict_for_selector = get_base_info_for_selector( + filter_dict, FILTER + ) + selection = filter_dict.get(DEFAULT_SELECTED) + # make sure we don't have an empty selection list, + # or a list that only contains an empty string + if selection and selection != [""]: + base_info_dict_for_selector[SELECTED] = ( + selection if isinstance(selection, list) else [selection] + ) + base_info_dict_for_selector[FILTERED_SELECTOR] = filter_dict.get( + FILTERED_SELECTOR, False + ) + operation_list.append(base_info_dict_for_selector) + + numerical_filter_list = selectable_data_dict.get(NUMERICAL_FILTER, []) + for index, numerical_filter_dict in enumerate(numerical_filter_list): + for extremum in [MAX, MIN]: + numerical_value = numerical_filter_dict.get(extremum) + if numerical_value == "" or numerical_value is None: + continue + base_info_dict_for_selector = get_base_info_for_selector( + numerical_filter_dict, NUMERICAL_FILTER + ) + numerical_filter_info = { + VALUE: convert_string_for_numerical_filter(numerical_value), + OPERATION: NUMERICAL_FILTER_DICT[extremum], + } + operation_list.append( + {**base_info_dict_for_selector, **numerical_filter_info} + ) + if operation_list: + self.graphic_dict[DATA_FILTERS] = operation_list + + def modify_graphic_dict_based_on_addendum_dict(self): + """ + Subclass this method if a subclass has data selectors defined that should change hte graphic_dict + See Plotly for an example + """ + pass + + def get_columns_that_need_unique_entries(self) -> set: + """ + extracts what data are needed for the selectors + :param plot_options: + :return: + """ + filter_list = self.graphic_dict.get(SELECTABLE_DATA_DICT, {}).get(FILTER, []) + column_list = [ + filter_dict[OPTION_COL] + for filter_dict in filter_list + if filter_dict.get(VISIBLE, True) + ] + return column_list + + def create_data_subselect_info_for_plot(self): + """ + puts selector data in form to be read by html file + Broken into two major parts read in info from selection_option_dict_for_plot and then populate + select_info elements + :param plot_specification: + :param data_handler: + :return: + """ + + selectable_data_dict = self.graphic_dict.get(SELECTABLE_DATA_DICT, {}) + + filter_list = selectable_data_dict.get(FILTER, []) + for index, filter_dict in enumerate(filter_list): + if filter_dict.get(VISIBLE, True): + column = filter_dict[OPTION_COL] + + selector_entries = self.unique_entry_dict[column] + selector_entries.sort() + # append show_all_rows to the front of the list + selector_entries.insert(0, SHOW_ALL_ROW) + self.select_info.append( + make_filter_dict(FILTER, filter_dict, index, selector_entries) + ) + + numerical_filter_list = selectable_data_dict.get(NUMERICAL_FILTER, []) + for index, numerical_filter_dict in enumerate(numerical_filter_list): + if numerical_filter_dict.get(VISIBLE, True): + self.select_info.append( + make_filter_dict(NUMERICAL_FILTER, numerical_filter_dict, index) + ) + + +def get_key_for_form(selector_type, index): + """ + We getting the div id based on string formatting here as set in make_filter_dict + For example for selector_type=filter + AVAILABLE_SELECTORS[selector_type][SELECTOR_NAME] is filter_{} + :param selector_type: + :param index: + :return: + """ + selection_index_str = AVAILABLE_SELECTORS[selector_type][SELECTOR_NAME].format( + index + ) + return selection_index_str + + +def convert_string_for_numerical_filter(value: str): + try: + return float(value) + except ValueError: + try: + return datetime_parse(value) + except ParserError: + raise (ValueError, "neither a float nor a datetime") + + +def get_base_info_for_selector(selection_dict, selector_type): + """ + Sets up the basic dictionary for data filters to be added on the data + :param selection_dict: + :param selector_type: + :return: + """ + base_info_dict_for_selector = { + OPTION_TYPE: selector_type, + COLUMN_NAME: selection_dict.get(COLUMN_NAME, ""), + } + return base_info_dict_for_selector + + +def make_filter_dict(selector_type, select_dict, index, selector_entries=None): + """ + Reformats convenient Python data structures into a dict easily parsed in Jinja + + See tests/test_controller test cases for example formats of the inputs and outputs of this function + + :param selector_type: one of AXIS, FILTER, GROUPBY, NUMERICAL_FILTER (see the explicit iteration in create_data_subselect_info_for_plot) + :param select_dict: + :param index: form index corresponds to ordering of selectors on the web interface + :param selector_entries: A list of options to include in UI dropdown + :return: dict structured for read by Jinja + """ + html_filter_dict = {SELECTOR_TYPE: selector_type} + selector_attributes = AVAILABLE_SELECTORS[selector_type] + column = select_dict.get(OPTION_COL, "") + html_filter_dict[JINJA_SELECT_HTML_FILE] = selector_attributes[SELECT_HTML_TEMPLATE] + html_filter_dict[SELECTOR_NAME] = selector_attributes[SELECTOR_NAME].format(index) + html_filter_dict[TEXT] = selector_attributes[TEXT].format(column) + html_filter_dict[ACTIVE_SELECTORS] = select_dict[ACTIVE_SELECTORS] + html_filter_dict[TYPE] = select_dict.get(TYPE, "") + html_filter_dict[MULTIPLE] = select_dict.get(MULTIPLE, False) + html_filter_dict[ENTRIES] = selector_entries + return html_filter_dict diff --git a/escalation/graphics/graphic_schema.py b/escalation/graphics/graphic_schema.py new file mode 100644 index 0000000..30c8d9a --- /dev/null +++ b/escalation/graphics/graphic_schema.py @@ -0,0 +1,484 @@ +# Copyright [2021] [Two Six Labs, LLC], +# Licensed under the Apache License, Version 2.0, + +from abc import ABC, abstractmethod +import itertools + +from flask import current_app +from sqlalchemy import Integer, Float, DateTime + +from utility.constants import * + +KEY_FILTER = "key_filter" + + +def build_data_sources_schema(data_source_names, possible_column_names, hidden): + data_sources_schema = { + "type": "object", + TITLE: "Data Sources", + "description": "Define which data tables are used in this graphic," + " and on which columns the data tables are joined", + "required": [MAIN_DATA_SOURCE], + OPTIONS: {DISABLE_COLLAPSE: True, DISABLE_PROPERTIES: True, HIDDEN: hidden}, + PROPERTIES: { + MAIN_DATA_SOURCE: { + TITLE: "Main Data Source", + "id": MAIN_DATA_SOURCE, + "type": "object", + "additionalProperties": False, + REQUIRED: [DATA_SOURCE_TYPE], + PROPERTIES: { + DATA_SOURCE_TYPE: { + "type": "string", + TITLE: "Data Source Type", + "enum": data_source_names, + }, + }, + OPTIONS: {DISABLE_COLLAPSE: True, DISABLE_PROPERTIES: True}, + }, + ADDITIONAL_DATA_SOURCES: { + "type": "array", + TITLE: "Additional Data Sources", + ITEMS: { + "type": "object", + TITLE: "Additional Data Source", + "additionalProperties": False, + "id": ADDITIONAL_DATA_SOURCES, + REQUIRED: [DATA_SOURCE_TYPE, JOIN_KEYS], + OPTIONS: {DISABLE_COLLAPSE: True, DISABLE_PROPERTIES: True}, + PROPERTIES: { + DATA_SOURCE_TYPE: { + "type": "string", + TITLE: "Data Source Type", + "enum": data_source_names, + }, + # Tuple validation (json-schema.org/understanding-json-schema/reference/array.html?highlight=default#tuple-validation) + # may be faster on the browser side and is the 'correct' way to use JSON schema + JOIN_KEYS: { + "type": "array", + TITLE: "Join Keys", + "description": "Column names along which to join the tables" + " (in the case of 2 or more tables)", + ITEMS: { + TITLE: "Pairs of Keys", + DESCRIPTION: "First key from a previous data source" + " and second key from the data source being added", + "type": "array", + "uniqueItems": True, + MIN_ITEMS: 2, + MAX_ITEMS: 2, + "items": { + "type": "string", + TITLE: "Key", + "watch": { + MAIN_DATA_SOURCE: ".".join( + [MAIN_DATA_SOURCE, DATA_SOURCE_TYPE] + ), + ADDITIONAL_DATA_SOURCES: ".".join( + [ADDITIONAL_DATA_SOURCES, DATA_SOURCE_TYPE] + ), + }, + "enumSource": [ + { + "source": possible_column_names, + "filter": KEY_FILTER, + "title": CALLBACK, + "value": CALLBACK, + } + ], + }, + }, + }, + }, + }, + }, + }, + } + return data_sources_schema + + +def get_data_sources(): + data_inventory_class = current_app.config.data_backend_writer + data_sources = data_inventory_class.get_available_data_sources() + return data_sources + + +def get_data_sources_and_column_names(): + """ + used for populating the data source schema in the wizard landing page + :return: + """ + data_sources = get_data_sources() + possible_column_names, _, _ = get_possible_column_names_and_values( + data_sources, get_types=False, get_unique_values=False + ) + return data_sources, possible_column_names + + +def get_possible_column_names_and_values( + data_sources, get_types=True, get_unique_values=True +): + """ + Used to populate a dropdown in the config wizard with any column from the data + sources included in a figure, unique_entries used to populate default selected. + :param data_source_names: list of data source name strings + :param get_types: if true calculates the type of each column + :param get_unique_values: if true calculates unique values for each column + """ + possible_column_names = [] + unique_entries = {} + column_types_dict = {} + data_handler_class = current_app.config.data_handler + for data_source_name in data_sources: + data_inventory = data_handler_class( + data_sources={MAIN_DATA_SOURCE: {DATA_SOURCE_TYPE: data_source_name}} + ) + column_names_for_data_source = data_inventory.get_column_names_for_data_source() + + possible_column_names.extend(column_names_for_data_source) + if get_types: + column_types_dict_for_data_source = ( + data_inventory.get_schema_for_data_source() + ) + column_types_dict.update(column_types_dict_for_data_source) + if get_unique_values: + unique_entries_for_data_source = data_inventory.get_column_unique_entries( + column_names_for_data_source + ) + unique_entries.update(unique_entries_for_data_source) + return possible_column_names, unique_entries, column_types_dict + + +class GraphicsConfigInterfaceBuilder(ABC): + def __init__(self, collapse_dict=None, active_data_source_names=None): + if active_data_source_names is None: + self.active_data_source_names = [] + else: + self.active_data_source_names = active_data_source_names + + # set by get_data_source_info + self.data_source_names = None + # set by divide_columns_into_type_of_filters called by get_data_source_info + self.filter_column_names = [] + self.numerical_filter_column_names = [] + # set by get_possible_column_names_and_values called by get_data_source_info + # todo: rename as data_source_column_names? + self.possible_column_names = None + self.column_types_dict = None + self.unique_entries_dict = None + self.unique_entry_values_list = None + + # todo set defaultdict with true? + self.collapse_dict = collapse_dict + + # defined in subclass. Todo: require it be defined + self.plot_selector_dict = None + + self.get_data_source_info() + + def build_header_schema(self): + schema = { + "$schema": "http://json-schema.org/draft/2019-09/schema#", + "type": "object", + "title": "Escalation Graphic Config Generator", + "description": "This form configures a single graphic", + "required": [ + PLOT_MANAGER, + GRAPHIC_TITLE, + GRAPHIC_DESC, + DATA_SOURCES, + PLOT_SPECIFIC_INFO, + ], + "additionalProperties": False, + OPTIONS: {DISABLE_COLLAPSE: True, DISABLE_PROPERTIES: True}, + PROPERTIES: { + PLOT_MANAGER: { + "type": "string", + TITLE: "Plot Backend", + "description": "plot library you would like to use", + "enum": PLOT_MANAGERS, + OPTIONS: {HIDDEN: True,}, + }, + PLOT_TYPE: { + "type": "string", + TITLE: "Plot Type", + "description": "Which plot manager schema does PLOT_SPECIFIC_INFO follow", + OPTIONS: {HIDDEN: True,}, + }, + GRAPHIC_TITLE: { + "type": "string", + TITLE: "Graph Title", + DEFAULT: "", + PATTERN: NON_EMPTY_STRING, + }, + GRAPHIC_DESC: { + "type": "string", + TITLE: "Graph Description", + "description": "Text caption shown above the graph (optional)", + }, + PLOT_SPECIFIC_INFO: { + "type": "object", + "title": "Plot Dictionary", + "description": "this dictionary depends on the graphing library", + OPTIONS: { + DISABLE_COLLAPSE: True, + DISABLE_PROPERTIES: True, + HIDDEN: True, + }, + }, + }, + } + + schema[PROPERTIES][DATA_SOURCES] = build_data_sources_schema( + self.data_source_names, self.possible_column_names, True + ) + return schema + + @staticmethod + def get_wizard_data_source_schema(): + data_sources, possible_column_names = get_data_sources_and_column_names() + return build_data_sources_schema(data_sources, possible_column_names, False) + + def build_data_filter_schema(self): + selectable_data_schema = { + "type": "object", + "title": "Data Selector Options", + "description": "Interactive data selectors: filter data by values, change axes," + " change columns to group by", + ADDITIONAL_PROPERTIES: False, + OPTIONS: { + COLLAPSED: self.collapse_dict[SELECTABLE_DATA_DICT], + REMOVE_EMPTY_PROPERTIES: True, + }, + PROPERTIES: { + FILTER: { + "type": "array", + "title": "List of Filters", + DESCRIPTION: "a filter operation based on label", + OPTIONS: {COLLAPSED: self.collapse_dict[FILTER]}, + "items": { + "type": "object", + TITLE: "Filter", + "id": "filter_item", + "required": [COLUMN_NAME], + "additionalProperties": False, + OPTIONS: {DISABLE_COLLAPSE: True}, + PROPERTIES: { + VISIBLE: { + TYPE: "boolean", + DEFAULT: True, + DESCRIPTION: "Determines whether or not the filter dropdown is visible to the user.", + }, + COLUMN_NAME: { + "type": "string", + TITLE: "Column Name", + "description": "any data column with fewer than 200 unique entries can be filtered" + " by identity matching and is listed here", + "enum": self.filter_column_names, + }, + "multiple": { + "type": "boolean", + TITLE: "Allow Multiple Selections", + DESCRIPTION: "Allow multiple values to be selected in this filter", + }, + DEFAULT_SELECTED: { + "type": "array", + TITLE: "Default Selected", + "description": "Optional, Default value(s) selected in this filter," + " a list of values to include", + "items": { + "type": "string", + # watch will call the functions in enumSource (default_selected_filter, + # identity_callback; defined in the JS) whenever COLUMN_NAME is changed, + # it will also store the value of COLUMN_NAME to a variable called COLUMN_NAME + # to be used by the JS + "watch": { + COLUMN_NAME: ".".join( + ["filter_item", COLUMN_NAME] + ) + }, + "enumSource": [ + { + "source": self.unique_entry_values_list, + "filter": COLUMN_VALUE_FILTER, + "title": CALLBACK, + "value": CALLBACK, + } + ], + }, + }, + FILTERED_SELECTOR: { + "type": "boolean", + TITLE: "Should Selector Be Filtered", + DESCRIPTION: "If selector is filtered, the user can only select values in this" + " field that are present in the data subsetted by the" + " currently-applied filters", + DEFAULT: False, + }, + }, + }, + }, + NUMERICAL_FILTER: { + "type": "array", + "title": "List of Numerical Filters", + DESCRIPTION: "a filter operation on numerical data", + OPTIONS: {COLLAPSED: self.collapse_dict[NUMERICAL_FILTER]}, + "items": { + TITLE: "Numerical Filters", + "type": "object", + "required": [COLUMN_NAME, TYPE], + OPTIONS: {DISABLE_COLLAPSE: True}, + "additionalProperties": False, + PROPERTIES: { + VISIBLE: { + TYPE: "boolean", + DEFAULT: True, + DESCRIPTION: "Determines whether or not the numerical filter dropdown" + " is visible to the user.", + }, + COLUMN_NAME: { + "type": "string", + TITLE: "Column Name", + "description": "name in table", + "enum": self.numerical_filter_column_names, + }, + TYPE: { + TYPE: "string", + DESCRIPTION: "number or datetime", + DEFAULT: "number", + ENUM: ["number", DATETIME], + }, + MAX: { + TITLE: "Default Maximum", + "description": "Optional, set null for no max", + ONEOF: [ + {"type": "null"}, + {"type": "number"}, + {"type": "string", "format": "datetime-local"}, + ], + }, + MIN: { + TITLE: "Default Minimum", + "description": "Optional, set null for no min", + ONEOF: [ + {"type": "null"}, + {"type": "number"}, + {"type": "string", "format": "datetime-local"}, + ], + }, + }, + }, + }, + }, + } + return selectable_data_schema + + @abstractmethod + def build_individual_plot_type_schema(self, plot_type): + # must be implemented in subclass for graphic library + raise NotImplementedError + + @staticmethod + @abstractmethod + def get_available_plots(): + """ + :return:An array of dictionaries with two keys: + Name: shown name to the user + Value: name of the schema, input to build_individual_plot_type_schema + """ + raise NotImplementedError + + def sort_data(self): + for info_list in [ + self.data_source_names, + self.possible_column_names, + self.filter_column_names, + self.numerical_filter_column_names, + self.unique_entry_values_list, + ]: + if info_list: + info_list.sort() + + def get_possible_column_names_and_values(self, get_unique_values=True): + """ + Used to populate a dropdown in the config wizard with any column from the data + sources included in a figure, unique_entries used to populate default selected. + :param data_source_names: list of data source name strings + :param data_handler_class: backend-specific data inventory class + :param get_unique_values: if true calculates unique values for each column + """ + ( + possible_column_names, + unique_entries, + column_types_dict, + ) = get_possible_column_names_and_values( + self.active_data_source_names, get_unique_values=get_unique_values + ) + self.unique_entries_dict = unique_entries + self.possible_column_names = possible_column_names + self.column_types_dict = column_types_dict + self.unique_entry_values_list = self.get_unique_entries_as_single_list() + + def get_data_source_info(self): + """ + gets the available data sources and the possible column names based on the data source in the config + :return: + """ + self.data_source_names = get_data_sources() + self.active_data_source_names = [ + data_source_name + for data_source_name in self.active_data_source_names + if data_source_name in self.data_source_names + ] + if self.data_source_names and not self.active_data_source_names: + # default to the first in alphabetical order + self.active_data_source_names = [min(self.data_source_names)] + + self.get_possible_column_names_and_values() + self.divide_columns_into_type_of_filters() + + def divide_columns_into_type_of_filters(self): + """ + We only allow numerical data types to be filtered with a numerical inequality filter, + and any data column with fewer than MAX_ENTRIES_FOR_FILTER_SELECTOR unique entries + to be filtered by identity matching + """ + numerical_types = [Integer, Float, DateTime] + + unique_entries_dict_copy = {} + for col_name, list_of_entries in self.unique_entries_dict.items(): + if any( + [ + isinstance(self.column_types_dict[col_name], num_type) + for num_type in numerical_types + ] + ): + self.numerical_filter_column_names.append(col_name) + if len(list_of_entries) <= MAX_ENTRIES_FOR_FILTER_SELECTOR: + self.filter_column_names.append(col_name) + unique_entries_dict_copy[col_name] = list_of_entries + self.unique_entries_dict = unique_entries_dict_copy + + def build_graphic_schemas_for_ui(self, plot_type): + """ + Builds 4 separate schemas to be used by the UI: + GRAPHIC_SCHEMA contains information specific to the plot as configured + PLOT_MANAGER_SCHEMA contains the overall schema from the plot_manager- e.g., Plotly's scatterplot schema + VISUALIZATION_SCHEMA contains the VISUALIZATION_OPTIONS dict describing graph-specific configured visualization choices + SELECTOR_SCHEMA has different plot types available + :return: a dict read by the wizard HTML jinja template + """ + self.sort_data() + ui_configurer_schema = { + GRAPHIC_SCHEMA: self.build_header_schema(), + PLOT_MANAGER_SCHEMA: self.build_individual_plot_type_schema(plot_type), + SELECTOR_SCHEMA: self.build_data_filter_schema(), + } + return ui_configurer_schema + + def get_unique_entries_as_single_list(self): + # concatenating into one large list with no duplicates + unique_entries_list = list( + set(itertools.chain.from_iterable(self.unique_entries_dict.values())) + ) + return unique_entries_list diff --git a/escalation/graphics/plotly_plot.py b/escalation/graphics/plotly_plot.py index 3c025ba..74281d2 100644 --- a/escalation/graphics/plotly_plot.py +++ b/escalation/graphics/plotly_plot.py @@ -4,30 +4,41 @@ import json import plotly from flask import render_template -from graphics.graphic_class import Graphic +from graphics.graphic_class import Graphic, get_key_for_form, make_filter_dict from utility.constants import ( - OPTION_COL, GROUPBY, - AGGREGATE, - HOVER_DATA, - AGGREGATIONS, SCATTER, SCATTERGL, TYPE, + DATA, + LAYOUT, + ARRAY_STRING, + TRANSFORMS, + GROUPS, + HOVERTEXT, + AGGREGATE, + NO_GROUP_BY, + ACTIVE_SELECTORS, + DEFAULT_SELECTED, + PLOT_SPECIFIC_INFO, + X, + Y, + Z, + ERROR_X, + ERROR_Y, + ERROR_Z, + SELECTABLE_DATA_DICT, + ENTRIES, ) HOVER_TEMPLATE_HTML = "hover_template.html" -DATA = "data" -LAYOUT = "layout" PLOT_AXIS = "{}axis" TITLE = "title" CUSTOM_DATA = "customdata" HOVER_TEMPLATE = "hovertemplate" VISUALIZATION_TYPE = "type" PLOTLY_TYPE = "type" -TRANSFORMS = "transforms" -GROUPS = "groups" OPTIONS = "options" STYLES = "styles" PLOT_OPTIONS = "plot_options" @@ -42,16 +53,9 @@ AUTOMARGIN = "automargin" HOVERMODE = "hovermode" CLOSEST = "closest" -X = "x" -Y = "y" -Z = "z" -ERROR_X = "error_x" -ERROR_Y = "error_y" -ERROR_Z = "error_z" POSSIBLE_AXIS = [X, Y, Z] POSSIBLE_ERROR_AXES = [ERROR_X, ERROR_Y, ERROR_Z] -ARRAY = "array" CONFIG = "config" MODE_BAR_REMOVE = "modeBarButtonsToRemove" @@ -62,84 +66,6 @@ BUTTONS = "buttons" -def get_hover_data_in_plotly_form( - data, hover_options, visualization_type, plot_options_data_dict -): - """ - - :param data: - :param hover_options: - :param plot_options_data_dict: - :return: - """ - # if data is a dataframe: plot_options[DATA][index]["customdata"] = data[hover_data].values.tolist() - # is equivalent to the two lines function - hover_column_names = hover_options[OPTION_COL] - # hover_data_list = [data[hover_col_name] for hover_col_name in hover_column_names] - # transposes a list of lists of column data to a list of lists of row data - plot_options_data_dict[CUSTOM_DATA] = ( - data[hover_column_names].astype(str).values.tolist() - ) - - plot_options_data_dict[HOVER_TEMPLATE] = render_template( - HOVER_TEMPLATE_HTML, hover_column_names=hover_column_names - ) - return plot_options_data_dict - - -def get_groupby_or_aggregate_in_plotly_form( - data, visualization_property, visualization_type, plot_options_data_dict -): - """ - aggregate allows you to do a function on an aggregation of the data - group_by allows you to change the color based on one of the columns - if group_by has options we only allow the key styles - we expect a dictionary of styles g.e. col_name: {marker: {color: blue}} - :param data: - :param visualization_property: a dictionary that includes the type (groupby or aggregate), column name (stored in a list of length one) - and options such as color for groupby and function for aggregate - :param plot_options_data_dict: - :return: - """ - group_labels = [ - ", ".join(data_list) - for data_list in data[visualization_property[OPTION_COL]] - .astype(str) - .values.tolist() - ] - property_dict = { - VISUALIZATION_TYPE: visualization_type, - GROUPS: group_labels, - } - - if visualization_type == GROUPBY and STYLES in visualization_property: - style_dict = visualization_property[STYLES] - plotly_style_list = [ - {"target": col_name, "value": style} - for col_name, style in style_dict.items() - ] - property_dict[STYLES] = plotly_style_list - elif visualization_type == AGGREGATE: - # attribute_name can be x, y or something like marker.size - # func can be avg, min, sum, count, stddev etc. - attribute_dict = visualization_property[AGGREGATIONS] - plotly_aggregations_list = [ - {"target": attribute_name, "func": func} - for attribute_name, func in attribute_dict.items() - ] - property_dict[AGGREGATIONS] = plotly_aggregations_list - - plot_options_data_dict[TRANSFORMS].append(property_dict) - return plot_options_data_dict - - -VISUALIZATION_OPTIONS = { - HOVER_DATA: get_hover_data_in_plotly_form, - GROUPBY: get_groupby_or_aggregate_in_plotly_form, - AGGREGATE: get_groupby_or_aggregate_in_plotly_form, -} - - def does_data_need_to_be_sorted(plot_info_data_dict: dict): """ Determines whether the data needs to be sorted if so does it inplace @@ -218,29 +144,31 @@ def add_toggle_layout_button(layout_dict: dict): class PlotlyPlot(Graphic): - @staticmethod - def make_dict_for_html_plot(data, plot_options, visualization_options=None): + def make_dict_for_html_plot(self): """ Makes the json file that plotly takes in :param data: a pandas dataframe - :param axis_to_data_columns: :param plot_options: :param visualization_options: :return: """ + plot_options = self.graphic_dict[PLOT_SPECIFIC_INFO] # todo: cut off all text data to used in group by or titles to 47 charaters data_sorted = False plot_options[CONFIG] = add_config_defaults(plot_options.get(CONFIG, {})) # only have a legend if there is more than one plot by having a group by. # Future: if we support having more than graphic plotted need- # or len(plot_options[DATA]) > 1 with some other logic. - if visualization_options and GROUPBY in visualization_options: - plot_options[LAYOUT] = add_toggle_layout_button( - plot_options.get(LAYOUT, {}) - ) + for index, plotly_data_dict in enumerate(plot_options[DATA]): + if GROUPBY in plotly_data_dict.get(TRANSFORMS, {}): + plot_options[LAYOUT] = add_toggle_layout_button( + plot_options.get(LAYOUT, {}) + ) if not data_sorted and does_data_need_to_be_sorted(plotly_data_dict): - data.sort_values(plotly_data_dict[AXIS_TO_SORT_ALONG], inplace=True) + self.data.sort_values( + plotly_data_dict[AXIS_TO_SORT_ALONG], inplace=True + ) data_sorted = True for axis in POSSIBLE_AXIS: if axis in plotly_data_dict: @@ -255,45 +183,157 @@ def make_dict_for_html_plot(data, plot_options, visualization_options=None): ) # moving the contents of the data to where plotly expects it # from the output of the database - plotly_data_dict[axis] = data[plotly_data_dict[axis]] + plotly_data_dict[axis] = self.data[plotly_data_dict[axis]] for error_axis in POSSIBLE_ERROR_AXES: if error_axis in plotly_data_dict: - plotly_data_dict[error_axis][ARRAY] = data[ - plotly_data_dict[error_axis][ARRAY] + plotly_data_dict[error_axis][ARRAY_STRING] = self.data[ + plotly_data_dict[error_axis][ARRAY_STRING] ] - plotly_data_dict[TRANSFORMS] = [] - - if visualization_options is not None: - for ( - visualization_type, - visualization_parameters, - ) in visualization_options.items(): - plotly_data_dict = VISUALIZATION_OPTIONS[visualization_type]( - data, - visualization_parameters, - visualization_type, - plotly_data_dict, - ) + if TRANSFORMS in plotly_data_dict: + plotly_data_dict[TRANSFORMS] = self.put_data_in_transforms( + plotly_data_dict[TRANSFORMS] + ) + if HOVERTEXT in plotly_data_dict: + plotly_data_dict = self.get_hover_data_in_plotly_form(plotly_data_dict) - graph_json = json.dumps(plot_options, cls=plotly.utils.PlotlyJSONEncoder) - return graph_json + self.graph_json_str = json.dumps( + plot_options, cls=plotly.utils.PlotlyJSONEncoder + ) + + def get_hover_data_in_plotly_form(self, plotly_data_dict): + hover_column_names = plotly_data_dict.pop(HOVERTEXT, []) + # transposes a list of lists of column data to a list of lists of row data + plotly_data_dict[CUSTOM_DATA] = list( + map(list, zip(*[self.data[col_name] for col_name in hover_column_names]),) + ) + + plotly_data_dict[HOVER_TEMPLATE] = render_template( + HOVER_TEMPLATE_HTML, hover_column_names=hover_column_names + ) + return plotly_data_dict + + def put_data_in_transforms(self, transform_dict): + """ + puts the data into the transform dictionary in the form 1st elem, 2 elem, ... + :param transform_list: + :return: + """ + + def concatenate_columns(transform_dict): + transform_dict[GROUPS] = [ + ", ".join(map(str, data_list)) + for data_list in zip( + *[self.data[col_name] for col_name in transform_dict[GROUPS]] + ) + ] + return transform_dict + + transform_list = [] + for transform_type, transform in transform_dict.items(): + if transform_type == GROUPBY: + transform = concatenate_columns(transform) + transform[TYPE] = GROUPBY + transform_list.append(transform) + elif transform_type == AGGREGATE: + for aggregate_dict in transform: + aggregate_dict = concatenate_columns(aggregate_dict) + aggregate_dict[TYPE] = AGGREGATE + transform_list.append(aggregate_dict) + + return transform_list @staticmethod - def get_data_columns(plot_options) -> set: + def get_graph_html_template() -> str: + return "plotly.html" + + def get_data_columns(self) -> set: """ - extracts what columns of data are needed from the plot_options + extracts what columns of data are needed from the self.graphic_dict[PLOT_SPECIFIC_INFO] :param plot_options: :return: """ + plot_options = self.graphic_dict[PLOT_SPECIFIC_INFO] set_of_column_names = set() - for dict_of_data_for_each_plot in plot_options[DATA]: + for data_dict_per_plot in plot_options[DATA]: for axis in POSSIBLE_AXIS: - if axis in dict_of_data_for_each_plot: - set_of_column_names.add(dict_of_data_for_each_plot[axis]) + if axis in data_dict_per_plot: + set_of_column_names.add(data_dict_per_plot[axis]) for error_axis in POSSIBLE_ERROR_AXES: - if error_axis in dict_of_data_for_each_plot: + if error_axis in data_dict_per_plot: # this is error specified in data set_of_column_names.add( - dict_of_data_for_each_plot[error_axis][ARRAY] + data_dict_per_plot[error_axis][ARRAY_STRING] ) + + for transform_type, transform in data_dict_per_plot.get( + TRANSFORMS, {} + ).items(): + if transform_type == GROUPBY: + set_of_column_names.update( + [column_name for column_name in transform.get(GROUPS, [])] + ) + elif transform_type == AGGREGATE: + for transform_dict in transform: + set_of_column_names.update( + [ + column_name + for column_name in transform_dict.get(GROUPS, []) + ] + ) + + set_of_column_names.update( + [column_name for column_name in data_dict_per_plot.get(HOVERTEXT, [])] + ) + return set_of_column_names + + def add_active_selectors_to_selectable_data_list(self): + + super().add_active_selectors_to_selectable_data_list() + + selectable_data_dict = self.graphic_dict[SELECTABLE_DATA_DICT] + if GROUPBY in selectable_data_dict: + group_by_dict = selectable_data_dict[GROUPBY] + selected_group_by = self.addendum_dict.get( + get_key_for_form(GROUPBY, ""), [] + ) + if NO_GROUP_BY in selected_group_by: + group_by_dict[ACTIVE_SELECTORS] = [NO_GROUP_BY] + else: + group_by_dict[ACTIVE_SELECTORS] = ( + selected_group_by + or group_by_dict.get(DEFAULT_SELECTED, [NO_GROUP_BY]) + ) + + def modify_graphic_dict_based_on_addendum_dict(self): + selectable_data_dict = self.graphic_dict[SELECTABLE_DATA_DICT] + if GROUPBY in selectable_data_dict: + selection = self.addendum_dict.get( + get_key_for_form(GROUPBY, "") + ) or selectable_data_dict[GROUPBY].get(DEFAULT_SELECTED, [NO_GROUP_BY]) + if NO_GROUP_BY not in selection: + # put selection in groups in the groupby dict without changing anythong else + transform_dict = self.graphic_dict[PLOT_SPECIFIC_INFO][DATA][0].get( + TRANSFORMS, {} + ) + groupby_dict = transform_dict.get(GROUPBY, {}) + groupby_dict[GROUPS] = selection + transform_dict[GROUPBY] = groupby_dict + self.graphic_dict[PLOT_SPECIFIC_INFO][DATA][0][ + TRANSFORMS + ] = transform_dict + + def create_data_subselect_info_for_plot(self): + + selectable_data_dict = self.graphic_dict.get(SELECTABLE_DATA_DICT, {}) + if GROUPBY in selectable_data_dict: + group_by_dict = selectable_data_dict[GROUPBY] + selector_entries = group_by_dict[ENTRIES] + selector_entries.sort() + # append no_group_by to the front of the list + selector_entries.insert(0, NO_GROUP_BY) + self.select_info.append( + make_filter_dict(GROUPBY, group_by_dict, "", selector_entries) + ) + + super().create_data_subselect_info_for_plot() diff --git a/escalation/graphics/seaborn_plot.py b/escalation/graphics/seaborn_plot.py new file mode 100644 index 0000000..20881b1 --- /dev/null +++ b/escalation/graphics/seaborn_plot.py @@ -0,0 +1,164 @@ +# Copyright [2020] [Two Six Labs, LLC] +# Licensed under the Apache License, Version 2.0 +""" +Note, avoiding Pyplot here because of this: +https://matplotlib.org/devdocs/gallery/user_interfaces/web_application_server_sgskip.html +'When using Matplotlib in a web server it is strongly recommended to not use pyplot (pyplot maintains references to the opened figures to make show work, but this will cause memory leaks unless the figures are properly closed).' + +""" + +import base64 +import io +import json + +from graphics.graphic_class import Graphic + +from utility.constants import * + +from matplotlib.backends.backend_agg import FigureCanvasAgg +from matplotlib.figure import Figure +import pandas as pd +import seaborn as sns + +X = "x" +Y = "y" +Z = "z" +ERROR_X = "error_x" +ERROR_Y = "error_y" +ERROR_Z = "error_z" +POSSIBLE_AXIS = [X, Y, Z] +POSSIBLE_ERROR_AXES = [ERROR_X, ERROR_Y, ERROR_Z] + + +NROWS = "nrows" +NCOLS = "ncols" +SHAREX = "sharex" +SHAREY = "sharey" + +DEFAULT_LAYOUT_OPTIONS = { + NROWS: 1, + NCOLS: 1, + FIGSIZE: [8, 6], + SHAREX: False, + SHAREY: False, +} +# seaborn has a number of plot attributes that can be defined using a data column +# todo: how do we find these exhaustively? +PLOT_DATA_DEFINED_ATTRIBUTES = [ + "hue", + "size", + "style", + "units", + "weights", + "row", + "col", +] + + +# todo: Some args take list of column names, which we need to account for +# https://seaborn.pydata.org/generated/seaborn.pairplot.html?highlight=keys%20data +lists_of_keys = ["vars", "x_vars", "y_vars"] + + +# Several of the seaborn commands create their own figure automatically. +# This is hardcoded into the seaborn code, so there is currently no way to produce such plots into existing figure objects. +forbidden_plots = [ + "PairGrid", + "FacetGrid", + "JointGrid", + "pairplot", + "jointplot", + "lmplot", +] + + +def create_seaborn_fig_from_data_and_definition(data, plot_options): + # plot_options[LAYOUT] define figure level formatting: + # figure size, number and arrangement of subplots, background, spines, axlabels + # get user defined values, and update with default values + layout_dict = plot_options.get(LAYOUT, {}) + for viz_key, default_value in DEFAULT_LAYOUT_OPTIONS.items(): + if viz_key not in layout_dict: + layout_dict[viz_key] = default_value + plot_options[LAYOUT] = layout_dict + + nrows = layout_dict[NROWS] + ncols = layout_dict[NCOLS] + fig = Figure(figsize=layout_dict[FIGSIZE]) + for ind in range(1, nrows * ncols + 1): + fig.add_subplot(nrows, ncols, ind) + + fig.set_size_inches(layout_dict[FIGSIZE]) + for plot_ind, plot_definition in enumerate(plot_options["data"]): + ax = fig.axes[plot_ind] + # get the seaborn plotting function from the seaborn library based on the name + plot_type = plot_definition.pop(TYPE) + if plot_type in forbidden_plots: + raise ValueError(f"{plot_type} not supported yet") + plot_function = getattr(sns, plot_type) + # separate out fig/ax parameters from seaborn function parameters + g = plot_function(data=data, ax=ax, **plot_definition) + # ax.legend(bbox_to_anchor=(1.1, 0.9), ncol=1) + # g.legend(loc='center left', bbox_to_anchor=(1.1, 0.9), ncol=1) + + return fig + + +def get_canvas_for_rendering_in_html(fig): + output = io.BytesIO() + FigureCanvasAgg(fig).print_png(output) + fig.savefig(output, format="png") + output.seek(0) + buffer = b"".join(output) + buffer_64_encoded = base64.b64encode(buffer) + + seaborn_fig_bytes = buffer_64_encoded.decode("utf-8") + return seaborn_fig_bytes + + +class SeabornPlot(Graphic): + def make_dict_for_html_plot(self): + fig = create_seaborn_fig_from_data_and_definition( + self.data, self.graphic_dict[PLOT_SPECIFIC_INFO] + ) + seaborn_fig_bytes = get_canvas_for_rendering_in_html(fig) + fig_size = self.graphic_dict[PLOT_SPECIFIC_INFO][LAYOUT][FIGSIZE] + # ASPECT_RATIO makes sure that the image on the webpage has the same aspect ratio as the seaborn figure + self.graph_json_str = json.dumps( + { + SEABORN_PLOT_BYTES: seaborn_fig_bytes, + ASPECT_RATIO: fig_size[0] / fig_size[1], + } + ) + + @staticmethod + def get_graph_html_template() -> str: + return "seaborn.html" + + def get_data_columns(self) -> set: + """ + extracts what columns of data are needed from the plot_options + :param plot_options: + :return: + """ + plot_options = self.graphic_dict[PLOT_SPECIFIC_INFO] + set_of_column_names = set() + for dict_of_data_for_each_plot in plot_options[DATA]: + for axis in POSSIBLE_AXIS: + if axis in dict_of_data_for_each_plot: + set_of_column_names.add(dict_of_data_for_each_plot[axis]) + for error_axis in POSSIBLE_ERROR_AXES: + if error_axis in dict_of_data_for_each_plot: + # this is error specified in data + set_of_column_names.add( + dict_of_data_for_each_plot[error_axis][ARRAY_STRING] + ) + + for plot_attribute in PLOT_DATA_DEFINED_ATTRIBUTES: + # column names are a string. Check for this to make sure that + # the attribute in plot_data_defined_attributes is defined using a data + # key and not a vector of values + if isinstance(dict_of_data_for_each_plot.get(plot_attribute), str): + set_of_column_names.add(dict_of_data_for_each_plot[plot_attribute]) + + return set_of_column_names diff --git a/escalation/graphics/utils/available_graphics.py b/escalation/graphics/utils/available_graphics.py index 227c301..07ae316 100644 --- a/escalation/graphics/utils/available_graphics.py +++ b/escalation/graphics/utils/available_graphics.py @@ -1,17 +1,41 @@ # Copyright [2020] [Two Six Labs, LLC] # Licensed under the Apache License, Version 2.0 - +from graphics.build_cytoscape_schema import CytoscapeGraphicSchema +from graphics.build_plotly_schema import PlotlyGraphicSchema +from graphics.build_seaborn_schema import SeabornGraphicSchema +from graphics.cytoscape import Cytoscape from graphics.plotly_plot import PlotlyPlot +from graphics.seaborn_plot import SeabornPlot from graphics.bootstrap_table import BootstrapTable +from utility.constants import ( + GRAPH_HTML_TEMPLATE, + OBJECT, + SCHEMA_CLASS, +) """ List of the available graphics """ AVAILABLE_GRAPHICS = { - "plotly": {"object": PlotlyPlot, "graph_html_template": "plotly.html"}, - "bootstrap-table": { - "object": BootstrapTable, - "graph_html_template": "bootstrap-table.html", - }, + "plotly": {OBJECT: PlotlyPlot, SCHEMA_CLASS: PlotlyGraphicSchema,}, + "cytoscape": {OBJECT: Cytoscape, SCHEMA_CLASS: CytoscapeGraphicSchema,}, + # "bootstrap-table": { + # "object": BootstrapTable, + # GRAPH_HTML_TEMPLATE: "bootstrap-table.html", + # }, + "seaborn": {OBJECT: SeabornPlot, SCHEMA_CLASS: SeabornGraphicSchema,}, } + +PLOT_DELIMITER = ";" + + +# VALUE is what our backend expects - DO NOT use PLOT_DELIMITER in VALUE +# GRAPHIC_NAME is the option shown to the user + + +def get_all_available_graphics(): + all_graphics_dict = {} + for plot_manager, plot_dict in AVAILABLE_GRAPHICS.items(): + all_graphics_dict[plot_manager] = plot_dict[SCHEMA_CLASS].get_available_plots() + return all_graphics_dict diff --git a/escalation/import_plotly_schema.py b/escalation/import_plotly_schema.py new file mode 100644 index 0000000..7ed2c03 --- /dev/null +++ b/escalation/import_plotly_schema.py @@ -0,0 +1,224 @@ +import json +from itertools import product + +import requests + +from utility.constants import ( + ENUM, + TYPE, + DESCRIPTION, + DEFAULT, + MINIMUM, + MAXIMUM, + PROPERTIES, + SCATTER, + BAR, + HEATMAP, + CONTOUR, + BOX, + VIOLIN, + HISTOGRAM, + HISTOGRAM2D, + SCATTER3D, + MESH3D, + LAYOUT, + OBJECT, + ARRAY_STRING, + ITEMS, + ATTRIBUTES, + LAYOUT_ATTRIBUTES, + TITLE, +) + +PLOTLY_TYPE = "valType" +PLOTLY_VALUES = "values" +ENUMERATED = "enumerated" +MIN = "min" +MAX = "max" +PLOTLY_DEFAULT = "dflt" +FLAGS = "flags" +EXTRAS = "extras" +ROLE = "role" + +SCHEMA = "schema" +TRACES = "traces" +SYMBOL = "symbol" + +# Build new plotly schemas with only specified graph configurations +PLOT_TYPE_LIST = [ + SCATTER, + BAR, + HEATMAP, + CONTOUR, + BOX, + VIOLIN, + HISTOGRAM, + HISTOGRAM2D, + SCATTER3D, + MESH3D, +] + + +def all_combos(flaglist: list) -> list: + """ + return all permutations of the elements in flaglist delimited by + + matches what plotly takes as input + :param flaglist: + :return: + """ + N = len(flaglist) + combos = [] + bool_values = [False, True] + for bool_list in product(bool_values, repeat=N): + if any(bool_list): + combos.append( + "+".join([val for val, in_list in zip(flaglist, bool_list) if in_list]) + ) + return combos + + +def build_dict_from_role_object(plotly_dict: dict, is_dict: bool = True) -> dict: + """ + a recursive function that takes in a plotly api dict and transforms it to json schema format + """ + # blacklist if for keys that we are not going to put into our wizard + blacklist = [ + "stream", + "transforms", + "tickformatstops", + "_deprecated", + "impliedEdits", + "r", + "t", + "hovertext", + "uid", + "ids", + "customdata", + ] + new_dict = {TYPE: OBJECT if is_dict else ARRAY_STRING} + property_dict = {} + inner_attributes = plotly_dict if is_dict else plotly_dict[ITEMS] + for key, plotly_value in inner_attributes.items(): + if ( + key not in blacklist + and (not key.endswith("src")) # gets rid of keys for "Plotly Chart Studio" + and isinstance(plotly_value, dict) + ): + if ROLE in plotly_value: + if plotly_value[ROLE] == OBJECT: + # plotly_value is a dictionary of dictionaries + # if ITEMS in plotly_value the the dictionary represents should be changed to type array in + # json schema + property_dict[key] = build_dict_from_role_object( + plotly_value, is_dict=(ITEMS not in plotly_value) + ) + else: + # Keys that point to anything that is not an object or array + # We are handling the key symbol separately + if key == SYMBOL: + property_dict[key] = symbol_dict(plotly_value) + else: + property_dict[key] = build_dict_from_plotly_one_level_dict( + plotly_value + ) + if property_dict: + new_dict[PROPERTIES if is_dict else ITEMS] = ( + property_dict if is_dict else property_dict[key] + ) + if not is_dict: + new_dict[TITLE] = key + return new_dict + + +def build_dict_from_plotly_one_level_dict(plotly_dict: dict) -> dict: + """ + Converts non nested plotly api dict and transforms it to json schema format + """ + type_dict = { + "enumerated": "string", + "boolean": "boolean", + "number": "number", + "integer": "integer", + "string": "string", + "angle": "number", + } + val_type = plotly_dict[PLOTLY_TYPE] + new_dict = {TYPE: type_dict.get(val_type, "string")} + for item, corresp_item in zip( + [DESCRIPTION, PLOTLY_DEFAULT], [DESCRIPTION, DEFAULT] + ): + if item in plotly_dict: + new_dict[corresp_item] = plotly_dict[item] + if val_type == "flaglist": + new_dict[ENUM] = all_combos(plotly_dict[FLAGS]) + plotly_dict.get(EXTRAS, []) + if val_type == "enumerated": + new_dict[ENUM] = plotly_dict[PLOTLY_VALUES] + if val_type == "integer" or val_type == "number": + for item, corresp_item in zip([MIN, MAX], [MINIMUM, MAXIMUM]): + if item in plotly_dict: + new_dict[corresp_item] = plotly_dict[item] + if val_type == "angle": + new_dict[MINIMUM] = -180 + new_dict[MAXIMUM] = 180 + return new_dict + + +def symbol_dict(plotly_dict: dict) -> dict: + """ + We do not make the 474 options for symbols seen in our wizard e.g. star-triangle-down-open-dot + :param plotly_dict: + :return: + """ + reduced_symbols = [ + "circle", + "square", + "diamond", + "cross", + "x", + "triangle-up", + "triangle-down", + "pentagon", + "hexagon", + "octagon", + "star", + ] + + new_dict = {TYPE: "string"} + for item, corresp_item in zip( + [DESCRIPTION, PLOTLY_DEFAULT], [DESCRIPTION, DEFAULT] + ): + if item in plotly_dict: + new_dict[corresp_item] = plotly_dict[item] + new_dict[ENUM] = reduced_symbols + return new_dict + + +def parse_plotly_api_into_schemas(): + # get api request from plotly and convert to type dictionary + response = requests.get("https://api.plot.ly/v2/plot-schema?sha1=%27%27").text + plotly_full = json.loads(response) + assert isinstance(plotly_full, dict) + for plot_type in PLOT_TYPE_LIST: + plotly_type_dict = plotly_full[SCHEMA][TRACES][plot_type] + json_schema = { + ATTRIBUTES: build_dict_from_role_object(plotly_type_dict[ATTRIBUTES])[ + PROPERTIES + ] + } + if LAYOUT_ATTRIBUTES in plotly_type_dict: + json_schema[LAYOUT_ATTRIBUTES] = build_dict_from_role_object( + plotly_type_dict[LAYOUT_ATTRIBUTES] + )[PROPERTIES] + # todo: move plotly_api to graphics folder + with open(f"plotly_api/{plot_type}_plotly_schema.json", "w") as outfile: + json.dump(json_schema, outfile, indent=4) + json_schema = build_dict_from_role_object( + plotly_full[SCHEMA][LAYOUT][LAYOUT_ATTRIBUTES] + ) + with open("plotly_api/layout_plotly_schema.json", "w") as outfile: + json.dump(json_schema, outfile, indent=4) + + +# todo: move this script to scripts or utilities folder +if __name__ == "__main__": + parse_plotly_api_into_schemas() diff --git a/escalation/plotly_api/bar_plotly_schema.json b/escalation/plotly_api/bar_plotly_schema.json new file mode 100644 index 0000000..bef6d2a --- /dev/null +++ b/escalation/plotly_api/bar_plotly_schema.json @@ -0,0 +1,1058 @@ +{ + "attributes": { + "visible": { + "type": "string", + "description": "Determines whether or not this trace is visible. If *legendonly*, the trace is not drawn, but can appear as a legend item (provided that the legend itself is visible).", + "default": true, + "enum": [ + true, + false, + "legendonly" + ] + }, + "showlegend": { + "type": "boolean", + "description": "Determines whether or not an item corresponding to this trace is shown in the legend.", + "default": true + }, + "legendgroup": { + "type": "string", + "description": "Sets the legend group for this trace. Traces part of the same legend group hide/show at the same time when toggling legend items.", + "default": "" + }, + "opacity": { + "type": "number", + "description": "Sets the opacity of the trace.", + "default": 1, + "minimum": 0, + "maximum": 1 + }, + "name": { + "type": "string", + "description": "Sets the trace name. The trace name appear as the legend item and on hover." + }, + "customdata": { + "type": "string", + "description": "Assigns extra data each datum. This may be useful when listening to hover, click and selection events. Note that, *scatter* traces also appends customdata items in the markers DOM elements" + }, + "meta": { + "type": "string", + "description": "Assigns extra meta information associated with this trace that can be used in various text attributes. Attributes such as trace `name`, graph, axis and colorbar `title.text`, annotation `text` `rangeselector`, `updatemenues` and `sliders` `label` text all support `meta`. To access the trace `meta` values in an attribute in the same trace, simply use `%{meta[i]}` where `i` is the index or key of the `meta` item in question. To access trace `meta` in layout attributes, use `%{data[n[.meta[i]}` where `i` is the index or key of the `meta` and `n` is the trace index." + }, + "selectedpoints": { + "type": "string", + "description": "Array containing integer indices of selected points. Has an effect only for traces that support selections. Note that an empty array means an empty selection where the `unselected` are turned on for all points, whereas, any other non-array values means no selection all where the `selected` and `unselected` styles have no effect." + }, + "hoverinfo": { + "type": "string", + "description": "Determines which trace information appear on hover. If `none` or `skip` are set, no information is displayed upon hovering. But, if `none` is set, click and hover events are still fired.", + "default": "all", + "enum": [ + "name", + "text", + "text+name", + "z", + "z+name", + "z+text", + "z+text+name", + "y", + "y+name", + "y+text", + "y+text+name", + "y+z", + "y+z+name", + "y+z+text", + "y+z+text+name", + "x", + "x+name", + "x+text", + "x+text+name", + "x+z", + "x+z+name", + "x+z+text", + "x+z+text+name", + "x+y", + "x+y+name", + "x+y+text", + "x+y+text+name", + "x+y+z", + "x+y+z+name", + "x+y+z+text", + "x+y+z+text+name", + "all", + "none", + "skip" + ] + }, + "hoverlabel": { + "type": "object", + "properties": { + "bgcolor": { + "type": "string", + "description": "Sets the background color of the hover labels for this trace" + }, + "bordercolor": { + "type": "string", + "description": "Sets the border color of the hover labels for this trace." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "align": { + "type": "string", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "default": "auto", + "enum": [ + "left", + "right", + "auto" + ] + }, + "namelength": { + "type": "integer", + "description": "Sets the default length (in number of characters) of the trace name in the hover labels for all traces. -1 shows the whole name regardless of length. 0-3 shows the first 0-3 characters, and an integer >3 will show the whole name if it is less than that many characters, but if it is longer, will truncate to `namelength - 3` characters and add an ellipsis.", + "default": 15, + "minimum": -1 + } + } + }, + "uirevision": { + "type": "string", + "description": "Controls persistence of some user-driven changes to the trace: `constraintrange` in `parcoords` traces, as well as some `editable: true` modifications such as `name` and `colorbar.title`. Defaults to `layout.uirevision`. Note that other user-driven trace attribute changes are controlled by `layout` attributes: `trace.visible` is controlled by `layout.legend.uirevision`, `selectedpoints` is controlled by `layout.selectionrevision`, and `colorbar.(x|y)` (accessible with `config: {editable: true}`) is controlled by `layout.editrevision`. Trace changes are tracked by `uid`, which only falls back on trace index if no `uid` is provided. So if your app can add/remove traces before the end of the `data` array, such that the same trace has a different index, you can still preserve user-driven changes if you give each trace a `uid` that stays with it as it moves." + }, + "x": { + "type": "string", + "description": "Sets the x coordinates." + }, + "x0": { + "type": "string", + "description": "Alternate to `x`. Builds a linear space of x coordinates. Use with `dx` where `x0` is the starting coordinate and `dx` the step.", + "default": 0 + }, + "dx": { + "type": "number", + "description": "Sets the x coordinate step. See `x0` for more info.", + "default": 1 + }, + "y": { + "type": "string", + "description": "Sets the y coordinates." + }, + "y0": { + "type": "string", + "description": "Alternate to `y`. Builds a linear space of y coordinates. Use with `dy` where `y0` is the starting coordinate and `dy` the step.", + "default": 0 + }, + "dy": { + "type": "number", + "description": "Sets the y coordinate step. See `y0` for more info.", + "default": 1 + }, + "xperiod": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the period positioning in milliseconds or *M* on the x axis. Special values in the form of *M* could be used to declare the number of months. In this case `n` must be a positive integer.", + "default": 0 + }, + "yperiod": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the period positioning in milliseconds or *M* on the y axis. Special values in the form of *M* could be used to declare the number of months. In this case `n` must be a positive integer.", + "default": 0 + }, + "xperiod0": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the base for period positioning in milliseconds or date string on the x0 axis. When `x0period` is round number of weeks, the `x0period0` by default would be on a Sunday i.e. 2000-01-02, otherwise it would be at 2000-01-01." + }, + "yperiod0": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the base for period positioning in milliseconds or date string on the y0 axis. When `y0period` is round number of weeks, the `y0period0` by default would be on a Sunday i.e. 2000-01-02, otherwise it would be at 2000-01-01." + }, + "xperiodalignment": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the alignment of data points on the x axis.", + "default": "middle", + "enum": [ + "start", + "middle", + "end" + ] + }, + "yperiodalignment": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the alignment of data points on the y axis.", + "default": "middle", + "enum": [ + "start", + "middle", + "end" + ] + }, + "text": { + "type": "string", + "description": "Sets text elements associated with each (x,y) pair. If a single string, the same string appears over all the data points. If an array of string, the items are mapped in order to the this trace's (x,y) coordinates. If trace `hoverinfo` contains a *text* flag and *hovertext* is not set, these elements will be seen in the hover labels.", + "default": "" + }, + "texttemplate": { + "type": "string", + "description": "Template string used for rendering the information text that appear on points. Note that this will override `textinfo`. Variables are inserted using %{variable}, for example \"y: %{y}\". Numbers are formatted using d3-format's syntax %{variable:d3-format}, for example \"Price: %{y:$.2f}\". https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format for details on the formatting syntax. Dates are formatted using d3-time-format's syntax %{variable|d3-time-format}, for example \"Day: %{2019-01-01|%A}\". https://github.com/d3/d3-time-format#locale_format for details on the date formatting syntax. Every attributes that can be specified per-point (the ones that are `arrayOk: true`) are available. variables `value` and `label`.", + "default": "" + }, + "textposition": { + "type": "string", + "description": "Specifies the location of the `text`. *inside* positions `text` inside, next to the bar end (rotated and scaled if needed). *outside* positions `text` outside, next to the bar end (scaled if needed), unless there is another bar stacked on this one, then the text gets pushed inside. *auto* tries to position `text` inside the bar, but if the bar is too small and no bar is stacked on this one the text is moved outside.", + "default": "none", + "enum": [ + "inside", + "outside", + "auto", + "none" + ] + }, + "insidetextanchor": { + "type": "string", + "description": "Determines if texts are kept at center or start/end points in `textposition` *inside* mode.", + "default": "end", + "enum": [ + "end", + "middle", + "start" + ] + }, + "textangle": { + "type": "number", + "description": "Sets the angle of the tick labels with respect to the bar. For example, a `tickangle` of -90 draws the tick labels vertically. With *auto* the texts may automatically be rotated to fit with the maximum size in bars.", + "default": "auto", + "minimum": -180, + "maximum": 180 + }, + "textfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "insidetextfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "outsidetextfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "constraintext": { + "type": "string", + "description": "Constrain the size of text inside or outside a bar to be no larger than the bar itself.", + "default": "both", + "enum": [ + "inside", + "outside", + "both", + "none" + ] + }, + "cliponaxis": { + "type": "boolean", + "description": "Determines whether the text nodes are clipped about the subplot axes. To show the text nodes above axis lines and tick labels, make sure to set `xaxis.layer` and `yaxis.layer` to *below traces*.", + "default": true + }, + "orientation": { + "type": "string", + "description": "Sets the orientation of the bars. With *v* (*h*), the value of the each bar spans along the vertical (horizontal).", + "enum": [ + "v", + "h" + ] + }, + "base": { + "type": "string", + "description": "Sets where the bar base is drawn (in position axis units). In *stack* or *relative* barmode, traces that set *base* will be excluded and drawn in *overlay* mode instead.", + "default": null + }, + "offset": { + "type": "number", + "description": "Shifts the position where the bar is drawn (in position axis units). In *group* barmode, traces that set *offset* will be excluded and drawn in *overlay* mode instead.", + "default": null + }, + "width": { + "type": "number", + "description": "Sets the bar width (in position axis units).", + "default": null, + "minimum": 0 + }, + "marker": { + "type": "object", + "properties": { + "line": { + "type": "object", + "properties": { + "width": { + "type": "number", + "description": "Sets the width (in px) of the lines bounding the marker points.", + "default": 0, + "minimum": 0 + }, + "color": { + "type": "string", + "description": "Sets themarker.linecolor. It accepts either a specific color or an array of numbers that are mapped to the colorscale relative to the max and min values of the array or relative to `marker.line.cmin` and `marker.line.cmax` if set." + }, + "cauto": { + "type": "boolean", + "description": "Determines whether or not the color domain is computed with respect to the input data (here in `marker.line.color`) or the bounds set in `marker.line.cmin` and `marker.line.cmax` Has an effect only if in `marker.line.color`is set to a numerical array. Defaults to `false` when `marker.line.cmin` and `marker.line.cmax` are set by the user.", + "default": true + }, + "cmin": { + "type": "number", + "description": "Sets the lower bound of the color domain. Has an effect only if in `marker.line.color`is set to a numerical array. Value should have the same units as in `marker.line.color` and if set, `marker.line.cmax` must be set as well.", + "default": null + }, + "cmax": { + "type": "number", + "description": "Sets the upper bound of the color domain. Has an effect only if in `marker.line.color`is set to a numerical array. Value should have the same units as in `marker.line.color` and if set, `marker.line.cmin` must be set as well.", + "default": null + }, + "cmid": { + "type": "number", + "description": "Sets the mid-point of the color domain by scaling `marker.line.cmin` and/or `marker.line.cmax` to be equidistant to this point. Has an effect only if in `marker.line.color`is set to a numerical array. Value should have the same units as in `marker.line.color`. Has no effect when `marker.line.cauto` is `false`.", + "default": null + }, + "colorscale": { + "type": "string", + "description": "Sets the colorscale. Has an effect only if in `marker.line.color`is set to a numerical array. The colorscale must be an array containing arrays mapping a normalized value to an rgb, rgba, hex, hsl, hsv, or named color string. At minimum, a mapping for the lowest (0) and highest (1) values are required. For example, `[[0, 'rgb(0,0,255)'], [1, 'rgb(255,0,0)']]`. To control the bounds of the colorscale in color space, use`marker.line.cmin` and `marker.line.cmax`. Alternatively, `colorscale` may be a palette name string of the following list: Greys,YlGnBu,Greens,YlOrRd,Bluered,RdBu,Reds,Blues,Picnic,Rainbow,Portland,Jet,Hot,Blackbody,Earth,Electric,Viridis,Cividis.", + "default": null + }, + "autocolorscale": { + "type": "boolean", + "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.line.colorscale`. Has an effect only if in `marker.line.color`is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.", + "default": true + }, + "reversescale": { + "type": "boolean", + "description": "Reverses the color mapping if true. Has an effect only if in `marker.line.color`is set to a numerical array. If true, `marker.line.cmin` will correspond to the last color in the array and `marker.line.cmax` will correspond to the first color.", + "default": false + }, + "coloraxis": { + "type": "string", + "description": "Sets a reference to a shared color axis. References to these shared color axes are *coloraxis*, *coloraxis2*, *coloraxis3*, etc. Settings for these shared color axes are set in the layout, under `layout.coloraxis`, `layout.coloraxis2`, etc. Note that multiple color scales can be linked to the same color axis.", + "default": null + } + } + }, + "color": { + "type": "string", + "description": "Sets themarkercolor. It accepts either a specific color or an array of numbers that are mapped to the colorscale relative to the max and min values of the array or relative to `marker.cmin` and `marker.cmax` if set." + }, + "cauto": { + "type": "boolean", + "description": "Determines whether or not the color domain is computed with respect to the input data (here in `marker.color`) or the bounds set in `marker.cmin` and `marker.cmax` Has an effect only if in `marker.color`is set to a numerical array. Defaults to `false` when `marker.cmin` and `marker.cmax` are set by the user.", + "default": true + }, + "cmin": { + "type": "number", + "description": "Sets the lower bound of the color domain. Has an effect only if in `marker.color`is set to a numerical array. Value should have the same units as in `marker.color` and if set, `marker.cmax` must be set as well.", + "default": null + }, + "cmax": { + "type": "number", + "description": "Sets the upper bound of the color domain. Has an effect only if in `marker.color`is set to a numerical array. Value should have the same units as in `marker.color` and if set, `marker.cmin` must be set as well.", + "default": null + }, + "cmid": { + "type": "number", + "description": "Sets the mid-point of the color domain by scaling `marker.cmin` and/or `marker.cmax` to be equidistant to this point. Has an effect only if in `marker.color`is set to a numerical array. Value should have the same units as in `marker.color`. Has no effect when `marker.cauto` is `false`.", + "default": null + }, + "colorscale": { + "type": "string", + "description": "Sets the colorscale. Has an effect only if in `marker.color`is set to a numerical array. The colorscale must be an array containing arrays mapping a normalized value to an rgb, rgba, hex, hsl, hsv, or named color string. At minimum, a mapping for the lowest (0) and highest (1) values are required. For example, `[[0, 'rgb(0,0,255)'], [1, 'rgb(255,0,0)']]`. To control the bounds of the colorscale in color space, use`marker.cmin` and `marker.cmax`. Alternatively, `colorscale` may be a palette name string of the following list: Greys,YlGnBu,Greens,YlOrRd,Bluered,RdBu,Reds,Blues,Picnic,Rainbow,Portland,Jet,Hot,Blackbody,Earth,Electric,Viridis,Cividis.", + "default": null + }, + "autocolorscale": { + "type": "boolean", + "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.colorscale`. Has an effect only if in `marker.color`is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.", + "default": true + }, + "reversescale": { + "type": "boolean", + "description": "Reverses the color mapping if true. Has an effect only if in `marker.color`is set to a numerical array. If true, `marker.cmin` will correspond to the last color in the array and `marker.cmax` will correspond to the first color.", + "default": false + }, + "showscale": { + "type": "boolean", + "description": "Determines whether or not a colorbar is displayed for this trace. Has an effect only if in `marker.color`is set to a numerical array.", + "default": false + }, + "colorbar": { + "type": "object", + "properties": { + "thicknessmode": { + "type": "string", + "description": "Determines whether this color bar's thickness (i.e. the measure in the constant color direction) is set in units of plot *fraction* or in *pixels*. Use `thickness` to set the value.", + "default": "pixels", + "enum": [ + "fraction", + "pixels" + ] + }, + "thickness": { + "type": "number", + "description": "Sets the thickness of the color bar This measure excludes the size of the padding, ticks and labels.", + "default": 30, + "minimum": 0 + }, + "lenmode": { + "type": "string", + "description": "Determines whether this color bar's length (i.e. the measure in the color variation direction) is set in units of plot *fraction* or in *pixels. Use `len` to set the value.", + "default": "fraction", + "enum": [ + "fraction", + "pixels" + ] + }, + "len": { + "type": "number", + "description": "Sets the length of the color bar This measure excludes the padding of both ends. That is, the color bar length is this length minus the padding on both ends.", + "default": 1, + "minimum": 0 + }, + "x": { + "type": "number", + "description": "Sets the x position of the color bar (in plot fraction).", + "default": 1.02, + "minimum": -2, + "maximum": 3 + }, + "xanchor": { + "type": "string", + "description": "Sets this color bar's horizontal position anchor. This anchor binds the `x` position to the *left*, *center* or *right* of the color bar.", + "default": "left", + "enum": [ + "left", + "center", + "right" + ] + }, + "xpad": { + "type": "number", + "description": "Sets the amount of padding (in px) along the x direction.", + "default": 10, + "minimum": 0 + }, + "y": { + "type": "number", + "description": "Sets the y position of the color bar (in plot fraction).", + "default": 0.5, + "minimum": -2, + "maximum": 3 + }, + "yanchor": { + "type": "string", + "description": "Sets this color bar's vertical position anchor This anchor binds the `y` position to the *top*, *middle* or *bottom* of the color bar.", + "default": "middle", + "enum": [ + "top", + "middle", + "bottom" + ] + }, + "ypad": { + "type": "number", + "description": "Sets the amount of padding (in px) along the y direction.", + "default": 10, + "minimum": 0 + }, + "outlinecolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "outlinewidth": { + "type": "number", + "description": "Sets the width (in px) of the axis line.", + "default": 1, + "minimum": 0 + }, + "bordercolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "borderwidth": { + "type": "number", + "description": "Sets the width (in px) or the border enclosing this color bar.", + "default": 0, + "minimum": 0 + }, + "bgcolor": { + "type": "string", + "description": "Sets the color of padded area.", + "default": "rgba(0,0,0,0)" + }, + "tickmode": { + "type": "string", + "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).", + "enum": [ + "auto", + "linear", + "array" + ] + }, + "nticks": { + "type": "integer", + "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", + "default": 0, + "minimum": 0 + }, + "tick0": { + "type": "string", + "description": "Sets the placement of the first tick on this axis. Use with `dtick`. If the axis `type` is *log*, then you must take the log of your starting tick (e.g. to set the starting tick to 100, set the `tick0` to 2) except when `dtick`=*L* (see `dtick` for more info). If the axis `type` is *date*, it should be a date string, like date data. If the axis `type` is *category*, it should be a number, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "dtick": { + "type": "string", + "description": "Sets the step in-between ticks on this axis. Use with `tick0`. Must be a positive number, or special strings available to *log* and *date* axes. If the axis `type` is *log*, then ticks are set every 10^(n*dtick) where n is the tick number. For example, to set a tick mark at 1, 10, 100, 1000, ... set dtick to 1. To set tick marks at 1, 100, 10000, ... set dtick to 2. To set tick marks at 1, 5, 25, 125, 625, 3125, ... set dtick to log_10(5), or 0.69897000433. *log* has several special values; *L*, where `f` is a positive number, gives ticks linearly spaced in value (but not position). For example `tick0` = 0.1, `dtick` = *L0.5* will put ticks at 0.1, 0.6, 1.1, 1.6 etc. To show powers of 10 plus small digits between, use *D1* (all digits) or *D2* (only 2 and 5). `tick0` is ignored for *D1* and *D2*. If the axis `type` is *date*, then you must convert the time to milliseconds. For example, to set the interval between ticks to one day, set `dtick` to 86400000.0. *date* also has special values *M* gives ticks spaced by a number of months. `n` must be a positive integer. To set ticks on the 15th of every third month, set `tick0` to *2000-01-15* and `dtick` to *M3*. To set ticks every 4 years, set `dtick` to *M48*" + }, + "tickvals": { + "type": "string", + "description": "Sets the values at which ticks on this axis appear. Only has an effect if `tickmode` is set to *array*. Used with `ticktext`." + }, + "ticktext": { + "type": "string", + "description": "Sets the text displayed at the ticks position via `tickvals`. Only has an effect if `tickmode` is set to *array*. Used with `tickvals`." + }, + "ticks": { + "type": "string", + "description": "Determines whether ticks are drawn or not. If **, this axis' ticks are not drawn. If *outside* (*inside*), this axis' are drawn outside (inside) the axis lines.", + "default": "", + "enum": [ + "outside", + "inside", + "" + ] + }, + "ticklabelposition": { + "type": "string", + "description": "Determines where tick labels are drawn.", + "default": "outside", + "enum": [ + "outside", + "inside", + "outside top", + "inside top", + "outside bottom", + "inside bottom" + ] + }, + "ticklen": { + "type": "number", + "description": "Sets the tick length (in px).", + "default": 5, + "minimum": 0 + }, + "tickwidth": { + "type": "number", + "description": "Sets the tick width (in px).", + "default": 1, + "minimum": 0 + }, + "tickcolor": { + "type": "string", + "description": "Sets the tick color.", + "default": "#444" + }, + "showticklabels": { + "type": "boolean", + "description": "Determines whether or not the tick labels are drawn.", + "default": true + }, + "tickfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "tickangle": { + "type": "number", + "description": "Sets the angle of the tick labels with respect to the horizontal. For example, a `tickangle` of -90 draws the tick labels vertically.", + "default": "auto", + "minimum": -180, + "maximum": 180 + }, + "tickformat": { + "type": "string", + "description": "Sets the tick label formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "tickprefix": { + "type": "string", + "description": "Sets a tick label prefix.", + "default": "" + }, + "showtickprefix": { + "type": "string", + "description": "If *all*, all tick labels are displayed with a prefix. If *first*, only the first tick is displayed with a prefix. If *last*, only the last tick is displayed with a suffix. If *none*, tick prefixes are hidden.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "ticksuffix": { + "type": "string", + "description": "Sets a tick label suffix.", + "default": "" + }, + "showticksuffix": { + "type": "string", + "description": "Same as `showtickprefix` but for tick suffixes.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "separatethousands": { + "type": "boolean", + "description": "If \"true\", even 4-digit integers are separated", + "default": false + }, + "exponentformat": { + "type": "string", + "description": "Determines a formatting rule for the tick exponents. For example, consider the number 1,000,000,000. If *none*, it appears as 1,000,000,000. If *e*, 1e+9. If *E*, 1E+9. If *power*, 1x10^9 (with 9 in a super script). If *SI*, 1G. If *B*, 1B.", + "default": "B", + "enum": [ + "none", + "e", + "E", + "power", + "SI", + "B" + ] + }, + "minexponent": { + "type": "number", + "description": "Hide SI prefix for 10^n if |n| is below this number. This only has an effect when `tickformat` is *SI* or *B*.", + "default": 3, + "minimum": 0 + }, + "showexponent": { + "type": "string", + "description": "If *all*, all exponents are shown besides their significands. If *first*, only the exponent of the first tick is shown. If *last*, only the exponent of the last tick is shown. If *none*, no exponents appear.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "title": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Sets the title of the color bar. Note that before the existence of `title.text`, the title's contents used to be defined as the `title` attribute itself. This behavior has been deprecated." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "side": { + "type": "string", + "description": "Determines the location of color bar's title with respect to the color bar. Note that the title's location used to be set by the now deprecated `titleside` attribute.", + "default": "top", + "enum": [ + "right", + "top", + "bottom" + ] + } + } + } + } + }, + "coloraxis": { + "type": "string", + "description": "Sets a reference to a shared color axis. References to these shared color axes are *coloraxis*, *coloraxis2*, *coloraxis3*, etc. Settings for these shared color axes are set in the layout, under `layout.coloraxis`, `layout.coloraxis2`, etc. Note that multiple color scales can be linked to the same color axis.", + "default": null + }, + "opacity": { + "type": "number", + "description": "Sets the opacity of the bars.", + "default": 1, + "minimum": 0, + "maximum": 1 + } + } + }, + "offsetgroup": { + "type": "string", + "description": "Set several traces linked to the same position axis or matching axes to the same offsetgroup where bars of the same position coordinate will line up.", + "default": "" + }, + "alignmentgroup": { + "type": "string", + "description": "Set several traces linked to the same position axis or matching axes to the same alignmentgroup. This controls whether bars compute their positional range dependently or independently.", + "default": "" + }, + "selected": { + "type": "object", + "properties": { + "marker": { + "type": "object", + "properties": { + "opacity": { + "type": "number", + "description": "Sets the marker opacity of selected points.", + "minimum": 0, + "maximum": 1 + }, + "color": { + "type": "string", + "description": "Sets the marker color of selected points." + } + } + }, + "textfont": { + "type": "object", + "properties": { + "color": { + "type": "string", + "description": "Sets the text font color of selected points." + } + } + } + } + }, + "unselected": { + "type": "object", + "properties": { + "marker": { + "type": "object", + "properties": { + "opacity": { + "type": "number", + "description": "Sets the marker opacity of unselected points, applied only when a selection exists.", + "minimum": 0, + "maximum": 1 + }, + "color": { + "type": "string", + "description": "Sets the marker color of unselected points, applied only when a selection exists." + } + } + }, + "textfont": { + "type": "object", + "properties": { + "color": { + "type": "string", + "description": "Sets the text font color of unselected points, applied only when a selection exists." + } + } + } + } + }, + "error_x": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Determines whether or not this set of error bars is visible." + }, + "type": { + "type": "string", + "description": "Determines the rule used to generate the error bars. If *constant`, the bar lengths are of a constant value. Set this constant in `value`. If *percent*, the bar lengths correspond to a percentage of underlying data. Set this percentage in `value`. If *sqrt*, the bar lengths correspond to the square of the underlying data. If *data*, the bar lengths are set with data set `array`.", + "enum": [ + "percent", + "constant", + "sqrt", + "data" + ] + }, + "symmetric": { + "type": "boolean", + "description": "Determines whether or not the error bars have the same length in both direction (top/bottom for vertical bars, left/right for horizontal bars." + }, + "array": { + "type": "string", + "description": "Sets the data corresponding the length of each error bar. Values are plotted relative to the underlying data." + }, + "arrayminus": { + "type": "string", + "description": "Sets the data corresponding the length of each error bar in the bottom (left) direction for vertical (horizontal) bars Values are plotted relative to the underlying data." + }, + "value": { + "type": "number", + "description": "Sets the value of either the percentage (if `type` is set to *percent*) or the constant (if `type` is set to *constant*) corresponding to the lengths of the error bars.", + "default": 10, + "minimum": 0 + }, + "valueminus": { + "type": "number", + "description": "Sets the value of either the percentage (if `type` is set to *percent*) or the constant (if `type` is set to *constant*) corresponding to the lengths of the error bars in the bottom (left) direction for vertical (horizontal) bars", + "default": 10, + "minimum": 0 + }, + "traceref": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "tracerefminus": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "copy_ystyle": { + "type": "boolean" + }, + "color": { + "type": "string", + "description": "Sets the stoke color of the error bars." + }, + "thickness": { + "type": "number", + "description": "Sets the thickness (in px) of the error bars.", + "default": 2, + "minimum": 0 + }, + "width": { + "type": "number", + "description": "Sets the width (in px) of the cross-bar at both ends of the error bars.", + "minimum": 0 + } + } + }, + "error_y": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Determines whether or not this set of error bars is visible." + }, + "type": { + "type": "string", + "description": "Determines the rule used to generate the error bars. If *constant`, the bar lengths are of a constant value. Set this constant in `value`. If *percent*, the bar lengths correspond to a percentage of underlying data. Set this percentage in `value`. If *sqrt*, the bar lengths correspond to the square of the underlying data. If *data*, the bar lengths are set with data set `array`.", + "enum": [ + "percent", + "constant", + "sqrt", + "data" + ] + }, + "symmetric": { + "type": "boolean", + "description": "Determines whether or not the error bars have the same length in both direction (top/bottom for vertical bars, left/right for horizontal bars." + }, + "array": { + "type": "string", + "description": "Sets the data corresponding the length of each error bar. Values are plotted relative to the underlying data." + }, + "arrayminus": { + "type": "string", + "description": "Sets the data corresponding the length of each error bar in the bottom (left) direction for vertical (horizontal) bars Values are plotted relative to the underlying data." + }, + "value": { + "type": "number", + "description": "Sets the value of either the percentage (if `type` is set to *percent*) or the constant (if `type` is set to *constant*) corresponding to the lengths of the error bars.", + "default": 10, + "minimum": 0 + }, + "valueminus": { + "type": "number", + "description": "Sets the value of either the percentage (if `type` is set to *percent*) or the constant (if `type` is set to *constant*) corresponding to the lengths of the error bars in the bottom (left) direction for vertical (horizontal) bars", + "default": 10, + "minimum": 0 + }, + "traceref": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "tracerefminus": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "color": { + "type": "string", + "description": "Sets the stoke color of the error bars." + }, + "thickness": { + "type": "number", + "description": "Sets the thickness (in px) of the error bars.", + "default": 2, + "minimum": 0 + }, + "width": { + "type": "number", + "description": "Sets the width (in px) of the cross-bar at both ends of the error bars.", + "minimum": 0 + } + } + }, + "xcalendar": { + "type": "string", + "description": "Sets the calendar system to use with `x` date data.", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + }, + "ycalendar": { + "type": "string", + "description": "Sets the calendar system to use with `y` date data.", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + }, + "xaxis": { + "type": "string", + "description": "Sets a reference between this trace's x coordinates and a 2D cartesian x axis. If *x* (the default value), the x coordinates refer to `layout.xaxis`. If *x2*, the x coordinates refer to `layout.xaxis2`, and so on.", + "default": "x" + }, + "yaxis": { + "type": "string", + "description": "Sets a reference between this trace's y coordinates and a 2D cartesian y axis. If *y* (the default value), the y coordinates refer to `layout.yaxis`. If *y2*, the y coordinates refer to `layout.yaxis2`, and so on.", + "default": "y" + } + }, + "layoutAttributes": { + "barmode": { + "type": "string", + "description": "Determines how bars at the same location coordinate are displayed on the graph. With *stack*, the bars are stacked on top of one another With *relative*, the bars are stacked on top of one another, with negative values below the axis, positive values above With *group*, the bars are plotted next to one another centered around the shared location. With *overlay*, the bars are plotted over one another, you might need to an *opacity* to see multiple bars.", + "default": "group", + "enum": [ + "stack", + "group", + "overlay", + "relative" + ] + }, + "barnorm": { + "type": "string", + "description": "Sets the normalization for bar traces on the graph. With *fraction*, the value of each bar is divided by the sum of all values at that location coordinate. *percent* is the same but multiplied by 100 to show percentages.", + "default": "", + "enum": [ + "", + "fraction", + "percent" + ] + }, + "bargap": { + "type": "number", + "description": "Sets the gap (in plot fraction) between bars of adjacent location coordinates.", + "minimum": 0, + "maximum": 1 + }, + "bargroupgap": { + "type": "number", + "description": "Sets the gap (in plot fraction) between bars of the same location coordinate.", + "default": 0, + "minimum": 0, + "maximum": 1 + } + } +} \ No newline at end of file diff --git a/escalation/plotly_api/box_plotly_schema.json b/escalation/plotly_api/box_plotly_schema.json new file mode 100644 index 0000000..ae497bb --- /dev/null +++ b/escalation/plotly_api/box_plotly_schema.json @@ -0,0 +1,550 @@ +{ + "attributes": { + "visible": { + "type": "string", + "description": "Determines whether or not this trace is visible. If *legendonly*, the trace is not drawn, but can appear as a legend item (provided that the legend itself is visible).", + "default": true, + "enum": [ + true, + false, + "legendonly" + ] + }, + "showlegend": { + "type": "boolean", + "description": "Determines whether or not an item corresponding to this trace is shown in the legend.", + "default": true + }, + "legendgroup": { + "type": "string", + "description": "Sets the legend group for this trace. Traces part of the same legend group hide/show at the same time when toggling legend items.", + "default": "" + }, + "opacity": { + "type": "number", + "description": "Sets the opacity of the trace.", + "default": 1, + "minimum": 0, + "maximum": 1 + }, + "customdata": { + "type": "string", + "description": "Assigns extra data each datum. This may be useful when listening to hover, click and selection events. Note that, *scatter* traces also appends customdata items in the markers DOM elements" + }, + "meta": { + "type": "string", + "description": "Assigns extra meta information associated with this trace that can be used in various text attributes. Attributes such as trace `name`, graph, axis and colorbar `title.text`, annotation `text` `rangeselector`, `updatemenues` and `sliders` `label` text all support `meta`. To access the trace `meta` values in an attribute in the same trace, simply use `%{meta[i]}` where `i` is the index or key of the `meta` item in question. To access trace `meta` in layout attributes, use `%{data[n[.meta[i]}` where `i` is the index or key of the `meta` and `n` is the trace index." + }, + "selectedpoints": { + "type": "string", + "description": "Array containing integer indices of selected points. Has an effect only for traces that support selections. Note that an empty array means an empty selection where the `unselected` are turned on for all points, whereas, any other non-array values means no selection all where the `selected` and `unselected` styles have no effect." + }, + "hoverinfo": { + "type": "string", + "description": "Determines which trace information appear on hover. If `none` or `skip` are set, no information is displayed upon hovering. But, if `none` is set, click and hover events are still fired.", + "default": "all", + "enum": [ + "name", + "text", + "text+name", + "z", + "z+name", + "z+text", + "z+text+name", + "y", + "y+name", + "y+text", + "y+text+name", + "y+z", + "y+z+name", + "y+z+text", + "y+z+text+name", + "x", + "x+name", + "x+text", + "x+text+name", + "x+z", + "x+z+name", + "x+z+text", + "x+z+text+name", + "x+y", + "x+y+name", + "x+y+text", + "x+y+text+name", + "x+y+z", + "x+y+z+name", + "x+y+z+text", + "x+y+z+text+name", + "all", + "none", + "skip" + ] + }, + "hoverlabel": { + "type": "object", + "properties": { + "bgcolor": { + "type": "string", + "description": "Sets the background color of the hover labels for this trace" + }, + "bordercolor": { + "type": "string", + "description": "Sets the border color of the hover labels for this trace." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "align": { + "type": "string", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "default": "auto", + "enum": [ + "left", + "right", + "auto" + ] + }, + "namelength": { + "type": "integer", + "description": "Sets the default length (in number of characters) of the trace name in the hover labels for all traces. -1 shows the whole name regardless of length. 0-3 shows the first 0-3 characters, and an integer >3 will show the whole name if it is less than that many characters, but if it is longer, will truncate to `namelength - 3` characters and add an ellipsis.", + "default": 15, + "minimum": -1 + } + } + }, + "uirevision": { + "type": "string", + "description": "Controls persistence of some user-driven changes to the trace: `constraintrange` in `parcoords` traces, as well as some `editable: true` modifications such as `name` and `colorbar.title`. Defaults to `layout.uirevision`. Note that other user-driven trace attribute changes are controlled by `layout` attributes: `trace.visible` is controlled by `layout.legend.uirevision`, `selectedpoints` is controlled by `layout.selectionrevision`, and `colorbar.(x|y)` (accessible with `config: {editable: true}`) is controlled by `layout.editrevision`. Trace changes are tracked by `uid`, which only falls back on trace index if no `uid` is provided. So if your app can add/remove traces before the end of the `data` array, such that the same trace has a different index, you can still preserve user-driven changes if you give each trace a `uid` that stays with it as it moves." + }, + "y": { + "type": "string", + "description": "Sets the y sample data or coordinates. See overview for more info." + }, + "x": { + "type": "string", + "description": "Sets the x sample data or coordinates. See overview for more info." + }, + "x0": { + "type": "string", + "description": "Sets the x coordinate for single-box traces or the starting coordinate for multi-box traces set using q1/median/q3. See overview for more info." + }, + "y0": { + "type": "string", + "description": "Sets the y coordinate for single-box traces or the starting coordinate for multi-box traces set using q1/median/q3. See overview for more info." + }, + "dx": { + "type": "number", + "description": "Sets the x coordinate step for multi-box traces set using q1/median/q3." + }, + "dy": { + "type": "number", + "description": "Sets the y coordinate step for multi-box traces set using q1/median/q3." + }, + "xperiod": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the period positioning in milliseconds or *M* on the x axis. Special values in the form of *M* could be used to declare the number of months. In this case `n` must be a positive integer.", + "default": 0 + }, + "yperiod": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the period positioning in milliseconds or *M* on the y axis. Special values in the form of *M* could be used to declare the number of months. In this case `n` must be a positive integer.", + "default": 0 + }, + "xperiod0": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the base for period positioning in milliseconds or date string on the x0 axis. When `x0period` is round number of weeks, the `x0period0` by default would be on a Sunday i.e. 2000-01-02, otherwise it would be at 2000-01-01." + }, + "yperiod0": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the base for period positioning in milliseconds or date string on the y0 axis. When `y0period` is round number of weeks, the `y0period0` by default would be on a Sunday i.e. 2000-01-02, otherwise it would be at 2000-01-01." + }, + "xperiodalignment": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the alignment of data points on the x axis.", + "default": "middle", + "enum": [ + "start", + "middle", + "end" + ] + }, + "yperiodalignment": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the alignment of data points on the y axis.", + "default": "middle", + "enum": [ + "start", + "middle", + "end" + ] + }, + "name": { + "type": "string", + "description": "Sets the trace name. The trace name appear as the legend item and on hover. For box traces, the name will also be used for the position coordinate, if `x` and `x0` (`y` and `y0` if horizontal) are missing and the position axis is categorical" + }, + "q1": { + "type": "string", + "description": "Sets the Quartile 1 values. There should be as many items as the number of boxes desired." + }, + "median": { + "type": "string", + "description": "Sets the median values. There should be as many items as the number of boxes desired." + }, + "q3": { + "type": "string", + "description": "Sets the Quartile 3 values. There should be as many items as the number of boxes desired." + }, + "lowerfence": { + "type": "string", + "description": "Sets the lower fence values. There should be as many items as the number of boxes desired. This attribute has effect only under the q1/median/q3 signature. If `lowerfence` is not provided but a sample (in `y` or `x`) is set, we compute the lower as the last sample point below 1.5 times the IQR." + }, + "upperfence": { + "type": "string", + "description": "Sets the upper fence values. There should be as many items as the number of boxes desired. This attribute has effect only under the q1/median/q3 signature. If `upperfence` is not provided but a sample (in `y` or `x`) is set, we compute the lower as the last sample point above 1.5 times the IQR." + }, + "notched": { + "type": "boolean", + "description": "Determines whether or not notches are drawn. Notches displays a confidence interval around the median. We compute the confidence interval as median +/- 1.57 * IQR / sqrt(N), where IQR is the interquartile range and N is the sample size. If two boxes' notches do not overlap there is 95% confidence their medians differ. See https://sites.google.com/site/davidsstatistics/home/notched-box-plots for more info. Defaults to *false* unless `notchwidth` or `notchspan` is set." + }, + "notchwidth": { + "type": "number", + "description": "Sets the width of the notches relative to the box' width. For example, with 0, the notches are as wide as the box(es).", + "default": 0.25, + "minimum": 0, + "maximum": 0.5 + }, + "notchspan": { + "type": "string", + "description": "Sets the notch span from the boxes' `median` values. There should be as many items as the number of boxes desired. This attribute has effect only under the q1/median/q3 signature. If `notchspan` is not provided but a sample (in `y` or `x`) is set, we compute it as 1.57 * IQR / sqrt(N), where N is the sample size." + }, + "boxpoints": { + "type": "string", + "description": "If *outliers*, only the sample points lying outside the whiskers are shown If *suspectedoutliers*, the outlier points are shown and points either less than 4*Q1-3*Q3 or greater than 4*Q3-3*Q1 are highlighted (see `outliercolor`) If *all*, all sample points are shown If *false*, only the box(es) are shown with no sample points Defaults to *suspectedoutliers* when `marker.outliercolor` or `marker.line.outliercolor` is set. Defaults to *all* under the q1/median/q3 signature. Otherwise defaults to *outliers*.", + "enum": [ + "all", + "outliers", + "suspectedoutliers", + false + ] + }, + "jitter": { + "type": "number", + "description": "Sets the amount of jitter in the sample points drawn. If *0*, the sample points align along the distribution axis. If *1*, the sample points are drawn in a random jitter of width equal to the width of the box(es).", + "minimum": 0, + "maximum": 1 + }, + "pointpos": { + "type": "number", + "description": "Sets the position of the sample points in relation to the box(es). If *0*, the sample points are places over the center of the box(es). Positive (negative) values correspond to positions to the right (left) for vertical boxes and above (below) for horizontal boxes", + "minimum": -2, + "maximum": 2 + }, + "boxmean": { + "type": "string", + "description": "If *true*, the mean of the box(es)' underlying distribution is drawn as a dashed line inside the box(es). If *sd* the standard deviation is also drawn. Defaults to *true* when `mean` is set. Defaults to *sd* when `sd` is set Otherwise defaults to *false*.", + "enum": [ + true, + "sd", + false + ] + }, + "mean": { + "type": "string", + "description": "Sets the mean values. There should be as many items as the number of boxes desired. This attribute has effect only under the q1/median/q3 signature. If `mean` is not provided but a sample (in `y` or `x`) is set, we compute the mean for each box using the sample values." + }, + "sd": { + "type": "string", + "description": "Sets the standard deviation values. There should be as many items as the number of boxes desired. This attribute has effect only under the q1/median/q3 signature. If `sd` is not provided but a sample (in `y` or `x`) is set, we compute the standard deviation for each box using the sample values." + }, + "orientation": { + "type": "string", + "description": "Sets the orientation of the box(es). If *v* (*h*), the distribution is visualized along the vertical (horizontal).", + "enum": [ + "v", + "h" + ] + }, + "quartilemethod": { + "type": "string", + "description": "Sets the method used to compute the sample's Q1 and Q3 quartiles. The *linear* method uses the 25th percentile for Q1 and 75th percentile for Q3 as computed using method #10 (listed on http://www.amstat.org/publications/jse/v14n3/langford.html). The *exclusive* method uses the median to divide the ordered dataset into two halves if the sample is odd, it does not include the median in either half - Q1 is then the median of the lower half and Q3 the median of the upper half. The *inclusive* method also uses the median to divide the ordered dataset into two halves but if the sample is odd, it includes the median in both halves - Q1 is then the median of the lower half and Q3 the median of the upper half.", + "default": "linear", + "enum": [ + "linear", + "exclusive", + "inclusive" + ] + }, + "width": { + "type": "number", + "description": "Sets the width of the box in data coordinate If *0* (default value) the width is automatically selected based on the positions of other box traces in the same subplot.", + "default": 0, + "minimum": 0 + }, + "marker": { + "type": "object", + "properties": { + "outliercolor": { + "type": "string", + "description": "Sets the color of the outlier sample points.", + "default": "rgba(0, 0, 0, 0)" + }, + "symbol": { + "type": "string", + "description": "Sets the marker symbol type. Adding 100 is equivalent to appending *-open* to a symbol name. Adding 200 is equivalent to appending *-dot* to a symbol name. Adding 300 is equivalent to appending *-open-dot* or *dot-open* to a symbol name.", + "default": "circle", + "enum": [ + "circle", + "square", + "diamond", + "cross", + "x", + "triangle-up", + "triangle-down", + "pentagon", + "hexagon", + "octagon", + "star" + ] + }, + "opacity": { + "type": "number", + "description": "Sets the marker opacity.", + "default": 1, + "minimum": 0, + "maximum": 1 + }, + "size": { + "type": "number", + "description": "Sets the marker size (in px).", + "default": 6, + "minimum": 0 + }, + "color": { + "type": "string", + "description": "Sets themarkercolor. It accepts either a specific color or an array of numbers that are mapped to the colorscale relative to the max and min values of the array or relative to `marker.cmin` and `marker.cmax` if set." + }, + "line": { + "type": "object", + "properties": { + "color": { + "type": "string", + "description": "Sets themarker.linecolor. It accepts either a specific color or an array of numbers that are mapped to the colorscale relative to the max and min values of the array or relative to `marker.line.cmin` and `marker.line.cmax` if set.", + "default": "#444" + }, + "width": { + "type": "number", + "description": "Sets the width (in px) of the lines bounding the marker points.", + "default": 0, + "minimum": 0 + }, + "outliercolor": { + "type": "string", + "description": "Sets the border line color of the outlier sample points. Defaults to marker.color" + }, + "outlierwidth": { + "type": "number", + "description": "Sets the border line width (in px) of the outlier sample points.", + "default": 1, + "minimum": 0 + } + } + } + } + }, + "line": { + "type": "object", + "properties": { + "color": { + "type": "string", + "description": "Sets the color of line bounding the box(es)." + }, + "width": { + "type": "number", + "description": "Sets the width (in px) of line bounding the box(es).", + "default": 2, + "minimum": 0 + } + } + }, + "fillcolor": { + "type": "string", + "description": "Sets the fill color. Defaults to a half-transparent variant of the line color, marker color, or marker line color, whichever is available." + }, + "whiskerwidth": { + "type": "number", + "description": "Sets the width of the whiskers relative to the box' width. For example, with 1, the whiskers are as wide as the box(es).", + "default": 0.5, + "minimum": 0, + "maximum": 1 + }, + "offsetgroup": { + "type": "string", + "description": "Set several traces linked to the same position axis or matching axes to the same offsetgroup where bars of the same position coordinate will line up.", + "default": "" + }, + "alignmentgroup": { + "type": "string", + "description": "Set several traces linked to the same position axis or matching axes to the same alignmentgroup. This controls whether bars compute their positional range dependently or independently.", + "default": "" + }, + "selected": { + "type": "object", + "properties": { + "marker": { + "type": "object", + "properties": { + "opacity": { + "type": "number", + "description": "Sets the marker opacity of selected points.", + "minimum": 0, + "maximum": 1 + }, + "color": { + "type": "string", + "description": "Sets the marker color of selected points." + }, + "size": { + "type": "number", + "description": "Sets the marker size of selected points.", + "minimum": 0 + } + } + } + } + }, + "unselected": { + "type": "object", + "properties": { + "marker": { + "type": "object", + "properties": { + "opacity": { + "type": "number", + "description": "Sets the marker opacity of unselected points, applied only when a selection exists.", + "minimum": 0, + "maximum": 1 + }, + "color": { + "type": "string", + "description": "Sets the marker color of unselected points, applied only when a selection exists." + }, + "size": { + "type": "number", + "description": "Sets the marker size of unselected points, applied only when a selection exists.", + "minimum": 0 + } + } + } + } + }, + "text": { + "type": "string", + "description": "Sets the text elements associated with each sample value. If a single string, the same string appears over all the data points. If an array of string, the items are mapped in order to the this trace's (x,y) coordinates. To be seen, trace `hoverinfo` must contain a *text* flag.", + "default": "" + }, + "hoveron": { + "type": "string", + "description": "Do the hover effects highlight individual boxes or sample points or both?", + "default": "boxes+points", + "enum": [ + "points", + "boxes", + "boxes+points" + ] + }, + "xcalendar": { + "type": "string", + "description": "Sets the calendar system to use with `x` date data.", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + }, + "ycalendar": { + "type": "string", + "description": "Sets the calendar system to use with `y` date data.", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + }, + "xaxis": { + "type": "string", + "description": "Sets a reference between this trace's x coordinates and a 2D cartesian x axis. If *x* (the default value), the x coordinates refer to `layout.xaxis`. If *x2*, the x coordinates refer to `layout.xaxis2`, and so on.", + "default": "x" + }, + "yaxis": { + "type": "string", + "description": "Sets a reference between this trace's y coordinates and a 2D cartesian y axis. If *y* (the default value), the y coordinates refer to `layout.yaxis`. If *y2*, the y coordinates refer to `layout.yaxis2`, and so on.", + "default": "y" + } + }, + "layoutAttributes": { + "boxmode": { + "type": "string", + "description": "Determines how boxes at the same location coordinate are displayed on the graph. If *group*, the boxes are plotted next to one another centered around the shared location. If *overlay*, the boxes are plotted over one another, you might need to set *opacity* to see them multiple boxes. Has no effect on traces that have *width* set.", + "default": "overlay", + "enum": [ + "group", + "overlay" + ] + }, + "boxgap": { + "type": "number", + "description": "Sets the gap (in plot fraction) between boxes of adjacent location coordinates. Has no effect on traces that have *width* set.", + "default": 0.3, + "minimum": 0, + "maximum": 1 + }, + "boxgroupgap": { + "type": "number", + "description": "Sets the gap (in plot fraction) between boxes of the same location coordinate. Has no effect on traces that have *width* set.", + "default": 0.3, + "minimum": 0, + "maximum": 1 + } + } +} \ No newline at end of file diff --git a/escalation/plotly_api/contour_plotly_schema.json b/escalation/plotly_api/contour_plotly_schema.json new file mode 100644 index 0000000..569df3c --- /dev/null +++ b/escalation/plotly_api/contour_plotly_schema.json @@ -0,0 +1,795 @@ +{ + "attributes": { + "visible": { + "type": "string", + "description": "Determines whether or not this trace is visible. If *legendonly*, the trace is not drawn, but can appear as a legend item (provided that the legend itself is visible).", + "default": true, + "enum": [ + true, + false, + "legendonly" + ] + }, + "showlegend": { + "type": "boolean", + "description": "Determines whether or not an item corresponding to this trace is shown in the legend.", + "default": true + }, + "legendgroup": { + "type": "string", + "description": "Sets the legend group for this trace. Traces part of the same legend group hide/show at the same time when toggling legend items.", + "default": "" + }, + "opacity": { + "type": "number", + "description": "Sets the opacity of the trace.", + "default": 1, + "minimum": 0, + "maximum": 1 + }, + "name": { + "type": "string", + "description": "Sets the trace name. The trace name appear as the legend item and on hover." + }, + "customdata": { + "type": "string", + "description": "Assigns extra data each datum. This may be useful when listening to hover, click and selection events. Note that, *scatter* traces also appends customdata items in the markers DOM elements" + }, + "meta": { + "type": "string", + "description": "Assigns extra meta information associated with this trace that can be used in various text attributes. Attributes such as trace `name`, graph, axis and colorbar `title.text`, annotation `text` `rangeselector`, `updatemenues` and `sliders` `label` text all support `meta`. To access the trace `meta` values in an attribute in the same trace, simply use `%{meta[i]}` where `i` is the index or key of the `meta` item in question. To access trace `meta` in layout attributes, use `%{data[n[.meta[i]}` where `i` is the index or key of the `meta` and `n` is the trace index." + }, + "hoverinfo": { + "type": "string", + "description": "Determines which trace information appear on hover. If `none` or `skip` are set, no information is displayed upon hovering. But, if `none` is set, click and hover events are still fired.", + "default": "all", + "enum": [ + "name", + "text", + "text+name", + "z", + "z+name", + "z+text", + "z+text+name", + "y", + "y+name", + "y+text", + "y+text+name", + "y+z", + "y+z+name", + "y+z+text", + "y+z+text+name", + "x", + "x+name", + "x+text", + "x+text+name", + "x+z", + "x+z+name", + "x+z+text", + "x+z+text+name", + "x+y", + "x+y+name", + "x+y+text", + "x+y+text+name", + "x+y+z", + "x+y+z+name", + "x+y+z+text", + "x+y+z+text+name", + "all", + "none", + "skip" + ] + }, + "hoverlabel": { + "type": "object", + "properties": { + "bgcolor": { + "type": "string", + "description": "Sets the background color of the hover labels for this trace" + }, + "bordercolor": { + "type": "string", + "description": "Sets the border color of the hover labels for this trace." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "align": { + "type": "string", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "default": "auto", + "enum": [ + "left", + "right", + "auto" + ] + }, + "namelength": { + "type": "integer", + "description": "Sets the default length (in number of characters) of the trace name in the hover labels for all traces. -1 shows the whole name regardless of length. 0-3 shows the first 0-3 characters, and an integer >3 will show the whole name if it is less than that many characters, but if it is longer, will truncate to `namelength - 3` characters and add an ellipsis.", + "default": 15, + "minimum": -1 + } + } + }, + "uirevision": { + "type": "string", + "description": "Controls persistence of some user-driven changes to the trace: `constraintrange` in `parcoords` traces, as well as some `editable: true` modifications such as `name` and `colorbar.title`. Defaults to `layout.uirevision`. Note that other user-driven trace attribute changes are controlled by `layout` attributes: `trace.visible` is controlled by `layout.legend.uirevision`, `selectedpoints` is controlled by `layout.selectionrevision`, and `colorbar.(x|y)` (accessible with `config: {editable: true}`) is controlled by `layout.editrevision`. Trace changes are tracked by `uid`, which only falls back on trace index if no `uid` is provided. So if your app can add/remove traces before the end of the `data` array, such that the same trace has a different index, you can still preserve user-driven changes if you give each trace a `uid` that stays with it as it moves." + }, + "z": { + "type": "string", + "description": "Sets the z data." + }, + "x": { + "type": "string", + "description": "Sets the x coordinates." + }, + "x0": { + "type": "string", + "description": "Alternate to `x`. Builds a linear space of x coordinates. Use with `dx` where `x0` is the starting coordinate and `dx` the step.", + "default": 0 + }, + "dx": { + "type": "number", + "description": "Sets the x coordinate step. See `x0` for more info.", + "default": 1 + }, + "y": { + "type": "string", + "description": "Sets the y coordinates." + }, + "y0": { + "type": "string", + "description": "Alternate to `y`. Builds a linear space of y coordinates. Use with `dy` where `y0` is the starting coordinate and `dy` the step.", + "default": 0 + }, + "dy": { + "type": "number", + "description": "Sets the y coordinate step. See `y0` for more info.", + "default": 1 + }, + "xperiod": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the period positioning in milliseconds or *M* on the x axis. Special values in the form of *M* could be used to declare the number of months. In this case `n` must be a positive integer.", + "default": 0 + }, + "yperiod": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the period positioning in milliseconds or *M* on the y axis. Special values in the form of *M* could be used to declare the number of months. In this case `n` must be a positive integer.", + "default": 0 + }, + "xperiod0": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the base for period positioning in milliseconds or date string on the x0 axis. When `x0period` is round number of weeks, the `x0period0` by default would be on a Sunday i.e. 2000-01-02, otherwise it would be at 2000-01-01." + }, + "yperiod0": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the base for period positioning in milliseconds or date string on the y0 axis. When `y0period` is round number of weeks, the `y0period0` by default would be on a Sunday i.e. 2000-01-02, otherwise it would be at 2000-01-01." + }, + "xperiodalignment": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the alignment of data points on the x axis.", + "default": "middle", + "enum": [ + "start", + "middle", + "end" + ] + }, + "yperiodalignment": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the alignment of data points on the y axis.", + "default": "middle", + "enum": [ + "start", + "middle", + "end" + ] + }, + "text": { + "type": "string", + "description": "Sets the text elements associated with each z value." + }, + "transpose": { + "type": "boolean", + "description": "Transposes the z data.", + "default": false + }, + "xtype": { + "type": "string", + "description": "If *array*, the heatmap's x coordinates are given by *x* (the default behavior when `x` is provided). If *scaled*, the heatmap's x coordinates are given by *x0* and *dx* (the default behavior when `x` is not provided).", + "enum": [ + "array", + "scaled" + ] + }, + "ytype": { + "type": "string", + "description": "If *array*, the heatmap's y coordinates are given by *y* (the default behavior when `y` is provided) If *scaled*, the heatmap's y coordinates are given by *y0* and *dy* (the default behavior when `y` is not provided)", + "enum": [ + "array", + "scaled" + ] + }, + "zhoverformat": { + "type": "string", + "description": "Sets the hover text formatting rule using d3 formatting mini-languages which are very similar to those in Python. See: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format", + "default": "" + }, + "hoverongaps": { + "type": "boolean", + "description": "Determines whether or not gaps (i.e. {nan} or missing values) in the `z` data have hover labels associated with them.", + "default": true + }, + "connectgaps": { + "type": "boolean", + "description": "Determines whether or not gaps (i.e. {nan} or missing values) in the `z` data are filled in. It is defaulted to true if `z` is a one dimensional array otherwise it is defaulted to false." + }, + "fillcolor": { + "type": "string", + "description": "Sets the fill color if `contours.type` is *constraint*. Defaults to a half-transparent variant of the line color, marker color, or marker line color, whichever is available." + }, + "autocontour": { + "type": "boolean", + "description": "Determines whether or not the contour level attributes are picked by an algorithm. If *true*, the number of contour levels can be set in `ncontours`. If *false*, set the contour level attributes in `contours`.", + "default": true + }, + "ncontours": { + "type": "integer", + "description": "Sets the maximum number of contour levels. The actual number of contours will be chosen automatically to be less than or equal to the value of `ncontours`. Has an effect only if `autocontour` is *true* or if `contours.size` is missing.", + "default": 15, + "minimum": 1 + }, + "contours": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "If `levels`, the data is represented as a contour plot with multiple levels displayed. If `constraint`, the data is represented as constraints with the invalid region shaded as specified by the `operation` and `value` parameters.", + "default": "levels", + "enum": [ + "levels", + "constraint" + ] + }, + "start": { + "type": "number", + "description": "Sets the starting contour level value. Must be less than `contours.end`", + "default": null + }, + "end": { + "type": "number", + "description": "Sets the end contour level value. Must be more than `contours.start`", + "default": null + }, + "size": { + "type": "number", + "description": "Sets the step between each contour level. Must be positive.", + "default": null, + "minimum": 0 + }, + "coloring": { + "type": "string", + "description": "Determines the coloring method showing the contour values. If *fill*, coloring is done evenly between each contour level If *heatmap*, a heatmap gradient coloring is applied between each contour level. If *lines*, coloring is done on the contour lines. If *none*, no coloring is applied on this trace.", + "default": "fill", + "enum": [ + "fill", + "heatmap", + "lines", + "none" + ] + }, + "showlines": { + "type": "boolean", + "description": "Determines whether or not the contour lines are drawn. Has an effect only if `contours.coloring` is set to *fill*.", + "default": true + }, + "showlabels": { + "type": "boolean", + "description": "Determines whether to label the contour lines with their values.", + "default": false + }, + "labelfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "labelformat": { + "type": "string", + "description": "Sets the contour label formatting rule using d3 formatting mini-language which is very similar to Python, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format", + "default": "" + }, + "operation": { + "type": "string", + "description": "Sets the constraint operation. *=* keeps regions equal to `value` *<* and *<=* keep regions less than `value` *>* and *>=* keep regions greater than `value` *[]*, *()*, *[)*, and *(]* keep regions inside `value[0]` to `value[1]` *][*, *)(*, *](*, *)[* keep regions outside `value[0]` to value[1]` Open vs. closed intervals make no difference to constraint display, but all versions are allowed for consistency with filter transforms.", + "default": "=", + "enum": [ + "=", + "<", + ">=", + ">", + "<=", + "[]", + "()", + "[)", + "(]", + "][", + ")(", + "](", + ")[" + ] + }, + "value": { + "type": "string", + "description": "Sets the value or values of the constraint boundary. When `operation` is set to one of the comparison values (=,<,>=,>,<=) *value* is expected to be a number. When `operation` is set to one of the interval values ([],(),[),(],][,)(,](,)[) *value* is expected to be an array of two numbers where the first is the lower bound and the second is the upper bound.", + "default": 0 + } + } + }, + "line": { + "type": "object", + "properties": { + "color": { + "type": "string", + "description": "Sets the color of the contour level. Has no effect if `contours.coloring` is set to *lines*." + }, + "width": { + "type": "number", + "description": "Sets the contour line width in (in px) Defaults to *0.5* when `contours.type` is *levels*. Defaults to *2* when `contour.type` is *constraint*.", + "minimum": 0 + }, + "dash": { + "type": "string", + "description": "Sets the dash style of lines. Set to a dash type string (*solid*, *dot*, *dash*, *longdash*, *dashdot*, or *longdashdot*) or a dash length list in px (eg *5px,10px,2px,2px*).", + "default": "solid" + }, + "smoothing": { + "type": "number", + "description": "Sets the amount of smoothing for the contour lines, where *0* corresponds to no smoothing.", + "default": 1, + "minimum": 0, + "maximum": 1.3 + } + } + }, + "zauto": { + "type": "boolean", + "description": "Determines whether or not the color domain is computed with respect to the input data (here in `z`) or the bounds set in `zmin` and `zmax` Defaults to `false` when `zmin` and `zmax` are set by the user.", + "default": true + }, + "zmin": { + "type": "number", + "description": "Sets the lower bound of the color domain. Value should have the same units as in `z` and if set, `zmax` must be set as well.", + "default": null + }, + "zmax": { + "type": "number", + "description": "Sets the upper bound of the color domain. Value should have the same units as in `z` and if set, `zmin` must be set as well.", + "default": null + }, + "zmid": { + "type": "number", + "description": "Sets the mid-point of the color domain by scaling `zmin` and/or `zmax` to be equidistant to this point. Value should have the same units as in `z`. Has no effect when `zauto` is `false`.", + "default": null + }, + "colorscale": { + "type": "string", + "description": "Sets the colorscale. The colorscale must be an array containing arrays mapping a normalized value to an rgb, rgba, hex, hsl, hsv, or named color string. At minimum, a mapping for the lowest (0) and highest (1) values are required. For example, `[[0, 'rgb(0,0,255)'], [1, 'rgb(255,0,0)']]`. To control the bounds of the colorscale in color space, use`zmin` and `zmax`. Alternatively, `colorscale` may be a palette name string of the following list: Greys,YlGnBu,Greens,YlOrRd,Bluered,RdBu,Reds,Blues,Picnic,Rainbow,Portland,Jet,Hot,Blackbody,Earth,Electric,Viridis,Cividis.", + "default": null + }, + "autocolorscale": { + "type": "boolean", + "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `colorscale`. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.", + "default": false + }, + "reversescale": { + "type": "boolean", + "description": "Reverses the color mapping if true. If true, `zmin` will correspond to the last color in the array and `zmax` will correspond to the first color.", + "default": false + }, + "showscale": { + "type": "boolean", + "description": "Determines whether or not a colorbar is displayed for this trace.", + "default": true + }, + "colorbar": { + "type": "object", + "properties": { + "thicknessmode": { + "type": "string", + "description": "Determines whether this color bar's thickness (i.e. the measure in the constant color direction) is set in units of plot *fraction* or in *pixels*. Use `thickness` to set the value.", + "default": "pixels", + "enum": [ + "fraction", + "pixels" + ] + }, + "thickness": { + "type": "number", + "description": "Sets the thickness of the color bar This measure excludes the size of the padding, ticks and labels.", + "default": 30, + "minimum": 0 + }, + "lenmode": { + "type": "string", + "description": "Determines whether this color bar's length (i.e. the measure in the color variation direction) is set in units of plot *fraction* or in *pixels. Use `len` to set the value.", + "default": "fraction", + "enum": [ + "fraction", + "pixels" + ] + }, + "len": { + "type": "number", + "description": "Sets the length of the color bar This measure excludes the padding of both ends. That is, the color bar length is this length minus the padding on both ends.", + "default": 1, + "minimum": 0 + }, + "x": { + "type": "number", + "description": "Sets the x position of the color bar (in plot fraction).", + "default": 1.02, + "minimum": -2, + "maximum": 3 + }, + "xanchor": { + "type": "string", + "description": "Sets this color bar's horizontal position anchor. This anchor binds the `x` position to the *left*, *center* or *right* of the color bar.", + "default": "left", + "enum": [ + "left", + "center", + "right" + ] + }, + "xpad": { + "type": "number", + "description": "Sets the amount of padding (in px) along the x direction.", + "default": 10, + "minimum": 0 + }, + "y": { + "type": "number", + "description": "Sets the y position of the color bar (in plot fraction).", + "default": 0.5, + "minimum": -2, + "maximum": 3 + }, + "yanchor": { + "type": "string", + "description": "Sets this color bar's vertical position anchor This anchor binds the `y` position to the *top*, *middle* or *bottom* of the color bar.", + "default": "middle", + "enum": [ + "top", + "middle", + "bottom" + ] + }, + "ypad": { + "type": "number", + "description": "Sets the amount of padding (in px) along the y direction.", + "default": 10, + "minimum": 0 + }, + "outlinecolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "outlinewidth": { + "type": "number", + "description": "Sets the width (in px) of the axis line.", + "default": 1, + "minimum": 0 + }, + "bordercolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "borderwidth": { + "type": "number", + "description": "Sets the width (in px) or the border enclosing this color bar.", + "default": 0, + "minimum": 0 + }, + "bgcolor": { + "type": "string", + "description": "Sets the color of padded area.", + "default": "rgba(0,0,0,0)" + }, + "tickmode": { + "type": "string", + "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).", + "enum": [ + "auto", + "linear", + "array" + ] + }, + "nticks": { + "type": "integer", + "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", + "default": 0, + "minimum": 0 + }, + "tick0": { + "type": "string", + "description": "Sets the placement of the first tick on this axis. Use with `dtick`. If the axis `type` is *log*, then you must take the log of your starting tick (e.g. to set the starting tick to 100, set the `tick0` to 2) except when `dtick`=*L* (see `dtick` for more info). If the axis `type` is *date*, it should be a date string, like date data. If the axis `type` is *category*, it should be a number, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "dtick": { + "type": "string", + "description": "Sets the step in-between ticks on this axis. Use with `tick0`. Must be a positive number, or special strings available to *log* and *date* axes. If the axis `type` is *log*, then ticks are set every 10^(n*dtick) where n is the tick number. For example, to set a tick mark at 1, 10, 100, 1000, ... set dtick to 1. To set tick marks at 1, 100, 10000, ... set dtick to 2. To set tick marks at 1, 5, 25, 125, 625, 3125, ... set dtick to log_10(5), or 0.69897000433. *log* has several special values; *L*, where `f` is a positive number, gives ticks linearly spaced in value (but not position). For example `tick0` = 0.1, `dtick` = *L0.5* will put ticks at 0.1, 0.6, 1.1, 1.6 etc. To show powers of 10 plus small digits between, use *D1* (all digits) or *D2* (only 2 and 5). `tick0` is ignored for *D1* and *D2*. If the axis `type` is *date*, then you must convert the time to milliseconds. For example, to set the interval between ticks to one day, set `dtick` to 86400000.0. *date* also has special values *M* gives ticks spaced by a number of months. `n` must be a positive integer. To set ticks on the 15th of every third month, set `tick0` to *2000-01-15* and `dtick` to *M3*. To set ticks every 4 years, set `dtick` to *M48*" + }, + "tickvals": { + "type": "string", + "description": "Sets the values at which ticks on this axis appear. Only has an effect if `tickmode` is set to *array*. Used with `ticktext`." + }, + "ticktext": { + "type": "string", + "description": "Sets the text displayed at the ticks position via `tickvals`. Only has an effect if `tickmode` is set to *array*. Used with `tickvals`." + }, + "ticks": { + "type": "string", + "description": "Determines whether ticks are drawn or not. If **, this axis' ticks are not drawn. If *outside* (*inside*), this axis' are drawn outside (inside) the axis lines.", + "default": "", + "enum": [ + "outside", + "inside", + "" + ] + }, + "ticklabelposition": { + "type": "string", + "description": "Determines where tick labels are drawn.", + "default": "outside", + "enum": [ + "outside", + "inside", + "outside top", + "inside top", + "outside bottom", + "inside bottom" + ] + }, + "ticklen": { + "type": "number", + "description": "Sets the tick length (in px).", + "default": 5, + "minimum": 0 + }, + "tickwidth": { + "type": "number", + "description": "Sets the tick width (in px).", + "default": 1, + "minimum": 0 + }, + "tickcolor": { + "type": "string", + "description": "Sets the tick color.", + "default": "#444" + }, + "showticklabels": { + "type": "boolean", + "description": "Determines whether or not the tick labels are drawn.", + "default": true + }, + "tickfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "tickangle": { + "type": "number", + "description": "Sets the angle of the tick labels with respect to the horizontal. For example, a `tickangle` of -90 draws the tick labels vertically.", + "default": "auto", + "minimum": -180, + "maximum": 180 + }, + "tickformat": { + "type": "string", + "description": "Sets the tick label formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "tickprefix": { + "type": "string", + "description": "Sets a tick label prefix.", + "default": "" + }, + "showtickprefix": { + "type": "string", + "description": "If *all*, all tick labels are displayed with a prefix. If *first*, only the first tick is displayed with a prefix. If *last*, only the last tick is displayed with a suffix. If *none*, tick prefixes are hidden.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "ticksuffix": { + "type": "string", + "description": "Sets a tick label suffix.", + "default": "" + }, + "showticksuffix": { + "type": "string", + "description": "Same as `showtickprefix` but for tick suffixes.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "separatethousands": { + "type": "boolean", + "description": "If \"true\", even 4-digit integers are separated", + "default": false + }, + "exponentformat": { + "type": "string", + "description": "Determines a formatting rule for the tick exponents. For example, consider the number 1,000,000,000. If *none*, it appears as 1,000,000,000. If *e*, 1e+9. If *E*, 1E+9. If *power*, 1x10^9 (with 9 in a super script). If *SI*, 1G. If *B*, 1B.", + "default": "B", + "enum": [ + "none", + "e", + "E", + "power", + "SI", + "B" + ] + }, + "minexponent": { + "type": "number", + "description": "Hide SI prefix for 10^n if |n| is below this number. This only has an effect when `tickformat` is *SI* or *B*.", + "default": 3, + "minimum": 0 + }, + "showexponent": { + "type": "string", + "description": "If *all*, all exponents are shown besides their significands. If *first*, only the exponent of the first tick is shown. If *last*, only the exponent of the last tick is shown. If *none*, no exponents appear.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "title": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Sets the title of the color bar. Note that before the existence of `title.text`, the title's contents used to be defined as the `title` attribute itself. This behavior has been deprecated." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "side": { + "type": "string", + "description": "Determines the location of color bar's title with respect to the color bar. Note that the title's location used to be set by the now deprecated `titleside` attribute.", + "default": "top", + "enum": [ + "right", + "top", + "bottom" + ] + } + } + } + } + }, + "coloraxis": { + "type": "string", + "description": "Sets a reference to a shared color axis. References to these shared color axes are *coloraxis*, *coloraxis2*, *coloraxis3*, etc. Settings for these shared color axes are set in the layout, under `layout.coloraxis`, `layout.coloraxis2`, etc. Note that multiple color scales can be linked to the same color axis.", + "default": null + }, + "xcalendar": { + "type": "string", + "description": "Sets the calendar system to use with `x` date data.", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + }, + "ycalendar": { + "type": "string", + "description": "Sets the calendar system to use with `y` date data.", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + }, + "xaxis": { + "type": "string", + "description": "Sets a reference between this trace's x coordinates and a 2D cartesian x axis. If *x* (the default value), the x coordinates refer to `layout.xaxis`. If *x2*, the x coordinates refer to `layout.xaxis2`, and so on.", + "default": "x" + }, + "yaxis": { + "type": "string", + "description": "Sets a reference between this trace's y coordinates and a 2D cartesian y axis. If *y* (the default value), the y coordinates refer to `layout.yaxis`. If *y2*, the y coordinates refer to `layout.yaxis2`, and so on.", + "default": "y" + } + } +} \ No newline at end of file diff --git a/escalation/plotly_api/heatmap_plotly_schema.json b/escalation/plotly_api/heatmap_plotly_schema.json new file mode 100644 index 0000000..d0ee02d --- /dev/null +++ b/escalation/plotly_api/heatmap_plotly_schema.json @@ -0,0 +1,679 @@ +{ + "attributes": { + "visible": { + "type": "string", + "description": "Determines whether or not this trace is visible. If *legendonly*, the trace is not drawn, but can appear as a legend item (provided that the legend itself is visible).", + "default": true, + "enum": [ + true, + false, + "legendonly" + ] + }, + "legendgroup": { + "type": "string", + "description": "Sets the legend group for this trace. Traces part of the same legend group hide/show at the same time when toggling legend items.", + "default": "" + }, + "opacity": { + "type": "number", + "description": "Sets the opacity of the trace.", + "default": 1, + "minimum": 0, + "maximum": 1 + }, + "name": { + "type": "string", + "description": "Sets the trace name. The trace name appear as the legend item and on hover." + }, + "customdata": { + "type": "string", + "description": "Assigns extra data each datum. This may be useful when listening to hover, click and selection events. Note that, *scatter* traces also appends customdata items in the markers DOM elements" + }, + "meta": { + "type": "string", + "description": "Assigns extra meta information associated with this trace that can be used in various text attributes. Attributes such as trace `name`, graph, axis and colorbar `title.text`, annotation `text` `rangeselector`, `updatemenues` and `sliders` `label` text all support `meta`. To access the trace `meta` values in an attribute in the same trace, simply use `%{meta[i]}` where `i` is the index or key of the `meta` item in question. To access trace `meta` in layout attributes, use `%{data[n[.meta[i]}` where `i` is the index or key of the `meta` and `n` is the trace index." + }, + "hoverinfo": { + "type": "string", + "description": "Determines which trace information appear on hover. If `none` or `skip` are set, no information is displayed upon hovering. But, if `none` is set, click and hover events are still fired.", + "default": "all", + "enum": [ + "name", + "text", + "text+name", + "z", + "z+name", + "z+text", + "z+text+name", + "y", + "y+name", + "y+text", + "y+text+name", + "y+z", + "y+z+name", + "y+z+text", + "y+z+text+name", + "x", + "x+name", + "x+text", + "x+text+name", + "x+z", + "x+z+name", + "x+z+text", + "x+z+text+name", + "x+y", + "x+y+name", + "x+y+text", + "x+y+text+name", + "x+y+z", + "x+y+z+name", + "x+y+z+text", + "x+y+z+text+name", + "all", + "none", + "skip" + ] + }, + "hoverlabel": { + "type": "object", + "properties": { + "bgcolor": { + "type": "string", + "description": "Sets the background color of the hover labels for this trace" + }, + "bordercolor": { + "type": "string", + "description": "Sets the border color of the hover labels for this trace." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "align": { + "type": "string", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "default": "auto", + "enum": [ + "left", + "right", + "auto" + ] + }, + "namelength": { + "type": "integer", + "description": "Sets the default length (in number of characters) of the trace name in the hover labels for all traces. -1 shows the whole name regardless of length. 0-3 shows the first 0-3 characters, and an integer >3 will show the whole name if it is less than that many characters, but if it is longer, will truncate to `namelength - 3` characters and add an ellipsis.", + "default": 15, + "minimum": -1 + } + } + }, + "uirevision": { + "type": "string", + "description": "Controls persistence of some user-driven changes to the trace: `constraintrange` in `parcoords` traces, as well as some `editable: true` modifications such as `name` and `colorbar.title`. Defaults to `layout.uirevision`. Note that other user-driven trace attribute changes are controlled by `layout` attributes: `trace.visible` is controlled by `layout.legend.uirevision`, `selectedpoints` is controlled by `layout.selectionrevision`, and `colorbar.(x|y)` (accessible with `config: {editable: true}`) is controlled by `layout.editrevision`. Trace changes are tracked by `uid`, which only falls back on trace index if no `uid` is provided. So if your app can add/remove traces before the end of the `data` array, such that the same trace has a different index, you can still preserve user-driven changes if you give each trace a `uid` that stays with it as it moves." + }, + "z": { + "type": "string", + "description": "Sets the z data." + }, + "x": { + "type": "string", + "description": "Sets the x coordinates." + }, + "x0": { + "type": "string", + "description": "Alternate to `x`. Builds a linear space of x coordinates. Use with `dx` where `x0` is the starting coordinate and `dx` the step.", + "default": 0 + }, + "dx": { + "type": "number", + "description": "Sets the x coordinate step. See `x0` for more info.", + "default": 1 + }, + "y": { + "type": "string", + "description": "Sets the y coordinates." + }, + "y0": { + "type": "string", + "description": "Alternate to `y`. Builds a linear space of y coordinates. Use with `dy` where `y0` is the starting coordinate and `dy` the step.", + "default": 0 + }, + "dy": { + "type": "number", + "description": "Sets the y coordinate step. See `y0` for more info.", + "default": 1 + }, + "xperiod": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the period positioning in milliseconds or *M* on the x axis. Special values in the form of *M* could be used to declare the number of months. In this case `n` must be a positive integer.", + "default": 0 + }, + "yperiod": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the period positioning in milliseconds or *M* on the y axis. Special values in the form of *M* could be used to declare the number of months. In this case `n` must be a positive integer.", + "default": 0 + }, + "xperiod0": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the base for period positioning in milliseconds or date string on the x0 axis. When `x0period` is round number of weeks, the `x0period0` by default would be on a Sunday i.e. 2000-01-02, otherwise it would be at 2000-01-01." + }, + "yperiod0": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the base for period positioning in milliseconds or date string on the y0 axis. When `y0period` is round number of weeks, the `y0period0` by default would be on a Sunday i.e. 2000-01-02, otherwise it would be at 2000-01-01." + }, + "xperiodalignment": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the alignment of data points on the x axis.", + "default": "middle", + "enum": [ + "start", + "middle", + "end" + ] + }, + "yperiodalignment": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the alignment of data points on the y axis.", + "default": "middle", + "enum": [ + "start", + "middle", + "end" + ] + }, + "text": { + "type": "string", + "description": "Sets the text elements associated with each z value." + }, + "transpose": { + "type": "boolean", + "description": "Transposes the z data.", + "default": false + }, + "xtype": { + "type": "string", + "description": "If *array*, the heatmap's x coordinates are given by *x* (the default behavior when `x` is provided). If *scaled*, the heatmap's x coordinates are given by *x0* and *dx* (the default behavior when `x` is not provided).", + "enum": [ + "array", + "scaled" + ] + }, + "ytype": { + "type": "string", + "description": "If *array*, the heatmap's y coordinates are given by *y* (the default behavior when `y` is provided) If *scaled*, the heatmap's y coordinates are given by *y0* and *dy* (the default behavior when `y` is not provided)", + "enum": [ + "array", + "scaled" + ] + }, + "zsmooth": { + "type": "string", + "description": "Picks a smoothing algorithm use to smooth `z` data.", + "default": false, + "enum": [ + "fast", + "best", + false + ] + }, + "hoverongaps": { + "type": "boolean", + "description": "Determines whether or not gaps (i.e. {nan} or missing values) in the `z` data have hover labels associated with them.", + "default": true + }, + "connectgaps": { + "type": "boolean", + "description": "Determines whether or not gaps (i.e. {nan} or missing values) in the `z` data are filled in. It is defaulted to true if `z` is a one dimensional array and `zsmooth` is not false; otherwise it is defaulted to false." + }, + "xgap": { + "type": "number", + "description": "Sets the horizontal gap (in pixels) between bricks.", + "default": 0, + "minimum": 0 + }, + "ygap": { + "type": "number", + "description": "Sets the vertical gap (in pixels) between bricks.", + "default": 0, + "minimum": 0 + }, + "zhoverformat": { + "type": "string", + "description": "Sets the hover text formatting rule using d3 formatting mini-languages which are very similar to those in Python. See: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format", + "default": "" + }, + "showlegend": { + "type": "boolean", + "description": "Determines whether or not an item corresponding to this trace is shown in the legend.", + "default": false + }, + "zauto": { + "type": "boolean", + "description": "Determines whether or not the color domain is computed with respect to the input data (here in `z`) or the bounds set in `zmin` and `zmax` Defaults to `false` when `zmin` and `zmax` are set by the user.", + "default": true + }, + "zmin": { + "type": "number", + "description": "Sets the lower bound of the color domain. Value should have the same units as in `z` and if set, `zmax` must be set as well.", + "default": null + }, + "zmax": { + "type": "number", + "description": "Sets the upper bound of the color domain. Value should have the same units as in `z` and if set, `zmin` must be set as well.", + "default": null + }, + "zmid": { + "type": "number", + "description": "Sets the mid-point of the color domain by scaling `zmin` and/or `zmax` to be equidistant to this point. Value should have the same units as in `z`. Has no effect when `zauto` is `false`.", + "default": null + }, + "colorscale": { + "type": "string", + "description": "Sets the colorscale. The colorscale must be an array containing arrays mapping a normalized value to an rgb, rgba, hex, hsl, hsv, or named color string. At minimum, a mapping for the lowest (0) and highest (1) values are required. For example, `[[0, 'rgb(0,0,255)'], [1, 'rgb(255,0,0)']]`. To control the bounds of the colorscale in color space, use`zmin` and `zmax`. Alternatively, `colorscale` may be a palette name string of the following list: Greys,YlGnBu,Greens,YlOrRd,Bluered,RdBu,Reds,Blues,Picnic,Rainbow,Portland,Jet,Hot,Blackbody,Earth,Electric,Viridis,Cividis.", + "default": null + }, + "autocolorscale": { + "type": "boolean", + "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `colorscale`. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.", + "default": false + }, + "reversescale": { + "type": "boolean", + "description": "Reverses the color mapping if true. If true, `zmin` will correspond to the last color in the array and `zmax` will correspond to the first color.", + "default": false + }, + "showscale": { + "type": "boolean", + "description": "Determines whether or not a colorbar is displayed for this trace.", + "default": true + }, + "colorbar": { + "type": "object", + "properties": { + "thicknessmode": { + "type": "string", + "description": "Determines whether this color bar's thickness (i.e. the measure in the constant color direction) is set in units of plot *fraction* or in *pixels*. Use `thickness` to set the value.", + "default": "pixels", + "enum": [ + "fraction", + "pixels" + ] + }, + "thickness": { + "type": "number", + "description": "Sets the thickness of the color bar This measure excludes the size of the padding, ticks and labels.", + "default": 30, + "minimum": 0 + }, + "lenmode": { + "type": "string", + "description": "Determines whether this color bar's length (i.e. the measure in the color variation direction) is set in units of plot *fraction* or in *pixels. Use `len` to set the value.", + "default": "fraction", + "enum": [ + "fraction", + "pixels" + ] + }, + "len": { + "type": "number", + "description": "Sets the length of the color bar This measure excludes the padding of both ends. That is, the color bar length is this length minus the padding on both ends.", + "default": 1, + "minimum": 0 + }, + "x": { + "type": "number", + "description": "Sets the x position of the color bar (in plot fraction).", + "default": 1.02, + "minimum": -2, + "maximum": 3 + }, + "xanchor": { + "type": "string", + "description": "Sets this color bar's horizontal position anchor. This anchor binds the `x` position to the *left*, *center* or *right* of the color bar.", + "default": "left", + "enum": [ + "left", + "center", + "right" + ] + }, + "xpad": { + "type": "number", + "description": "Sets the amount of padding (in px) along the x direction.", + "default": 10, + "minimum": 0 + }, + "y": { + "type": "number", + "description": "Sets the y position of the color bar (in plot fraction).", + "default": 0.5, + "minimum": -2, + "maximum": 3 + }, + "yanchor": { + "type": "string", + "description": "Sets this color bar's vertical position anchor This anchor binds the `y` position to the *top*, *middle* or *bottom* of the color bar.", + "default": "middle", + "enum": [ + "top", + "middle", + "bottom" + ] + }, + "ypad": { + "type": "number", + "description": "Sets the amount of padding (in px) along the y direction.", + "default": 10, + "minimum": 0 + }, + "outlinecolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "outlinewidth": { + "type": "number", + "description": "Sets the width (in px) of the axis line.", + "default": 1, + "minimum": 0 + }, + "bordercolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "borderwidth": { + "type": "number", + "description": "Sets the width (in px) or the border enclosing this color bar.", + "default": 0, + "minimum": 0 + }, + "bgcolor": { + "type": "string", + "description": "Sets the color of padded area.", + "default": "rgba(0,0,0,0)" + }, + "tickmode": { + "type": "string", + "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).", + "enum": [ + "auto", + "linear", + "array" + ] + }, + "nticks": { + "type": "integer", + "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", + "default": 0, + "minimum": 0 + }, + "tick0": { + "type": "string", + "description": "Sets the placement of the first tick on this axis. Use with `dtick`. If the axis `type` is *log*, then you must take the log of your starting tick (e.g. to set the starting tick to 100, set the `tick0` to 2) except when `dtick`=*L* (see `dtick` for more info). If the axis `type` is *date*, it should be a date string, like date data. If the axis `type` is *category*, it should be a number, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "dtick": { + "type": "string", + "description": "Sets the step in-between ticks on this axis. Use with `tick0`. Must be a positive number, or special strings available to *log* and *date* axes. If the axis `type` is *log*, then ticks are set every 10^(n*dtick) where n is the tick number. For example, to set a tick mark at 1, 10, 100, 1000, ... set dtick to 1. To set tick marks at 1, 100, 10000, ... set dtick to 2. To set tick marks at 1, 5, 25, 125, 625, 3125, ... set dtick to log_10(5), or 0.69897000433. *log* has several special values; *L*, where `f` is a positive number, gives ticks linearly spaced in value (but not position). For example `tick0` = 0.1, `dtick` = *L0.5* will put ticks at 0.1, 0.6, 1.1, 1.6 etc. To show powers of 10 plus small digits between, use *D1* (all digits) or *D2* (only 2 and 5). `tick0` is ignored for *D1* and *D2*. If the axis `type` is *date*, then you must convert the time to milliseconds. For example, to set the interval between ticks to one day, set `dtick` to 86400000.0. *date* also has special values *M* gives ticks spaced by a number of months. `n` must be a positive integer. To set ticks on the 15th of every third month, set `tick0` to *2000-01-15* and `dtick` to *M3*. To set ticks every 4 years, set `dtick` to *M48*" + }, + "tickvals": { + "type": "string", + "description": "Sets the values at which ticks on this axis appear. Only has an effect if `tickmode` is set to *array*. Used with `ticktext`." + }, + "ticktext": { + "type": "string", + "description": "Sets the text displayed at the ticks position via `tickvals`. Only has an effect if `tickmode` is set to *array*. Used with `tickvals`." + }, + "ticks": { + "type": "string", + "description": "Determines whether ticks are drawn or not. If **, this axis' ticks are not drawn. If *outside* (*inside*), this axis' are drawn outside (inside) the axis lines.", + "default": "", + "enum": [ + "outside", + "inside", + "" + ] + }, + "ticklabelposition": { + "type": "string", + "description": "Determines where tick labels are drawn.", + "default": "outside", + "enum": [ + "outside", + "inside", + "outside top", + "inside top", + "outside bottom", + "inside bottom" + ] + }, + "ticklen": { + "type": "number", + "description": "Sets the tick length (in px).", + "default": 5, + "minimum": 0 + }, + "tickwidth": { + "type": "number", + "description": "Sets the tick width (in px).", + "default": 1, + "minimum": 0 + }, + "tickcolor": { + "type": "string", + "description": "Sets the tick color.", + "default": "#444" + }, + "showticklabels": { + "type": "boolean", + "description": "Determines whether or not the tick labels are drawn.", + "default": true + }, + "tickfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "tickangle": { + "type": "number", + "description": "Sets the angle of the tick labels with respect to the horizontal. For example, a `tickangle` of -90 draws the tick labels vertically.", + "default": "auto", + "minimum": -180, + "maximum": 180 + }, + "tickformat": { + "type": "string", + "description": "Sets the tick label formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "tickprefix": { + "type": "string", + "description": "Sets a tick label prefix.", + "default": "" + }, + "showtickprefix": { + "type": "string", + "description": "If *all*, all tick labels are displayed with a prefix. If *first*, only the first tick is displayed with a prefix. If *last*, only the last tick is displayed with a suffix. If *none*, tick prefixes are hidden.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "ticksuffix": { + "type": "string", + "description": "Sets a tick label suffix.", + "default": "" + }, + "showticksuffix": { + "type": "string", + "description": "Same as `showtickprefix` but for tick suffixes.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "separatethousands": { + "type": "boolean", + "description": "If \"true\", even 4-digit integers are separated", + "default": false + }, + "exponentformat": { + "type": "string", + "description": "Determines a formatting rule for the tick exponents. For example, consider the number 1,000,000,000. If *none*, it appears as 1,000,000,000. If *e*, 1e+9. If *E*, 1E+9. If *power*, 1x10^9 (with 9 in a super script). If *SI*, 1G. If *B*, 1B.", + "default": "B", + "enum": [ + "none", + "e", + "E", + "power", + "SI", + "B" + ] + }, + "minexponent": { + "type": "number", + "description": "Hide SI prefix for 10^n if |n| is below this number. This only has an effect when `tickformat` is *SI* or *B*.", + "default": 3, + "minimum": 0 + }, + "showexponent": { + "type": "string", + "description": "If *all*, all exponents are shown besides their significands. If *first*, only the exponent of the first tick is shown. If *last*, only the exponent of the last tick is shown. If *none*, no exponents appear.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "title": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Sets the title of the color bar. Note that before the existence of `title.text`, the title's contents used to be defined as the `title` attribute itself. This behavior has been deprecated." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "side": { + "type": "string", + "description": "Determines the location of color bar's title with respect to the color bar. Note that the title's location used to be set by the now deprecated `titleside` attribute.", + "default": "top", + "enum": [ + "right", + "top", + "bottom" + ] + } + } + } + } + }, + "coloraxis": { + "type": "string", + "description": "Sets a reference to a shared color axis. References to these shared color axes are *coloraxis*, *coloraxis2*, *coloraxis3*, etc. Settings for these shared color axes are set in the layout, under `layout.coloraxis`, `layout.coloraxis2`, etc. Note that multiple color scales can be linked to the same color axis.", + "default": null + }, + "xcalendar": { + "type": "string", + "description": "Sets the calendar system to use with `x` date data.", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + }, + "ycalendar": { + "type": "string", + "description": "Sets the calendar system to use with `y` date data.", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + }, + "xaxis": { + "type": "string", + "description": "Sets a reference between this trace's x coordinates and a 2D cartesian x axis. If *x* (the default value), the x coordinates refer to `layout.xaxis`. If *x2*, the x coordinates refer to `layout.xaxis2`, and so on.", + "default": "x" + }, + "yaxis": { + "type": "string", + "description": "Sets a reference between this trace's y coordinates and a 2D cartesian y axis. If *y* (the default value), the y coordinates refer to `layout.yaxis`. If *y2*, the y coordinates refer to `layout.yaxis2`, and so on.", + "default": "y" + } + } +} \ No newline at end of file diff --git a/escalation/plotly_api/histogram2d_plotly_schema.json b/escalation/plotly_api/histogram2d_plotly_schema.json new file mode 100644 index 0000000..dba7ff7 --- /dev/null +++ b/escalation/plotly_api/histogram2d_plotly_schema.json @@ -0,0 +1,691 @@ +{ + "attributes": { + "visible": { + "type": "string", + "description": "Determines whether or not this trace is visible. If *legendonly*, the trace is not drawn, but can appear as a legend item (provided that the legend itself is visible).", + "default": true, + "enum": [ + true, + false, + "legendonly" + ] + }, + "legendgroup": { + "type": "string", + "description": "Sets the legend group for this trace. Traces part of the same legend group hide/show at the same time when toggling legend items.", + "default": "" + }, + "opacity": { + "type": "number", + "description": "Sets the opacity of the trace.", + "default": 1, + "minimum": 0, + "maximum": 1 + }, + "name": { + "type": "string", + "description": "Sets the trace name. The trace name appear as the legend item and on hover." + }, + "customdata": { + "type": "string", + "description": "Assigns extra data each datum. This may be useful when listening to hover, click and selection events. Note that, *scatter* traces also appends customdata items in the markers DOM elements" + }, + "meta": { + "type": "string", + "description": "Assigns extra meta information associated with this trace that can be used in various text attributes. Attributes such as trace `name`, graph, axis and colorbar `title.text`, annotation `text` `rangeselector`, `updatemenues` and `sliders` `label` text all support `meta`. To access the trace `meta` values in an attribute in the same trace, simply use `%{meta[i]}` where `i` is the index or key of the `meta` item in question. To access trace `meta` in layout attributes, use `%{data[n[.meta[i]}` where `i` is the index or key of the `meta` and `n` is the trace index." + }, + "hoverinfo": { + "type": "string", + "description": "Determines which trace information appear on hover. If `none` or `skip` are set, no information is displayed upon hovering. But, if `none` is set, click and hover events are still fired.", + "default": "all", + "enum": [ + "name", + "text", + "text+name", + "z", + "z+name", + "z+text", + "z+text+name", + "y", + "y+name", + "y+text", + "y+text+name", + "y+z", + "y+z+name", + "y+z+text", + "y+z+text+name", + "x", + "x+name", + "x+text", + "x+text+name", + "x+z", + "x+z+name", + "x+z+text", + "x+z+text+name", + "x+y", + "x+y+name", + "x+y+text", + "x+y+text+name", + "x+y+z", + "x+y+z+name", + "x+y+z+text", + "x+y+z+text+name", + "all", + "none", + "skip" + ] + }, + "hoverlabel": { + "type": "object", + "properties": { + "bgcolor": { + "type": "string", + "description": "Sets the background color of the hover labels for this trace" + }, + "bordercolor": { + "type": "string", + "description": "Sets the border color of the hover labels for this trace." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "align": { + "type": "string", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "default": "auto", + "enum": [ + "left", + "right", + "auto" + ] + }, + "namelength": { + "type": "integer", + "description": "Sets the default length (in number of characters) of the trace name in the hover labels for all traces. -1 shows the whole name regardless of length. 0-3 shows the first 0-3 characters, and an integer >3 will show the whole name if it is less than that many characters, but if it is longer, will truncate to `namelength - 3` characters and add an ellipsis.", + "default": 15, + "minimum": -1 + } + } + }, + "uirevision": { + "type": "string", + "description": "Controls persistence of some user-driven changes to the trace: `constraintrange` in `parcoords` traces, as well as some `editable: true` modifications such as `name` and `colorbar.title`. Defaults to `layout.uirevision`. Note that other user-driven trace attribute changes are controlled by `layout` attributes: `trace.visible` is controlled by `layout.legend.uirevision`, `selectedpoints` is controlled by `layout.selectionrevision`, and `colorbar.(x|y)` (accessible with `config: {editable: true}`) is controlled by `layout.editrevision`. Trace changes are tracked by `uid`, which only falls back on trace index if no `uid` is provided. So if your app can add/remove traces before the end of the `data` array, such that the same trace has a different index, you can still preserve user-driven changes if you give each trace a `uid` that stays with it as it moves." + }, + "x": { + "type": "string", + "description": "Sets the sample data to be binned on the x axis." + }, + "y": { + "type": "string", + "description": "Sets the sample data to be binned on the y axis." + }, + "z": { + "type": "string", + "description": "Sets the aggregation data." + }, + "marker": { + "type": "object", + "properties": { + "color": { + "type": "string", + "description": "Sets the aggregation data." + } + } + }, + "histnorm": { + "type": "string", + "description": "Specifies the type of normalization used for this histogram trace. If **, the span of each bar corresponds to the number of occurrences (i.e. the number of data points lying inside the bins). If *percent* / *probability*, the span of each bar corresponds to the percentage / fraction of occurrences with respect to the total number of sample points (here, the sum of all bin HEIGHTS equals 100% / 1). If *density*, the span of each bar corresponds to the number of occurrences in a bin divided by the size of the bin interval (here, the sum of all bin AREAS equals the total number of sample points). If *probability density*, the area of each bar corresponds to the probability that an event will fall into the corresponding bin (here, the sum of all bin AREAS equals 1).", + "default": "", + "enum": [ + "", + "percent", + "probability", + "density", + "probability density" + ] + }, + "histfunc": { + "type": "string", + "description": "Specifies the binning function used for this histogram trace. If *count*, the histogram values are computed by counting the number of values lying inside each bin. If *sum*, *avg*, *min*, *max*, the histogram values are computed using the sum, the average, the minimum or the maximum of the values lying inside each bin respectively.", + "default": "count", + "enum": [ + "count", + "sum", + "avg", + "min", + "max" + ] + }, + "nbinsx": { + "type": "integer", + "description": "Specifies the maximum number of desired bins. This value will be used in an algorithm that will decide the optimal bin size such that the histogram best visualizes the distribution of the data. Ignored if `xbins.size` is provided.", + "default": 0, + "minimum": 0 + }, + "xbins": { + "type": "object", + "properties": { + "start": { + "type": "string", + "description": "Sets the starting value for the x axis bins. Defaults to the minimum data value, shifted down if necessary to make nice round values and to remove ambiguous bin edges. For example, if most of the data is integers we shift the bin edges 0.5 down, so a `size` of 5 would have a default `start` of -0.5, so it is clear that 0-4 are in the first bin, 5-9 in the second, but continuous data gets a start of 0 and bins [0,5), [5,10) etc. Dates behave similarly, and `start` should be a date string. For category data, `start` is based on the category serial numbers, and defaults to -0.5. " + }, + "end": { + "type": "string", + "description": "Sets the end value for the x axis bins. The last bin may not end exactly at this value, we increment the bin edge by `size` from `start` until we reach or exceed `end`. Defaults to the maximum data value. Like `start`, for dates use a date string, and for category data `end` is based on the category serial numbers." + }, + "size": { + "type": "string", + "description": "Sets the size of each x axis bin. Default behavior: If `nbinsx` is 0 or omitted, we choose a nice round bin size such that the number of bins is about the same as the typical number of samples in each bin. If `nbinsx` is provided, we choose a nice round bin size giving no more than that many bins. For date data, use milliseconds or *M* for months, as in `axis.dtick`. For category data, the number of categories to bin together (always defaults to 1). " + } + } + }, + "nbinsy": { + "type": "integer", + "description": "Specifies the maximum number of desired bins. This value will be used in an algorithm that will decide the optimal bin size such that the histogram best visualizes the distribution of the data. Ignored if `ybins.size` is provided.", + "default": 0, + "minimum": 0 + }, + "ybins": { + "type": "object", + "properties": { + "start": { + "type": "string", + "description": "Sets the starting value for the y axis bins. Defaults to the minimum data value, shifted down if necessary to make nice round values and to remove ambiguous bin edges. For example, if most of the data is integers we shift the bin edges 0.5 down, so a `size` of 5 would have a default `start` of -0.5, so it is clear that 0-4 are in the first bin, 5-9 in the second, but continuous data gets a start of 0 and bins [0,5), [5,10) etc. Dates behave similarly, and `start` should be a date string. For category data, `start` is based on the category serial numbers, and defaults to -0.5. " + }, + "end": { + "type": "string", + "description": "Sets the end value for the y axis bins. The last bin may not end exactly at this value, we increment the bin edge by `size` from `start` until we reach or exceed `end`. Defaults to the maximum data value. Like `start`, for dates use a date string, and for category data `end` is based on the category serial numbers." + }, + "size": { + "type": "string", + "description": "Sets the size of each y axis bin. Default behavior: If `nbinsy` is 0 or omitted, we choose a nice round bin size such that the number of bins is about the same as the typical number of samples in each bin. If `nbinsy` is provided, we choose a nice round bin size giving no more than that many bins. For date data, use milliseconds or *M* for months, as in `axis.dtick`. For category data, the number of categories to bin together (always defaults to 1). " + } + } + }, + "autobinx": { + "type": "boolean", + "description": "Obsolete: since v1.42 each bin attribute is auto-determined separately and `autobinx` is not needed. However, we accept `autobinx: true` or `false` and will update `xbins` accordingly before deleting `autobinx` from the trace.", + "default": null + }, + "autobiny": { + "type": "boolean", + "description": "Obsolete: since v1.42 each bin attribute is auto-determined separately and `autobiny` is not needed. However, we accept `autobiny: true` or `false` and will update `ybins` accordingly before deleting `autobiny` from the trace.", + "default": null + }, + "bingroup": { + "type": "string", + "description": "Set the `xbingroup` and `ybingroup` default prefix For example, setting a `bingroup` of *1* on two histogram2d traces will make them their x-bins and y-bins match separately.", + "default": "" + }, + "xbingroup": { + "type": "string", + "description": "Set a group of histogram traces which will have compatible x-bin settings. Using `xbingroup`, histogram2d and histogram2dcontour traces (on axes of the same axis type) can have compatible x-bin settings. Note that the same `xbingroup` value can be used to set (1D) histogram `bingroup`", + "default": "" + }, + "ybingroup": { + "type": "string", + "description": "Set a group of histogram traces which will have compatible y-bin settings. Using `ybingroup`, histogram2d and histogram2dcontour traces (on axes of the same axis type) can have compatible y-bin settings. Note that the same `ybingroup` value can be used to set (1D) histogram `bingroup`", + "default": "" + }, + "xgap": { + "type": "number", + "description": "Sets the horizontal gap (in pixels) between bricks.", + "default": 0, + "minimum": 0 + }, + "ygap": { + "type": "number", + "description": "Sets the vertical gap (in pixels) between bricks.", + "default": 0, + "minimum": 0 + }, + "zsmooth": { + "type": "string", + "description": "Picks a smoothing algorithm use to smooth `z` data.", + "default": false, + "enum": [ + "fast", + "best", + false + ] + }, + "zhoverformat": { + "type": "string", + "description": "Sets the hover text formatting rule using d3 formatting mini-languages which are very similar to those in Python. See: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format", + "default": "" + }, + "showlegend": { + "type": "boolean", + "description": "Determines whether or not an item corresponding to this trace is shown in the legend.", + "default": false + }, + "zauto": { + "type": "boolean", + "description": "Determines whether or not the color domain is computed with respect to the input data (here in `z`) or the bounds set in `zmin` and `zmax` Defaults to `false` when `zmin` and `zmax` are set by the user.", + "default": true + }, + "zmin": { + "type": "number", + "description": "Sets the lower bound of the color domain. Value should have the same units as in `z` and if set, `zmax` must be set as well.", + "default": null + }, + "zmax": { + "type": "number", + "description": "Sets the upper bound of the color domain. Value should have the same units as in `z` and if set, `zmin` must be set as well.", + "default": null + }, + "zmid": { + "type": "number", + "description": "Sets the mid-point of the color domain by scaling `zmin` and/or `zmax` to be equidistant to this point. Value should have the same units as in `z`. Has no effect when `zauto` is `false`.", + "default": null + }, + "colorscale": { + "type": "string", + "description": "Sets the colorscale. The colorscale must be an array containing arrays mapping a normalized value to an rgb, rgba, hex, hsl, hsv, or named color string. At minimum, a mapping for the lowest (0) and highest (1) values are required. For example, `[[0, 'rgb(0,0,255)'], [1, 'rgb(255,0,0)']]`. To control the bounds of the colorscale in color space, use`zmin` and `zmax`. Alternatively, `colorscale` may be a palette name string of the following list: Greys,YlGnBu,Greens,YlOrRd,Bluered,RdBu,Reds,Blues,Picnic,Rainbow,Portland,Jet,Hot,Blackbody,Earth,Electric,Viridis,Cividis.", + "default": null + }, + "autocolorscale": { + "type": "boolean", + "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `colorscale`. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.", + "default": false + }, + "reversescale": { + "type": "boolean", + "description": "Reverses the color mapping if true. If true, `zmin` will correspond to the last color in the array and `zmax` will correspond to the first color.", + "default": false + }, + "showscale": { + "type": "boolean", + "description": "Determines whether or not a colorbar is displayed for this trace.", + "default": true + }, + "colorbar": { + "type": "object", + "properties": { + "thicknessmode": { + "type": "string", + "description": "Determines whether this color bar's thickness (i.e. the measure in the constant color direction) is set in units of plot *fraction* or in *pixels*. Use `thickness` to set the value.", + "default": "pixels", + "enum": [ + "fraction", + "pixels" + ] + }, + "thickness": { + "type": "number", + "description": "Sets the thickness of the color bar This measure excludes the size of the padding, ticks and labels.", + "default": 30, + "minimum": 0 + }, + "lenmode": { + "type": "string", + "description": "Determines whether this color bar's length (i.e. the measure in the color variation direction) is set in units of plot *fraction* or in *pixels. Use `len` to set the value.", + "default": "fraction", + "enum": [ + "fraction", + "pixels" + ] + }, + "len": { + "type": "number", + "description": "Sets the length of the color bar This measure excludes the padding of both ends. That is, the color bar length is this length minus the padding on both ends.", + "default": 1, + "minimum": 0 + }, + "x": { + "type": "number", + "description": "Sets the x position of the color bar (in plot fraction).", + "default": 1.02, + "minimum": -2, + "maximum": 3 + }, + "xanchor": { + "type": "string", + "description": "Sets this color bar's horizontal position anchor. This anchor binds the `x` position to the *left*, *center* or *right* of the color bar.", + "default": "left", + "enum": [ + "left", + "center", + "right" + ] + }, + "xpad": { + "type": "number", + "description": "Sets the amount of padding (in px) along the x direction.", + "default": 10, + "minimum": 0 + }, + "y": { + "type": "number", + "description": "Sets the y position of the color bar (in plot fraction).", + "default": 0.5, + "minimum": -2, + "maximum": 3 + }, + "yanchor": { + "type": "string", + "description": "Sets this color bar's vertical position anchor This anchor binds the `y` position to the *top*, *middle* or *bottom* of the color bar.", + "default": "middle", + "enum": [ + "top", + "middle", + "bottom" + ] + }, + "ypad": { + "type": "number", + "description": "Sets the amount of padding (in px) along the y direction.", + "default": 10, + "minimum": 0 + }, + "outlinecolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "outlinewidth": { + "type": "number", + "description": "Sets the width (in px) of the axis line.", + "default": 1, + "minimum": 0 + }, + "bordercolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "borderwidth": { + "type": "number", + "description": "Sets the width (in px) or the border enclosing this color bar.", + "default": 0, + "minimum": 0 + }, + "bgcolor": { + "type": "string", + "description": "Sets the color of padded area.", + "default": "rgba(0,0,0,0)" + }, + "tickmode": { + "type": "string", + "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).", + "enum": [ + "auto", + "linear", + "array" + ] + }, + "nticks": { + "type": "integer", + "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", + "default": 0, + "minimum": 0 + }, + "tick0": { + "type": "string", + "description": "Sets the placement of the first tick on this axis. Use with `dtick`. If the axis `type` is *log*, then you must take the log of your starting tick (e.g. to set the starting tick to 100, set the `tick0` to 2) except when `dtick`=*L* (see `dtick` for more info). If the axis `type` is *date*, it should be a date string, like date data. If the axis `type` is *category*, it should be a number, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "dtick": { + "type": "string", + "description": "Sets the step in-between ticks on this axis. Use with `tick0`. Must be a positive number, or special strings available to *log* and *date* axes. If the axis `type` is *log*, then ticks are set every 10^(n*dtick) where n is the tick number. For example, to set a tick mark at 1, 10, 100, 1000, ... set dtick to 1. To set tick marks at 1, 100, 10000, ... set dtick to 2. To set tick marks at 1, 5, 25, 125, 625, 3125, ... set dtick to log_10(5), or 0.69897000433. *log* has several special values; *L*, where `f` is a positive number, gives ticks linearly spaced in value (but not position). For example `tick0` = 0.1, `dtick` = *L0.5* will put ticks at 0.1, 0.6, 1.1, 1.6 etc. To show powers of 10 plus small digits between, use *D1* (all digits) or *D2* (only 2 and 5). `tick0` is ignored for *D1* and *D2*. If the axis `type` is *date*, then you must convert the time to milliseconds. For example, to set the interval between ticks to one day, set `dtick` to 86400000.0. *date* also has special values *M* gives ticks spaced by a number of months. `n` must be a positive integer. To set ticks on the 15th of every third month, set `tick0` to *2000-01-15* and `dtick` to *M3*. To set ticks every 4 years, set `dtick` to *M48*" + }, + "tickvals": { + "type": "string", + "description": "Sets the values at which ticks on this axis appear. Only has an effect if `tickmode` is set to *array*. Used with `ticktext`." + }, + "ticktext": { + "type": "string", + "description": "Sets the text displayed at the ticks position via `tickvals`. Only has an effect if `tickmode` is set to *array*. Used with `tickvals`." + }, + "ticks": { + "type": "string", + "description": "Determines whether ticks are drawn or not. If **, this axis' ticks are not drawn. If *outside* (*inside*), this axis' are drawn outside (inside) the axis lines.", + "default": "", + "enum": [ + "outside", + "inside", + "" + ] + }, + "ticklabelposition": { + "type": "string", + "description": "Determines where tick labels are drawn.", + "default": "outside", + "enum": [ + "outside", + "inside", + "outside top", + "inside top", + "outside bottom", + "inside bottom" + ] + }, + "ticklen": { + "type": "number", + "description": "Sets the tick length (in px).", + "default": 5, + "minimum": 0 + }, + "tickwidth": { + "type": "number", + "description": "Sets the tick width (in px).", + "default": 1, + "minimum": 0 + }, + "tickcolor": { + "type": "string", + "description": "Sets the tick color.", + "default": "#444" + }, + "showticklabels": { + "type": "boolean", + "description": "Determines whether or not the tick labels are drawn.", + "default": true + }, + "tickfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "tickangle": { + "type": "number", + "description": "Sets the angle of the tick labels with respect to the horizontal. For example, a `tickangle` of -90 draws the tick labels vertically.", + "default": "auto", + "minimum": -180, + "maximum": 180 + }, + "tickformat": { + "type": "string", + "description": "Sets the tick label formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "tickprefix": { + "type": "string", + "description": "Sets a tick label prefix.", + "default": "" + }, + "showtickprefix": { + "type": "string", + "description": "If *all*, all tick labels are displayed with a prefix. If *first*, only the first tick is displayed with a prefix. If *last*, only the last tick is displayed with a suffix. If *none*, tick prefixes are hidden.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "ticksuffix": { + "type": "string", + "description": "Sets a tick label suffix.", + "default": "" + }, + "showticksuffix": { + "type": "string", + "description": "Same as `showtickprefix` but for tick suffixes.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "separatethousands": { + "type": "boolean", + "description": "If \"true\", even 4-digit integers are separated", + "default": false + }, + "exponentformat": { + "type": "string", + "description": "Determines a formatting rule for the tick exponents. For example, consider the number 1,000,000,000. If *none*, it appears as 1,000,000,000. If *e*, 1e+9. If *E*, 1E+9. If *power*, 1x10^9 (with 9 in a super script). If *SI*, 1G. If *B*, 1B.", + "default": "B", + "enum": [ + "none", + "e", + "E", + "power", + "SI", + "B" + ] + }, + "minexponent": { + "type": "number", + "description": "Hide SI prefix for 10^n if |n| is below this number. This only has an effect when `tickformat` is *SI* or *B*.", + "default": 3, + "minimum": 0 + }, + "showexponent": { + "type": "string", + "description": "If *all*, all exponents are shown besides their significands. If *first*, only the exponent of the first tick is shown. If *last*, only the exponent of the last tick is shown. If *none*, no exponents appear.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "title": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Sets the title of the color bar. Note that before the existence of `title.text`, the title's contents used to be defined as the `title` attribute itself. This behavior has been deprecated." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "side": { + "type": "string", + "description": "Determines the location of color bar's title with respect to the color bar. Note that the title's location used to be set by the now deprecated `titleside` attribute.", + "default": "top", + "enum": [ + "right", + "top", + "bottom" + ] + } + } + } + } + }, + "coloraxis": { + "type": "string", + "description": "Sets a reference to a shared color axis. References to these shared color axes are *coloraxis*, *coloraxis2*, *coloraxis3*, etc. Settings for these shared color axes are set in the layout, under `layout.coloraxis`, `layout.coloraxis2`, etc. Note that multiple color scales can be linked to the same color axis.", + "default": null + }, + "xcalendar": { + "type": "string", + "description": "Sets the calendar system to use with `x` date data.", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + }, + "ycalendar": { + "type": "string", + "description": "Sets the calendar system to use with `y` date data.", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + }, + "xaxis": { + "type": "string", + "description": "Sets a reference between this trace's x coordinates and a 2D cartesian x axis. If *x* (the default value), the x coordinates refer to `layout.xaxis`. If *x2*, the x coordinates refer to `layout.xaxis2`, and so on.", + "default": "x" + }, + "yaxis": { + "type": "string", + "description": "Sets a reference between this trace's y coordinates and a 2D cartesian y axis. If *y* (the default value), the y coordinates refer to `layout.yaxis`. If *y2*, the y coordinates refer to `layout.yaxis2`, and so on.", + "default": "y" + } + } +} \ No newline at end of file diff --git a/escalation/plotly_api/histogram_plotly_schema.json b/escalation/plotly_api/histogram_plotly_schema.json new file mode 100644 index 0000000..d6a5b9f --- /dev/null +++ b/escalation/plotly_api/histogram_plotly_schema.json @@ -0,0 +1,1001 @@ +{ + "attributes": { + "visible": { + "type": "string", + "description": "Determines whether or not this trace is visible. If *legendonly*, the trace is not drawn, but can appear as a legend item (provided that the legend itself is visible).", + "default": true, + "enum": [ + true, + false, + "legendonly" + ] + }, + "showlegend": { + "type": "boolean", + "description": "Determines whether or not an item corresponding to this trace is shown in the legend.", + "default": true + }, + "legendgroup": { + "type": "string", + "description": "Sets the legend group for this trace. Traces part of the same legend group hide/show at the same time when toggling legend items.", + "default": "" + }, + "opacity": { + "type": "number", + "description": "Sets the opacity of the trace.", + "default": 1, + "minimum": 0, + "maximum": 1 + }, + "name": { + "type": "string", + "description": "Sets the trace name. The trace name appear as the legend item and on hover." + }, + "customdata": { + "type": "string", + "description": "Assigns extra data each datum. This may be useful when listening to hover, click and selection events. Note that, *scatter* traces also appends customdata items in the markers DOM elements" + }, + "meta": { + "type": "string", + "description": "Assigns extra meta information associated with this trace that can be used in various text attributes. Attributes such as trace `name`, graph, axis and colorbar `title.text`, annotation `text` `rangeselector`, `updatemenues` and `sliders` `label` text all support `meta`. To access the trace `meta` values in an attribute in the same trace, simply use `%{meta[i]}` where `i` is the index or key of the `meta` item in question. To access trace `meta` in layout attributes, use `%{data[n[.meta[i]}` where `i` is the index or key of the `meta` and `n` is the trace index." + }, + "selectedpoints": { + "type": "string", + "description": "Array containing integer indices of selected points. Has an effect only for traces that support selections. Note that an empty array means an empty selection where the `unselected` are turned on for all points, whereas, any other non-array values means no selection all where the `selected` and `unselected` styles have no effect." + }, + "hoverinfo": { + "type": "string", + "description": "Determines which trace information appear on hover. If `none` or `skip` are set, no information is displayed upon hovering. But, if `none` is set, click and hover events are still fired.", + "default": "all", + "enum": [ + "name", + "text", + "text+name", + "z", + "z+name", + "z+text", + "z+text+name", + "y", + "y+name", + "y+text", + "y+text+name", + "y+z", + "y+z+name", + "y+z+text", + "y+z+text+name", + "x", + "x+name", + "x+text", + "x+text+name", + "x+z", + "x+z+name", + "x+z+text", + "x+z+text+name", + "x+y", + "x+y+name", + "x+y+text", + "x+y+text+name", + "x+y+z", + "x+y+z+name", + "x+y+z+text", + "x+y+z+text+name", + "all", + "none", + "skip" + ] + }, + "hoverlabel": { + "type": "object", + "properties": { + "bgcolor": { + "type": "string", + "description": "Sets the background color of the hover labels for this trace" + }, + "bordercolor": { + "type": "string", + "description": "Sets the border color of the hover labels for this trace." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "align": { + "type": "string", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "default": "auto", + "enum": [ + "left", + "right", + "auto" + ] + }, + "namelength": { + "type": "integer", + "description": "Sets the default length (in number of characters) of the trace name in the hover labels for all traces. -1 shows the whole name regardless of length. 0-3 shows the first 0-3 characters, and an integer >3 will show the whole name if it is less than that many characters, but if it is longer, will truncate to `namelength - 3` characters and add an ellipsis.", + "default": 15, + "minimum": -1 + } + } + }, + "uirevision": { + "type": "string", + "description": "Controls persistence of some user-driven changes to the trace: `constraintrange` in `parcoords` traces, as well as some `editable: true` modifications such as `name` and `colorbar.title`. Defaults to `layout.uirevision`. Note that other user-driven trace attribute changes are controlled by `layout` attributes: `trace.visible` is controlled by `layout.legend.uirevision`, `selectedpoints` is controlled by `layout.selectionrevision`, and `colorbar.(x|y)` (accessible with `config: {editable: true}`) is controlled by `layout.editrevision`. Trace changes are tracked by `uid`, which only falls back on trace index if no `uid` is provided. So if your app can add/remove traces before the end of the `data` array, such that the same trace has a different index, you can still preserve user-driven changes if you give each trace a `uid` that stays with it as it moves." + }, + "x": { + "type": "string", + "description": "Sets the sample data to be binned on the x axis." + }, + "y": { + "type": "string", + "description": "Sets the sample data to be binned on the y axis." + }, + "text": { + "type": "string", + "description": "Sets hover text elements associated with each bar. If a single string, the same string appears over all bars. If an array of string, the items are mapped in order to the this trace's coordinates.", + "default": "" + }, + "orientation": { + "type": "string", + "description": "Sets the orientation of the bars. With *v* (*h*), the value of the each bar spans along the vertical (horizontal).", + "enum": [ + "v", + "h" + ] + }, + "histfunc": { + "type": "string", + "description": "Specifies the binning function used for this histogram trace. If *count*, the histogram values are computed by counting the number of values lying inside each bin. If *sum*, *avg*, *min*, *max*, the histogram values are computed using the sum, the average, the minimum or the maximum of the values lying inside each bin respectively.", + "default": "count", + "enum": [ + "count", + "sum", + "avg", + "min", + "max" + ] + }, + "histnorm": { + "type": "string", + "description": "Specifies the type of normalization used for this histogram trace. If **, the span of each bar corresponds to the number of occurrences (i.e. the number of data points lying inside the bins). If *percent* / *probability*, the span of each bar corresponds to the percentage / fraction of occurrences with respect to the total number of sample points (here, the sum of all bin HEIGHTS equals 100% / 1). If *density*, the span of each bar corresponds to the number of occurrences in a bin divided by the size of the bin interval (here, the sum of all bin AREAS equals the total number of sample points). If *probability density*, the area of each bar corresponds to the probability that an event will fall into the corresponding bin (here, the sum of all bin AREAS equals 1).", + "default": "", + "enum": [ + "", + "percent", + "probability", + "density", + "probability density" + ] + }, + "cumulative": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "If true, display the cumulative distribution by summing the binned values. Use the `direction` and `centralbin` attributes to tune the accumulation method. Note: in this mode, the *density* `histnorm` settings behave the same as their equivalents without *density*: ** and *density* both rise to the number of data points, and *probability* and *probability density* both rise to the number of sample points.", + "default": false + }, + "direction": { + "type": "string", + "description": "Only applies if cumulative is enabled. If *increasing* (default) we sum all prior bins, so the result increases from left to right. If *decreasing* we sum later bins so the result decreases from left to right.", + "default": "increasing", + "enum": [ + "increasing", + "decreasing" + ] + }, + "currentbin": { + "type": "string", + "description": "Only applies if cumulative is enabled. Sets whether the current bin is included, excluded, or has half of its value included in the current cumulative value. *include* is the default for compatibility with various other tools, however it introduces a half-bin bias to the results. *exclude* makes the opposite half-bin bias, and *half* removes it.", + "default": "include", + "enum": [ + "include", + "exclude", + "half" + ] + } + } + }, + "nbinsx": { + "type": "integer", + "description": "Specifies the maximum number of desired bins. This value will be used in an algorithm that will decide the optimal bin size such that the histogram best visualizes the distribution of the data. Ignored if `xbins.size` is provided.", + "default": 0, + "minimum": 0 + }, + "xbins": { + "type": "object", + "properties": { + "start": { + "type": "string", + "description": "Sets the starting value for the x axis bins. Defaults to the minimum data value, shifted down if necessary to make nice round values and to remove ambiguous bin edges. For example, if most of the data is integers we shift the bin edges 0.5 down, so a `size` of 5 would have a default `start` of -0.5, so it is clear that 0-4 are in the first bin, 5-9 in the second, but continuous data gets a start of 0 and bins [0,5), [5,10) etc. Dates behave similarly, and `start` should be a date string. For category data, `start` is based on the category serial numbers, and defaults to -0.5. If multiple non-overlaying histograms share a subplot, the first explicit `start` is used exactly and all others are shifted down (if necessary) to differ from that one by an integer number of bins." + }, + "end": { + "type": "string", + "description": "Sets the end value for the x axis bins. The last bin may not end exactly at this value, we increment the bin edge by `size` from `start` until we reach or exceed `end`. Defaults to the maximum data value. Like `start`, for dates use a date string, and for category data `end` is based on the category serial numbers." + }, + "size": { + "type": "string", + "description": "Sets the size of each x axis bin. Default behavior: If `nbinsx` is 0 or omitted, we choose a nice round bin size such that the number of bins is about the same as the typical number of samples in each bin. If `nbinsx` is provided, we choose a nice round bin size giving no more than that many bins. For date data, use milliseconds or *M* for months, as in `axis.dtick`. For category data, the number of categories to bin together (always defaults to 1). If multiple non-overlaying histograms share a subplot, the first explicit `size` is used and all others discarded. If no `size` is provided,the sample data from all traces is combined to determine `size` as described above." + } + } + }, + "nbinsy": { + "type": "integer", + "description": "Specifies the maximum number of desired bins. This value will be used in an algorithm that will decide the optimal bin size such that the histogram best visualizes the distribution of the data. Ignored if `ybins.size` is provided.", + "default": 0, + "minimum": 0 + }, + "ybins": { + "type": "object", + "properties": { + "start": { + "type": "string", + "description": "Sets the starting value for the y axis bins. Defaults to the minimum data value, shifted down if necessary to make nice round values and to remove ambiguous bin edges. For example, if most of the data is integers we shift the bin edges 0.5 down, so a `size` of 5 would have a default `start` of -0.5, so it is clear that 0-4 are in the first bin, 5-9 in the second, but continuous data gets a start of 0 and bins [0,5), [5,10) etc. Dates behave similarly, and `start` should be a date string. For category data, `start` is based on the category serial numbers, and defaults to -0.5. If multiple non-overlaying histograms share a subplot, the first explicit `start` is used exactly and all others are shifted down (if necessary) to differ from that one by an integer number of bins." + }, + "end": { + "type": "string", + "description": "Sets the end value for the y axis bins. The last bin may not end exactly at this value, we increment the bin edge by `size` from `start` until we reach or exceed `end`. Defaults to the maximum data value. Like `start`, for dates use a date string, and for category data `end` is based on the category serial numbers." + }, + "size": { + "type": "string", + "description": "Sets the size of each y axis bin. Default behavior: If `nbinsy` is 0 or omitted, we choose a nice round bin size such that the number of bins is about the same as the typical number of samples in each bin. If `nbinsy` is provided, we choose a nice round bin size giving no more than that many bins. For date data, use milliseconds or *M* for months, as in `axis.dtick`. For category data, the number of categories to bin together (always defaults to 1). If multiple non-overlaying histograms share a subplot, the first explicit `size` is used and all others discarded. If no `size` is provided,the sample data from all traces is combined to determine `size` as described above." + } + } + }, + "autobinx": { + "type": "boolean", + "description": "Obsolete: since v1.42 each bin attribute is auto-determined separately and `autobinx` is not needed. However, we accept `autobinx: true` or `false` and will update `xbins` accordingly before deleting `autobinx` from the trace.", + "default": null + }, + "autobiny": { + "type": "boolean", + "description": "Obsolete: since v1.42 each bin attribute is auto-determined separately and `autobiny` is not needed. However, we accept `autobiny: true` or `false` and will update `ybins` accordingly before deleting `autobiny` from the trace.", + "default": null + }, + "bingroup": { + "type": "string", + "description": "Set a group of histogram traces which will have compatible bin settings. Note that traces on the same subplot and with the same *orientation* under `barmode` *stack*, *relative* and *group* are forced into the same bingroup, Using `bingroup`, traces under `barmode` *overlay* and on different axes (of the same axis type) can have compatible bin settings. Note that histogram and histogram2d* trace can share the same `bingroup`", + "default": "" + }, + "marker": { + "type": "object", + "properties": { + "line": { + "type": "object", + "properties": { + "width": { + "type": "number", + "description": "Sets the width (in px) of the lines bounding the marker points.", + "default": 0, + "minimum": 0 + }, + "color": { + "type": "string", + "description": "Sets themarker.linecolor. It accepts either a specific color or an array of numbers that are mapped to the colorscale relative to the max and min values of the array or relative to `marker.line.cmin` and `marker.line.cmax` if set." + }, + "cauto": { + "type": "boolean", + "description": "Determines whether or not the color domain is computed with respect to the input data (here in `marker.line.color`) or the bounds set in `marker.line.cmin` and `marker.line.cmax` Has an effect only if in `marker.line.color`is set to a numerical array. Defaults to `false` when `marker.line.cmin` and `marker.line.cmax` are set by the user.", + "default": true + }, + "cmin": { + "type": "number", + "description": "Sets the lower bound of the color domain. Has an effect only if in `marker.line.color`is set to a numerical array. Value should have the same units as in `marker.line.color` and if set, `marker.line.cmax` must be set as well.", + "default": null + }, + "cmax": { + "type": "number", + "description": "Sets the upper bound of the color domain. Has an effect only if in `marker.line.color`is set to a numerical array. Value should have the same units as in `marker.line.color` and if set, `marker.line.cmin` must be set as well.", + "default": null + }, + "cmid": { + "type": "number", + "description": "Sets the mid-point of the color domain by scaling `marker.line.cmin` and/or `marker.line.cmax` to be equidistant to this point. Has an effect only if in `marker.line.color`is set to a numerical array. Value should have the same units as in `marker.line.color`. Has no effect when `marker.line.cauto` is `false`.", + "default": null + }, + "colorscale": { + "type": "string", + "description": "Sets the colorscale. Has an effect only if in `marker.line.color`is set to a numerical array. The colorscale must be an array containing arrays mapping a normalized value to an rgb, rgba, hex, hsl, hsv, or named color string. At minimum, a mapping for the lowest (0) and highest (1) values are required. For example, `[[0, 'rgb(0,0,255)'], [1, 'rgb(255,0,0)']]`. To control the bounds of the colorscale in color space, use`marker.line.cmin` and `marker.line.cmax`. Alternatively, `colorscale` may be a palette name string of the following list: Greys,YlGnBu,Greens,YlOrRd,Bluered,RdBu,Reds,Blues,Picnic,Rainbow,Portland,Jet,Hot,Blackbody,Earth,Electric,Viridis,Cividis.", + "default": null + }, + "autocolorscale": { + "type": "boolean", + "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.line.colorscale`. Has an effect only if in `marker.line.color`is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.", + "default": true + }, + "reversescale": { + "type": "boolean", + "description": "Reverses the color mapping if true. Has an effect only if in `marker.line.color`is set to a numerical array. If true, `marker.line.cmin` will correspond to the last color in the array and `marker.line.cmax` will correspond to the first color.", + "default": false + }, + "coloraxis": { + "type": "string", + "description": "Sets a reference to a shared color axis. References to these shared color axes are *coloraxis*, *coloraxis2*, *coloraxis3*, etc. Settings for these shared color axes are set in the layout, under `layout.coloraxis`, `layout.coloraxis2`, etc. Note that multiple color scales can be linked to the same color axis.", + "default": null + } + } + }, + "color": { + "type": "string", + "description": "Sets themarkercolor. It accepts either a specific color or an array of numbers that are mapped to the colorscale relative to the max and min values of the array or relative to `marker.cmin` and `marker.cmax` if set." + }, + "cauto": { + "type": "boolean", + "description": "Determines whether or not the color domain is computed with respect to the input data (here in `marker.color`) or the bounds set in `marker.cmin` and `marker.cmax` Has an effect only if in `marker.color`is set to a numerical array. Defaults to `false` when `marker.cmin` and `marker.cmax` are set by the user.", + "default": true + }, + "cmin": { + "type": "number", + "description": "Sets the lower bound of the color domain. Has an effect only if in `marker.color`is set to a numerical array. Value should have the same units as in `marker.color` and if set, `marker.cmax` must be set as well.", + "default": null + }, + "cmax": { + "type": "number", + "description": "Sets the upper bound of the color domain. Has an effect only if in `marker.color`is set to a numerical array. Value should have the same units as in `marker.color` and if set, `marker.cmin` must be set as well.", + "default": null + }, + "cmid": { + "type": "number", + "description": "Sets the mid-point of the color domain by scaling `marker.cmin` and/or `marker.cmax` to be equidistant to this point. Has an effect only if in `marker.color`is set to a numerical array. Value should have the same units as in `marker.color`. Has no effect when `marker.cauto` is `false`.", + "default": null + }, + "colorscale": { + "type": "string", + "description": "Sets the colorscale. Has an effect only if in `marker.color`is set to a numerical array. The colorscale must be an array containing arrays mapping a normalized value to an rgb, rgba, hex, hsl, hsv, or named color string. At minimum, a mapping for the lowest (0) and highest (1) values are required. For example, `[[0, 'rgb(0,0,255)'], [1, 'rgb(255,0,0)']]`. To control the bounds of the colorscale in color space, use`marker.cmin` and `marker.cmax`. Alternatively, `colorscale` may be a palette name string of the following list: Greys,YlGnBu,Greens,YlOrRd,Bluered,RdBu,Reds,Blues,Picnic,Rainbow,Portland,Jet,Hot,Blackbody,Earth,Electric,Viridis,Cividis.", + "default": null + }, + "autocolorscale": { + "type": "boolean", + "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.colorscale`. Has an effect only if in `marker.color`is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.", + "default": true + }, + "reversescale": { + "type": "boolean", + "description": "Reverses the color mapping if true. Has an effect only if in `marker.color`is set to a numerical array. If true, `marker.cmin` will correspond to the last color in the array and `marker.cmax` will correspond to the first color.", + "default": false + }, + "showscale": { + "type": "boolean", + "description": "Determines whether or not a colorbar is displayed for this trace. Has an effect only if in `marker.color`is set to a numerical array.", + "default": false + }, + "colorbar": { + "type": "object", + "properties": { + "thicknessmode": { + "type": "string", + "description": "Determines whether this color bar's thickness (i.e. the measure in the constant color direction) is set in units of plot *fraction* or in *pixels*. Use `thickness` to set the value.", + "default": "pixels", + "enum": [ + "fraction", + "pixels" + ] + }, + "thickness": { + "type": "number", + "description": "Sets the thickness of the color bar This measure excludes the size of the padding, ticks and labels.", + "default": 30, + "minimum": 0 + }, + "lenmode": { + "type": "string", + "description": "Determines whether this color bar's length (i.e. the measure in the color variation direction) is set in units of plot *fraction* or in *pixels. Use `len` to set the value.", + "default": "fraction", + "enum": [ + "fraction", + "pixels" + ] + }, + "len": { + "type": "number", + "description": "Sets the length of the color bar This measure excludes the padding of both ends. That is, the color bar length is this length minus the padding on both ends.", + "default": 1, + "minimum": 0 + }, + "x": { + "type": "number", + "description": "Sets the x position of the color bar (in plot fraction).", + "default": 1.02, + "minimum": -2, + "maximum": 3 + }, + "xanchor": { + "type": "string", + "description": "Sets this color bar's horizontal position anchor. This anchor binds the `x` position to the *left*, *center* or *right* of the color bar.", + "default": "left", + "enum": [ + "left", + "center", + "right" + ] + }, + "xpad": { + "type": "number", + "description": "Sets the amount of padding (in px) along the x direction.", + "default": 10, + "minimum": 0 + }, + "y": { + "type": "number", + "description": "Sets the y position of the color bar (in plot fraction).", + "default": 0.5, + "minimum": -2, + "maximum": 3 + }, + "yanchor": { + "type": "string", + "description": "Sets this color bar's vertical position anchor This anchor binds the `y` position to the *top*, *middle* or *bottom* of the color bar.", + "default": "middle", + "enum": [ + "top", + "middle", + "bottom" + ] + }, + "ypad": { + "type": "number", + "description": "Sets the amount of padding (in px) along the y direction.", + "default": 10, + "minimum": 0 + }, + "outlinecolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "outlinewidth": { + "type": "number", + "description": "Sets the width (in px) of the axis line.", + "default": 1, + "minimum": 0 + }, + "bordercolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "borderwidth": { + "type": "number", + "description": "Sets the width (in px) or the border enclosing this color bar.", + "default": 0, + "minimum": 0 + }, + "bgcolor": { + "type": "string", + "description": "Sets the color of padded area.", + "default": "rgba(0,0,0,0)" + }, + "tickmode": { + "type": "string", + "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).", + "enum": [ + "auto", + "linear", + "array" + ] + }, + "nticks": { + "type": "integer", + "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", + "default": 0, + "minimum": 0 + }, + "tick0": { + "type": "string", + "description": "Sets the placement of the first tick on this axis. Use with `dtick`. If the axis `type` is *log*, then you must take the log of your starting tick (e.g. to set the starting tick to 100, set the `tick0` to 2) except when `dtick`=*L* (see `dtick` for more info). If the axis `type` is *date*, it should be a date string, like date data. If the axis `type` is *category*, it should be a number, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "dtick": { + "type": "string", + "description": "Sets the step in-between ticks on this axis. Use with `tick0`. Must be a positive number, or special strings available to *log* and *date* axes. If the axis `type` is *log*, then ticks are set every 10^(n*dtick) where n is the tick number. For example, to set a tick mark at 1, 10, 100, 1000, ... set dtick to 1. To set tick marks at 1, 100, 10000, ... set dtick to 2. To set tick marks at 1, 5, 25, 125, 625, 3125, ... set dtick to log_10(5), or 0.69897000433. *log* has several special values; *L*, where `f` is a positive number, gives ticks linearly spaced in value (but not position). For example `tick0` = 0.1, `dtick` = *L0.5* will put ticks at 0.1, 0.6, 1.1, 1.6 etc. To show powers of 10 plus small digits between, use *D1* (all digits) or *D2* (only 2 and 5). `tick0` is ignored for *D1* and *D2*. If the axis `type` is *date*, then you must convert the time to milliseconds. For example, to set the interval between ticks to one day, set `dtick` to 86400000.0. *date* also has special values *M* gives ticks spaced by a number of months. `n` must be a positive integer. To set ticks on the 15th of every third month, set `tick0` to *2000-01-15* and `dtick` to *M3*. To set ticks every 4 years, set `dtick` to *M48*" + }, + "tickvals": { + "type": "string", + "description": "Sets the values at which ticks on this axis appear. Only has an effect if `tickmode` is set to *array*. Used with `ticktext`." + }, + "ticktext": { + "type": "string", + "description": "Sets the text displayed at the ticks position via `tickvals`. Only has an effect if `tickmode` is set to *array*. Used with `tickvals`." + }, + "ticks": { + "type": "string", + "description": "Determines whether ticks are drawn or not. If **, this axis' ticks are not drawn. If *outside* (*inside*), this axis' are drawn outside (inside) the axis lines.", + "default": "", + "enum": [ + "outside", + "inside", + "" + ] + }, + "ticklabelposition": { + "type": "string", + "description": "Determines where tick labels are drawn.", + "default": "outside", + "enum": [ + "outside", + "inside", + "outside top", + "inside top", + "outside bottom", + "inside bottom" + ] + }, + "ticklen": { + "type": "number", + "description": "Sets the tick length (in px).", + "default": 5, + "minimum": 0 + }, + "tickwidth": { + "type": "number", + "description": "Sets the tick width (in px).", + "default": 1, + "minimum": 0 + }, + "tickcolor": { + "type": "string", + "description": "Sets the tick color.", + "default": "#444" + }, + "showticklabels": { + "type": "boolean", + "description": "Determines whether or not the tick labels are drawn.", + "default": true + }, + "tickfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "tickangle": { + "type": "number", + "description": "Sets the angle of the tick labels with respect to the horizontal. For example, a `tickangle` of -90 draws the tick labels vertically.", + "default": "auto", + "minimum": -180, + "maximum": 180 + }, + "tickformat": { + "type": "string", + "description": "Sets the tick label formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "tickprefix": { + "type": "string", + "description": "Sets a tick label prefix.", + "default": "" + }, + "showtickprefix": { + "type": "string", + "description": "If *all*, all tick labels are displayed with a prefix. If *first*, only the first tick is displayed with a prefix. If *last*, only the last tick is displayed with a suffix. If *none*, tick prefixes are hidden.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "ticksuffix": { + "type": "string", + "description": "Sets a tick label suffix.", + "default": "" + }, + "showticksuffix": { + "type": "string", + "description": "Same as `showtickprefix` but for tick suffixes.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "separatethousands": { + "type": "boolean", + "description": "If \"true\", even 4-digit integers are separated", + "default": false + }, + "exponentformat": { + "type": "string", + "description": "Determines a formatting rule for the tick exponents. For example, consider the number 1,000,000,000. If *none*, it appears as 1,000,000,000. If *e*, 1e+9. If *E*, 1E+9. If *power*, 1x10^9 (with 9 in a super script). If *SI*, 1G. If *B*, 1B.", + "default": "B", + "enum": [ + "none", + "e", + "E", + "power", + "SI", + "B" + ] + }, + "minexponent": { + "type": "number", + "description": "Hide SI prefix for 10^n if |n| is below this number. This only has an effect when `tickformat` is *SI* or *B*.", + "default": 3, + "minimum": 0 + }, + "showexponent": { + "type": "string", + "description": "If *all*, all exponents are shown besides their significands. If *first*, only the exponent of the first tick is shown. If *last*, only the exponent of the last tick is shown. If *none*, no exponents appear.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "title": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Sets the title of the color bar. Note that before the existence of `title.text`, the title's contents used to be defined as the `title` attribute itself. This behavior has been deprecated." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "side": { + "type": "string", + "description": "Determines the location of color bar's title with respect to the color bar. Note that the title's location used to be set by the now deprecated `titleside` attribute.", + "default": "top", + "enum": [ + "right", + "top", + "bottom" + ] + } + } + } + } + }, + "coloraxis": { + "type": "string", + "description": "Sets a reference to a shared color axis. References to these shared color axes are *coloraxis*, *coloraxis2*, *coloraxis3*, etc. Settings for these shared color axes are set in the layout, under `layout.coloraxis`, `layout.coloraxis2`, etc. Note that multiple color scales can be linked to the same color axis.", + "default": null + }, + "opacity": { + "type": "number", + "description": "Sets the opacity of the bars.", + "default": 1, + "minimum": 0, + "maximum": 1 + } + } + }, + "offsetgroup": { + "type": "string", + "description": "Set several traces linked to the same position axis or matching axes to the same offsetgroup where bars of the same position coordinate will line up.", + "default": "" + }, + "alignmentgroup": { + "type": "string", + "description": "Set several traces linked to the same position axis or matching axes to the same alignmentgroup. This controls whether bars compute their positional range dependently or independently.", + "default": "" + }, + "selected": { + "type": "object", + "properties": { + "marker": { + "type": "object", + "properties": { + "opacity": { + "type": "number", + "description": "Sets the marker opacity of selected points.", + "minimum": 0, + "maximum": 1 + }, + "color": { + "type": "string", + "description": "Sets the marker color of selected points." + } + } + }, + "textfont": { + "type": "object", + "properties": { + "color": { + "type": "string", + "description": "Sets the text font color of selected points." + } + } + } + } + }, + "unselected": { + "type": "object", + "properties": { + "marker": { + "type": "object", + "properties": { + "opacity": { + "type": "number", + "description": "Sets the marker opacity of unselected points, applied only when a selection exists.", + "minimum": 0, + "maximum": 1 + }, + "color": { + "type": "string", + "description": "Sets the marker color of unselected points, applied only when a selection exists." + } + } + }, + "textfont": { + "type": "object", + "properties": { + "color": { + "type": "string", + "description": "Sets the text font color of unselected points, applied only when a selection exists." + } + } + } + } + }, + "error_x": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Determines whether or not this set of error bars is visible." + }, + "type": { + "type": "string", + "description": "Determines the rule used to generate the error bars. If *constant`, the bar lengths are of a constant value. Set this constant in `value`. If *percent*, the bar lengths correspond to a percentage of underlying data. Set this percentage in `value`. If *sqrt*, the bar lengths correspond to the square of the underlying data. If *data*, the bar lengths are set with data set `array`.", + "enum": [ + "percent", + "constant", + "sqrt", + "data" + ] + }, + "symmetric": { + "type": "boolean", + "description": "Determines whether or not the error bars have the same length in both direction (top/bottom for vertical bars, left/right for horizontal bars." + }, + "array": { + "type": "string", + "description": "Sets the data corresponding the length of each error bar. Values are plotted relative to the underlying data." + }, + "arrayminus": { + "type": "string", + "description": "Sets the data corresponding the length of each error bar in the bottom (left) direction for vertical (horizontal) bars Values are plotted relative to the underlying data." + }, + "value": { + "type": "number", + "description": "Sets the value of either the percentage (if `type` is set to *percent*) or the constant (if `type` is set to *constant*) corresponding to the lengths of the error bars.", + "default": 10, + "minimum": 0 + }, + "valueminus": { + "type": "number", + "description": "Sets the value of either the percentage (if `type` is set to *percent*) or the constant (if `type` is set to *constant*) corresponding to the lengths of the error bars in the bottom (left) direction for vertical (horizontal) bars", + "default": 10, + "minimum": 0 + }, + "traceref": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "tracerefminus": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "copy_ystyle": { + "type": "boolean" + }, + "color": { + "type": "string", + "description": "Sets the stoke color of the error bars." + }, + "thickness": { + "type": "number", + "description": "Sets the thickness (in px) of the error bars.", + "default": 2, + "minimum": 0 + }, + "width": { + "type": "number", + "description": "Sets the width (in px) of the cross-bar at both ends of the error bars.", + "minimum": 0 + } + } + }, + "error_y": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Determines whether or not this set of error bars is visible." + }, + "type": { + "type": "string", + "description": "Determines the rule used to generate the error bars. If *constant`, the bar lengths are of a constant value. Set this constant in `value`. If *percent*, the bar lengths correspond to a percentage of underlying data. Set this percentage in `value`. If *sqrt*, the bar lengths correspond to the square of the underlying data. If *data*, the bar lengths are set with data set `array`.", + "enum": [ + "percent", + "constant", + "sqrt", + "data" + ] + }, + "symmetric": { + "type": "boolean", + "description": "Determines whether or not the error bars have the same length in both direction (top/bottom for vertical bars, left/right for horizontal bars." + }, + "array": { + "type": "string", + "description": "Sets the data corresponding the length of each error bar. Values are plotted relative to the underlying data." + }, + "arrayminus": { + "type": "string", + "description": "Sets the data corresponding the length of each error bar in the bottom (left) direction for vertical (horizontal) bars Values are plotted relative to the underlying data." + }, + "value": { + "type": "number", + "description": "Sets the value of either the percentage (if `type` is set to *percent*) or the constant (if `type` is set to *constant*) corresponding to the lengths of the error bars.", + "default": 10, + "minimum": 0 + }, + "valueminus": { + "type": "number", + "description": "Sets the value of either the percentage (if `type` is set to *percent*) or the constant (if `type` is set to *constant*) corresponding to the lengths of the error bars in the bottom (left) direction for vertical (horizontal) bars", + "default": 10, + "minimum": 0 + }, + "traceref": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "tracerefminus": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "color": { + "type": "string", + "description": "Sets the stoke color of the error bars." + }, + "thickness": { + "type": "number", + "description": "Sets the thickness (in px) of the error bars.", + "default": 2, + "minimum": 0 + }, + "width": { + "type": "number", + "description": "Sets the width (in px) of the cross-bar at both ends of the error bars.", + "minimum": 0 + } + } + }, + "xcalendar": { + "type": "string", + "description": "Sets the calendar system to use with `x` date data.", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + }, + "ycalendar": { + "type": "string", + "description": "Sets the calendar system to use with `y` date data.", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + }, + "xaxis": { + "type": "string", + "description": "Sets a reference between this trace's x coordinates and a 2D cartesian x axis. If *x* (the default value), the x coordinates refer to `layout.xaxis`. If *x2*, the x coordinates refer to `layout.xaxis2`, and so on.", + "default": "x" + }, + "yaxis": { + "type": "string", + "description": "Sets a reference between this trace's y coordinates and a 2D cartesian y axis. If *y* (the default value), the y coordinates refer to `layout.yaxis`. If *y2*, the y coordinates refer to `layout.yaxis2`, and so on.", + "default": "y" + } + }, + "layoutAttributes": { + "barmode": { + "type": "string", + "description": "Determines how bars at the same location coordinate are displayed on the graph. With *stack*, the bars are stacked on top of one another With *relative*, the bars are stacked on top of one another, with negative values below the axis, positive values above With *group*, the bars are plotted next to one another centered around the shared location. With *overlay*, the bars are plotted over one another, you might need to an *opacity* to see multiple bars.", + "default": "group", + "enum": [ + "stack", + "group", + "overlay", + "relative" + ] + }, + "barnorm": { + "type": "string", + "description": "Sets the normalization for bar traces on the graph. With *fraction*, the value of each bar is divided by the sum of all values at that location coordinate. *percent* is the same but multiplied by 100 to show percentages.", + "default": "", + "enum": [ + "", + "fraction", + "percent" + ] + }, + "bargap": { + "type": "number", + "description": "Sets the gap (in plot fraction) between bars of adjacent location coordinates.", + "minimum": 0, + "maximum": 1 + }, + "bargroupgap": { + "type": "number", + "description": "Sets the gap (in plot fraction) between bars of the same location coordinate.", + "default": 0, + "minimum": 0, + "maximum": 1 + } + } +} \ No newline at end of file diff --git a/escalation/plotly_api/layout_plotly_schema.json b/escalation/plotly_api/layout_plotly_schema.json new file mode 100644 index 0000000..8e39fc6 --- /dev/null +++ b/escalation/plotly_api/layout_plotly_schema.json @@ -0,0 +1,7600 @@ +{ + "type": "object", + "properties": { + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*.", + "default": "\"Open Sans\", verdana, arial, sans-serif" + }, + "size": { + "type": "number", + "default": 12, + "minimum": 1 + }, + "color": { + "type": "string", + "default": "#444" + } + } + }, + "title": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Sets the plot's title. Note that before the existence of `title.text`, the title's contents used to be defined as the `title` attribute itself. This behavior has been deprecated." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "xref": { + "type": "string", + "description": "Sets the container `x` refers to. *container* spans the entire `width` of the plot. *paper* refers to the width of the plotting area only.", + "default": "container", + "enum": [ + "container", + "paper" + ] + }, + "yref": { + "type": "string", + "description": "Sets the container `y` refers to. *container* spans the entire `height` of the plot. *paper* refers to the height of the plotting area only.", + "default": "container", + "enum": [ + "container", + "paper" + ] + }, + "x": { + "type": "number", + "description": "Sets the x position with respect to `xref` in normalized coordinates from *0* (left) to *1* (right).", + "default": 0.5, + "minimum": 0, + "maximum": 1 + }, + "y": { + "type": "number", + "description": "Sets the y position with respect to `yref` in normalized coordinates from *0* (bottom) to *1* (top). *auto* places the baseline of the title onto the vertical center of the top margin.", + "default": "auto", + "minimum": 0, + "maximum": 1 + }, + "xanchor": { + "type": "string", + "description": "Sets the title's horizontal alignment with respect to its x position. *left* means that the title starts at x, *right* means that the title ends at x and *center* means that the title's center is at x. *auto* divides `xref` by three and calculates the `xanchor` value automatically based on the value of `x`.", + "default": "auto", + "enum": [ + "auto", + "left", + "center", + "right" + ] + }, + "yanchor": { + "type": "string", + "description": "Sets the title's vertical alignment with respect to its y position. *top* means that the title's cap line is at y, *bottom* means that the title's baseline is at y and *middle* means that the title's midline is at y. *auto* divides `yref` by three and calculates the `yanchor` value automatically based on the value of `y`.", + "default": "auto", + "enum": [ + "auto", + "top", + "middle", + "bottom" + ] + }, + "pad": { + "type": "object", + "properties": { + "b": { + "type": "number", + "description": "The amount of padding (in px) along the bottom of the component.", + "default": 0 + }, + "l": { + "type": "number", + "description": "The amount of padding (in px) on the left side of the component.", + "default": 0 + } + } + } + } + }, + "uniformtext": { + "type": "object", + "properties": { + "mode": { + "type": "string", + "description": "Determines how the font size for various text elements are uniformed between each trace type. If the computed text sizes were smaller than the minimum size defined by `uniformtext.minsize` using *hide* option hides the text; and using *show* option shows the text without further downscaling. Please note that if the size defined by `minsize` is greater than the font size defined by trace, then the `minsize` is used.", + "default": false, + "enum": [ + false, + "hide", + "show" + ] + }, + "minsize": { + "type": "number", + "description": "Sets the minimum text size between traces of the same type.", + "default": 0, + "minimum": 0 + } + } + }, + "autosize": { + "type": "boolean", + "description": "Determines whether or not a layout width or height that has been left undefined by the user is initialized on each relayout. Note that, regardless of this attribute, an undefined layout width or height is always initialized on the first call to plot.", + "default": false + }, + "width": { + "type": "number", + "description": "Sets the plot's width (in px).", + "default": 700, + "minimum": 10 + }, + "height": { + "type": "number", + "description": "Sets the plot's height (in px).", + "default": 450, + "minimum": 10 + }, + "margin": { + "type": "object", + "properties": { + "l": { + "type": "number", + "description": "Sets the left margin (in px).", + "default": 80, + "minimum": 0 + }, + "b": { + "type": "number", + "description": "Sets the bottom margin (in px).", + "default": 80, + "minimum": 0 + }, + "pad": { + "type": "number", + "description": "Sets the amount of padding (in px) between the plotting area and the axis lines", + "default": 0, + "minimum": 0 + }, + "autoexpand": { + "type": "boolean", + "description": "Turns on/off margin expansion computations. Legends, colorbars, updatemenus, sliders, axis rangeselector and rangeslider are allowed to push the margins by defaults.", + "default": true + } + } + }, + "computed": { + "type": "string", + "description": "Placeholder for exporting automargin-impacting values namely `margin.t`, `margin.b`, `margin.l` and `margin.r` in *full-json* mode." + }, + "paper_bgcolor": { + "type": "string", + "description": "Sets the background color of the paper where the graph is drawn.", + "default": "#fff" + }, + "plot_bgcolor": { + "type": "string", + "description": "Sets the background color of the plotting area in-between x and y axes.", + "default": "#fff" + }, + "autotypenumbers": { + "type": "string", + "description": "Using *strict* a numeric string in trace data is not converted to a number. Using *convert types* a numeric string in trace data may be treated as a number during automatic axis `type` detection. This is the default value; however it could be overridden for individual axes.", + "default": "convert types", + "enum": [ + "convert types", + "strict" + ] + }, + "separators": { + "type": "string", + "description": "Sets the decimal and thousand separators. For example, *. * puts a '.' before decimals and a space between thousands. In English locales, dflt is *.,* but other locales may alter this default." + }, + "hidesources": { + "type": "boolean", + "description": "Determines whether or not a text link citing the data source is placed at the bottom-right cored of the figure. Has only an effect only on graphs that have been generated via forked graphs from the Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise).", + "default": false + }, + "showlegend": { + "type": "boolean", + "description": "Determines whether or not a legend is drawn. Default is `true` if there is a trace to show and any of these: a) Two or more traces would by default be shown in the legend. b) One pie trace is shown in the legend. c) One trace is explicitly given with `showlegend: true`." + }, + "colorway": { + "type": "string", + "description": "Sets the default trace colors.", + "default": [ + "#1f77b4", + "#ff7f0e", + "#2ca02c", + "#d62728", + "#9467bd", + "#8c564b", + "#e377c2", + "#7f7f7f", + "#bcbd22", + "#17becf" + ] + }, + "datarevision": { + "type": "string", + "description": "If provided, a changed value tells `Plotly.react` that one or more data arrays has changed. This way you can modify arrays in-place rather than making a complete new copy for an incremental change. If NOT provided, `Plotly.react` assumes that data arrays are being treated as immutable, thus any data array with a different identity from its predecessor contains new data." + }, + "uirevision": { + "type": "string", + "description": "Used to allow user interactions with the plot to persist after `Plotly.react` calls that are unaware of these interactions. If `uirevision` is omitted, or if it is given and it changed from the previous `Plotly.react` call, the exact new figure is used. If `uirevision` is truthy and did NOT change, any attribute that has been affected by user interactions and did not receive a different value in the new figure will keep the interaction value. `layout.uirevision` attribute serves as the default for `uirevision` attributes in various sub-containers. For finer control you can set these sub-attributes directly. For example, if your app separately controls the data on the x and y axes you might set `xaxis.uirevision=*time*` and `yaxis.uirevision=*cost*`. Then if only the y data is changed, you can update `yaxis.uirevision=*quantity*` and the y axis range will reset but the x axis range will retain any user-driven zoom." + }, + "editrevision": { + "type": "string", + "description": "Controls persistence of user-driven changes in `editable: true` configuration, other than trace names and axis titles. Defaults to `layout.uirevision`." + }, + "selectionrevision": { + "type": "string", + "description": "Controls persistence of user-driven changes in selected points from all traces." + }, + "template": { + "type": "string", + "description": "Default attributes to be applied to the plot. Templates can be created from existing plots using `Plotly.makeTemplate`, or created manually. They should be objects with format: `{layout: layoutTemplate, data: {[type]: [traceTemplate, ...]}, ...}` `layoutTemplate` and `traceTemplate` are objects matching the attribute structure of `layout` and a data trace. Trace templates are applied cyclically to traces of each type. Container arrays (eg `annotations`) have special handling: An object ending in `defaults` (eg `annotationdefaults`) is applied to each array item. But if an item has a `templateitemname` key we look in the template array for an item with matching `name` and apply that instead. If no matching `name` is found we mark the item invisible. Any named template item not referenced is appended to the end of the array, so you can use this for a watermark annotation or a logo image, for example. To omit one of these items on the plot, make an item with matching `templateitemname` and `visible: false`." + }, + "modebar": { + "type": "object", + "properties": { + "orientation": { + "type": "string", + "description": "Sets the orientation of the modebar.", + "default": "h", + "enum": [ + "v", + "h" + ] + }, + "bgcolor": { + "type": "string", + "description": "Sets the background color of the modebar." + }, + "color": { + "type": "string", + "description": "Sets the color of the icons in the modebar." + }, + "activecolor": { + "type": "string", + "description": "Sets the color of the active or hovered on icons in the modebar." + }, + "uirevision": { + "type": "string", + "description": "Controls persistence of user-driven changes related to the modebar, including `hovermode`, `dragmode`, and `showspikes` at both the root level and inside subplots. Defaults to `layout.uirevision`." + } + } + }, + "newshape": { + "type": "object", + "properties": { + "line": { + "type": "object", + "properties": { + "color": { + "type": "string", + "description": "Sets the line color. By default uses either dark grey or white to increase contrast with background color." + }, + "width": { + "type": "number", + "description": "Sets the line width (in px).", + "default": 4, + "minimum": 0 + }, + "dash": { + "type": "string", + "description": "Sets the dash style of lines. Set to a dash type string (*solid*, *dot*, *dash*, *longdash*, *dashdot*, or *longdashdot*) or a dash length list in px (eg *5px,10px,2px,2px*).", + "default": "solid" + } + } + }, + "fillcolor": { + "type": "string", + "description": "Sets the color filling new shapes' interior. Please note that if using a fillcolor with alpha greater than half, drag inside the active shape starts moving the shape underneath, otherwise a new shape could be started over.", + "default": "rgba(0,0,0,0)" + }, + "fillrule": { + "type": "string", + "description": "Determines the path's interior. For more info please visit https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill-rule", + "default": "evenodd", + "enum": [ + "evenodd", + "nonzero" + ] + }, + "opacity": { + "type": "number", + "description": "Sets the opacity of new shapes.", + "default": 1, + "minimum": 0, + "maximum": 1 + }, + "layer": { + "type": "string", + "description": "Specifies whether new shapes are drawn below or above traces.", + "default": "above", + "enum": [ + "below", + "above" + ] + }, + "drawdirection": { + "type": "string", + "description": "When `dragmode` is set to *drawrect*, *drawline* or *drawcircle* this limits the drag to be horizontal, vertical or diagonal. Using *diagonal* there is no limit e.g. in drawing lines in any direction. *ortho* limits the draw to be either horizontal or vertical. *horizontal* allows horizontal extend. *vertical* allows vertical extend.", + "default": "diagonal", + "enum": [ + "ortho", + "horizontal", + "vertical", + "diagonal" + ] + } + } + }, + "activeshape": { + "type": "object", + "properties": { + "fillcolor": { + "type": "string", + "description": "Sets the color filling the active shape' interior.", + "default": "rgb(255,0,255)" + }, + "opacity": { + "type": "number", + "description": "Sets the opacity of the active shape.", + "default": 0.5, + "minimum": 0, + "maximum": 1 + } + } + }, + "meta": { + "type": "string", + "description": "Assigns extra meta information that can be used in various `text` attributes. Attributes such as the graph, axis and colorbar `title.text`, annotation `text` `trace.name` in legend items, `rangeselector`, `updatemenus` and `sliders` `label` text all support `meta`. One can access `meta` fields using template strings: `%{meta[i]}` where `i` is the index of the `meta` item in question. `meta` can also be an object for example `{key: value}` which can be accessed %{meta[key]}." + }, + "transition": { + "type": "object", + "properties": { + "duration": { + "type": "number", + "description": "The duration of the transition, in milliseconds. If equal to zero, updates are synchronous.", + "default": 500, + "minimum": 0 + }, + "easing": { + "type": "string", + "description": "The easing function used for the transition", + "default": "cubic-in-out", + "enum": [ + "linear", + "quad", + "cubic", + "sin", + "exp", + "circle", + "elastic", + "back", + "bounce", + "linear-in", + "quad-in", + "cubic-in", + "sin-in", + "exp-in", + "circle-in", + "elastic-in", + "back-in", + "bounce-in", + "linear-out", + "quad-out", + "cubic-out", + "sin-out", + "exp-out", + "circle-out", + "elastic-out", + "back-out", + "bounce-out", + "linear-in-out", + "quad-in-out", + "cubic-in-out", + "sin-in-out", + "exp-in-out", + "circle-in-out", + "elastic-in-out", + "back-in-out", + "bounce-in-out" + ] + }, + "ordering": { + "type": "string", + "description": "Determines whether the figure's layout or traces smoothly transitions during updates that make both traces and layout change.", + "default": "layout first", + "enum": [ + "layout first", + "traces first" + ] + } + } + }, + "clickmode": { + "type": "string", + "description": "Determines the mode of single click interactions. *event* is the default value and emits the `plotly_click` event. In addition this mode emits the `plotly_selected` event in drag modes *lasso* and *select*, but with no event data attached (kept for compatibility reasons). The *select* flag enables selecting single data points via click. This mode also supports persistent selections, meaning that pressing Shift while clicking, adds to / subtracts from an existing selection. *select* with `hovermode`: *x* can be confusing, consider explicitly setting `hovermode`: *closest* when using this feature. Selection events are sent accordingly as long as *event* flag is set as well. When the *event* flag is missing, `plotly_click` and `plotly_selected` events are not fired.", + "default": "event", + "enum": [ + "select", + "event", + "event+select", + "none" + ] + }, + "dragmode": { + "type": "string", + "description": "Determines the mode of drag interactions. *select* and *lasso* apply only to scatter traces with markers or text. *orbit* and *turntable* apply only to 3D scenes.", + "default": "zoom", + "enum": [ + "zoom", + "pan", + "select", + "lasso", + "drawclosedpath", + "drawopenpath", + "drawline", + "drawrect", + "drawcircle", + "orbit", + "turntable", + false + ] + }, + "hovermode": { + "type": "string", + "description": "Determines the mode of hover interactions. If *closest*, a single hoverlabel will appear for the *closest* point within the `hoverdistance`. If *x* (or *y*), multiple hoverlabels will appear for multiple points at the *closest* x- (or y-) coordinate within the `hoverdistance`, with the caveat that no more than one hoverlabel will appear per trace. If *x unified* (or *y unified*), a single hoverlabel will appear multiple points at the closest x- (or y-) coordinate within the `hoverdistance` with the caveat that no more than one hoverlabel will appear per trace. In this mode, spikelines are enabled by default perpendicular to the specified axis. If false, hover interactions are disabled. If `clickmode` includes the *select* flag, `hovermode` defaults to *closest*. If `clickmode` lacks the *select* flag, it defaults to *x* or *y* (depending on the trace's `orientation` value) for plots based on cartesian coordinates. For anything else the default value is *closest*.", + "enum": [ + "x", + "y", + "closest", + false, + "x unified", + "y unified" + ] + }, + "hoverdistance": { + "type": "integer", + "description": "Sets the default distance (in pixels) to look for data to add hover labels (-1 means no cutoff, 0 means no looking for data). This is only a real distance for hovering on point-like objects, like scatter points. For area-like objects (bars, scatter fills, etc) hovering is on inside the area and off outside, but these objects will not supersede hover on point-like objects in case of conflict.", + "default": 20, + "minimum": -1 + }, + "spikedistance": { + "type": "integer", + "description": "Sets the default distance (in pixels) to look for data to draw spikelines to (-1 means no cutoff, 0 means no looking for data). As with hoverdistance, distance does not apply to area-like objects. In addition, some objects can be hovered on but will not generate spikelines, such as scatter fills.", + "default": 20, + "minimum": -1 + }, + "hoverlabel": { + "type": "object", + "properties": { + "bgcolor": { + "type": "string", + "description": "Sets the background color of all hover labels on graph" + }, + "bordercolor": { + "type": "string", + "description": "Sets the border color of all hover labels on graph." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*.", + "default": "Arial, sans-serif" + }, + "size": { + "type": "number", + "default": 13, + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "align": { + "type": "string", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "default": "auto", + "enum": [ + "left", + "right", + "auto" + ] + }, + "namelength": { + "type": "integer", + "description": "Sets the default length (in number of characters) of the trace name in the hover labels for all traces. -1 shows the whole name regardless of length. 0-3 shows the first 0-3 characters, and an integer >3 will show the whole name if it is less than that many characters, but if it is longer, will truncate to `namelength - 3` characters and add an ellipsis.", + "default": 15, + "minimum": -1 + } + } + }, + "selectdirection": { + "type": "string", + "description": "When `dragmode` is set to *select*, this limits the selection of the drag to horizontal, vertical or diagonal. *h* only allows horizontal selection, *v* only vertical, *d* only diagonal and *any* sets no limit.", + "default": "any", + "enum": [ + "h", + "v", + "d", + "any" + ] + }, + "grid": { + "type": "object", + "properties": { + "rows": { + "type": "integer", + "description": "The number of rows in the grid. If you provide a 2D `subplots` array or a `yaxes` array, its length is used as the default. But it's also possible to have a different length, if you want to leave a row at the end for non-cartesian subplots.", + "minimum": 1 + }, + "roworder": { + "type": "string", + "description": "Is the first row the top or the bottom? Note that columns are always enumerated from left to right.", + "default": "top to bottom", + "enum": [ + "top to bottom", + "bottom to top" + ] + }, + "columns": { + "type": "integer", + "description": "The number of columns in the grid. If you provide a 2D `subplots` array, the length of its longest row is used as the default. If you give an `xaxes` array, its length is used as the default. But it's also possible to have a different length, if you want to leave a row at the end for non-cartesian subplots.", + "minimum": 1 + }, + "subplots": { + "type": "string", + "description": "Used for freeform grids, where some axes may be shared across subplots but others are not. Each entry should be a cartesian subplot id, like *xy* or *x3y2*, or ** to leave that cell empty. You may reuse x axes within the same column, and y axes within the same row. Non-cartesian subplots and traces that support `domain` can place themselves in this grid separately using the `gridcell` attribute." + }, + "xaxes": { + "type": "string", + "description": "Used with `yaxes` when the x and y axes are shared across columns and rows. Each entry should be an x axis id like *x*, *x2*, etc., or ** to not put an x axis in that column. Entries other than ** must be unique. Ignored if `subplots` is present. If missing but `yaxes` is present, will generate consecutive IDs." + }, + "yaxes": { + "type": "string", + "description": "Used with `yaxes` when the x and y axes are shared across columns and rows. Each entry should be an y axis id like *y*, *y2*, etc., or ** to not put a y axis in that row. Entries other than ** must be unique. Ignored if `subplots` is present. If missing but `xaxes` is present, will generate consecutive IDs." + }, + "pattern": { + "type": "string", + "description": "If no `subplots`, `xaxes`, or `yaxes` are given but we do have `rows` and `columns`, we can generate defaults using consecutive axis IDs, in two ways: *coupled* gives one x axis per column and one y axis per row. *independent* uses a new xy pair for each cell, left-to-right across each row then iterating rows according to `roworder`.", + "default": "coupled", + "enum": [ + "independent", + "coupled" + ] + }, + "xgap": { + "type": "number", + "description": "Horizontal space between grid cells, expressed as a fraction of the total width available to one cell. Defaults to 0.1 for coupled-axes grids and 0.2 for independent grids.", + "minimum": 0, + "maximum": 1 + }, + "ygap": { + "type": "number", + "description": "Vertical space between grid cells, expressed as a fraction of the total height available to one cell. Defaults to 0.1 for coupled-axes grids and 0.3 for independent grids.", + "minimum": 0, + "maximum": 1 + }, + "domain": { + "type": "object", + "properties": { + "x": { + "type": "string", + "description": "Sets the horizontal domain of this grid subplot (in plot fraction). The first and last cells end exactly at the domain edges, with no grout around the edges.", + "default": [ + 0, + 1 + ] + }, + "y": { + "type": "string", + "description": "Sets the vertical domain of this grid subplot (in plot fraction). The first and last cells end exactly at the domain edges, with no grout around the edges.", + "default": [ + 0, + 1 + ] + } + } + }, + "xside": { + "type": "string", + "description": "Sets where the x axis labels and titles go. *bottom* means the very bottom of the grid. *bottom plot* is the lowest plot that each x axis is used in. *top* and *top plot* are similar.", + "default": "bottom plot", + "enum": [ + "bottom", + "bottom plot", + "top plot", + "top" + ] + }, + "yside": { + "type": "string", + "description": "Sets where the y axis labels and titles go. *left* means the very left edge of the grid. *left plot* is the leftmost plot that each y axis is used in. *right* and *right plot* are similar.", + "default": "left plot", + "enum": [ + "left", + "left plot", + "right plot", + "right" + ] + } + } + }, + "calendar": { + "type": "string", + "description": "Sets the default calendar system to use for interpreting and displaying dates throughout the plot.", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + }, + "xaxis": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "A single toggle to hide the axis while preserving interaction like dragging. Default is true when a cheater plot is present on the axis, otherwise false" + }, + "color": { + "type": "string", + "description": "Sets default for all colors associated with this axis all at once: line, font, tick, and grid colors. Grid color is lightened by blending this with the plot background Individual pieces can override this.", + "default": "#444" + }, + "title": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Sets the title of this axis. Note that before the existence of `title.text`, the title's contents used to be defined as the `title` attribute itself. This behavior has been deprecated." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "standoff": { + "type": "number", + "description": "Sets the standoff distance (in px) between the axis labels and the title text The default value is a function of the axis tick labels, the title `font.size` and the axis `linewidth`. Note that the axis title position is always constrained within the margins, so the actual standoff distance is always less than the set or default value. By setting `standoff` and turning on `automargin`, plotly.js will push the margins to fit the axis title at given standoff distance.", + "minimum": 0 + } + } + }, + "type": { + "type": "string", + "description": "Sets the axis type. By default, plotly attempts to determined the axis type by looking into the data of the traces that referenced the axis in question.", + "default": "-", + "enum": [ + "-", + "linear", + "log", + "date", + "category", + "multicategory" + ] + }, + "autotypenumbers": { + "type": "string", + "description": "Using *strict* a numeric string in trace data is not converted to a number. Using *convert types* a numeric string in trace data may be treated as a number during automatic axis `type` detection. Defaults to layout.autotypenumbers.", + "default": "convert types", + "enum": [ + "convert types", + "strict" + ] + }, + "autorange": { + "type": "string", + "description": "Determines whether or not the range of this axis is computed in relation to the input data. See `rangemode` for more info. If `range` is provided, then `autorange` is set to *false*.", + "default": true, + "enum": [ + true, + false, + "reversed" + ] + }, + "rangemode": { + "type": "string", + "description": "If *normal*, the range is computed in relation to the extrema of the input data. If *tozero*`, the range extends to 0, regardless of the input data If *nonnegative*, the range is non-negative, regardless of the input data. Applies only to linear axes.", + "default": "normal", + "enum": [ + "normal", + "tozero", + "nonnegative" + ] + }, + "range": { + "type": "string", + "description": "Sets the range of this axis. If the axis `type` is *log*, then you must take the log of your desired range (e.g. to set the range from 1 to 100, set the range from 0 to 2). If the axis `type` is *date*, it should be date strings, like date data, though Date objects and unix milliseconds will be accepted and converted to strings. If the axis `type` is *category*, it should be numbers, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "fixedrange": { + "type": "boolean", + "description": "Determines whether or not this axis is zoom-able. If true, then zoom is disabled.", + "default": false + }, + "scaleanchor": { + "type": "string", + "description": "If set to another axis id (e.g. `x2`, `y`), the range of this axis changes together with the range of the corresponding axis such that the scale of pixels per unit is in a constant ratio. Both axes are still zoomable, but when you zoom one, the other will zoom the same amount, keeping a fixed midpoint. `constrain` and `constraintoward` determine how we enforce the constraint. You can chain these, ie `yaxis: {scaleanchor: *x*}, xaxis2: {scaleanchor: *y*}` but you can only link axes of the same `type`. The linked axis can have the opposite letter (to constrain the aspect ratio) or the same letter (to match scales across subplots). Loops (`yaxis: {scaleanchor: *x*}, xaxis: {scaleanchor: *y*}` or longer) are redundant and the last constraint encountered will be ignored to avoid possible inconsistent constraints via `scaleratio`. Note that setting axes simultaneously in both a `scaleanchor` and a `matches` constraint is currently forbidden.", + "enum": [ + "/^x([2-9]|[1-9][0-9]+)?( domain)?$/", + "/^y([2-9]|[1-9][0-9]+)?( domain)?$/" + ] + }, + "scaleratio": { + "type": "number", + "description": "If this axis is linked to another by `scaleanchor`, this determines the pixel to unit scale ratio. For example, if this value is 10, then every unit on this axis spans 10 times the number of pixels as a unit on the linked axis. Use this for example to create an elevation profile where the vertical scale is exaggerated a fixed amount with respect to the horizontal.", + "default": 1, + "minimum": 0 + }, + "constrain": { + "type": "string", + "description": "If this axis needs to be compressed (either due to its own `scaleanchor` and `scaleratio` or those of the other axis), determines how that happens: by increasing the *range*, or by decreasing the *domain*. Default is *domain* for axes containing image traces, *range* otherwise.", + "enum": [ + "range", + "domain" + ] + }, + "constraintoward": { + "type": "string", + "description": "If this axis needs to be compressed (either due to its own `scaleanchor` and `scaleratio` or those of the other axis), determines which direction we push the originally specified plot area. Options are *left*, *center* (default), and *right* for x axes, and *top*, *middle* (default), and *bottom* for y axes.", + "enum": [ + "left", + "center", + "right", + "top", + "middle", + "bottom" + ] + }, + "matches": { + "type": "string", + "description": "If set to another axis id (e.g. `x2`, `y`), the range of this axis will match the range of the corresponding axis in data-coordinates space. Moreover, matching axes share auto-range values, category lists and histogram auto-bins. Note that setting axes simultaneously in both a `scaleanchor` and a `matches` constraint is currently forbidden. Moreover, note that matching axes must have the same `type`.", + "enum": [ + "/^x([2-9]|[1-9][0-9]+)?( domain)?$/", + "/^y([2-9]|[1-9][0-9]+)?( domain)?$/" + ] + }, + "rangebreaks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Determines whether this axis rangebreak is enabled or disabled. Please note that `rangebreaks` only work for *date* axis type.", + "default": true + }, + "bounds": { + "type": "string", + "description": "Sets the lower and upper bounds of this axis rangebreak. Can be used with `pattern`." + }, + "pattern": { + "type": "string", + "description": "Determines a pattern on the time line that generates breaks. If *day of week* - days of the week in English e.g. 'Sunday' or `sun` (matching is case-insensitive and considers only the first three characters), as well as Sunday-based integers between 0 and 6. If *hour* - hour (24-hour clock) as decimal numbers between 0 and 24. for more info. Examples: - { pattern: 'day of week', bounds: [6, 1] } or simply { bounds: ['sat', 'mon'] } breaks from Saturday to Monday (i.e. skips the weekends). - { pattern: 'hour', bounds: [17, 8] } breaks from 5pm to 8am (i.e. skips non-work hours).", + "enum": [ + "day of week", + "hour", + "" + ] + }, + "values": { + "type": "string", + "description": "Sets the coordinate values corresponding to the rangebreaks. An alternative to `bounds`. Use `dvalue` to set the size of the values along the axis." + }, + "dvalue": { + "type": "number", + "description": "Sets the size of each `values` item. The default is one day in milliseconds.", + "default": 86400000, + "minimum": 0 + }, + "name": { + "type": "string", + "description": "When used in a template, named items are created in the output figure in addition to any items the figure already has in this array. You can modify these items in the output figure by making your own item with `templateitemname` matching this `name` alongside your modifications (including `visible: false` or `enabled: false` to hide it). Has no effect outside of a template." + }, + "templateitemname": { + "type": "string", + "description": "Used to refer to a named item in this array in the template. Named items from the template will be created even without a matching item in the input figure, but you can modify one by making an item with `templateitemname` matching its `name`, alongside your modifications (including `visible: false` or `enabled: false` to hide it). If there is no template or no matching item, this item will be hidden unless you explicitly show it with `visible: true`." + } + } + }, + "title": "rangebreak" + }, + "tickmode": { + "type": "string", + "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).", + "enum": [ + "auto", + "linear", + "array" + ] + }, + "nticks": { + "type": "integer", + "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", + "default": 0, + "minimum": 0 + }, + "tick0": { + "type": "string", + "description": "Sets the placement of the first tick on this axis. Use with `dtick`. If the axis `type` is *log*, then you must take the log of your starting tick (e.g. to set the starting tick to 100, set the `tick0` to 2) except when `dtick`=*L* (see `dtick` for more info). If the axis `type` is *date*, it should be a date string, like date data. If the axis `type` is *category*, it should be a number, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "dtick": { + "type": "string", + "description": "Sets the step in-between ticks on this axis. Use with `tick0`. Must be a positive number, or special strings available to *log* and *date* axes. If the axis `type` is *log*, then ticks are set every 10^(n*dtick) where n is the tick number. For example, to set a tick mark at 1, 10, 100, 1000, ... set dtick to 1. To set tick marks at 1, 100, 10000, ... set dtick to 2. To set tick marks at 1, 5, 25, 125, 625, 3125, ... set dtick to log_10(5), or 0.69897000433. *log* has several special values; *L*, where `f` is a positive number, gives ticks linearly spaced in value (but not position). For example `tick0` = 0.1, `dtick` = *L0.5* will put ticks at 0.1, 0.6, 1.1, 1.6 etc. To show powers of 10 plus small digits between, use *D1* (all digits) or *D2* (only 2 and 5). `tick0` is ignored for *D1* and *D2*. If the axis `type` is *date*, then you must convert the time to milliseconds. For example, to set the interval between ticks to one day, set `dtick` to 86400000.0. *date* also has special values *M* gives ticks spaced by a number of months. `n` must be a positive integer. To set ticks on the 15th of every third month, set `tick0` to *2000-01-15* and `dtick` to *M3*. To set ticks every 4 years, set `dtick` to *M48*" + }, + "tickvals": { + "type": "string", + "description": "Sets the values at which ticks on this axis appear. Only has an effect if `tickmode` is set to *array*. Used with `ticktext`." + }, + "ticktext": { + "type": "string", + "description": "Sets the text displayed at the ticks position via `tickvals`. Only has an effect if `tickmode` is set to *array*. Used with `tickvals`." + }, + "ticks": { + "type": "string", + "description": "Determines whether ticks are drawn or not. If **, this axis' ticks are not drawn. If *outside* (*inside*), this axis' are drawn outside (inside) the axis lines.", + "enum": [ + "outside", + "inside", + "" + ] + }, + "tickson": { + "type": "string", + "description": "Determines where ticks and grid lines are drawn with respect to their corresponding tick labels. Only has an effect for axes of `type` *category* or *multicategory*. When set to *boundaries*, ticks and grid lines are drawn half a category to the left/bottom of labels.", + "default": "labels", + "enum": [ + "labels", + "boundaries" + ] + }, + "ticklabelmode": { + "type": "string", + "description": "Determines where tick labels are drawn with respect to their corresponding ticks and grid lines. Only has an effect for axes of `type` *date* When set to *period*, tick labels are drawn in the middle of the period between ticks.", + "default": "instant", + "enum": [ + "instant", + "period" + ] + }, + "ticklabelposition": { + "type": "string", + "description": "Determines where tick labels are drawn with respect to the axis Please note that top or bottom has no effect on x axes or when `ticklabelmode` is set to *period*. Similarly left or right has no effect on y axes or when `ticklabelmode` is set to *period*. Has no effect on *multicategory* axes or when `tickson` is set to *boundaries*. When used on axes linked by `matches` or `scaleanchor`, no extra padding for inside labels would be added by autorange, so that the scales could match.", + "default": "outside", + "enum": [ + "outside", + "inside", + "outside top", + "inside top", + "outside left", + "inside left", + "outside right", + "inside right", + "outside bottom", + "inside bottom" + ] + }, + "mirror": { + "type": "string", + "description": "Determines if the axis lines or/and ticks are mirrored to the opposite side of the plotting area. If *true*, the axis lines are mirrored. If *ticks*, the axis lines and ticks are mirrored. If *false*, mirroring is disable. If *all*, axis lines are mirrored on all shared-axes subplots. If *allticks*, axis lines and ticks are mirrored on all shared-axes subplots.", + "default": false, + "enum": [ + true, + "ticks", + false, + "all", + "allticks" + ] + }, + "ticklen": { + "type": "number", + "description": "Sets the tick length (in px).", + "default": 5, + "minimum": 0 + }, + "tickwidth": { + "type": "number", + "description": "Sets the tick width (in px).", + "default": 1, + "minimum": 0 + }, + "tickcolor": { + "type": "string", + "description": "Sets the tick color.", + "default": "#444" + }, + "showticklabels": { + "type": "boolean", + "description": "Determines whether or not the tick labels are drawn.", + "default": true + }, + "automargin": { + "type": "boolean", + "description": "Determines whether long tick labels automatically grow the figure margins.", + "default": false + }, + "showspikes": { + "type": "boolean", + "description": "Determines whether or not spikes (aka droplines) are drawn for this axis. Note: This only takes affect when hovermode = closest", + "default": false + }, + "spikecolor": { + "type": "string", + "description": "Sets the spike color. If undefined, will use the series color", + "default": null + }, + "spikethickness": { + "type": "number", + "description": "Sets the width (in px) of the zero line.", + "default": 3 + }, + "spikedash": { + "type": "string", + "description": "Sets the dash style of lines. Set to a dash type string (*solid*, *dot*, *dash*, *longdash*, *dashdot*, or *longdashdot*) or a dash length list in px (eg *5px,10px,2px,2px*).", + "default": "dash" + }, + "spikemode": { + "type": "string", + "description": "Determines the drawing mode for the spike line If *toaxis*, the line is drawn from the data point to the axis the series is plotted on. If *across*, the line is drawn across the entire plot area, and supercedes *toaxis*. If *marker*, then a marker dot is drawn on the axis the series is plotted on", + "default": "toaxis", + "enum": [ + "marker", + "across", + "across+marker", + "toaxis", + "toaxis+marker", + "toaxis+across", + "toaxis+across+marker" + ] + }, + "spikesnap": { + "type": "string", + "description": "Determines whether spikelines are stuck to the cursor or to the closest datapoints.", + "default": "data", + "enum": [ + "data", + "cursor", + "hovered data" + ] + }, + "tickfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "tickangle": { + "type": "number", + "description": "Sets the angle of the tick labels with respect to the horizontal. For example, a `tickangle` of -90 draws the tick labels vertically.", + "default": "auto", + "minimum": -180, + "maximum": 180 + }, + "tickprefix": { + "type": "string", + "description": "Sets a tick label prefix.", + "default": "" + }, + "showtickprefix": { + "type": "string", + "description": "If *all*, all tick labels are displayed with a prefix. If *first*, only the first tick is displayed with a prefix. If *last*, only the last tick is displayed with a suffix. If *none*, tick prefixes are hidden.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "ticksuffix": { + "type": "string", + "description": "Sets a tick label suffix.", + "default": "" + }, + "showticksuffix": { + "type": "string", + "description": "Same as `showtickprefix` but for tick suffixes.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "showexponent": { + "type": "string", + "description": "If *all*, all exponents are shown besides their significands. If *first*, only the exponent of the first tick is shown. If *last*, only the exponent of the last tick is shown. If *none*, no exponents appear.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "exponentformat": { + "type": "string", + "description": "Determines a formatting rule for the tick exponents. For example, consider the number 1,000,000,000. If *none*, it appears as 1,000,000,000. If *e*, 1e+9. If *E*, 1E+9. If *power*, 1x10^9 (with 9 in a super script). If *SI*, 1G. If *B*, 1B.", + "default": "B", + "enum": [ + "none", + "e", + "E", + "power", + "SI", + "B" + ] + }, + "minexponent": { + "type": "number", + "description": "Hide SI prefix for 10^n if |n| is below this number. This only has an effect when `tickformat` is *SI* or *B*.", + "default": 3, + "minimum": 0 + }, + "separatethousands": { + "type": "boolean", + "description": "If \"true\", even 4-digit integers are separated", + "default": false + }, + "tickformat": { + "type": "string", + "description": "Sets the tick label formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "hoverformat": { + "type": "string", + "description": "Sets the hover text formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "showline": { + "type": "boolean", + "description": "Determines whether or not a line bounding this axis is drawn.", + "default": false + }, + "linecolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "linewidth": { + "type": "number", + "description": "Sets the width (in px) of the axis line.", + "default": 1, + "minimum": 0 + }, + "showgrid": { + "type": "boolean", + "description": "Determines whether or not grid lines are drawn. If *true*, the grid lines are drawn at every tick mark." + }, + "gridcolor": { + "type": "string", + "description": "Sets the color of the grid lines.", + "default": "#eee" + }, + "gridwidth": { + "type": "number", + "description": "Sets the width (in px) of the grid lines.", + "default": 1, + "minimum": 0 + }, + "zeroline": { + "type": "boolean", + "description": "Determines whether or not a line is drawn at along the 0 value of this axis. If *true*, the zero line is drawn on top of the grid lines." + }, + "zerolinecolor": { + "type": "string", + "description": "Sets the line color of the zero line.", + "default": "#444" + }, + "zerolinewidth": { + "type": "number", + "description": "Sets the width (in px) of the zero line.", + "default": 1 + }, + "showdividers": { + "type": "boolean", + "description": "Determines whether or not a dividers are drawn between the category levels of this axis. Only has an effect on *multicategory* axes.", + "default": true + }, + "dividercolor": { + "type": "string", + "description": "Sets the color of the dividers Only has an effect on *multicategory* axes.", + "default": "#444" + }, + "dividerwidth": { + "type": "number", + "description": "Sets the width (in px) of the dividers Only has an effect on *multicategory* axes.", + "default": 1 + }, + "anchor": { + "type": "string", + "description": "If set to an opposite-letter axis id (e.g. `x2`, `y`), this axis is bound to the corresponding opposite-letter axis. If set to *free*, this axis' position is determined by `position`.", + "enum": [ + "free", + "/^x([2-9]|[1-9][0-9]+)?( domain)?$/", + "/^y([2-9]|[1-9][0-9]+)?( domain)?$/" + ] + }, + "side": { + "type": "string", + "description": "Determines whether a x (y) axis is positioned at the *bottom* (*left*) or *top* (*right*) of the plotting area.", + "enum": [ + "top", + "bottom", + "left", + "right" + ] + }, + "overlaying": { + "type": "string", + "description": "If set a same-letter axis id, this axis is overlaid on top of the corresponding same-letter axis, with traces and axes visible for both axes. If *false*, this axis does not overlay any same-letter axes. In this case, for axes with overlapping domains only the highest-numbered axis will be visible.", + "enum": [ + "free", + "/^x([2-9]|[1-9][0-9]+)?( domain)?$/", + "/^y([2-9]|[1-9][0-9]+)?( domain)?$/" + ] + }, + "layer": { + "type": "string", + "description": "Sets the layer on which this axis is displayed. If *above traces*, this axis is displayed above all the subplot's traces If *below traces*, this axis is displayed below all the subplot's traces, but above the grid lines. Useful when used together with scatter-like traces with `cliponaxis` set to *false* to show markers and/or text nodes above this axis.", + "default": "above traces", + "enum": [ + "above traces", + "below traces" + ] + }, + "domain": { + "type": "string", + "description": "Sets the domain of this axis (in plot fraction).", + "default": [ + 0, + 1 + ] + }, + "position": { + "type": "number", + "description": "Sets the position of this axis in the plotting space (in normalized coordinates). Only has an effect if `anchor` is set to *free*.", + "default": 0, + "minimum": 0, + "maximum": 1 + }, + "categoryorder": { + "type": "string", + "description": "Specifies the ordering logic for the case of categorical variables. By default, plotly uses *trace*, which specifies the order that is present in the data supplied. Set `categoryorder` to *category ascending* or *category descending* if order should be determined by the alphanumerical order of the category names. Set `categoryorder` to *array* to derive the ordering from the attribute `categoryarray`. If a category is not found in the `categoryarray` array, the sorting behavior for that attribute will be identical to the *trace* mode. The unspecified categories will follow the categories in `categoryarray`. Set `categoryorder` to *total ascending* or *total descending* if order should be determined by the numerical order of the values. Similarly, the order can be determined by the min, max, sum, mean or median of all the values.", + "default": "trace", + "enum": [ + "trace", + "category ascending", + "category descending", + "array", + "total ascending", + "total descending", + "min ascending", + "min descending", + "max ascending", + "max descending", + "sum ascending", + "sum descending", + "mean ascending", + "mean descending", + "median ascending", + "median descending" + ] + }, + "categoryarray": { + "type": "string", + "description": "Sets the order in which categories on this axis appear. Only has an effect if `categoryorder` is set to *array*. Used with `categoryorder`." + }, + "uirevision": { + "type": "string", + "description": "Controls persistence of user-driven changes in axis `range`, `autorange`, and `title` if in `editable: true` configuration. Defaults to `layout.uirevision`." + }, + "rangeslider": { + "type": "object", + "properties": { + "bgcolor": { + "type": "string", + "description": "Sets the background color of the range slider.", + "default": "#fff" + }, + "bordercolor": { + "type": "string", + "description": "Sets the border color of the range slider.", + "default": "#444" + }, + "borderwidth": { + "type": "integer", + "description": "Sets the border width of the range slider.", + "default": 0, + "minimum": 0 + }, + "autorange": { + "type": "boolean", + "description": "Determines whether or not the range slider range is computed in relation to the input data. If `range` is provided, then `autorange` is set to *false*.", + "default": true + }, + "range": { + "type": "string", + "description": "Sets the range of the range slider. If not set, defaults to the full xaxis range. If the axis `type` is *log*, then you must take the log of your desired range. If the axis `type` is *date*, it should be date strings, like date data, though Date objects and unix milliseconds will be accepted and converted to strings. If the axis `type` is *category*, it should be numbers, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "thickness": { + "type": "number", + "description": "The height of the range slider as a fraction of the total plot area height.", + "default": 0.15, + "minimum": 0, + "maximum": 1 + }, + "visible": { + "type": "boolean", + "description": "Determines whether or not the range slider will be visible. If visible, perpendicular axes will be set to `fixedrange`", + "default": true + }, + "yaxis": { + "type": "object", + "properties": { + "rangemode": { + "type": "string", + "description": "Determines whether or not the range of this axis in the rangeslider use the same value than in the main plot when zooming in/out. If *auto*, the autorange will be used. If *fixed*, the `range` is used. If *match*, the current range of the corresponding y-axis on the main subplot is used.", + "default": "match", + "enum": [ + "auto", + "fixed", + "match" + ] + }, + "range": { + "type": "string", + "description": "Sets the range of this axis for the rangeslider." + } + } + } + } + }, + "rangeselector": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Determines whether or not this range selector is visible. Note that range selectors are only available for x axes of `type` set to or auto-typed to *date*." + }, + "buttons": { + "type": "array", + "items": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Determines whether or not this button is visible.", + "default": true + }, + "step": { + "type": "string", + "description": "The unit of measurement that the `count` value will set the range by.", + "default": "month", + "enum": [ + "month", + "year", + "day", + "hour", + "minute", + "second", + "all" + ] + }, + "stepmode": { + "type": "string", + "description": "Sets the range update mode. If *backward*, the range update shifts the start of range back *count* times *step* milliseconds. If *todate*, the range update shifts the start of range back to the first timestamp from *count* times *step* milliseconds back. For example, with `step` set to *year* and `count` set to *1* the range update shifts the start of the range back to January 01 of the current year. Month and year *todate* are currently available only for the built-in (Gregorian) calendar.", + "default": "backward", + "enum": [ + "backward", + "todate" + ] + }, + "count": { + "type": "number", + "description": "Sets the number of steps to take to update the range. Use with `step` to specify the update interval.", + "default": 1, + "minimum": 0 + }, + "label": { + "type": "string", + "description": "Sets the text label to appear on the button." + }, + "name": { + "type": "string", + "description": "When used in a template, named items are created in the output figure in addition to any items the figure already has in this array. You can modify these items in the output figure by making your own item with `templateitemname` matching this `name` alongside your modifications (including `visible: false` or `enabled: false` to hide it). Has no effect outside of a template." + }, + "templateitemname": { + "type": "string", + "description": "Used to refer to a named item in this array in the template. Named items from the template will be created even without a matching item in the input figure, but you can modify one by making an item with `templateitemname` matching its `name`, alongside your modifications (including `visible: false` or `enabled: false` to hide it). If there is no template or no matching item, this item will be hidden unless you explicitly show it with `visible: true`." + } + } + }, + "title": "button" + }, + "x": { + "type": "number", + "description": "Sets the x position (in normalized coordinates) of the range selector.", + "minimum": -2, + "maximum": 3 + }, + "xanchor": { + "type": "string", + "description": "Sets the range selector's horizontal position anchor. This anchor binds the `x` position to the *left*, *center* or *right* of the range selector.", + "default": "left", + "enum": [ + "auto", + "left", + "center", + "right" + ] + }, + "y": { + "type": "number", + "description": "Sets the y position (in normalized coordinates) of the range selector.", + "minimum": -2, + "maximum": 3 + }, + "yanchor": { + "type": "string", + "description": "Sets the range selector's vertical position anchor This anchor binds the `y` position to the *top*, *middle* or *bottom* of the range selector.", + "default": "bottom", + "enum": [ + "auto", + "top", + "middle", + "bottom" + ] + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "bgcolor": { + "type": "string", + "description": "Sets the background color of the range selector buttons.", + "default": "#eee" + }, + "activecolor": { + "type": "string", + "description": "Sets the background color of the active range selector button." + }, + "bordercolor": { + "type": "string", + "description": "Sets the color of the border enclosing the range selector.", + "default": "#444" + }, + "borderwidth": { + "type": "number", + "description": "Sets the width (in px) of the border enclosing the range selector.", + "default": 0, + "minimum": 0 + } + } + }, + "calendar": { + "type": "string", + "description": "Sets the calendar system to use for `range` and `tick0` if this is a date axis. This does not set the calendar for interpreting data on this axis, that's specified in the trace or via the global `layout.calendar`", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + } + } + }, + "yaxis": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "A single toggle to hide the axis while preserving interaction like dragging. Default is true when a cheater plot is present on the axis, otherwise false" + }, + "color": { + "type": "string", + "description": "Sets default for all colors associated with this axis all at once: line, font, tick, and grid colors. Grid color is lightened by blending this with the plot background Individual pieces can override this.", + "default": "#444" + }, + "title": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Sets the title of this axis. Note that before the existence of `title.text`, the title's contents used to be defined as the `title` attribute itself. This behavior has been deprecated." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "standoff": { + "type": "number", + "description": "Sets the standoff distance (in px) between the axis labels and the title text The default value is a function of the axis tick labels, the title `font.size` and the axis `linewidth`. Note that the axis title position is always constrained within the margins, so the actual standoff distance is always less than the set or default value. By setting `standoff` and turning on `automargin`, plotly.js will push the margins to fit the axis title at given standoff distance.", + "minimum": 0 + } + } + }, + "type": { + "type": "string", + "description": "Sets the axis type. By default, plotly attempts to determined the axis type by looking into the data of the traces that referenced the axis in question.", + "default": "-", + "enum": [ + "-", + "linear", + "log", + "date", + "category", + "multicategory" + ] + }, + "autotypenumbers": { + "type": "string", + "description": "Using *strict* a numeric string in trace data is not converted to a number. Using *convert types* a numeric string in trace data may be treated as a number during automatic axis `type` detection. Defaults to layout.autotypenumbers.", + "default": "convert types", + "enum": [ + "convert types", + "strict" + ] + }, + "autorange": { + "type": "string", + "description": "Determines whether or not the range of this axis is computed in relation to the input data. See `rangemode` for more info. If `range` is provided, then `autorange` is set to *false*.", + "default": true, + "enum": [ + true, + false, + "reversed" + ] + }, + "rangemode": { + "type": "string", + "description": "If *normal*, the range is computed in relation to the extrema of the input data. If *tozero*`, the range extends to 0, regardless of the input data If *nonnegative*, the range is non-negative, regardless of the input data. Applies only to linear axes.", + "default": "normal", + "enum": [ + "normal", + "tozero", + "nonnegative" + ] + }, + "range": { + "type": "string", + "description": "Sets the range of this axis. If the axis `type` is *log*, then you must take the log of your desired range (e.g. to set the range from 1 to 100, set the range from 0 to 2). If the axis `type` is *date*, it should be date strings, like date data, though Date objects and unix milliseconds will be accepted and converted to strings. If the axis `type` is *category*, it should be numbers, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "fixedrange": { + "type": "boolean", + "description": "Determines whether or not this axis is zoom-able. If true, then zoom is disabled.", + "default": false + }, + "scaleanchor": { + "type": "string", + "description": "If set to another axis id (e.g. `x2`, `y`), the range of this axis changes together with the range of the corresponding axis such that the scale of pixels per unit is in a constant ratio. Both axes are still zoomable, but when you zoom one, the other will zoom the same amount, keeping a fixed midpoint. `constrain` and `constraintoward` determine how we enforce the constraint. You can chain these, ie `yaxis: {scaleanchor: *x*}, xaxis2: {scaleanchor: *y*}` but you can only link axes of the same `type`. The linked axis can have the opposite letter (to constrain the aspect ratio) or the same letter (to match scales across subplots). Loops (`yaxis: {scaleanchor: *x*}, xaxis: {scaleanchor: *y*}` or longer) are redundant and the last constraint encountered will be ignored to avoid possible inconsistent constraints via `scaleratio`. Note that setting axes simultaneously in both a `scaleanchor` and a `matches` constraint is currently forbidden.", + "enum": [ + "/^x([2-9]|[1-9][0-9]+)?( domain)?$/", + "/^y([2-9]|[1-9][0-9]+)?( domain)?$/" + ] + }, + "scaleratio": { + "type": "number", + "description": "If this axis is linked to another by `scaleanchor`, this determines the pixel to unit scale ratio. For example, if this value is 10, then every unit on this axis spans 10 times the number of pixels as a unit on the linked axis. Use this for example to create an elevation profile where the vertical scale is exaggerated a fixed amount with respect to the horizontal.", + "default": 1, + "minimum": 0 + }, + "constrain": { + "type": "string", + "description": "If this axis needs to be compressed (either due to its own `scaleanchor` and `scaleratio` or those of the other axis), determines how that happens: by increasing the *range*, or by decreasing the *domain*. Default is *domain* for axes containing image traces, *range* otherwise.", + "enum": [ + "range", + "domain" + ] + }, + "constraintoward": { + "type": "string", + "description": "If this axis needs to be compressed (either due to its own `scaleanchor` and `scaleratio` or those of the other axis), determines which direction we push the originally specified plot area. Options are *left*, *center* (default), and *right* for x axes, and *top*, *middle* (default), and *bottom* for y axes.", + "enum": [ + "left", + "center", + "right", + "top", + "middle", + "bottom" + ] + }, + "matches": { + "type": "string", + "description": "If set to another axis id (e.g. `x2`, `y`), the range of this axis will match the range of the corresponding axis in data-coordinates space. Moreover, matching axes share auto-range values, category lists and histogram auto-bins. Note that setting axes simultaneously in both a `scaleanchor` and a `matches` constraint is currently forbidden. Moreover, note that matching axes must have the same `type`.", + "enum": [ + "/^x([2-9]|[1-9][0-9]+)?( domain)?$/", + "/^y([2-9]|[1-9][0-9]+)?( domain)?$/" + ] + }, + "rangebreaks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Determines whether this axis rangebreak is enabled or disabled. Please note that `rangebreaks` only work for *date* axis type.", + "default": true + }, + "bounds": { + "type": "string", + "description": "Sets the lower and upper bounds of this axis rangebreak. Can be used with `pattern`." + }, + "pattern": { + "type": "string", + "description": "Determines a pattern on the time line that generates breaks. If *day of week* - days of the week in English e.g. 'Sunday' or `sun` (matching is case-insensitive and considers only the first three characters), as well as Sunday-based integers between 0 and 6. If *hour* - hour (24-hour clock) as decimal numbers between 0 and 24. for more info. Examples: - { pattern: 'day of week', bounds: [6, 1] } or simply { bounds: ['sat', 'mon'] } breaks from Saturday to Monday (i.e. skips the weekends). - { pattern: 'hour', bounds: [17, 8] } breaks from 5pm to 8am (i.e. skips non-work hours).", + "enum": [ + "day of week", + "hour", + "" + ] + }, + "values": { + "type": "string", + "description": "Sets the coordinate values corresponding to the rangebreaks. An alternative to `bounds`. Use `dvalue` to set the size of the values along the axis." + }, + "dvalue": { + "type": "number", + "description": "Sets the size of each `values` item. The default is one day in milliseconds.", + "default": 86400000, + "minimum": 0 + }, + "name": { + "type": "string", + "description": "When used in a template, named items are created in the output figure in addition to any items the figure already has in this array. You can modify these items in the output figure by making your own item with `templateitemname` matching this `name` alongside your modifications (including `visible: false` or `enabled: false` to hide it). Has no effect outside of a template." + }, + "templateitemname": { + "type": "string", + "description": "Used to refer to a named item in this array in the template. Named items from the template will be created even without a matching item in the input figure, but you can modify one by making an item with `templateitemname` matching its `name`, alongside your modifications (including `visible: false` or `enabled: false` to hide it). If there is no template or no matching item, this item will be hidden unless you explicitly show it with `visible: true`." + } + } + }, + "title": "rangebreak" + }, + "tickmode": { + "type": "string", + "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).", + "enum": [ + "auto", + "linear", + "array" + ] + }, + "nticks": { + "type": "integer", + "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", + "default": 0, + "minimum": 0 + }, + "tick0": { + "type": "string", + "description": "Sets the placement of the first tick on this axis. Use with `dtick`. If the axis `type` is *log*, then you must take the log of your starting tick (e.g. to set the starting tick to 100, set the `tick0` to 2) except when `dtick`=*L* (see `dtick` for more info). If the axis `type` is *date*, it should be a date string, like date data. If the axis `type` is *category*, it should be a number, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "dtick": { + "type": "string", + "description": "Sets the step in-between ticks on this axis. Use with `tick0`. Must be a positive number, or special strings available to *log* and *date* axes. If the axis `type` is *log*, then ticks are set every 10^(n*dtick) where n is the tick number. For example, to set a tick mark at 1, 10, 100, 1000, ... set dtick to 1. To set tick marks at 1, 100, 10000, ... set dtick to 2. To set tick marks at 1, 5, 25, 125, 625, 3125, ... set dtick to log_10(5), or 0.69897000433. *log* has several special values; *L*, where `f` is a positive number, gives ticks linearly spaced in value (but not position). For example `tick0` = 0.1, `dtick` = *L0.5* will put ticks at 0.1, 0.6, 1.1, 1.6 etc. To show powers of 10 plus small digits between, use *D1* (all digits) or *D2* (only 2 and 5). `tick0` is ignored for *D1* and *D2*. If the axis `type` is *date*, then you must convert the time to milliseconds. For example, to set the interval between ticks to one day, set `dtick` to 86400000.0. *date* also has special values *M* gives ticks spaced by a number of months. `n` must be a positive integer. To set ticks on the 15th of every third month, set `tick0` to *2000-01-15* and `dtick` to *M3*. To set ticks every 4 years, set `dtick` to *M48*" + }, + "tickvals": { + "type": "string", + "description": "Sets the values at which ticks on this axis appear. Only has an effect if `tickmode` is set to *array*. Used with `ticktext`." + }, + "ticktext": { + "type": "string", + "description": "Sets the text displayed at the ticks position via `tickvals`. Only has an effect if `tickmode` is set to *array*. Used with `tickvals`." + }, + "ticks": { + "type": "string", + "description": "Determines whether ticks are drawn or not. If **, this axis' ticks are not drawn. If *outside* (*inside*), this axis' are drawn outside (inside) the axis lines.", + "enum": [ + "outside", + "inside", + "" + ] + }, + "tickson": { + "type": "string", + "description": "Determines where ticks and grid lines are drawn with respect to their corresponding tick labels. Only has an effect for axes of `type` *category* or *multicategory*. When set to *boundaries*, ticks and grid lines are drawn half a category to the left/bottom of labels.", + "default": "labels", + "enum": [ + "labels", + "boundaries" + ] + }, + "ticklabelmode": { + "type": "string", + "description": "Determines where tick labels are drawn with respect to their corresponding ticks and grid lines. Only has an effect for axes of `type` *date* When set to *period*, tick labels are drawn in the middle of the period between ticks.", + "default": "instant", + "enum": [ + "instant", + "period" + ] + }, + "ticklabelposition": { + "type": "string", + "description": "Determines where tick labels are drawn with respect to the axis Please note that top or bottom has no effect on x axes or when `ticklabelmode` is set to *period*. Similarly left or right has no effect on y axes or when `ticklabelmode` is set to *period*. Has no effect on *multicategory* axes or when `tickson` is set to *boundaries*. When used on axes linked by `matches` or `scaleanchor`, no extra padding for inside labels would be added by autorange, so that the scales could match.", + "default": "outside", + "enum": [ + "outside", + "inside", + "outside top", + "inside top", + "outside left", + "inside left", + "outside right", + "inside right", + "outside bottom", + "inside bottom" + ] + }, + "mirror": { + "type": "string", + "description": "Determines if the axis lines or/and ticks are mirrored to the opposite side of the plotting area. If *true*, the axis lines are mirrored. If *ticks*, the axis lines and ticks are mirrored. If *false*, mirroring is disable. If *all*, axis lines are mirrored on all shared-axes subplots. If *allticks*, axis lines and ticks are mirrored on all shared-axes subplots.", + "default": false, + "enum": [ + true, + "ticks", + false, + "all", + "allticks" + ] + }, + "ticklen": { + "type": "number", + "description": "Sets the tick length (in px).", + "default": 5, + "minimum": 0 + }, + "tickwidth": { + "type": "number", + "description": "Sets the tick width (in px).", + "default": 1, + "minimum": 0 + }, + "tickcolor": { + "type": "string", + "description": "Sets the tick color.", + "default": "#444" + }, + "showticklabels": { + "type": "boolean", + "description": "Determines whether or not the tick labels are drawn.", + "default": true + }, + "automargin": { + "type": "boolean", + "description": "Determines whether long tick labels automatically grow the figure margins.", + "default": false + }, + "showspikes": { + "type": "boolean", + "description": "Determines whether or not spikes (aka droplines) are drawn for this axis. Note: This only takes affect when hovermode = closest", + "default": false + }, + "spikecolor": { + "type": "string", + "description": "Sets the spike color. If undefined, will use the series color", + "default": null + }, + "spikethickness": { + "type": "number", + "description": "Sets the width (in px) of the zero line.", + "default": 3 + }, + "spikedash": { + "type": "string", + "description": "Sets the dash style of lines. Set to a dash type string (*solid*, *dot*, *dash*, *longdash*, *dashdot*, or *longdashdot*) or a dash length list in px (eg *5px,10px,2px,2px*).", + "default": "dash" + }, + "spikemode": { + "type": "string", + "description": "Determines the drawing mode for the spike line If *toaxis*, the line is drawn from the data point to the axis the series is plotted on. If *across*, the line is drawn across the entire plot area, and supercedes *toaxis*. If *marker*, then a marker dot is drawn on the axis the series is plotted on", + "default": "toaxis", + "enum": [ + "marker", + "across", + "across+marker", + "toaxis", + "toaxis+marker", + "toaxis+across", + "toaxis+across+marker" + ] + }, + "spikesnap": { + "type": "string", + "description": "Determines whether spikelines are stuck to the cursor or to the closest datapoints.", + "default": "data", + "enum": [ + "data", + "cursor", + "hovered data" + ] + }, + "tickfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "tickangle": { + "type": "number", + "description": "Sets the angle of the tick labels with respect to the horizontal. For example, a `tickangle` of -90 draws the tick labels vertically.", + "default": "auto", + "minimum": -180, + "maximum": 180 + }, + "tickprefix": { + "type": "string", + "description": "Sets a tick label prefix.", + "default": "" + }, + "showtickprefix": { + "type": "string", + "description": "If *all*, all tick labels are displayed with a prefix. If *first*, only the first tick is displayed with a prefix. If *last*, only the last tick is displayed with a suffix. If *none*, tick prefixes are hidden.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "ticksuffix": { + "type": "string", + "description": "Sets a tick label suffix.", + "default": "" + }, + "showticksuffix": { + "type": "string", + "description": "Same as `showtickprefix` but for tick suffixes.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "showexponent": { + "type": "string", + "description": "If *all*, all exponents are shown besides their significands. If *first*, only the exponent of the first tick is shown. If *last*, only the exponent of the last tick is shown. If *none*, no exponents appear.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "exponentformat": { + "type": "string", + "description": "Determines a formatting rule for the tick exponents. For example, consider the number 1,000,000,000. If *none*, it appears as 1,000,000,000. If *e*, 1e+9. If *E*, 1E+9. If *power*, 1x10^9 (with 9 in a super script). If *SI*, 1G. If *B*, 1B.", + "default": "B", + "enum": [ + "none", + "e", + "E", + "power", + "SI", + "B" + ] + }, + "minexponent": { + "type": "number", + "description": "Hide SI prefix for 10^n if |n| is below this number. This only has an effect when `tickformat` is *SI* or *B*.", + "default": 3, + "minimum": 0 + }, + "separatethousands": { + "type": "boolean", + "description": "If \"true\", even 4-digit integers are separated", + "default": false + }, + "tickformat": { + "type": "string", + "description": "Sets the tick label formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "hoverformat": { + "type": "string", + "description": "Sets the hover text formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "showline": { + "type": "boolean", + "description": "Determines whether or not a line bounding this axis is drawn.", + "default": false + }, + "linecolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "linewidth": { + "type": "number", + "description": "Sets the width (in px) of the axis line.", + "default": 1, + "minimum": 0 + }, + "showgrid": { + "type": "boolean", + "description": "Determines whether or not grid lines are drawn. If *true*, the grid lines are drawn at every tick mark." + }, + "gridcolor": { + "type": "string", + "description": "Sets the color of the grid lines.", + "default": "#eee" + }, + "gridwidth": { + "type": "number", + "description": "Sets the width (in px) of the grid lines.", + "default": 1, + "minimum": 0 + }, + "zeroline": { + "type": "boolean", + "description": "Determines whether or not a line is drawn at along the 0 value of this axis. If *true*, the zero line is drawn on top of the grid lines." + }, + "zerolinecolor": { + "type": "string", + "description": "Sets the line color of the zero line.", + "default": "#444" + }, + "zerolinewidth": { + "type": "number", + "description": "Sets the width (in px) of the zero line.", + "default": 1 + }, + "showdividers": { + "type": "boolean", + "description": "Determines whether or not a dividers are drawn between the category levels of this axis. Only has an effect on *multicategory* axes.", + "default": true + }, + "dividercolor": { + "type": "string", + "description": "Sets the color of the dividers Only has an effect on *multicategory* axes.", + "default": "#444" + }, + "dividerwidth": { + "type": "number", + "description": "Sets the width (in px) of the dividers Only has an effect on *multicategory* axes.", + "default": 1 + }, + "anchor": { + "type": "string", + "description": "If set to an opposite-letter axis id (e.g. `x2`, `y`), this axis is bound to the corresponding opposite-letter axis. If set to *free*, this axis' position is determined by `position`.", + "enum": [ + "free", + "/^x([2-9]|[1-9][0-9]+)?( domain)?$/", + "/^y([2-9]|[1-9][0-9]+)?( domain)?$/" + ] + }, + "side": { + "type": "string", + "description": "Determines whether a x (y) axis is positioned at the *bottom* (*left*) or *top* (*right*) of the plotting area.", + "enum": [ + "top", + "bottom", + "left", + "right" + ] + }, + "overlaying": { + "type": "string", + "description": "If set a same-letter axis id, this axis is overlaid on top of the corresponding same-letter axis, with traces and axes visible for both axes. If *false*, this axis does not overlay any same-letter axes. In this case, for axes with overlapping domains only the highest-numbered axis will be visible.", + "enum": [ + "free", + "/^x([2-9]|[1-9][0-9]+)?( domain)?$/", + "/^y([2-9]|[1-9][0-9]+)?( domain)?$/" + ] + }, + "layer": { + "type": "string", + "description": "Sets the layer on which this axis is displayed. If *above traces*, this axis is displayed above all the subplot's traces If *below traces*, this axis is displayed below all the subplot's traces, but above the grid lines. Useful when used together with scatter-like traces with `cliponaxis` set to *false* to show markers and/or text nodes above this axis.", + "default": "above traces", + "enum": [ + "above traces", + "below traces" + ] + }, + "domain": { + "type": "string", + "description": "Sets the domain of this axis (in plot fraction).", + "default": [ + 0, + 1 + ] + }, + "position": { + "type": "number", + "description": "Sets the position of this axis in the plotting space (in normalized coordinates). Only has an effect if `anchor` is set to *free*.", + "default": 0, + "minimum": 0, + "maximum": 1 + }, + "categoryorder": { + "type": "string", + "description": "Specifies the ordering logic for the case of categorical variables. By default, plotly uses *trace*, which specifies the order that is present in the data supplied. Set `categoryorder` to *category ascending* or *category descending* if order should be determined by the alphanumerical order of the category names. Set `categoryorder` to *array* to derive the ordering from the attribute `categoryarray`. If a category is not found in the `categoryarray` array, the sorting behavior for that attribute will be identical to the *trace* mode. The unspecified categories will follow the categories in `categoryarray`. Set `categoryorder` to *total ascending* or *total descending* if order should be determined by the numerical order of the values. Similarly, the order can be determined by the min, max, sum, mean or median of all the values.", + "default": "trace", + "enum": [ + "trace", + "category ascending", + "category descending", + "array", + "total ascending", + "total descending", + "min ascending", + "min descending", + "max ascending", + "max descending", + "sum ascending", + "sum descending", + "mean ascending", + "mean descending", + "median ascending", + "median descending" + ] + }, + "categoryarray": { + "type": "string", + "description": "Sets the order in which categories on this axis appear. Only has an effect if `categoryorder` is set to *array*. Used with `categoryorder`." + }, + "uirevision": { + "type": "string", + "description": "Controls persistence of user-driven changes in axis `range`, `autorange`, and `title` if in `editable: true` configuration. Defaults to `layout.uirevision`." + }, + "calendar": { + "type": "string", + "description": "Sets the calendar system to use for `range` and `tick0` if this is a date axis. This does not set the calendar for interpreting data on this axis, that's specified in the trace or via the global `layout.calendar`", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + } + } + }, + "ternary": { + "type": "object", + "properties": { + "domain": { + "type": "object", + "properties": { + "x": { + "type": "string", + "description": "Sets the horizontal domain of this ternary subplot (in plot fraction).", + "default": [ + 0, + 1 + ] + }, + "y": { + "type": "string", + "description": "Sets the vertical domain of this ternary subplot (in plot fraction).", + "default": [ + 0, + 1 + ] + }, + "row": { + "type": "integer", + "description": "If there is a layout grid, use the domain for this row in the grid for this ternary subplot .", + "default": 0, + "minimum": 0 + }, + "column": { + "type": "integer", + "description": "If there is a layout grid, use the domain for this column in the grid for this ternary subplot .", + "default": 0, + "minimum": 0 + } + } + }, + "bgcolor": { + "type": "string", + "description": "Set the background color of the subplot", + "default": "#fff" + }, + "sum": { + "type": "number", + "description": "The number each triplet should sum to, and the maximum range of each axis", + "default": 1, + "minimum": 0 + }, + "aaxis": { + "type": "object", + "properties": { + "title": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Sets the title of this axis. Note that before the existence of `title.text`, the title's contents used to be defined as the `title` attribute itself. This behavior has been deprecated." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + } + } + }, + "color": { + "type": "string", + "description": "Sets default for all colors associated with this axis all at once: line, font, tick, and grid colors. Grid color is lightened by blending this with the plot background Individual pieces can override this.", + "default": "#444" + }, + "tickmode": { + "type": "string", + "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).", + "enum": [ + "auto", + "linear", + "array" + ] + }, + "nticks": { + "type": "integer", + "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", + "default": 6, + "minimum": 1 + }, + "tick0": { + "type": "string", + "description": "Sets the placement of the first tick on this axis. Use with `dtick`. If the axis `type` is *log*, then you must take the log of your starting tick (e.g. to set the starting tick to 100, set the `tick0` to 2) except when `dtick`=*L* (see `dtick` for more info). If the axis `type` is *date*, it should be a date string, like date data. If the axis `type` is *category*, it should be a number, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "dtick": { + "type": "string", + "description": "Sets the step in-between ticks on this axis. Use with `tick0`. Must be a positive number, or special strings available to *log* and *date* axes. If the axis `type` is *log*, then ticks are set every 10^(n*dtick) where n is the tick number. For example, to set a tick mark at 1, 10, 100, 1000, ... set dtick to 1. To set tick marks at 1, 100, 10000, ... set dtick to 2. To set tick marks at 1, 5, 25, 125, 625, 3125, ... set dtick to log_10(5), or 0.69897000433. *log* has several special values; *L*, where `f` is a positive number, gives ticks linearly spaced in value (but not position). For example `tick0` = 0.1, `dtick` = *L0.5* will put ticks at 0.1, 0.6, 1.1, 1.6 etc. To show powers of 10 plus small digits between, use *D1* (all digits) or *D2* (only 2 and 5). `tick0` is ignored for *D1* and *D2*. If the axis `type` is *date*, then you must convert the time to milliseconds. For example, to set the interval between ticks to one day, set `dtick` to 86400000.0. *date* also has special values *M* gives ticks spaced by a number of months. `n` must be a positive integer. To set ticks on the 15th of every third month, set `tick0` to *2000-01-15* and `dtick` to *M3*. To set ticks every 4 years, set `dtick` to *M48*" + }, + "tickvals": { + "type": "string", + "description": "Sets the values at which ticks on this axis appear. Only has an effect if `tickmode` is set to *array*. Used with `ticktext`." + }, + "ticktext": { + "type": "string", + "description": "Sets the text displayed at the ticks position via `tickvals`. Only has an effect if `tickmode` is set to *array*. Used with `tickvals`." + }, + "ticks": { + "type": "string", + "description": "Determines whether ticks are drawn or not. If **, this axis' ticks are not drawn. If *outside* (*inside*), this axis' are drawn outside (inside) the axis lines.", + "enum": [ + "outside", + "inside", + "" + ] + }, + "ticklen": { + "type": "number", + "description": "Sets the tick length (in px).", + "default": 5, + "minimum": 0 + }, + "tickwidth": { + "type": "number", + "description": "Sets the tick width (in px).", + "default": 1, + "minimum": 0 + }, + "tickcolor": { + "type": "string", + "description": "Sets the tick color.", + "default": "#444" + }, + "showticklabels": { + "type": "boolean", + "description": "Determines whether or not the tick labels are drawn.", + "default": true + }, + "showtickprefix": { + "type": "string", + "description": "If *all*, all tick labels are displayed with a prefix. If *first*, only the first tick is displayed with a prefix. If *last*, only the last tick is displayed with a suffix. If *none*, tick prefixes are hidden.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "tickprefix": { + "type": "string", + "description": "Sets a tick label prefix.", + "default": "" + }, + "showticksuffix": { + "type": "string", + "description": "Same as `showtickprefix` but for tick suffixes.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "ticksuffix": { + "type": "string", + "description": "Sets a tick label suffix.", + "default": "" + }, + "showexponent": { + "type": "string", + "description": "If *all*, all exponents are shown besides their significands. If *first*, only the exponent of the first tick is shown. If *last*, only the exponent of the last tick is shown. If *none*, no exponents appear.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "exponentformat": { + "type": "string", + "description": "Determines a formatting rule for the tick exponents. For example, consider the number 1,000,000,000. If *none*, it appears as 1,000,000,000. If *e*, 1e+9. If *E*, 1E+9. If *power*, 1x10^9 (with 9 in a super script). If *SI*, 1G. If *B*, 1B.", + "default": "B", + "enum": [ + "none", + "e", + "E", + "power", + "SI", + "B" + ] + }, + "minexponent": { + "type": "number", + "description": "Hide SI prefix for 10^n if |n| is below this number. This only has an effect when `tickformat` is *SI* or *B*.", + "default": 3, + "minimum": 0 + }, + "separatethousands": { + "type": "boolean", + "description": "If \"true\", even 4-digit integers are separated", + "default": false + }, + "tickfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "tickangle": { + "type": "number", + "description": "Sets the angle of the tick labels with respect to the horizontal. For example, a `tickangle` of -90 draws the tick labels vertically.", + "default": "auto", + "minimum": -180, + "maximum": 180 + }, + "tickformat": { + "type": "string", + "description": "Sets the tick label formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "hoverformat": { + "type": "string", + "description": "Sets the hover text formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "showline": { + "type": "boolean", + "description": "Determines whether or not a line bounding this axis is drawn.", + "default": true + }, + "linecolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "linewidth": { + "type": "number", + "description": "Sets the width (in px) of the axis line.", + "default": 1, + "minimum": 0 + }, + "showgrid": { + "type": "boolean", + "description": "Determines whether or not grid lines are drawn. If *true*, the grid lines are drawn at every tick mark.", + "default": true + }, + "gridcolor": { + "type": "string", + "description": "Sets the color of the grid lines.", + "default": "#eee" + }, + "gridwidth": { + "type": "number", + "description": "Sets the width (in px) of the grid lines.", + "default": 1, + "minimum": 0 + }, + "layer": { + "type": "string", + "description": "Sets the layer on which this axis is displayed. If *above traces*, this axis is displayed above all the subplot's traces If *below traces*, this axis is displayed below all the subplot's traces, but above the grid lines. Useful when used together with scatter-like traces with `cliponaxis` set to *false* to show markers and/or text nodes above this axis.", + "default": "above traces", + "enum": [ + "above traces", + "below traces" + ] + }, + "min": { + "type": "number", + "description": "The minimum value visible on this axis. The maximum is determined by the sum minus the minimum values of the other two axes. The full view corresponds to all the minima set to zero.", + "default": 0, + "minimum": 0 + }, + "uirevision": { + "type": "string", + "description": "Controls persistence of user-driven changes in axis `min`, and `title` if in `editable: true` configuration. Defaults to `ternary.uirevision`." + } + } + }, + "baxis": { + "type": "object", + "properties": { + "title": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Sets the title of this axis. Note that before the existence of `title.text`, the title's contents used to be defined as the `title` attribute itself. This behavior has been deprecated." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + } + } + }, + "color": { + "type": "string", + "description": "Sets default for all colors associated with this axis all at once: line, font, tick, and grid colors. Grid color is lightened by blending this with the plot background Individual pieces can override this.", + "default": "#444" + }, + "tickmode": { + "type": "string", + "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).", + "enum": [ + "auto", + "linear", + "array" + ] + }, + "nticks": { + "type": "integer", + "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", + "default": 6, + "minimum": 1 + }, + "tick0": { + "type": "string", + "description": "Sets the placement of the first tick on this axis. Use with `dtick`. If the axis `type` is *log*, then you must take the log of your starting tick (e.g. to set the starting tick to 100, set the `tick0` to 2) except when `dtick`=*L* (see `dtick` for more info). If the axis `type` is *date*, it should be a date string, like date data. If the axis `type` is *category*, it should be a number, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "dtick": { + "type": "string", + "description": "Sets the step in-between ticks on this axis. Use with `tick0`. Must be a positive number, or special strings available to *log* and *date* axes. If the axis `type` is *log*, then ticks are set every 10^(n*dtick) where n is the tick number. For example, to set a tick mark at 1, 10, 100, 1000, ... set dtick to 1. To set tick marks at 1, 100, 10000, ... set dtick to 2. To set tick marks at 1, 5, 25, 125, 625, 3125, ... set dtick to log_10(5), or 0.69897000433. *log* has several special values; *L*, where `f` is a positive number, gives ticks linearly spaced in value (but not position). For example `tick0` = 0.1, `dtick` = *L0.5* will put ticks at 0.1, 0.6, 1.1, 1.6 etc. To show powers of 10 plus small digits between, use *D1* (all digits) or *D2* (only 2 and 5). `tick0` is ignored for *D1* and *D2*. If the axis `type` is *date*, then you must convert the time to milliseconds. For example, to set the interval between ticks to one day, set `dtick` to 86400000.0. *date* also has special values *M* gives ticks spaced by a number of months. `n` must be a positive integer. To set ticks on the 15th of every third month, set `tick0` to *2000-01-15* and `dtick` to *M3*. To set ticks every 4 years, set `dtick` to *M48*" + }, + "tickvals": { + "type": "string", + "description": "Sets the values at which ticks on this axis appear. Only has an effect if `tickmode` is set to *array*. Used with `ticktext`." + }, + "ticktext": { + "type": "string", + "description": "Sets the text displayed at the ticks position via `tickvals`. Only has an effect if `tickmode` is set to *array*. Used with `tickvals`." + }, + "ticks": { + "type": "string", + "description": "Determines whether ticks are drawn or not. If **, this axis' ticks are not drawn. If *outside* (*inside*), this axis' are drawn outside (inside) the axis lines.", + "enum": [ + "outside", + "inside", + "" + ] + }, + "ticklen": { + "type": "number", + "description": "Sets the tick length (in px).", + "default": 5, + "minimum": 0 + }, + "tickwidth": { + "type": "number", + "description": "Sets the tick width (in px).", + "default": 1, + "minimum": 0 + }, + "tickcolor": { + "type": "string", + "description": "Sets the tick color.", + "default": "#444" + }, + "showticklabels": { + "type": "boolean", + "description": "Determines whether or not the tick labels are drawn.", + "default": true + }, + "showtickprefix": { + "type": "string", + "description": "If *all*, all tick labels are displayed with a prefix. If *first*, only the first tick is displayed with a prefix. If *last*, only the last tick is displayed with a suffix. If *none*, tick prefixes are hidden.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "tickprefix": { + "type": "string", + "description": "Sets a tick label prefix.", + "default": "" + }, + "showticksuffix": { + "type": "string", + "description": "Same as `showtickprefix` but for tick suffixes.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "ticksuffix": { + "type": "string", + "description": "Sets a tick label suffix.", + "default": "" + }, + "showexponent": { + "type": "string", + "description": "If *all*, all exponents are shown besides their significands. If *first*, only the exponent of the first tick is shown. If *last*, only the exponent of the last tick is shown. If *none*, no exponents appear.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "exponentformat": { + "type": "string", + "description": "Determines a formatting rule for the tick exponents. For example, consider the number 1,000,000,000. If *none*, it appears as 1,000,000,000. If *e*, 1e+9. If *E*, 1E+9. If *power*, 1x10^9 (with 9 in a super script). If *SI*, 1G. If *B*, 1B.", + "default": "B", + "enum": [ + "none", + "e", + "E", + "power", + "SI", + "B" + ] + }, + "minexponent": { + "type": "number", + "description": "Hide SI prefix for 10^n if |n| is below this number. This only has an effect when `tickformat` is *SI* or *B*.", + "default": 3, + "minimum": 0 + }, + "separatethousands": { + "type": "boolean", + "description": "If \"true\", even 4-digit integers are separated", + "default": false + }, + "tickfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "tickangle": { + "type": "number", + "description": "Sets the angle of the tick labels with respect to the horizontal. For example, a `tickangle` of -90 draws the tick labels vertically.", + "default": "auto", + "minimum": -180, + "maximum": 180 + }, + "tickformat": { + "type": "string", + "description": "Sets the tick label formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "hoverformat": { + "type": "string", + "description": "Sets the hover text formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "showline": { + "type": "boolean", + "description": "Determines whether or not a line bounding this axis is drawn.", + "default": true + }, + "linecolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "linewidth": { + "type": "number", + "description": "Sets the width (in px) of the axis line.", + "default": 1, + "minimum": 0 + }, + "showgrid": { + "type": "boolean", + "description": "Determines whether or not grid lines are drawn. If *true*, the grid lines are drawn at every tick mark.", + "default": true + }, + "gridcolor": { + "type": "string", + "description": "Sets the color of the grid lines.", + "default": "#eee" + }, + "gridwidth": { + "type": "number", + "description": "Sets the width (in px) of the grid lines.", + "default": 1, + "minimum": 0 + }, + "layer": { + "type": "string", + "description": "Sets the layer on which this axis is displayed. If *above traces*, this axis is displayed above all the subplot's traces If *below traces*, this axis is displayed below all the subplot's traces, but above the grid lines. Useful when used together with scatter-like traces with `cliponaxis` set to *false* to show markers and/or text nodes above this axis.", + "default": "above traces", + "enum": [ + "above traces", + "below traces" + ] + }, + "min": { + "type": "number", + "description": "The minimum value visible on this axis. The maximum is determined by the sum minus the minimum values of the other two axes. The full view corresponds to all the minima set to zero.", + "default": 0, + "minimum": 0 + }, + "uirevision": { + "type": "string", + "description": "Controls persistence of user-driven changes in axis `min`, and `title` if in `editable: true` configuration. Defaults to `ternary.uirevision`." + } + } + }, + "caxis": { + "type": "object", + "properties": { + "title": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Sets the title of this axis. Note that before the existence of `title.text`, the title's contents used to be defined as the `title` attribute itself. This behavior has been deprecated." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + } + } + }, + "color": { + "type": "string", + "description": "Sets default for all colors associated with this axis all at once: line, font, tick, and grid colors. Grid color is lightened by blending this with the plot background Individual pieces can override this.", + "default": "#444" + }, + "tickmode": { + "type": "string", + "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).", + "enum": [ + "auto", + "linear", + "array" + ] + }, + "nticks": { + "type": "integer", + "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", + "default": 6, + "minimum": 1 + }, + "tick0": { + "type": "string", + "description": "Sets the placement of the first tick on this axis. Use with `dtick`. If the axis `type` is *log*, then you must take the log of your starting tick (e.g. to set the starting tick to 100, set the `tick0` to 2) except when `dtick`=*L* (see `dtick` for more info). If the axis `type` is *date*, it should be a date string, like date data. If the axis `type` is *category*, it should be a number, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "dtick": { + "type": "string", + "description": "Sets the step in-between ticks on this axis. Use with `tick0`. Must be a positive number, or special strings available to *log* and *date* axes. If the axis `type` is *log*, then ticks are set every 10^(n*dtick) where n is the tick number. For example, to set a tick mark at 1, 10, 100, 1000, ... set dtick to 1. To set tick marks at 1, 100, 10000, ... set dtick to 2. To set tick marks at 1, 5, 25, 125, 625, 3125, ... set dtick to log_10(5), or 0.69897000433. *log* has several special values; *L*, where `f` is a positive number, gives ticks linearly spaced in value (but not position). For example `tick0` = 0.1, `dtick` = *L0.5* will put ticks at 0.1, 0.6, 1.1, 1.6 etc. To show powers of 10 plus small digits between, use *D1* (all digits) or *D2* (only 2 and 5). `tick0` is ignored for *D1* and *D2*. If the axis `type` is *date*, then you must convert the time to milliseconds. For example, to set the interval between ticks to one day, set `dtick` to 86400000.0. *date* also has special values *M* gives ticks spaced by a number of months. `n` must be a positive integer. To set ticks on the 15th of every third month, set `tick0` to *2000-01-15* and `dtick` to *M3*. To set ticks every 4 years, set `dtick` to *M48*" + }, + "tickvals": { + "type": "string", + "description": "Sets the values at which ticks on this axis appear. Only has an effect if `tickmode` is set to *array*. Used with `ticktext`." + }, + "ticktext": { + "type": "string", + "description": "Sets the text displayed at the ticks position via `tickvals`. Only has an effect if `tickmode` is set to *array*. Used with `tickvals`." + }, + "ticks": { + "type": "string", + "description": "Determines whether ticks are drawn or not. If **, this axis' ticks are not drawn. If *outside* (*inside*), this axis' are drawn outside (inside) the axis lines.", + "enum": [ + "outside", + "inside", + "" + ] + }, + "ticklen": { + "type": "number", + "description": "Sets the tick length (in px).", + "default": 5, + "minimum": 0 + }, + "tickwidth": { + "type": "number", + "description": "Sets the tick width (in px).", + "default": 1, + "minimum": 0 + }, + "tickcolor": { + "type": "string", + "description": "Sets the tick color.", + "default": "#444" + }, + "showticklabels": { + "type": "boolean", + "description": "Determines whether or not the tick labels are drawn.", + "default": true + }, + "showtickprefix": { + "type": "string", + "description": "If *all*, all tick labels are displayed with a prefix. If *first*, only the first tick is displayed with a prefix. If *last*, only the last tick is displayed with a suffix. If *none*, tick prefixes are hidden.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "tickprefix": { + "type": "string", + "description": "Sets a tick label prefix.", + "default": "" + }, + "showticksuffix": { + "type": "string", + "description": "Same as `showtickprefix` but for tick suffixes.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "ticksuffix": { + "type": "string", + "description": "Sets a tick label suffix.", + "default": "" + }, + "showexponent": { + "type": "string", + "description": "If *all*, all exponents are shown besides their significands. If *first*, only the exponent of the first tick is shown. If *last*, only the exponent of the last tick is shown. If *none*, no exponents appear.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "exponentformat": { + "type": "string", + "description": "Determines a formatting rule for the tick exponents. For example, consider the number 1,000,000,000. If *none*, it appears as 1,000,000,000. If *e*, 1e+9. If *E*, 1E+9. If *power*, 1x10^9 (with 9 in a super script). If *SI*, 1G. If *B*, 1B.", + "default": "B", + "enum": [ + "none", + "e", + "E", + "power", + "SI", + "B" + ] + }, + "minexponent": { + "type": "number", + "description": "Hide SI prefix for 10^n if |n| is below this number. This only has an effect when `tickformat` is *SI* or *B*.", + "default": 3, + "minimum": 0 + }, + "separatethousands": { + "type": "boolean", + "description": "If \"true\", even 4-digit integers are separated", + "default": false + }, + "tickfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "tickangle": { + "type": "number", + "description": "Sets the angle of the tick labels with respect to the horizontal. For example, a `tickangle` of -90 draws the tick labels vertically.", + "default": "auto", + "minimum": -180, + "maximum": 180 + }, + "tickformat": { + "type": "string", + "description": "Sets the tick label formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "hoverformat": { + "type": "string", + "description": "Sets the hover text formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "showline": { + "type": "boolean", + "description": "Determines whether or not a line bounding this axis is drawn.", + "default": true + }, + "linecolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "linewidth": { + "type": "number", + "description": "Sets the width (in px) of the axis line.", + "default": 1, + "minimum": 0 + }, + "showgrid": { + "type": "boolean", + "description": "Determines whether or not grid lines are drawn. If *true*, the grid lines are drawn at every tick mark.", + "default": true + }, + "gridcolor": { + "type": "string", + "description": "Sets the color of the grid lines.", + "default": "#eee" + }, + "gridwidth": { + "type": "number", + "description": "Sets the width (in px) of the grid lines.", + "default": 1, + "minimum": 0 + }, + "layer": { + "type": "string", + "description": "Sets the layer on which this axis is displayed. If *above traces*, this axis is displayed above all the subplot's traces If *below traces*, this axis is displayed below all the subplot's traces, but above the grid lines. Useful when used together with scatter-like traces with `cliponaxis` set to *false* to show markers and/or text nodes above this axis.", + "default": "above traces", + "enum": [ + "above traces", + "below traces" + ] + }, + "min": { + "type": "number", + "description": "The minimum value visible on this axis. The maximum is determined by the sum minus the minimum values of the other two axes. The full view corresponds to all the minima set to zero.", + "default": 0, + "minimum": 0 + }, + "uirevision": { + "type": "string", + "description": "Controls persistence of user-driven changes in axis `min`, and `title` if in `editable: true` configuration. Defaults to `ternary.uirevision`." + } + } + }, + "uirevision": { + "type": "string", + "description": "Controls persistence of user-driven changes in axis `min` and `title`, if not overridden in the individual axes. Defaults to `layout.uirevision`." + } + } + }, + "scene": { + "type": "object", + "properties": { + "bgcolor": { + "type": "string", + "default": "rgba(0,0,0,0)" + }, + "camera": { + "type": "object", + "properties": { + "up": { + "type": "object", + "properties": { + "x": { + "type": "number", + "default": 0 + }, + "y": { + "type": "number", + "default": 0 + }, + "z": { + "type": "number", + "default": 1 + } + } + }, + "center": { + "type": "object", + "properties": { + "x": { + "type": "number", + "default": 0 + }, + "y": { + "type": "number", + "default": 0 + }, + "z": { + "type": "number", + "default": 0 + } + } + }, + "eye": { + "type": "object", + "properties": { + "x": { + "type": "number", + "default": 1.25 + }, + "y": { + "type": "number", + "default": 1.25 + }, + "z": { + "type": "number", + "default": 1.25 + } + } + }, + "projection": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Sets the projection type. The projection type could be either *perspective* or *orthographic*. The default is *perspective*.", + "default": "perspective", + "enum": [ + "perspective", + "orthographic" + ] + } + } + } + } + }, + "domain": { + "type": "object", + "properties": { + "x": { + "type": "string", + "description": "Sets the horizontal domain of this scene subplot (in plot fraction).", + "default": [ + 0, + 1 + ] + }, + "y": { + "type": "string", + "description": "Sets the vertical domain of this scene subplot (in plot fraction).", + "default": [ + 0, + 1 + ] + }, + "row": { + "type": "integer", + "description": "If there is a layout grid, use the domain for this row in the grid for this scene subplot .", + "default": 0, + "minimum": 0 + }, + "column": { + "type": "integer", + "description": "If there is a layout grid, use the domain for this column in the grid for this scene subplot .", + "default": 0, + "minimum": 0 + } + } + }, + "aspectmode": { + "type": "string", + "description": "If *cube*, this scene's axes are drawn as a cube, regardless of the axes' ranges. If *data*, this scene's axes are drawn in proportion with the axes' ranges. If *manual*, this scene's axes are drawn in proportion with the input of *aspectratio* (the default behavior if *aspectratio* is provided). If *auto*, this scene's axes are drawn using the results of *data* except when one axis is more than four times the size of the two others, where in that case the results of *cube* are used.", + "default": "auto", + "enum": [ + "auto", + "cube", + "data", + "manual" + ] + }, + "aspectratio": { + "type": "object", + "properties": { + "x": { + "type": "number", + "minimum": 0 + }, + "y": { + "type": "number", + "minimum": 0 + }, + "z": { + "type": "number", + "minimum": 0 + } + } + }, + "xaxis": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "A single toggle to hide the axis while preserving interaction like dragging. Default is true when a cheater plot is present on the axis, otherwise false" + }, + "showspikes": { + "type": "boolean", + "description": "Sets whether or not spikes starting from data points to this axis' wall are shown on hover.", + "default": true + }, + "spikesides": { + "type": "boolean", + "description": "Sets whether or not spikes extending from the projection data points to this axis' wall boundaries are shown on hover.", + "default": true + }, + "spikethickness": { + "type": "number", + "description": "Sets the thickness (in px) of the spikes.", + "default": 2, + "minimum": 0 + }, + "spikecolor": { + "type": "string", + "description": "Sets the color of the spikes.", + "default": "#444" + }, + "showbackground": { + "type": "boolean", + "description": "Sets whether or not this axis' wall has a background color.", + "default": false + }, + "backgroundcolor": { + "type": "string", + "description": "Sets the background color of this axis' wall.", + "default": "rgba(204, 204, 204, 0.5)" + }, + "showaxeslabels": { + "type": "boolean", + "description": "Sets whether or not this axis is labeled", + "default": true + }, + "color": { + "type": "string", + "description": "Sets default for all colors associated with this axis all at once: line, font, tick, and grid colors. Grid color is lightened by blending this with the plot background Individual pieces can override this.", + "default": "#444" + }, + "categoryorder": { + "type": "string", + "description": "Specifies the ordering logic for the case of categorical variables. By default, plotly uses *trace*, which specifies the order that is present in the data supplied. Set `categoryorder` to *category ascending* or *category descending* if order should be determined by the alphanumerical order of the category names. Set `categoryorder` to *array* to derive the ordering from the attribute `categoryarray`. If a category is not found in the `categoryarray` array, the sorting behavior for that attribute will be identical to the *trace* mode. The unspecified categories will follow the categories in `categoryarray`. Set `categoryorder` to *total ascending* or *total descending* if order should be determined by the numerical order of the values. Similarly, the order can be determined by the min, max, sum, mean or median of all the values.", + "default": "trace", + "enum": [ + "trace", + "category ascending", + "category descending", + "array", + "total ascending", + "total descending", + "min ascending", + "min descending", + "max ascending", + "max descending", + "sum ascending", + "sum descending", + "mean ascending", + "mean descending", + "median ascending", + "median descending" + ] + }, + "categoryarray": { + "type": "string", + "description": "Sets the order in which categories on this axis appear. Only has an effect if `categoryorder` is set to *array*. Used with `categoryorder`." + }, + "title": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Sets the title of this axis. Note that before the existence of `title.text`, the title's contents used to be defined as the `title` attribute itself. This behavior has been deprecated." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + } + } + }, + "type": { + "type": "string", + "description": "Sets the axis type. By default, plotly attempts to determined the axis type by looking into the data of the traces that referenced the axis in question.", + "default": "-", + "enum": [ + "-", + "linear", + "log", + "date", + "category" + ] + }, + "autotypenumbers": { + "type": "string", + "description": "Using *strict* a numeric string in trace data is not converted to a number. Using *convert types* a numeric string in trace data may be treated as a number during automatic axis `type` detection. Defaults to layout.autotypenumbers.", + "default": "convert types", + "enum": [ + "convert types", + "strict" + ] + }, + "autorange": { + "type": "string", + "description": "Determines whether or not the range of this axis is computed in relation to the input data. See `rangemode` for more info. If `range` is provided, then `autorange` is set to *false*.", + "default": true, + "enum": [ + true, + false, + "reversed" + ] + }, + "rangemode": { + "type": "string", + "description": "If *normal*, the range is computed in relation to the extrema of the input data. If *tozero*`, the range extends to 0, regardless of the input data If *nonnegative*, the range is non-negative, regardless of the input data. Applies only to linear axes.", + "default": "normal", + "enum": [ + "normal", + "tozero", + "nonnegative" + ] + }, + "range": { + "type": "string", + "description": "Sets the range of this axis. If the axis `type` is *log*, then you must take the log of your desired range (e.g. to set the range from 1 to 100, set the range from 0 to 2). If the axis `type` is *date*, it should be date strings, like date data, though Date objects and unix milliseconds will be accepted and converted to strings. If the axis `type` is *category*, it should be numbers, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "tickmode": { + "type": "string", + "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).", + "enum": [ + "auto", + "linear", + "array" + ] + }, + "nticks": { + "type": "integer", + "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", + "default": 0, + "minimum": 0 + }, + "tick0": { + "type": "string", + "description": "Sets the placement of the first tick on this axis. Use with `dtick`. If the axis `type` is *log*, then you must take the log of your starting tick (e.g. to set the starting tick to 100, set the `tick0` to 2) except when `dtick`=*L* (see `dtick` for more info). If the axis `type` is *date*, it should be a date string, like date data. If the axis `type` is *category*, it should be a number, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "dtick": { + "type": "string", + "description": "Sets the step in-between ticks on this axis. Use with `tick0`. Must be a positive number, or special strings available to *log* and *date* axes. If the axis `type` is *log*, then ticks are set every 10^(n*dtick) where n is the tick number. For example, to set a tick mark at 1, 10, 100, 1000, ... set dtick to 1. To set tick marks at 1, 100, 10000, ... set dtick to 2. To set tick marks at 1, 5, 25, 125, 625, 3125, ... set dtick to log_10(5), or 0.69897000433. *log* has several special values; *L*, where `f` is a positive number, gives ticks linearly spaced in value (but not position). For example `tick0` = 0.1, `dtick` = *L0.5* will put ticks at 0.1, 0.6, 1.1, 1.6 etc. To show powers of 10 plus small digits between, use *D1* (all digits) or *D2* (only 2 and 5). `tick0` is ignored for *D1* and *D2*. If the axis `type` is *date*, then you must convert the time to milliseconds. For example, to set the interval between ticks to one day, set `dtick` to 86400000.0. *date* also has special values *M* gives ticks spaced by a number of months. `n` must be a positive integer. To set ticks on the 15th of every third month, set `tick0` to *2000-01-15* and `dtick` to *M3*. To set ticks every 4 years, set `dtick` to *M48*" + }, + "tickvals": { + "type": "string", + "description": "Sets the values at which ticks on this axis appear. Only has an effect if `tickmode` is set to *array*. Used with `ticktext`." + }, + "ticktext": { + "type": "string", + "description": "Sets the text displayed at the ticks position via `tickvals`. Only has an effect if `tickmode` is set to *array*. Used with `tickvals`." + }, + "ticks": { + "type": "string", + "description": "Determines whether ticks are drawn or not. If **, this axis' ticks are not drawn. If *outside* (*inside*), this axis' are drawn outside (inside) the axis lines.", + "enum": [ + "outside", + "inside", + "" + ] + }, + "mirror": { + "type": "string", + "description": "Determines if the axis lines or/and ticks are mirrored to the opposite side of the plotting area. If *true*, the axis lines are mirrored. If *ticks*, the axis lines and ticks are mirrored. If *false*, mirroring is disable. If *all*, axis lines are mirrored on all shared-axes subplots. If *allticks*, axis lines and ticks are mirrored on all shared-axes subplots.", + "default": false, + "enum": [ + true, + "ticks", + false, + "all", + "allticks" + ] + }, + "ticklen": { + "type": "number", + "description": "Sets the tick length (in px).", + "default": 5, + "minimum": 0 + }, + "tickwidth": { + "type": "number", + "description": "Sets the tick width (in px).", + "default": 1, + "minimum": 0 + }, + "tickcolor": { + "type": "string", + "description": "Sets the tick color.", + "default": "#444" + }, + "showticklabels": { + "type": "boolean", + "description": "Determines whether or not the tick labels are drawn.", + "default": true + }, + "tickfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "tickangle": { + "type": "number", + "description": "Sets the angle of the tick labels with respect to the horizontal. For example, a `tickangle` of -90 draws the tick labels vertically.", + "default": "auto", + "minimum": -180, + "maximum": 180 + }, + "tickprefix": { + "type": "string", + "description": "Sets a tick label prefix.", + "default": "" + }, + "showtickprefix": { + "type": "string", + "description": "If *all*, all tick labels are displayed with a prefix. If *first*, only the first tick is displayed with a prefix. If *last*, only the last tick is displayed with a suffix. If *none*, tick prefixes are hidden.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "ticksuffix": { + "type": "string", + "description": "Sets a tick label suffix.", + "default": "" + }, + "showticksuffix": { + "type": "string", + "description": "Same as `showtickprefix` but for tick suffixes.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "showexponent": { + "type": "string", + "description": "If *all*, all exponents are shown besides their significands. If *first*, only the exponent of the first tick is shown. If *last*, only the exponent of the last tick is shown. If *none*, no exponents appear.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "exponentformat": { + "type": "string", + "description": "Determines a formatting rule for the tick exponents. For example, consider the number 1,000,000,000. If *none*, it appears as 1,000,000,000. If *e*, 1e+9. If *E*, 1E+9. If *power*, 1x10^9 (with 9 in a super script). If *SI*, 1G. If *B*, 1B.", + "default": "B", + "enum": [ + "none", + "e", + "E", + "power", + "SI", + "B" + ] + }, + "minexponent": { + "type": "number", + "description": "Hide SI prefix for 10^n if |n| is below this number. This only has an effect when `tickformat` is *SI* or *B*.", + "default": 3, + "minimum": 0 + }, + "separatethousands": { + "type": "boolean", + "description": "If \"true\", even 4-digit integers are separated", + "default": false + }, + "tickformat": { + "type": "string", + "description": "Sets the tick label formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "hoverformat": { + "type": "string", + "description": "Sets the hover text formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "showline": { + "type": "boolean", + "description": "Determines whether or not a line bounding this axis is drawn.", + "default": false + }, + "linecolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "linewidth": { + "type": "number", + "description": "Sets the width (in px) of the axis line.", + "default": 1, + "minimum": 0 + }, + "showgrid": { + "type": "boolean", + "description": "Determines whether or not grid lines are drawn. If *true*, the grid lines are drawn at every tick mark." + }, + "gridcolor": { + "type": "string", + "description": "Sets the color of the grid lines.", + "default": "rgb(204, 204, 204)" + }, + "gridwidth": { + "type": "number", + "description": "Sets the width (in px) of the grid lines.", + "default": 1, + "minimum": 0 + }, + "zeroline": { + "type": "boolean", + "description": "Determines whether or not a line is drawn at along the 0 value of this axis. If *true*, the zero line is drawn on top of the grid lines." + }, + "zerolinecolor": { + "type": "string", + "description": "Sets the line color of the zero line.", + "default": "#444" + }, + "zerolinewidth": { + "type": "number", + "description": "Sets the width (in px) of the zero line.", + "default": 1 + }, + "calendar": { + "type": "string", + "description": "Sets the calendar system to use for `range` and `tick0` if this is a date axis. This does not set the calendar for interpreting data on this axis, that's specified in the trace or via the global `layout.calendar`", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + } + } + }, + "yaxis": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "A single toggle to hide the axis while preserving interaction like dragging. Default is true when a cheater plot is present on the axis, otherwise false" + }, + "showspikes": { + "type": "boolean", + "description": "Sets whether or not spikes starting from data points to this axis' wall are shown on hover.", + "default": true + }, + "spikesides": { + "type": "boolean", + "description": "Sets whether or not spikes extending from the projection data points to this axis' wall boundaries are shown on hover.", + "default": true + }, + "spikethickness": { + "type": "number", + "description": "Sets the thickness (in px) of the spikes.", + "default": 2, + "minimum": 0 + }, + "spikecolor": { + "type": "string", + "description": "Sets the color of the spikes.", + "default": "#444" + }, + "showbackground": { + "type": "boolean", + "description": "Sets whether or not this axis' wall has a background color.", + "default": false + }, + "backgroundcolor": { + "type": "string", + "description": "Sets the background color of this axis' wall.", + "default": "rgba(204, 204, 204, 0.5)" + }, + "showaxeslabels": { + "type": "boolean", + "description": "Sets whether or not this axis is labeled", + "default": true + }, + "color": { + "type": "string", + "description": "Sets default for all colors associated with this axis all at once: line, font, tick, and grid colors. Grid color is lightened by blending this with the plot background Individual pieces can override this.", + "default": "#444" + }, + "categoryorder": { + "type": "string", + "description": "Specifies the ordering logic for the case of categorical variables. By default, plotly uses *trace*, which specifies the order that is present in the data supplied. Set `categoryorder` to *category ascending* or *category descending* if order should be determined by the alphanumerical order of the category names. Set `categoryorder` to *array* to derive the ordering from the attribute `categoryarray`. If a category is not found in the `categoryarray` array, the sorting behavior for that attribute will be identical to the *trace* mode. The unspecified categories will follow the categories in `categoryarray`. Set `categoryorder` to *total ascending* or *total descending* if order should be determined by the numerical order of the values. Similarly, the order can be determined by the min, max, sum, mean or median of all the values.", + "default": "trace", + "enum": [ + "trace", + "category ascending", + "category descending", + "array", + "total ascending", + "total descending", + "min ascending", + "min descending", + "max ascending", + "max descending", + "sum ascending", + "sum descending", + "mean ascending", + "mean descending", + "median ascending", + "median descending" + ] + }, + "categoryarray": { + "type": "string", + "description": "Sets the order in which categories on this axis appear. Only has an effect if `categoryorder` is set to *array*. Used with `categoryorder`." + }, + "title": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Sets the title of this axis. Note that before the existence of `title.text`, the title's contents used to be defined as the `title` attribute itself. This behavior has been deprecated." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + } + } + }, + "type": { + "type": "string", + "description": "Sets the axis type. By default, plotly attempts to determined the axis type by looking into the data of the traces that referenced the axis in question.", + "default": "-", + "enum": [ + "-", + "linear", + "log", + "date", + "category" + ] + }, + "autotypenumbers": { + "type": "string", + "description": "Using *strict* a numeric string in trace data is not converted to a number. Using *convert types* a numeric string in trace data may be treated as a number during automatic axis `type` detection. Defaults to layout.autotypenumbers.", + "default": "convert types", + "enum": [ + "convert types", + "strict" + ] + }, + "autorange": { + "type": "string", + "description": "Determines whether or not the range of this axis is computed in relation to the input data. See `rangemode` for more info. If `range` is provided, then `autorange` is set to *false*.", + "default": true, + "enum": [ + true, + false, + "reversed" + ] + }, + "rangemode": { + "type": "string", + "description": "If *normal*, the range is computed in relation to the extrema of the input data. If *tozero*`, the range extends to 0, regardless of the input data If *nonnegative*, the range is non-negative, regardless of the input data. Applies only to linear axes.", + "default": "normal", + "enum": [ + "normal", + "tozero", + "nonnegative" + ] + }, + "range": { + "type": "string", + "description": "Sets the range of this axis. If the axis `type` is *log*, then you must take the log of your desired range (e.g. to set the range from 1 to 100, set the range from 0 to 2). If the axis `type` is *date*, it should be date strings, like date data, though Date objects and unix milliseconds will be accepted and converted to strings. If the axis `type` is *category*, it should be numbers, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "tickmode": { + "type": "string", + "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).", + "enum": [ + "auto", + "linear", + "array" + ] + }, + "nticks": { + "type": "integer", + "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", + "default": 0, + "minimum": 0 + }, + "tick0": { + "type": "string", + "description": "Sets the placement of the first tick on this axis. Use with `dtick`. If the axis `type` is *log*, then you must take the log of your starting tick (e.g. to set the starting tick to 100, set the `tick0` to 2) except when `dtick`=*L* (see `dtick` for more info). If the axis `type` is *date*, it should be a date string, like date data. If the axis `type` is *category*, it should be a number, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "dtick": { + "type": "string", + "description": "Sets the step in-between ticks on this axis. Use with `tick0`. Must be a positive number, or special strings available to *log* and *date* axes. If the axis `type` is *log*, then ticks are set every 10^(n*dtick) where n is the tick number. For example, to set a tick mark at 1, 10, 100, 1000, ... set dtick to 1. To set tick marks at 1, 100, 10000, ... set dtick to 2. To set tick marks at 1, 5, 25, 125, 625, 3125, ... set dtick to log_10(5), or 0.69897000433. *log* has several special values; *L*, where `f` is a positive number, gives ticks linearly spaced in value (but not position). For example `tick0` = 0.1, `dtick` = *L0.5* will put ticks at 0.1, 0.6, 1.1, 1.6 etc. To show powers of 10 plus small digits between, use *D1* (all digits) or *D2* (only 2 and 5). `tick0` is ignored for *D1* and *D2*. If the axis `type` is *date*, then you must convert the time to milliseconds. For example, to set the interval between ticks to one day, set `dtick` to 86400000.0. *date* also has special values *M* gives ticks spaced by a number of months. `n` must be a positive integer. To set ticks on the 15th of every third month, set `tick0` to *2000-01-15* and `dtick` to *M3*. To set ticks every 4 years, set `dtick` to *M48*" + }, + "tickvals": { + "type": "string", + "description": "Sets the values at which ticks on this axis appear. Only has an effect if `tickmode` is set to *array*. Used with `ticktext`." + }, + "ticktext": { + "type": "string", + "description": "Sets the text displayed at the ticks position via `tickvals`. Only has an effect if `tickmode` is set to *array*. Used with `tickvals`." + }, + "ticks": { + "type": "string", + "description": "Determines whether ticks are drawn or not. If **, this axis' ticks are not drawn. If *outside* (*inside*), this axis' are drawn outside (inside) the axis lines.", + "enum": [ + "outside", + "inside", + "" + ] + }, + "mirror": { + "type": "string", + "description": "Determines if the axis lines or/and ticks are mirrored to the opposite side of the plotting area. If *true*, the axis lines are mirrored. If *ticks*, the axis lines and ticks are mirrored. If *false*, mirroring is disable. If *all*, axis lines are mirrored on all shared-axes subplots. If *allticks*, axis lines and ticks are mirrored on all shared-axes subplots.", + "default": false, + "enum": [ + true, + "ticks", + false, + "all", + "allticks" + ] + }, + "ticklen": { + "type": "number", + "description": "Sets the tick length (in px).", + "default": 5, + "minimum": 0 + }, + "tickwidth": { + "type": "number", + "description": "Sets the tick width (in px).", + "default": 1, + "minimum": 0 + }, + "tickcolor": { + "type": "string", + "description": "Sets the tick color.", + "default": "#444" + }, + "showticklabels": { + "type": "boolean", + "description": "Determines whether or not the tick labels are drawn.", + "default": true + }, + "tickfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "tickangle": { + "type": "number", + "description": "Sets the angle of the tick labels with respect to the horizontal. For example, a `tickangle` of -90 draws the tick labels vertically.", + "default": "auto", + "minimum": -180, + "maximum": 180 + }, + "tickprefix": { + "type": "string", + "description": "Sets a tick label prefix.", + "default": "" + }, + "showtickprefix": { + "type": "string", + "description": "If *all*, all tick labels are displayed with a prefix. If *first*, only the first tick is displayed with a prefix. If *last*, only the last tick is displayed with a suffix. If *none*, tick prefixes are hidden.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "ticksuffix": { + "type": "string", + "description": "Sets a tick label suffix.", + "default": "" + }, + "showticksuffix": { + "type": "string", + "description": "Same as `showtickprefix` but for tick suffixes.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "showexponent": { + "type": "string", + "description": "If *all*, all exponents are shown besides their significands. If *first*, only the exponent of the first tick is shown. If *last*, only the exponent of the last tick is shown. If *none*, no exponents appear.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "exponentformat": { + "type": "string", + "description": "Determines a formatting rule for the tick exponents. For example, consider the number 1,000,000,000. If *none*, it appears as 1,000,000,000. If *e*, 1e+9. If *E*, 1E+9. If *power*, 1x10^9 (with 9 in a super script). If *SI*, 1G. If *B*, 1B.", + "default": "B", + "enum": [ + "none", + "e", + "E", + "power", + "SI", + "B" + ] + }, + "minexponent": { + "type": "number", + "description": "Hide SI prefix for 10^n if |n| is below this number. This only has an effect when `tickformat` is *SI* or *B*.", + "default": 3, + "minimum": 0 + }, + "separatethousands": { + "type": "boolean", + "description": "If \"true\", even 4-digit integers are separated", + "default": false + }, + "tickformat": { + "type": "string", + "description": "Sets the tick label formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "hoverformat": { + "type": "string", + "description": "Sets the hover text formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "showline": { + "type": "boolean", + "description": "Determines whether or not a line bounding this axis is drawn.", + "default": false + }, + "linecolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "linewidth": { + "type": "number", + "description": "Sets the width (in px) of the axis line.", + "default": 1, + "minimum": 0 + }, + "showgrid": { + "type": "boolean", + "description": "Determines whether or not grid lines are drawn. If *true*, the grid lines are drawn at every tick mark." + }, + "gridcolor": { + "type": "string", + "description": "Sets the color of the grid lines.", + "default": "rgb(204, 204, 204)" + }, + "gridwidth": { + "type": "number", + "description": "Sets the width (in px) of the grid lines.", + "default": 1, + "minimum": 0 + }, + "zeroline": { + "type": "boolean", + "description": "Determines whether or not a line is drawn at along the 0 value of this axis. If *true*, the zero line is drawn on top of the grid lines." + }, + "zerolinecolor": { + "type": "string", + "description": "Sets the line color of the zero line.", + "default": "#444" + }, + "zerolinewidth": { + "type": "number", + "description": "Sets the width (in px) of the zero line.", + "default": 1 + }, + "calendar": { + "type": "string", + "description": "Sets the calendar system to use for `range` and `tick0` if this is a date axis. This does not set the calendar for interpreting data on this axis, that's specified in the trace or via the global `layout.calendar`", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + } + } + }, + "zaxis": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "A single toggle to hide the axis while preserving interaction like dragging. Default is true when a cheater plot is present on the axis, otherwise false" + }, + "showspikes": { + "type": "boolean", + "description": "Sets whether or not spikes starting from data points to this axis' wall are shown on hover.", + "default": true + }, + "spikesides": { + "type": "boolean", + "description": "Sets whether or not spikes extending from the projection data points to this axis' wall boundaries are shown on hover.", + "default": true + }, + "spikethickness": { + "type": "number", + "description": "Sets the thickness (in px) of the spikes.", + "default": 2, + "minimum": 0 + }, + "spikecolor": { + "type": "string", + "description": "Sets the color of the spikes.", + "default": "#444" + }, + "showbackground": { + "type": "boolean", + "description": "Sets whether or not this axis' wall has a background color.", + "default": false + }, + "backgroundcolor": { + "type": "string", + "description": "Sets the background color of this axis' wall.", + "default": "rgba(204, 204, 204, 0.5)" + }, + "showaxeslabels": { + "type": "boolean", + "description": "Sets whether or not this axis is labeled", + "default": true + }, + "color": { + "type": "string", + "description": "Sets default for all colors associated with this axis all at once: line, font, tick, and grid colors. Grid color is lightened by blending this with the plot background Individual pieces can override this.", + "default": "#444" + }, + "categoryorder": { + "type": "string", + "description": "Specifies the ordering logic for the case of categorical variables. By default, plotly uses *trace*, which specifies the order that is present in the data supplied. Set `categoryorder` to *category ascending* or *category descending* if order should be determined by the alphanumerical order of the category names. Set `categoryorder` to *array* to derive the ordering from the attribute `categoryarray`. If a category is not found in the `categoryarray` array, the sorting behavior for that attribute will be identical to the *trace* mode. The unspecified categories will follow the categories in `categoryarray`. Set `categoryorder` to *total ascending* or *total descending* if order should be determined by the numerical order of the values. Similarly, the order can be determined by the min, max, sum, mean or median of all the values.", + "default": "trace", + "enum": [ + "trace", + "category ascending", + "category descending", + "array", + "total ascending", + "total descending", + "min ascending", + "min descending", + "max ascending", + "max descending", + "sum ascending", + "sum descending", + "mean ascending", + "mean descending", + "median ascending", + "median descending" + ] + }, + "categoryarray": { + "type": "string", + "description": "Sets the order in which categories on this axis appear. Only has an effect if `categoryorder` is set to *array*. Used with `categoryorder`." + }, + "title": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Sets the title of this axis. Note that before the existence of `title.text`, the title's contents used to be defined as the `title` attribute itself. This behavior has been deprecated." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + } + } + }, + "type": { + "type": "string", + "description": "Sets the axis type. By default, plotly attempts to determined the axis type by looking into the data of the traces that referenced the axis in question.", + "default": "-", + "enum": [ + "-", + "linear", + "log", + "date", + "category" + ] + }, + "autotypenumbers": { + "type": "string", + "description": "Using *strict* a numeric string in trace data is not converted to a number. Using *convert types* a numeric string in trace data may be treated as a number during automatic axis `type` detection. Defaults to layout.autotypenumbers.", + "default": "convert types", + "enum": [ + "convert types", + "strict" + ] + }, + "autorange": { + "type": "string", + "description": "Determines whether or not the range of this axis is computed in relation to the input data. See `rangemode` for more info. If `range` is provided, then `autorange` is set to *false*.", + "default": true, + "enum": [ + true, + false, + "reversed" + ] + }, + "rangemode": { + "type": "string", + "description": "If *normal*, the range is computed in relation to the extrema of the input data. If *tozero*`, the range extends to 0, regardless of the input data If *nonnegative*, the range is non-negative, regardless of the input data. Applies only to linear axes.", + "default": "normal", + "enum": [ + "normal", + "tozero", + "nonnegative" + ] + }, + "range": { + "type": "string", + "description": "Sets the range of this axis. If the axis `type` is *log*, then you must take the log of your desired range (e.g. to set the range from 1 to 100, set the range from 0 to 2). If the axis `type` is *date*, it should be date strings, like date data, though Date objects and unix milliseconds will be accepted and converted to strings. If the axis `type` is *category*, it should be numbers, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "tickmode": { + "type": "string", + "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).", + "enum": [ + "auto", + "linear", + "array" + ] + }, + "nticks": { + "type": "integer", + "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", + "default": 0, + "minimum": 0 + }, + "tick0": { + "type": "string", + "description": "Sets the placement of the first tick on this axis. Use with `dtick`. If the axis `type` is *log*, then you must take the log of your starting tick (e.g. to set the starting tick to 100, set the `tick0` to 2) except when `dtick`=*L* (see `dtick` for more info). If the axis `type` is *date*, it should be a date string, like date data. If the axis `type` is *category*, it should be a number, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "dtick": { + "type": "string", + "description": "Sets the step in-between ticks on this axis. Use with `tick0`. Must be a positive number, or special strings available to *log* and *date* axes. If the axis `type` is *log*, then ticks are set every 10^(n*dtick) where n is the tick number. For example, to set a tick mark at 1, 10, 100, 1000, ... set dtick to 1. To set tick marks at 1, 100, 10000, ... set dtick to 2. To set tick marks at 1, 5, 25, 125, 625, 3125, ... set dtick to log_10(5), or 0.69897000433. *log* has several special values; *L*, where `f` is a positive number, gives ticks linearly spaced in value (but not position). For example `tick0` = 0.1, `dtick` = *L0.5* will put ticks at 0.1, 0.6, 1.1, 1.6 etc. To show powers of 10 plus small digits between, use *D1* (all digits) or *D2* (only 2 and 5). `tick0` is ignored for *D1* and *D2*. If the axis `type` is *date*, then you must convert the time to milliseconds. For example, to set the interval between ticks to one day, set `dtick` to 86400000.0. *date* also has special values *M* gives ticks spaced by a number of months. `n` must be a positive integer. To set ticks on the 15th of every third month, set `tick0` to *2000-01-15* and `dtick` to *M3*. To set ticks every 4 years, set `dtick` to *M48*" + }, + "tickvals": { + "type": "string", + "description": "Sets the values at which ticks on this axis appear. Only has an effect if `tickmode` is set to *array*. Used with `ticktext`." + }, + "ticktext": { + "type": "string", + "description": "Sets the text displayed at the ticks position via `tickvals`. Only has an effect if `tickmode` is set to *array*. Used with `tickvals`." + }, + "ticks": { + "type": "string", + "description": "Determines whether ticks are drawn or not. If **, this axis' ticks are not drawn. If *outside* (*inside*), this axis' are drawn outside (inside) the axis lines.", + "enum": [ + "outside", + "inside", + "" + ] + }, + "mirror": { + "type": "string", + "description": "Determines if the axis lines or/and ticks are mirrored to the opposite side of the plotting area. If *true*, the axis lines are mirrored. If *ticks*, the axis lines and ticks are mirrored. If *false*, mirroring is disable. If *all*, axis lines are mirrored on all shared-axes subplots. If *allticks*, axis lines and ticks are mirrored on all shared-axes subplots.", + "default": false, + "enum": [ + true, + "ticks", + false, + "all", + "allticks" + ] + }, + "ticklen": { + "type": "number", + "description": "Sets the tick length (in px).", + "default": 5, + "minimum": 0 + }, + "tickwidth": { + "type": "number", + "description": "Sets the tick width (in px).", + "default": 1, + "minimum": 0 + }, + "tickcolor": { + "type": "string", + "description": "Sets the tick color.", + "default": "#444" + }, + "showticklabels": { + "type": "boolean", + "description": "Determines whether or not the tick labels are drawn.", + "default": true + }, + "tickfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "tickangle": { + "type": "number", + "description": "Sets the angle of the tick labels with respect to the horizontal. For example, a `tickangle` of -90 draws the tick labels vertically.", + "default": "auto", + "minimum": -180, + "maximum": 180 + }, + "tickprefix": { + "type": "string", + "description": "Sets a tick label prefix.", + "default": "" + }, + "showtickprefix": { + "type": "string", + "description": "If *all*, all tick labels are displayed with a prefix. If *first*, only the first tick is displayed with a prefix. If *last*, only the last tick is displayed with a suffix. If *none*, tick prefixes are hidden.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "ticksuffix": { + "type": "string", + "description": "Sets a tick label suffix.", + "default": "" + }, + "showticksuffix": { + "type": "string", + "description": "Same as `showtickprefix` but for tick suffixes.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "showexponent": { + "type": "string", + "description": "If *all*, all exponents are shown besides their significands. If *first*, only the exponent of the first tick is shown. If *last*, only the exponent of the last tick is shown. If *none*, no exponents appear.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "exponentformat": { + "type": "string", + "description": "Determines a formatting rule for the tick exponents. For example, consider the number 1,000,000,000. If *none*, it appears as 1,000,000,000. If *e*, 1e+9. If *E*, 1E+9. If *power*, 1x10^9 (with 9 in a super script). If *SI*, 1G. If *B*, 1B.", + "default": "B", + "enum": [ + "none", + "e", + "E", + "power", + "SI", + "B" + ] + }, + "minexponent": { + "type": "number", + "description": "Hide SI prefix for 10^n if |n| is below this number. This only has an effect when `tickformat` is *SI* or *B*.", + "default": 3, + "minimum": 0 + }, + "separatethousands": { + "type": "boolean", + "description": "If \"true\", even 4-digit integers are separated", + "default": false + }, + "tickformat": { + "type": "string", + "description": "Sets the tick label formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "hoverformat": { + "type": "string", + "description": "Sets the hover text formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "showline": { + "type": "boolean", + "description": "Determines whether or not a line bounding this axis is drawn.", + "default": false + }, + "linecolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "linewidth": { + "type": "number", + "description": "Sets the width (in px) of the axis line.", + "default": 1, + "minimum": 0 + }, + "showgrid": { + "type": "boolean", + "description": "Determines whether or not grid lines are drawn. If *true*, the grid lines are drawn at every tick mark." + }, + "gridcolor": { + "type": "string", + "description": "Sets the color of the grid lines.", + "default": "rgb(204, 204, 204)" + }, + "gridwidth": { + "type": "number", + "description": "Sets the width (in px) of the grid lines.", + "default": 1, + "minimum": 0 + }, + "zeroline": { + "type": "boolean", + "description": "Determines whether or not a line is drawn at along the 0 value of this axis. If *true*, the zero line is drawn on top of the grid lines." + }, + "zerolinecolor": { + "type": "string", + "description": "Sets the line color of the zero line.", + "default": "#444" + }, + "zerolinewidth": { + "type": "number", + "description": "Sets the width (in px) of the zero line.", + "default": 1 + }, + "calendar": { + "type": "string", + "description": "Sets the calendar system to use for `range` and `tick0` if this is a date axis. This does not set the calendar for interpreting data on this axis, that's specified in the trace or via the global `layout.calendar`", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + } + } + }, + "dragmode": { + "type": "string", + "description": "Determines the mode of drag interactions for this scene.", + "enum": [ + "orbit", + "turntable", + "zoom", + "pan", + false + ] + }, + "hovermode": { + "type": "string", + "description": "Determines the mode of hover interactions for this scene.", + "default": "closest", + "enum": [ + "closest", + false + ] + }, + "uirevision": { + "type": "string", + "description": "Controls persistence of user-driven changes in camera attributes. Defaults to `layout.uirevision`." + }, + "annotations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Determines whether or not this annotation is visible.", + "default": true + }, + "x": { + "type": "string", + "description": "Sets the annotation's x position." + }, + "y": { + "type": "string", + "description": "Sets the annotation's y position." + }, + "z": { + "type": "string", + "description": "Sets the annotation's z position." + }, + "ax": { + "type": "number", + "description": "Sets the x component of the arrow tail about the arrow head (in pixels)." + }, + "ay": { + "type": "number", + "description": "Sets the y component of the arrow tail about the arrow head (in pixels)." + }, + "xanchor": { + "type": "string", + "description": "Sets the text box's horizontal position anchor This anchor binds the `x` position to the *left*, *center* or *right* of the annotation. For example, if `x` is set to 1, `xref` to *paper* and `xanchor` to *right* then the right-most portion of the annotation lines up with the right-most edge of the plotting area. If *auto*, the anchor is equivalent to *center* for data-referenced annotations or if there is an arrow, whereas for paper-referenced with no arrow, the anchor picked corresponds to the closest side.", + "default": "auto", + "enum": [ + "auto", + "left", + "center", + "right" + ] + }, + "xshift": { + "type": "number", + "description": "Shifts the position of the whole annotation and arrow to the right (positive) or left (negative) by this many pixels.", + "default": 0 + }, + "yanchor": { + "type": "string", + "description": "Sets the text box's vertical position anchor This anchor binds the `y` position to the *top*, *middle* or *bottom* of the annotation. For example, if `y` is set to 1, `yref` to *paper* and `yanchor` to *top* then the top-most portion of the annotation lines up with the top-most edge of the plotting area. If *auto*, the anchor is equivalent to *middle* for data-referenced annotations or if there is an arrow, whereas for paper-referenced with no arrow, the anchor picked corresponds to the closest side.", + "default": "auto", + "enum": [ + "auto", + "top", + "middle", + "bottom" + ] + }, + "yshift": { + "type": "number", + "description": "Shifts the position of the whole annotation and arrow up (positive) or down (negative) by this many pixels.", + "default": 0 + }, + "text": { + "type": "string", + "description": "Sets the text associated with this annotation. Plotly uses a subset of HTML tags to do things like newline (
), bold (), italics (), hyperlinks (). Tags , , are also supported." + }, + "textangle": { + "type": "number", + "description": "Sets the angle at which the `text` is drawn with respect to the horizontal.", + "default": 0, + "minimum": -180, + "maximum": 180 + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "width": { + "type": "number", + "description": "Sets an explicit width for the text box. null (default) lets the text set the box width. Wider text will be clipped. There is no automatic wrapping; use
to start a new line.", + "default": null, + "minimum": 1 + }, + "height": { + "type": "number", + "description": "Sets an explicit height for the text box. null (default) lets the text set the box height. Taller text will be clipped.", + "default": null, + "minimum": 1 + }, + "opacity": { + "type": "number", + "description": "Sets the opacity of the annotation (text + arrow).", + "default": 1, + "minimum": 0, + "maximum": 1 + }, + "align": { + "type": "string", + "description": "Sets the horizontal alignment of the `text` within the box. Has an effect only if `text` spans two or more lines (i.e. `text` contains one or more
HTML tags) or if an explicit width is set to override the text width.", + "default": "center", + "enum": [ + "left", + "center", + "right" + ] + }, + "valign": { + "type": "string", + "description": "Sets the vertical alignment of the `text` within the box. Has an effect only if an explicit height is set to override the text height.", + "default": "middle", + "enum": [ + "top", + "middle", + "bottom" + ] + }, + "bgcolor": { + "type": "string", + "description": "Sets the background color of the annotation.", + "default": "rgba(0,0,0,0)" + }, + "bordercolor": { + "type": "string", + "description": "Sets the color of the border enclosing the annotation `text`.", + "default": "rgba(0,0,0,0)" + }, + "borderpad": { + "type": "number", + "description": "Sets the padding (in px) between the `text` and the enclosing border.", + "default": 1, + "minimum": 0 + }, + "borderwidth": { + "type": "number", + "description": "Sets the width (in px) of the border enclosing the annotation `text`.", + "default": 1, + "minimum": 0 + }, + "showarrow": { + "type": "boolean", + "description": "Determines whether or not the annotation is drawn with an arrow. If *true*, `text` is placed near the arrow's tail. If *false*, `text` lines up with the `x` and `y` provided.", + "default": true + }, + "arrowcolor": { + "type": "string", + "description": "Sets the color of the annotation arrow." + }, + "arrowhead": { + "type": "integer", + "description": "Sets the end annotation arrow head style.", + "default": 1, + "minimum": 0, + "maximum": 8 + }, + "startarrowhead": { + "type": "integer", + "description": "Sets the start annotation arrow head style.", + "default": 1, + "minimum": 0, + "maximum": 8 + }, + "arrowside": { + "type": "string", + "description": "Sets the annotation arrow head position.", + "default": "end", + "enum": [ + "start", + "end", + "end+start", + "none" + ] + }, + "arrowsize": { + "type": "number", + "description": "Sets the size of the end annotation arrow head, relative to `arrowwidth`. A value of 1 (default) gives a head about 3x as wide as the line.", + "default": 1, + "minimum": 0.3 + }, + "startarrowsize": { + "type": "number", + "description": "Sets the size of the start annotation arrow head, relative to `arrowwidth`. A value of 1 (default) gives a head about 3x as wide as the line.", + "default": 1, + "minimum": 0.3 + }, + "arrowwidth": { + "type": "number", + "description": "Sets the width (in px) of annotation arrow line.", + "minimum": 0.1 + }, + "standoff": { + "type": "number", + "description": "Sets a distance, in pixels, to move the end arrowhead away from the position it is pointing at, for example to point at the edge of a marker independent of zoom. Note that this shortens the arrow from the `ax` / `ay` vector, in contrast to `xshift` / `yshift` which moves everything by this amount.", + "default": 0, + "minimum": 0 + }, + "startstandoff": { + "type": "number", + "description": "Sets a distance, in pixels, to move the start arrowhead away from the position it is pointing at, for example to point at the edge of a marker independent of zoom. Note that this shortens the arrow from the `ax` / `ay` vector, in contrast to `xshift` / `yshift` which moves everything by this amount.", + "default": 0, + "minimum": 0 + }, + "hoverlabel": { + "type": "object", + "properties": { + "bgcolor": { + "type": "string", + "description": "Sets the background color of the hover label. By default uses the annotation's `bgcolor` made opaque, or white if it was transparent." + }, + "bordercolor": { + "type": "string", + "description": "Sets the border color of the hover label. By default uses either dark grey or white, for maximum contrast with `hoverlabel.bgcolor`." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + } + } + }, + "captureevents": { + "type": "boolean", + "description": "Determines whether the annotation text box captures mouse move and click events, or allows those events to pass through to data points in the plot that may be behind the annotation. By default `captureevents` is *false* unless `hovertext` is provided. If you use the event `plotly_clickannotation` without `hovertext` you must explicitly enable `captureevents`." + }, + "name": { + "type": "string", + "description": "When used in a template, named items are created in the output figure in addition to any items the figure already has in this array. You can modify these items in the output figure by making your own item with `templateitemname` matching this `name` alongside your modifications (including `visible: false` or `enabled: false` to hide it). Has no effect outside of a template." + }, + "templateitemname": { + "type": "string", + "description": "Used to refer to a named item in this array in the template. Named items from the template will be created even without a matching item in the input figure, but you can modify one by making an item with `templateitemname` matching its `name`, alongside your modifications (including `visible: false` or `enabled: false` to hide it). If there is no template or no matching item, this item will be hidden unless you explicitly show it with `visible: true`." + } + } + }, + "title": "annotation" + } + } + }, + "geo": { + "type": "object", + "properties": { + "domain": { + "type": "object", + "properties": { + "x": { + "type": "string", + "description": "Sets the horizontal domain of this geo subplot (in plot fraction). Note that geo subplots are constrained by domain. In general, when `projection.scale` is set to 1. a map will fit either its x or y domain, but not both.", + "default": [ + 0, + 1 + ] + }, + "y": { + "type": "string", + "description": "Sets the vertical domain of this geo subplot (in plot fraction). Note that geo subplots are constrained by domain. In general, when `projection.scale` is set to 1. a map will fit either its x or y domain, but not both.", + "default": [ + 0, + 1 + ] + }, + "row": { + "type": "integer", + "description": "If there is a layout grid, use the domain for this row in the grid for this geo subplot . Note that geo subplots are constrained by domain. In general, when `projection.scale` is set to 1. a map will fit either its x or y domain, but not both.", + "default": 0, + "minimum": 0 + }, + "column": { + "type": "integer", + "description": "If there is a layout grid, use the domain for this column in the grid for this geo subplot . Note that geo subplots are constrained by domain. In general, when `projection.scale` is set to 1. a map will fit either its x or y domain, but not both.", + "default": 0, + "minimum": 0 + } + } + }, + "fitbounds": { + "type": "string", + "description": "Determines if this subplot's view settings are auto-computed to fit trace data. On scoped maps, setting `fitbounds` leads to `center.lon` and `center.lat` getting auto-filled. On maps with a non-clipped projection, setting `fitbounds` leads to `center.lon`, `center.lat`, and `projection.rotation.lon` getting auto-filled. On maps with a clipped projection, setting `fitbounds` leads to `center.lon`, `center.lat`, `projection.rotation.lon`, `projection.rotation.lat`, `lonaxis.range` and `lonaxis.range` getting auto-filled. If *locations*, only the trace's visible locations are considered in the `fitbounds` computations. If *geojson*, the entire trace input `geojson` (if provided) is considered in the `fitbounds` computations, Defaults to *false*.", + "default": false, + "enum": [ + false, + "locations", + "geojson" + ] + }, + "resolution": { + "type": "string", + "description": "Sets the resolution of the base layers. The values have units of km/mm e.g. 110 corresponds to a scale ratio of 1:110,000,000.", + "default": 110, + "enum": [ + 110, + 50 + ] + }, + "scope": { + "type": "string", + "description": "Set the scope of the map.", + "default": "world", + "enum": [ + "world", + "usa", + "europe", + "asia", + "africa", + "north america", + "south america" + ] + }, + "projection": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Sets the projection type.", + "enum": [ + "equirectangular", + "mercator", + "orthographic", + "natural earth", + "kavrayskiy7", + "miller", + "robinson", + "eckert4", + "azimuthal equal area", + "azimuthal equidistant", + "conic equal area", + "conic conformal", + "conic equidistant", + "gnomonic", + "stereographic", + "mollweide", + "hammer", + "transverse mercator", + "albers usa", + "winkel tripel", + "aitoff", + "sinusoidal" + ] + }, + "rotation": { + "type": "object", + "properties": { + "lon": { + "type": "number", + "description": "Rotates the map along parallels (in degrees East). Defaults to the center of the `lonaxis.range` values." + }, + "lat": { + "type": "number", + "description": "Rotates the map along meridians (in degrees North)." + }, + "roll": { + "type": "number", + "description": "Roll the map (in degrees) For example, a roll of *180* makes the map appear upside down." + } + } + }, + "parallels": { + "type": "string", + "description": "For conic projection types only. Sets the parallels (tangent, secant) where the cone intersects the sphere." + }, + "scale": { + "type": "number", + "description": "Zooms in or out on the map view. A scale of *1* corresponds to the largest zoom level that fits the map's lon and lat ranges. ", + "default": 1, + "minimum": 0 + } + } + }, + "center": { + "type": "object", + "properties": { + "lon": { + "type": "number", + "description": "Sets the longitude of the map's center. By default, the map's longitude center lies at the middle of the longitude range for scoped projection and above `projection.rotation.lon` otherwise." + }, + "lat": { + "type": "number", + "description": "Sets the latitude of the map's center. For all projection types, the map's latitude center lies at the middle of the latitude range by default." + } + } + }, + "visible": { + "type": "boolean", + "description": "Sets the default visibility of the base layers.", + "default": true + }, + "showcoastlines": { + "type": "boolean", + "description": "Sets whether or not the coastlines are drawn." + }, + "coastlinecolor": { + "type": "string", + "description": "Sets the coastline color.", + "default": "#444" + }, + "coastlinewidth": { + "type": "number", + "description": "Sets the coastline stroke width (in px).", + "default": 1, + "minimum": 0 + }, + "showland": { + "type": "boolean", + "description": "Sets whether or not land masses are filled in color.", + "default": false + }, + "landcolor": { + "type": "string", + "description": "Sets the land mass color.", + "default": "#F0DC82" + }, + "showocean": { + "type": "boolean", + "description": "Sets whether or not oceans are filled in color.", + "default": false + }, + "oceancolor": { + "type": "string", + "description": "Sets the ocean color", + "default": "#3399FF" + }, + "showlakes": { + "type": "boolean", + "description": "Sets whether or not lakes are drawn.", + "default": false + }, + "lakecolor": { + "type": "string", + "description": "Sets the color of the lakes.", + "default": "#3399FF" + }, + "showrivers": { + "type": "boolean", + "description": "Sets whether or not rivers are drawn.", + "default": false + }, + "rivercolor": { + "type": "string", + "description": "Sets color of the rivers.", + "default": "#3399FF" + }, + "riverwidth": { + "type": "number", + "description": "Sets the stroke width (in px) of the rivers.", + "default": 1, + "minimum": 0 + }, + "showcountries": { + "type": "boolean", + "description": "Sets whether or not country boundaries are drawn." + }, + "countrycolor": { + "type": "string", + "description": "Sets line color of the country boundaries.", + "default": "#444" + }, + "countrywidth": { + "type": "number", + "description": "Sets line width (in px) of the country boundaries.", + "default": 1, + "minimum": 0 + }, + "showsubunits": { + "type": "boolean", + "description": "Sets whether or not boundaries of subunits within countries (e.g. states, provinces) are drawn." + }, + "subunitcolor": { + "type": "string", + "description": "Sets the color of the subunits boundaries.", + "default": "#444" + }, + "subunitwidth": { + "type": "number", + "description": "Sets the stroke width (in px) of the subunits boundaries.", + "default": 1, + "minimum": 0 + }, + "showframe": { + "type": "boolean", + "description": "Sets whether or not a frame is drawn around the map." + }, + "framecolor": { + "type": "string", + "description": "Sets the color the frame.", + "default": "#444" + }, + "framewidth": { + "type": "number", + "description": "Sets the stroke width (in px) of the frame.", + "default": 1, + "minimum": 0 + }, + "bgcolor": { + "type": "string", + "description": "Set the background color of the map", + "default": "#fff" + }, + "lonaxis": { + "type": "object", + "properties": { + "range": { + "type": "string", + "description": "Sets the range of this axis (in degrees), sets the map's clipped coordinates." + }, + "showgrid": { + "type": "boolean", + "description": "Sets whether or not graticule are shown on the map.", + "default": false + }, + "tick0": { + "type": "number", + "description": "Sets the graticule's starting tick longitude/latitude.", + "default": 0 + }, + "dtick": { + "type": "number", + "description": "Sets the graticule's longitude/latitude tick step." + }, + "gridcolor": { + "type": "string", + "description": "Sets the graticule's stroke color.", + "default": "#eee" + }, + "gridwidth": { + "type": "number", + "description": "Sets the graticule's stroke width (in px).", + "default": 1, + "minimum": 0 + } + } + }, + "lataxis": { + "type": "object", + "properties": { + "range": { + "type": "string", + "description": "Sets the range of this axis (in degrees), sets the map's clipped coordinates." + }, + "showgrid": { + "type": "boolean", + "description": "Sets whether or not graticule are shown on the map.", + "default": false + }, + "tick0": { + "type": "number", + "description": "Sets the graticule's starting tick longitude/latitude.", + "default": 0 + }, + "dtick": { + "type": "number", + "description": "Sets the graticule's longitude/latitude tick step." + }, + "gridcolor": { + "type": "string", + "description": "Sets the graticule's stroke color.", + "default": "#eee" + }, + "gridwidth": { + "type": "number", + "description": "Sets the graticule's stroke width (in px).", + "default": 1, + "minimum": 0 + } + } + }, + "uirevision": { + "type": "string", + "description": "Controls persistence of user-driven changes in the view (projection and center). Defaults to `layout.uirevision`." + } + } + }, + "mapbox": { + "type": "object", + "properties": { + "domain": { + "type": "object", + "properties": { + "x": { + "type": "string", + "description": "Sets the horizontal domain of this mapbox subplot (in plot fraction).", + "default": [ + 0, + 1 + ] + }, + "y": { + "type": "string", + "description": "Sets the vertical domain of this mapbox subplot (in plot fraction).", + "default": [ + 0, + 1 + ] + }, + "row": { + "type": "integer", + "description": "If there is a layout grid, use the domain for this row in the grid for this mapbox subplot .", + "default": 0, + "minimum": 0 + }, + "column": { + "type": "integer", + "description": "If there is a layout grid, use the domain for this column in the grid for this mapbox subplot .", + "default": 0, + "minimum": 0 + } + } + }, + "accesstoken": { + "type": "string", + "description": "Sets the mapbox access token to be used for this mapbox map. Alternatively, the mapbox access token can be set in the configuration options under `mapboxAccessToken`. Note that accessToken are only required when `style` (e.g with values : basic, streets, outdoors, light, dark, satellite, satellite-streets ) and/or a layout layer references the Mapbox server." + }, + "style": { + "type": "string", + "description": "Defines the map layers that are rendered by default below the trace layers defined in `data`, which are themselves by default rendered below the layers defined in `layout.mapbox.layers`. These layers can be defined either explicitly as a Mapbox Style object which can contain multiple layer definitions that load data from any public or private Tile Map Service (TMS or XYZ) or Web Map Service (WMS) or implicitly by using one of the built-in style objects which use WMSes which do not require any access tokens, or by using a default Mapbox style or custom Mapbox style URL, both of which require a Mapbox access token Note that Mapbox access token can be set in the `accesstoken` attribute or in the `mapboxAccessToken` config option. Mapbox Style objects are of the form described in the Mapbox GL JS documentation available at https://docs.mapbox.com/mapbox-gl-js/style-spec The built-in plotly.js styles objects are: open-street-map, white-bg, carto-positron, carto-darkmatter, stamen-terrain, stamen-toner, stamen-watercolor The built-in Mapbox styles are: basic, streets, outdoors, light, dark, satellite, satellite-streets Mapbox style URLs are of the form: mapbox://mapbox.mapbox--", + "default": "basic" + }, + "center": { + "type": "object", + "properties": { + "lon": { + "type": "number", + "description": "Sets the longitude of the center of the map (in degrees East).", + "default": 0 + }, + "lat": { + "type": "number", + "description": "Sets the latitude of the center of the map (in degrees North).", + "default": 0 + } + } + }, + "zoom": { + "type": "number", + "description": "Sets the zoom level of the map (mapbox.zoom).", + "default": 1 + }, + "bearing": { + "type": "number", + "description": "Sets the bearing angle of the map in degrees counter-clockwise from North (mapbox.bearing).", + "default": 0 + }, + "pitch": { + "type": "number", + "description": "Sets the pitch angle of the map (in degrees, where *0* means perpendicular to the surface of the map) (mapbox.pitch).", + "default": 0 + }, + "layers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Determines whether this layer is displayed", + "default": true + }, + "sourcetype": { + "type": "string", + "description": "Sets the source type for this layer, that is the type of the layer data.", + "default": "geojson", + "enum": [ + "geojson", + "vector", + "raster", + "image" + ] + }, + "source": { + "type": "string", + "description": "Sets the source data for this layer (mapbox.layer.source). When `sourcetype` is set to *geojson*, `source` can be a URL to a GeoJSON or a GeoJSON object. When `sourcetype` is set to *vector* or *raster*, `source` can be a URL or an array of tile URLs. When `sourcetype` is set to *image*, `source` can be a URL to an image." + }, + "sourcelayer": { + "type": "string", + "description": "Specifies the layer to use from a vector tile source (mapbox.layer.source-layer). Required for *vector* source type that supports multiple layers.", + "default": "" + }, + "sourceattribution": { + "type": "string", + "description": "Sets the attribution for this source." + }, + "type": { + "type": "string", + "description": "Sets the layer type, that is the how the layer data set in `source` will be rendered With `sourcetype` set to *geojson*, the following values are allowed: *circle*, *line*, *fill* and *symbol*. but note that *line* and *fill* are not compatible with Point GeoJSON geometries. With `sourcetype` set to *vector*, the following values are allowed: *circle*, *line*, *fill* and *symbol*. With `sourcetype` set to *raster* or `*image*`, only the *raster* value is allowed.", + "default": "circle", + "enum": [ + "circle", + "line", + "fill", + "symbol", + "raster" + ] + }, + "coordinates": { + "type": "string", + "description": "Sets the coordinates array contains [longitude, latitude] pairs for the image corners listed in clockwise order: top left, top right, bottom right, bottom left. Only has an effect for *image* `sourcetype`." + }, + "below": { + "type": "string", + "description": "Determines if the layer will be inserted before the layer with the specified ID. If omitted or set to '', the layer will be inserted above every existing layer." + }, + "color": { + "type": "string", + "description": "Sets the primary layer color. If `type` is *circle*, color corresponds to the circle color (mapbox.layer.paint.circle-color) If `type` is *line*, color corresponds to the line color (mapbox.layer.paint.line-color) If `type` is *fill*, color corresponds to the fill color (mapbox.layer.paint.fill-color) If `type` is *symbol*, color corresponds to the icon color (mapbox.layer.paint.icon-color)", + "default": "#444" + }, + "opacity": { + "type": "number", + "description": "Sets the opacity of the layer. If `type` is *circle*, opacity corresponds to the circle opacity (mapbox.layer.paint.circle-opacity) If `type` is *line*, opacity corresponds to the line opacity (mapbox.layer.paint.line-opacity) If `type` is *fill*, opacity corresponds to the fill opacity (mapbox.layer.paint.fill-opacity) If `type` is *symbol*, opacity corresponds to the icon/text opacity (mapbox.layer.paint.text-opacity)", + "default": 1, + "minimum": 0, + "maximum": 1 + }, + "minzoom": { + "type": "number", + "description": "Sets the minimum zoom level (mapbox.layer.minzoom). At zoom levels less than the minzoom, the layer will be hidden.", + "default": 0, + "minimum": 0, + "maximum": 24 + }, + "maxzoom": { + "type": "number", + "description": "Sets the maximum zoom level (mapbox.layer.maxzoom). At zoom levels equal to or greater than the maxzoom, the layer will be hidden.", + "default": 24, + "minimum": 0, + "maximum": 24 + }, + "circle": { + "type": "object", + "properties": { + "radius": { + "type": "number", + "description": "Sets the circle radius (mapbox.layer.paint.circle-radius). Has an effect only when `type` is set to *circle*.", + "default": 15 + } + } + }, + "line": { + "type": "object", + "properties": { + "width": { + "type": "number", + "description": "Sets the line width (mapbox.layer.paint.line-width). Has an effect only when `type` is set to *line*.", + "default": 2 + }, + "dash": { + "type": "string", + "description": "Sets the length of dashes and gaps (mapbox.layer.paint.line-dasharray). Has an effect only when `type` is set to *line*." + } + } + }, + "fill": { + "type": "object", + "properties": { + "outlinecolor": { + "type": "string", + "description": "Sets the fill outline color (mapbox.layer.paint.fill-outline-color). Has an effect only when `type` is set to *fill*.", + "default": "#444" + } + } + }, + "symbol": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "Sets the symbol icon image (mapbox.layer.layout.icon-image). Full list: https://www.mapbox.com/maki-icons/", + "default": "marker" + }, + "iconsize": { + "type": "number", + "description": "Sets the symbol icon size (mapbox.layer.layout.icon-size). Has an effect only when `type` is set to *symbol*.", + "default": 10 + }, + "text": { + "type": "string", + "description": "Sets the symbol text (mapbox.layer.layout.text-field).", + "default": "" + }, + "placement": { + "type": "string", + "description": "Sets the symbol and/or text placement (mapbox.layer.layout.symbol-placement). If `placement` is *point*, the label is placed where the geometry is located If `placement` is *line*, the label is placed along the line of the geometry If `placement` is *line-center*, the label is placed on the center of the geometry", + "default": "point", + "enum": [ + "point", + "line", + "line-center" + ] + }, + "textfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*.", + "default": "Open Sans Regular, Arial Unicode MS Regular" + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "textposition": { + "type": "string", + "description": "Sets the positions of the `text` elements with respects to the (x,y) coordinates.", + "default": "middle center", + "enum": [ + "top left", + "top center", + "top right", + "middle left", + "middle center", + "middle right", + "bottom left", + "bottom center", + "bottom right" + ] + } + } + }, + "name": { + "type": "string", + "description": "When used in a template, named items are created in the output figure in addition to any items the figure already has in this array. You can modify these items in the output figure by making your own item with `templateitemname` matching this `name` alongside your modifications (including `visible: false` or `enabled: false` to hide it). Has no effect outside of a template." + }, + "templateitemname": { + "type": "string", + "description": "Used to refer to a named item in this array in the template. Named items from the template will be created even without a matching item in the input figure, but you can modify one by making an item with `templateitemname` matching its `name`, alongside your modifications (including `visible: false` or `enabled: false` to hide it). If there is no template or no matching item, this item will be hidden unless you explicitly show it with `visible: true`." + } + } + }, + "title": "layer" + }, + "uirevision": { + "type": "string", + "description": "Controls persistence of user-driven changes in the view: `center`, `zoom`, `bearing`, `pitch`. Defaults to `layout.uirevision`." + } + } + }, + "polar": { + "type": "object", + "properties": { + "domain": { + "type": "object", + "properties": { + "x": { + "type": "string", + "description": "Sets the horizontal domain of this polar subplot (in plot fraction).", + "default": [ + 0, + 1 + ] + }, + "y": { + "type": "string", + "description": "Sets the vertical domain of this polar subplot (in plot fraction).", + "default": [ + 0, + 1 + ] + }, + "row": { + "type": "integer", + "description": "If there is a layout grid, use the domain for this row in the grid for this polar subplot .", + "default": 0, + "minimum": 0 + }, + "column": { + "type": "integer", + "description": "If there is a layout grid, use the domain for this column in the grid for this polar subplot .", + "default": 0, + "minimum": 0 + } + } + }, + "sector": { + "type": "string", + "description": "Sets angular span of this polar subplot with two angles (in degrees). Sector are assumed to be spanned in the counterclockwise direction with *0* corresponding to rightmost limit of the polar subplot.", + "default": [ + 0, + 360 + ] + }, + "hole": { + "type": "number", + "description": "Sets the fraction of the radius to cut out of the polar subplot.", + "default": 0, + "minimum": 0, + "maximum": 1 + }, + "bgcolor": { + "type": "string", + "description": "Set the background color of the subplot", + "default": "#fff" + }, + "radialaxis": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "A single toggle to hide the axis while preserving interaction like dragging. Default is true when a cheater plot is present on the axis, otherwise false", + "default": true + }, + "type": { + "type": "string", + "description": "Sets the axis type. By default, plotly attempts to determined the axis type by looking into the data of the traces that referenced the axis in question.", + "default": "-", + "enum": [ + "-", + "linear", + "log", + "date", + "category" + ] + }, + "autotypenumbers": { + "type": "string", + "description": "Using *strict* a numeric string in trace data is not converted to a number. Using *convert types* a numeric string in trace data may be treated as a number during automatic axis `type` detection. Defaults to layout.autotypenumbers.", + "default": "convert types", + "enum": [ + "convert types", + "strict" + ] + }, + "autorange": { + "type": "string", + "description": "Determines whether or not the range of this axis is computed in relation to the input data. See `rangemode` for more info. If `range` is provided, then `autorange` is set to *false*.", + "default": true, + "enum": [ + true, + false, + "reversed" + ] + }, + "rangemode": { + "type": "string", + "description": "If *tozero*`, the range extends to 0, regardless of the input data If *nonnegative*, the range is non-negative, regardless of the input data. If *normal*, the range is computed in relation to the extrema of the input data (same behavior as for cartesian axes).", + "default": "tozero", + "enum": [ + "tozero", + "nonnegative", + "normal" + ] + }, + "range": { + "type": "string", + "description": "Sets the range of this axis. If the axis `type` is *log*, then you must take the log of your desired range (e.g. to set the range from 1 to 100, set the range from 0 to 2). If the axis `type` is *date*, it should be date strings, like date data, though Date objects and unix milliseconds will be accepted and converted to strings. If the axis `type` is *category*, it should be numbers, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "categoryorder": { + "type": "string", + "description": "Specifies the ordering logic for the case of categorical variables. By default, plotly uses *trace*, which specifies the order that is present in the data supplied. Set `categoryorder` to *category ascending* or *category descending* if order should be determined by the alphanumerical order of the category names. Set `categoryorder` to *array* to derive the ordering from the attribute `categoryarray`. If a category is not found in the `categoryarray` array, the sorting behavior for that attribute will be identical to the *trace* mode. The unspecified categories will follow the categories in `categoryarray`. Set `categoryorder` to *total ascending* or *total descending* if order should be determined by the numerical order of the values. Similarly, the order can be determined by the min, max, sum, mean or median of all the values.", + "default": "trace", + "enum": [ + "trace", + "category ascending", + "category descending", + "array", + "total ascending", + "total descending", + "min ascending", + "min descending", + "max ascending", + "max descending", + "sum ascending", + "sum descending", + "mean ascending", + "mean descending", + "median ascending", + "median descending" + ] + }, + "categoryarray": { + "type": "string", + "description": "Sets the order in which categories on this axis appear. Only has an effect if `categoryorder` is set to *array*. Used with `categoryorder`." + }, + "angle": { + "type": "number", + "description": "Sets the angle (in degrees) from which the radial axis is drawn. Note that by default, radial axis line on the theta=0 line corresponds to a line pointing right (like what mathematicians prefer). Defaults to the first `polar.sector` angle.", + "minimum": -180, + "maximum": 180 + }, + "side": { + "type": "string", + "description": "Determines on which side of radial axis line the tick and tick labels appear.", + "default": "clockwise", + "enum": [ + "clockwise", + "counterclockwise" + ] + }, + "title": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Sets the title of this axis. Note that before the existence of `title.text`, the title's contents used to be defined as the `title` attribute itself. This behavior has been deprecated.", + "default": "" + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + } + } + }, + "hoverformat": { + "type": "string", + "description": "Sets the hover text formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "uirevision": { + "type": "string", + "description": "Controls persistence of user-driven changes in axis `range`, `autorange`, `angle`, and `title` if in `editable: true` configuration. Defaults to `polar.uirevision`." + }, + "color": { + "type": "string", + "description": "Sets default for all colors associated with this axis all at once: line, font, tick, and grid colors. Grid color is lightened by blending this with the plot background Individual pieces can override this.", + "default": "#444" + }, + "showline": { + "type": "boolean", + "description": "Determines whether or not a line bounding this axis is drawn.", + "default": true + }, + "linecolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "linewidth": { + "type": "number", + "description": "Sets the width (in px) of the axis line.", + "default": 1, + "minimum": 0 + }, + "showgrid": { + "type": "boolean", + "description": "Determines whether or not grid lines are drawn. If *true*, the grid lines are drawn at every tick mark.", + "default": true + }, + "gridcolor": { + "type": "string", + "description": "Sets the color of the grid lines.", + "default": "#eee" + }, + "gridwidth": { + "type": "number", + "description": "Sets the width (in px) of the grid lines.", + "default": 1, + "minimum": 0 + }, + "tickmode": { + "type": "string", + "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).", + "enum": [ + "auto", + "linear", + "array" + ] + }, + "nticks": { + "type": "integer", + "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", + "default": 0, + "minimum": 0 + }, + "tick0": { + "type": "string", + "description": "Sets the placement of the first tick on this axis. Use with `dtick`. If the axis `type` is *log*, then you must take the log of your starting tick (e.g. to set the starting tick to 100, set the `tick0` to 2) except when `dtick`=*L* (see `dtick` for more info). If the axis `type` is *date*, it should be a date string, like date data. If the axis `type` is *category*, it should be a number, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "dtick": { + "type": "string", + "description": "Sets the step in-between ticks on this axis. Use with `tick0`. Must be a positive number, or special strings available to *log* and *date* axes. If the axis `type` is *log*, then ticks are set every 10^(n*dtick) where n is the tick number. For example, to set a tick mark at 1, 10, 100, 1000, ... set dtick to 1. To set tick marks at 1, 100, 10000, ... set dtick to 2. To set tick marks at 1, 5, 25, 125, 625, 3125, ... set dtick to log_10(5), or 0.69897000433. *log* has several special values; *L*, where `f` is a positive number, gives ticks linearly spaced in value (but not position). For example `tick0` = 0.1, `dtick` = *L0.5* will put ticks at 0.1, 0.6, 1.1, 1.6 etc. To show powers of 10 plus small digits between, use *D1* (all digits) or *D2* (only 2 and 5). `tick0` is ignored for *D1* and *D2*. If the axis `type` is *date*, then you must convert the time to milliseconds. For example, to set the interval between ticks to one day, set `dtick` to 86400000.0. *date* also has special values *M* gives ticks spaced by a number of months. `n` must be a positive integer. To set ticks on the 15th of every third month, set `tick0` to *2000-01-15* and `dtick` to *M3*. To set ticks every 4 years, set `dtick` to *M48*" + }, + "tickvals": { + "type": "string", + "description": "Sets the values at which ticks on this axis appear. Only has an effect if `tickmode` is set to *array*. Used with `ticktext`." + }, + "ticktext": { + "type": "string", + "description": "Sets the text displayed at the ticks position via `tickvals`. Only has an effect if `tickmode` is set to *array*. Used with `tickvals`." + }, + "ticks": { + "type": "string", + "description": "Determines whether ticks are drawn or not. If **, this axis' ticks are not drawn. If *outside* (*inside*), this axis' are drawn outside (inside) the axis lines.", + "enum": [ + "outside", + "inside", + "" + ] + }, + "ticklen": { + "type": "number", + "description": "Sets the tick length (in px).", + "default": 5, + "minimum": 0 + }, + "tickwidth": { + "type": "number", + "description": "Sets the tick width (in px).", + "default": 1, + "minimum": 0 + }, + "tickcolor": { + "type": "string", + "description": "Sets the tick color.", + "default": "#444" + }, + "showticklabels": { + "type": "boolean", + "description": "Determines whether or not the tick labels are drawn.", + "default": true + }, + "showtickprefix": { + "type": "string", + "description": "If *all*, all tick labels are displayed with a prefix. If *first*, only the first tick is displayed with a prefix. If *last*, only the last tick is displayed with a suffix. If *none*, tick prefixes are hidden.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "tickprefix": { + "type": "string", + "description": "Sets a tick label prefix.", + "default": "" + }, + "showticksuffix": { + "type": "string", + "description": "Same as `showtickprefix` but for tick suffixes.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "ticksuffix": { + "type": "string", + "description": "Sets a tick label suffix.", + "default": "" + }, + "showexponent": { + "type": "string", + "description": "If *all*, all exponents are shown besides their significands. If *first*, only the exponent of the first tick is shown. If *last*, only the exponent of the last tick is shown. If *none*, no exponents appear.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "exponentformat": { + "type": "string", + "description": "Determines a formatting rule for the tick exponents. For example, consider the number 1,000,000,000. If *none*, it appears as 1,000,000,000. If *e*, 1e+9. If *E*, 1E+9. If *power*, 1x10^9 (with 9 in a super script). If *SI*, 1G. If *B*, 1B.", + "default": "B", + "enum": [ + "none", + "e", + "E", + "power", + "SI", + "B" + ] + }, + "minexponent": { + "type": "number", + "description": "Hide SI prefix for 10^n if |n| is below this number. This only has an effect when `tickformat` is *SI* or *B*.", + "default": 3, + "minimum": 0 + }, + "separatethousands": { + "type": "boolean", + "description": "If \"true\", even 4-digit integers are separated", + "default": false + }, + "tickfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "tickangle": { + "type": "number", + "description": "Sets the angle of the tick labels with respect to the horizontal. For example, a `tickangle` of -90 draws the tick labels vertically.", + "default": "auto", + "minimum": -180, + "maximum": 180 + }, + "tickformat": { + "type": "string", + "description": "Sets the tick label formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "layer": { + "type": "string", + "description": "Sets the layer on which this axis is displayed. If *above traces*, this axis is displayed above all the subplot's traces If *below traces*, this axis is displayed below all the subplot's traces, but above the grid lines. Useful when used together with scatter-like traces with `cliponaxis` set to *false* to show markers and/or text nodes above this axis.", + "default": "above traces", + "enum": [ + "above traces", + "below traces" + ] + }, + "calendar": { + "type": "string", + "description": "Sets the calendar system to use for `range` and `tick0` if this is a date axis. This does not set the calendar for interpreting data on this axis, that's specified in the trace or via the global `layout.calendar`", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + } + } + }, + "angularaxis": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "A single toggle to hide the axis while preserving interaction like dragging. Default is true when a cheater plot is present on the axis, otherwise false", + "default": true + }, + "type": { + "type": "string", + "description": "Sets the angular axis type. If *linear*, set `thetaunit` to determine the unit in which axis value are shown. If *category, use `period` to set the number of integer coordinates around polar axis.", + "default": "-", + "enum": [ + "-", + "linear", + "category" + ] + }, + "autotypenumbers": { + "type": "string", + "description": "Using *strict* a numeric string in trace data is not converted to a number. Using *convert types* a numeric string in trace data may be treated as a number during automatic axis `type` detection. Defaults to layout.autotypenumbers.", + "default": "convert types", + "enum": [ + "convert types", + "strict" + ] + }, + "categoryorder": { + "type": "string", + "description": "Specifies the ordering logic for the case of categorical variables. By default, plotly uses *trace*, which specifies the order that is present in the data supplied. Set `categoryorder` to *category ascending* or *category descending* if order should be determined by the alphanumerical order of the category names. Set `categoryorder` to *array* to derive the ordering from the attribute `categoryarray`. If a category is not found in the `categoryarray` array, the sorting behavior for that attribute will be identical to the *trace* mode. The unspecified categories will follow the categories in `categoryarray`. Set `categoryorder` to *total ascending* or *total descending* if order should be determined by the numerical order of the values. Similarly, the order can be determined by the min, max, sum, mean or median of all the values.", + "default": "trace", + "enum": [ + "trace", + "category ascending", + "category descending", + "array", + "total ascending", + "total descending", + "min ascending", + "min descending", + "max ascending", + "max descending", + "sum ascending", + "sum descending", + "mean ascending", + "mean descending", + "median ascending", + "median descending" + ] + }, + "categoryarray": { + "type": "string", + "description": "Sets the order in which categories on this axis appear. Only has an effect if `categoryorder` is set to *array*. Used with `categoryorder`." + }, + "thetaunit": { + "type": "string", + "description": "Sets the format unit of the formatted *theta* values. Has an effect only when `angularaxis.type` is *linear*.", + "default": "degrees", + "enum": [ + "radians", + "degrees" + ] + }, + "period": { + "type": "number", + "description": "Set the angular period. Has an effect only when `angularaxis.type` is *category*.", + "minimum": 0 + }, + "direction": { + "type": "string", + "description": "Sets the direction corresponding to positive angles.", + "default": "counterclockwise", + "enum": [ + "counterclockwise", + "clockwise" + ] + }, + "rotation": { + "type": "number", + "description": "Sets that start position (in degrees) of the angular axis By default, polar subplots with `direction` set to *counterclockwise* get a `rotation` of *0* which corresponds to due East (like what mathematicians prefer). In turn, polar with `direction` set to *clockwise* get a rotation of *90* which corresponds to due North (like on a compass),", + "minimum": -180, + "maximum": 180 + }, + "hoverformat": { + "type": "string", + "description": "Sets the hover text formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "uirevision": { + "type": "string", + "description": "Controls persistence of user-driven changes in axis `rotation`. Defaults to `polar.uirevision`." + }, + "color": { + "type": "string", + "description": "Sets default for all colors associated with this axis all at once: line, font, tick, and grid colors. Grid color is lightened by blending this with the plot background Individual pieces can override this.", + "default": "#444" + }, + "showline": { + "type": "boolean", + "description": "Determines whether or not a line bounding this axis is drawn.", + "default": true + }, + "linecolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "linewidth": { + "type": "number", + "description": "Sets the width (in px) of the axis line.", + "default": 1, + "minimum": 0 + }, + "showgrid": { + "type": "boolean", + "description": "Determines whether or not grid lines are drawn. If *true*, the grid lines are drawn at every tick mark.", + "default": true + }, + "gridcolor": { + "type": "string", + "description": "Sets the color of the grid lines.", + "default": "#eee" + }, + "gridwidth": { + "type": "number", + "description": "Sets the width (in px) of the grid lines.", + "default": 1, + "minimum": 0 + }, + "tickmode": { + "type": "string", + "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).", + "enum": [ + "auto", + "linear", + "array" + ] + }, + "nticks": { + "type": "integer", + "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", + "default": 0, + "minimum": 0 + }, + "tick0": { + "type": "string", + "description": "Sets the placement of the first tick on this axis. Use with `dtick`. If the axis `type` is *log*, then you must take the log of your starting tick (e.g. to set the starting tick to 100, set the `tick0` to 2) except when `dtick`=*L* (see `dtick` for more info). If the axis `type` is *date*, it should be a date string, like date data. If the axis `type` is *category*, it should be a number, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "dtick": { + "type": "string", + "description": "Sets the step in-between ticks on this axis. Use with `tick0`. Must be a positive number, or special strings available to *log* and *date* axes. If the axis `type` is *log*, then ticks are set every 10^(n*dtick) where n is the tick number. For example, to set a tick mark at 1, 10, 100, 1000, ... set dtick to 1. To set tick marks at 1, 100, 10000, ... set dtick to 2. To set tick marks at 1, 5, 25, 125, 625, 3125, ... set dtick to log_10(5), or 0.69897000433. *log* has several special values; *L*, where `f` is a positive number, gives ticks linearly spaced in value (but not position). For example `tick0` = 0.1, `dtick` = *L0.5* will put ticks at 0.1, 0.6, 1.1, 1.6 etc. To show powers of 10 plus small digits between, use *D1* (all digits) or *D2* (only 2 and 5). `tick0` is ignored for *D1* and *D2*. If the axis `type` is *date*, then you must convert the time to milliseconds. For example, to set the interval between ticks to one day, set `dtick` to 86400000.0. *date* also has special values *M* gives ticks spaced by a number of months. `n` must be a positive integer. To set ticks on the 15th of every third month, set `tick0` to *2000-01-15* and `dtick` to *M3*. To set ticks every 4 years, set `dtick` to *M48*" + }, + "tickvals": { + "type": "string", + "description": "Sets the values at which ticks on this axis appear. Only has an effect if `tickmode` is set to *array*. Used with `ticktext`." + }, + "ticktext": { + "type": "string", + "description": "Sets the text displayed at the ticks position via `tickvals`. Only has an effect if `tickmode` is set to *array*. Used with `tickvals`." + }, + "ticks": { + "type": "string", + "description": "Determines whether ticks are drawn or not. If **, this axis' ticks are not drawn. If *outside* (*inside*), this axis' are drawn outside (inside) the axis lines.", + "enum": [ + "outside", + "inside", + "" + ] + }, + "ticklen": { + "type": "number", + "description": "Sets the tick length (in px).", + "default": 5, + "minimum": 0 + }, + "tickwidth": { + "type": "number", + "description": "Sets the tick width (in px).", + "default": 1, + "minimum": 0 + }, + "tickcolor": { + "type": "string", + "description": "Sets the tick color.", + "default": "#444" + }, + "showticklabels": { + "type": "boolean", + "description": "Determines whether or not the tick labels are drawn.", + "default": true + }, + "showtickprefix": { + "type": "string", + "description": "If *all*, all tick labels are displayed with a prefix. If *first*, only the first tick is displayed with a prefix. If *last*, only the last tick is displayed with a suffix. If *none*, tick prefixes are hidden.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "tickprefix": { + "type": "string", + "description": "Sets a tick label prefix.", + "default": "" + }, + "showticksuffix": { + "type": "string", + "description": "Same as `showtickprefix` but for tick suffixes.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "ticksuffix": { + "type": "string", + "description": "Sets a tick label suffix.", + "default": "" + }, + "showexponent": { + "type": "string", + "description": "If *all*, all exponents are shown besides their significands. If *first*, only the exponent of the first tick is shown. If *last*, only the exponent of the last tick is shown. If *none*, no exponents appear.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "exponentformat": { + "type": "string", + "description": "Determines a formatting rule for the tick exponents. For example, consider the number 1,000,000,000. If *none*, it appears as 1,000,000,000. If *e*, 1e+9. If *E*, 1E+9. If *power*, 1x10^9 (with 9 in a super script). If *SI*, 1G. If *B*, 1B.", + "default": "B", + "enum": [ + "none", + "e", + "E", + "power", + "SI", + "B" + ] + }, + "minexponent": { + "type": "number", + "description": "Hide SI prefix for 10^n if |n| is below this number. This only has an effect when `tickformat` is *SI* or *B*.", + "default": 3, + "minimum": 0 + }, + "separatethousands": { + "type": "boolean", + "description": "If \"true\", even 4-digit integers are separated", + "default": false + }, + "tickfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "tickangle": { + "type": "number", + "description": "Sets the angle of the tick labels with respect to the horizontal. For example, a `tickangle` of -90 draws the tick labels vertically.", + "default": "auto", + "minimum": -180, + "maximum": 180 + }, + "tickformat": { + "type": "string", + "description": "Sets the tick label formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "layer": { + "type": "string", + "description": "Sets the layer on which this axis is displayed. If *above traces*, this axis is displayed above all the subplot's traces If *below traces*, this axis is displayed below all the subplot's traces, but above the grid lines. Useful when used together with scatter-like traces with `cliponaxis` set to *false* to show markers and/or text nodes above this axis.", + "default": "above traces", + "enum": [ + "above traces", + "below traces" + ] + } + } + }, + "gridshape": { + "type": "string", + "description": "Determines if the radial axis grid lines and angular axis line are drawn as *circular* sectors or as *linear* (polygon) sectors. Has an effect only when the angular axis has `type` *category*. Note that `radialaxis.angle` is snapped to the angle of the closest vertex when `gridshape` is *circular* (so that radial axis scale is the same as the data scale).", + "default": "circular", + "enum": [ + "circular", + "linear" + ] + }, + "uirevision": { + "type": "string", + "description": "Controls persistence of user-driven changes in axis attributes, if not overridden in the individual axes. Defaults to `layout.uirevision`." + } + } + }, + "radialaxis": { + "type": "object", + "properties": { + "range": { + "type": "string", + "description": "Legacy polar charts are deprecated! Please switch to *polar* subplots. Defines the start and end point of this radial axis." + }, + "domain": { + "type": "string", + "description": "Polar chart subplots are not supported yet. This key has currently no effect.", + "default": [ + 0, + 1 + ] + }, + "orientation": { + "type": "number", + "description": "Legacy polar charts are deprecated! Please switch to *polar* subplots. Sets the orientation (an angle with respect to the origin) of the radial axis." + }, + "showline": { + "type": "boolean", + "description": "Legacy polar charts are deprecated! Please switch to *polar* subplots. Determines whether or not the line bounding this radial axis will be shown on the figure." + }, + "showticklabels": { + "type": "boolean", + "description": "Legacy polar charts are deprecated! Please switch to *polar* subplots. Determines whether or not the radial axis ticks will feature tick labels." + }, + "tickorientation": { + "type": "string", + "description": "Legacy polar charts are deprecated! Please switch to *polar* subplots. Sets the orientation (from the paper perspective) of the radial axis tick labels.", + "enum": [ + "horizontal", + "vertical" + ] + }, + "ticklen": { + "type": "number", + "description": "Legacy polar charts are deprecated! Please switch to *polar* subplots. Sets the length of the tick lines on this radial axis.", + "minimum": 0 + }, + "tickcolor": { + "type": "string", + "description": "Legacy polar charts are deprecated! Please switch to *polar* subplots. Sets the color of the tick lines on this radial axis." + }, + "ticksuffix": { + "type": "string", + "description": "Legacy polar charts are deprecated! Please switch to *polar* subplots. Sets the length of the tick lines on this radial axis." + }, + "endpadding": { + "type": "number", + "description": "Legacy polar charts are deprecated! Please switch to *polar* subplots." + }, + "visible": { + "type": "boolean", + "description": "Legacy polar charts are deprecated! Please switch to *polar* subplots. Determines whether or not this axis will be visible." + } + } + }, + "angularaxis": { + "type": "object", + "properties": { + "range": { + "type": "string", + "description": "Legacy polar charts are deprecated! Please switch to *polar* subplots. Defines the start and end point of this angular axis." + }, + "domain": { + "type": "string", + "description": "Polar chart subplots are not supported yet. This key has currently no effect.", + "default": [ + 0, + 1 + ] + }, + "showline": { + "type": "boolean", + "description": "Legacy polar charts are deprecated! Please switch to *polar* subplots. Determines whether or not the line bounding this angular axis will be shown on the figure." + }, + "showticklabels": { + "type": "boolean", + "description": "Legacy polar charts are deprecated! Please switch to *polar* subplots. Determines whether or not the angular axis ticks will feature tick labels." + }, + "tickorientation": { + "type": "string", + "description": "Legacy polar charts are deprecated! Please switch to *polar* subplots. Sets the orientation (from the paper perspective) of the angular axis tick labels.", + "enum": [ + "horizontal", + "vertical" + ] + }, + "ticklen": { + "type": "number", + "description": "Legacy polar charts are deprecated! Please switch to *polar* subplots. Sets the length of the tick lines on this angular axis.", + "minimum": 0 + }, + "tickcolor": { + "type": "string", + "description": "Legacy polar charts are deprecated! Please switch to *polar* subplots. Sets the color of the tick lines on this angular axis." + }, + "ticksuffix": { + "type": "string", + "description": "Legacy polar charts are deprecated! Please switch to *polar* subplots. Sets the length of the tick lines on this angular axis." + }, + "endpadding": { + "type": "number", + "description": "Legacy polar charts are deprecated! Please switch to *polar* subplots." + }, + "visible": { + "type": "boolean", + "description": "Legacy polar charts are deprecated! Please switch to *polar* subplots. Determines whether or not this axis will be visible." + } + } + }, + "direction": { + "type": "string", + "description": "Legacy polar charts are deprecated! Please switch to *polar* subplots. Sets the direction corresponding to positive angles in legacy polar charts.", + "enum": [ + "clockwise", + "counterclockwise" + ] + }, + "orientation": { + "type": "number", + "description": "Legacy polar charts are deprecated! Please switch to *polar* subplots. Rotates the entire polar by the given angle in legacy polar charts.", + "minimum": -180, + "maximum": 180 + }, + "legend": { + "type": "object", + "properties": { + "bgcolor": { + "type": "string", + "description": "Sets the legend background color. Defaults to `layout.paper_bgcolor`." + }, + "bordercolor": { + "type": "string", + "description": "Sets the color of the border enclosing the legend.", + "default": "#444" + }, + "borderwidth": { + "type": "number", + "description": "Sets the width (in px) of the border enclosing the legend.", + "default": 0, + "minimum": 0 + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "orientation": { + "type": "string", + "description": "Sets the orientation of the legend.", + "default": "v", + "enum": [ + "v", + "h" + ] + }, + "traceorder": { + "type": "string", + "description": "Determines the order at which the legend items are displayed. If *normal*, the items are displayed top-to-bottom in the same order as the input data. If *reversed*, the items are displayed in the opposite order as *normal*. If *grouped*, the items are displayed in groups (when a trace `legendgroup` is provided). if *grouped+reversed*, the items are displayed in the opposite order as *grouped*.", + "enum": [ + "grouped", + "reversed", + "reversed+grouped", + "normal" + ] + }, + "tracegroupgap": { + "type": "number", + "description": "Sets the amount of vertical space (in px) between legend groups.", + "default": 10, + "minimum": 0 + }, + "itemsizing": { + "type": "string", + "description": "Determines if the legend items symbols scale with their corresponding *trace* attributes or remain *constant* independent of the symbol size on the graph.", + "default": "trace", + "enum": [ + "trace", + "constant" + ] + }, + "itemwidth": { + "type": "number", + "description": "Sets the width (in px) of the legend item symbols (the part other than the title.text).", + "default": 30, + "minimum": 30 + }, + "itemclick": { + "type": "string", + "description": "Determines the behavior on legend item click. *toggle* toggles the visibility of the item clicked on the graph. *toggleothers* makes the clicked item the sole visible item on the graph. *false* disable legend item click interactions.", + "default": "toggle", + "enum": [ + "toggle", + "toggleothers", + false + ] + }, + "itemdoubleclick": { + "type": "string", + "description": "Determines the behavior on legend item double-click. *toggle* toggles the visibility of the item clicked on the graph. *toggleothers* makes the clicked item the sole visible item on the graph. *false* disable legend item double-click interactions.", + "default": "toggleothers", + "enum": [ + "toggle", + "toggleothers", + false + ] + }, + "x": { + "type": "number", + "description": "Sets the x position (in normalized coordinates) of the legend. Defaults to *1.02* for vertical legends and defaults to *0* for horizontal legends.", + "minimum": -2, + "maximum": 3 + }, + "xanchor": { + "type": "string", + "description": "Sets the legend's horizontal position anchor. This anchor binds the `x` position to the *left*, *center* or *right* of the legend. Value *auto* anchors legends to the right for `x` values greater than or equal to 2/3, anchors legends to the left for `x` values less than or equal to 1/3 and anchors legends with respect to their center otherwise.", + "default": "left", + "enum": [ + "auto", + "left", + "center", + "right" + ] + }, + "y": { + "type": "number", + "description": "Sets the y position (in normalized coordinates) of the legend. Defaults to *1* for vertical legends, defaults to *-0.1* for horizontal legends on graphs w/o range sliders and defaults to *1.1* for horizontal legends on graph with one or multiple range sliders.", + "minimum": -2, + "maximum": 3 + }, + "yanchor": { + "type": "string", + "description": "Sets the legend's vertical position anchor This anchor binds the `y` position to the *top*, *middle* or *bottom* of the legend. Value *auto* anchors legends at their bottom for `y` values less than or equal to 1/3, anchors legends to at their top for `y` values greater than or equal to 2/3 and anchors legends with respect to their middle otherwise.", + "enum": [ + "auto", + "top", + "middle", + "bottom" + ] + }, + "uirevision": { + "type": "string", + "description": "Controls persistence of legend-driven changes in trace and pie label visibility. Defaults to `layout.uirevision`." + }, + "valign": { + "type": "string", + "description": "Sets the vertical alignment of the symbols with respect to their associated text.", + "default": "middle", + "enum": [ + "top", + "middle", + "bottom" + ] + }, + "title": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Sets the title of the legend.", + "default": "" + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "side": { + "type": "string", + "description": "Determines the location of legend's title with respect to the legend items. Defaulted to *top* with `orientation` is *h*. Defaulted to *left* with `orientation` is *v*. The *top left* options could be used to expand legend area in both x and y sides.", + "enum": [ + "top", + "left", + "top left" + ] + } + } + } + } + }, + "annotations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Determines whether or not this annotation is visible.", + "default": true + }, + "text": { + "type": "string", + "description": "Sets the text associated with this annotation. Plotly uses a subset of HTML tags to do things like newline (
), bold (), italics (), hyperlinks (). Tags , , are also supported." + }, + "textangle": { + "type": "number", + "description": "Sets the angle at which the `text` is drawn with respect to the horizontal.", + "default": 0, + "minimum": -180, + "maximum": 180 + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "width": { + "type": "number", + "description": "Sets an explicit width for the text box. null (default) lets the text set the box width. Wider text will be clipped. There is no automatic wrapping; use
to start a new line.", + "default": null, + "minimum": 1 + }, + "height": { + "type": "number", + "description": "Sets an explicit height for the text box. null (default) lets the text set the box height. Taller text will be clipped.", + "default": null, + "minimum": 1 + }, + "opacity": { + "type": "number", + "description": "Sets the opacity of the annotation (text + arrow).", + "default": 1, + "minimum": 0, + "maximum": 1 + }, + "align": { + "type": "string", + "description": "Sets the horizontal alignment of the `text` within the box. Has an effect only if `text` spans two or more lines (i.e. `text` contains one or more
HTML tags) or if an explicit width is set to override the text width.", + "default": "center", + "enum": [ + "left", + "center", + "right" + ] + }, + "valign": { + "type": "string", + "description": "Sets the vertical alignment of the `text` within the box. Has an effect only if an explicit height is set to override the text height.", + "default": "middle", + "enum": [ + "top", + "middle", + "bottom" + ] + }, + "bgcolor": { + "type": "string", + "description": "Sets the background color of the annotation.", + "default": "rgba(0,0,0,0)" + }, + "bordercolor": { + "type": "string", + "description": "Sets the color of the border enclosing the annotation `text`.", + "default": "rgba(0,0,0,0)" + }, + "borderpad": { + "type": "number", + "description": "Sets the padding (in px) between the `text` and the enclosing border.", + "default": 1, + "minimum": 0 + }, + "borderwidth": { + "type": "number", + "description": "Sets the width (in px) of the border enclosing the annotation `text`.", + "default": 1, + "minimum": 0 + }, + "showarrow": { + "type": "boolean", + "description": "Determines whether or not the annotation is drawn with an arrow. If *true*, `text` is placed near the arrow's tail. If *false*, `text` lines up with the `x` and `y` provided.", + "default": true + }, + "arrowcolor": { + "type": "string", + "description": "Sets the color of the annotation arrow." + }, + "arrowhead": { + "type": "integer", + "description": "Sets the end annotation arrow head style.", + "default": 1, + "minimum": 0, + "maximum": 8 + }, + "startarrowhead": { + "type": "integer", + "description": "Sets the start annotation arrow head style.", + "default": 1, + "minimum": 0, + "maximum": 8 + }, + "arrowside": { + "type": "string", + "description": "Sets the annotation arrow head position.", + "default": "end", + "enum": [ + "start", + "end", + "end+start", + "none" + ] + }, + "arrowsize": { + "type": "number", + "description": "Sets the size of the end annotation arrow head, relative to `arrowwidth`. A value of 1 (default) gives a head about 3x as wide as the line.", + "default": 1, + "minimum": 0.3 + }, + "startarrowsize": { + "type": "number", + "description": "Sets the size of the start annotation arrow head, relative to `arrowwidth`. A value of 1 (default) gives a head about 3x as wide as the line.", + "default": 1, + "minimum": 0.3 + }, + "arrowwidth": { + "type": "number", + "description": "Sets the width (in px) of annotation arrow line.", + "minimum": 0.1 + }, + "standoff": { + "type": "number", + "description": "Sets a distance, in pixels, to move the end arrowhead away from the position it is pointing at, for example to point at the edge of a marker independent of zoom. Note that this shortens the arrow from the `ax` / `ay` vector, in contrast to `xshift` / `yshift` which moves everything by this amount.", + "default": 0, + "minimum": 0 + }, + "startstandoff": { + "type": "number", + "description": "Sets a distance, in pixels, to move the start arrowhead away from the position it is pointing at, for example to point at the edge of a marker independent of zoom. Note that this shortens the arrow from the `ax` / `ay` vector, in contrast to `xshift` / `yshift` which moves everything by this amount.", + "default": 0, + "minimum": 0 + }, + "ax": { + "type": "string", + "description": "Sets the x component of the arrow tail about the arrow head. If `axref` is `pixel`, a positive (negative) component corresponds to an arrow pointing from right to left (left to right). If `axref` is not `pixel` and is exactly the same as `xref`, this is an absolute value on that axis, like `x`, specified in the same coordinates as `xref`." + }, + "ay": { + "type": "string", + "description": "Sets the y component of the arrow tail about the arrow head. If `ayref` is `pixel`, a positive (negative) component corresponds to an arrow pointing from bottom to top (top to bottom). If `ayref` is not `pixel` and is exactly the same as `yref`, this is an absolute value on that axis, like `y`, specified in the same coordinates as `yref`." + }, + "axref": { + "type": "string", + "description": "Indicates in what coordinates the tail of the annotation (ax,ay) is specified. If set to a ax axis id (e.g. *ax* or *ax2*), the `ax` position refers to a ax coordinate. If set to *paper*, the `ax` position refers to the distance from the left of the plotting area in normalized coordinates where *0* (*1*) corresponds to the left (right). If set to a ax axis ID followed by *domain* (separated by a space), the position behaves like for *paper*, but refers to the distance in fractions of the domain length from the left of the domain of that axis: e.g., *ax2 domain* refers to the domain of the second ax axis and a ax position of 0.5 refers to the point between the left and the right of the domain of the second ax axis. In order for absolute positioning of the arrow to work, *axref* must be exactly the same as *xref*, otherwise *axref* will revert to *pixel* (explained next). For relative positioning, *axref* can be set to *pixel*, in which case the *ax* value is specified in pixels relative to *x*. Absolute positioning is useful for trendline annotations which should continue to indicate the correct trend when zoomed. Relative positioning is useful for specifying the text offset for an annotated point.", + "default": "pixel", + "enum": [ + "pixel", + "/^x([2-9]|[1-9][0-9]+)?( domain)?$/" + ] + }, + "ayref": { + "type": "string", + "description": "Indicates in what coordinates the tail of the annotation (ax,ay) is specified. If set to a ay axis id (e.g. *ay* or *ay2*), the `ay` position refers to a ay coordinate. If set to *paper*, the `ay` position refers to the distance from the bottom of the plotting area in normalized coordinates where *0* (*1*) corresponds to the bottom (top). If set to a ay axis ID followed by *domain* (separated by a space), the position behaves like for *paper*, but refers to the distance in fractions of the domain length from the bottom of the domain of that axis: e.g., *ay2 domain* refers to the domain of the second ay axis and a ay position of 0.5 refers to the point between the bottom and the top of the domain of the second ay axis. In order for absolute positioning of the arrow to work, *ayref* must be exactly the same as *yref*, otherwise *ayref* will revert to *pixel* (explained next). For relative positioning, *ayref* can be set to *pixel*, in which case the *ay* value is specified in pixels relative to *y*. Absolute positioning is useful for trendline annotations which should continue to indicate the correct trend when zoomed. Relative positioning is useful for specifying the text offset for an annotated point.", + "default": "pixel", + "enum": [ + "pixel", + "/^y([2-9]|[1-9][0-9]+)?( domain)?$/" + ] + }, + "xref": { + "type": "string", + "description": "Sets the annotation's x coordinate axis. If set to a x axis id (e.g. *x* or *x2*), the `x` position refers to a x coordinate. If set to *paper*, the `x` position refers to the distance from the left of the plotting area in normalized coordinates where *0* (*1*) corresponds to the left (right). If set to a x axis ID followed by *domain* (separated by a space), the position behaves like for *paper*, but refers to the distance in fractions of the domain length from the left of the domain of that axis: e.g., *x2 domain* refers to the domain of the second x axis and a x position of 0.5 refers to the point between the left and the right of the domain of the second x axis.", + "enum": [ + "paper", + "/^x([2-9]|[1-9][0-9]+)?( domain)?$/" + ] + }, + "x": { + "type": "string", + "description": "Sets the annotation's x position. If the axis `type` is *log*, then you must take the log of your desired range. If the axis `type` is *date*, it should be date strings, like date data, though Date objects and unix milliseconds will be accepted and converted to strings. If the axis `type` is *category*, it should be numbers, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "xanchor": { + "type": "string", + "description": "Sets the text box's horizontal position anchor This anchor binds the `x` position to the *left*, *center* or *right* of the annotation. For example, if `x` is set to 1, `xref` to *paper* and `xanchor` to *right* then the right-most portion of the annotation lines up with the right-most edge of the plotting area. If *auto*, the anchor is equivalent to *center* for data-referenced annotations or if there is an arrow, whereas for paper-referenced with no arrow, the anchor picked corresponds to the closest side.", + "default": "auto", + "enum": [ + "auto", + "left", + "center", + "right" + ] + }, + "xshift": { + "type": "number", + "description": "Shifts the position of the whole annotation and arrow to the right (positive) or left (negative) by this many pixels.", + "default": 0 + }, + "yref": { + "type": "string", + "description": "Sets the annotation's y coordinate axis. If set to a y axis id (e.g. *y* or *y2*), the `y` position refers to a y coordinate. If set to *paper*, the `y` position refers to the distance from the bottom of the plotting area in normalized coordinates where *0* (*1*) corresponds to the bottom (top). If set to a y axis ID followed by *domain* (separated by a space), the position behaves like for *paper*, but refers to the distance in fractions of the domain length from the bottom of the domain of that axis: e.g., *y2 domain* refers to the domain of the second y axis and a y position of 0.5 refers to the point between the bottom and the top of the domain of the second y axis.", + "enum": [ + "paper", + "/^y([2-9]|[1-9][0-9]+)?( domain)?$/" + ] + }, + "y": { + "type": "string", + "description": "Sets the annotation's y position. If the axis `type` is *log*, then you must take the log of your desired range. If the axis `type` is *date*, it should be date strings, like date data, though Date objects and unix milliseconds will be accepted and converted to strings. If the axis `type` is *category*, it should be numbers, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "yanchor": { + "type": "string", + "description": "Sets the text box's vertical position anchor This anchor binds the `y` position to the *top*, *middle* or *bottom* of the annotation. For example, if `y` is set to 1, `yref` to *paper* and `yanchor` to *top* then the top-most portion of the annotation lines up with the top-most edge of the plotting area. If *auto*, the anchor is equivalent to *middle* for data-referenced annotations or if there is an arrow, whereas for paper-referenced with no arrow, the anchor picked corresponds to the closest side.", + "default": "auto", + "enum": [ + "auto", + "top", + "middle", + "bottom" + ] + }, + "yshift": { + "type": "number", + "description": "Shifts the position of the whole annotation and arrow up (positive) or down (negative) by this many pixels.", + "default": 0 + }, + "clicktoshow": { + "type": "string", + "description": "Makes this annotation respond to clicks on the plot. If you click a data point that exactly matches the `x` and `y` values of this annotation, and it is hidden (visible: false), it will appear. In *onoff* mode, you must click the same point again to make it disappear, so if you click multiple points, you can show multiple annotations. In *onout* mode, a click anywhere else in the plot (on another data point or not) will hide this annotation. If you need to show/hide this annotation in response to different `x` or `y` values, you can set `xclick` and/or `yclick`. This is useful for example to label the side of a bar. To label markers though, `standoff` is preferred over `xclick` and `yclick`.", + "default": false, + "enum": [ + false, + "onoff", + "onout" + ] + }, + "xclick": { + "type": "string", + "description": "Toggle this annotation when clicking a data point whose `x` value is `xclick` rather than the annotation's `x` value." + }, + "yclick": { + "type": "string", + "description": "Toggle this annotation when clicking a data point whose `y` value is `yclick` rather than the annotation's `y` value." + }, + "hoverlabel": { + "type": "object", + "properties": { + "bgcolor": { + "type": "string", + "description": "Sets the background color of the hover label. By default uses the annotation's `bgcolor` made opaque, or white if it was transparent." + }, + "bordercolor": { + "type": "string", + "description": "Sets the border color of the hover label. By default uses either dark grey or white, for maximum contrast with `hoverlabel.bgcolor`." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + } + } + }, + "captureevents": { + "type": "boolean", + "description": "Determines whether the annotation text box captures mouse move and click events, or allows those events to pass through to data points in the plot that may be behind the annotation. By default `captureevents` is *false* unless `hovertext` is provided. If you use the event `plotly_clickannotation` without `hovertext` you must explicitly enable `captureevents`." + }, + "name": { + "type": "string", + "description": "When used in a template, named items are created in the output figure in addition to any items the figure already has in this array. You can modify these items in the output figure by making your own item with `templateitemname` matching this `name` alongside your modifications (including `visible: false` or `enabled: false` to hide it). Has no effect outside of a template." + }, + "templateitemname": { + "type": "string", + "description": "Used to refer to a named item in this array in the template. Named items from the template will be created even without a matching item in the input figure, but you can modify one by making an item with `templateitemname` matching its `name`, alongside your modifications (including `visible: false` or `enabled: false` to hide it). If there is no template or no matching item, this item will be hidden unless you explicitly show it with `visible: true`." + } + } + }, + "title": "annotation" + }, + "shapes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Determines whether or not this shape is visible.", + "default": true + }, + "type": { + "type": "string", + "description": "Specifies the shape type to be drawn. If *line*, a line is drawn from (`x0`,`y0`) to (`x1`,`y1`) with respect to the axes' sizing mode. If *circle*, a circle is drawn from ((`x0`+`x1`)/2, (`y0`+`y1`)/2)) with radius (|(`x0`+`x1`)/2 - `x0`|, |(`y0`+`y1`)/2 -`y0`)|) with respect to the axes' sizing mode. If *rect*, a rectangle is drawn linking (`x0`,`y0`), (`x1`,`y0`), (`x1`,`y1`), (`x0`,`y1`), (`x0`,`y0`) with respect to the axes' sizing mode. If *path*, draw a custom SVG path using `path`. with respect to the axes' sizing mode.", + "enum": [ + "circle", + "rect", + "path", + "line" + ] + }, + "layer": { + "type": "string", + "description": "Specifies whether shapes are drawn below or above traces.", + "default": "above", + "enum": [ + "below", + "above" + ] + }, + "xref": { + "type": "string", + "description": "Sets the shape's x coordinate axis. If set to a x axis id (e.g. *x* or *x2*), the `x` position refers to a x coordinate. If set to *paper*, the `x` position refers to the distance from the left of the plotting area in normalized coordinates where *0* (*1*) corresponds to the left (right). If set to a x axis ID followed by *domain* (separated by a space), the position behaves like for *paper*, but refers to the distance in fractions of the domain length from the left of the domain of that axis: e.g., *x2 domain* refers to the domain of the second x axis and a x position of 0.5 refers to the point between the left and the right of the domain of the second x axis. If the axis `type` is *log*, then you must take the log of your desired range. If the axis `type` is *date*, then you must convert the date to unix time in milliseconds.", + "enum": [ + "paper", + "/^x([2-9]|[1-9][0-9]+)?( domain)?$/" + ] + }, + "xsizemode": { + "type": "string", + "description": "Sets the shapes's sizing mode along the x axis. If set to *scaled*, `x0`, `x1` and x coordinates within `path` refer to data values on the x axis or a fraction of the plot area's width (`xref` set to *paper*). If set to *pixel*, `xanchor` specifies the x position in terms of data or plot fraction but `x0`, `x1` and x coordinates within `path` are pixels relative to `xanchor`. This way, the shape can have a fixed width while maintaining a position relative to data or plot fraction.", + "default": "scaled", + "enum": [ + "scaled", + "pixel" + ] + }, + "xanchor": { + "type": "string", + "description": "Only relevant in conjunction with `xsizemode` set to *pixel*. Specifies the anchor point on the x axis to which `x0`, `x1` and x coordinates within `path` are relative to. E.g. useful to attach a pixel sized shape to a certain data value. No effect when `xsizemode` not set to *pixel*." + }, + "x0": { + "type": "string", + "description": "Sets the shape's starting x position. See `type` and `xsizemode` for more info." + }, + "x1": { + "type": "string", + "description": "Sets the shape's end x position. See `type` and `xsizemode` for more info." + }, + "yref": { + "type": "string", + "description": "Sets the annotation's y coordinate axis. If set to a y axis id (e.g. *y* or *y2*), the `y` position refers to a y coordinate. If set to *paper*, the `y` position refers to the distance from the bottom of the plotting area in normalized coordinates where *0* (*1*) corresponds to the bottom (top). If set to a y axis ID followed by *domain* (separated by a space), the position behaves like for *paper*, but refers to the distance in fractions of the domain length from the bottom of the domain of that axis: e.g., *y2 domain* refers to the domain of the second y axis and a y position of 0.5 refers to the point between the bottom and the top of the domain of the second y axis.", + "enum": [ + "paper", + "/^y([2-9]|[1-9][0-9]+)?( domain)?$/" + ] + }, + "ysizemode": { + "type": "string", + "description": "Sets the shapes's sizing mode along the y axis. If set to *scaled*, `y0`, `y1` and y coordinates within `path` refer to data values on the y axis or a fraction of the plot area's height (`yref` set to *paper*). If set to *pixel*, `yanchor` specifies the y position in terms of data or plot fraction but `y0`, `y1` and y coordinates within `path` are pixels relative to `yanchor`. This way, the shape can have a fixed height while maintaining a position relative to data or plot fraction.", + "default": "scaled", + "enum": [ + "scaled", + "pixel" + ] + }, + "yanchor": { + "type": "string", + "description": "Only relevant in conjunction with `ysizemode` set to *pixel*. Specifies the anchor point on the y axis to which `y0`, `y1` and y coordinates within `path` are relative to. E.g. useful to attach a pixel sized shape to a certain data value. No effect when `ysizemode` not set to *pixel*." + }, + "y0": { + "type": "string", + "description": "Sets the shape's starting y position. See `type` and `ysizemode` for more info." + }, + "y1": { + "type": "string", + "description": "Sets the shape's end y position. See `type` and `ysizemode` for more info." + }, + "path": { + "type": "string", + "description": "For `type` *path* - a valid SVG path with the pixel values replaced by data values in `xsizemode`/`ysizemode` being *scaled* and taken unmodified as pixels relative to `xanchor` and `yanchor` in case of *pixel* size mode. There are a few restrictions / quirks only absolute instructions, not relative. So the allowed segments are: M, L, H, V, Q, C, T, S, and Z arcs (A) are not allowed because radius rx and ry are relative. In the future we could consider supporting relative commands, but we would have to decide on how to handle date and log axes. Note that even as is, Q and C Bezier paths that are smooth on linear axes may not be smooth on log, and vice versa. no chained \"polybezier\" commands - specify the segment type for each one. On category axes, values are numbers scaled to the serial numbers of categories because using the categories themselves there would be no way to describe fractional positions On data axes: because space and T are both normal components of path strings, we can't use either to separate date from time parts. Therefore we'll use underscore for this purpose: 2015-02-21_13:45:56.789" + }, + "opacity": { + "type": "number", + "description": "Sets the opacity of the shape.", + "default": 1, + "minimum": 0, + "maximum": 1 + }, + "line": { + "type": "object", + "properties": { + "color": { + "type": "string", + "description": "Sets the line color." + }, + "width": { + "type": "number", + "description": "Sets the line width (in px).", + "default": 2, + "minimum": 0 + }, + "dash": { + "type": "string", + "description": "Sets the dash style of lines. Set to a dash type string (*solid*, *dot*, *dash*, *longdash*, *dashdot*, or *longdashdot*) or a dash length list in px (eg *5px,10px,2px,2px*).", + "default": "solid" + } + } + }, + "fillcolor": { + "type": "string", + "description": "Sets the color filling the shape's interior. Only applies to closed shapes.", + "default": "rgba(0,0,0,0)" + }, + "fillrule": { + "type": "string", + "description": "Determines which regions of complex paths constitute the interior. For more info please visit https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill-rule", + "default": "evenodd", + "enum": [ + "evenodd", + "nonzero" + ] + }, + "editable": { + "type": "boolean", + "description": "Determines whether the shape could be activated for edit or not. Has no effect when the older editable shapes mode is enabled via `config.editable` or `config.edits.shapePosition`.", + "default": false + }, + "name": { + "type": "string", + "description": "When used in a template, named items are created in the output figure in addition to any items the figure already has in this array. You can modify these items in the output figure by making your own item with `templateitemname` matching this `name` alongside your modifications (including `visible: false` or `enabled: false` to hide it). Has no effect outside of a template." + }, + "templateitemname": { + "type": "string", + "description": "Used to refer to a named item in this array in the template. Named items from the template will be created even without a matching item in the input figure, but you can modify one by making an item with `templateitemname` matching its `name`, alongside your modifications (including `visible: false` or `enabled: false` to hide it). If there is no template or no matching item, this item will be hidden unless you explicitly show it with `visible: true`." + } + } + }, + "title": "shape" + }, + "images": { + "type": "array", + "items": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Determines whether or not this image is visible.", + "default": true + }, + "source": { + "type": "string", + "description": "Specifies the URL of the image to be used. The URL must be accessible from the domain where the plot code is run, and can be either relative or absolute." + }, + "layer": { + "type": "string", + "description": "Specifies whether images are drawn below or above traces. When `xref` and `yref` are both set to `paper`, image is drawn below the entire plot area.", + "default": "above", + "enum": [ + "below", + "above" + ] + }, + "sizex": { + "type": "number", + "description": "Sets the image container size horizontally. The image will be sized based on the `position` value. When `xref` is set to `paper`, units are sized relative to the plot width. When `xref` ends with ` domain`, units are sized relative to the axis width.", + "default": 0 + }, + "sizey": { + "type": "number", + "description": "Sets the image container size vertically. The image will be sized based on the `position` value. When `yref` is set to `paper`, units are sized relative to the plot height. When `yref` ends with ` domain`, units are sized relative to the axis height.", + "default": 0 + }, + "sizing": { + "type": "string", + "description": "Specifies which dimension of the image to constrain.", + "default": "contain", + "enum": [ + "fill", + "contain", + "stretch" + ] + }, + "opacity": { + "type": "number", + "description": "Sets the opacity of the image.", + "default": 1, + "minimum": 0, + "maximum": 1 + }, + "x": { + "type": "string", + "description": "Sets the image's x position. When `xref` is set to `paper`, units are sized relative to the plot height. See `xref` for more info", + "default": 0 + }, + "y": { + "type": "string", + "description": "Sets the image's y position. When `yref` is set to `paper`, units are sized relative to the plot height. See `yref` for more info", + "default": 0 + }, + "xanchor": { + "type": "string", + "description": "Sets the anchor for the x position", + "default": "left", + "enum": [ + "left", + "center", + "right" + ] + }, + "yanchor": { + "type": "string", + "description": "Sets the anchor for the y position.", + "default": "top", + "enum": [ + "top", + "middle", + "bottom" + ] + }, + "xref": { + "type": "string", + "description": "Sets the images's x coordinate axis. If set to a x axis id (e.g. *x* or *x2*), the `x` position refers to a x coordinate. If set to *paper*, the `x` position refers to the distance from the left of the plotting area in normalized coordinates where *0* (*1*) corresponds to the left (right). If set to a x axis ID followed by *domain* (separated by a space), the position behaves like for *paper*, but refers to the distance in fractions of the domain length from the left of the domain of that axis: e.g., *x2 domain* refers to the domain of the second x axis and a x position of 0.5 refers to the point between the left and the right of the domain of the second x axis.", + "default": "paper", + "enum": [ + "paper", + "/^x([2-9]|[1-9][0-9]+)?( domain)?$/" + ] + }, + "yref": { + "type": "string", + "description": "Sets the images's y coordinate axis. If set to a y axis id (e.g. *y* or *y2*), the `y` position refers to a y coordinate. If set to *paper*, the `y` position refers to the distance from the bottom of the plotting area in normalized coordinates where *0* (*1*) corresponds to the bottom (top). If set to a y axis ID followed by *domain* (separated by a space), the position behaves like for *paper*, but refers to the distance in fractions of the domain length from the bottom of the domain of that axis: e.g., *y2 domain* refers to the domain of the second y axis and a y position of 0.5 refers to the point between the bottom and the top of the domain of the second y axis.", + "default": "paper", + "enum": [ + "paper", + "/^y([2-9]|[1-9][0-9]+)?( domain)?$/" + ] + }, + "name": { + "type": "string", + "description": "When used in a template, named items are created in the output figure in addition to any items the figure already has in this array. You can modify these items in the output figure by making your own item with `templateitemname` matching this `name` alongside your modifications (including `visible: false` or `enabled: false` to hide it). Has no effect outside of a template." + }, + "templateitemname": { + "type": "string", + "description": "Used to refer to a named item in this array in the template. Named items from the template will be created even without a matching item in the input figure, but you can modify one by making an item with `templateitemname` matching its `name`, alongside your modifications (including `visible: false` or `enabled: false` to hide it). If there is no template or no matching item, this item will be hidden unless you explicitly show it with `visible: true`." + } + } + }, + "title": "image" + }, + "updatemenus": { + "type": "array", + "items": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Determines whether or not the update menu is visible." + }, + "type": { + "type": "string", + "description": "Determines whether the buttons are accessible via a dropdown menu or whether the buttons are stacked horizontally or vertically", + "default": "dropdown", + "enum": [ + "dropdown", + "buttons" + ] + }, + "direction": { + "type": "string", + "description": "Determines the direction in which the buttons are laid out, whether in a dropdown menu or a row/column of buttons. For `left` and `up`, the buttons will still appear in left-to-right or top-to-bottom order respectively.", + "default": "down", + "enum": [ + "left", + "right", + "up", + "down" + ] + }, + "active": { + "type": "integer", + "description": "Determines which button (by index starting from 0) is considered active.", + "default": 0, + "minimum": -1 + }, + "showactive": { + "type": "boolean", + "description": "Highlights active dropdown item or active button if true.", + "default": true + }, + "buttons": { + "type": "array", + "items": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Determines whether or not this button is visible." + }, + "method": { + "type": "string", + "description": "Sets the Plotly method to be called on click. If the `skip` method is used, the API updatemenu will function as normal but will perform no API calls and will not bind automatically to state updates. This may be used to create a component interface and attach to updatemenu events manually via JavaScript.", + "default": "restyle", + "enum": [ + "restyle", + "relayout", + "animate", + "update", + "skip" + ] + }, + "args": { + "type": "string", + "description": "Sets the arguments values to be passed to the Plotly method set in `method` on click." + }, + "args2": { + "type": "string", + "description": "Sets a 2nd set of `args`, these arguments values are passed to the Plotly method set in `method` when clicking this button while in the active state. Use this to create toggle buttons." + }, + "label": { + "type": "string", + "description": "Sets the text label to appear on the button.", + "default": "" + }, + "execute": { + "type": "boolean", + "description": "When true, the API method is executed. When false, all other behaviors are the same and command execution is skipped. This may be useful when hooking into, for example, the `plotly_buttonclicked` method and executing the API command manually without losing the benefit of the updatemenu automatically binding to the state of the plot through the specification of `method` and `args`.", + "default": true + }, + "name": { + "type": "string", + "description": "When used in a template, named items are created in the output figure in addition to any items the figure already has in this array. You can modify these items in the output figure by making your own item with `templateitemname` matching this `name` alongside your modifications (including `visible: false` or `enabled: false` to hide it). Has no effect outside of a template." + }, + "templateitemname": { + "type": "string", + "description": "Used to refer to a named item in this array in the template. Named items from the template will be created even without a matching item in the input figure, but you can modify one by making an item with `templateitemname` matching its `name`, alongside your modifications (including `visible: false` or `enabled: false` to hide it). If there is no template or no matching item, this item will be hidden unless you explicitly show it with `visible: true`." + } + } + }, + "title": "button" + }, + "x": { + "type": "number", + "description": "Sets the x position (in normalized coordinates) of the update menu.", + "default": -0.05, + "minimum": -2, + "maximum": 3 + }, + "xanchor": { + "type": "string", + "description": "Sets the update menu's horizontal position anchor. This anchor binds the `x` position to the *left*, *center* or *right* of the range selector.", + "default": "right", + "enum": [ + "auto", + "left", + "center", + "right" + ] + }, + "y": { + "type": "number", + "description": "Sets the y position (in normalized coordinates) of the update menu.", + "default": 1, + "minimum": -2, + "maximum": 3 + }, + "yanchor": { + "type": "string", + "description": "Sets the update menu's vertical position anchor This anchor binds the `y` position to the *top*, *middle* or *bottom* of the range selector.", + "default": "top", + "enum": [ + "auto", + "top", + "middle", + "bottom" + ] + }, + "pad": { + "type": "object", + "properties": { + "b": { + "type": "number", + "description": "The amount of padding (in px) along the bottom of the component.", + "default": 0 + }, + "l": { + "type": "number", + "description": "The amount of padding (in px) on the left side of the component.", + "default": 0 + } + } + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "bgcolor": { + "type": "string", + "description": "Sets the background color of the update menu buttons." + }, + "bordercolor": { + "type": "string", + "description": "Sets the color of the border enclosing the update menu.", + "default": "#BEC8D9" + }, + "borderwidth": { + "type": "number", + "description": "Sets the width (in px) of the border enclosing the update menu.", + "default": 1, + "minimum": 0 + }, + "name": { + "type": "string", + "description": "When used in a template, named items are created in the output figure in addition to any items the figure already has in this array. You can modify these items in the output figure by making your own item with `templateitemname` matching this `name` alongside your modifications (including `visible: false` or `enabled: false` to hide it). Has no effect outside of a template." + }, + "templateitemname": { + "type": "string", + "description": "Used to refer to a named item in this array in the template. Named items from the template will be created even without a matching item in the input figure, but you can modify one by making an item with `templateitemname` matching its `name`, alongside your modifications (including `visible: false` or `enabled: false` to hide it). If there is no template or no matching item, this item will be hidden unless you explicitly show it with `visible: true`." + } + } + }, + "title": "updatemenu" + }, + "sliders": { + "type": "array", + "items": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Determines whether or not the slider is visible.", + "default": true + }, + "active": { + "type": "number", + "description": "Determines which button (by index starting from 0) is considered active.", + "default": 0, + "minimum": 0 + }, + "steps": { + "type": "array", + "items": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Determines whether or not this step is included in the slider.", + "default": true + }, + "method": { + "type": "string", + "description": "Sets the Plotly method to be called when the slider value is changed. If the `skip` method is used, the API slider will function as normal but will perform no API calls and will not bind automatically to state updates. This may be used to create a component interface and attach to slider events manually via JavaScript.", + "default": "restyle", + "enum": [ + "restyle", + "relayout", + "animate", + "update", + "skip" + ] + }, + "args": { + "type": "string", + "description": "Sets the arguments values to be passed to the Plotly method set in `method` on slide." + }, + "label": { + "type": "string", + "description": "Sets the text label to appear on the slider" + }, + "value": { + "type": "string", + "description": "Sets the value of the slider step, used to refer to the step programatically. Defaults to the slider label if not provided." + }, + "execute": { + "type": "boolean", + "description": "When true, the API method is executed. When false, all other behaviors are the same and command execution is skipped. This may be useful when hooking into, for example, the `plotly_sliderchange` method and executing the API command manually without losing the benefit of the slider automatically binding to the state of the plot through the specification of `method` and `args`.", + "default": true + }, + "name": { + "type": "string", + "description": "When used in a template, named items are created in the output figure in addition to any items the figure already has in this array. You can modify these items in the output figure by making your own item with `templateitemname` matching this `name` alongside your modifications (including `visible: false` or `enabled: false` to hide it). Has no effect outside of a template." + }, + "templateitemname": { + "type": "string", + "description": "Used to refer to a named item in this array in the template. Named items from the template will be created even without a matching item in the input figure, but you can modify one by making an item with `templateitemname` matching its `name`, alongside your modifications (including `visible: false` or `enabled: false` to hide it). If there is no template or no matching item, this item will be hidden unless you explicitly show it with `visible: true`." + } + } + }, + "title": "step" + }, + "lenmode": { + "type": "string", + "description": "Determines whether this slider length is set in units of plot *fraction* or in *pixels. Use `len` to set the value.", + "default": "fraction", + "enum": [ + "fraction", + "pixels" + ] + }, + "len": { + "type": "number", + "description": "Sets the length of the slider This measure excludes the padding of both ends. That is, the slider's length is this length minus the padding on both ends.", + "default": 1, + "minimum": 0 + }, + "x": { + "type": "number", + "description": "Sets the x position (in normalized coordinates) of the slider.", + "default": 0, + "minimum": -2, + "maximum": 3 + }, + "pad": { + "type": "object", + "properties": { + "b": { + "type": "number", + "description": "The amount of padding (in px) along the bottom of the component.", + "default": 0 + }, + "l": { + "type": "number", + "description": "The amount of padding (in px) on the left side of the component.", + "default": 0 + } + } + }, + "xanchor": { + "type": "string", + "description": "Sets the slider's horizontal position anchor. This anchor binds the `x` position to the *left*, *center* or *right* of the range selector.", + "default": "left", + "enum": [ + "auto", + "left", + "center", + "right" + ] + }, + "y": { + "type": "number", + "description": "Sets the y position (in normalized coordinates) of the slider.", + "default": 0, + "minimum": -2, + "maximum": 3 + }, + "yanchor": { + "type": "string", + "description": "Sets the slider's vertical position anchor This anchor binds the `y` position to the *top*, *middle* or *bottom* of the range selector.", + "default": "top", + "enum": [ + "auto", + "top", + "middle", + "bottom" + ] + }, + "transition": { + "type": "object", + "properties": { + "duration": { + "type": "number", + "description": "Sets the duration of the slider transition", + "default": 150, + "minimum": 0 + }, + "easing": { + "type": "string", + "description": "Sets the easing function of the slider transition", + "default": "cubic-in-out", + "enum": [ + "linear", + "quad", + "cubic", + "sin", + "exp", + "circle", + "elastic", + "back", + "bounce", + "linear-in", + "quad-in", + "cubic-in", + "sin-in", + "exp-in", + "circle-in", + "elastic-in", + "back-in", + "bounce-in", + "linear-out", + "quad-out", + "cubic-out", + "sin-out", + "exp-out", + "circle-out", + "elastic-out", + "back-out", + "bounce-out", + "linear-in-out", + "quad-in-out", + "cubic-in-out", + "sin-in-out", + "exp-in-out", + "circle-in-out", + "elastic-in-out", + "back-in-out", + "bounce-in-out" + ] + } + } + }, + "currentvalue": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Shows the currently-selected value above the slider.", + "default": true + }, + "xanchor": { + "type": "string", + "description": "The alignment of the value readout relative to the length of the slider.", + "default": "left", + "enum": [ + "left", + "center", + "right" + ] + }, + "offset": { + "type": "number", + "description": "The amount of space, in pixels, between the current value label and the slider.", + "default": 10 + }, + "prefix": { + "type": "string", + "description": "When currentvalue.visible is true, this sets the prefix of the label." + }, + "suffix": { + "type": "string", + "description": "When currentvalue.visible is true, this sets the suffix of the label." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + } + } + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "activebgcolor": { + "type": "string", + "description": "Sets the background color of the slider grip while dragging.", + "default": "#dbdde0" + }, + "bgcolor": { + "type": "string", + "description": "Sets the background color of the slider.", + "default": "#f8fafc" + }, + "bordercolor": { + "type": "string", + "description": "Sets the color of the border enclosing the slider.", + "default": "#bec8d9" + }, + "borderwidth": { + "type": "number", + "description": "Sets the width (in px) of the border enclosing the slider.", + "default": 1, + "minimum": 0 + }, + "ticklen": { + "type": "number", + "description": "Sets the length in pixels of step tick marks", + "default": 7, + "minimum": 0 + }, + "tickcolor": { + "type": "string", + "description": "Sets the color of the border enclosing the slider.", + "default": "#333" + }, + "tickwidth": { + "type": "number", + "description": "Sets the tick width (in px).", + "default": 1, + "minimum": 0 + }, + "minorticklen": { + "type": "number", + "description": "Sets the length in pixels of minor step tick marks", + "default": 4, + "minimum": 0 + }, + "name": { + "type": "string", + "description": "When used in a template, named items are created in the output figure in addition to any items the figure already has in this array. You can modify these items in the output figure by making your own item with `templateitemname` matching this `name` alongside your modifications (including `visible: false` or `enabled: false` to hide it). Has no effect outside of a template." + }, + "templateitemname": { + "type": "string", + "description": "Used to refer to a named item in this array in the template. Named items from the template will be created even without a matching item in the input figure, but you can modify one by making an item with `templateitemname` matching its `name`, alongside your modifications (including `visible: false` or `enabled: false` to hide it). If there is no template or no matching item, this item will be hidden unless you explicitly show it with `visible: true`." + } + } + }, + "title": "slider" + }, + "colorscale": { + "type": "object", + "properties": { + "sequential": { + "type": "string", + "description": "Sets the default sequential colorscale for positive values. Note that `autocolorscale` must be true for this attribute to work.", + "default": [ + [ + 0, + "rgb(220,220,220)" + ], + [ + 0.2, + "rgb(245,195,157)" + ], + [ + 0.4, + "rgb(245,160,105)" + ], + [ + 1, + "rgb(178,10,28)" + ] + ] + }, + "sequentialminus": { + "type": "string", + "description": "Sets the default sequential colorscale for negative values. Note that `autocolorscale` must be true for this attribute to work.", + "default": [ + [ + 0, + "rgb(5,10,172)" + ], + [ + 0.35, + "rgb(40,60,190)" + ], + [ + 0.5, + "rgb(70,100,245)" + ], + [ + 0.6, + "rgb(90,120,245)" + ], + [ + 0.7, + "rgb(106,137,247)" + ], + [ + 1, + "rgb(220,220,220)" + ] + ] + }, + "diverging": { + "type": "string", + "description": "Sets the default diverging colorscale. Note that `autocolorscale` must be true for this attribute to work.", + "default": [ + [ + 0, + "rgb(5,10,172)" + ], + [ + 0.35, + "rgb(106,137,247)" + ], + [ + 0.5, + "rgb(190,190,190)" + ], + [ + 0.6, + "rgb(220,170,132)" + ], + [ + 0.7, + "rgb(230,145,90)" + ], + [ + 1, + "rgb(178,10,28)" + ] + ] + } + } + }, + "coloraxis": { + "type": "object", + "properties": { + "cauto": { + "type": "boolean", + "description": "Determines whether or not the color domain is computed with respect to the input data (here corresponding trace color array(s)) or the bounds set in `cmin` and `cmax` Defaults to `false` when `cmin` and `cmax` are set by the user.", + "default": true + }, + "cmin": { + "type": "number", + "description": "Sets the lower bound of the color domain. Value should have the same units as corresponding trace color array(s) and if set, `cmax` must be set as well.", + "default": null + }, + "cmax": { + "type": "number", + "description": "Sets the upper bound of the color domain. Value should have the same units as corresponding trace color array(s) and if set, `cmin` must be set as well.", + "default": null + }, + "cmid": { + "type": "number", + "description": "Sets the mid-point of the color domain by scaling `cmin` and/or `cmax` to be equidistant to this point. Value should have the same units as corresponding trace color array(s). Has no effect when `cauto` is `false`.", + "default": null + }, + "colorscale": { + "type": "string", + "description": "Sets the colorscale. The colorscale must be an array containing arrays mapping a normalized value to an rgb, rgba, hex, hsl, hsv, or named color string. At minimum, a mapping for the lowest (0) and highest (1) values are required. For example, `[[0, 'rgb(0,0,255)'], [1, 'rgb(255,0,0)']]`. To control the bounds of the colorscale in color space, use`cmin` and `cmax`. Alternatively, `colorscale` may be a palette name string of the following list: Greys,YlGnBu,Greens,YlOrRd,Bluered,RdBu,Reds,Blues,Picnic,Rainbow,Portland,Jet,Hot,Blackbody,Earth,Electric,Viridis,Cividis.", + "default": null + }, + "autocolorscale": { + "type": "boolean", + "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `colorscale`. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.", + "default": true + }, + "reversescale": { + "type": "boolean", + "description": "Reverses the color mapping if true. If true, `cmin` will correspond to the last color in the array and `cmax` will correspond to the first color.", + "default": false + }, + "showscale": { + "type": "boolean", + "description": "Determines whether or not a colorbar is displayed for this trace.", + "default": true + }, + "colorbar": { + "type": "object", + "properties": { + "thicknessmode": { + "type": "string", + "description": "Determines whether this color bar's thickness (i.e. the measure in the constant color direction) is set in units of plot *fraction* or in *pixels*. Use `thickness` to set the value.", + "default": "pixels", + "enum": [ + "fraction", + "pixels" + ] + }, + "thickness": { + "type": "number", + "description": "Sets the thickness of the color bar This measure excludes the size of the padding, ticks and labels.", + "default": 30, + "minimum": 0 + }, + "lenmode": { + "type": "string", + "description": "Determines whether this color bar's length (i.e. the measure in the color variation direction) is set in units of plot *fraction* or in *pixels. Use `len` to set the value.", + "default": "fraction", + "enum": [ + "fraction", + "pixels" + ] + }, + "len": { + "type": "number", + "description": "Sets the length of the color bar This measure excludes the padding of both ends. That is, the color bar length is this length minus the padding on both ends.", + "default": 1, + "minimum": 0 + }, + "x": { + "type": "number", + "description": "Sets the x position of the color bar (in plot fraction).", + "default": 1.02, + "minimum": -2, + "maximum": 3 + }, + "xanchor": { + "type": "string", + "description": "Sets this color bar's horizontal position anchor. This anchor binds the `x` position to the *left*, *center* or *right* of the color bar.", + "default": "left", + "enum": [ + "left", + "center", + "right" + ] + }, + "xpad": { + "type": "number", + "description": "Sets the amount of padding (in px) along the x direction.", + "default": 10, + "minimum": 0 + }, + "y": { + "type": "number", + "description": "Sets the y position of the color bar (in plot fraction).", + "default": 0.5, + "minimum": -2, + "maximum": 3 + }, + "yanchor": { + "type": "string", + "description": "Sets this color bar's vertical position anchor This anchor binds the `y` position to the *top*, *middle* or *bottom* of the color bar.", + "default": "middle", + "enum": [ + "top", + "middle", + "bottom" + ] + }, + "ypad": { + "type": "number", + "description": "Sets the amount of padding (in px) along the y direction.", + "default": 10, + "minimum": 0 + }, + "outlinecolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "outlinewidth": { + "type": "number", + "description": "Sets the width (in px) of the axis line.", + "default": 1, + "minimum": 0 + }, + "bordercolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "borderwidth": { + "type": "number", + "description": "Sets the width (in px) or the border enclosing this color bar.", + "default": 0, + "minimum": 0 + }, + "bgcolor": { + "type": "string", + "description": "Sets the color of padded area.", + "default": "rgba(0,0,0,0)" + }, + "tickmode": { + "type": "string", + "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).", + "enum": [ + "auto", + "linear", + "array" + ] + }, + "nticks": { + "type": "integer", + "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", + "default": 0, + "minimum": 0 + }, + "tick0": { + "type": "string", + "description": "Sets the placement of the first tick on this axis. Use with `dtick`. If the axis `type` is *log*, then you must take the log of your starting tick (e.g. to set the starting tick to 100, set the `tick0` to 2) except when `dtick`=*L* (see `dtick` for more info). If the axis `type` is *date*, it should be a date string, like date data. If the axis `type` is *category*, it should be a number, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "dtick": { + "type": "string", + "description": "Sets the step in-between ticks on this axis. Use with `tick0`. Must be a positive number, or special strings available to *log* and *date* axes. If the axis `type` is *log*, then ticks are set every 10^(n*dtick) where n is the tick number. For example, to set a tick mark at 1, 10, 100, 1000, ... set dtick to 1. To set tick marks at 1, 100, 10000, ... set dtick to 2. To set tick marks at 1, 5, 25, 125, 625, 3125, ... set dtick to log_10(5), or 0.69897000433. *log* has several special values; *L*, where `f` is a positive number, gives ticks linearly spaced in value (but not position). For example `tick0` = 0.1, `dtick` = *L0.5* will put ticks at 0.1, 0.6, 1.1, 1.6 etc. To show powers of 10 plus small digits between, use *D1* (all digits) or *D2* (only 2 and 5). `tick0` is ignored for *D1* and *D2*. If the axis `type` is *date*, then you must convert the time to milliseconds. For example, to set the interval between ticks to one day, set `dtick` to 86400000.0. *date* also has special values *M* gives ticks spaced by a number of months. `n` must be a positive integer. To set ticks on the 15th of every third month, set `tick0` to *2000-01-15* and `dtick` to *M3*. To set ticks every 4 years, set `dtick` to *M48*" + }, + "tickvals": { + "type": "string", + "description": "Sets the values at which ticks on this axis appear. Only has an effect if `tickmode` is set to *array*. Used with `ticktext`." + }, + "ticktext": { + "type": "string", + "description": "Sets the text displayed at the ticks position via `tickvals`. Only has an effect if `tickmode` is set to *array*. Used with `tickvals`." + }, + "ticks": { + "type": "string", + "description": "Determines whether ticks are drawn or not. If **, this axis' ticks are not drawn. If *outside* (*inside*), this axis' are drawn outside (inside) the axis lines.", + "default": "", + "enum": [ + "outside", + "inside", + "" + ] + }, + "ticklabelposition": { + "type": "string", + "description": "Determines where tick labels are drawn.", + "default": "outside", + "enum": [ + "outside", + "inside", + "outside top", + "inside top", + "outside bottom", + "inside bottom" + ] + }, + "ticklen": { + "type": "number", + "description": "Sets the tick length (in px).", + "default": 5, + "minimum": 0 + }, + "tickwidth": { + "type": "number", + "description": "Sets the tick width (in px).", + "default": 1, + "minimum": 0 + }, + "tickcolor": { + "type": "string", + "description": "Sets the tick color.", + "default": "#444" + }, + "showticklabels": { + "type": "boolean", + "description": "Determines whether or not the tick labels are drawn.", + "default": true + }, + "tickfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "tickangle": { + "type": "number", + "description": "Sets the angle of the tick labels with respect to the horizontal. For example, a `tickangle` of -90 draws the tick labels vertically.", + "default": "auto", + "minimum": -180, + "maximum": 180 + }, + "tickformat": { + "type": "string", + "description": "Sets the tick label formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "tickprefix": { + "type": "string", + "description": "Sets a tick label prefix.", + "default": "" + }, + "showtickprefix": { + "type": "string", + "description": "If *all*, all tick labels are displayed with a prefix. If *first*, only the first tick is displayed with a prefix. If *last*, only the last tick is displayed with a suffix. If *none*, tick prefixes are hidden.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "ticksuffix": { + "type": "string", + "description": "Sets a tick label suffix.", + "default": "" + }, + "showticksuffix": { + "type": "string", + "description": "Same as `showtickprefix` but for tick suffixes.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "separatethousands": { + "type": "boolean", + "description": "If \"true\", even 4-digit integers are separated", + "default": false + }, + "exponentformat": { + "type": "string", + "description": "Determines a formatting rule for the tick exponents. For example, consider the number 1,000,000,000. If *none*, it appears as 1,000,000,000. If *e*, 1e+9. If *E*, 1E+9. If *power*, 1x10^9 (with 9 in a super script). If *SI*, 1G. If *B*, 1B.", + "default": "B", + "enum": [ + "none", + "e", + "E", + "power", + "SI", + "B" + ] + }, + "minexponent": { + "type": "number", + "description": "Hide SI prefix for 10^n if |n| is below this number. This only has an effect when `tickformat` is *SI* or *B*.", + "default": 3, + "minimum": 0 + }, + "showexponent": { + "type": "string", + "description": "If *all*, all exponents are shown besides their significands. If *first*, only the exponent of the first tick is shown. If *last*, only the exponent of the last tick is shown. If *none*, no exponents appear.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "title": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Sets the title of the color bar. Note that before the existence of `title.text`, the title's contents used to be defined as the `title` attribute itself. This behavior has been deprecated." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "side": { + "type": "string", + "description": "Determines the location of color bar's title with respect to the color bar. Note that the title's location used to be set by the now deprecated `titleside` attribute.", + "default": "top", + "enum": [ + "right", + "top", + "bottom" + ] + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/escalation/plotly_api/mesh3d_plotly_schema.json b/escalation/plotly_api/mesh3d_plotly_schema.json new file mode 100644 index 0000000..3bb67ad --- /dev/null +++ b/escalation/plotly_api/mesh3d_plotly_schema.json @@ -0,0 +1,742 @@ +{ + "attributes": { + "visible": { + "type": "string", + "description": "Determines whether or not this trace is visible. If *legendonly*, the trace is not drawn, but can appear as a legend item (provided that the legend itself is visible).", + "default": true, + "enum": [ + true, + false, + "legendonly" + ] + }, + "legendgroup": { + "type": "string", + "description": "Sets the legend group for this trace. Traces part of the same legend group hide/show at the same time when toggling legend items.", + "default": "" + }, + "name": { + "type": "string", + "description": "Sets the trace name. The trace name appear as the legend item and on hover." + }, + "customdata": { + "type": "string", + "description": "Assigns extra data each datum. This may be useful when listening to hover, click and selection events. Note that, *scatter* traces also appends customdata items in the markers DOM elements" + }, + "meta": { + "type": "string", + "description": "Assigns extra meta information associated with this trace that can be used in various text attributes. Attributes such as trace `name`, graph, axis and colorbar `title.text`, annotation `text` `rangeselector`, `updatemenues` and `sliders` `label` text all support `meta`. To access the trace `meta` values in an attribute in the same trace, simply use `%{meta[i]}` where `i` is the index or key of the `meta` item in question. To access trace `meta` in layout attributes, use `%{data[n[.meta[i]}` where `i` is the index or key of the `meta` and `n` is the trace index." + }, + "hoverlabel": { + "type": "object", + "properties": { + "bgcolor": { + "type": "string", + "description": "Sets the background color of the hover labels for this trace" + }, + "bordercolor": { + "type": "string", + "description": "Sets the border color of the hover labels for this trace." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "align": { + "type": "string", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "default": "auto", + "enum": [ + "left", + "right", + "auto" + ] + }, + "namelength": { + "type": "integer", + "description": "Sets the default length (in number of characters) of the trace name in the hover labels for all traces. -1 shows the whole name regardless of length. 0-3 shows the first 0-3 characters, and an integer >3 will show the whole name if it is less than that many characters, but if it is longer, will truncate to `namelength - 3` characters and add an ellipsis.", + "default": 15, + "minimum": -1 + } + } + }, + "uirevision": { + "type": "string", + "description": "Controls persistence of some user-driven changes to the trace: `constraintrange` in `parcoords` traces, as well as some `editable: true` modifications such as `name` and `colorbar.title`. Defaults to `layout.uirevision`. Note that other user-driven trace attribute changes are controlled by `layout` attributes: `trace.visible` is controlled by `layout.legend.uirevision`, `selectedpoints` is controlled by `layout.selectionrevision`, and `colorbar.(x|y)` (accessible with `config: {editable: true}`) is controlled by `layout.editrevision`. Trace changes are tracked by `uid`, which only falls back on trace index if no `uid` is provided. So if your app can add/remove traces before the end of the `data` array, such that the same trace has a different index, you can still preserve user-driven changes if you give each trace a `uid` that stays with it as it moves." + }, + "x": { + "type": "string", + "description": "Sets the X coordinates of the vertices. The nth element of vectors `x`, `y` and `z` jointly represent the X, Y and Z coordinates of the nth vertex." + }, + "y": { + "type": "string", + "description": "Sets the Y coordinates of the vertices. The nth element of vectors `x`, `y` and `z` jointly represent the X, Y and Z coordinates of the nth vertex." + }, + "z": { + "type": "string", + "description": "Sets the Z coordinates of the vertices. The nth element of vectors `x`, `y` and `z` jointly represent the X, Y and Z coordinates of the nth vertex." + }, + "i": { + "type": "string", + "description": "A vector of vertex indices, i.e. integer values between 0 and the length of the vertex vectors, representing the *first* vertex of a triangle. For example, `{i[m], j[m], k[m]}` together represent face m (triangle m) in the mesh, where `i[m] = n` points to the triplet `{x[n], y[n], z[n]}` in the vertex arrays. Therefore, each element in `i` represents a point in space, which is the first vertex of a triangle." + }, + "j": { + "type": "string", + "description": "A vector of vertex indices, i.e. integer values between 0 and the length of the vertex vectors, representing the *second* vertex of a triangle. For example, `{i[m], j[m], k[m]}` together represent face m (triangle m) in the mesh, where `j[m] = n` points to the triplet `{x[n], y[n], z[n]}` in the vertex arrays. Therefore, each element in `j` represents a point in space, which is the second vertex of a triangle." + }, + "k": { + "type": "string", + "description": "A vector of vertex indices, i.e. integer values between 0 and the length of the vertex vectors, representing the *third* vertex of a triangle. For example, `{i[m], j[m], k[m]}` together represent face m (triangle m) in the mesh, where `k[m] = n` points to the triplet `{x[n], y[n], z[n]}` in the vertex arrays. Therefore, each element in `k` represents a point in space, which is the third vertex of a triangle." + }, + "text": { + "type": "string", + "description": "Sets the text elements associated with the vertices. If trace `hoverinfo` contains a *text* flag and *hovertext* is not set, these elements will be seen in the hover labels.", + "default": "" + }, + "delaunayaxis": { + "type": "string", + "description": "Sets the Delaunay axis, which is the axis that is perpendicular to the surface of the Delaunay triangulation. It has an effect if `i`, `j`, `k` are not provided and `alphahull` is set to indicate Delaunay triangulation.", + "default": "z", + "enum": [ + "x", + "y", + "z" + ] + }, + "alphahull": { + "type": "number", + "description": "Determines how the mesh surface triangles are derived from the set of vertices (points) represented by the `x`, `y` and `z` arrays, if the `i`, `j`, `k` arrays are not supplied. For general use of `mesh3d` it is preferred that `i`, `j`, `k` are supplied. If *-1*, Delaunay triangulation is used, which is mainly suitable if the mesh is a single, more or less layer surface that is perpendicular to `delaunayaxis`. In case the `delaunayaxis` intersects the mesh surface at more than one point it will result triangles that are very long in the dimension of `delaunayaxis`. If *>0*, the alpha-shape algorithm is used. In this case, the positive `alphahull` value signals the use of the alpha-shape algorithm, _and_ its value acts as the parameter for the mesh fitting. If *0*, the convex-hull algorithm is used. It is suitable for convex bodies or if the intention is to enclose the `x`, `y` and `z` point set into a convex hull.", + "default": -1 + }, + "intensity": { + "type": "string", + "description": "Sets the intensity values for vertices or cells as defined by `intensitymode`. It can be used for plotting fields on meshes." + }, + "intensitymode": { + "type": "string", + "description": "Determines the source of `intensity` values.", + "default": "vertex", + "enum": [ + "vertex", + "cell" + ] + }, + "color": { + "type": "string", + "description": "Sets the color of the whole mesh" + }, + "vertexcolor": { + "type": "string", + "description": "Sets the color of each vertex Overrides *color*. While Red, green and blue colors are in the range of 0 and 255; in the case of having vertex color data in RGBA format, the alpha color should be normalized to be between 0 and 1." + }, + "facecolor": { + "type": "string", + "description": "Sets the color of each face Overrides *color* and *vertexcolor*." + }, + "cauto": { + "type": "boolean", + "description": "Determines whether or not the color domain is computed with respect to the input data (here `intensity`) or the bounds set in `cmin` and `cmax` Defaults to `false` when `cmin` and `cmax` are set by the user.", + "default": true + }, + "cmin": { + "type": "number", + "description": "Sets the lower bound of the color domain. Value should have the same units as `intensity` and if set, `cmax` must be set as well.", + "default": null + }, + "cmax": { + "type": "number", + "description": "Sets the upper bound of the color domain. Value should have the same units as `intensity` and if set, `cmin` must be set as well.", + "default": null + }, + "cmid": { + "type": "number", + "description": "Sets the mid-point of the color domain by scaling `cmin` and/or `cmax` to be equidistant to this point. Value should have the same units as `intensity`. Has no effect when `cauto` is `false`.", + "default": null + }, + "colorscale": { + "type": "string", + "description": "Sets the colorscale. The colorscale must be an array containing arrays mapping a normalized value to an rgb, rgba, hex, hsl, hsv, or named color string. At minimum, a mapping for the lowest (0) and highest (1) values are required. For example, `[[0, 'rgb(0,0,255)'], [1, 'rgb(255,0,0)']]`. To control the bounds of the colorscale in color space, use`cmin` and `cmax`. Alternatively, `colorscale` may be a palette name string of the following list: Greys,YlGnBu,Greens,YlOrRd,Bluered,RdBu,Reds,Blues,Picnic,Rainbow,Portland,Jet,Hot,Blackbody,Earth,Electric,Viridis,Cividis.", + "default": null + }, + "autocolorscale": { + "type": "boolean", + "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `colorscale`. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.", + "default": true + }, + "reversescale": { + "type": "boolean", + "description": "Reverses the color mapping if true. If true, `cmin` will correspond to the last color in the array and `cmax` will correspond to the first color.", + "default": false + }, + "showscale": { + "type": "boolean", + "description": "Determines whether or not a colorbar is displayed for this trace.", + "default": true + }, + "colorbar": { + "type": "object", + "properties": { + "thicknessmode": { + "type": "string", + "description": "Determines whether this color bar's thickness (i.e. the measure in the constant color direction) is set in units of plot *fraction* or in *pixels*. Use `thickness` to set the value.", + "default": "pixels", + "enum": [ + "fraction", + "pixels" + ] + }, + "thickness": { + "type": "number", + "description": "Sets the thickness of the color bar This measure excludes the size of the padding, ticks and labels.", + "default": 30, + "minimum": 0 + }, + "lenmode": { + "type": "string", + "description": "Determines whether this color bar's length (i.e. the measure in the color variation direction) is set in units of plot *fraction* or in *pixels. Use `len` to set the value.", + "default": "fraction", + "enum": [ + "fraction", + "pixels" + ] + }, + "len": { + "type": "number", + "description": "Sets the length of the color bar This measure excludes the padding of both ends. That is, the color bar length is this length minus the padding on both ends.", + "default": 1, + "minimum": 0 + }, + "x": { + "type": "number", + "description": "Sets the x position of the color bar (in plot fraction).", + "default": 1.02, + "minimum": -2, + "maximum": 3 + }, + "xanchor": { + "type": "string", + "description": "Sets this color bar's horizontal position anchor. This anchor binds the `x` position to the *left*, *center* or *right* of the color bar.", + "default": "left", + "enum": [ + "left", + "center", + "right" + ] + }, + "xpad": { + "type": "number", + "description": "Sets the amount of padding (in px) along the x direction.", + "default": 10, + "minimum": 0 + }, + "y": { + "type": "number", + "description": "Sets the y position of the color bar (in plot fraction).", + "default": 0.5, + "minimum": -2, + "maximum": 3 + }, + "yanchor": { + "type": "string", + "description": "Sets this color bar's vertical position anchor This anchor binds the `y` position to the *top*, *middle* or *bottom* of the color bar.", + "default": "middle", + "enum": [ + "top", + "middle", + "bottom" + ] + }, + "ypad": { + "type": "number", + "description": "Sets the amount of padding (in px) along the y direction.", + "default": 10, + "minimum": 0 + }, + "outlinecolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "outlinewidth": { + "type": "number", + "description": "Sets the width (in px) of the axis line.", + "default": 1, + "minimum": 0 + }, + "bordercolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "borderwidth": { + "type": "number", + "description": "Sets the width (in px) or the border enclosing this color bar.", + "default": 0, + "minimum": 0 + }, + "bgcolor": { + "type": "string", + "description": "Sets the color of padded area.", + "default": "rgba(0,0,0,0)" + }, + "tickmode": { + "type": "string", + "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).", + "enum": [ + "auto", + "linear", + "array" + ] + }, + "nticks": { + "type": "integer", + "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", + "default": 0, + "minimum": 0 + }, + "tick0": { + "type": "string", + "description": "Sets the placement of the first tick on this axis. Use with `dtick`. If the axis `type` is *log*, then you must take the log of your starting tick (e.g. to set the starting tick to 100, set the `tick0` to 2) except when `dtick`=*L* (see `dtick` for more info). If the axis `type` is *date*, it should be a date string, like date data. If the axis `type` is *category*, it should be a number, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "dtick": { + "type": "string", + "description": "Sets the step in-between ticks on this axis. Use with `tick0`. Must be a positive number, or special strings available to *log* and *date* axes. If the axis `type` is *log*, then ticks are set every 10^(n*dtick) where n is the tick number. For example, to set a tick mark at 1, 10, 100, 1000, ... set dtick to 1. To set tick marks at 1, 100, 10000, ... set dtick to 2. To set tick marks at 1, 5, 25, 125, 625, 3125, ... set dtick to log_10(5), or 0.69897000433. *log* has several special values; *L*, where `f` is a positive number, gives ticks linearly spaced in value (but not position). For example `tick0` = 0.1, `dtick` = *L0.5* will put ticks at 0.1, 0.6, 1.1, 1.6 etc. To show powers of 10 plus small digits between, use *D1* (all digits) or *D2* (only 2 and 5). `tick0` is ignored for *D1* and *D2*. If the axis `type` is *date*, then you must convert the time to milliseconds. For example, to set the interval between ticks to one day, set `dtick` to 86400000.0. *date* also has special values *M* gives ticks spaced by a number of months. `n` must be a positive integer. To set ticks on the 15th of every third month, set `tick0` to *2000-01-15* and `dtick` to *M3*. To set ticks every 4 years, set `dtick` to *M48*" + }, + "tickvals": { + "type": "string", + "description": "Sets the values at which ticks on this axis appear. Only has an effect if `tickmode` is set to *array*. Used with `ticktext`." + }, + "ticktext": { + "type": "string", + "description": "Sets the text displayed at the ticks position via `tickvals`. Only has an effect if `tickmode` is set to *array*. Used with `tickvals`." + }, + "ticks": { + "type": "string", + "description": "Determines whether ticks are drawn or not. If **, this axis' ticks are not drawn. If *outside* (*inside*), this axis' are drawn outside (inside) the axis lines.", + "default": "", + "enum": [ + "outside", + "inside", + "" + ] + }, + "ticklabelposition": { + "type": "string", + "description": "Determines where tick labels are drawn.", + "default": "outside", + "enum": [ + "outside", + "inside", + "outside top", + "inside top", + "outside bottom", + "inside bottom" + ] + }, + "ticklen": { + "type": "number", + "description": "Sets the tick length (in px).", + "default": 5, + "minimum": 0 + }, + "tickwidth": { + "type": "number", + "description": "Sets the tick width (in px).", + "default": 1, + "minimum": 0 + }, + "tickcolor": { + "type": "string", + "description": "Sets the tick color.", + "default": "#444" + }, + "showticklabels": { + "type": "boolean", + "description": "Determines whether or not the tick labels are drawn.", + "default": true + }, + "tickfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "tickangle": { + "type": "number", + "description": "Sets the angle of the tick labels with respect to the horizontal. For example, a `tickangle` of -90 draws the tick labels vertically.", + "default": "auto", + "minimum": -180, + "maximum": 180 + }, + "tickformat": { + "type": "string", + "description": "Sets the tick label formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "tickprefix": { + "type": "string", + "description": "Sets a tick label prefix.", + "default": "" + }, + "showtickprefix": { + "type": "string", + "description": "If *all*, all tick labels are displayed with a prefix. If *first*, only the first tick is displayed with a prefix. If *last*, only the last tick is displayed with a suffix. If *none*, tick prefixes are hidden.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "ticksuffix": { + "type": "string", + "description": "Sets a tick label suffix.", + "default": "" + }, + "showticksuffix": { + "type": "string", + "description": "Same as `showtickprefix` but for tick suffixes.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "separatethousands": { + "type": "boolean", + "description": "If \"true\", even 4-digit integers are separated", + "default": false + }, + "exponentformat": { + "type": "string", + "description": "Determines a formatting rule for the tick exponents. For example, consider the number 1,000,000,000. If *none*, it appears as 1,000,000,000. If *e*, 1e+9. If *E*, 1E+9. If *power*, 1x10^9 (with 9 in a super script). If *SI*, 1G. If *B*, 1B.", + "default": "B", + "enum": [ + "none", + "e", + "E", + "power", + "SI", + "B" + ] + }, + "minexponent": { + "type": "number", + "description": "Hide SI prefix for 10^n if |n| is below this number. This only has an effect when `tickformat` is *SI* or *B*.", + "default": 3, + "minimum": 0 + }, + "showexponent": { + "type": "string", + "description": "If *all*, all exponents are shown besides their significands. If *first*, only the exponent of the first tick is shown. If *last*, only the exponent of the last tick is shown. If *none*, no exponents appear.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "title": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Sets the title of the color bar. Note that before the existence of `title.text`, the title's contents used to be defined as the `title` attribute itself. This behavior has been deprecated." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "side": { + "type": "string", + "description": "Determines the location of color bar's title with respect to the color bar. Note that the title's location used to be set by the now deprecated `titleside` attribute.", + "default": "top", + "enum": [ + "right", + "top", + "bottom" + ] + } + } + } + } + }, + "coloraxis": { + "type": "string", + "description": "Sets a reference to a shared color axis. References to these shared color axes are *coloraxis*, *coloraxis2*, *coloraxis3*, etc. Settings for these shared color axes are set in the layout, under `layout.coloraxis`, `layout.coloraxis2`, etc. Note that multiple color scales can be linked to the same color axis.", + "default": null + }, + "opacity": { + "type": "number", + "description": "Sets the opacity of the surface. Please note that in the case of using high `opacity` values for example a value greater than or equal to 0.5 on two surfaces (and 0.25 with four surfaces), an overlay of multiple transparent surfaces may not perfectly be sorted in depth by the webgl API. This behavior may be improved in the near future and is subject to change.", + "default": 1, + "minimum": 0, + "maximum": 1 + }, + "flatshading": { + "type": "boolean", + "description": "Determines whether or not normal smoothing is applied to the meshes, creating meshes with an angular, low-poly look via flat reflections.", + "default": false + }, + "contour": { + "type": "object", + "properties": { + "show": { + "type": "boolean", + "description": "Sets whether or not dynamic contours are shown on hover", + "default": false + }, + "color": { + "type": "string", + "description": "Sets the color of the contour lines.", + "default": "#444" + }, + "width": { + "type": "number", + "description": "Sets the width of the contour lines.", + "default": 2, + "minimum": 1, + "maximum": 16 + } + } + }, + "lightposition": { + "type": "object", + "properties": { + "x": { + "type": "number", + "description": "Numeric vector, representing the X coordinate for each vertex.", + "default": 100000, + "minimum": -100000, + "maximum": 100000 + }, + "y": { + "type": "number", + "description": "Numeric vector, representing the Y coordinate for each vertex.", + "default": 100000, + "minimum": -100000, + "maximum": 100000 + }, + "z": { + "type": "number", + "description": "Numeric vector, representing the Z coordinate for each vertex.", + "default": 0, + "minimum": -100000, + "maximum": 100000 + } + } + }, + "lighting": { + "type": "object", + "properties": { + "vertexnormalsepsilon": { + "type": "number", + "description": "Epsilon for vertex normals calculation avoids math issues arising from degenerate geometry.", + "default": 1e-12, + "minimum": 0, + "maximum": 1 + }, + "facenormalsepsilon": { + "type": "number", + "description": "Epsilon for face normals calculation avoids math issues arising from degenerate geometry.", + "default": 1e-06, + "minimum": 0, + "maximum": 1 + }, + "ambient": { + "type": "number", + "description": "Ambient light increases overall color visibility but can wash out the image.", + "default": 0.8, + "minimum": 0, + "maximum": 1 + }, + "diffuse": { + "type": "number", + "description": "Represents the extent that incident rays are reflected in a range of angles.", + "default": 0.8, + "minimum": 0, + "maximum": 1 + }, + "specular": { + "type": "number", + "description": "Represents the level that incident rays are reflected in a single direction, causing shine.", + "default": 0.05, + "minimum": 0, + "maximum": 2 + }, + "roughness": { + "type": "number", + "description": "Alters specular reflection; the rougher the surface, the wider and less contrasty the shine.", + "default": 0.5, + "minimum": 0, + "maximum": 1 + }, + "fresnel": { + "type": "number", + "description": "Represents the reflectance as a dependency of the viewing angle; e.g. paper is reflective when viewing it from the edge of the paper (almost 90 degrees), causing shine.", + "default": 0.2, + "minimum": 0, + "maximum": 5 + } + } + }, + "hoverinfo": { + "type": "string", + "description": "Determines which trace information appear on hover. If `none` or `skip` are set, no information is displayed upon hovering. But, if `none` is set, click and hover events are still fired.", + "default": "all", + "enum": [ + "name", + "text", + "text+name", + "z", + "z+name", + "z+text", + "z+text+name", + "y", + "y+name", + "y+text", + "y+text+name", + "y+z", + "y+z+name", + "y+z+text", + "y+z+text+name", + "x", + "x+name", + "x+text", + "x+text+name", + "x+z", + "x+z+name", + "x+z+text", + "x+z+text+name", + "x+y", + "x+y+name", + "x+y+text", + "x+y+text+name", + "x+y+z", + "x+y+z+name", + "x+y+z+text", + "x+y+z+text+name", + "all", + "none", + "skip" + ] + }, + "showlegend": { + "type": "boolean", + "description": "Determines whether or not an item corresponding to this trace is shown in the legend.", + "default": false + }, + "xcalendar": { + "type": "string", + "description": "Sets the calendar system to use with `x` date data.", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + }, + "ycalendar": { + "type": "string", + "description": "Sets the calendar system to use with `y` date data.", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + }, + "zcalendar": { + "type": "string", + "description": "Sets the calendar system to use with `z` date data.", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + }, + "scene": { + "type": "string", + "description": "Sets a reference between this trace's 3D coordinate system and a 3D scene. If *scene* (the default value), the (x,y,z) coordinates refer to `layout.scene`. If *scene2*, the (x,y,z) coordinates refer to `layout.scene2`, and so on.", + "default": "scene" + } + } +} \ No newline at end of file diff --git a/escalation/plotly_api/scatter3d_plotly_schema.json b/escalation/plotly_api/scatter3d_plotly_schema.json new file mode 100644 index 0000000..60d76e6 --- /dev/null +++ b/escalation/plotly_api/scatter3d_plotly_schema.json @@ -0,0 +1,1442 @@ +{ + "attributes": { + "visible": { + "type": "string", + "description": "Determines whether or not this trace is visible. If *legendonly*, the trace is not drawn, but can appear as a legend item (provided that the legend itself is visible).", + "default": true, + "enum": [ + true, + false, + "legendonly" + ] + }, + "showlegend": { + "type": "boolean", + "description": "Determines whether or not an item corresponding to this trace is shown in the legend.", + "default": true + }, + "legendgroup": { + "type": "string", + "description": "Sets the legend group for this trace. Traces part of the same legend group hide/show at the same time when toggling legend items.", + "default": "" + }, + "opacity": { + "type": "number", + "description": "Sets the opacity of the trace.", + "default": 1, + "minimum": 0, + "maximum": 1 + }, + "name": { + "type": "string", + "description": "Sets the trace name. The trace name appear as the legend item and on hover." + }, + "customdata": { + "type": "string", + "description": "Assigns extra data each datum. This may be useful when listening to hover, click and selection events. Note that, *scatter* traces also appends customdata items in the markers DOM elements" + }, + "meta": { + "type": "string", + "description": "Assigns extra meta information associated with this trace that can be used in various text attributes. Attributes such as trace `name`, graph, axis and colorbar `title.text`, annotation `text` `rangeselector`, `updatemenues` and `sliders` `label` text all support `meta`. To access the trace `meta` values in an attribute in the same trace, simply use `%{meta[i]}` where `i` is the index or key of the `meta` item in question. To access trace `meta` in layout attributes, use `%{data[n[.meta[i]}` where `i` is the index or key of the `meta` and `n` is the trace index." + }, + "hoverlabel": { + "type": "object", + "properties": { + "bgcolor": { + "type": "string", + "description": "Sets the background color of the hover labels for this trace" + }, + "bordercolor": { + "type": "string", + "description": "Sets the border color of the hover labels for this trace." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "align": { + "type": "string", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "default": "auto", + "enum": [ + "left", + "right", + "auto" + ] + }, + "namelength": { + "type": "integer", + "description": "Sets the default length (in number of characters) of the trace name in the hover labels for all traces. -1 shows the whole name regardless of length. 0-3 shows the first 0-3 characters, and an integer >3 will show the whole name if it is less than that many characters, but if it is longer, will truncate to `namelength - 3` characters and add an ellipsis.", + "default": 15, + "minimum": -1 + } + } + }, + "uirevision": { + "type": "string", + "description": "Controls persistence of some user-driven changes to the trace: `constraintrange` in `parcoords` traces, as well as some `editable: true` modifications such as `name` and `colorbar.title`. Defaults to `layout.uirevision`. Note that other user-driven trace attribute changes are controlled by `layout` attributes: `trace.visible` is controlled by `layout.legend.uirevision`, `selectedpoints` is controlled by `layout.selectionrevision`, and `colorbar.(x|y)` (accessible with `config: {editable: true}`) is controlled by `layout.editrevision`. Trace changes are tracked by `uid`, which only falls back on trace index if no `uid` is provided. So if your app can add/remove traces before the end of the `data` array, such that the same trace has a different index, you can still preserve user-driven changes if you give each trace a `uid` that stays with it as it moves." + }, + "x": { + "type": "string", + "description": "Sets the x coordinates." + }, + "y": { + "type": "string", + "description": "Sets the y coordinates." + }, + "z": { + "type": "string", + "description": "Sets the z coordinates." + }, + "text": { + "type": "string", + "description": "Sets text elements associated with each (x,y,z) triplet. If a single string, the same string appears over all the data points. If an array of string, the items are mapped in order to the this trace's (x,y,z) coordinates. If trace `hoverinfo` contains a *text* flag and *hovertext* is not set, these elements will be seen in the hover labels.", + "default": "" + }, + "texttemplate": { + "type": "string", + "description": "Template string used for rendering the information text that appear on points. Note that this will override `textinfo`. Variables are inserted using %{variable}, for example \"y: %{y}\". Numbers are formatted using d3-format's syntax %{variable:d3-format}, for example \"Price: %{y:$.2f}\". https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format for details on the formatting syntax. Dates are formatted using d3-time-format's syntax %{variable|d3-time-format}, for example \"Day: %{2019-01-01|%A}\". https://github.com/d3/d3-time-format#locale_format for details on the date formatting syntax. Every attributes that can be specified per-point (the ones that are `arrayOk: true`) are available. ", + "default": "" + }, + "mode": { + "type": "string", + "description": "Determines the drawing mode for this scatter trace. If the provided `mode` includes *text* then the `text` elements appear at the coordinates. Otherwise, the `text` elements appear on hover. If there are less than 20 points and the trace is not stacked then the default is *lines+markers*. Otherwise, *lines*.", + "default": "lines+markers", + "enum": [ + "text", + "markers", + "markers+text", + "lines", + "lines+text", + "lines+markers", + "lines+markers+text", + "none" + ] + }, + "surfaceaxis": { + "type": "string", + "description": "If *-1*, the scatter points are not fill with a surface If *0*, *1*, *2*, the scatter points are filled with a Delaunay surface about the x, y, z respectively.", + "default": -1, + "enum": [ + -1, + 0, + 1, + 2 + ] + }, + "surfacecolor": { + "type": "string", + "description": "Sets the surface fill color." + }, + "projection": { + "type": "object", + "properties": { + "x": { + "type": "object", + "properties": { + "show": { + "type": "boolean", + "description": "Sets whether or not projections are shown along the x axis.", + "default": false + }, + "opacity": { + "type": "number", + "description": "Sets the projection color.", + "default": 1, + "minimum": 0, + "maximum": 1 + }, + "scale": { + "type": "number", + "description": "Sets the scale factor determining the size of the projection marker points.", + "default": 0.6666666666666666, + "minimum": 0, + "maximum": 10 + } + } + }, + "y": { + "type": "object", + "properties": { + "show": { + "type": "boolean", + "description": "Sets whether or not projections are shown along the y axis.", + "default": false + }, + "opacity": { + "type": "number", + "description": "Sets the projection color.", + "default": 1, + "minimum": 0, + "maximum": 1 + }, + "scale": { + "type": "number", + "description": "Sets the scale factor determining the size of the projection marker points.", + "default": 0.6666666666666666, + "minimum": 0, + "maximum": 10 + } + } + }, + "z": { + "type": "object", + "properties": { + "show": { + "type": "boolean", + "description": "Sets whether or not projections are shown along the z axis.", + "default": false + }, + "opacity": { + "type": "number", + "description": "Sets the projection color.", + "default": 1, + "minimum": 0, + "maximum": 1 + }, + "scale": { + "type": "number", + "description": "Sets the scale factor determining the size of the projection marker points.", + "default": 0.6666666666666666, + "minimum": 0, + "maximum": 10 + } + } + } + } + }, + "connectgaps": { + "type": "boolean", + "description": "Determines whether or not gaps (i.e. {nan} or missing values) in the provided data arrays are connected.", + "default": false + }, + "line": { + "type": "object", + "properties": { + "width": { + "type": "number", + "description": "Sets the line width (in px).", + "default": 2, + "minimum": 0 + }, + "dash": { + "type": "string", + "description": "Sets the dash style of the lines.", + "default": "solid", + "enum": [ + "solid", + "dot", + "dash", + "longdash", + "dashdot", + "longdashdot" + ] + }, + "color": { + "type": "string", + "description": "Sets thelinecolor. It accepts either a specific color or an array of numbers that are mapped to the colorscale relative to the max and min values of the array or relative to `line.cmin` and `line.cmax` if set." + }, + "cauto": { + "type": "boolean", + "description": "Determines whether or not the color domain is computed with respect to the input data (here in `line.color`) or the bounds set in `line.cmin` and `line.cmax` Has an effect only if in `line.color`is set to a numerical array. Defaults to `false` when `line.cmin` and `line.cmax` are set by the user.", + "default": true + }, + "cmin": { + "type": "number", + "description": "Sets the lower bound of the color domain. Has an effect only if in `line.color`is set to a numerical array. Value should have the same units as in `line.color` and if set, `line.cmax` must be set as well.", + "default": null + }, + "cmax": { + "type": "number", + "description": "Sets the upper bound of the color domain. Has an effect only if in `line.color`is set to a numerical array. Value should have the same units as in `line.color` and if set, `line.cmin` must be set as well.", + "default": null + }, + "cmid": { + "type": "number", + "description": "Sets the mid-point of the color domain by scaling `line.cmin` and/or `line.cmax` to be equidistant to this point. Has an effect only if in `line.color`is set to a numerical array. Value should have the same units as in `line.color`. Has no effect when `line.cauto` is `false`.", + "default": null + }, + "colorscale": { + "type": "string", + "description": "Sets the colorscale. Has an effect only if in `line.color`is set to a numerical array. The colorscale must be an array containing arrays mapping a normalized value to an rgb, rgba, hex, hsl, hsv, or named color string. At minimum, a mapping for the lowest (0) and highest (1) values are required. For example, `[[0, 'rgb(0,0,255)'], [1, 'rgb(255,0,0)']]`. To control the bounds of the colorscale in color space, use`line.cmin` and `line.cmax`. Alternatively, `colorscale` may be a palette name string of the following list: Greys,YlGnBu,Greens,YlOrRd,Bluered,RdBu,Reds,Blues,Picnic,Rainbow,Portland,Jet,Hot,Blackbody,Earth,Electric,Viridis,Cividis.", + "default": null + }, + "autocolorscale": { + "type": "boolean", + "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `line.colorscale`. Has an effect only if in `line.color`is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.", + "default": true + }, + "reversescale": { + "type": "boolean", + "description": "Reverses the color mapping if true. Has an effect only if in `line.color`is set to a numerical array. If true, `line.cmin` will correspond to the last color in the array and `line.cmax` will correspond to the first color.", + "default": false + }, + "showscale": { + "type": "boolean", + "description": "Determines whether or not a colorbar is displayed for this trace. Has an effect only if in `line.color`is set to a numerical array.", + "default": false + }, + "colorbar": { + "type": "object", + "properties": { + "thicknessmode": { + "type": "string", + "description": "Determines whether this color bar's thickness (i.e. the measure in the constant color direction) is set in units of plot *fraction* or in *pixels*. Use `thickness` to set the value.", + "default": "pixels", + "enum": [ + "fraction", + "pixels" + ] + }, + "thickness": { + "type": "number", + "description": "Sets the thickness of the color bar This measure excludes the size of the padding, ticks and labels.", + "default": 30, + "minimum": 0 + }, + "lenmode": { + "type": "string", + "description": "Determines whether this color bar's length (i.e. the measure in the color variation direction) is set in units of plot *fraction* or in *pixels. Use `len` to set the value.", + "default": "fraction", + "enum": [ + "fraction", + "pixels" + ] + }, + "len": { + "type": "number", + "description": "Sets the length of the color bar This measure excludes the padding of both ends. That is, the color bar length is this length minus the padding on both ends.", + "default": 1, + "minimum": 0 + }, + "x": { + "type": "number", + "description": "Sets the x position of the color bar (in plot fraction).", + "default": 1.02, + "minimum": -2, + "maximum": 3 + }, + "xanchor": { + "type": "string", + "description": "Sets this color bar's horizontal position anchor. This anchor binds the `x` position to the *left*, *center* or *right* of the color bar.", + "default": "left", + "enum": [ + "left", + "center", + "right" + ] + }, + "xpad": { + "type": "number", + "description": "Sets the amount of padding (in px) along the x direction.", + "default": 10, + "minimum": 0 + }, + "y": { + "type": "number", + "description": "Sets the y position of the color bar (in plot fraction).", + "default": 0.5, + "minimum": -2, + "maximum": 3 + }, + "yanchor": { + "type": "string", + "description": "Sets this color bar's vertical position anchor This anchor binds the `y` position to the *top*, *middle* or *bottom* of the color bar.", + "default": "middle", + "enum": [ + "top", + "middle", + "bottom" + ] + }, + "ypad": { + "type": "number", + "description": "Sets the amount of padding (in px) along the y direction.", + "default": 10, + "minimum": 0 + }, + "outlinecolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "outlinewidth": { + "type": "number", + "description": "Sets the width (in px) of the axis line.", + "default": 1, + "minimum": 0 + }, + "bordercolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "borderwidth": { + "type": "number", + "description": "Sets the width (in px) or the border enclosing this color bar.", + "default": 0, + "minimum": 0 + }, + "bgcolor": { + "type": "string", + "description": "Sets the color of padded area.", + "default": "rgba(0,0,0,0)" + }, + "tickmode": { + "type": "string", + "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).", + "enum": [ + "auto", + "linear", + "array" + ] + }, + "nticks": { + "type": "integer", + "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", + "default": 0, + "minimum": 0 + }, + "tick0": { + "type": "string", + "description": "Sets the placement of the first tick on this axis. Use with `dtick`. If the axis `type` is *log*, then you must take the log of your starting tick (e.g. to set the starting tick to 100, set the `tick0` to 2) except when `dtick`=*L* (see `dtick` for more info). If the axis `type` is *date*, it should be a date string, like date data. If the axis `type` is *category*, it should be a number, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "dtick": { + "type": "string", + "description": "Sets the step in-between ticks on this axis. Use with `tick0`. Must be a positive number, or special strings available to *log* and *date* axes. If the axis `type` is *log*, then ticks are set every 10^(n*dtick) where n is the tick number. For example, to set a tick mark at 1, 10, 100, 1000, ... set dtick to 1. To set tick marks at 1, 100, 10000, ... set dtick to 2. To set tick marks at 1, 5, 25, 125, 625, 3125, ... set dtick to log_10(5), or 0.69897000433. *log* has several special values; *L*, where `f` is a positive number, gives ticks linearly spaced in value (but not position). For example `tick0` = 0.1, `dtick` = *L0.5* will put ticks at 0.1, 0.6, 1.1, 1.6 etc. To show powers of 10 plus small digits between, use *D1* (all digits) or *D2* (only 2 and 5). `tick0` is ignored for *D1* and *D2*. If the axis `type` is *date*, then you must convert the time to milliseconds. For example, to set the interval between ticks to one day, set `dtick` to 86400000.0. *date* also has special values *M* gives ticks spaced by a number of months. `n` must be a positive integer. To set ticks on the 15th of every third month, set `tick0` to *2000-01-15* and `dtick` to *M3*. To set ticks every 4 years, set `dtick` to *M48*" + }, + "tickvals": { + "type": "string", + "description": "Sets the values at which ticks on this axis appear. Only has an effect if `tickmode` is set to *array*. Used with `ticktext`." + }, + "ticktext": { + "type": "string", + "description": "Sets the text displayed at the ticks position via `tickvals`. Only has an effect if `tickmode` is set to *array*. Used with `tickvals`." + }, + "ticks": { + "type": "string", + "description": "Determines whether ticks are drawn or not. If **, this axis' ticks are not drawn. If *outside* (*inside*), this axis' are drawn outside (inside) the axis lines.", + "default": "", + "enum": [ + "outside", + "inside", + "" + ] + }, + "ticklabelposition": { + "type": "string", + "description": "Determines where tick labels are drawn.", + "default": "outside", + "enum": [ + "outside", + "inside", + "outside top", + "inside top", + "outside bottom", + "inside bottom" + ] + }, + "ticklen": { + "type": "number", + "description": "Sets the tick length (in px).", + "default": 5, + "minimum": 0 + }, + "tickwidth": { + "type": "number", + "description": "Sets the tick width (in px).", + "default": 1, + "minimum": 0 + }, + "tickcolor": { + "type": "string", + "description": "Sets the tick color.", + "default": "#444" + }, + "showticklabels": { + "type": "boolean", + "description": "Determines whether or not the tick labels are drawn.", + "default": true + }, + "tickfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "tickangle": { + "type": "number", + "description": "Sets the angle of the tick labels with respect to the horizontal. For example, a `tickangle` of -90 draws the tick labels vertically.", + "default": "auto", + "minimum": -180, + "maximum": 180 + }, + "tickformat": { + "type": "string", + "description": "Sets the tick label formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "tickprefix": { + "type": "string", + "description": "Sets a tick label prefix.", + "default": "" + }, + "showtickprefix": { + "type": "string", + "description": "If *all*, all tick labels are displayed with a prefix. If *first*, only the first tick is displayed with a prefix. If *last*, only the last tick is displayed with a suffix. If *none*, tick prefixes are hidden.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "ticksuffix": { + "type": "string", + "description": "Sets a tick label suffix.", + "default": "" + }, + "showticksuffix": { + "type": "string", + "description": "Same as `showtickprefix` but for tick suffixes.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "separatethousands": { + "type": "boolean", + "description": "If \"true\", even 4-digit integers are separated", + "default": false + }, + "exponentformat": { + "type": "string", + "description": "Determines a formatting rule for the tick exponents. For example, consider the number 1,000,000,000. If *none*, it appears as 1,000,000,000. If *e*, 1e+9. If *E*, 1E+9. If *power*, 1x10^9 (with 9 in a super script). If *SI*, 1G. If *B*, 1B.", + "default": "B", + "enum": [ + "none", + "e", + "E", + "power", + "SI", + "B" + ] + }, + "minexponent": { + "type": "number", + "description": "Hide SI prefix for 10^n if |n| is below this number. This only has an effect when `tickformat` is *SI* or *B*.", + "default": 3, + "minimum": 0 + }, + "showexponent": { + "type": "string", + "description": "If *all*, all exponents are shown besides their significands. If *first*, only the exponent of the first tick is shown. If *last*, only the exponent of the last tick is shown. If *none*, no exponents appear.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "title": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Sets the title of the color bar. Note that before the existence of `title.text`, the title's contents used to be defined as the `title` attribute itself. This behavior has been deprecated." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "side": { + "type": "string", + "description": "Determines the location of color bar's title with respect to the color bar. Note that the title's location used to be set by the now deprecated `titleside` attribute.", + "default": "top", + "enum": [ + "right", + "top", + "bottom" + ] + } + } + } + } + }, + "coloraxis": { + "type": "string", + "description": "Sets a reference to a shared color axis. References to these shared color axes are *coloraxis*, *coloraxis2*, *coloraxis3*, etc. Settings for these shared color axes are set in the layout, under `layout.coloraxis`, `layout.coloraxis2`, etc. Note that multiple color scales can be linked to the same color axis.", + "default": null + } + } + }, + "marker": { + "type": "object", + "properties": { + "symbol": { + "type": "string", + "description": "Sets the marker symbol type.", + "default": "circle", + "enum": [ + "circle", + "square", + "diamond", + "cross", + "x", + "triangle-up", + "triangle-down", + "pentagon", + "hexagon", + "octagon", + "star" + ] + }, + "size": { + "type": "number", + "description": "Sets the marker size (in px).", + "default": 8, + "minimum": 0 + }, + "sizeref": { + "type": "number", + "description": "Has an effect only if `marker.size` is set to a numerical array. Sets the scale factor used to determine the rendered size of marker points. Use with `sizemin` and `sizemode`.", + "default": 1 + }, + "sizemin": { + "type": "number", + "description": "Has an effect only if `marker.size` is set to a numerical array. Sets the minimum size (in px) of the rendered marker points.", + "default": 0, + "minimum": 0 + }, + "sizemode": { + "type": "string", + "description": "Has an effect only if `marker.size` is set to a numerical array. Sets the rule for which the data in `size` is converted to pixels.", + "default": "diameter", + "enum": [ + "diameter", + "area" + ] + }, + "opacity": { + "type": "number", + "description": "Sets the marker opacity. Note that the marker opacity for scatter3d traces must be a scalar value for performance reasons. To set a blending opacity value (i.e. which is not transparent), set *marker.color* to an rgba color and use its alpha channel.", + "minimum": 0, + "maximum": 1 + }, + "colorbar": { + "type": "object", + "properties": { + "thicknessmode": { + "type": "string", + "description": "Determines whether this color bar's thickness (i.e. the measure in the constant color direction) is set in units of plot *fraction* or in *pixels*. Use `thickness` to set the value.", + "default": "pixels", + "enum": [ + "fraction", + "pixels" + ] + }, + "thickness": { + "type": "number", + "description": "Sets the thickness of the color bar This measure excludes the size of the padding, ticks and labels.", + "default": 30, + "minimum": 0 + }, + "lenmode": { + "type": "string", + "description": "Determines whether this color bar's length (i.e. the measure in the color variation direction) is set in units of plot *fraction* or in *pixels. Use `len` to set the value.", + "default": "fraction", + "enum": [ + "fraction", + "pixels" + ] + }, + "len": { + "type": "number", + "description": "Sets the length of the color bar This measure excludes the padding of both ends. That is, the color bar length is this length minus the padding on both ends.", + "default": 1, + "minimum": 0 + }, + "x": { + "type": "number", + "description": "Sets the x position of the color bar (in plot fraction).", + "default": 1.02, + "minimum": -2, + "maximum": 3 + }, + "xanchor": { + "type": "string", + "description": "Sets this color bar's horizontal position anchor. This anchor binds the `x` position to the *left*, *center* or *right* of the color bar.", + "default": "left", + "enum": [ + "left", + "center", + "right" + ] + }, + "xpad": { + "type": "number", + "description": "Sets the amount of padding (in px) along the x direction.", + "default": 10, + "minimum": 0 + }, + "y": { + "type": "number", + "description": "Sets the y position of the color bar (in plot fraction).", + "default": 0.5, + "minimum": -2, + "maximum": 3 + }, + "yanchor": { + "type": "string", + "description": "Sets this color bar's vertical position anchor This anchor binds the `y` position to the *top*, *middle* or *bottom* of the color bar.", + "default": "middle", + "enum": [ + "top", + "middle", + "bottom" + ] + }, + "ypad": { + "type": "number", + "description": "Sets the amount of padding (in px) along the y direction.", + "default": 10, + "minimum": 0 + }, + "outlinecolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "outlinewidth": { + "type": "number", + "description": "Sets the width (in px) of the axis line.", + "default": 1, + "minimum": 0 + }, + "bordercolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "borderwidth": { + "type": "number", + "description": "Sets the width (in px) or the border enclosing this color bar.", + "default": 0, + "minimum": 0 + }, + "bgcolor": { + "type": "string", + "description": "Sets the color of padded area.", + "default": "rgba(0,0,0,0)" + }, + "tickmode": { + "type": "string", + "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).", + "enum": [ + "auto", + "linear", + "array" + ] + }, + "nticks": { + "type": "integer", + "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", + "default": 0, + "minimum": 0 + }, + "tick0": { + "type": "string", + "description": "Sets the placement of the first tick on this axis. Use with `dtick`. If the axis `type` is *log*, then you must take the log of your starting tick (e.g. to set the starting tick to 100, set the `tick0` to 2) except when `dtick`=*L* (see `dtick` for more info). If the axis `type` is *date*, it should be a date string, like date data. If the axis `type` is *category*, it should be a number, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "dtick": { + "type": "string", + "description": "Sets the step in-between ticks on this axis. Use with `tick0`. Must be a positive number, or special strings available to *log* and *date* axes. If the axis `type` is *log*, then ticks are set every 10^(n*dtick) where n is the tick number. For example, to set a tick mark at 1, 10, 100, 1000, ... set dtick to 1. To set tick marks at 1, 100, 10000, ... set dtick to 2. To set tick marks at 1, 5, 25, 125, 625, 3125, ... set dtick to log_10(5), or 0.69897000433. *log* has several special values; *L*, where `f` is a positive number, gives ticks linearly spaced in value (but not position). For example `tick0` = 0.1, `dtick` = *L0.5* will put ticks at 0.1, 0.6, 1.1, 1.6 etc. To show powers of 10 plus small digits between, use *D1* (all digits) or *D2* (only 2 and 5). `tick0` is ignored for *D1* and *D2*. If the axis `type` is *date*, then you must convert the time to milliseconds. For example, to set the interval between ticks to one day, set `dtick` to 86400000.0. *date* also has special values *M* gives ticks spaced by a number of months. `n` must be a positive integer. To set ticks on the 15th of every third month, set `tick0` to *2000-01-15* and `dtick` to *M3*. To set ticks every 4 years, set `dtick` to *M48*" + }, + "tickvals": { + "type": "string", + "description": "Sets the values at which ticks on this axis appear. Only has an effect if `tickmode` is set to *array*. Used with `ticktext`." + }, + "ticktext": { + "type": "string", + "description": "Sets the text displayed at the ticks position via `tickvals`. Only has an effect if `tickmode` is set to *array*. Used with `tickvals`." + }, + "ticks": { + "type": "string", + "description": "Determines whether ticks are drawn or not. If **, this axis' ticks are not drawn. If *outside* (*inside*), this axis' are drawn outside (inside) the axis lines.", + "default": "", + "enum": [ + "outside", + "inside", + "" + ] + }, + "ticklabelposition": { + "type": "string", + "description": "Determines where tick labels are drawn.", + "default": "outside", + "enum": [ + "outside", + "inside", + "outside top", + "inside top", + "outside bottom", + "inside bottom" + ] + }, + "ticklen": { + "type": "number", + "description": "Sets the tick length (in px).", + "default": 5, + "minimum": 0 + }, + "tickwidth": { + "type": "number", + "description": "Sets the tick width (in px).", + "default": 1, + "minimum": 0 + }, + "tickcolor": { + "type": "string", + "description": "Sets the tick color.", + "default": "#444" + }, + "showticklabels": { + "type": "boolean", + "description": "Determines whether or not the tick labels are drawn.", + "default": true + }, + "tickfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "tickangle": { + "type": "number", + "description": "Sets the angle of the tick labels with respect to the horizontal. For example, a `tickangle` of -90 draws the tick labels vertically.", + "default": "auto", + "minimum": -180, + "maximum": 180 + }, + "tickformat": { + "type": "string", + "description": "Sets the tick label formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "tickprefix": { + "type": "string", + "description": "Sets a tick label prefix.", + "default": "" + }, + "showtickprefix": { + "type": "string", + "description": "If *all*, all tick labels are displayed with a prefix. If *first*, only the first tick is displayed with a prefix. If *last*, only the last tick is displayed with a suffix. If *none*, tick prefixes are hidden.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "ticksuffix": { + "type": "string", + "description": "Sets a tick label suffix.", + "default": "" + }, + "showticksuffix": { + "type": "string", + "description": "Same as `showtickprefix` but for tick suffixes.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "separatethousands": { + "type": "boolean", + "description": "If \"true\", even 4-digit integers are separated", + "default": false + }, + "exponentformat": { + "type": "string", + "description": "Determines a formatting rule for the tick exponents. For example, consider the number 1,000,000,000. If *none*, it appears as 1,000,000,000. If *e*, 1e+9. If *E*, 1E+9. If *power*, 1x10^9 (with 9 in a super script). If *SI*, 1G. If *B*, 1B.", + "default": "B", + "enum": [ + "none", + "e", + "E", + "power", + "SI", + "B" + ] + }, + "minexponent": { + "type": "number", + "description": "Hide SI prefix for 10^n if |n| is below this number. This only has an effect when `tickformat` is *SI* or *B*.", + "default": 3, + "minimum": 0 + }, + "showexponent": { + "type": "string", + "description": "If *all*, all exponents are shown besides their significands. If *first*, only the exponent of the first tick is shown. If *last*, only the exponent of the last tick is shown. If *none*, no exponents appear.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "title": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Sets the title of the color bar. Note that before the existence of `title.text`, the title's contents used to be defined as the `title` attribute itself. This behavior has been deprecated." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "side": { + "type": "string", + "description": "Determines the location of color bar's title with respect to the color bar. Note that the title's location used to be set by the now deprecated `titleside` attribute.", + "default": "top", + "enum": [ + "right", + "top", + "bottom" + ] + } + } + } + } + }, + "line": { + "type": "object", + "properties": { + "width": { + "type": "number", + "description": "Sets the width (in px) of the lines bounding the marker points.", + "minimum": 0 + }, + "color": { + "type": "string", + "description": "Sets themarker.linecolor. It accepts either a specific color or an array of numbers that are mapped to the colorscale relative to the max and min values of the array or relative to `marker.line.cmin` and `marker.line.cmax` if set." + }, + "cauto": { + "type": "boolean", + "description": "Determines whether or not the color domain is computed with respect to the input data (here in `marker.line.color`) or the bounds set in `marker.line.cmin` and `marker.line.cmax` Has an effect only if in `marker.line.color`is set to a numerical array. Defaults to `false` when `marker.line.cmin` and `marker.line.cmax` are set by the user.", + "default": true + }, + "cmin": { + "type": "number", + "description": "Sets the lower bound of the color domain. Has an effect only if in `marker.line.color`is set to a numerical array. Value should have the same units as in `marker.line.color` and if set, `marker.line.cmax` must be set as well.", + "default": null + }, + "cmax": { + "type": "number", + "description": "Sets the upper bound of the color domain. Has an effect only if in `marker.line.color`is set to a numerical array. Value should have the same units as in `marker.line.color` and if set, `marker.line.cmin` must be set as well.", + "default": null + }, + "cmid": { + "type": "number", + "description": "Sets the mid-point of the color domain by scaling `marker.line.cmin` and/or `marker.line.cmax` to be equidistant to this point. Has an effect only if in `marker.line.color`is set to a numerical array. Value should have the same units as in `marker.line.color`. Has no effect when `marker.line.cauto` is `false`.", + "default": null + }, + "colorscale": { + "type": "string", + "description": "Sets the colorscale. Has an effect only if in `marker.line.color`is set to a numerical array. The colorscale must be an array containing arrays mapping a normalized value to an rgb, rgba, hex, hsl, hsv, or named color string. At minimum, a mapping for the lowest (0) and highest (1) values are required. For example, `[[0, 'rgb(0,0,255)'], [1, 'rgb(255,0,0)']]`. To control the bounds of the colorscale in color space, use`marker.line.cmin` and `marker.line.cmax`. Alternatively, `colorscale` may be a palette name string of the following list: Greys,YlGnBu,Greens,YlOrRd,Bluered,RdBu,Reds,Blues,Picnic,Rainbow,Portland,Jet,Hot,Blackbody,Earth,Electric,Viridis,Cividis.", + "default": null + }, + "autocolorscale": { + "type": "boolean", + "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.line.colorscale`. Has an effect only if in `marker.line.color`is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.", + "default": true + }, + "reversescale": { + "type": "boolean", + "description": "Reverses the color mapping if true. Has an effect only if in `marker.line.color`is set to a numerical array. If true, `marker.line.cmin` will correspond to the last color in the array and `marker.line.cmax` will correspond to the first color.", + "default": false + }, + "coloraxis": { + "type": "string", + "description": "Sets a reference to a shared color axis. References to these shared color axes are *coloraxis*, *coloraxis2*, *coloraxis3*, etc. Settings for these shared color axes are set in the layout, under `layout.coloraxis`, `layout.coloraxis2`, etc. Note that multiple color scales can be linked to the same color axis.", + "default": null + } + } + }, + "color": { + "type": "string", + "description": "Sets themarkercolor. It accepts either a specific color or an array of numbers that are mapped to the colorscale relative to the max and min values of the array or relative to `marker.cmin` and `marker.cmax` if set." + }, + "cauto": { + "type": "boolean", + "description": "Determines whether or not the color domain is computed with respect to the input data (here in `marker.color`) or the bounds set in `marker.cmin` and `marker.cmax` Has an effect only if in `marker.color`is set to a numerical array. Defaults to `false` when `marker.cmin` and `marker.cmax` are set by the user.", + "default": true + }, + "cmin": { + "type": "number", + "description": "Sets the lower bound of the color domain. Has an effect only if in `marker.color`is set to a numerical array. Value should have the same units as in `marker.color` and if set, `marker.cmax` must be set as well.", + "default": null + }, + "cmax": { + "type": "number", + "description": "Sets the upper bound of the color domain. Has an effect only if in `marker.color`is set to a numerical array. Value should have the same units as in `marker.color` and if set, `marker.cmin` must be set as well.", + "default": null + }, + "cmid": { + "type": "number", + "description": "Sets the mid-point of the color domain by scaling `marker.cmin` and/or `marker.cmax` to be equidistant to this point. Has an effect only if in `marker.color`is set to a numerical array. Value should have the same units as in `marker.color`. Has no effect when `marker.cauto` is `false`.", + "default": null + }, + "colorscale": { + "type": "string", + "description": "Sets the colorscale. Has an effect only if in `marker.color`is set to a numerical array. The colorscale must be an array containing arrays mapping a normalized value to an rgb, rgba, hex, hsl, hsv, or named color string. At minimum, a mapping for the lowest (0) and highest (1) values are required. For example, `[[0, 'rgb(0,0,255)'], [1, 'rgb(255,0,0)']]`. To control the bounds of the colorscale in color space, use`marker.cmin` and `marker.cmax`. Alternatively, `colorscale` may be a palette name string of the following list: Greys,YlGnBu,Greens,YlOrRd,Bluered,RdBu,Reds,Blues,Picnic,Rainbow,Portland,Jet,Hot,Blackbody,Earth,Electric,Viridis,Cividis.", + "default": null + }, + "autocolorscale": { + "type": "boolean", + "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.colorscale`. Has an effect only if in `marker.color`is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.", + "default": true + }, + "reversescale": { + "type": "boolean", + "description": "Reverses the color mapping if true. Has an effect only if in `marker.color`is set to a numerical array. If true, `marker.cmin` will correspond to the last color in the array and `marker.cmax` will correspond to the first color.", + "default": false + }, + "showscale": { + "type": "boolean", + "description": "Determines whether or not a colorbar is displayed for this trace. Has an effect only if in `marker.color`is set to a numerical array.", + "default": false + }, + "coloraxis": { + "type": "string", + "description": "Sets a reference to a shared color axis. References to these shared color axes are *coloraxis*, *coloraxis2*, *coloraxis3*, etc. Settings for these shared color axes are set in the layout, under `layout.coloraxis`, `layout.coloraxis2`, etc. Note that multiple color scales can be linked to the same color axis.", + "default": null + } + } + }, + "textposition": { + "type": "string", + "description": "Sets the positions of the `text` elements with respects to the (x,y) coordinates.", + "default": "top center", + "enum": [ + "top left", + "top center", + "top right", + "middle left", + "middle center", + "middle right", + "bottom left", + "bottom center", + "bottom right" + ] + }, + "textfont": { + "type": "object", + "properties": { + "color": { + "type": "string" + }, + "size": { + "type": "number", + "minimum": 1 + }, + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + } + } + }, + "hoverinfo": { + "type": "string", + "description": "Determines which trace information appear on hover. If `none` or `skip` are set, no information is displayed upon hovering. But, if `none` is set, click and hover events are still fired.", + "default": "all", + "enum": [ + "name", + "text", + "text+name", + "z", + "z+name", + "z+text", + "z+text+name", + "y", + "y+name", + "y+text", + "y+text+name", + "y+z", + "y+z+name", + "y+z+text", + "y+z+text+name", + "x", + "x+name", + "x+text", + "x+text+name", + "x+z", + "x+z+name", + "x+z+text", + "x+z+text+name", + "x+y", + "x+y+name", + "x+y+text", + "x+y+text+name", + "x+y+z", + "x+y+z+name", + "x+y+z+text", + "x+y+z+text+name", + "all", + "none", + "skip" + ] + }, + "error_x": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Determines whether or not this set of error bars is visible." + }, + "type": { + "type": "string", + "description": "Determines the rule used to generate the error bars. If *constant`, the bar lengths are of a constant value. Set this constant in `value`. If *percent*, the bar lengths correspond to a percentage of underlying data. Set this percentage in `value`. If *sqrt*, the bar lengths correspond to the square of the underlying data. If *data*, the bar lengths are set with data set `array`.", + "enum": [ + "percent", + "constant", + "sqrt", + "data" + ] + }, + "symmetric": { + "type": "boolean", + "description": "Determines whether or not the error bars have the same length in both direction (top/bottom for vertical bars, left/right for horizontal bars." + }, + "array": { + "type": "string", + "description": "Sets the data corresponding the length of each error bar. Values are plotted relative to the underlying data." + }, + "arrayminus": { + "type": "string", + "description": "Sets the data corresponding the length of each error bar in the bottom (left) direction for vertical (horizontal) bars Values are plotted relative to the underlying data." + }, + "value": { + "type": "number", + "description": "Sets the value of either the percentage (if `type` is set to *percent*) or the constant (if `type` is set to *constant*) corresponding to the lengths of the error bars.", + "default": 10, + "minimum": 0 + }, + "valueminus": { + "type": "number", + "description": "Sets the value of either the percentage (if `type` is set to *percent*) or the constant (if `type` is set to *constant*) corresponding to the lengths of the error bars in the bottom (left) direction for vertical (horizontal) bars", + "default": 10, + "minimum": 0 + }, + "traceref": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "tracerefminus": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "copy_zstyle": { + "type": "boolean" + }, + "color": { + "type": "string", + "description": "Sets the stoke color of the error bars." + }, + "thickness": { + "type": "number", + "description": "Sets the thickness (in px) of the error bars.", + "default": 2, + "minimum": 0 + }, + "width": { + "type": "number", + "description": "Sets the width (in px) of the cross-bar at both ends of the error bars.", + "minimum": 0 + } + } + }, + "error_y": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Determines whether or not this set of error bars is visible." + }, + "type": { + "type": "string", + "description": "Determines the rule used to generate the error bars. If *constant`, the bar lengths are of a constant value. Set this constant in `value`. If *percent*, the bar lengths correspond to a percentage of underlying data. Set this percentage in `value`. If *sqrt*, the bar lengths correspond to the square of the underlying data. If *data*, the bar lengths are set with data set `array`.", + "enum": [ + "percent", + "constant", + "sqrt", + "data" + ] + }, + "symmetric": { + "type": "boolean", + "description": "Determines whether or not the error bars have the same length in both direction (top/bottom for vertical bars, left/right for horizontal bars." + }, + "array": { + "type": "string", + "description": "Sets the data corresponding the length of each error bar. Values are plotted relative to the underlying data." + }, + "arrayminus": { + "type": "string", + "description": "Sets the data corresponding the length of each error bar in the bottom (left) direction for vertical (horizontal) bars Values are plotted relative to the underlying data." + }, + "value": { + "type": "number", + "description": "Sets the value of either the percentage (if `type` is set to *percent*) or the constant (if `type` is set to *constant*) corresponding to the lengths of the error bars.", + "default": 10, + "minimum": 0 + }, + "valueminus": { + "type": "number", + "description": "Sets the value of either the percentage (if `type` is set to *percent*) or the constant (if `type` is set to *constant*) corresponding to the lengths of the error bars in the bottom (left) direction for vertical (horizontal) bars", + "default": 10, + "minimum": 0 + }, + "traceref": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "tracerefminus": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "copy_zstyle": { + "type": "boolean" + }, + "color": { + "type": "string", + "description": "Sets the stoke color of the error bars." + }, + "thickness": { + "type": "number", + "description": "Sets the thickness (in px) of the error bars.", + "default": 2, + "minimum": 0 + }, + "width": { + "type": "number", + "description": "Sets the width (in px) of the cross-bar at both ends of the error bars.", + "minimum": 0 + } + } + }, + "error_z": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Determines whether or not this set of error bars is visible." + }, + "type": { + "type": "string", + "description": "Determines the rule used to generate the error bars. If *constant`, the bar lengths are of a constant value. Set this constant in `value`. If *percent*, the bar lengths correspond to a percentage of underlying data. Set this percentage in `value`. If *sqrt*, the bar lengths correspond to the square of the underlying data. If *data*, the bar lengths are set with data set `array`.", + "enum": [ + "percent", + "constant", + "sqrt", + "data" + ] + }, + "symmetric": { + "type": "boolean", + "description": "Determines whether or not the error bars have the same length in both direction (top/bottom for vertical bars, left/right for horizontal bars." + }, + "array": { + "type": "string", + "description": "Sets the data corresponding the length of each error bar. Values are plotted relative to the underlying data." + }, + "arrayminus": { + "type": "string", + "description": "Sets the data corresponding the length of each error bar in the bottom (left) direction for vertical (horizontal) bars Values are plotted relative to the underlying data." + }, + "value": { + "type": "number", + "description": "Sets the value of either the percentage (if `type` is set to *percent*) or the constant (if `type` is set to *constant*) corresponding to the lengths of the error bars.", + "default": 10, + "minimum": 0 + }, + "valueminus": { + "type": "number", + "description": "Sets the value of either the percentage (if `type` is set to *percent*) or the constant (if `type` is set to *constant*) corresponding to the lengths of the error bars in the bottom (left) direction for vertical (horizontal) bars", + "default": 10, + "minimum": 0 + }, + "traceref": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "tracerefminus": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "color": { + "type": "string", + "description": "Sets the stoke color of the error bars." + }, + "thickness": { + "type": "number", + "description": "Sets the thickness (in px) of the error bars.", + "default": 2, + "minimum": 0 + }, + "width": { + "type": "number", + "description": "Sets the width (in px) of the cross-bar at both ends of the error bars.", + "minimum": 0 + } + } + }, + "xcalendar": { + "type": "string", + "description": "Sets the calendar system to use with `x` date data.", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + }, + "ycalendar": { + "type": "string", + "description": "Sets the calendar system to use with `y` date data.", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + }, + "zcalendar": { + "type": "string", + "description": "Sets the calendar system to use with `z` date data.", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + }, + "scene": { + "type": "string", + "description": "Sets a reference between this trace's 3D coordinate system and a 3D scene. If *scene* (the default value), the (x,y,z) coordinates refer to `layout.scene`. If *scene2*, the (x,y,z) coordinates refer to `layout.scene2`, and so on.", + "default": "scene" + } + } +} \ No newline at end of file diff --git a/escalation/plotly_api/scatter_plotly_schema.json b/escalation/plotly_api/scatter_plotly_schema.json new file mode 100644 index 0000000..368e675 --- /dev/null +++ b/escalation/plotly_api/scatter_plotly_schema.json @@ -0,0 +1,1133 @@ +{ + "attributes": { + "visible": { + "type": "string", + "description": "Determines whether or not this trace is visible. If *legendonly*, the trace is not drawn, but can appear as a legend item (provided that the legend itself is visible).", + "default": true, + "enum": [ + true, + false, + "legendonly" + ] + }, + "showlegend": { + "type": "boolean", + "description": "Determines whether or not an item corresponding to this trace is shown in the legend.", + "default": true + }, + "legendgroup": { + "type": "string", + "description": "Sets the legend group for this trace. Traces part of the same legend group hide/show at the same time when toggling legend items.", + "default": "" + }, + "opacity": { + "type": "number", + "description": "Sets the opacity of the trace.", + "default": 1, + "minimum": 0, + "maximum": 1 + }, + "name": { + "type": "string", + "description": "Sets the trace name. The trace name appear as the legend item and on hover." + }, + "customdata": { + "type": "string", + "description": "Assigns extra data each datum. This may be useful when listening to hover, click and selection events. Note that, *scatter* traces also appends customdata items in the markers DOM elements" + }, + "meta": { + "type": "string", + "description": "Assigns extra meta information associated with this trace that can be used in various text attributes. Attributes such as trace `name`, graph, axis and colorbar `title.text`, annotation `text` `rangeselector`, `updatemenues` and `sliders` `label` text all support `meta`. To access the trace `meta` values in an attribute in the same trace, simply use `%{meta[i]}` where `i` is the index or key of the `meta` item in question. To access trace `meta` in layout attributes, use `%{data[n[.meta[i]}` where `i` is the index or key of the `meta` and `n` is the trace index." + }, + "selectedpoints": { + "type": "string", + "description": "Array containing integer indices of selected points. Has an effect only for traces that support selections. Note that an empty array means an empty selection where the `unselected` are turned on for all points, whereas, any other non-array values means no selection all where the `selected` and `unselected` styles have no effect." + }, + "hoverinfo": { + "type": "string", + "description": "Determines which trace information appear on hover. If `none` or `skip` are set, no information is displayed upon hovering. But, if `none` is set, click and hover events are still fired.", + "default": "all", + "enum": [ + "name", + "text", + "text+name", + "z", + "z+name", + "z+text", + "z+text+name", + "y", + "y+name", + "y+text", + "y+text+name", + "y+z", + "y+z+name", + "y+z+text", + "y+z+text+name", + "x", + "x+name", + "x+text", + "x+text+name", + "x+z", + "x+z+name", + "x+z+text", + "x+z+text+name", + "x+y", + "x+y+name", + "x+y+text", + "x+y+text+name", + "x+y+z", + "x+y+z+name", + "x+y+z+text", + "x+y+z+text+name", + "all", + "none", + "skip" + ] + }, + "hoverlabel": { + "type": "object", + "properties": { + "bgcolor": { + "type": "string", + "description": "Sets the background color of the hover labels for this trace" + }, + "bordercolor": { + "type": "string", + "description": "Sets the border color of the hover labels for this trace." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "align": { + "type": "string", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "default": "auto", + "enum": [ + "left", + "right", + "auto" + ] + }, + "namelength": { + "type": "integer", + "description": "Sets the default length (in number of characters) of the trace name in the hover labels for all traces. -1 shows the whole name regardless of length. 0-3 shows the first 0-3 characters, and an integer >3 will show the whole name if it is less than that many characters, but if it is longer, will truncate to `namelength - 3` characters and add an ellipsis.", + "default": 15, + "minimum": -1 + } + } + }, + "uirevision": { + "type": "string", + "description": "Controls persistence of some user-driven changes to the trace: `constraintrange` in `parcoords` traces, as well as some `editable: true` modifications such as `name` and `colorbar.title`. Defaults to `layout.uirevision`. Note that other user-driven trace attribute changes are controlled by `layout` attributes: `trace.visible` is controlled by `layout.legend.uirevision`, `selectedpoints` is controlled by `layout.selectionrevision`, and `colorbar.(x|y)` (accessible with `config: {editable: true}`) is controlled by `layout.editrevision`. Trace changes are tracked by `uid`, which only falls back on trace index if no `uid` is provided. So if your app can add/remove traces before the end of the `data` array, such that the same trace has a different index, you can still preserve user-driven changes if you give each trace a `uid` that stays with it as it moves." + }, + "x": { + "type": "string", + "description": "Sets the x coordinates." + }, + "x0": { + "type": "string", + "description": "Alternate to `x`. Builds a linear space of x coordinates. Use with `dx` where `x0` is the starting coordinate and `dx` the step.", + "default": 0 + }, + "dx": { + "type": "number", + "description": "Sets the x coordinate step. See `x0` for more info.", + "default": 1 + }, + "y": { + "type": "string", + "description": "Sets the y coordinates." + }, + "y0": { + "type": "string", + "description": "Alternate to `y`. Builds a linear space of y coordinates. Use with `dy` where `y0` is the starting coordinate and `dy` the step.", + "default": 0 + }, + "dy": { + "type": "number", + "description": "Sets the y coordinate step. See `y0` for more info.", + "default": 1 + }, + "xperiod": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the period positioning in milliseconds or *M* on the x axis. Special values in the form of *M* could be used to declare the number of months. In this case `n` must be a positive integer.", + "default": 0 + }, + "yperiod": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the period positioning in milliseconds or *M* on the y axis. Special values in the form of *M* could be used to declare the number of months. In this case `n` must be a positive integer.", + "default": 0 + }, + "xperiod0": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the base for period positioning in milliseconds or date string on the x0 axis. When `x0period` is round number of weeks, the `x0period0` by default would be on a Sunday i.e. 2000-01-02, otherwise it would be at 2000-01-01." + }, + "yperiod0": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the base for period positioning in milliseconds or date string on the y0 axis. When `y0period` is round number of weeks, the `y0period0` by default would be on a Sunday i.e. 2000-01-02, otherwise it would be at 2000-01-01." + }, + "xperiodalignment": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the alignment of data points on the x axis.", + "default": "middle", + "enum": [ + "start", + "middle", + "end" + ] + }, + "yperiodalignment": { + "type": "string", + "description": "Only relevant when the axis `type` is *date*. Sets the alignment of data points on the y axis.", + "default": "middle", + "enum": [ + "start", + "middle", + "end" + ] + }, + "stackgroup": { + "type": "string", + "description": "Set several scatter traces (on the same subplot) to the same stackgroup in order to add their y values (or their x values if `orientation` is *h*). If blank or omitted this trace will not be stacked. Stacking also turns `fill` on by default, using *tonexty* (*tonextx*) if `orientation` is *h* (*v*) and sets the default `mode` to *lines* irrespective of point count. You can only stack on a numeric (linear or log) axis. Traces in a `stackgroup` will only fill to (or be filled to) other traces in the same group. With multiple `stackgroup`s or some traces stacked and some not, if fill-linked traces are not already consecutive, the later ones will be pushed down in the drawing order.", + "default": "" + }, + "orientation": { + "type": "string", + "description": "Only relevant when `stackgroup` is used, and only the first `orientation` found in the `stackgroup` will be used - including if `visible` is *legendonly* but not if it is `false`. Sets the stacking direction. With *v* (*h*), the y (x) values of subsequent traces are added. Also affects the default value of `fill`.", + "enum": [ + "v", + "h" + ] + }, + "groupnorm": { + "type": "string", + "description": "Only relevant when `stackgroup` is used, and only the first `groupnorm` found in the `stackgroup` will be used - including if `visible` is *legendonly* but not if it is `false`. Sets the normalization for the sum of this `stackgroup`. With *fraction*, the value of each trace at each location is divided by the sum of all trace values at that location. *percent* is the same but multiplied by 100 to show percentages. If there are multiple subplots, or multiple `stackgroup`s on one subplot, each will be normalized within its own set.", + "default": "", + "enum": [ + "", + "fraction", + "percent" + ] + }, + "stackgaps": { + "type": "string", + "description": "Only relevant when `stackgroup` is used, and only the first `stackgaps` found in the `stackgroup` will be used - including if `visible` is *legendonly* but not if it is `false`. Determines how we handle locations at which other traces in this group have data but this one does not. With *infer zero* we insert a zero at these locations. With *interpolate* we linearly interpolate between existing values, and extrapolate a constant beyond the existing values.", + "default": "infer zero", + "enum": [ + "infer zero", + "interpolate" + ] + }, + "text": { + "type": "string", + "description": "Sets text elements associated with each (x,y) pair. If a single string, the same string appears over all the data points. If an array of string, the items are mapped in order to the this trace's (x,y) coordinates. If trace `hoverinfo` contains a *text* flag and *hovertext* is not set, these elements will be seen in the hover labels.", + "default": "" + }, + "texttemplate": { + "type": "string", + "description": "Template string used for rendering the information text that appear on points. Note that this will override `textinfo`. Variables are inserted using %{variable}, for example \"y: %{y}\". Numbers are formatted using d3-format's syntax %{variable:d3-format}, for example \"Price: %{y:$.2f}\". https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format for details on the formatting syntax. Dates are formatted using d3-time-format's syntax %{variable|d3-time-format}, for example \"Day: %{2019-01-01|%A}\". https://github.com/d3/d3-time-format#locale_format for details on the date formatting syntax. Every attributes that can be specified per-point (the ones that are `arrayOk: true`) are available. ", + "default": "" + }, + "mode": { + "type": "string", + "description": "Determines the drawing mode for this scatter trace. If the provided `mode` includes *text* then the `text` elements appear at the coordinates. Otherwise, the `text` elements appear on hover. If there are less than 20 points and the trace is not stacked then the default is *lines+markers*. Otherwise, *lines*.", + "enum": [ + "text", + "markers", + "markers+text", + "lines", + "lines+text", + "lines+markers", + "lines+markers+text", + "none" + ] + }, + "hoveron": { + "type": "string", + "description": "Do the hover effects highlight individual points (markers or line points) or do they highlight filled regions? If the fill is *toself* or *tonext* and there are no markers or text, then the default is *fills*, otherwise it is *points*.", + "enum": [ + "fills", + "points", + "points+fills" + ] + }, + "line": { + "type": "object", + "properties": { + "color": { + "type": "string", + "description": "Sets the line color." + }, + "width": { + "type": "number", + "description": "Sets the line width (in px).", + "default": 2, + "minimum": 0 + }, + "shape": { + "type": "string", + "description": "Determines the line shape. With *spline* the lines are drawn using spline interpolation. The other available values correspond to step-wise line shapes.", + "default": "linear", + "enum": [ + "linear", + "spline", + "hv", + "vh", + "hvh", + "vhv" + ] + }, + "smoothing": { + "type": "number", + "description": "Has an effect only if `shape` is set to *spline* Sets the amount of smoothing. *0* corresponds to no smoothing (equivalent to a *linear* shape).", + "default": 1, + "minimum": 0, + "maximum": 1.3 + }, + "dash": { + "type": "string", + "description": "Sets the dash style of lines. Set to a dash type string (*solid*, *dot*, *dash*, *longdash*, *dashdot*, or *longdashdot*) or a dash length list in px (eg *5px,10px,2px,2px*).", + "default": "solid" + }, + "simplify": { + "type": "boolean", + "description": "Simplifies lines by removing nearly-collinear points. When transitioning lines, it may be desirable to disable this so that the number of points along the resulting SVG path is unaffected.", + "default": true + } + } + }, + "connectgaps": { + "type": "boolean", + "description": "Determines whether or not gaps (i.e. {nan} or missing values) in the provided data arrays are connected.", + "default": false + }, + "cliponaxis": { + "type": "boolean", + "description": "Determines whether or not markers and text nodes are clipped about the subplot axes. To show markers and text nodes above axis lines and tick labels, make sure to set `xaxis.layer` and `yaxis.layer` to *below traces*.", + "default": true + }, + "fill": { + "type": "string", + "description": "Sets the area to fill with a solid color. Defaults to *none* unless this trace is stacked, then it gets *tonexty* (*tonextx*) if `orientation` is *v* (*h*) Use with `fillcolor` if not *none*. *tozerox* and *tozeroy* fill to x=0 and y=0 respectively. *tonextx* and *tonexty* fill between the endpoints of this trace and the endpoints of the trace before it, connecting those endpoints with straight lines (to make a stacked area graph); if there is no trace before it, they behave like *tozerox* and *tozeroy*. *toself* connects the endpoints of the trace (or each segment of the trace if it has gaps) into a closed shape. *tonext* fills the space between two traces if one completely encloses the other (eg consecutive contour lines), and behaves like *toself* if there is no trace before it. *tonext* should not be used if one trace does not enclose the other. Traces in a `stackgroup` will only fill to (or be filled to) other traces in the same group. With multiple `stackgroup`s or some traces stacked and some not, if fill-linked traces are not already consecutive, the later ones will be pushed down in the drawing order.", + "enum": [ + "none", + "tozeroy", + "tozerox", + "tonexty", + "tonextx", + "toself", + "tonext" + ] + }, + "fillcolor": { + "type": "string", + "description": "Sets the fill color. Defaults to a half-transparent variant of the line color, marker color, or marker line color, whichever is available." + }, + "marker": { + "type": "object", + "properties": { + "symbol": { + "type": "string", + "description": "Sets the marker symbol type. Adding 100 is equivalent to appending *-open* to a symbol name. Adding 200 is equivalent to appending *-dot* to a symbol name. Adding 300 is equivalent to appending *-open-dot* or *dot-open* to a symbol name.", + "default": "circle", + "enum": [ + "circle", + "square", + "diamond", + "cross", + "x", + "triangle-up", + "triangle-down", + "pentagon", + "hexagon", + "octagon", + "star" + ] + }, + "opacity": { + "type": "number", + "description": "Sets the marker opacity.", + "minimum": 0, + "maximum": 1 + }, + "size": { + "type": "number", + "description": "Sets the marker size (in px).", + "default": 6, + "minimum": 0 + }, + "maxdisplayed": { + "type": "number", + "description": "Sets a maximum number of points to be drawn on the graph. *0* corresponds to no limit.", + "default": 0, + "minimum": 0 + }, + "sizeref": { + "type": "number", + "description": "Has an effect only if `marker.size` is set to a numerical array. Sets the scale factor used to determine the rendered size of marker points. Use with `sizemin` and `sizemode`.", + "default": 1 + }, + "sizemin": { + "type": "number", + "description": "Has an effect only if `marker.size` is set to a numerical array. Sets the minimum size (in px) of the rendered marker points.", + "default": 0, + "minimum": 0 + }, + "sizemode": { + "type": "string", + "description": "Has an effect only if `marker.size` is set to a numerical array. Sets the rule for which the data in `size` is converted to pixels.", + "default": "diameter", + "enum": [ + "diameter", + "area" + ] + }, + "line": { + "type": "object", + "properties": { + "width": { + "type": "number", + "description": "Sets the width (in px) of the lines bounding the marker points.", + "minimum": 0 + }, + "color": { + "type": "string", + "description": "Sets themarker.linecolor. It accepts either a specific color or an array of numbers that are mapped to the colorscale relative to the max and min values of the array or relative to `marker.line.cmin` and `marker.line.cmax` if set." + }, + "cauto": { + "type": "boolean", + "description": "Determines whether or not the color domain is computed with respect to the input data (here in `marker.line.color`) or the bounds set in `marker.line.cmin` and `marker.line.cmax` Has an effect only if in `marker.line.color`is set to a numerical array. Defaults to `false` when `marker.line.cmin` and `marker.line.cmax` are set by the user.", + "default": true + }, + "cmin": { + "type": "number", + "description": "Sets the lower bound of the color domain. Has an effect only if in `marker.line.color`is set to a numerical array. Value should have the same units as in `marker.line.color` and if set, `marker.line.cmax` must be set as well.", + "default": null + }, + "cmax": { + "type": "number", + "description": "Sets the upper bound of the color domain. Has an effect only if in `marker.line.color`is set to a numerical array. Value should have the same units as in `marker.line.color` and if set, `marker.line.cmin` must be set as well.", + "default": null + }, + "cmid": { + "type": "number", + "description": "Sets the mid-point of the color domain by scaling `marker.line.cmin` and/or `marker.line.cmax` to be equidistant to this point. Has an effect only if in `marker.line.color`is set to a numerical array. Value should have the same units as in `marker.line.color`. Has no effect when `marker.line.cauto` is `false`.", + "default": null + }, + "colorscale": { + "type": "string", + "description": "Sets the colorscale. Has an effect only if in `marker.line.color`is set to a numerical array. The colorscale must be an array containing arrays mapping a normalized value to an rgb, rgba, hex, hsl, hsv, or named color string. At minimum, a mapping for the lowest (0) and highest (1) values are required. For example, `[[0, 'rgb(0,0,255)'], [1, 'rgb(255,0,0)']]`. To control the bounds of the colorscale in color space, use`marker.line.cmin` and `marker.line.cmax`. Alternatively, `colorscale` may be a palette name string of the following list: Greys,YlGnBu,Greens,YlOrRd,Bluered,RdBu,Reds,Blues,Picnic,Rainbow,Portland,Jet,Hot,Blackbody,Earth,Electric,Viridis,Cividis.", + "default": null + }, + "autocolorscale": { + "type": "boolean", + "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.line.colorscale`. Has an effect only if in `marker.line.color`is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.", + "default": true + }, + "reversescale": { + "type": "boolean", + "description": "Reverses the color mapping if true. Has an effect only if in `marker.line.color`is set to a numerical array. If true, `marker.line.cmin` will correspond to the last color in the array and `marker.line.cmax` will correspond to the first color.", + "default": false + }, + "coloraxis": { + "type": "string", + "description": "Sets a reference to a shared color axis. References to these shared color axes are *coloraxis*, *coloraxis2*, *coloraxis3*, etc. Settings for these shared color axes are set in the layout, under `layout.coloraxis`, `layout.coloraxis2`, etc. Note that multiple color scales can be linked to the same color axis.", + "default": null + } + } + }, + "gradient": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Sets the type of gradient used to fill the markers", + "default": "none", + "enum": [ + "radial", + "horizontal", + "vertical", + "none" + ] + }, + "color": { + "type": "string", + "description": "Sets the final color of the gradient fill: the center color for radial, the right for horizontal, or the bottom for vertical." + } + } + }, + "color": { + "type": "string", + "description": "Sets themarkercolor. It accepts either a specific color or an array of numbers that are mapped to the colorscale relative to the max and min values of the array or relative to `marker.cmin` and `marker.cmax` if set." + }, + "cauto": { + "type": "boolean", + "description": "Determines whether or not the color domain is computed with respect to the input data (here in `marker.color`) or the bounds set in `marker.cmin` and `marker.cmax` Has an effect only if in `marker.color`is set to a numerical array. Defaults to `false` when `marker.cmin` and `marker.cmax` are set by the user.", + "default": true + }, + "cmin": { + "type": "number", + "description": "Sets the lower bound of the color domain. Has an effect only if in `marker.color`is set to a numerical array. Value should have the same units as in `marker.color` and if set, `marker.cmax` must be set as well.", + "default": null + }, + "cmax": { + "type": "number", + "description": "Sets the upper bound of the color domain. Has an effect only if in `marker.color`is set to a numerical array. Value should have the same units as in `marker.color` and if set, `marker.cmin` must be set as well.", + "default": null + }, + "cmid": { + "type": "number", + "description": "Sets the mid-point of the color domain by scaling `marker.cmin` and/or `marker.cmax` to be equidistant to this point. Has an effect only if in `marker.color`is set to a numerical array. Value should have the same units as in `marker.color`. Has no effect when `marker.cauto` is `false`.", + "default": null + }, + "colorscale": { + "type": "string", + "description": "Sets the colorscale. Has an effect only if in `marker.color`is set to a numerical array. The colorscale must be an array containing arrays mapping a normalized value to an rgb, rgba, hex, hsl, hsv, or named color string. At minimum, a mapping for the lowest (0) and highest (1) values are required. For example, `[[0, 'rgb(0,0,255)'], [1, 'rgb(255,0,0)']]`. To control the bounds of the colorscale in color space, use`marker.cmin` and `marker.cmax`. Alternatively, `colorscale` may be a palette name string of the following list: Greys,YlGnBu,Greens,YlOrRd,Bluered,RdBu,Reds,Blues,Picnic,Rainbow,Portland,Jet,Hot,Blackbody,Earth,Electric,Viridis,Cividis.", + "default": null + }, + "autocolorscale": { + "type": "boolean", + "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.colorscale`. Has an effect only if in `marker.color`is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.", + "default": true + }, + "reversescale": { + "type": "boolean", + "description": "Reverses the color mapping if true. Has an effect only if in `marker.color`is set to a numerical array. If true, `marker.cmin` will correspond to the last color in the array and `marker.cmax` will correspond to the first color.", + "default": false + }, + "showscale": { + "type": "boolean", + "description": "Determines whether or not a colorbar is displayed for this trace. Has an effect only if in `marker.color`is set to a numerical array.", + "default": false + }, + "colorbar": { + "type": "object", + "properties": { + "thicknessmode": { + "type": "string", + "description": "Determines whether this color bar's thickness (i.e. the measure in the constant color direction) is set in units of plot *fraction* or in *pixels*. Use `thickness` to set the value.", + "default": "pixels", + "enum": [ + "fraction", + "pixels" + ] + }, + "thickness": { + "type": "number", + "description": "Sets the thickness of the color bar This measure excludes the size of the padding, ticks and labels.", + "default": 30, + "minimum": 0 + }, + "lenmode": { + "type": "string", + "description": "Determines whether this color bar's length (i.e. the measure in the color variation direction) is set in units of plot *fraction* or in *pixels. Use `len` to set the value.", + "default": "fraction", + "enum": [ + "fraction", + "pixels" + ] + }, + "len": { + "type": "number", + "description": "Sets the length of the color bar This measure excludes the padding of both ends. That is, the color bar length is this length minus the padding on both ends.", + "default": 1, + "minimum": 0 + }, + "x": { + "type": "number", + "description": "Sets the x position of the color bar (in plot fraction).", + "default": 1.02, + "minimum": -2, + "maximum": 3 + }, + "xanchor": { + "type": "string", + "description": "Sets this color bar's horizontal position anchor. This anchor binds the `x` position to the *left*, *center* or *right* of the color bar.", + "default": "left", + "enum": [ + "left", + "center", + "right" + ] + }, + "xpad": { + "type": "number", + "description": "Sets the amount of padding (in px) along the x direction.", + "default": 10, + "minimum": 0 + }, + "y": { + "type": "number", + "description": "Sets the y position of the color bar (in plot fraction).", + "default": 0.5, + "minimum": -2, + "maximum": 3 + }, + "yanchor": { + "type": "string", + "description": "Sets this color bar's vertical position anchor This anchor binds the `y` position to the *top*, *middle* or *bottom* of the color bar.", + "default": "middle", + "enum": [ + "top", + "middle", + "bottom" + ] + }, + "ypad": { + "type": "number", + "description": "Sets the amount of padding (in px) along the y direction.", + "default": 10, + "minimum": 0 + }, + "outlinecolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "outlinewidth": { + "type": "number", + "description": "Sets the width (in px) of the axis line.", + "default": 1, + "minimum": 0 + }, + "bordercolor": { + "type": "string", + "description": "Sets the axis line color.", + "default": "#444" + }, + "borderwidth": { + "type": "number", + "description": "Sets the width (in px) or the border enclosing this color bar.", + "default": 0, + "minimum": 0 + }, + "bgcolor": { + "type": "string", + "description": "Sets the color of padded area.", + "default": "rgba(0,0,0,0)" + }, + "tickmode": { + "type": "string", + "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).", + "enum": [ + "auto", + "linear", + "array" + ] + }, + "nticks": { + "type": "integer", + "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", + "default": 0, + "minimum": 0 + }, + "tick0": { + "type": "string", + "description": "Sets the placement of the first tick on this axis. Use with `dtick`. If the axis `type` is *log*, then you must take the log of your starting tick (e.g. to set the starting tick to 100, set the `tick0` to 2) except when `dtick`=*L* (see `dtick` for more info). If the axis `type` is *date*, it should be a date string, like date data. If the axis `type` is *category*, it should be a number, using the scale where each category is assigned a serial number from zero in the order it appears." + }, + "dtick": { + "type": "string", + "description": "Sets the step in-between ticks on this axis. Use with `tick0`. Must be a positive number, or special strings available to *log* and *date* axes. If the axis `type` is *log*, then ticks are set every 10^(n*dtick) where n is the tick number. For example, to set a tick mark at 1, 10, 100, 1000, ... set dtick to 1. To set tick marks at 1, 100, 10000, ... set dtick to 2. To set tick marks at 1, 5, 25, 125, 625, 3125, ... set dtick to log_10(5), or 0.69897000433. *log* has several special values; *L*, where `f` is a positive number, gives ticks linearly spaced in value (but not position). For example `tick0` = 0.1, `dtick` = *L0.5* will put ticks at 0.1, 0.6, 1.1, 1.6 etc. To show powers of 10 plus small digits between, use *D1* (all digits) or *D2* (only 2 and 5). `tick0` is ignored for *D1* and *D2*. If the axis `type` is *date*, then you must convert the time to milliseconds. For example, to set the interval between ticks to one day, set `dtick` to 86400000.0. *date* also has special values *M* gives ticks spaced by a number of months. `n` must be a positive integer. To set ticks on the 15th of every third month, set `tick0` to *2000-01-15* and `dtick` to *M3*. To set ticks every 4 years, set `dtick` to *M48*" + }, + "tickvals": { + "type": "string", + "description": "Sets the values at which ticks on this axis appear. Only has an effect if `tickmode` is set to *array*. Used with `ticktext`." + }, + "ticktext": { + "type": "string", + "description": "Sets the text displayed at the ticks position via `tickvals`. Only has an effect if `tickmode` is set to *array*. Used with `tickvals`." + }, + "ticks": { + "type": "string", + "description": "Determines whether ticks are drawn or not. If **, this axis' ticks are not drawn. If *outside* (*inside*), this axis' are drawn outside (inside) the axis lines.", + "default": "", + "enum": [ + "outside", + "inside", + "" + ] + }, + "ticklabelposition": { + "type": "string", + "description": "Determines where tick labels are drawn.", + "default": "outside", + "enum": [ + "outside", + "inside", + "outside top", + "inside top", + "outside bottom", + "inside bottom" + ] + }, + "ticklen": { + "type": "number", + "description": "Sets the tick length (in px).", + "default": 5, + "minimum": 0 + }, + "tickwidth": { + "type": "number", + "description": "Sets the tick width (in px).", + "default": 1, + "minimum": 0 + }, + "tickcolor": { + "type": "string", + "description": "Sets the tick color.", + "default": "#444" + }, + "showticklabels": { + "type": "boolean", + "description": "Determines whether or not the tick labels are drawn.", + "default": true + }, + "tickfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "tickangle": { + "type": "number", + "description": "Sets the angle of the tick labels with respect to the horizontal. For example, a `tickangle` of -90 draws the tick labels vertically.", + "default": "auto", + "minimum": -180, + "maximum": 180 + }, + "tickformat": { + "type": "string", + "description": "Sets the tick label formatting rule using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format And for dates see: https://github.com/d3/d3-time-format#locale_format We add one item to d3's date formatter: *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*", + "default": "" + }, + "tickprefix": { + "type": "string", + "description": "Sets a tick label prefix.", + "default": "" + }, + "showtickprefix": { + "type": "string", + "description": "If *all*, all tick labels are displayed with a prefix. If *first*, only the first tick is displayed with a prefix. If *last*, only the last tick is displayed with a suffix. If *none*, tick prefixes are hidden.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "ticksuffix": { + "type": "string", + "description": "Sets a tick label suffix.", + "default": "" + }, + "showticksuffix": { + "type": "string", + "description": "Same as `showtickprefix` but for tick suffixes.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "separatethousands": { + "type": "boolean", + "description": "If \"true\", even 4-digit integers are separated", + "default": false + }, + "exponentformat": { + "type": "string", + "description": "Determines a formatting rule for the tick exponents. For example, consider the number 1,000,000,000. If *none*, it appears as 1,000,000,000. If *e*, 1e+9. If *E*, 1E+9. If *power*, 1x10^9 (with 9 in a super script). If *SI*, 1G. If *B*, 1B.", + "default": "B", + "enum": [ + "none", + "e", + "E", + "power", + "SI", + "B" + ] + }, + "minexponent": { + "type": "number", + "description": "Hide SI prefix for 10^n if |n| is below this number. This only has an effect when `tickformat` is *SI* or *B*.", + "default": 3, + "minimum": 0 + }, + "showexponent": { + "type": "string", + "description": "If *all*, all exponents are shown besides their significands. If *first*, only the exponent of the first tick is shown. If *last*, only the exponent of the last tick is shown. If *none*, no exponents appear.", + "default": "all", + "enum": [ + "all", + "first", + "last", + "none" + ] + }, + "title": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Sets the title of the color bar. Note that before the existence of `title.text`, the title's contents used to be defined as the `title` attribute itself. This behavior has been deprecated." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "side": { + "type": "string", + "description": "Determines the location of color bar's title with respect to the color bar. Note that the title's location used to be set by the now deprecated `titleside` attribute.", + "default": "top", + "enum": [ + "right", + "top", + "bottom" + ] + } + } + } + } + }, + "coloraxis": { + "type": "string", + "description": "Sets a reference to a shared color axis. References to these shared color axes are *coloraxis*, *coloraxis2*, *coloraxis3*, etc. Settings for these shared color axes are set in the layout, under `layout.coloraxis`, `layout.coloraxis2`, etc. Note that multiple color scales can be linked to the same color axis.", + "default": null + } + } + }, + "selected": { + "type": "object", + "properties": { + "marker": { + "type": "object", + "properties": { + "opacity": { + "type": "number", + "description": "Sets the marker opacity of selected points.", + "minimum": 0, + "maximum": 1 + }, + "color": { + "type": "string", + "description": "Sets the marker color of selected points." + }, + "size": { + "type": "number", + "description": "Sets the marker size of selected points.", + "minimum": 0 + } + } + }, + "textfont": { + "type": "object", + "properties": { + "color": { + "type": "string", + "description": "Sets the text font color of selected points." + } + } + } + } + }, + "unselected": { + "type": "object", + "properties": { + "marker": { + "type": "object", + "properties": { + "opacity": { + "type": "number", + "description": "Sets the marker opacity of unselected points, applied only when a selection exists.", + "minimum": 0, + "maximum": 1 + }, + "color": { + "type": "string", + "description": "Sets the marker color of unselected points, applied only when a selection exists." + }, + "size": { + "type": "number", + "description": "Sets the marker size of unselected points, applied only when a selection exists.", + "minimum": 0 + } + } + }, + "textfont": { + "type": "object", + "properties": { + "color": { + "type": "string", + "description": "Sets the text font color of unselected points, applied only when a selection exists." + } + } + } + } + }, + "textposition": { + "type": "string", + "description": "Sets the positions of the `text` elements with respects to the (x,y) coordinates.", + "default": "middle center", + "enum": [ + "top left", + "top center", + "top right", + "middle left", + "middle center", + "middle right", + "bottom left", + "bottom center", + "bottom right" + ] + }, + "textfont": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "error_x": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Determines whether or not this set of error bars is visible." + }, + "type": { + "type": "string", + "description": "Determines the rule used to generate the error bars. If *constant`, the bar lengths are of a constant value. Set this constant in `value`. If *percent*, the bar lengths correspond to a percentage of underlying data. Set this percentage in `value`. If *sqrt*, the bar lengths correspond to the square of the underlying data. If *data*, the bar lengths are set with data set `array`.", + "enum": [ + "percent", + "constant", + "sqrt", + "data" + ] + }, + "symmetric": { + "type": "boolean", + "description": "Determines whether or not the error bars have the same length in both direction (top/bottom for vertical bars, left/right for horizontal bars." + }, + "array": { + "type": "string", + "description": "Sets the data corresponding the length of each error bar. Values are plotted relative to the underlying data." + }, + "arrayminus": { + "type": "string", + "description": "Sets the data corresponding the length of each error bar in the bottom (left) direction for vertical (horizontal) bars Values are plotted relative to the underlying data." + }, + "value": { + "type": "number", + "description": "Sets the value of either the percentage (if `type` is set to *percent*) or the constant (if `type` is set to *constant*) corresponding to the lengths of the error bars.", + "default": 10, + "minimum": 0 + }, + "valueminus": { + "type": "number", + "description": "Sets the value of either the percentage (if `type` is set to *percent*) or the constant (if `type` is set to *constant*) corresponding to the lengths of the error bars in the bottom (left) direction for vertical (horizontal) bars", + "default": 10, + "minimum": 0 + }, + "traceref": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "tracerefminus": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "copy_ystyle": { + "type": "boolean" + }, + "color": { + "type": "string", + "description": "Sets the stoke color of the error bars." + }, + "thickness": { + "type": "number", + "description": "Sets the thickness (in px) of the error bars.", + "default": 2, + "minimum": 0 + }, + "width": { + "type": "number", + "description": "Sets the width (in px) of the cross-bar at both ends of the error bars.", + "minimum": 0 + } + } + }, + "error_y": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Determines whether or not this set of error bars is visible." + }, + "type": { + "type": "string", + "description": "Determines the rule used to generate the error bars. If *constant`, the bar lengths are of a constant value. Set this constant in `value`. If *percent*, the bar lengths correspond to a percentage of underlying data. Set this percentage in `value`. If *sqrt*, the bar lengths correspond to the square of the underlying data. If *data*, the bar lengths are set with data set `array`.", + "enum": [ + "percent", + "constant", + "sqrt", + "data" + ] + }, + "symmetric": { + "type": "boolean", + "description": "Determines whether or not the error bars have the same length in both direction (top/bottom for vertical bars, left/right for horizontal bars." + }, + "array": { + "type": "string", + "description": "Sets the data corresponding the length of each error bar. Values are plotted relative to the underlying data." + }, + "arrayminus": { + "type": "string", + "description": "Sets the data corresponding the length of each error bar in the bottom (left) direction for vertical (horizontal) bars Values are plotted relative to the underlying data." + }, + "value": { + "type": "number", + "description": "Sets the value of either the percentage (if `type` is set to *percent*) or the constant (if `type` is set to *constant*) corresponding to the lengths of the error bars.", + "default": 10, + "minimum": 0 + }, + "valueminus": { + "type": "number", + "description": "Sets the value of either the percentage (if `type` is set to *percent*) or the constant (if `type` is set to *constant*) corresponding to the lengths of the error bars in the bottom (left) direction for vertical (horizontal) bars", + "default": 10, + "minimum": 0 + }, + "traceref": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "tracerefminus": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "color": { + "type": "string", + "description": "Sets the stoke color of the error bars." + }, + "thickness": { + "type": "number", + "description": "Sets the thickness (in px) of the error bars.", + "default": 2, + "minimum": 0 + }, + "width": { + "type": "number", + "description": "Sets the width (in px) of the cross-bar at both ends of the error bars.", + "minimum": 0 + } + } + }, + "xcalendar": { + "type": "string", + "description": "Sets the calendar system to use with `x` date data.", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + }, + "ycalendar": { + "type": "string", + "description": "Sets the calendar system to use with `y` date data.", + "default": "gregorian", + "enum": [ + "gregorian", + "chinese", + "coptic", + "discworld", + "ethiopian", + "hebrew", + "islamic", + "julian", + "mayan", + "nanakshahi", + "nepali", + "persian", + "jalali", + "taiwan", + "thai", + "ummalqura" + ] + }, + "xaxis": { + "type": "string", + "description": "Sets a reference between this trace's x coordinates and a 2D cartesian x axis. If *x* (the default value), the x coordinates refer to `layout.xaxis`. If *x2*, the x coordinates refer to `layout.xaxis2`, and so on.", + "default": "x" + }, + "yaxis": { + "type": "string", + "description": "Sets a reference between this trace's y coordinates and a 2D cartesian y axis. If *y* (the default value), the y coordinates refer to `layout.yaxis`. If *y2*, the y coordinates refer to `layout.yaxis2`, and so on.", + "default": "y" + } + } +} \ No newline at end of file diff --git a/escalation/plotly_api/violin_plotly_schema.json b/escalation/plotly_api/violin_plotly_schema.json new file mode 100644 index 0000000..aac4003 --- /dev/null +++ b/escalation/plotly_api/violin_plotly_schema.json @@ -0,0 +1,491 @@ +{ + "attributes": { + "visible": { + "type": "string", + "description": "Determines whether or not this trace is visible. If *legendonly*, the trace is not drawn, but can appear as a legend item (provided that the legend itself is visible).", + "default": true, + "enum": [ + true, + false, + "legendonly" + ] + }, + "showlegend": { + "type": "boolean", + "description": "Determines whether or not an item corresponding to this trace is shown in the legend.", + "default": true + }, + "legendgroup": { + "type": "string", + "description": "Sets the legend group for this trace. Traces part of the same legend group hide/show at the same time when toggling legend items.", + "default": "" + }, + "opacity": { + "type": "number", + "description": "Sets the opacity of the trace.", + "default": 1, + "minimum": 0, + "maximum": 1 + }, + "customdata": { + "type": "string", + "description": "Assigns extra data each datum. This may be useful when listening to hover, click and selection events. Note that, *scatter* traces also appends customdata items in the markers DOM elements" + }, + "meta": { + "type": "string", + "description": "Assigns extra meta information associated with this trace that can be used in various text attributes. Attributes such as trace `name`, graph, axis and colorbar `title.text`, annotation `text` `rangeselector`, `updatemenues` and `sliders` `label` text all support `meta`. To access the trace `meta` values in an attribute in the same trace, simply use `%{meta[i]}` where `i` is the index or key of the `meta` item in question. To access trace `meta` in layout attributes, use `%{data[n[.meta[i]}` where `i` is the index or key of the `meta` and `n` is the trace index." + }, + "selectedpoints": { + "type": "string", + "description": "Array containing integer indices of selected points. Has an effect only for traces that support selections. Note that an empty array means an empty selection where the `unselected` are turned on for all points, whereas, any other non-array values means no selection all where the `selected` and `unselected` styles have no effect." + }, + "hoverinfo": { + "type": "string", + "description": "Determines which trace information appear on hover. If `none` or `skip` are set, no information is displayed upon hovering. But, if `none` is set, click and hover events are still fired.", + "default": "all", + "enum": [ + "name", + "text", + "text+name", + "z", + "z+name", + "z+text", + "z+text+name", + "y", + "y+name", + "y+text", + "y+text+name", + "y+z", + "y+z+name", + "y+z+text", + "y+z+text+name", + "x", + "x+name", + "x+text", + "x+text+name", + "x+z", + "x+z+name", + "x+z+text", + "x+z+text+name", + "x+y", + "x+y+name", + "x+y+text", + "x+y+text+name", + "x+y+z", + "x+y+z+name", + "x+y+z+text", + "x+y+z+text+name", + "all", + "none", + "skip" + ] + }, + "hoverlabel": { + "type": "object", + "properties": { + "bgcolor": { + "type": "string", + "description": "Sets the background color of the hover labels for this trace" + }, + "bordercolor": { + "type": "string", + "description": "Sets the border color of the hover labels for this trace." + }, + "font": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*." + }, + "size": { + "type": "number", + "minimum": 1 + }, + "color": { + "type": "string" + } + } + }, + "align": { + "type": "string", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "default": "auto", + "enum": [ + "left", + "right", + "auto" + ] + }, + "namelength": { + "type": "integer", + "description": "Sets the default length (in number of characters) of the trace name in the hover labels for all traces. -1 shows the whole name regardless of length. 0-3 shows the first 0-3 characters, and an integer >3 will show the whole name if it is less than that many characters, but if it is longer, will truncate to `namelength - 3` characters and add an ellipsis.", + "default": 15, + "minimum": -1 + } + } + }, + "uirevision": { + "type": "string", + "description": "Controls persistence of some user-driven changes to the trace: `constraintrange` in `parcoords` traces, as well as some `editable: true` modifications such as `name` and `colorbar.title`. Defaults to `layout.uirevision`. Note that other user-driven trace attribute changes are controlled by `layout` attributes: `trace.visible` is controlled by `layout.legend.uirevision`, `selectedpoints` is controlled by `layout.selectionrevision`, and `colorbar.(x|y)` (accessible with `config: {editable: true}`) is controlled by `layout.editrevision`. Trace changes are tracked by `uid`, which only falls back on trace index if no `uid` is provided. So if your app can add/remove traces before the end of the `data` array, such that the same trace has a different index, you can still preserve user-driven changes if you give each trace a `uid` that stays with it as it moves." + }, + "y": { + "type": "string", + "description": "Sets the y sample data or coordinates. See overview for more info." + }, + "x": { + "type": "string", + "description": "Sets the x sample data or coordinates. See overview for more info." + }, + "x0": { + "type": "string", + "description": "Sets the x coordinate for single-box traces or the starting coordinate for multi-box traces set using q1/median/q3. See overview for more info." + }, + "y0": { + "type": "string", + "description": "Sets the y coordinate for single-box traces or the starting coordinate for multi-box traces set using q1/median/q3. See overview for more info." + }, + "name": { + "type": "string", + "description": "Sets the trace name. The trace name appear as the legend item and on hover. For violin traces, the name will also be used for the position coordinate, if `x` and `x0` (`y` and `y0` if horizontal) are missing and the position axis is categorical. Note that the trace name is also used as a default value for attribute `scalegroup` (please see its description for details)." + }, + "orientation": { + "type": "string", + "description": "Sets the orientation of the violin(s). If *v* (*h*), the distribution is visualized along the vertical (horizontal).", + "enum": [ + "v", + "h" + ] + }, + "bandwidth": { + "type": "number", + "description": "Sets the bandwidth used to compute the kernel density estimate. By default, the bandwidth is determined by Silverman's rule of thumb.", + "minimum": 0 + }, + "scalegroup": { + "type": "string", + "description": "If there are multiple violins that should be sized according to to some metric (see `scalemode`), link them by providing a non-empty group id here shared by every trace in the same group. If a violin's `width` is undefined, `scalegroup` will default to the trace's name. In this case, violins with the same names will be linked together", + "default": "" + }, + "scalemode": { + "type": "string", + "description": "Sets the metric by which the width of each violin is determined.*width* means each violin has the same (max) width*count* means the violins are scaled by the number of sample points makingup each violin.", + "default": "width", + "enum": [ + "width", + "count" + ] + }, + "spanmode": { + "type": "string", + "description": "Sets the method by which the span in data space where the density function will be computed. *soft* means the span goes from the sample's minimum value minus two bandwidths to the sample's maximum value plus two bandwidths. *hard* means the span goes from the sample's minimum to its maximum value. For custom span settings, use mode *manual* and fill in the `span` attribute.", + "default": "soft", + "enum": [ + "soft", + "hard", + "manual" + ] + }, + "span": { + "type": "string", + "description": "Sets the span in data space for which the density function will be computed. Has an effect only when `spanmode` is set to *manual*." + }, + "line": { + "type": "object", + "properties": { + "color": { + "type": "string", + "description": "Sets the color of line bounding the violin(s)." + }, + "width": { + "type": "number", + "description": "Sets the width (in px) of line bounding the violin(s).", + "default": 2, + "minimum": 0 + } + } + }, + "fillcolor": { + "type": "string", + "description": "Sets the fill color. Defaults to a half-transparent variant of the line color, marker color, or marker line color, whichever is available." + }, + "points": { + "type": "string", + "description": "If *outliers*, only the sample points lying outside the whiskers are shown If *suspectedoutliers*, the outlier points are shown and points either less than 4*Q1-3*Q3 or greater than 4*Q3-3*Q1 are highlighted (see `outliercolor`) If *all*, all sample points are shown If *false*, only the violins are shown with no sample points. Defaults to *suspectedoutliers* when `marker.outliercolor` or `marker.line.outliercolor` is set, otherwise defaults to *outliers*.", + "enum": [ + "all", + "outliers", + "suspectedoutliers", + false + ] + }, + "jitter": { + "type": "number", + "description": "Sets the amount of jitter in the sample points drawn. If *0*, the sample points align along the distribution axis. If *1*, the sample points are drawn in a random jitter of width equal to the width of the violins.", + "minimum": 0, + "maximum": 1 + }, + "pointpos": { + "type": "number", + "description": "Sets the position of the sample points in relation to the violins. If *0*, the sample points are places over the center of the violins. Positive (negative) values correspond to positions to the right (left) for vertical violins and above (below) for horizontal violins.", + "minimum": -2, + "maximum": 2 + }, + "width": { + "type": "number", + "description": "Sets the width of the violin in data coordinates. If *0* (default value) the width is automatically selected based on the positions of other violin traces in the same subplot.", + "default": 0, + "minimum": 0 + }, + "marker": { + "type": "object", + "properties": { + "outliercolor": { + "type": "string", + "description": "Sets the color of the outlier sample points.", + "default": "rgba(0, 0, 0, 0)" + }, + "symbol": { + "type": "string", + "description": "Sets the marker symbol type. Adding 100 is equivalent to appending *-open* to a symbol name. Adding 200 is equivalent to appending *-dot* to a symbol name. Adding 300 is equivalent to appending *-open-dot* or *dot-open* to a symbol name.", + "default": "circle", + "enum": [ + "circle", + "square", + "diamond", + "cross", + "x", + "triangle-up", + "triangle-down", + "pentagon", + "hexagon", + "octagon", + "star" + ] + }, + "opacity": { + "type": "number", + "description": "Sets the marker opacity.", + "default": 1, + "minimum": 0, + "maximum": 1 + }, + "size": { + "type": "number", + "description": "Sets the marker size (in px).", + "default": 6, + "minimum": 0 + }, + "color": { + "type": "string", + "description": "Sets themarkercolor. It accepts either a specific color or an array of numbers that are mapped to the colorscale relative to the max and min values of the array or relative to `marker.cmin` and `marker.cmax` if set." + }, + "line": { + "type": "object", + "properties": { + "color": { + "type": "string", + "description": "Sets themarker.linecolor. It accepts either a specific color or an array of numbers that are mapped to the colorscale relative to the max and min values of the array or relative to `marker.line.cmin` and `marker.line.cmax` if set.", + "default": "#444" + }, + "width": { + "type": "number", + "description": "Sets the width (in px) of the lines bounding the marker points.", + "default": 0, + "minimum": 0 + }, + "outliercolor": { + "type": "string", + "description": "Sets the border line color of the outlier sample points. Defaults to marker.color" + }, + "outlierwidth": { + "type": "number", + "description": "Sets the border line width (in px) of the outlier sample points.", + "default": 1, + "minimum": 0 + } + } + } + } + }, + "text": { + "type": "string", + "description": "Sets the text elements associated with each sample value. If a single string, the same string appears over all the data points. If an array of string, the items are mapped in order to the this trace's (x,y) coordinates. To be seen, trace `hoverinfo` must contain a *text* flag.", + "default": "" + }, + "box": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Determines if an miniature box plot is drawn inside the violins. ", + "default": false + }, + "width": { + "type": "number", + "description": "Sets the width of the inner box plots relative to the violins' width. For example, with 1, the inner box plots are as wide as the violins.", + "default": 0.25, + "minimum": 0, + "maximum": 1 + }, + "fillcolor": { + "type": "string", + "description": "Sets the inner box plot fill color." + }, + "line": { + "type": "object", + "properties": { + "color": { + "type": "string", + "description": "Sets the inner box plot bounding line color." + }, + "width": { + "type": "number", + "description": "Sets the inner box plot bounding line width.", + "minimum": 0 + } + } + } + } + }, + "meanline": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Determines if a line corresponding to the sample's mean is shown inside the violins. If `box.visible` is turned on, the mean line is drawn inside the inner box. Otherwise, the mean line is drawn from one side of the violin to other.", + "default": false + }, + "color": { + "type": "string", + "description": "Sets the mean line color." + }, + "width": { + "type": "number", + "description": "Sets the mean line width.", + "minimum": 0 + } + } + }, + "side": { + "type": "string", + "description": "Determines on which side of the position value the density function making up one half of a violin is plotted. Useful when comparing two violin traces under *overlay* mode, where one trace has `side` set to *positive* and the other to *negative*.", + "default": "both", + "enum": [ + "both", + "positive", + "negative" + ] + }, + "offsetgroup": { + "type": "string", + "description": "Set several traces linked to the same position axis or matching axes to the same offsetgroup where bars of the same position coordinate will line up.", + "default": "" + }, + "alignmentgroup": { + "type": "string", + "description": "Set several traces linked to the same position axis or matching axes to the same alignmentgroup. This controls whether bars compute their positional range dependently or independently.", + "default": "" + }, + "selected": { + "type": "object", + "properties": { + "marker": { + "type": "object", + "properties": { + "opacity": { + "type": "number", + "description": "Sets the marker opacity of selected points.", + "minimum": 0, + "maximum": 1 + }, + "color": { + "type": "string", + "description": "Sets the marker color of selected points." + }, + "size": { + "type": "number", + "description": "Sets the marker size of selected points.", + "minimum": 0 + } + } + } + } + }, + "unselected": { + "type": "object", + "properties": { + "marker": { + "type": "object", + "properties": { + "opacity": { + "type": "number", + "description": "Sets the marker opacity of unselected points, applied only when a selection exists.", + "minimum": 0, + "maximum": 1 + }, + "color": { + "type": "string", + "description": "Sets the marker color of unselected points, applied only when a selection exists." + }, + "size": { + "type": "number", + "description": "Sets the marker size of unselected points, applied only when a selection exists.", + "minimum": 0 + } + } + } + } + }, + "hoveron": { + "type": "string", + "description": "Do the hover effects highlight individual violins or sample points or the kernel density estimate or any combination of them?", + "default": "violins+points+kde", + "enum": [ + "kde", + "points", + "points+kde", + "violins", + "violins+kde", + "violins+points", + "violins+points+kde", + "all" + ] + }, + "xaxis": { + "type": "string", + "description": "Sets a reference between this trace's x coordinates and a 2D cartesian x axis. If *x* (the default value), the x coordinates refer to `layout.xaxis`. If *x2*, the x coordinates refer to `layout.xaxis2`, and so on.", + "default": "x" + }, + "yaxis": { + "type": "string", + "description": "Sets a reference between this trace's y coordinates and a 2D cartesian y axis. If *y* (the default value), the y coordinates refer to `layout.yaxis`. If *y2*, the y coordinates refer to `layout.yaxis2`, and so on.", + "default": "y" + } + }, + "layoutAttributes": { + "violinmode": { + "type": "string", + "description": "Determines how violins at the same location coordinate are displayed on the graph. If *group*, the violins are plotted next to one another centered around the shared location. If *overlay*, the violins are plotted over one another, you might need to set *opacity* to see them multiple violins. Has no effect on traces that have *width* set.", + "default": "overlay", + "enum": [ + "group", + "overlay" + ] + }, + "violingap": { + "type": "number", + "description": "Sets the gap (in plot fraction) between violins of adjacent location coordinates. Has no effect on traces that have *width* set.", + "default": 0.3, + "minimum": 0, + "maximum": 1 + }, + "violingroupgap": { + "type": "number", + "description": "Sets the gap (in plot fraction) between violins of the same location coordinate. Has no effect on traces that have *width* set.", + "default": 0.3, + "minimum": 0, + "maximum": 1 + } + } +} \ No newline at end of file diff --git a/escalation/requirements-app.txt b/escalation/requirements-app.txt index 5f4fc37..4e6f4a2 100644 --- a/escalation/requirements-app.txt +++ b/escalation/requirements-app.txt @@ -17,9 +17,12 @@ pyyaml requests setuptools six +seaborn sqlacodegen -sqlalchemy +# 1.4.0 seems to break several of the ways we're accessing tables and classes in sqlalchemy +sqlalchemy<1.4.0 tableschema werkzeug[watchdog] wheel -Flask-HTTPAuth \ No newline at end of file +Flask-HTTPAuth +numpydoc \ No newline at end of file diff --git a/escalation/scripts/csv_to_sql.py b/escalation/scripts/csv_to_sql.py index d81dc25..e355ac4 100644 --- a/escalation/scripts/csv_to_sql.py +++ b/escalation/scripts/csv_to_sql.py @@ -6,17 +6,16 @@ codegen to create entries in the model file for the table. This script assumes you are running locally, and have a Docker container running the database as per readme + +In order to create new tables, the app must be running in setup wizard mode """ -from io import open as io_open -import os import sys import re import requests -from utility.constants import DATA_SOURCE, CSVFILE - +from utility.constants import DATA_SOURCE, CSVFILE, POSTGRES_TABLE_NAME_FORMAT_REGEX REPLACE = "replace" APPEND = "append" @@ -26,7 +25,7 @@ if __name__ == "__main__": """ example usage: - python database/csv_to_sql.py penguin_size escalation/test_app_deploy_data/data/penguin_size/penguin_size.csv my_user "this is from the experiment 2020-09-10" + python scripts/csv_to_sql.py penguin_size test_app_deploy_data/data/penguin_size/penguin_size.csv append my_user "this is from the experiment 2020-09-10" """ # todo - better arg handling with argparse or something @@ -43,7 +42,6 @@ else: FLASK_UPLOAD_URL = "http://localhost:8000/upload" - POSTGRES_TABLE_NAME_FORMAT_REGEX = r"^[a-zA-Z_]\w+$" if not re.match(POSTGRES_TABLE_NAME_FORMAT_REGEX, table_name): print( "Table names name must start with a letter or an underscore;" @@ -61,12 +59,22 @@ "Postgres SQL table names are case insensitive- " "tablename will be converted to lowercase letters" ) - - response = requests.post( - FLASK_UPLOAD_URL, - data={DATA_SOURCE: table_name, "username": username, "notes": notes,}, - files={CSVFILE: open(filepath, "rb")}, - ) + try: + response = requests.post( + FLASK_UPLOAD_URL, + data={DATA_SOURCE: table_name, "username": username, "notes": notes,}, + files=[(CSVFILE, open(filepath, "rb")),], + # to upload more than one file at a time, need to provide a list like this + # files=[ + # (CSVFILE, open("file1.csv", "rb")), + # (CSVFILE, open("file_2.csv", "rb")), + # ], + ) + except Exception: + raise ( + Exception, + "Is escalation running in debug mode?", + ) if response.status_code != 200: raise ( diff --git a/escalation/scripts/empty_db.sh b/escalation/scripts/empty_db.sh new file mode 100755 index 0000000..8d62f62 --- /dev/null +++ b/escalation/scripts/empty_db.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + + +if [[ ! -d "$(pwd)/escalation/app_deploy_data" ]]; then + echo "Script must be run from the top directory of the Escalation code repository" + exit 1 +fi + +read -p "This will totally empty the db, including metadata info, requiring some manual reconstruction. Are you sure? Enter [Y] to confirm" -n 1 -r +echo # move to a new line +if [[ $REPLY =~ ^[Yy]$ ]] +then + echo "running" + echo "docker-compose down --volumes" + docker-compose down --volumes + docker-compose up -d +else + echo "Aborting- no action taken" +fi diff --git a/escalation/scripts/testing_wizard_launcher.sh b/escalation/scripts/testing_wizard_launcher.sh new file mode 100755 index 0000000..3f899e3 --- /dev/null +++ b/escalation/scripts/testing_wizard_launcher.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +# This script is called by the user from the Host machine, and it launches the app in debug mode in a container +# launch the web app using a debug mode, +# mounting the app_deploy data directly in order to monitor and relaunch when changed by the config wizard + +if [[ ! -d "$(pwd)/escalation/app_deploy_data" ]]; then + echo "Script must be run from the top directory of the Escalation code repository" + exit 1 +fi + +docker-compose run --entrypoint /escalation/scripts/boot_escalation_debug_mode.sh \ +-p "8000:8000" \ +-v "$(pwd)/escalation/app_deploy_data":/escalation/app_deploy_data \ +-v "$(pwd)/escalation/templates/:/escalation/templates/" \ +-e FLASK_DEBUG=true \ +-e DATABASE_CONFIG="postgresql+psycopg2://escalation:escalation_pwd@postgres_db:5432/testing_escalation" \ +web + diff --git a/escalation/static/chart.css b/escalation/static/chart.css new file mode 100644 index 0000000..1e31bc5 --- /dev/null +++ b/escalation/static/chart.css @@ -0,0 +1,7 @@ +.chart{ + width: 100%; + height: 400px; + position: relative; + top: 0px; + left: 0px; +} \ No newline at end of file diff --git a/escalation/static/favicon.ico b/escalation/static/favicon.ico index 4cde3a1..ccc73f9 100644 Binary files a/escalation/static/favicon.ico and b/escalation/static/favicon.ico differ diff --git a/escalation/static/js_utils.js b/escalation/static/js_utils.js index 04cf319..c9e72ec 100644 --- a/escalation/static/js_utils.js +++ b/escalation/static/js_utils.js @@ -39,12 +39,14 @@ function show_all_row_handler(selector_id, exclusive_value) { } -function edit_graphic(page_id,graphic,graphic_status) { +function edit_graphic(page_id,graphic,graphic_status,post_url) { //This allows the web page to focus where the plot was updated instead of starting at the top of the web page let graphic_form=$('#form_button_click'); + graphic_form[0].page_id.value=page_id; graphic_form[0].graphic.value=graphic; graphic_form[0].graphic_status.value=graphic_status; + graphic_form.attr('action', post_url); graphic_form.submit(); } @@ -69,12 +71,12 @@ function modify_config(modification, page_id=-1,graphic=''){ } function get_main_data_sources(data_source_dict){ - let data_sources = new Set(); - data_sources.add(data_source_dict['main_data_source']['data_source_type']); + let data_sources = []; + data_sources.push(data_source_dict['main_data_source']['data_source_type']); let additional_data_source; if ('additional_data_sources' in data_source_dict) { for (additional_data_source of data_source_dict['additional_data_sources']) { - data_sources.add(additional_data_source['data_source_type']) + data_sources.push(additional_data_source['data_source_type']) } } return data_sources @@ -104,7 +106,7 @@ function any_identifiers_active(data_source) { const table_data=$(`#table_${data_source}`).bootstrapTable('getData') let flag=0; function active_table_data_to_form(dict) { - web_form[0][dict['upload_id']].value=(dict['active'] ? 'active' : 'inactive'); + web_form[0]["id_".concat(dict['upload_id'])].value=(dict['active'] ? 'active' : 'inactive'); if (dict['active']){ flag=1 } @@ -145,4 +147,114 @@ function get_collapse_dict(editors) { collapse_dict['visualization_options'] = editors['visualization'].getEditor('root').collapsed collapse_dict['selectable_data_dict'] = editors['selector'].getEditor('root').collapsed return collapse_dict -} \ No newline at end of file +} + +function find_word_in_dict(word_list,found_word,dict,key){ + let check_all_true = arr => arr.every(Boolean); + //Performs a depth first search to match word + // output is the path delimited by '.' + + const DESCRIPTION = "description"; + const PROPERTIES = "properties"; + const ITEMS = "items"; + let stack = []; + const name=key; + const dict_type = dict["type"]; + let any_word = false; + + word_list.forEach(function(word,i){ + if (key.toLowerCase().includes(word.toLowerCase())){ + found_word[i] = true; + any_word = true; + } + }); + // More useful results if we only pass down whether the ancestors had words in name not descriptions + let found_word_copy=found_word; + if (dict.hasOwnProperty(DESCRIPTION)) { + word_list.forEach(function (word, i) { + if (dict[DESCRIPTION].toLowerCase().includes(word.toLowerCase())){ + found_word_copy[i] = true; + any_word = true; + } + }); + } + // This is a better order for display + if (any_word && check_all_true(found_word_copy)){ + stack.push(name); + } + + if (dict_type === "object"){ + for (const new_key in dict[PROPERTIES]){ + find_word_in_dict(word_list,[...found_word],dict[PROPERTIES][new_key],new_key).forEach(function(suffix){ + stack.push(name + "." + PROPERTIES + "." + suffix) + + }); + } + }else if(dict_type === "array"){ + find_word_in_dict(word_list,[...found_word],dict[ITEMS],ITEMS).forEach(function(suffix){ + stack.push(name + "." + suffix) + }); + } + + return stack +} + +function json_editor_path(path,dict){ + // Replaces keys in the config path with 'title' to match what the user sees in the editor + const PROPERTIES = "properties"; + const TITLE = "title"; + let path_list = path.split(".") + for (let index = 0; index < path_list.length; index++) { + const field=path_list[index] + if (index) { + dict = dict[field]; + } + + if (field !== PROPERTIES && dict.hasOwnProperty(TITLE)){ + path_list[index]=dict[TITLE] + } + } + //Performs a depth first search to match word + // output is the path + return path_list.join(".") +} + +function get_description(path,dict){ + // given a path gets the relevant fields to show to the user + const fields_to_display = [ + "title", + "type", + "pattern", + "enum", + "minLength", + "maxLength", + "minimum", + "maximum", + "minItems", + "maxItems", + "required", + "default", + "description", + "examples" + ] + + const getNestedObject = (pathArr,nestedObj) => { + return pathArr.reduce((obj, key) => + (obj && obj[key] !== 'undefined') ? obj[key] : undefined, nestedObj); + }; + const fields=getNestedObject(path.split(".").slice(1),dict); + let field_list = []; + fields_to_display.forEach(function(field) { + if (fields.hasOwnProperty(field)){ + field_list.push(field+": "+String(fields[field])) + } + }); + return field_list.join("
") +} + + function new_editor(type,display_required_only,schema) { + return new JSONEditor(document.getElementById('editor_holder_'.concat(type)), + options={theme: 'bootstrap4',display_required_only: display_required_only, disable_edit_json:true, + disable_array_delete_last_row:true, iconlib: 'fontawesome5', show_errors:'always' ,schema: schema }); +} + diff --git a/escalation/static/lighter_buttons.css b/escalation/static/lighter_buttons.css index d1568bd..ea7a6d8 100644 --- a/escalation/static/lighter_buttons.css +++ b/escalation/static/lighter_buttons.css @@ -1,4 +1,4 @@ -.btn.btn-secondary.btn-sm { +button[class*="json-editor-btntype-"] { background-color: #ffffff; border-color: #cccccc; color: #333333; diff --git a/escalation/static/search.css b/escalation/static/search.css new file mode 100644 index 0000000..4263214 --- /dev/null +++ b/escalation/static/search.css @@ -0,0 +1,17 @@ +.list-group-item { + font-size: 12px; + padding: 1px 1px; + word-break: break-all; +} + +.list-group { + height: 120px; + overflow: auto; + +} +.search_info{ + font-size: 12px; + height: 120px; + overflow: auto; + word-break: normal; +} \ No newline at end of file diff --git a/escalation/templates/base.html b/escalation/templates/base.html index 765ae61..567db85 100644 --- a/escalation/templates/base.html +++ b/escalation/templates/base.html @@ -39,6 +39,9 @@ + Escalation {% block header %}{% endblock %} diff --git a/escalation/templates/config_buttons.html b/escalation/templates/config_buttons.html index 481fa20..7efa2bf 100644 --- a/escalation/templates/config_buttons.html +++ b/escalation/templates/config_buttons.html @@ -1,20 +1,30 @@ -
-

Buttons:

- - + + + + +
- - -Back to Dashboard Layout - -
\ No newline at end of file +--> + diff --git a/escalation/templates/cytoscape.html b/escalation/templates/cytoscape.html new file mode 100644 index 0000000..d34ae03 --- /dev/null +++ b/escalation/templates/cytoscape.html @@ -0,0 +1,7 @@ + + + + +graph["container"] = document.getElementById("{{ plot_id_str }}"); +cytoscape(graph); + diff --git a/escalation/templates/data_layout.html b/escalation/templates/data_layout.html index 2e53cae..effb470 100644 --- a/escalation/templates/data_layout.html +++ b/escalation/templates/data_layout.html @@ -2,6 +2,11 @@ {% extends 'base.html' %} +{% block header %} + + + +{% endblock %} {% block content %} {% include 'navbar.html' %} @@ -36,17 +41,27 @@

{{ plot['title'] }}

- {% include plot['graph_html_file']%} + {% set plot_id_str = "plot_{}".format(plot_id) %} +
+ +
- {% for selector in plot['select_info'] %} - {% set selector_index = loop.index0 %} - {% include selector['select_html_file'] ignore missing %} - {% endfor %} - - + {% if plot['select_info'] %} + {% for selector in plot['select_info'] %} + {% set selector_index = loop.index0 %} + {% include selector['select_html_file'] ignore missing %} + {% endfor %} + + + {% endif %}
diff --git a/escalation/templates/data_upload.html b/escalation/templates/data_upload.html index 057f7be..f89a0a8 100644 --- a/escalation/templates/data_upload.html +++ b/escalation/templates/data_upload.html @@ -13,9 +13,10 @@

Upload CSV


-

Upload data csvs to existing data tables. To add new data tables, you'll have to run the app in development mode using the setup wizard.

+

Upload data CSVs to existing data tables. To add new data tables, you'll have to run the app in development mode using the setup wizard.

+ {% if success_text %}
{% endif %} + +{% if failure_text %} +
+ +
+{% endif %} + {% if config.ENV == "development" %}
@@ -47,9 +60,9 @@

Upload CSV

- + - +
diff --git a/escalation/templates/numerical_filter.html b/escalation/templates/numerical_filter.html index 0b3a3e4..2373bed 100644 --- a/escalation/templates/numerical_filter.html +++ b/escalation/templates/numerical_filter.html @@ -2,13 +2,13 @@ {% set name=selector['name'] %} - +{% set type="datetime-local" if selector["type"]=="datetime" else "number" %}
{{ selector['text'] }}
{% for position in ['max', 'min'] %}
{{ position }}:
- +
{% endfor %} \ No newline at end of file diff --git a/escalation/templates/plotly.html b/escalation/templates/plotly.html index a7c3554..b9df947 100644 --- a/escalation/templates/plotly.html +++ b/escalation/templates/plotly.html @@ -1,9 +1,4 @@ -
- -
\ No newline at end of file +Plotly.newPlot({{ plot_id_str }},graph || {}); diff --git a/escalation/templates/seaborn.html b/escalation/templates/seaborn.html new file mode 100644 index 0000000..b098adb --- /dev/null +++ b/escalation/templates/seaborn.html @@ -0,0 +1,20 @@ + + + +var img = document.querySelector("#{{ plot_id_str }} img"); +if (img === null) { + img = document.createElement("img"); + img.src = "data:image/png;base64, ".concat(graph['seaborn_plot_bytes']); + const base_height = Math.min(width/graph['aspect_ratio'],height); + img.width = base_height*graph['aspect_ratio']; + img.height = base_height; + let chart_div = document.getElementById("{{ plot_id_str }}"); + chart_div.appendChild(img); +} +else{ + img.src = "data:image/png;base64, ".concat(graph['seaborn_plot_bytes']); + const base_height = Math.min(width/graph['aspect_ratio'],height); + img.width = base_height*graph['aspect_ratio']; + img.height = base_height; +} + diff --git a/escalation/templates/view_uploaded_data.html b/escalation/templates/view_uploaded_data.html index 7a53b69..56180e7 100644 --- a/escalation/templates/view_uploaded_data.html +++ b/escalation/templates/view_uploaded_data.html @@ -27,7 +27,7 @@

Download

{% for id in identifiers_metadata_dict_list['ids'] %} - + {% endfor %}
diff --git a/escalation/templates/wizard_config_landing_page.html b/escalation/templates/wizard_config_landing_page.html new file mode 100644 index 0000000..7bf62a0 --- /dev/null +++ b/escalation/templates/wizard_config_landing_page.html @@ -0,0 +1,96 @@ + + +{% extends 'base.html' %} + +{% block header %} + + + + +{% endblock %} + +{% block content %} +{% include 'navbar.html' %} +
+

Graph Setup Page

+
+
+
+
+ + + + + +
+
+
+ +
+
+

Type of Plot

+
+
+ +
+ + + + + Cancel + +
+
+
+
+ +{% endblock %} \ No newline at end of file diff --git a/escalation/templates/wizard_configurer.html b/escalation/templates/wizard_configurer.html index 494a993..86e259a 100644 --- a/escalation/templates/wizard_configurer.html +++ b/escalation/templates/wizard_configurer.html @@ -11,7 +11,7 @@

Dashboard Layout Editor

-
+
@@ -80,15 +80,15 @@

Dashboard Layout Editor

{% for graphic in page["graphic_config_files"] %}
- - + +
{% endfor %}
-
diff --git a/escalation/templates/wizard_data_upload.html b/escalation/templates/wizard_data_upload.html index fa25429..b400243 100644 --- a/escalation/templates/wizard_data_upload.html +++ b/escalation/templates/wizard_data_upload.html @@ -17,6 +17,7 @@

Upload new type of CSV file to Database

Create new tables from data sources not already uploaded

+ {% if success_text %}
{% endif %} + +{% if failure_text %} +
+ +
+{% endif %} +
Append to a Existing Table @@ -49,7 +62,7 @@

Upload new type of CSV file to Database

- +
diff --git a/escalation/templates/wizard_graphic_config_editor.html b/escalation/templates/wizard_graphic_config_editor.html index e382286..53c3aac 100644 --- a/escalation/templates/wizard_graphic_config_editor.html +++ b/escalation/templates/wizard_graphic_config_editor.html @@ -8,6 +8,7 @@ + {% endblock %} {% block content %} @@ -20,26 +21,14 @@
-
-

Create or edit a dashboard graphic

- - -
-
+
-
-
-
-
-
-
-
- +
-
+
-
+
@@ -48,8 +37,38 @@

Create or edit a dashboard graphic


-
- {% include "config_buttons.html" %} +
+
+
+
+ +
+
+
+
+
    +
+
+
+
+
+
+
+
+ {% include "config_buttons.html" %} +
+
+ + + Selectors not shown +
+
+
+
+
@@ -60,36 +79,42 @@

Create or edit a dashboard graphic

let graphic_status = '{{ graphic_status }}'; let graphic_path = '{{ graphic_path }}' let current_state = {{ current_config | safe }}; - let dict_of_schemas={{ schema | safe }}; - let dict_of_plotly_schemas = dict_of_schemas['plotly_schema']; + let dict_of_schemas = {{ schema | safe }}; let editors= new Object(); - let current_plot_type = "{{ current_schema }}"; - let saved_plot_type = current_plot_type; - let selector_editor=new_editor('plot_selector',true,{{ schema_selector_dict | safe }}); + make_json_editor(); // The only values shown in default filter are the ones corresponding to the column - let default_entries_dict ={{ default_entries_dict | safe }} + let default_entries_dict ={{ default_entries_dict | safe }}; window.JSONEditor.defaults.callbacks.template = { - "default_selected_filter": (jseditor, e) => { + "column_value_filter": (jseditor, e) => { if (default_entries_dict[e.watched.column].includes(e.item)) return e.item; return ""; }, "identity_callback": (jseditor, e) => e.item, }; + // Has to come after setting the callbacks + set_json_editor_value(current_state); + + + function save_json_editor_to_file(){ + const valid=validate_schema(); + + if(valid){ + + const webpage = "{{ url_for('.file_tree') }}"; + const url = "{{ url_for('.update_graphic_json_config_with_ui_changes') }}"; + + current_state=get_json_editor_value(); + // The form contains nested dictionaries so it is easier to send a json than a form + let data = JSON.stringify({"config_dict" : current_state, + "page_id":{{ page_id }}, + "graphic_path": graphic_path, + "graphic_status":graphic_status}); + send_json_in_post_request(url, data, webpage); + } - make_json_editor(); - {% if graphic_status in ['old', 'copy'] %} - set_json_editor_value(current_state); - selector_editor.setValue({"plot_type":current_plot_type}); - {% endif %} - let current_data_sources = get_main_data_sources(editors['graphic_meta_info'].getEditor('root.data_sources').getValue()); - add_watcher_to_data_sources(); - function new_editor(type,display_required_only,schema) { - return new JSONEditor(document.getElementById('editor_holder_'.concat(type)), - options={theme: 'bootstrap4',display_required_only: display_required_only, disable_edit_json:true, - disable_array_delete_last_row:true, iconlib: 'fontawesome5', show_errors:'always' ,schema: schema }); } - function save_json_editor_to_file(redirect){ + function validate_schema(){ const errors=get_json_editor_errors(); const duplicate_data_source = check_duplicate_data_sources(editors['graphic_meta_info'].getEditor('root.data_sources').getValue()); if (errors.length || duplicate_data_source){ @@ -104,23 +129,56 @@

Create or edit a dashboard graphic

setTimeout(function() { success_text.innerHTML=""; }, 5000); + return false } - else{ - const url = "{{ url_for('.update_graphic_json_config_with_ui_changes') }}"; + else { + return true + } + } + + function get_preview(){ + const valid=validate_schema(); + if (valid){ - let webpage = ''; - if (redirect) {webpage = "{{ url_for('.file_tree') }}"} + const url = "{{ url_for('.preview_graphic_json_config') }}"; current_state=get_json_editor_value(); - saved_plot_type = current_plot_type; // The form contains nested dictionaries so it is easier to send a json than a form - let data = JSON.stringify({"config_dict" : current_state, - "page_id":{{ page_id }}, - "graphic_path": graphic_path, - "graphic_status":graphic_status}); - send_json_in_post_request(url, data, webpage);// The form contains nested dictionaries so it is easier to send a json than a form + let data = JSON.stringify({"config_dict" : current_state}); + + let xhr = new XMLHttpRequest(); + xhr.open("POST", url); + xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); + xhr.onreadystatechange = function () { + let success_text = document.querySelector('#feedback_message'); + if (xhr.readyState === 4 && xhr.status === 200) { + //success_text.innerHTML = "Applied"; + const jsonResponse = JSON.parse(xhr.response); + let graph = JSON.parse(jsonResponse['preview']); + const preview=document.getElementById('preview') + const width = (window.innerWidth - preview.getBoundingClientRect().left)*.95; + let height = (window.innerHeight - preview.getBoundingClientRect().top)*.95; + if (width Create or edit a dashboard graphic let success_text = document.querySelector('#feedback_message'); if (xhr.readyState === 4 && xhr.status === 200) { if (webpage) {window.location.href = webpage} - success_text.innerHTML = "Applied"; + //success_text.innerHTML = "Applied"; const jsonResponse = JSON.parse(xhr.response); graphic_status = "old"; graphic_path = jsonResponse['graphic_path']; } else { success_text.innerHTML = "Failed"; + // Message disappears after 5 secs + setTimeout(function() { + success_text.innerHTML=""; + }, 5000); } - // Message disappears after 5 secs - setTimeout(function() { - success_text.innerHTML=""; - }, 5000); + }; xhr.send(data); } + + + function reset_json_editor(){ for(let editor_name in editors){ editors[editor_name].setValue(); @@ -154,9 +216,8 @@

Create or edit a dashboard graphic

} } function make_json_editor(){ - editors['graphic_meta_info'] = new_editor('graphic', false, dict_of_schemas['graphic_schema']); - editors['plotly'] = new_editor('plotly', true, dict_of_plotly_schemas[current_plot_type]); - editors['visualization'] = new_editor('visualization', false, dict_of_schemas["visualization_schema"]); + editors['graphic_meta_info'] = new_editor('header', false, dict_of_schemas['graphic_schema']); + editors['graphic'] = new_editor('graphic', true, dict_of_schemas['plot_manager_schema']); editors['selector'] = new_editor('selector', false, dict_of_schemas["selector_schema"]); } function get_json_editor_value(){ @@ -174,12 +235,6 @@

Create or edit a dashboard graphic

return errors } function reload_last_save(){ - if (current_plot_type != saved_plot_type){ - current_plot_type=saved_plot_type; - selector_editor.setValue({"plot_type":current_plot_type}); - editors['plotly'].destroy(); - editors['plotly'] = new_editor('plotly',true, dict_of_plotly_schemas[current_plot_type]) - } set_json_editor_value(current_state); } function set_json_editor_value(values_to_set) { @@ -187,53 +242,44 @@

Create or edit a dashboard graphic

editors[editor_name].setValue(values_to_set[editor_name]); } } - selector_editor.watch('root.plot_type', () => { - current_plot_type = selector_editor.getEditor('root.plot_type').getValue(); - editors['plotly'].destroy(); - editors['plotly'] = new_editor('plotly',true, dict_of_plotly_schemas[current_plot_type]); - - }); - function add_watcher_to_data_sources() { - editors['graphic_meta_info'].watch('root.data_sources', () => { - let new_data_sources = get_main_data_sources(editors['graphic_meta_info'].getEditor('root.data_sources').getValue()); - // check if sets are not equal - if (!(new_data_sources.size === current_data_sources.size && [...new_data_sources].every(value => current_data_sources.has(value)))) { - current_data_sources = new_data_sources; - const collapse_dict=get_collapse_dict(editors) - - var loading_div = document.getElementById("loading") - loading_div.style.display = "block"; - - let xhr = new XMLHttpRequest(); - xhr.open("POST", "{{ url_for('.get_updated_schemas') }}"); - xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); - xhr.onreadystatechange = function () { - if (xhr.readyState === 4 && xhr.status === 200) { - const jsonResponse = JSON.parse(xhr.response); - dict_of_schemas = jsonResponse["new_schemas"]; - default_entries_dict = jsonResponse["new_default_entries_dict"]; - dict_of_plotly_schemas = dict_of_schemas['plotly_schema']; - current_data_sources = new_data_sources; - let save_state = get_json_editor_value(); - destroy_json_editor(); - make_json_editor(); - set_json_editor_value(save_state); - add_watcher_to_data_sources(); - loading_div.style.display = "none"; - } else if (xhr.readyState === 4) { - console.log("Failed") - loading_div.style.display = "none"; - } - - }; - xhr.send(JSON.stringify({ - "data_sources": Array.from(new_data_sources), - "collapse_dict": collapse_dict - })); + function search_and_post_results() { + const search_word = document.getElementById("search_bar").value + const path_info = document.getElementById("path_info"); + path_info.innerHTML = ""; + if (search_word.length >2) { + const word_list = search_word.trim().split(" "); + let bool_word_array = []; + for (let i = 0; i < word_list.length; i++) { + bool_word_array.push(false); + } + const schema_path = document.getElementById("schema_path"); + while (schema_path.firstChild) { + schema_path.removeChild(schema_path.firstChild); } - }); + const dict_names=[dict_of_schemas['plot_manager_schema']['title'], "Data Selector Options"]; + [dict_of_schemas['plot_manager_schema'], dict_of_schemas["selector_schema"]].forEach(function(dict,index){ + const path_list = find_word_in_dict(word_list,bool_word_array, dict, dict_names[index]); + for (let i = 0; i < path_list.length; ++i) { + // create an item for each one + let listItem = document.createElement('button'); + // Add the item properties + listItem.setAttribute("type", "button"); + listItem.classList.add("list-group-item","list-group-item-action"); + listItem.onclick = function(){post_description(path_list[i],dict)}; + listItem.innerHTML = json_editor_path(path_list[i],dict); + // Add listItem + schema_path.appendChild(listItem); + } + }); + } } + + function post_description(path,dict) { + const path_info = document.getElementById("path_info"); + path_info.innerHTML = get_description(path,dict); + } + {% endblock %} \ No newline at end of file diff --git a/escalation/test_app_deploy_data/big_penguins.json b/escalation/test_app_deploy_data/big_penguins.json index e74c7a6..b01dbcc 100644 --- a/escalation/test_app_deploy_data/big_penguins.json +++ b/escalation/test_app_deploy_data/big_penguins.json @@ -11,23 +11,21 @@ "type": "scatter", "x": "penguin_size:body_mass_g", "y": "penguin_size:flipper_length_mm", - "mode": "markers" - } - ] - }, - "visualization_options": { - "hover_data": { - "column": [ - "penguin_size:species", - "penguin_size:culmen_length_mm" - ] - }, - "groupby": { - "column": [ + "mode": "markers", + "hovertext": [ + "penguin_size:species", + "penguin_size:culmen_length_mm" + ], + "transforms": { + "groupby": { + "groups": [ "penguin_size:sex", "penguin_size:island" ] - } + } + } + } + ] }, "selectable_data_dict": { "filter": [ diff --git a/escalation/test_app_deploy_data/big_penguins_seaborn_multi_axis.json b/escalation/test_app_deploy_data/big_penguins_seaborn_multi_axis.json new file mode 100644 index 0000000..0606b04 --- /dev/null +++ b/escalation/test_app_deploy_data/big_penguins_seaborn_multi_axis.json @@ -0,0 +1,63 @@ +{ + "plot_manager": "seaborn", + "data_sources": { + "main_data_source": {"data_source_type": "penguin_size"} + }, + "title": "Many views of flippers flippers?", + "brief_desc": "This plot looks at the relationship between...", + "plot_specific_info": { + "data": [ + { + "type": "scatterplot", + "x": "penguin_size:body_mass_g", + "y": "penguin_size:flipper_length_mm", + "size": "penguin_size:culmen_length_mm", + "hue": "penguin_size:sex", + "alpha": 0.6, + "linewidth": 0, + "legend": null + }, + { + "type": "barplot", + "x": "penguin_size:sex", + "y": "penguin_size:culmen_length_mm", + "hue": "penguin_size:island" + }, + { + "type": "boxplot", + "x": "penguin_size:island", + "y": "penguin_size:culmen_length_mm", + "hue": "penguin_size:sex" + } + ], + "layout": { + "nrows": 2, + "ncols": 2, + "figsize": [ + 10, + 10 + ] + } + }, + "visualization_options": {}, + "selectable_data_dict": { + "filter": [ + { + "column": "penguin_size:sex", + "multiple": false + }, + { + "column": "penguin_size:island", + "multiple": true, + "default_selected": [ + "Dream" + ] + } + ], + "numerical_filter": [ + { + "column": "penguin_size:culmen_length_mm" + } + ] + } +} \ No newline at end of file diff --git a/escalation/test_app_deploy_data/big_penguins_seaborn_scatter.json b/escalation/test_app_deploy_data/big_penguins_seaborn_scatter.json new file mode 100644 index 0000000..5fccdc1 --- /dev/null +++ b/escalation/test_app_deploy_data/big_penguins_seaborn_scatter.json @@ -0,0 +1,48 @@ +{ + "plot_manager": "seaborn", + "data_sources": { + "main_data_source": {"data_source_type": "penguin_size"} + }, + "title": "Do massive penguins have long flippers?", + "brief_desc": "This plot looks at the relationship between...", + "plot_specific_info": { + "data": [ + { + "type": "scatterplot", + "x": "penguin_size:body_mass_g", + "y": "penguin_size:flipper_length_mm", + "size": "penguin_size:culmen_length_mm", + "hue": "penguin_size:sex", + "alpha": 0.6, + "linewidth": 0, + "legend": "brief" + } + ], + "layout": { + "nrows": 1, + "ncols": 1 + } + }, + "visualization_options": { + }, + "selectable_data_dict": { + "filter": [ + { + "column": "penguin_size:sex", + "multiple": false + }, + { + "column": "penguin_size:island", + "multiple": true, + "default_selected": [ + "Dream" + ] + } + ], + "numerical_filter": [ + { + "column": "penguin_size:culmen_length_mm" + } + ] + } +} \ No newline at end of file diff --git a/escalation/test_app_deploy_data/build_test_app_config_json.py b/escalation/test_app_deploy_data/build_test_app_config_json.py index e04e59a..e9b74c0 100644 --- a/escalation/test_app_deploy_data/build_test_app_config_json.py +++ b/escalation/test_app_deploy_data/build_test_app_config_json.py @@ -9,7 +9,6 @@ def build_config_json(data_backend, data_file_directory): SITE_TITLE: "Escalation Test", "brief_desc": "This is a test/demo for the Escalation OS", DATA_BACKEND: data_backend, - DATA_FILE_DIRECTORY: data_file_directory, DATA_SOURCES: ["penguin_size", "mean_penguin_stat", "penguin_size_small"], AVAILABLE_PAGES: [ { @@ -162,7 +161,7 @@ def build_config_json_graphic_radio_penguins(): PATH_TO_GRAPHIC_CONFIG_FILES = "path_to_graphic_config_files" path_to_test_files = os.path.join("test_app_deploy_data") config_file_definitions = { - "test_app_sql_config.json": {DATA_BACKEND: POSTGRES}, + "main_config.json": {DATA_BACKEND: POSTGRES}, } for config_file_name, config in config_file_definitions.items(): diff --git a/escalation/test_app_deploy_data/data/miserables_edges/miserables_edges.csv b/escalation/test_app_deploy_data/data/miserables_edges/miserables_edges.csv new file mode 100644 index 0000000..47d1eea --- /dev/null +++ b/escalation/test_app_deploy_data/data/miserables_edges/miserables_edges.csv @@ -0,0 +1,255 @@ +start,end,count,edge_id +Myriel,Napoleon,1,0 +Myriel,MlleBaptistine,8,1 +Myriel,MmeMagloire,10,2 +Myriel,CountessDeLo,1,3 +Myriel,Geborand,1,4 +Myriel,Champtercier,1,5 +Myriel,Cravatte,1,6 +Myriel,Count,2,7 +Myriel,OldMan,1,8 +Myriel,Valjean,5,9 +MlleBaptistine,MmeMagloire,6,10 +MlleBaptistine,Valjean,3,11 +MmeMagloire,Valjean,3,12 +Labarre,Valjean,1,13 +Valjean,Marguerite,1,14 +Valjean,MmeDeR,1,15 +Valjean,Isabeau,1,16 +Valjean,Gervais,1,17 +Valjean,Fantine,9,18 +Valjean,MmeThenardier,7,19 +Valjean,Thenardier,12,20 +Valjean,Cosette,31,21 +Valjean,Javert,17,22 +Valjean,Fauchelevent,8,23 +Valjean,Bamatabois,2,24 +Valjean,Simplice,3,25 +Valjean,Scaufflaire,1,26 +Valjean,Woman1,2,27 +Valjean,Judge,3,28 +Valjean,Champmathieu,3,29 +Valjean,Brevet,2,30 +Valjean,Chenildieu,2,31 +Valjean,Cochepaille,2,32 +Valjean,Woman2,3,33 +Valjean,MotherInnocent,1,34 +Valjean,Gavroche,1,35 +Valjean,Gillenormand,2,36 +Valjean,MlleGillenormand,2,37 +Valjean,Marius,19,38 +Valjean,Enjolras,4,39 +Valjean,Bossuet,1,40 +Valjean,Gueulemer,1,41 +Valjean,Babet,1,42 +Valjean,Claquesous,1,43 +Valjean,Montparnasse,1,44 +Valjean,Toussaint,1,45 +Marguerite,Fantine,2,46 +Tholomyes,Listolier,4,47 +Tholomyes,Fameuil,4,48 +Tholomyes,Blacheville,4,49 +Tholomyes,Favourite,3,50 +Tholomyes,Dahlia,3,51 +Tholomyes,Zephine,3,52 +Tholomyes,Fantine,3,53 +Tholomyes,Cosette,1,54 +Tholomyes,Marius,1,55 +Listolier,Fameuil,4,56 +Listolier,Blacheville,4,57 +Listolier,Favourite,3,58 +Listolier,Dahlia,3,59 +Listolier,Zephine,3,60 +Listolier,Fantine,3,61 +Fameuil,Blacheville,4,62 +Fameuil,Favourite,3,63 +Fameuil,Dahlia,3,64 +Fameuil,Zephine,3,65 +Fameuil,Fantine,3,66 +Blacheville,Favourite,4,67 +Blacheville,Dahlia,3,68 +Blacheville,Zephine,3,69 +Blacheville,Fantine,3,70 +Favourite,Dahlia,5,71 +Favourite,Zephine,4,72 +Favourite,Fantine,4,73 +Dahlia,Zephine,4,74 +Dahlia,Fantine,4,75 +Zephine,Fantine,4,76 +Fantine,MmeThenardier,2,77 +Fantine,Thenardier,1,78 +Fantine,Javert,5,79 +Fantine,Bamatabois,1,80 +Fantine,Perpetue,1,81 +Fantine,Simplice,2,82 +MmeThenardier,Thenardier,13,83 +MmeThenardier,Cosette,4,84 +MmeThenardier,Javert,1,85 +MmeThenardier,Eponine,2,86 +MmeThenardier,Anzelma,1,87 +MmeThenardier,Magnon,1,88 +MmeThenardier,Gueulemer,1,89 +MmeThenardier,Babet,1,90 +MmeThenardier,Claquesous,1,91 +Thenardier,Cosette,1,92 +Thenardier,Javert,5,93 +Thenardier,Pontmercy,1,94 +Thenardier,Boulatruelle,1,95 +Thenardier,Eponine,3,96 +Thenardier,Anzelma,2,97 +Thenardier,Gavroche,1,98 +Thenardier,Marius,2,99 +Thenardier,Gueulemer,5,100 +Thenardier,Babet,6,101 +Thenardier,Claquesous,4,102 +Thenardier,Montparnasse,1,103 +Thenardier,Brujon,3,104 +Cosette,Javert,1,105 +Cosette,Woman2,1,106 +Cosette,Gillenormand,3,107 +Cosette,MlleGillenormand,2,108 +Cosette,LtGillenormand,1,109 +Cosette,Marius,21,110 +Cosette,Toussaint,2,111 +Javert,Fauchelevent,1,112 +Javert,Bamatabois,1,113 +Javert,Simplice,1,114 +Javert,Woman1,1,115 +Javert,Woman2,1,116 +Javert,Gavroche,1,117 +Javert,Enjolras,6,118 +Javert,Gueulemer,1,119 +Javert,Babet,2,120 +Javert,Claquesous,1,121 +Javert,Montparnasse,1,122 +Javert,Toussaint,1,123 +Fauchelevent,MotherInnocent,3,124 +Fauchelevent,Gribier,2,125 +Bamatabois,Judge,2,126 +Bamatabois,Champmathieu,2,127 +Bamatabois,Brevet,1,128 +Bamatabois,Chenildieu,1,129 +Bamatabois,Cochepaille,1,130 +Perpetue,Simplice,2,131 +Judge,Champmathieu,3,132 +Judge,Brevet,2,133 +Judge,Chenildieu,2,134 +Judge,Cochepaille,2,135 +Champmathieu,Brevet,2,136 +Champmathieu,Chenildieu,2,137 +Champmathieu,Cochepaille,2,138 +Brevet,Chenildieu,2,139 +Brevet,Cochepaille,2,140 +Chenildieu,Cochepaille,2,141 +Pontmercy,MmePontmercy,1,142 +Pontmercy,Marius,1,143 +Eponine,Anzelma,2,144 +Eponine,Marius,5,145 +Eponine,Mabeuf,1,146 +Eponine,Courfeyrac,1,147 +Eponine,Gueulemer,1,148 +Eponine,Babet,1,149 +Eponine,Claquesous,1,150 +Eponine,Montparnasse,1,151 +Eponine,Brujon,1,152 +Jondrette,MmeBurgon,1,153 +MmeBurgon,Gavroche,2,154 +Gavroche,Marius,4,155 +Gavroche,Mabeuf,1,156 +Gavroche,Enjolras,7,157 +Gavroche,Combeferre,6,158 +Gavroche,Prouvaire,1,159 +Gavroche,Feuilly,2,160 +Gavroche,Courfeyrac,7,161 +Gavroche,Bahorel,5,162 +Gavroche,Bossuet,5,163 +Gavroche,Joly,3,164 +Gavroche,Grantaire,1,165 +Gavroche,Gueulemer,1,166 +Gavroche,Babet,1,167 +Gavroche,Montparnasse,1,168 +Gavroche,Child1,2,169 +Gavroche,Child2,2,170 +Gavroche,Brujon,1,171 +Gavroche,MmeHucheloup,1,172 +Gillenormand,Magnon,1,173 +Gillenormand,MlleGillenormand,9,174 +Gillenormand,LtGillenormand,1,175 +Gillenormand,Marius,12,176 +Gillenormand,BaronessT,1,177 +MlleGillenormand,MmePontmercy,1,178 +MlleGillenormand,MlleVaubois,1,179 +MlleGillenormand,LtGillenormand,2,180 +MlleGillenormand,Marius,6,181 +LtGillenormand,Marius,1,182 +Marius,BaronessT,1,183 +Marius,Mabeuf,1,184 +Marius,Enjolras,7,185 +Marius,Combeferre,5,186 +Marius,Feuilly,1,187 +Marius,Courfeyrac,9,188 +Marius,Bahorel,1,189 +Marius,Bossuet,5,190 +Marius,Joly,2,191 +Mabeuf,Enjolras,1,192 +Mabeuf,Combeferre,2,193 +Mabeuf,Feuilly,1,194 +Mabeuf,Courfeyrac,2,195 +Mabeuf,Bahorel,2,196 +Mabeuf,Bossuet,1,197 +Mabeuf,Joly,1,198 +Mabeuf,MotherPlutarch,3,199 +Enjolras,Combeferre,15,200 +Enjolras,Prouvaire,4,201 +Enjolras,Feuilly,6,202 +Enjolras,Courfeyrac,17,203 +Enjolras,Bahorel,4,204 +Enjolras,Bossuet,10,205 +Enjolras,Joly,5,206 +Enjolras,Grantaire,3,207 +Enjolras,Claquesous,1,208 +Enjolras,MmeHucheloup,1,209 +Combeferre,Prouvaire,2,210 +Combeferre,Feuilly,5,211 +Combeferre,Courfeyrac,13,212 +Combeferre,Bahorel,5,213 +Combeferre,Bossuet,9,214 +Combeferre,Joly,5,215 +Combeferre,Grantaire,1,216 +Prouvaire,Feuilly,2,217 +Prouvaire,Courfeyrac,3,218 +Prouvaire,Bahorel,2,219 +Prouvaire,Bossuet,2,220 +Prouvaire,Joly,2,221 +Prouvaire,Grantaire,1,222 +Feuilly,Courfeyrac,6,223 +Feuilly,Bahorel,3,224 +Feuilly,Bossuet,6,225 +Feuilly,Joly,5,226 +Feuilly,Grantaire,1,227 +Courfeyrac,Bahorel,6,228 +Courfeyrac,Bossuet,12,229 +Courfeyrac,Joly,5,230 +Courfeyrac,Grantaire,2,231 +Courfeyrac,MmeHucheloup,1,232 +Bahorel,Bossuet,4,233 +Bahorel,Joly,5,234 +Bahorel,Grantaire,1,235 +Bahorel,MmeHucheloup,1,236 +Bossuet,Joly,7,237 +Bossuet,Grantaire,3,238 +Bossuet,MmeHucheloup,1,239 +Joly,Grantaire,2,240 +Joly,MmeHucheloup,1,241 +Grantaire,MmeHucheloup,1,242 +Gueulemer,Babet,6,243 +Gueulemer,Claquesous,4,244 +Gueulemer,Montparnasse,2,245 +Gueulemer,Brujon,3,246 +Babet,Claquesous,4,247 +Babet,Montparnasse,2,248 +Babet,Brujon,3,249 +Claquesous,Montparnasse,2,250 +Claquesous,Brujon,1,251 +Montparnasse,Brujon,1,252 +Child1,Child2,3,253 diff --git a/escalation/test_app_deploy_data/data/miserables_nodes/miserables_nodes.csv b/escalation/test_app_deploy_data/data/miserables_nodes/miserables_nodes.csv new file mode 100644 index 0000000..991ef42 --- /dev/null +++ b/escalation/test_app_deploy_data/data/miserables_nodes/miserables_nodes.csv @@ -0,0 +1,78 @@ +name,color +Babet,#9C9D90 +Javert,#95BF92 +Mabeuf,#73973F +Pontmercy,#8878AE +Perpetue,#B07E68 +OldMan,#9BB647 +MmePontmercy,#7E7A8C +Napoleon,#6D954B +Jondrette,#D2B08C +Prouvaire,#7A647F +BaronessT,#DA0BCE +Courfeyrac,#727CB5 +MlleBaptistine,#B00491 +Enjolras,#477CC7 +Marguerite,#309E58 +Boulatruelle,#D34DE8 +Labarre,#75645B +Toussaint,#AFDBAD +Grantaire,#920E03 +MmeDeR,#6E7B74 +MmeMagloire,#9ADC68 +Claquesous,#58C89E +Bamatabois,#D2872E +Myriel,#6EE6B7 +Valjean,#5F48A0 +MotherInnocent,#4D74BF +Scaufflaire,#39DD4C +Eponine,#E2B418 +Thenardier,#2D249A +Gribier,#508E7D +LtGillenormand,#901414 +Feuilly,#0CB0BD +Gervais,#7F5748 +Brevet,#E183F8 +CountessDeLo,#2F907A +Dahlia,#E2C41C +Fantine,#303023 +MlleGillenormand,#8308AF +Bossuet,#3B9CC2 +Geborand,#541D3D +Magnon,#EB3800 +Marius,#ED5621 +MmeBurgon,#17A24D +Gavroche,#76F70B +Champtercier,#114349 +Cravatte,#FD800B +Gillenormand,#8C5EBB +Anzelma,#3D441F +Woman1,#9E21C9 +Joly,#81770B +Isabeau,#70DE30 +Zephine,#100A8E +Fauchelevent,#C3B50F +Cochepaille,#B4B0DA +Listolier,#5F1747 +Simplice,#B885F2 +Fameuil,#F17C1A +Gueulemer,#EB6A6B +Combeferre,#90F020 +Chenildieu,#1989F6 +Woman2,#C031C7 +Brujon,#2ACB60 +Child1,#E1C40F +Montparnasse,#388655 +Champmathieu,#D35AAF +Blacheville,#DECB45 +Favourite,#A8B9E6 +MotherPlutarch,#1AE3C7 +Bahorel,#1B1E53 +Tholomyes,#15B4D9 +MmeThenardier,#EC6D2F +Cosette,#7850C0 +Child2,#1386D3 +Judge,#AE8C1B +MmeHucheloup,#F2B125 +MlleVaubois,#7173EE +Count,#C86F9C diff --git a/escalation/test_app_deploy_data/data/temperature/temperature.csv b/escalation/test_app_deploy_data/data/temperature/temperature.csv new file mode 100644 index 0000000..28334f7 --- /dev/null +++ b/escalation/test_app_deploy_data/data/temperature/temperature.csv @@ -0,0 +1,3651 @@ +Date,Temp +1981-01-01,20.7 +1981-01-02,17.9 +1981-01-03,18.8 +1981-01-04,14.6 +1981-01-05,15.8 +1981-01-06,15.8 +1981-01-07,15.8 +1981-01-08,17.4 +1981-01-09,21.8 +1981-01-10,20.0 +1981-01-11,16.2 +1981-01-12,13.3 +1981-01-13,16.7 +1981-01-14,21.5 +1981-01-15,25.0 +1981-01-16,20.7 +1981-01-17,20.6 +1981-01-18,24.8 +1981-01-19,17.7 +1981-01-20,15.5 +1981-01-21,18.2 +1981-01-22,12.1 +1981-01-23,14.4 +1981-01-24,16.0 +1981-01-25,16.5 +1981-01-26,18.7 +1981-01-27,19.4 +1981-01-28,17.2 +1981-01-29,15.5 +1981-01-30,15.1 +1981-01-31,15.4 +1981-02-01,15.3 +1981-02-02,18.8 +1981-02-03,21.9 +1981-02-04,19.9 +1981-02-05,16.6 +1981-02-06,16.8 +1981-02-07,14.6 +1981-02-08,17.1 +1981-02-09,25.0 +1981-02-10,15.0 +1981-02-11,13.7 +1981-02-12,13.9 +1981-02-13,18.3 +1981-02-14,22.0 +1981-02-15,22.1 +1981-02-16,21.2 +1981-02-17,18.4 +1981-02-18,16.6 +1981-02-19,16.1 +1981-02-20,15.7 +1981-02-21,16.6 +1981-02-22,16.5 +1981-02-23,14.4 +1981-02-24,14.4 +1981-02-25,18.5 +1981-02-26,16.9 +1981-02-27,17.5 +1981-02-28,21.2 +1981-03-01,17.8 +1981-03-02,18.6 +1981-03-03,17.0 +1981-03-04,16.0 +1981-03-05,13.3 +1981-03-06,14.3 +1981-03-07,11.4 +1981-03-08,16.3 +1981-03-09,16.1 +1981-03-10,11.8 +1981-03-11,12.2 +1981-03-12,14.7 +1981-03-13,11.8 +1981-03-14,11.3 +1981-03-15,10.6 +1981-03-16,11.7 +1981-03-17,14.2 +1981-03-18,11.2 +1981-03-19,16.9 +1981-03-20,16.7 +1981-03-21,8.1 +1981-03-22,8.0 +1981-03-23,8.8 +1981-03-24,13.4 +1981-03-25,10.9 +1981-03-26,13.4 +1981-03-27,11.0 +1981-03-28,15.0 +1981-03-29,15.7 +1981-03-30,14.5 +1981-03-31,15.8 +1981-04-01,16.7 +1981-04-02,16.8 +1981-04-03,17.5 +1981-04-04,17.1 +1981-04-05,18.1 +1981-04-06,16.6 +1981-04-07,10.0 +1981-04-08,14.9 +1981-04-09,15.9 +1981-04-10,13.0 +1981-04-11,7.6 +1981-04-12,11.5 +1981-04-13,13.5 +1981-04-14,13.0 +1981-04-15,13.3 +1981-04-16,12.1 +1981-04-17,12.4 +1981-04-18,13.2 +1981-04-19,13.8 +1981-04-20,10.6 +1981-04-21,9.0 +1981-04-22,10.0 +1981-04-23,9.8 +1981-04-24,11.5 +1981-04-25,8.9 +1981-04-26,7.4 +1981-04-27,9.9 +1981-04-28,9.3 +1981-04-29,9.9 +1981-04-30,7.4 +1981-05-01,8.6 +1981-05-02,11.9 +1981-05-03,14.0 +1981-05-04,8.6 +1981-05-05,10.0 +1981-05-06,13.5 +1981-05-07,12.0 +1981-05-08,10.5 +1981-05-09,10.7 +1981-05-10,8.1 +1981-05-11,10.1 +1981-05-12,10.6 +1981-05-13,5.3 +1981-05-14,6.6 +1981-05-15,8.5 +1981-05-16,11.2 +1981-05-17,9.8 +1981-05-18,5.9 +1981-05-19,3.2 +1981-05-20,2.1 +1981-05-21,3.4 +1981-05-22,5.4 +1981-05-23,9.6 +1981-05-24,11.5 +1981-05-25,12.3 +1981-05-26,12.6 +1981-05-27,11.0 +1981-05-28,11.2 +1981-05-29,11.4 +1981-05-30,11.8 +1981-05-31,12.8 +1981-06-01,11.6 +1981-06-02,10.6 +1981-06-03,9.8 +1981-06-04,11.2 +1981-06-05,5.7 +1981-06-06,7.1 +1981-06-07,2.5 +1981-06-08,3.5 +1981-06-09,4.6 +1981-06-10,11.0 +1981-06-11,5.7 +1981-06-12,7.7 +1981-06-13,10.4 +1981-06-14,11.4 +1981-06-15,9.2 +1981-06-16,6.1 +1981-06-17,2.7 +1981-06-18,4.3 +1981-06-19,6.3 +1981-06-20,3.8 +1981-06-21,4.4 +1981-06-22,7.1 +1981-06-23,4.8 +1981-06-24,5.8 +1981-06-25,6.2 +1981-06-26,7.3 +1981-06-27,9.2 +1981-06-28,10.2 +1981-06-29,9.5 +1981-06-30,9.5 +1981-07-01,10.7 +1981-07-02,10.0 +1981-07-03,6.5 +1981-07-04,7.0 +1981-07-05,7.4 +1981-07-06,8.1 +1981-07-07,6.6 +1981-07-08,8.3 +1981-07-09,8.9 +1981-07-10,4.6 +1981-07-11,6.8 +1981-07-12,5.7 +1981-07-13,6.1 +1981-07-14,7.0 +1981-07-15,7.2 +1981-07-16,6.3 +1981-07-17,8.8 +1981-07-18,5.0 +1981-07-19,7.4 +1981-07-20,10.1 +1981-07-21,12.0 +1981-07-22,9.0 +1981-07-23,8.9 +1981-07-24,9.8 +1981-07-25,9.0 +1981-07-26,9.2 +1981-07-27,7.7 +1981-07-28,8.0 +1981-07-29,6.1 +1981-07-30,3.5 +1981-07-31,3.2 +1981-08-01,5.7 +1981-08-02,7.7 +1981-08-03,9.0 +1981-08-04,10.0 +1981-08-05,6.2 +1981-08-06,6.9 +1981-08-07,6.5 +1981-08-08,6.8 +1981-08-09,7.0 +1981-08-10,5.2 +1981-08-11,3.0 +1981-08-12,5.6 +1981-08-13,7.9 +1981-08-14,9.0 +1981-08-15,8.6 +1981-08-16,10.3 +1981-08-17,10.5 +1981-08-18,7.6 +1981-08-19,9.7 +1981-08-20,12.5 +1981-08-21,7.4 +1981-08-22,7.9 +1981-08-23,3.9 +1981-08-24,6.6 +1981-08-25,4.6 +1981-08-26,7.0 +1981-08-27,6.0 +1981-08-28,5.5 +1981-08-29,8.1 +1981-08-30,5.5 +1981-08-31,6.2 +1981-09-01,8.0 +1981-09-02,10.3 +1981-09-03,9.8 +1981-09-04,9.6 +1981-09-05,8.5 +1981-09-06,7.5 +1981-09-07,11.2 +1981-09-08,14.6 +1981-09-09,11.7 +1981-09-10,7.8 +1981-09-11,12.3 +1981-09-12,10.1 +1981-09-13,11.5 +1981-09-14,7.3 +1981-09-15,10.9 +1981-09-16,14.1 +1981-09-17,10.7 +1981-09-18,16.9 +1981-09-19,10.5 +1981-09-20,6.5 +1981-09-21,11.0 +1981-09-22,6.3 +1981-09-23,10.5 +1981-09-24,7.2 +1981-09-25,7.6 +1981-09-26,10.7 +1981-09-27,7.8 +1981-09-28,9.6 +1981-09-29,11.4 +1981-09-30,12.4 +1981-10-01,8.9 +1981-10-02,13.2 +1981-10-03,8.6 +1981-10-04,6.2 +1981-10-05,11.4 +1981-10-06,13.2 +1981-10-07,14.3 +1981-10-08,7.3 +1981-10-09,12.9 +1981-10-10,7.8 +1981-10-11,6.2 +1981-10-12,5.6 +1981-10-13,10.0 +1981-10-14,13.3 +1981-10-15,8.3 +1981-10-16,10.2 +1981-10-17,8.6 +1981-10-18,7.3 +1981-10-19,10.4 +1981-10-20,11.2 +1981-10-21,13.2 +1981-10-22,11.4 +1981-10-23,9.1 +1981-10-24,6.6 +1981-10-25,8.4 +1981-10-26,9.7 +1981-10-27,13.2 +1981-10-28,12.5 +1981-10-29,11.0 +1981-10-30,11.0 +1981-10-31,11.7 +1981-11-01,9.2 +1981-11-02,11.5 +1981-11-03,13.6 +1981-11-04,13.7 +1981-11-05,10.4 +1981-11-06,11.5 +1981-11-07,7.6 +1981-11-08,9.6 +1981-11-09,14.2 +1981-11-10,15.7 +1981-11-11,10.5 +1981-11-12,10.5 +1981-11-13,9.7 +1981-11-14,9.5 +1981-11-15,11.3 +1981-11-16,8.9 +1981-11-17,9.4 +1981-11-18,11.9 +1981-11-19,11.7 +1981-11-20,13.4 +1981-11-21,12.6 +1981-11-22,10.1 +1981-11-23,15.8 +1981-11-24,13.6 +1981-11-25,11.9 +1981-11-26,9.9 +1981-11-27,12.6 +1981-11-28,17.8 +1981-11-29,15.0 +1981-11-30,13.6 +1981-12-01,13.4 +1981-12-02,10.5 +1981-12-03,14.2 +1981-12-04,11.5 +1981-12-05,13.0 +1981-12-06,15.0 +1981-12-07,14.7 +1981-12-08,12.6 +1981-12-09,12.5 +1981-12-10,13.5 +1981-12-11,14.8 +1981-12-12,17.2 +1981-12-13,9.7 +1981-12-14,12.1 +1981-12-15,12.8 +1981-12-16,11.2 +1981-12-17,16.4 +1981-12-18,15.6 +1981-12-19,13.3 +1981-12-20,11.0 +1981-12-21,11.1 +1981-12-22,15.0 +1981-12-23,12.8 +1981-12-24,15.0 +1981-12-25,14.2 +1981-12-26,14.0 +1981-12-27,15.5 +1981-12-28,13.3 +1981-12-29,15.6 +1981-12-30,15.2 +1981-12-31,17.4 +1982-01-01,17.0 +1982-01-02,15.0 +1982-01-03,13.5 +1982-01-04,15.2 +1982-01-05,13.0 +1982-01-06,12.5 +1982-01-07,14.1 +1982-01-08,14.8 +1982-01-09,16.2 +1982-01-10,15.8 +1982-01-11,19.1 +1982-01-12,22.2 +1982-01-13,15.9 +1982-01-14,13.0 +1982-01-15,14.1 +1982-01-16,15.8 +1982-01-17,24.0 +1982-01-18,18.0 +1982-01-19,19.7 +1982-01-20,25.2 +1982-01-21,20.5 +1982-01-22,19.3 +1982-01-23,15.8 +1982-01-24,17.0 +1982-01-25,18.4 +1982-01-26,13.3 +1982-01-27,14.6 +1982-01-28,12.5 +1982-01-29,17.0 +1982-01-30,17.1 +1982-01-31,14.0 +1982-02-01,14.6 +1982-02-02,13.3 +1982-02-03,14.8 +1982-02-04,15.1 +1982-02-05,13.1 +1982-02-06,13.6 +1982-02-07,19.5 +1982-02-08,22.7 +1982-02-09,17.2 +1982-02-10,13.5 +1982-02-11,15.4 +1982-02-12,17.0 +1982-02-13,19.2 +1982-02-14,22.8 +1982-02-15,26.3 +1982-02-16,18.2 +1982-02-17,17.0 +1982-02-18,14.8 +1982-02-19,12.8 +1982-02-20,15.5 +1982-02-21,15.6 +1982-02-22,13.1 +1982-02-23,15.2 +1982-02-24,14.1 +1982-02-25,12.5 +1982-02-26,14.6 +1982-02-27,10.4 +1982-02-28,13.9 +1982-03-01,11.9 +1982-03-02,13.5 +1982-03-03,9.8 +1982-03-04,14.0 +1982-03-05,21.5 +1982-03-06,19.5 +1982-03-07,16.7 +1982-03-08,19.1 +1982-03-09,11.0 +1982-03-10,9.0 +1982-03-11,10.0 +1982-03-12,14.6 +1982-03-13,12.5 +1982-03-14,17.2 +1982-03-15,19.2 +1982-03-16,22.2 +1982-03-17,15.7 +1982-03-18,14.2 +1982-03-19,9.8 +1982-03-20,14.0 +1982-03-21,17.5 +1982-03-22,20.7 +1982-03-23,15.6 +1982-03-24,13.2 +1982-03-25,14.5 +1982-03-26,16.8 +1982-03-27,17.2 +1982-03-28,13.4 +1982-03-29,14.2 +1982-03-30,14.3 +1982-03-31,10.2 +1982-04-01,10.4 +1982-04-02,12.3 +1982-04-03,11.9 +1982-04-04,11.2 +1982-04-05,8.5 +1982-04-06,12.0 +1982-04-07,12.4 +1982-04-08,12.9 +1982-04-09,10.1 +1982-04-10,15.0 +1982-04-11,13.6 +1982-04-12,12.4 +1982-04-13,13.6 +1982-04-14,16.1 +1982-04-15,19.5 +1982-04-16,14.2 +1982-04-17,9.3 +1982-04-18,10.1 +1982-04-19,7.4 +1982-04-20,8.6 +1982-04-21,7.8 +1982-04-22,9.1 +1982-04-23,13.0 +1982-04-24,16.5 +1982-04-25,12.9 +1982-04-26,6.9 +1982-04-27,6.9 +1982-04-28,8.7 +1982-04-29,10.0 +1982-04-30,10.8 +1982-05-01,7.5 +1982-05-02,6.3 +1982-05-03,11.9 +1982-05-04,13.8 +1982-05-05,11.8 +1982-05-06,11.0 +1982-05-07,10.1 +1982-05-08,8.5 +1982-05-09,5.5 +1982-05-10,7.6 +1982-05-11,8.7 +1982-05-12,10.8 +1982-05-13,11.2 +1982-05-14,9.1 +1982-05-15,3.7 +1982-05-16,4.6 +1982-05-17,6.6 +1982-05-18,13.2 +1982-05-19,15.2 +1982-05-20,7.6 +1982-05-21,8.4 +1982-05-22,6.0 +1982-05-23,8.3 +1982-05-24,8.6 +1982-05-25,11.1 +1982-05-26,12.1 +1982-05-27,12.9 +1982-05-28,14.0 +1982-05-29,12.5 +1982-05-30,11.5 +1982-05-31,7.0 +1982-06-01,7.1 +1982-06-02,9.0 +1982-06-03,3.1 +1982-06-04,2.5 +1982-06-05,0.0 +1982-06-06,1.6 +1982-06-07,2.6 +1982-06-08,5.7 +1982-06-09,2.3 +1982-06-10,4.5 +1982-06-11,8.2 +1982-06-12,6.9 +1982-06-13,7.3 +1982-06-14,6.0 +1982-06-15,7.3 +1982-06-16,7.6 +1982-06-17,8.0 +1982-06-18,8.0 +1982-06-19,6.8 +1982-06-20,7.3 +1982-06-21,6.2 +1982-06-22,6.9 +1982-06-23,8.9 +1982-06-24,4.0 +1982-06-25,1.3 +1982-06-26,0.8 +1982-06-27,4.3 +1982-06-28,7.3 +1982-06-29,7.7 +1982-06-30,9.0 +1982-07-01,4.2 +1982-07-02,1.6 +1982-07-03,2.6 +1982-07-04,3.4 +1982-07-05,3.9 +1982-07-06,7.0 +1982-07-07,7.8 +1982-07-08,5.3 +1982-07-09,2.4 +1982-07-10,2.8 +1982-07-11,4.0 +1982-07-12,7.5 +1982-07-13,7.8 +1982-07-14,5.6 +1982-07-15,3.3 +1982-07-16,5.0 +1982-07-17,3.7 +1982-07-18,3.9 +1982-07-19,5.2 +1982-07-20,0.2 +1982-07-21,0.8 +1982-07-22,0.9 +1982-07-23,3.5 +1982-07-24,6.6 +1982-07-25,9.5 +1982-07-26,9.0 +1982-07-27,3.5 +1982-07-28,4.5 +1982-07-29,5.7 +1982-07-30,5.6 +1982-07-31,7.1 +1982-08-01,9.7 +1982-08-02,8.3 +1982-08-03,9.1 +1982-08-04,2.8 +1982-08-05,2.2 +1982-08-06,4.5 +1982-08-07,3.8 +1982-08-08,3.8 +1982-08-09,6.2 +1982-08-10,11.5 +1982-08-11,10.2 +1982-08-12,7.9 +1982-08-13,9.0 +1982-08-14,9.5 +1982-08-15,6.0 +1982-08-16,8.2 +1982-08-17,9.2 +1982-08-18,4.3 +1982-08-19,6.6 +1982-08-20,9.4 +1982-08-21,13.2 +1982-08-22,6.6 +1982-08-23,5.1 +1982-08-24,12.1 +1982-08-25,11.2 +1982-08-26,8.5 +1982-08-27,4.6 +1982-08-28,7.0 +1982-08-29,14.2 +1982-08-30,12.7 +1982-08-31,7.6 +1982-09-01,4.0 +1982-09-02,10.0 +1982-09-03,10.5 +1982-09-04,5.0 +1982-09-05,4.5 +1982-09-06,8.2 +1982-09-07,4.3 +1982-09-08,9.8 +1982-09-09,5.8 +1982-09-10,5.0 +1982-09-11,8.5 +1982-09-12,9.0 +1982-09-13,3.6 +1982-09-14,6.7 +1982-09-15,6.7 +1982-09-16,10.1 +1982-09-17,15.0 +1982-09-18,8.9 +1982-09-19,5.7 +1982-09-20,4.2 +1982-09-21,4.0 +1982-09-22,5.3 +1982-09-23,6.3 +1982-09-24,8.5 +1982-09-25,11.5 +1982-09-26,7.7 +1982-09-27,9.2 +1982-09-28,7.8 +1982-09-29,6.3 +1982-09-30,6.3 +1982-10-01,8.6 +1982-10-02,6.1 +1982-10-03,13.2 +1982-10-04,9.9 +1982-10-05,4.7 +1982-10-06,5.8 +1982-10-07,14.9 +1982-10-08,10.7 +1982-10-09,8.6 +1982-10-10,9.4 +1982-10-11,5.7 +1982-10-12,10.9 +1982-10-13,13.1 +1982-10-14,10.4 +1982-10-15,8.2 +1982-10-16,9.8 +1982-10-17,7.5 +1982-10-18,5.8 +1982-10-19,9.8 +1982-10-20,7.9 +1982-10-21,8.7 +1982-10-22,10.0 +1982-10-23,10.6 +1982-10-24,8.0 +1982-10-25,10.2 +1982-10-26,15.1 +1982-10-27,13.9 +1982-10-28,9.2 +1982-10-29,9.0 +1982-10-30,13.2 +1982-10-31,7.0 +1982-11-01,10.6 +1982-11-02,6.9 +1982-11-03,9.5 +1982-11-04,12.5 +1982-11-05,13.6 +1982-11-06,17.7 +1982-11-07,16.0 +1982-11-08,11.3 +1982-11-09,10.5 +1982-11-10,14.4 +1982-11-11,10.3 +1982-11-12,9.0 +1982-11-13,11.1 +1982-11-14,14.5 +1982-11-15,18.0 +1982-11-16,12.8 +1982-11-17,10.7 +1982-11-18,9.1 +1982-11-19,8.7 +1982-11-20,12.4 +1982-11-21,12.6 +1982-11-22,10.3 +1982-11-23,13.7 +1982-11-24,16.0 +1982-11-25,15.8 +1982-11-26,12.1 +1982-11-27,12.5 +1982-11-28,12.2 +1982-11-29,13.7 +1982-11-30,16.1 +1982-12-01,15.5 +1982-12-02,10.3 +1982-12-03,10.5 +1982-12-04,11.0 +1982-12-05,11.9 +1982-12-06,13.0 +1982-12-07,12.2 +1982-12-08,10.6 +1982-12-09,13.0 +1982-12-10,13.0 +1982-12-11,12.2 +1982-12-12,12.6 +1982-12-13,18.7 +1982-12-14,15.2 +1982-12-15,15.3 +1982-12-16,13.9 +1982-12-17,15.8 +1982-12-18,13.0 +1982-12-19,13.0 +1982-12-20,13.7 +1982-12-21,12.0 +1982-12-22,10.8 +1982-12-23,15.6 +1982-12-24,15.3 +1982-12-25,13.9 +1982-12-26,13.0 +1982-12-27,15.3 +1982-12-28,16.3 +1982-12-29,15.8 +1982-12-30,17.7 +1982-12-31,16.3 +1983-01-01,18.4 +1983-01-02,15.0 +1983-01-03,10.9 +1983-01-04,11.4 +1983-01-05,14.8 +1983-01-06,12.1 +1983-01-07,12.8 +1983-01-08,16.2 +1983-01-09,15.5 +1983-01-10,13.0 +1983-01-11,10.5 +1983-01-12,9.1 +1983-01-13,10.5 +1983-01-14,11.8 +1983-01-15,12.7 +1983-01-16,12.7 +1983-01-17,11.5 +1983-01-18,13.8 +1983-01-19,13.3 +1983-01-20,11.6 +1983-01-21,15.4 +1983-01-22,12.4 +1983-01-23,16.9 +1983-01-24,14.7 +1983-01-25,10.6 +1983-01-26,15.6 +1983-01-27,10.7 +1983-01-28,12.6 +1983-01-29,13.8 +1983-01-30,14.3 +1983-01-31,14.0 +1983-02-01,18.1 +1983-02-02,17.3 +1983-02-03,13.0 +1983-02-04,16.0 +1983-02-05,14.9 +1983-02-06,16.2 +1983-02-07,20.3 +1983-02-08,22.5 +1983-02-09,17.2 +1983-02-10,15.9 +1983-02-11,16.8 +1983-02-12,13.8 +1983-02-13,12.8 +1983-02-14,14.0 +1983-02-15,17.5 +1983-02-16,21.5 +1983-02-17,16.8 +1983-02-18,13.6 +1983-02-19,14.5 +1983-02-20,14.2 +1983-02-21,15.7 +1983-02-22,19.7 +1983-02-23,17.4 +1983-02-24,14.4 +1983-02-25,16.9 +1983-02-26,19.1 +1983-02-27,20.4 +1983-02-28,20.1 +1983-03-01,19.9 +1983-03-02,22.0 +1983-03-03,20.5 +1983-03-04,22.1 +1983-03-05,20.6 +1983-03-06,15.0 +1983-03-07,20.6 +1983-03-08,21.5 +1983-03-09,16.2 +1983-03-10,14.1 +1983-03-11,14.5 +1983-03-12,21.1 +1983-03-13,15.9 +1983-03-14,15.2 +1983-03-15,13.1 +1983-03-16,13.2 +1983-03-17,12.5 +1983-03-18,15.2 +1983-03-19,17.6 +1983-03-20,15.5 +1983-03-21,16.7 +1983-03-22,16.3 +1983-03-23,15.1 +1983-03-24,12.7 +1983-03-25,10.0 +1983-03-26,11.4 +1983-03-27,12.6 +1983-03-28,10.7 +1983-03-29,10.0 +1983-03-30,13.9 +1983-03-31,13.4 +1983-04-01,12.5 +1983-04-02,12.8 +1983-04-03,7.8 +1983-04-04,11.1 +1983-04-05,10.7 +1983-04-06,7.1 +1983-04-07,6.7 +1983-04-08,5.7 +1983-04-09,9.1 +1983-04-10,15.2 +1983-04-11,15.5 +1983-04-12,11.1 +1983-04-13,11.7 +1983-04-14,11.5 +1983-04-15,9.8 +1983-04-16,6.2 +1983-04-17,6.7 +1983-04-18,7.5 +1983-04-19,8.8 +1983-04-20,8.0 +1983-04-21,10.4 +1983-04-22,14.5 +1983-04-23,16.5 +1983-04-24,14.1 +1983-04-25,10.5 +1983-04-26,12.6 +1983-04-27,13.0 +1983-04-28,8.7 +1983-04-29,10.1 +1983-04-30,12.0 +1983-05-01,12.5 +1983-05-02,13.5 +1983-05-03,13.7 +1983-05-04,13.5 +1983-05-05,10.7 +1983-05-06,13.0 +1983-05-07,11.6 +1983-05-08,13.0 +1983-05-09,11.2 +1983-05-10,13.5 +1983-05-11,12.9 +1983-05-12,6.8 +1983-05-13,10.0 +1983-05-14,14.5 +1983-05-15,11.7 +1983-05-16,6.7 +1983-05-17,4.6 +1983-05-18,4.9 +1983-05-19,7.4 +1983-05-20,8.3 +1983-05-21,7.5 +1983-05-22,6.2 +1983-05-23,7.8 +1983-05-24,13.2 +1983-05-25,11.9 +1983-05-26,6.5 +1983-05-27,8.3 +1983-05-28,12.1 +1983-05-29,9.3 +1983-05-30,7.5 +1983-05-31,9.3 +1983-06-01,11.0 +1983-06-02,10.8 +1983-06-03,5.3 +1983-06-04,7.6 +1983-06-05,5.6 +1983-06-06,7.2 +1983-06-07,9.6 +1983-06-08,7.0 +1983-06-09,8.3 +1983-06-10,7.8 +1983-06-11,4.7 +1983-06-12,6.8 +1983-06-13,7.2 +1983-06-14,8.3 +1983-06-15,9.5 +1983-06-16,4.7 +1983-06-17,3.0 +1983-06-18,1.5 +1983-06-19,2.5 +1983-06-20,6.2 +1983-06-21,11.6 +1983-06-22,6.6 +1983-06-23,6.6 +1983-06-24,8.0 +1983-06-25,7.9 +1983-06-26,3.3 +1983-06-27,3.9 +1983-06-28,6.0 +1983-06-29,4.0 +1983-06-30,5.5 +1983-07-01,8.5 +1983-07-02,9.8 +1983-07-03,9.5 +1983-07-04,7.2 +1983-07-05,8.1 +1983-07-06,8.0 +1983-07-07,8.5 +1983-07-08,8.8 +1983-07-09,8.3 +1983-07-10,2.4 +1983-07-11,4.9 +1983-07-12,5.9 +1983-07-13,6.7 +1983-07-14,8.4 +1983-07-15,6.5 +1983-07-16,7.9 +1983-07-17,4.1 +1983-07-18,5.4 +1983-07-19,7.5 +1983-07-20,3.9 +1983-07-21,2.5 +1983-07-22,5.3 +1983-07-23,6.6 +1983-07-24,0.0 +1983-07-25,0.7 +1983-07-26,7.6 +1983-07-27,12.3 +1983-07-28,9.2 +1983-07-29,9.6 +1983-07-30,9.5 +1983-07-31,10.0 +1983-08-01,7.7 +1983-08-02,8.0 +1983-08-03,8.3 +1983-08-04,8.3 +1983-08-05,4.5 +1983-08-06,6.5 +1983-08-07,9.4 +1983-08-08,9.4 +1983-08-09,10.5 +1983-08-10,10.7 +1983-08-11,9.9 +1983-08-12,7.6 +1983-08-13,5.8 +1983-08-14,8.5 +1983-08-15,13.8 +1983-08-16,14.3 +1983-08-17,8.3 +1983-08-18,5.3 +1983-08-19,3.0 +1983-08-20,5.2 +1983-08-21,10.3 +1983-08-22,11.1 +1983-08-23,10.5 +1983-08-24,9.0 +1983-08-25,13.0 +1983-08-26,6.4 +1983-08-27,8.4 +1983-08-28,6.7 +1983-08-29,8.3 +1983-08-30,11.2 +1983-08-31,10.0 +1983-09-01,10.1 +1983-09-02,10.6 +1983-09-03,10.9 +1983-09-04,5.7 +1983-09-05,9.5 +1983-09-06,10.4 +1983-09-07,11.1 +1983-09-08,12.2 +1983-09-09,10.6 +1983-09-10,8.8 +1983-09-11,9.2 +1983-09-12,5.5 +1983-09-13,7.1 +1983-09-14,6.5 +1983-09-15,4.3 +1983-09-16,5.0 +1983-09-17,11.2 +1983-09-18,7.5 +1983-09-19,12.0 +1983-09-20,13.6 +1983-09-21,8.3 +1983-09-22,8.5 +1983-09-23,12.9 +1983-09-24,7.7 +1983-09-25,7.6 +1983-09-26,3.5 +1983-09-27,10.4 +1983-09-28,15.4 +1983-09-29,10.6 +1983-09-30,9.6 +1983-10-01,9.3 +1983-10-02,13.9 +1983-10-03,7.7 +1983-10-04,9.5 +1983-10-05,7.6 +1983-10-06,6.9 +1983-10-07,6.8 +1983-10-08,5.8 +1983-10-09,6.0 +1983-10-10,8.3 +1983-10-11,9.1 +1983-10-12,12.5 +1983-10-13,13.2 +1983-10-14,16.2 +1983-10-15,12.5 +1983-10-16,11.8 +1983-10-17,10.6 +1983-10-18,10.0 +1983-10-19,12.2 +1983-10-20,8.9 +1983-10-21,10.3 +1983-10-22,7.5 +1983-10-23,11.6 +1983-10-24,12.6 +1983-10-25,12.9 +1983-10-26,11.7 +1983-10-27,14.0 +1983-10-28,12.3 +1983-10-29,9.0 +1983-10-30,9.2 +1983-10-31,9.8 +1983-11-01,11.8 +1983-11-02,10.6 +1983-11-03,12.6 +1983-11-04,11.0 +1983-11-05,8.2 +1983-11-06,7.5 +1983-11-07,13.6 +1983-11-08,14.8 +1983-11-09,10.9 +1983-11-10,7.7 +1983-11-11,10.2 +1983-11-12,10.8 +1983-11-13,10.8 +1983-11-14,12.5 +1983-11-15,13.2 +1983-11-16,8.7 +1983-11-17,5.7 +1983-11-18,9.8 +1983-11-19,7.3 +1983-11-20,10.8 +1983-11-21,10.0 +1983-11-22,16.2 +1983-11-23,15.0 +1983-11-24,14.5 +1983-11-25,15.9 +1983-11-26,14.9 +1983-11-27,14.2 +1983-11-28,15.8 +1983-11-29,17.2 +1983-11-30,17.6 +1983-12-01,12.1 +1983-12-02,11.4 +1983-12-03,13.0 +1983-12-04,13.2 +1983-12-05,12.0 +1983-12-06,15.3 +1983-12-07,12.7 +1983-12-08,12.1 +1983-12-09,13.8 +1983-12-10,10.9 +1983-12-11,12.0 +1983-12-12,16.5 +1983-12-13,15.0 +1983-12-14,11.2 +1983-12-15,13.9 +1983-12-16,15.0 +1983-12-17,14.8 +1983-12-18,15.0 +1983-12-19,13.3 +1983-12-20,20.4 +1983-12-21,18.0 +1983-12-22,12.2 +1983-12-23,16.7 +1983-12-24,13.8 +1983-12-25,17.5 +1983-12-26,15.0 +1983-12-27,13.9 +1983-12-28,11.1 +1983-12-29,16.1 +1983-12-30,20.4 +1983-12-31,18.0 +1984-01-01,19.5 +1984-01-02,17.1 +1984-01-03,17.1 +1984-01-04,12.0 +1984-01-05,11.0 +1984-01-06,16.3 +1984-01-07,16.1 +1984-01-08,13.0 +1984-01-09,13.4 +1984-01-10,15.2 +1984-01-11,12.5 +1984-01-12,14.3 +1984-01-13,16.5 +1984-01-14,18.6 +1984-01-15,18.0 +1984-01-16,18.2 +1984-01-17,11.4 +1984-01-18,11.9 +1984-01-19,12.2 +1984-01-20,14.8 +1984-01-21,13.1 +1984-01-22,12.7 +1984-01-23,10.5 +1984-01-24,13.8 +1984-01-25,18.8 +1984-01-26,13.9 +1984-01-27,11.2 +1984-01-28,10.6 +1984-01-29,14.7 +1984-01-30,13.1 +1984-01-31,12.1 +1984-02-01,14.7 +1984-02-02,11.1 +1984-02-03,13.0 +1984-02-04,15.6 +1984-02-05,14.2 +1984-02-06,15.5 +1984-02-07,18.0 +1984-02-08,15.0 +1984-02-09,15.9 +1984-02-10,15.5 +1984-02-11,15.8 +1984-02-12,16.6 +1984-02-13,13.6 +1984-02-14,13.8 +1984-02-15,14.6 +1984-02-16,15.6 +1984-02-17,16.6 +1984-02-18,14.3 +1984-02-19,16.3 +1984-02-20,18.9 +1984-02-21,18.7 +1984-02-22,14.5 +1984-02-23,16.5 +1984-02-24,14.1 +1984-02-25,13.5 +1984-02-26,11.7 +1984-02-27,15.1 +1984-02-28,11.2 +1984-02-29,13.5 +1984-03-01,12.6 +1984-03-02,8.8 +1984-03-03,10.5 +1984-03-04,12.1 +1984-03-05,14.5 +1984-03-06,19.5 +1984-03-07,14.0 +1984-03-08,13.8 +1984-03-09,10.5 +1984-03-10,13.8 +1984-03-11,11.4 +1984-03-12,15.6 +1984-03-13,11.1 +1984-03-14,12.1 +1984-03-15,14.2 +1984-03-16,10.9 +1984-03-17,14.2 +1984-03-18,13.8 +1984-03-19,15.1 +1984-03-20,14.0 +1984-03-21,12.1 +1984-03-22,13.8 +1984-03-23,16.6 +1984-03-24,17.8 +1984-03-25,9.4 +1984-03-26,10.2 +1984-03-27,7.4 +1984-03-28,8.7 +1984-03-29,14.0 +1984-03-30,15.3 +1984-03-31,11.1 +1984-04-01,9.7 +1984-04-02,10.3 +1984-04-03,9.2 +1984-04-04,8.2 +1984-04-05,9.7 +1984-04-06,12.4 +1984-04-07,12.5 +1984-04-08,9.0 +1984-04-09,9.7 +1984-04-10,10.1 +1984-04-11,11.2 +1984-04-12,12.0 +1984-04-13,11.1 +1984-04-14,10.8 +1984-04-15,12.8 +1984-04-16,9.8 +1984-04-17,13.7 +1984-04-18,11.0 +1984-04-19,13.2 +1984-04-20,13.0 +1984-04-21,10.2 +1984-04-22,13.2 +1984-04-23,9.3 +1984-04-24,11.1 +1984-04-25,10.3 +1984-04-26,8.7 +1984-04-27,11.7 +1984-04-28,12.5 +1984-04-29,6.5 +1984-04-30,9.6 +1984-05-01,13.8 +1984-05-02,14.7 +1984-05-03,9.1 +1984-05-04,4.8 +1984-05-05,3.3 +1984-05-06,3.5 +1984-05-07,5.7 +1984-05-08,5.5 +1984-05-09,7.0 +1984-05-10,9.5 +1984-05-11,9.9 +1984-05-12,4.9 +1984-05-13,6.3 +1984-05-14,4.8 +1984-05-15,6.2 +1984-05-16,7.1 +1984-05-17,7.5 +1984-05-18,9.4 +1984-05-19,8.7 +1984-05-20,9.5 +1984-05-21,12.1 +1984-05-22,9.5 +1984-05-23,9.3 +1984-05-24,8.5 +1984-05-25,8.0 +1984-05-26,9.8 +1984-05-27,6.2 +1984-05-28,7.3 +1984-05-29,10.9 +1984-05-30,10.0 +1984-05-31,8.7 +1984-06-01,9.0 +1984-06-02,10.8 +1984-06-03,12.4 +1984-06-04,7.2 +1984-06-05,7.2 +1984-06-06,11.1 +1984-06-07,9.3 +1984-06-08,10.1 +1984-06-09,3.9 +1984-06-10,5.0 +1984-06-11,8.2 +1984-06-12,2.8 +1984-06-13,4.3 +1984-06-14,8.1 +1984-06-15,11.1 +1984-06-16,4.7 +1984-06-17,5.3 +1984-06-18,10.0 +1984-06-19,5.6 +1984-06-20,2.2 +1984-06-21,7.1 +1984-06-22,8.3 +1984-06-23,8.6 +1984-06-24,10.1 +1984-06-25,8.3 +1984-06-26,7.2 +1984-06-27,7.7 +1984-06-28,7.8 +1984-06-29,9.1 +1984-06-30,9.4 +1984-07-01,7.8 +1984-07-02,2.6 +1984-07-03,2.4 +1984-07-04,3.9 +1984-07-05,1.3 +1984-07-06,2.1 +1984-07-07,7.4 +1984-07-08,7.2 +1984-07-09,8.8 +1984-07-10,8.9 +1984-07-11,8.8 +1984-07-12,8.0 +1984-07-13,0.7 +1984-07-14,0.1 +1984-07-15,0.9 +1984-07-16,7.8 +1984-07-17,7.2 +1984-07-18,8.0 +1984-07-19,4.6 +1984-07-20,5.2 +1984-07-21,5.8 +1984-07-22,6.8 +1984-07-23,8.1 +1984-07-24,7.5 +1984-07-25,5.4 +1984-07-26,4.6 +1984-07-27,6.4 +1984-07-28,9.7 +1984-07-29,7.0 +1984-07-30,10.0 +1984-07-31,10.6 +1984-08-01,11.5 +1984-08-02,10.2 +1984-08-03,11.1 +1984-08-04,11.0 +1984-08-05,8.9 +1984-08-06,9.9 +1984-08-07,11.7 +1984-08-08,11.6 +1984-08-09,9.0 +1984-08-10,6.3 +1984-08-11,8.7 +1984-08-12,8.5 +1984-08-13,8.5 +1984-08-14,8.0 +1984-08-15,6.0 +1984-08-16,8.0 +1984-08-17,8.5 +1984-08-18,7.7 +1984-08-19,8.4 +1984-08-20,9.0 +1984-08-21,8.3 +1984-08-22,6.8 +1984-08-23,9.3 +1984-08-24,6.7 +1984-08-25,9.0 +1984-08-26,7.3 +1984-08-27,6.3 +1984-08-28,7.9 +1984-08-29,5.2 +1984-08-30,9.0 +1984-08-31,11.3 +1984-09-01,9.2 +1984-09-02,11.3 +1984-09-03,7.0 +1984-09-04,8.0 +1984-09-05,4.6 +1984-09-06,8.5 +1984-09-07,9.5 +1984-09-08,9.4 +1984-09-09,10.5 +1984-09-10,9.7 +1984-09-11,4.9 +1984-09-12,8.0 +1984-09-13,5.8 +1984-09-14,5.5 +1984-09-15,10.9 +1984-09-16,11.7 +1984-09-17,9.2 +1984-09-18,8.9 +1984-09-19,11.3 +1984-09-20,8.6 +1984-09-21,6.2 +1984-09-22,6.6 +1984-09-23,9.1 +1984-09-24,6.1 +1984-09-25,7.5 +1984-09-26,10.7 +1984-09-27,6.3 +1984-09-28,5.5 +1984-09-29,6.7 +1984-09-30,4.2 +1984-10-01,11.3 +1984-10-02,16.3 +1984-10-03,10.5 +1984-10-04,10.3 +1984-10-05,7.9 +1984-10-06,7.7 +1984-10-07,16.0 +1984-10-08,14.6 +1984-10-09,12.5 +1984-10-10,8.1 +1984-10-11,12.2 +1984-10-12,17.2 +1984-10-13,9.4 +1984-10-14,8.7 +1984-10-15,5.9 +1984-10-16,4.8 +1984-10-17,7.4 +1984-10-18,9.4 +1984-10-19,9.7 +1984-10-20,9.9 +1984-10-21,6.5 +1984-10-22,9.8 +1984-10-23,18.2 +1984-10-24,11.3 +1984-10-25,9.1 +1984-10-26,9.6 +1984-10-27,13.5 +1984-10-28,10.7 +1984-10-29,10.0 +1984-10-30,8.5 +1984-10-31,12.6 +1984-11-01,16.6 +1984-11-02,11.6 +1984-11-03,12.2 +1984-11-04,11.2 +1984-11-05,9.2 +1984-11-06,9.9 +1984-11-07,11.9 +1984-11-08,15.6 +1984-11-09,19.0 +1984-11-10,12.8 +1984-11-11,12.2 +1984-11-12,12.0 +1984-11-13,11.1 +1984-11-14,11.8 +1984-11-15,7.6 +1984-11-16,13.0 +1984-11-17,12.7 +1984-11-18,16.0 +1984-11-19,14.8 +1984-11-20,14.2 +1984-11-21,10.0 +1984-11-22,8.8 +1984-11-23,11.6 +1984-11-24,8.6 +1984-11-25,14.6 +1984-11-26,24.3 +1984-11-27,11.6 +1984-11-28,10.8 +1984-11-29,12.0 +1984-11-30,11.0 +1984-12-01,12.6 +1984-12-02,10.8 +1984-12-03,9.1 +1984-12-04,11.0 +1984-12-05,13.0 +1984-12-06,12.8 +1984-12-07,9.9 +1984-12-08,11.6 +1984-12-09,10.5 +1984-12-10,15.9 +1984-12-11,12.2 +1984-12-12,13.0 +1984-12-13,12.5 +1984-12-14,12.5 +1984-12-15,11.4 +1984-12-16,12.1 +1984-12-17,16.8 +1984-12-18,12.1 +1984-12-19,11.3 +1984-12-20,10.4 +1984-12-21,14.2 +1984-12-22,11.4 +1984-12-23,13.7 +1984-12-24,16.5 +1984-12-25,12.8 +1984-12-26,12.2 +1984-12-27,12.0 +1984-12-28,12.6 +1984-12-29,16.0 +1984-12-30,16.4 +1985-01-01,13.3 +1985-01-02,15.2 +1985-01-03,13.1 +1985-01-04,12.7 +1985-01-05,14.6 +1985-01-06,11.0 +1985-01-07,13.2 +1985-01-08,12.2 +1985-01-09,14.4 +1985-01-10,13.7 +1985-01-11,14.5 +1985-01-12,14.1 +1985-01-13,14.4 +1985-01-14,19.7 +1985-01-15,16.5 +1985-01-16,15.9 +1985-01-17,11.8 +1985-01-18,12.0 +1985-01-19,11.4 +1985-01-20,14.4 +1985-01-21,12.4 +1985-01-22,15.1 +1985-01-23,15.6 +1985-01-24,15.2 +1985-01-25,12.8 +1985-01-26,13.3 +1985-01-27,17.5 +1985-01-28,15.4 +1985-01-29,13.5 +1985-01-30,16.7 +1985-01-31,15.2 +1985-02-01,14.9 +1985-02-02,10.2 +1985-02-03,13.6 +1985-02-04,19.0 +1985-02-05,15.7 +1985-02-06,18.0 +1985-02-07,14.8 +1985-02-08,13.9 +1985-02-09,13.0 +1985-02-10,15.3 +1985-02-11,14.3 +1985-02-12,15.6 +1985-02-13,16.0 +1985-02-14,14.9 +1985-02-15,11.1 +1985-02-16,14.8 +1985-02-17,13.0 +1985-02-18,12.2 +1985-02-19,10.9 +1985-02-20,14.6 +1985-02-21,16.6 +1985-02-22,18.1 +1985-02-23,13.4 +1985-02-24,10.3 +1985-02-25,13.6 +1985-02-26,13.8 +1985-02-27,10.3 +1985-02-28,11.0 +1985-03-01,14.3 +1985-03-02,15.5 +1985-03-03,14.7 +1985-03-04,12.7 +1985-03-05,10.7 +1985-03-06,12.6 +1985-03-07,9.8 +1985-03-08,13.2 +1985-03-09,15.2 +1985-03-10,16.6 +1985-03-11,21.0 +1985-03-12,22.4 +1985-03-13,17.0 +1985-03-14,21.7 +1985-03-15,21.4 +1985-03-16,18.6 +1985-03-17,16.2 +1985-03-18,16.8 +1985-03-19,17.0 +1985-03-20,18.4 +1985-03-21,17.2 +1985-03-22,18.4 +1985-03-23,18.8 +1985-03-24,16.5 +1985-03-25,13.3 +1985-03-26,12.2 +1985-03-27,11.3 +1985-03-28,13.8 +1985-03-29,16.6 +1985-03-30,14.0 +1985-03-31,14.3 +1985-04-01,16.4 +1985-04-02,11.9 +1985-04-03,15.7 +1985-04-04,17.6 +1985-04-05,17.5 +1985-04-06,15.9 +1985-04-07,16.2 +1985-04-08,16.0 +1985-04-09,15.9 +1985-04-10,16.2 +1985-04-11,16.2 +1985-04-12,19.5 +1985-04-13,18.2 +1985-04-14,21.8 +1985-04-15,15.1 +1985-04-16,11.0 +1985-04-17,8.1 +1985-04-18,9.5 +1985-04-19,9.3 +1985-04-20,10.6 +1985-04-21,6.3 +1985-04-22,8.6 +1985-04-23,6.8 +1985-04-24,8.7 +1985-04-25,8.4 +1985-04-26,9.3 +1985-04-27,10.0 +1985-04-28,10.5 +1985-04-29,12.0 +1985-04-30,10.1 +1985-05-01,9.4 +1985-05-02,10.1 +1985-05-03,8.0 +1985-05-04,10.6 +1985-05-05,13.6 +1985-05-06,15.4 +1985-05-07,9.0 +1985-05-08,10.4 +1985-05-09,11.0 +1985-05-10,12.1 +1985-05-11,13.4 +1985-05-12,11.3 +1985-05-13,6.7 +1985-05-14,9.8 +1985-05-15,10.8 +1985-05-16,7.8 +1985-05-17,4.5 +1985-05-18,7.6 +1985-05-19,6.9 +1985-05-20,7.5 +1985-05-21,8.5 +1985-05-22,5.5 +1985-05-23,9.5 +1985-05-24,7.3 +1985-05-25,5.4 +1985-05-26,5.5 +1985-05-27,8.1 +1985-05-28,11.2 +1985-05-29,13.4 +1985-05-30,11.6 +1985-05-31,10.1 +1985-06-01,4.3 +1985-06-02,5.5 +1985-06-03,4.4 +1985-06-04,5.9 +1985-06-05,5.7 +1985-06-06,8.2 +1985-06-07,8.2 +1985-06-08,4.2 +1985-06-09,6.5 +1985-06-10,10.0 +1985-06-11,8.8 +1985-06-12,6.6 +1985-06-13,7.8 +1985-06-14,10.1 +1985-06-15,7.1 +1985-06-16,7.7 +1985-06-17,8.5 +1985-06-18,7.3 +1985-06-19,6.9 +1985-06-20,8.4 +1985-06-21,7.1 +1985-06-22,6.3 +1985-06-23,0.6 +1985-06-24,1.6 +1985-06-25,7.0 +1985-06-26,8.3 +1985-06-27,8.0 +1985-06-28,10.2 +1985-06-29,10.6 +1985-06-30,10.4 +1985-07-01,11.6 +1985-07-02,11.0 +1985-07-03,10.7 +1985-07-04,7.3 +1985-07-05,4.2 +1985-07-06,4.7 +1985-07-07,5.6 +1985-07-08,7.7 +1985-07-09,7.5 +1985-07-10,4.9 +1985-07-11,5.9 +1985-07-12,7.8 +1985-07-13,5.8 +1985-07-14,7.0 +1985-07-15,8.4 +1985-07-16,6.2 +1985-07-17,7.5 +1985-07-18,4.8 +1985-07-19,3.3 +1985-07-20,3.2 +1985-07-21,7.0 +1985-07-22,8.4 +1985-07-23,0.3 +1985-07-24,0.3 +1985-07-25,2.1 +1985-07-26,8.5 +1985-07-27,1.4 +1985-07-28,4.1 +1985-07-29,10.3 +1985-07-30,6.6 +1985-07-31,6.1 +1985-08-01,7.0 +1985-08-02,5.1 +1985-08-03,6.3 +1985-08-04,6.9 +1985-08-05,11.4 +1985-08-06,10.4 +1985-08-07,10.3 +1985-08-08,9.2 +1985-08-09,7.2 +1985-08-10,7.5 +1985-08-11,4.0 +1985-08-12,5.6 +1985-08-13,6.7 +1985-08-14,8.4 +1985-08-15,11.0 +1985-08-16,8.4 +1985-08-17,8.8 +1985-08-18,8.6 +1985-08-19,8.3 +1985-08-20,4.0 +1985-08-21,3.6 +1985-08-22,5.7 +1985-08-23,10.6 +1985-08-24,6.9 +1985-08-25,10.0 +1985-08-26,9.8 +1985-08-27,7.2 +1985-08-28,10.5 +1985-08-29,3.6 +1985-08-30,5.3 +1985-08-31,8.4 +1985-09-01,10.3 +1985-09-02,7.9 +1985-09-03,8.5 +1985-09-04,7.9 +1985-09-05,8.0 +1985-09-06,9.8 +1985-09-07,6.7 +1985-09-08,4.8 +1985-09-09,9.9 +1985-09-10,12.8 +1985-09-11,10.9 +1985-09-12,11.7 +1985-09-13,11.7 +1985-09-14,11.0 +1985-09-15,8.2 +1985-09-16,7.5 +1985-09-17,5.4 +1985-09-18,7.2 +1985-09-19,9.7 +1985-09-20,8.4 +1985-09-21,9.0 +1985-09-22,8.7 +1985-09-23,6.6 +1985-09-24,11.6 +1985-09-25,13.1 +1985-09-26,6.7 +1985-09-27,6.5 +1985-09-28,7.7 +1985-09-29,8.7 +1985-09-30,7.2 +1985-10-01,10.5 +1985-10-02,8.6 +1985-10-03,7.2 +1985-10-04,11.4 +1985-10-05,16.2 +1985-10-06,6.1 +1985-10-07,9.6 +1985-10-08,11.1 +1985-10-09,13.6 +1985-10-10,10.7 +1985-10-11,14.7 +1985-10-12,11.6 +1985-10-13,7.3 +1985-10-14,8.0 +1985-10-15,9.6 +1985-10-16,16.0 +1985-10-17,15.1 +1985-10-18,12.8 +1985-10-19,6.2 +1985-10-20,7.1 +1985-10-21,8.4 +1985-10-22,10.0 +1985-10-23,12.7 +1985-10-24,10.0 +1985-10-25,10.2 +1985-10-26,6.5 +1985-10-27,9.2 +1985-10-28,11.9 +1985-10-29,14.7 +1985-10-30,11.4 +1985-10-31,6.8 +1985-11-01,7.4 +1985-11-02,11.2 +1985-11-03,9.2 +1985-11-04,12.6 +1985-11-05,16.0 +1985-11-06,17.1 +1985-11-07,15.3 +1985-11-08,13.3 +1985-11-09,15.4 +1985-11-10,13.2 +1985-11-11,14.4 +1985-11-12,14.0 +1985-11-13,15.5 +1985-11-14,21.0 +1985-11-15,10.0 +1985-11-16,9.6 +1985-11-17,12.0 +1985-11-18,12.2 +1985-11-19,11.3 +1985-11-20,13.2 +1985-11-21,10.5 +1985-11-22,10.1 +1985-11-23,8.8 +1985-11-24,13.7 +1985-11-25,16.2 +1985-11-26,16.0 +1985-11-27,14.0 +1985-11-28,13.7 +1985-11-29,12.5 +1985-11-30,12.8 +1985-12-01,12.3 +1985-12-02,15.2 +1985-12-03,15.0 +1985-12-04,16.4 +1985-12-05,16.1 +1985-12-06,14.6 +1985-12-07,18.2 +1985-12-08,16.4 +1985-12-09,16.6 +1985-12-10,14.7 +1985-12-11,15.8 +1985-12-12,14.1 +1985-12-13,13.5 +1985-12-14,13.6 +1985-12-15,13.7 +1985-12-16,13.6 +1985-12-17,12.1 +1985-12-18,12.7 +1985-12-19,13.3 +1985-12-20,14.2 +1985-12-21,15.0 +1985-12-22,13.7 +1985-12-23,12.0 +1985-12-24,13.1 +1985-12-25,13.2 +1985-12-26,13.3 +1985-12-27,11.5 +1985-12-28,10.8 +1985-12-29,12.0 +1985-12-30,16.3 +1985-12-31,14.4 +1986-01-01,12.9 +1986-01-02,13.8 +1986-01-03,10.6 +1986-01-04,12.6 +1986-01-05,13.7 +1986-01-06,12.6 +1986-01-07,13.1 +1986-01-08,15.4 +1986-01-09,11.9 +1986-01-10,13.8 +1986-01-11,14.4 +1986-01-12,15.2 +1986-01-13,12.5 +1986-01-14,12.2 +1986-01-15,16.1 +1986-01-16,14.6 +1986-01-17,11.6 +1986-01-18,13.1 +1986-01-19,12.8 +1986-01-20,15.2 +1986-01-21,13.8 +1986-01-22,15.0 +1986-01-23,13.5 +1986-01-24,11.8 +1986-01-25,15.3 +1986-01-26,13.5 +1986-01-27,15.3 +1986-01-28,13.8 +1986-01-29,15.8 +1986-01-30,17.4 +1986-01-31,15.3 +1986-02-01,14.6 +1986-02-02,14.8 +1986-02-03,10.7 +1986-02-04,11.6 +1986-02-05,13.6 +1986-02-06,14.4 +1986-02-07,11.8 +1986-02-08,15.8 +1986-02-09,16.0 +1986-02-10,11.8 +1986-02-11,14.5 +1986-02-12,10.7 +1986-02-13,14.2 +1986-02-14,19.5 +1986-02-15,21.4 +1986-02-16,17.9 +1986-02-17,17.4 +1986-02-18,12.7 +1986-02-19,13.8 +1986-02-20,14.0 +1986-02-21,15.0 +1986-02-22,14.5 +1986-02-23,13.1 +1986-02-24,11.4 +1986-02-25,12.5 +1986-02-26,12.0 +1986-02-27,13.4 +1986-02-28,14.4 +1986-03-01,17.7 +1986-03-02,13.9 +1986-03-03,13.3 +1986-03-04,14.6 +1986-03-05,16.4 +1986-03-06,16.8 +1986-03-07,20.0 +1986-03-08,12.5 +1986-03-09,12.7 +1986-03-10,11.7 +1986-03-11,12.7 +1986-03-12,8.6 +1986-03-13,11.9 +1986-03-14,16.0 +1986-03-15,15.2 +1986-03-16,13.4 +1986-03-17,11.6 +1986-03-18,11.1 +1986-03-19,15.6 +1986-03-20,17.0 +1986-03-21,18.5 +1986-03-22,17.4 +1986-03-23,16.5 +1986-03-24,16.2 +1986-03-25,16.1 +1986-03-26,13.2 +1986-03-27,18.0 +1986-03-28,12.8 +1986-03-29,11.7 +1986-03-30,16.7 +1986-03-31,15.6 +1986-04-01,10.2 +1986-04-02,10.3 +1986-04-03,15.0 +1986-04-04,18.0 +1986-04-05,13.8 +1986-04-06,10.5 +1986-04-07,11.8 +1986-04-08,7.2 +1986-04-09,11.6 +1986-04-10,7.4 +1986-04-11,14.2 +1986-04-12,12.2 +1986-04-13,9.0 +1986-04-14,12.3 +1986-04-15,19.7 +1986-04-16,12.8 +1986-04-17,12.4 +1986-04-18,12.0 +1986-04-19,12.0 +1986-04-20,11.1 +1986-04-21,12.7 +1986-04-22,14.2 +1986-04-23,11.6 +1986-04-24,12.0 +1986-04-25,11.5 +1986-04-26,8.3 +1986-04-27,10.5 +1986-04-28,9.0 +1986-04-29,6.9 +1986-04-30,9.4 +1986-05-01,11.1 +1986-05-02,9.1 +1986-05-03,7.7 +1986-05-04,10.0 +1986-05-05,10.4 +1986-05-06,8.0 +1986-05-07,9.8 +1986-05-08,12.4 +1986-05-09,12.9 +1986-05-10,12.3 +1986-05-11,6.9 +1986-05-12,10.5 +1986-05-13,11.0 +1986-05-14,9.7 +1986-05-15,11.1 +1986-05-16,11.5 +1986-05-17,13.4 +1986-05-18,10.9 +1986-05-19,12.0 +1986-05-20,12.1 +1986-05-21,10.4 +1986-05-22,10.0 +1986-05-23,9.6 +1986-05-24,11.3 +1986-05-25,8.5 +1986-05-26,6.3 +1986-05-27,8.2 +1986-05-28,10.7 +1986-05-29,10.3 +1986-05-30,9.5 +1986-05-31,10.9 +1986-06-01,10.9 +1986-06-02,4.3 +1986-06-03,5.2 +1986-06-04,11.0 +1986-06-05,11.6 +1986-06-06,10.6 +1986-06-07,9.4 +1986-06-08,10.0 +1986-06-09,9.6 +1986-06-10,9.5 +1986-06-11,9.7 +1986-06-12,9.6 +1986-06-13,7.0 +1986-06-14,7.0 +1986-06-15,6.8 +1986-06-16,6.9 +1986-06-17,8.0 +1986-06-18,7.6 +1986-06-19,8.6 +1986-06-20,5.7 +1986-06-21,5.5 +1986-06-22,5.7 +1986-06-23,5.7 +1986-06-24,6.6 +1986-06-25,6.0 +1986-06-26,6.9 +1986-06-27,7.7 +1986-06-28,8.0 +1986-06-29,3.9 +1986-06-30,0.8 +1986-07-01,2.8 +1986-07-02,8.0 +1986-07-03,9.8 +1986-07-04,11.4 +1986-07-05,8.6 +1986-07-06,5.2 +1986-07-07,6.6 +1986-07-08,5.7 +1986-07-09,4.6 +1986-07-10,5.8 +1986-07-11,7.0 +1986-07-12,4.8 +1986-07-13,4.4 +1986-07-14,4.4 +1986-07-15,7.9 +1986-07-16,10.6 +1986-07-17,5.0 +1986-07-18,7.6 +1986-07-19,9.2 +1986-07-20,9.7 +1986-07-21,8.8 +1986-07-22,6.8 +1986-07-23,9.4 +1986-07-24,11.0 +1986-07-25,2.5 +1986-07-26,2.1 +1986-07-27,5.4 +1986-07-28,6.2 +1986-07-29,7.8 +1986-07-30,7.4 +1986-07-31,9.3 +1986-08-01,9.3 +1986-08-02,9.5 +1986-08-03,8.5 +1986-08-04,10.0 +1986-08-05,7.7 +1986-08-06,9.3 +1986-08-07,9.1 +1986-08-08,3.5 +1986-08-09,3.6 +1986-08-10,2.5 +1986-08-11,1.7 +1986-08-12,2.7 +1986-08-13,2.9 +1986-08-14,5.3 +1986-08-15,7.7 +1986-08-16,9.1 +1986-08-17,9.4 +1986-08-18,7.3 +1986-08-19,8.4 +1986-08-20,9.2 +1986-08-21,6.6 +1986-08-22,9.7 +1986-08-23,12.4 +1986-08-24,10.2 +1986-08-25,5.9 +1986-08-26,7.1 +1986-08-27,7.5 +1986-08-28,9.7 +1986-08-29,12.2 +1986-08-30,5.6 +1986-08-31,5.4 +1986-09-01,8.3 +1986-09-02,10.6 +1986-09-03,9.1 +1986-09-04,11.3 +1986-09-05,10.9 +1986-09-06,8.9 +1986-09-07,6.3 +1986-09-08,9.0 +1986-09-09,6.1 +1986-09-10,9.1 +1986-09-11,9.6 +1986-09-12,6.0 +1986-09-13,10.0 +1986-09-14,11.0 +1986-09-15,6.2 +1986-09-16,8.3 +1986-09-17,11.3 +1986-09-18,11.3 +1986-09-19,6.7 +1986-09-20,6.6 +1986-09-21,11.4 +1986-09-22,6.9 +1986-09-23,10.6 +1986-09-24,8.6 +1986-09-25,11.3 +1986-09-26,12.5 +1986-09-27,9.9 +1986-09-28,6.9 +1986-09-29,5.5 +1986-09-30,7.8 +1986-10-01,11.0 +1986-10-02,16.2 +1986-10-03,9.9 +1986-10-04,8.7 +1986-10-05,10.5 +1986-10-06,12.2 +1986-10-07,10.6 +1986-10-08,8.3 +1986-10-09,5.5 +1986-10-10,9.0 +1986-10-11,6.4 +1986-10-12,7.2 +1986-10-13,12.9 +1986-10-14,12.0 +1986-10-15,7.3 +1986-10-16,9.7 +1986-10-17,8.4 +1986-10-18,14.7 +1986-10-19,9.5 +1986-10-20,7.9 +1986-10-21,6.8 +1986-10-22,12.6 +1986-10-23,5.2 +1986-10-24,7.5 +1986-10-25,8.7 +1986-10-26,7.6 +1986-10-27,9.0 +1986-10-28,7.2 +1986-10-29,10.7 +1986-10-30,13.1 +1986-10-31,13.9 +1986-11-01,10.8 +1986-11-02,10.4 +1986-11-03,9.1 +1986-11-04,16.0 +1986-11-05,21.0 +1986-11-06,16.2 +1986-11-07,8.6 +1986-11-08,9.2 +1986-11-09,12.5 +1986-11-10,9.7 +1986-11-11,12.5 +1986-11-12,10.3 +1986-11-13,12.0 +1986-11-14,11.0 +1986-11-15,14.8 +1986-11-16,15.0 +1986-11-17,15.3 +1986-11-18,10.3 +1986-11-19,10.7 +1986-11-20,10.5 +1986-11-21,8.9 +1986-11-22,8.1 +1986-11-23,11.5 +1986-11-24,12.8 +1986-11-25,9.1 +1986-11-26,14.6 +1986-11-27,11.6 +1986-11-28,11.2 +1986-11-29,12.6 +1986-11-30,7.5 +1986-12-01,11.0 +1986-12-02,14.5 +1986-12-03,18.5 +1986-12-04,15.4 +1986-12-05,13.1 +1986-12-06,16.3 +1986-12-07,20.2 +1986-12-08,11.5 +1986-12-09,12.4 +1986-12-10,10.9 +1986-12-11,12.7 +1986-12-12,12.2 +1986-12-13,12.4 +1986-12-14,9.8 +1986-12-15,8.5 +1986-12-16,14.7 +1986-12-17,12.0 +1986-12-18,10.3 +1986-12-19,11.0 +1986-12-20,10.2 +1986-12-21,12.6 +1986-12-22,11.6 +1986-12-23,9.7 +1986-12-24,13.4 +1986-12-25,10.5 +1986-12-26,14.7 +1986-12-27,14.6 +1986-12-28,14.2 +1986-12-29,13.2 +1986-12-30,11.7 +1986-12-31,17.2 +1987-01-01,12.3 +1987-01-02,13.8 +1987-01-03,15.3 +1987-01-04,15.6 +1987-01-05,16.2 +1987-01-06,16.3 +1987-01-07,16.8 +1987-01-08,11.0 +1987-01-09,8.5 +1987-01-10,13.2 +1987-01-11,13.0 +1987-01-12,12.4 +1987-01-13,13.0 +1987-01-14,16.6 +1987-01-15,12.0 +1987-01-16,12.4 +1987-01-17,15.0 +1987-01-18,11.8 +1987-01-19,11.6 +1987-01-20,12.2 +1987-01-21,13.7 +1987-01-22,11.2 +1987-01-23,12.4 +1987-01-24,11.5 +1987-01-25,13.8 +1987-01-26,15.7 +1987-01-27,12.9 +1987-01-28,11.5 +1987-01-29,11.0 +1987-01-30,12.7 +1987-01-31,14.9 +1987-02-01,16.5 +1987-02-02,12.8 +1987-02-03,12.7 +1987-02-04,12.7 +1987-02-05,11.6 +1987-02-06,13.3 +1987-02-07,15.2 +1987-02-08,16.4 +1987-02-09,11.9 +1987-02-10,15.1 +1987-02-11,10.6 +1987-02-12,13.6 +1987-02-13,12.1 +1987-02-14,16.0 +1987-02-15,16.8 +1987-02-16,16.6 +1987-02-17,15.6 +1987-02-18,15.2 +1987-02-19,17.7 +1987-02-20,21.0 +1987-02-21,13.4 +1987-02-22,10.5 +1987-02-23,9.5 +1987-02-24,12.0 +1987-02-25,10.4 +1987-02-26,11.5 +1987-02-27,13.2 +1987-02-28,15.0 +1987-03-01,14.1 +1987-03-02,12.4 +1987-03-03,13.4 +1987-03-04,12.5 +1987-03-05,14.3 +1987-03-06,17.6 +1987-03-07,10.4 +1987-03-08,9.9 +1987-03-09,10.2 +1987-03-10,11.3 +1987-03-11,9.5 +1987-03-12,11.8 +1987-03-13,11.5 +1987-03-14,10.5 +1987-03-15,10.8 +1987-03-16,13.0 +1987-03-17,18.5 +1987-03-18,18.7 +1987-03-19,15.0 +1987-03-20,13.0 +1987-03-21,11.3 +1987-03-22,13.0 +1987-03-23,13.3 +1987-03-24,11.0 +1987-03-25,10.3 +1987-03-26,13.0 +1987-03-27,12.3 +1987-03-28,15.6 +1987-03-29,10.2 +1987-03-30,10.8 +1987-03-31,12.0 +1987-04-01,13.3 +1987-04-02,11.7 +1987-04-03,12.5 +1987-04-04,13.7 +1987-04-05,14.9 +1987-04-06,20.2 +1987-04-07,16.3 +1987-04-08,13.9 +1987-04-09,10.1 +1987-04-10,7.3 +1987-04-11,14.0 +1987-04-12,17.7 +1987-04-13,16.3 +1987-04-14,10.6 +1987-04-15,9.7 +1987-04-16,7.8 +1987-04-17,10.4 +1987-04-18,10.4 +1987-04-19,14.1 +1987-04-20,7.1 +1987-04-21,8.1 +1987-04-22,7.8 +1987-04-23,10.6 +1987-04-24,9.1 +1987-04-25,9.0 +1987-04-26,11.9 +1987-04-27,17.1 +1987-04-28,16.8 +1987-04-29,13.5 +1987-04-30,11.6 +1987-05-01,7.0 +1987-05-02,9.7 +1987-05-03,9.9 +1987-05-04,11.2 +1987-05-05,11.3 +1987-05-06,11.8 +1987-05-07,9.9 +1987-05-08,7.1 +1987-05-09,9.6 +1987-05-10,9.8 +1987-05-11,10.6 +1987-05-12,12.8 +1987-05-13,16.5 +1987-05-14,11.7 +1987-05-15,12.3 +1987-05-16,12.2 +1987-05-17,11.8 +1987-05-18,10.7 +1987-05-19,10.2 +1987-05-20,10.0 +1987-05-21,8.3 +1987-05-22,6.6 +1987-05-23,9.5 +1987-05-24,12.3 +1987-05-25,7.6 +1987-05-26,9.3 +1987-05-27,5.0 +1987-05-28,4.3 +1987-05-29,6.4 +1987-05-30,10.8 +1987-05-31,7.8 +1987-06-01,8.5 +1987-06-02,9.7 +1987-06-03,10.0 +1987-06-04,11.0 +1987-06-05,10.2 +1987-06-06,6.6 +1987-06-07,6.1 +1987-06-08,5.9 +1987-06-09,8.9 +1987-06-10,13.0 +1987-06-11,12.6 +1987-06-12,5.4 +1987-06-13,6.0 +1987-06-14,7.8 +1987-06-15,9.0 +1987-06-16,4.2 +1987-06-17,3.0 +1987-06-18,4.5 +1987-06-19,6.2 +1987-06-20,11.9 +1987-06-21,11.8 +1987-06-22,9.4 +1987-06-23,9.6 +1987-06-24,9.4 +1987-06-25,7.0 +1987-06-26,8.9 +1987-06-27,9.3 +1987-06-28,6.8 +1987-06-29,7.5 +1987-06-30,8.0 +1987-07-01,8.3 +1987-07-02,2.7 +1987-07-03,3.9 +1987-07-04,4.1 +1987-07-05,5.0 +1987-07-06,5.8 +1987-07-07,4.4 +1987-07-08,4.1 +1987-07-09,5.8 +1987-07-10,9.1 +1987-07-11,7.9 +1987-07-12,5.0 +1987-07-13,2.8 +1987-07-14,4.7 +1987-07-15,8.9 +1987-07-16,5.4 +1987-07-17,7.1 +1987-07-18,9.0 +1987-07-19,9.4 +1987-07-20,6.3 +1987-07-21,7.0 +1987-07-22,6.4 +1987-07-23,6.7 +1987-07-24,1.5 +1987-07-25,2.9 +1987-07-26,4.8 +1987-07-27,6.3 +1987-07-28,5.7 +1987-07-29,7.0 +1987-07-30,8.8 +1987-07-31,8.7 +1987-08-01,9.0 +1987-08-02,9.6 +1987-08-03,8.0 +1987-08-04,8.4 +1987-08-05,8.1 +1987-08-06,9.0 +1987-08-07,5.3 +1987-08-08,8.9 +1987-08-09,8.7 +1987-08-10,4.9 +1987-08-11,7.0 +1987-08-12,7.5 +1987-08-13,7.0 +1987-08-14,9.1 +1987-08-15,11.8 +1987-08-16,9.9 +1987-08-17,5.6 +1987-08-18,4.2 +1987-08-19,4.3 +1987-08-20,8.0 +1987-08-21,5.1 +1987-08-22,9.4 +1987-08-23,9.1 +1987-08-24,9.7 +1987-08-25,10.6 +1987-08-26,8.6 +1987-08-27,10.1 +1987-08-28,11.0 +1987-08-29,9.7 +1987-08-30,5.0 +1987-08-31,6.1 +1987-09-01,5.4 +1987-09-02,5.8 +1987-09-03,7.3 +1987-09-04,6.3 +1987-09-05,4.8 +1987-09-06,7.6 +1987-09-07,8.1 +1987-09-08,9.5 +1987-09-09,10.3 +1987-09-10,7.0 +1987-09-11,9.0 +1987-09-12,10.2 +1987-09-13,6.8 +1987-09-14,9.3 +1987-09-15,9.8 +1987-09-16,10.7 +1987-09-17,7.8 +1987-09-18,9.2 +1987-09-19,15.0 +1987-09-20,7.8 +1987-09-21,5.3 +1987-09-22,9.5 +1987-09-23,7.6 +1987-09-24,14.0 +1987-09-25,14.9 +1987-09-26,14.9 +1987-09-27,19.2 +1987-09-28,17.0 +1987-09-29,13.0 +1987-09-30,11.2 +1987-10-01,9.5 +1987-10-02,10.3 +1987-10-03,9.3 +1987-10-04,11.3 +1987-10-05,6.5 +1987-10-06,12.0 +1987-10-07,8.3 +1987-10-08,8.7 +1987-10-09,8.7 +1987-10-10,10.2 +1987-10-11,6.9 +1987-10-12,4.9 +1987-10-13,10.0 +1987-10-14,7.6 +1987-10-15,14.5 +1987-10-16,13.2 +1987-10-17,9.9 +1987-10-18,10.1 +1987-10-19,11.3 +1987-10-20,10.4 +1987-10-21,10.9 +1987-10-22,9.2 +1987-10-23,10.5 +1987-10-24,11.4 +1987-10-25,13.5 +1987-10-26,9.8 +1987-10-27,13.1 +1987-10-28,9.7 +1987-10-29,11.4 +1987-10-30,9.9 +1987-10-31,14.4 +1987-11-01,19.0 +1987-11-02,23.0 +1987-11-03,15.4 +1987-11-04,9.6 +1987-11-05,10.8 +1987-11-06,12.1 +1987-11-07,11.0 +1987-11-08,12.6 +1987-11-09,14.7 +1987-11-10,11.1 +1987-11-11,10.1 +1987-11-12,11.4 +1987-11-13,13.0 +1987-11-14,11.9 +1987-11-15,9.5 +1987-11-16,13.5 +1987-11-17,15.2 +1987-11-18,18.4 +1987-11-19,24.1 +1987-11-20,14.1 +1987-11-21,10.7 +1987-11-22,8.7 +1987-11-23,13.3 +1987-11-24,11.6 +1987-11-25,9.9 +1987-11-26,10.8 +1987-11-27,11.5 +1987-11-28,10.0 +1987-11-29,13.9 +1987-11-30,13.6 +1987-12-01,11.9 +1987-12-02,11.1 +1987-12-03,8.2 +1987-12-04,9.4 +1987-12-05,12.7 +1987-12-06,11.6 +1987-12-07,11.0 +1987-12-08,11.3 +1987-12-09,13.4 +1987-12-10,14.9 +1987-12-11,15.2 +1987-12-12,13.9 +1987-12-13,15.0 +1987-12-14,16.2 +1987-12-15,17.7 +1987-12-16,20.5 +1987-12-17,14.7 +1987-12-18,12.5 +1987-12-19,10.9 +1987-12-20,12.8 +1987-12-21,12.7 +1987-12-22,11.2 +1987-12-23,11.4 +1987-12-24,11.2 +1987-12-25,12.1 +1987-12-26,12.7 +1987-12-27,16.2 +1987-12-28,14.2 +1987-12-29,14.3 +1987-12-30,13.3 +1987-12-31,16.7 +1988-01-01,15.3 +1988-01-02,14.3 +1988-01-03,13.5 +1988-01-04,15.0 +1988-01-05,13.6 +1988-01-06,15.2 +1988-01-07,17.0 +1988-01-08,18.7 +1988-01-09,16.5 +1988-01-10,17.4 +1988-01-11,18.3 +1988-01-12,18.3 +1988-01-13,22.4 +1988-01-14,21.4 +1988-01-15,20.9 +1988-01-16,17.6 +1988-01-17,15.5 +1988-01-18,16.6 +1988-01-19,16.2 +1988-01-20,15.6 +1988-01-21,14.5 +1988-01-22,14.0 +1988-01-23,15.6 +1988-01-24,12.3 +1988-01-25,11.6 +1988-01-26,12.6 +1988-01-27,14.9 +1988-01-28,17.3 +1988-01-29,21.4 +1988-01-30,23.4 +1988-01-31,14.4 +1988-02-01,14.1 +1988-02-02,15.0 +1988-02-03,14.5 +1988-02-04,15.1 +1988-02-05,13.9 +1988-02-06,13.4 +1988-02-07,9.2 +1988-02-08,12.5 +1988-02-09,15.1 +1988-02-10,12.1 +1988-02-11,14.5 +1988-02-12,16.3 +1988-02-13,16.5 +1988-02-14,14.9 +1988-02-15,13.2 +1988-02-16,11.8 +1988-02-17,13.6 +1988-02-18,16.2 +1988-02-19,14.1 +1988-02-20,13.5 +1988-02-21,15.0 +1988-02-22,14.8 +1988-02-23,16.2 +1988-02-24,16.2 +1988-02-25,13.3 +1988-02-26,15.3 +1988-02-27,18.4 +1988-02-28,16.2 +1988-02-29,16.3 +1988-03-01,12.4 +1988-03-02,15.6 +1988-03-03,14.9 +1988-03-04,14.8 +1988-03-05,12.7 +1988-03-06,14.2 +1988-03-07,16.8 +1988-03-08,16.7 +1988-03-09,16.2 +1988-03-10,14.5 +1988-03-11,10.0 +1988-03-12,12.6 +1988-03-13,11.9 +1988-03-14,11.8 +1988-03-15,13.4 +1988-03-16,14.5 +1988-03-17,15.7 +1988-03-18,15.3 +1988-03-19,13.9 +1988-03-20,13.7 +1988-03-21,15.1 +1988-03-22,15.6 +1988-03-23,14.4 +1988-03-24,13.9 +1988-03-25,16.2 +1988-03-26,16.7 +1988-03-27,15.5 +1988-03-28,16.4 +1988-03-29,17.5 +1988-03-30,18.2 +1988-03-31,16.1 +1988-04-01,16.5 +1988-04-02,14.6 +1988-04-03,16.4 +1988-04-04,13.6 +1988-04-05,15.9 +1988-04-06,11.9 +1988-04-07,14.7 +1988-04-08,9.4 +1988-04-09,6.6 +1988-04-10,7.9 +1988-04-11,11.0 +1988-04-12,15.7 +1988-04-13,15.2 +1988-04-14,15.9 +1988-04-15,10.6 +1988-04-16,8.3 +1988-04-17,8.6 +1988-04-18,12.7 +1988-04-19,10.5 +1988-04-20,12.0 +1988-04-21,11.1 +1988-04-22,13.0 +1988-04-23,12.4 +1988-04-24,13.3 +1988-04-25,15.9 +1988-04-26,12.0 +1988-04-27,13.7 +1988-04-28,17.6 +1988-04-29,14.3 +1988-04-30,13.7 +1988-05-01,15.2 +1988-05-02,14.5 +1988-05-03,14.9 +1988-05-04,15.5 +1988-05-05,16.4 +1988-05-06,14.5 +1988-05-07,12.6 +1988-05-08,13.6 +1988-05-09,11.2 +1988-05-10,11.0 +1988-05-11,12.0 +1988-05-12,6.8 +1988-05-13,10.6 +1988-05-14,13.1 +1988-05-15,13.5 +1988-05-16,11.7 +1988-05-17,13.2 +1988-05-18,12.0 +1988-05-19,10.4 +1988-05-20,10.0 +1988-05-21,8.2 +1988-05-22,9.4 +1988-05-23,10.3 +1988-05-24,8.1 +1988-05-25,8.7 +1988-05-26,12.6 +1988-05-27,10.9 +1988-05-28,8.7 +1988-05-29,9.3 +1988-05-30,6.3 +1988-05-31,7.8 +1988-06-01,10.0 +1988-06-02,11.0 +1988-06-03,11.1 +1988-06-04,12.6 +1988-06-05,10.2 +1988-06-06,11.1 +1988-06-07,8.7 +1988-06-08,9.5 +1988-06-09,9.7 +1988-06-10,8.2 +1988-06-11,5.0 +1988-06-12,6.5 +1988-06-13,12.1 +1988-06-14,8.9 +1988-06-15,6.1 +1988-06-16,2.8 +1988-06-17,3.7 +1988-06-18,6.8 +1988-06-19,6.6 +1988-06-20,7.0 +1988-06-21,7.3 +1988-06-22,7.9 +1988-06-23,10.6 +1988-06-24,8.1 +1988-06-25,6.7 +1988-06-26,8.0 +1988-06-27,10.0 +1988-06-28,6.7 +1988-06-29,9.4 +1988-06-30,9.3 +1988-07-01,6.0 +1988-07-02,5.8 +1988-07-03,4.9 +1988-07-04,5.0 +1988-07-05,8.4 +1988-07-06,12.3 +1988-07-07,13.0 +1988-07-08,11.4 +1988-07-09,6.8 +1988-07-10,7.6 +1988-07-11,12.4 +1988-07-12,7.1 +1988-07-13,7.5 +1988-07-14,10.0 +1988-07-15,5.3 +1988-07-16,6.3 +1988-07-17,8.0 +1988-07-18,8.3 +1988-07-19,9.3 +1988-07-20,9.5 +1988-07-21,5.6 +1988-07-22,7.0 +1988-07-23,8.5 +1988-07-24,8.5 +1988-07-25,8.2 +1988-07-26,8.5 +1988-07-27,9.6 +1988-07-28,9.7 +1988-07-29,7.1 +1988-07-30,8.4 +1988-07-31,9.2 +1988-08-01,9.8 +1988-08-02,8.1 +1988-08-03,9.4 +1988-08-04,10.0 +1988-08-05,5.1 +1988-08-06,6.7 +1988-08-07,6.9 +1988-08-08,6.8 +1988-08-09,8.6 +1988-08-10,9.1 +1988-08-11,3.9 +1988-08-12,4.8 +1988-08-13,8.4 +1988-08-14,11.6 +1988-08-15,12.1 +1988-08-16,12.4 +1988-08-17,10.0 +1988-08-18,10.1 +1988-08-19,9.7 +1988-08-20,11.7 +1988-08-21,7.9 +1988-08-22,8.6 +1988-08-23,7.7 +1988-08-24,5.8 +1988-08-25,8.7 +1988-08-26,10.6 +1988-08-27,6.7 +1988-08-28,8.8 +1988-08-29,9.7 +1988-08-30,9.0 +1988-08-31,11.8 +1988-09-01,15.2 +1988-09-02,10.0 +1988-09-03,10.5 +1988-09-04,5.5 +1988-09-05,9.4 +1988-09-06,8.8 +1988-09-07,5.3 +1988-09-08,13.0 +1988-09-09,15.2 +1988-09-10,13.2 +1988-09-11,11.5 +1988-09-12,6.8 +1988-09-13,4.7 +1988-09-14,5.2 +1988-09-15,6.8 +1988-09-16,10.7 +1988-09-17,10.1 +1988-09-18,10.0 +1988-09-19,9.8 +1988-09-20,5.5 +1988-09-21,13.5 +1988-09-22,16.6 +1988-09-23,8.4 +1988-09-24,8.2 +1988-09-25,11.1 +1988-09-26,10.8 +1988-09-27,8.8 +1988-09-28,10.8 +1988-09-29,8.7 +1988-09-30,12.4 +1988-10-01,9.0 +1988-10-02,13.5 +1988-10-03,14.7 +1988-10-04,10.9 +1988-10-05,8.5 +1988-10-06,6.0 +1988-10-07,12.7 +1988-10-08,11.1 +1988-10-09,8.7 +1988-10-10,12.3 +1988-10-11,13.3 +1988-10-12,5.6 +1988-10-13,13.7 +1988-10-14,8.5 +1988-10-15,11.2 +1988-10-16,8.7 +1988-10-17,11.7 +1988-10-18,12.5 +1988-10-19,8.2 +1988-10-20,15.6 +1988-10-21,10.3 +1988-10-22,11.4 +1988-10-23,9.7 +1988-10-24,6.3 +1988-10-25,14.3 +1988-10-26,11.3 +1988-10-27,7.3 +1988-10-28,12.8 +1988-10-29,11.9 +1988-10-30,14.3 +1988-10-31,11.6 +1988-11-01,13.2 +1988-11-02,15.5 +1988-11-03,14.1 +1988-11-04,9.5 +1988-11-05,7.2 +1988-11-06,11.8 +1988-11-07,16.8 +1988-11-08,12.5 +1988-11-09,9.4 +1988-11-10,11.9 +1988-11-11,10.3 +1988-11-12,16.9 +1988-11-13,17.5 +1988-11-14,7.5 +1988-11-15,8.6 +1988-11-16,11.1 +1988-11-17,11.5 +1988-11-18,10.7 +1988-11-19,15.7 +1988-11-20,12.8 +1988-11-21,13.0 +1988-11-22,12.9 +1988-11-23,14.3 +1988-11-24,13.7 +1988-11-25,12.1 +1988-11-26,11.9 +1988-11-27,11.8 +1988-11-28,11.4 +1988-11-29,10.3 +1988-11-30,11.7 +1988-12-01,12.0 +1988-12-02,17.4 +1988-12-03,16.8 +1988-12-04,16.2 +1988-12-05,13.0 +1988-12-06,12.5 +1988-12-07,12.4 +1988-12-08,16.1 +1988-12-09,20.2 +1988-12-10,14.3 +1988-12-11,11.0 +1988-12-12,14.4 +1988-12-13,15.7 +1988-12-14,19.7 +1988-12-15,20.7 +1988-12-16,23.9 +1988-12-17,16.6 +1988-12-18,17.5 +1988-12-19,14.9 +1988-12-20,13.6 +1988-12-21,11.9 +1988-12-22,15.2 +1988-12-23,17.3 +1988-12-24,19.8 +1988-12-25,15.8 +1988-12-26,9.5 +1988-12-27,12.9 +1988-12-28,12.9 +1988-12-29,14.8 +1988-12-30,14.1 +1989-01-01,14.3 +1989-01-02,17.4 +1989-01-03,18.5 +1989-01-04,16.8 +1989-01-05,11.5 +1989-01-06,9.5 +1989-01-07,12.2 +1989-01-08,15.7 +1989-01-09,16.3 +1989-01-10,13.6 +1989-01-11,12.6 +1989-01-12,13.8 +1989-01-13,12.1 +1989-01-14,13.4 +1989-01-15,17.3 +1989-01-16,19.4 +1989-01-17,16.6 +1989-01-18,13.9 +1989-01-19,13.1 +1989-01-20,16.0 +1989-01-21,14.5 +1989-01-22,15.0 +1989-01-23,12.6 +1989-01-24,12.5 +1989-01-25,15.2 +1989-01-26,16.2 +1989-01-27,16.5 +1989-01-28,20.1 +1989-01-29,20.6 +1989-01-30,16.9 +1989-01-31,16.5 +1989-02-01,16.1 +1989-02-02,14.4 +1989-02-03,16.3 +1989-02-04,15.7 +1989-02-05,14.2 +1989-02-06,13.2 +1989-02-07,16.8 +1989-02-08,18.5 +1989-02-09,16.7 +1989-02-10,15.3 +1989-02-11,15.9 +1989-02-12,15.2 +1989-02-13,17.5 +1989-02-14,18.3 +1989-02-15,19.4 +1989-02-16,19.4 +1989-02-17,19.5 +1989-02-18,20.5 +1989-02-19,15.7 +1989-02-20,15.0 +1989-02-21,16.1 +1989-02-22,14.3 +1989-02-23,13.0 +1989-02-24,16.2 +1989-02-25,17.7 +1989-02-26,13.2 +1989-02-27,15.8 +1989-02-28,18.5 +1989-03-01,20.4 +1989-03-02,22.0 +1989-03-03,19.7 +1989-03-04,19.6 +1989-03-05,20.3 +1989-03-06,18.3 +1989-03-07,18.9 +1989-03-08,20.3 +1989-03-09,21.4 +1989-03-10,18.3 +1989-03-11,17.8 +1989-03-12,17.7 +1989-03-13,12.8 +1989-03-14,15.1 +1989-03-15,15.0 +1989-03-16,14.8 +1989-03-17,12.0 +1989-03-18,12.5 +1989-03-19,15.0 +1989-03-20,17.1 +1989-03-21,17.3 +1989-03-22,16.9 +1989-03-23,16.5 +1989-03-24,13.6 +1989-03-25,13.2 +1989-03-26,9.4 +1989-03-27,9.5 +1989-03-28,11.8 +1989-03-29,10.4 +1989-03-30,9.7 +1989-03-31,12.6 +1989-04-01,13.3 +1989-04-02,15.1 +1989-04-03,14.2 +1989-04-04,14.2 +1989-04-05,19.2 +1989-04-06,12.6 +1989-04-07,14.2 +1989-04-08,11.9 +1989-04-09,13.9 +1989-04-10,13.5 +1989-04-11,15.3 +1989-04-12,13.9 +1989-04-13,14.0 +1989-04-14,12.9 +1989-04-15,8.5 +1989-04-16,11.4 +1989-04-17,10.9 +1989-04-18,12.0 +1989-04-19,8.6 +1989-04-20,9.0 +1989-04-21,9.6 +1989-04-22,10.2 +1989-04-23,9.8 +1989-04-24,8.3 +1989-04-25,11.0 +1989-04-26,11.9 +1989-04-27,14.0 +1989-04-28,15.8 +1989-04-29,14.5 +1989-04-30,13.2 +1989-05-01,14.2 +1989-05-02,14.6 +1989-05-03,11.8 +1989-05-04,14.4 +1989-05-05,10.4 +1989-05-06,10.3 +1989-05-07,10.8 +1989-05-08,10.5 +1989-05-09,9.5 +1989-05-10,12.5 +1989-05-11,13.7 +1989-05-12,12.7 +1989-05-13,11.9 +1989-05-14,11.4 +1989-05-15,9.7 +1989-05-16,8.3 +1989-05-17,8.1 +1989-05-18,11.7 +1989-05-19,11.6 +1989-05-20,7.4 +1989-05-21,5.2 +1989-05-22,11.0 +1989-05-23,9.5 +1989-05-24,9.2 +1989-05-25,10.7 +1989-05-26,9.0 +1989-05-27,10.2 +1989-05-28,10.3 +1989-05-29,12.1 +1989-05-30,13.2 +1989-05-31,6.6 +1989-06-01,2.3 +1989-06-02,1.4 +1989-06-03,2.1 +1989-06-04,6.6 +1989-06-05,8.9 +1989-06-06,7.8 +1989-06-07,9.0 +1989-06-08,10.3 +1989-06-09,7.9 +1989-06-10,7.2 +1989-06-11,8.6 +1989-06-12,8.8 +1989-06-13,6.2 +1989-06-14,9.5 +1989-06-15,10.2 +1989-06-16,9.7 +1989-06-17,11.2 +1989-06-18,10.2 +1989-06-19,10.1 +1989-06-20,8.1 +1989-06-21,6.6 +1989-06-22,5.0 +1989-06-23,4.7 +1989-06-24,5.3 +1989-06-25,4.5 +1989-06-26,2.3 +1989-06-27,1.4 +1989-06-28,0.5 +1989-06-29,2.4 +1989-06-30,8.0 +1989-07-01,6.0 +1989-07-02,7.1 +1989-07-03,9.7 +1989-07-04,6.9 +1989-07-05,5.3 +1989-07-06,7.0 +1989-07-07,6.2 +1989-07-08,7.0 +1989-07-09,9.7 +1989-07-10,8.0 +1989-07-11,8.5 +1989-07-12,7.1 +1989-07-13,7.5 +1989-07-14,3.3 +1989-07-15,1.8 +1989-07-16,2.6 +1989-07-17,5.3 +1989-07-18,5.8 +1989-07-19,5.8 +1989-07-20,7.2 +1989-07-21,5.3 +1989-07-22,1.6 +1989-07-23,3.1 +1989-07-24,5.3 +1989-07-25,7.7 +1989-07-26,4.2 +1989-07-27,5.5 +1989-07-28,9.0 +1989-07-29,11.2 +1989-07-30,8.0 +1989-07-31,7.6 +1989-08-01,3.7 +1989-08-02,7.5 +1989-08-03,8.1 +1989-08-04,8.4 +1989-08-05,7.1 +1989-08-06,7.6 +1989-08-07,7.6 +1989-08-08,5.6 +1989-08-09,7.0 +1989-08-10,10.5 +1989-08-11,7.3 +1989-08-12,7.8 +1989-08-13,5.8 +1989-08-14,3.8 +1989-08-15,5.8 +1989-08-16,6.7 +1989-08-17,6.6 +1989-08-18,6.6 +1989-08-19,9.0 +1989-08-20,8.1 +1989-08-21,5.1 +1989-08-22,8.6 +1989-08-23,7.0 +1989-08-24,5.5 +1989-08-25,7.4 +1989-08-26,6.2 +1989-08-27,4.2 +1989-08-28,6.3 +1989-08-29,7.0 +1989-08-30,4.0 +1989-08-31,8.0 +1989-09-01,8.8 +1989-09-02,8.8 +1989-09-03,6.1 +1989-09-04,8.6 +1989-09-05,8.9 +1989-09-06,7.8 +1989-09-07,5.0 +1989-09-08,7.0 +1989-09-09,13.3 +1989-09-10,7.9 +1989-09-11,7.5 +1989-09-12,8.3 +1989-09-13,7.2 +1989-09-14,6.5 +1989-09-15,8.9 +1989-09-16,7.4 +1989-09-17,9.9 +1989-09-18,9.3 +1989-09-19,10.6 +1989-09-20,8.6 +1989-09-21,7.2 +1989-09-22,12.6 +1989-09-23,7.8 +1989-09-24,6.3 +1989-09-25,9.2 +1989-09-26,5.8 +1989-09-27,9.0 +1989-09-28,5.0 +1989-09-29,11.9 +1989-09-30,13.4 +1989-10-01,10.5 +1989-10-02,6.2 +1989-10-03,5.1 +1989-10-04,9.5 +1989-10-05,11.7 +1989-10-06,9.2 +1989-10-07,7.3 +1989-10-08,9.7 +1989-10-09,9.4 +1989-10-10,10.0 +1989-10-11,10.9 +1989-10-12,11.0 +1989-10-13,10.9 +1989-10-14,8.0 +1989-10-15,11.2 +1989-10-16,7.5 +1989-10-17,7.2 +1989-10-18,13.2 +1989-10-19,12.9 +1989-10-20,9.4 +1989-10-21,10.2 +1989-10-22,9.5 +1989-10-23,12.4 +1989-10-24,10.2 +1989-10-25,13.4 +1989-10-26,11.6 +1989-10-27,8.0 +1989-10-28,9.0 +1989-10-29,9.3 +1989-10-30,13.5 +1989-10-31,8.0 +1989-11-01,8.1 +1989-11-02,10.0 +1989-11-03,8.5 +1989-11-04,12.5 +1989-11-05,15.0 +1989-11-06,13.3 +1989-11-07,11.0 +1989-11-08,11.9 +1989-11-09,8.3 +1989-11-10,9.7 +1989-11-11,11.3 +1989-11-12,12.5 +1989-11-13,9.4 +1989-11-14,11.4 +1989-11-15,13.2 +1989-11-16,13.8 +1989-11-17,16.0 +1989-11-18,10.9 +1989-11-19,11.9 +1989-11-20,12.4 +1989-11-21,13.2 +1989-11-22,15.5 +1989-11-23,21.6 +1989-11-24,14.9 +1989-11-25,14.4 +1989-11-26,12.9 +1989-11-27,13.1 +1989-11-28,14.0 +1989-11-29,17.9 +1989-11-30,17.7 +1989-12-01,16.3 +1989-12-02,18.3 +1989-12-03,13.7 +1989-12-04,13.3 +1989-12-05,10.6 +1989-12-06,14.1 +1989-12-07,16.0 +1989-12-08,16.5 +1989-12-09,14.1 +1989-12-10,18.7 +1989-12-11,16.2 +1989-12-12,14.8 +1989-12-13,12.6 +1989-12-14,10.4 +1989-12-15,12.2 +1989-12-16,12.6 +1989-12-17,12.1 +1989-12-18,17.3 +1989-12-19,16.4 +1989-12-20,12.6 +1989-12-21,12.3 +1989-12-22,11.8 +1989-12-23,12.0 +1989-12-24,12.7 +1989-12-25,16.4 +1989-12-26,16.0 +1989-12-27,13.3 +1989-12-28,11.7 +1989-12-29,10.4 +1989-12-30,14.4 +1989-12-31,12.7 +1990-01-01,14.8 +1990-01-02,13.3 +1990-01-03,15.6 +1990-01-04,14.5 +1990-01-05,14.3 +1990-01-06,15.3 +1990-01-07,16.4 +1990-01-08,14.8 +1990-01-09,17.4 +1990-01-10,18.8 +1990-01-11,22.1 +1990-01-12,19.0 +1990-01-13,15.5 +1990-01-14,15.8 +1990-01-15,14.7 +1990-01-16,10.7 +1990-01-17,11.5 +1990-01-18,15.0 +1990-01-19,14.5 +1990-01-20,14.5 +1990-01-21,13.3 +1990-01-22,14.3 +1990-01-23,14.3 +1990-01-24,20.5 +1990-01-25,15.0 +1990-01-26,17.1 +1990-01-27,16.9 +1990-01-28,16.9 +1990-01-29,13.6 +1990-01-30,16.4 +1990-01-31,16.1 +1990-02-01,12.0 +1990-02-02,12.2 +1990-02-03,14.8 +1990-02-04,14.8 +1990-02-05,14.4 +1990-02-06,12.9 +1990-02-07,13.4 +1990-02-08,15.9 +1990-02-09,16.1 +1990-02-10,17.6 +1990-02-11,15.6 +1990-02-12,15.0 +1990-02-13,13.0 +1990-02-14,14.1 +1990-02-15,17.3 +1990-02-16,15.7 +1990-02-17,18.6 +1990-02-18,12.7 +1990-02-19,14.0 +1990-02-20,13.7 +1990-02-21,16.3 +1990-02-22,20.0 +1990-02-23,17.0 +1990-02-24,15.2 +1990-02-25,16.5 +1990-02-26,16.5 +1990-02-27,17.3 +1990-02-28,19.1 +1990-03-01,19.3 +1990-03-02,17.3 +1990-03-03,19.0 +1990-03-04,19.8 +1990-03-05,19.3 +1990-03-06,17.2 +1990-03-07,14.2 +1990-03-08,10.3 +1990-03-09,13.0 +1990-03-10,15.3 +1990-03-11,15.0 +1990-03-12,12.1 +1990-03-13,9.2 +1990-03-14,11.0 +1990-03-15,15.0 +1990-03-16,11.6 +1990-03-17,11.6 +1990-03-18,15.1 +1990-03-19,15.0 +1990-03-20,13.6 +1990-03-21,12.5 +1990-03-22,14.3 +1990-03-23,16.0 +1990-03-24,17.4 +1990-03-25,16.9 +1990-03-26,18.0 +1990-03-27,20.6 +1990-03-28,14.2 +1990-03-29,10.9 +1990-03-30,11.9 +1990-03-31,13.3 +1990-04-01,15.3 +1990-04-02,14.7 +1990-04-03,11.0 +1990-04-04,12.2 +1990-04-05,14.2 +1990-04-06,17.0 +1990-04-07,15.8 +1990-04-08,15.2 +1990-04-09,15.1 +1990-04-10,14.7 +1990-04-11,18.5 +1990-04-12,16.4 +1990-04-13,18.4 +1990-04-14,15.1 +1990-04-15,9.9 +1990-04-16,10.2 +1990-04-17,12.6 +1990-04-18,13.2 +1990-04-19,11.5 +1990-04-20,13.8 +1990-04-21,14.5 +1990-04-22,14.7 +1990-04-23,11.2 +1990-04-24,12.7 +1990-04-25,13.7 +1990-04-26,11.5 +1990-04-27,10.4 +1990-04-28,8.9 +1990-04-29,11.1 +1990-04-30,9.5 +1990-05-01,13.0 +1990-05-02,13.9 +1990-05-03,12.6 +1990-05-04,14.3 +1990-05-05,16.0 +1990-05-06,13.3 +1990-05-07,7.0 +1990-05-08,4.9 +1990-05-09,6.9 +1990-05-10,13.7 +1990-05-11,10.6 +1990-05-12,12.3 +1990-05-13,11.1 +1990-05-14,10.2 +1990-05-15,9.5 +1990-05-16,8.9 +1990-05-17,13.4 +1990-05-18,9.1 +1990-05-19,9.4 +1990-05-20,8.7 +1990-05-21,5.8 +1990-05-22,4.5 +1990-05-23,7.2 +1990-05-24,10.0 +1990-05-25,10.5 +1990-05-26,10.7 +1990-05-27,8.2 +1990-05-28,6.1 +1990-05-29,4.5 +1990-05-30,6.1 +1990-05-31,9.8 +1990-06-01,9.7 +1990-06-02,8.2 +1990-06-03,8.4 +1990-06-04,8.5 +1990-06-05,10.4 +1990-06-06,6.8 +1990-06-07,6.0 +1990-06-08,6.6 +1990-06-09,7.8 +1990-06-10,10.3 +1990-06-11,7.2 +1990-06-12,7.4 +1990-06-13,11.4 +1990-06-14,5.4 +1990-06-15,4.4 +1990-06-16,6.4 +1990-06-17,9.3 +1990-06-18,7.7 +1990-06-19,8.1 +1990-06-20,8.3 +1990-06-21,9.1 +1990-06-22,7.7 +1990-06-23,10.6 +1990-06-24,8.2 +1990-06-25,7.9 +1990-06-26,5.2 +1990-06-27,5.9 +1990-06-28,3.7 +1990-06-29,5.6 +1990-06-30,9.4 +1990-07-01,7.4 +1990-07-02,7.3 +1990-07-03,7.7 +1990-07-04,7.7 +1990-07-05,9.3 +1990-07-06,4.4 +1990-07-07,5.7 +1990-07-08,10.2 +1990-07-09,10.2 +1990-07-10,9.3 +1990-07-11,5.4 +1990-07-12,5.0 +1990-07-13,7.6 +1990-07-14,9.6 +1990-07-15,10.4 +1990-07-16,11.2 +1990-07-17,9.1 +1990-07-18,11.2 +1990-07-19,6.8 +1990-07-20,8.3 +1990-07-21,9.7 +1990-07-22,9.6 +1990-07-23,9.8 +1990-07-24,10.8 +1990-07-25,9.2 +1990-07-26,6.5 +1990-07-27,8.1 +1990-07-28,7.3 +1990-07-29,7.9 +1990-07-30,6.0 +1990-07-31,5.0 +1990-08-01,6.8 +1990-08-02,9.8 +1990-08-03,5.7 +1990-08-04,8.6 +1990-08-05,10.6 +1990-08-06,7.8 +1990-08-07,7.7 +1990-08-08,8.6 +1990-08-09,6.5 +1990-08-10,6.9 +1990-08-11,6.4 +1990-08-12,8.5 +1990-08-13,7.8 +1990-08-14,9.3 +1990-08-15,8.4 +1990-08-16,7.8 +1990-08-17,7.4 +1990-08-18,7.7 +1990-08-19,8.9 +1990-08-20,9.7 +1990-08-21,9.9 +1990-08-22,6.1 +1990-08-23,6.6 +1990-08-24,7.6 +1990-08-25,7.4 +1990-08-26,8.0 +1990-08-27,2.1 +1990-08-28,5.9 +1990-08-29,11.6 +1990-08-30,8.6 +1990-08-31,7.9 +1990-09-01,6.0 +1990-09-02,9.5 +1990-09-03,8.6 +1990-09-04,7.6 +1990-09-05,10.4 +1990-09-06,10.3 +1990-09-07,7.5 +1990-09-08,3.0 +1990-09-09,5.3 +1990-09-10,10.5 +1990-09-11,14.6 +1990-09-12,12.6 +1990-09-13,9.8 +1990-09-14,7.2 +1990-09-15,10.1 +1990-09-16,10.4 +1990-09-17,3.7 +1990-09-18,7.3 +1990-09-19,11.6 +1990-09-20,16.3 +1990-09-21,9.6 +1990-09-22,6.8 +1990-09-23,5.2 +1990-09-24,10.6 +1990-09-25,16.3 +1990-09-26,9.8 +1990-09-27,4.6 +1990-09-28,11.1 +1990-09-29,8.7 +1990-09-30,10.0 +1990-10-01,11.3 +1990-10-02,10.5 +1990-10-03,9.9 +1990-10-04,11.0 +1990-10-05,14.0 +1990-10-06,9.2 +1990-10-07,9.8 +1990-10-08,6.0 +1990-10-09,9.8 +1990-10-10,9.2 +1990-10-11,11.8 +1990-10-12,10.3 +1990-10-13,7.5 +1990-10-14,7.7 +1990-10-15,15.8 +1990-10-16,14.6 +1990-10-17,10.5 +1990-10-18,11.3 +1990-10-19,10.9 +1990-10-20,6.4 +1990-10-21,10.9 +1990-10-22,9.0 +1990-10-23,10.9 +1990-10-24,12.4 +1990-10-25,11.6 +1990-10-26,13.3 +1990-10-27,14.4 +1990-10-28,18.4 +1990-10-29,13.6 +1990-10-30,14.9 +1990-10-31,14.8 +1990-11-01,15.4 +1990-11-02,11.8 +1990-11-03,13.0 +1990-11-04,11.1 +1990-11-05,12.5 +1990-11-06,18.3 +1990-11-07,19.2 +1990-11-08,15.4 +1990-11-09,13.1 +1990-11-10,11.5 +1990-11-11,8.6 +1990-11-12,12.6 +1990-11-13,13.8 +1990-11-14,14.6 +1990-11-15,13.2 +1990-11-16,12.3 +1990-11-17,8.8 +1990-11-18,10.7 +1990-11-19,9.9 +1990-11-20,8.3 +1990-11-21,15.0 +1990-11-22,12.2 +1990-11-23,10.5 +1990-11-24,11.1 +1990-11-25,13.0 +1990-11-26,12.9 +1990-11-27,8.8 +1990-11-28,14.7 +1990-11-29,14.7 +1990-11-30,12.7 +1990-12-01,13.3 +1990-12-02,13.2 +1990-12-03,16.2 +1990-12-04,17.3 +1990-12-05,20.5 +1990-12-06,20.2 +1990-12-07,19.4 +1990-12-08,15.5 +1990-12-09,14.1 +1990-12-10,11.0 +1990-12-11,11.1 +1990-12-12,14.0 +1990-12-13,11.4 +1990-12-14,12.5 +1990-12-15,13.4 +1990-12-16,13.6 +1990-12-17,13.9 +1990-12-18,17.2 +1990-12-19,14.7 +1990-12-20,15.4 +1990-12-21,13.1 +1990-12-22,13.2 +1990-12-23,13.9 +1990-12-24,10.0 +1990-12-25,12.9 +1990-12-26,14.6 +1990-12-27,14.0 +1990-12-28,13.6 +1990-12-29,13.5 +1990-12-30,15.7 +1990-12-31,13.0 \ No newline at end of file diff --git a/escalation/test_app_deploy_data/hist_penguins.json b/escalation/test_app_deploy_data/hist_penguins.json index 35abd1c..0333526 100644 --- a/escalation/test_app_deploy_data/hist_penguins.json +++ b/escalation/test_app_deploy_data/hist_penguins.json @@ -23,17 +23,6 @@ } }, "selectable_data_dict": { - "axis": [ - { - "column": "x", - "entries": [ - "penguin_size:culmen_length_mm", - "penguin_size:flipper_length_mm", - "penguin_size:body_mass_g", - "penguin_size:culmen_depth_mm" - ] - } - ], "groupby": { "entries": [ "penguin_size:sex", diff --git a/escalation/test_app_deploy_data/test_app_sql_config.json b/escalation/test_app_deploy_data/main_config.json similarity index 72% rename from escalation/test_app_deploy_data/test_app_sql_config.json rename to escalation/test_app_deploy_data/main_config.json index 4d45721..a899570 100644 --- a/escalation/test_app_deploy_data/test_app_sql_config.json +++ b/escalation/test_app_deploy_data/main_config.json @@ -17,6 +17,15 @@ "hist_penguins.json" ] }, + { + "webpage_label": "seaborn penguins!", + "url_endpoint": "seaborn_penguin", + "graphic_config_files": [ + "big_penguins_seaborn_scatter.json", + "big_penguins_seaborn_multi_axis.json" + + ] + }, { "webpage_label": "Radio Penguins", "url_endpoint": "radio_penguins", diff --git a/escalation/test_app_deploy_data/miserables.json b/escalation/test_app_deploy_data/miserables.json new file mode 100644 index 0000000..e91d714 --- /dev/null +++ b/escalation/test_app_deploy_data/miserables.json @@ -0,0 +1,21 @@ +{ + "plot_manager": "cytoscape", + "data_sources": { + "main_data_source": {"data_source_type": "miserables_edges"} + }, + "title": "What is the co-occurrence of charaters in Les Miserables (by chapter)?", + "brief_desc": "Just asking the question we were all thinking.", + "plot_specific_info": { + "source": "miserables_edges:start", + "target": "miserables_edges:end", + "edge_id": "miserables_edges:edge_id", + "layout": {"name": "cose"}, + "style": [{ + "selector": "node", "style": {"label": "data(id)"}}], + "element_style": [{ + "style_name": "width", + "group": "edges", + "column": "miserables_edges:count" + }] + } +} \ No newline at end of file diff --git a/escalation/test_app_deploy_data/models.py b/escalation/test_app_deploy_data/models.py index cc52cad..5a6af83 100644 --- a/escalation/test_app_deploy_data/models.py +++ b/escalation/test_app_deploy_data/models.py @@ -1,5 +1,5 @@ # coding: utf-8 -from sqlalchemy import Boolean, Column, DateTime, Float, Integer, Text, ARRAY, String +from sqlalchemy import Boolean, Column, DateTime, Float, Integer, Text from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() @@ -33,6 +33,38 @@ class MeanPenguinStat(Base): delta_13_c = Column(Float(53)) +class MiserablesEdges(Base): + __tablename__ = "miserables_edges" + + upload_id = Column(Integer, primary_key=True, nullable=False) + row_index = Column(Integer, primary_key=True, nullable=False) + start = Column(Text) + end = Column(Text) + count = Column(Integer) + edge_id = Column(Integer) + + +class MiserablesNodes(Base): + __tablename__ = "miserables_nodes" + + upload_id = Column(Integer, primary_key=True, nullable=False) + row_index = Column(Integer, primary_key=True, nullable=False) + name = Column(Text) + color = Column(Integer) + + +class PenguinLterSmall(Base): + __tablename__ = "penguin_lter_small" + + upload_id = Column(Integer, primary_key=True, nullable=False) + row_index = Column(Integer, primary_key=True, nullable=False) + date_egg = Column(DateTime) + island = Column(Text) + study_name = Column(Text) + region_culmen_list = Column(Text) + clutch_completion = Column(Integer) + + class PenguinSize(Base): __tablename__ = "penguin_size" @@ -63,13 +95,10 @@ class PenguinSizeSmall(Base): sex = Column(Text) -class PenguinLterSmall(Base): - __tablename__ = "penguin_lter_small" +class Temperature(Base): + __tablename__ = "temperature" upload_id = Column(Integer, primary_key=True, nullable=False) row_index = Column(Integer, primary_key=True, nullable=False) - study_name = Column(Text) - island = Column(Text) - date_egg = Column(DateTime) - region_culmen_list = Column(ARRAY(String)) - clutch_completion = Column(Boolean) + Date = Column(DateTime) + Temp = Column(Float(53)) diff --git a/escalation/test_app_deploy_data/radio_penguins.json b/escalation/test_app_deploy_data/radio_penguins.json index 5ae2220..7479979 100644 --- a/escalation/test_app_deploy_data/radio_penguins.json +++ b/escalation/test_app_deploy_data/radio_penguins.json @@ -21,35 +21,15 @@ "type": "scatter", "x": "penguin_size:culmen_length_mm", "y": "mean_penguin_stat:delta_15_n", - "mode": "markers" - } - ] - }, - "visualization_options": { - "hover_data": {"column": ["penguin_size:sex", "penguin_size:culmen_length_mm"]}, - "groupby": {"column": ["penguin_size:sex"]}, - "aggregate": { - "column": ["mean_penguin_stat:delta_15_n"], - "aggregations": {"y": "avg"} - } - }, - "selectable_data_dict": { - "axis": [ - { - "column": "x", - "entries": [ - "penguin_size:culmen_length_mm", - "penguin_size:flipper_length_mm", - "penguin_size:body_mass_g", - "penguin_size:culmen_depth_mm" - ] - }, - { - "column": "y", - "entries": [ - "mean_penguin_stat:delta_15_n", - "mean_penguin_stat:delta_13_c" - ] + "mode": "markers", + "hovertext": ["penguin_size:sex", "penguin_size:culmen_length_mm"], + "transforms": { + "groupby": {"groups": ["penguin_size:sex"]}, + "aggregate": [{ + "groups": ["mean_penguin_stat:delta_15_n"], + "aggregations": {"y": "avg"} + }] + } } ] } diff --git a/escalation/test_app_deploy_data/sql_tables_from_penguins_csvs.sh b/escalation/test_app_deploy_data/sql_tables_from_penguins_csvs.sh index bc52899..d597e52 100755 --- a/escalation/test_app_deploy_data/sql_tables_from_penguins_csvs.sh +++ b/escalation/test_app_deploy_data/sql_tables_from_penguins_csvs.sh @@ -10,10 +10,14 @@ MAIN_DATABASE=escalation # write to the TESTING database. export SQLALCHEMY_DATABASE_URI=postgresql+psycopg2://escalation:escalation_pwd@localhost:5432/$TESTING_DATABASE +python scripts/csv_to_sql.py miserables_edges test_app_deploy_data/data/miserables_edges/miserables_edges.csv replace setup_script "test setup" +python scripts/csv_to_sql.py miserables_nodes test_app_deploy_data/data/miserables_nodes/miserables_nodes.csv replace setup_script "test setup" +python scripts/csv_to_sql.py temperature test_app_deploy_data/data/temperature/temperature.csv replace setup_script "test setup" python scripts/csv_to_sql.py penguin_size test_app_deploy_data/data/penguin_size/penguin_size.csv replace setup_script "test setup" python scripts/csv_to_sql.py penguin_size test_app_deploy_data/data/penguin_size/penguin_size_2.csv append setup_script "test setup" python scripts/csv_to_sql.py mean_penguin_stat test_app_deploy_data/data/mean_penguin_stat/mean_penguin_stat.csv replace setup_script "test setup" python scripts/csv_to_sql.py penguin_size_small test_app_deploy_data/data/penguin_size_small/penguin_size_small.csv replace setup_script "test setup" +python scripts/csv_to_sql.py penguin_lter_small test_app_deploy_data/data/penguin_lter_small/penguin_lter_small.csv replace setup_script "test setup" sqlacodegen $SQLALCHEMY_DATABASE_URI --outfile test_app_deploy_data/models.py --noinflect diff --git a/escalation/tests/conftest.py b/escalation/tests/conftest.py index a321688..84f8484 100644 --- a/escalation/tests/conftest.py +++ b/escalation/tests/conftest.py @@ -13,9 +13,11 @@ from flask import current_app from sqlalchemy import create_engine from sqlalchemy.engine.url import URL +from werkzeug.datastructures import FileStorage from database.sql_handler import SqlDataInventory, SqlHandler from graphics.plotly_plot import LAYOUT, TITLE +from tests.test_file_uploads import TEST_FILENAME from utility.constants import * from app_setup import configure_backend, create_app from utility.constants import ( @@ -42,6 +44,19 @@ PEARL_HARBOR = datetime(1941, 12, 7, 7, 55, 0) +@pytest.fixture() +def addendum_dict(): + addendum_dict = { + "graphic_0": { + "filter_0": ["MALE"], + "filter_1": ["Torgersen", "Dream"], + "numerical_filter_0_max_value": ["4"], + "numerical_filter_0_min_value": [""], + } + } + return addendum_dict + + @pytest.fixture() def rebuild_test_database(test_app_client_sql_backed, mocker): # drop all tables associated with the testing app Sqlalchemy Base @@ -71,10 +86,11 @@ def rebuild_test_database(test_app_client_sql_backed, mocker): @pytest.fixture() def test_app_client_sql_backed(main_json_sql_backend_fixture): - flask_app = create_app(db_uri=TESTING_DB_URI) + # set an env other than DEVELOPMENT, which is used as a gate for some features + flask_app = create_app(db_uri=TESTING_DB_URI, env="testing") flask_app.config[APP_CONFIG_JSON] = MappingProxyType(main_json_sql_backend_fixture) flask_app.config[CONFIG_FILE_FOLDER] = TEST_APP_DEPLOY_DATA - configure_backend(flask_app, models_path="test_app_deploy_data.models") + configure_backend(flask_app) # Flask provides a way to test your application by exposing the Werkzeug test Client # and handling the context locals for you. testing_client = flask_app @@ -93,7 +109,7 @@ def test_app_client_sql_backed_development_env(main_json_sql_backend_fixture): flask_app = create_app(db_uri=TESTING_DB_URI, env=DEVELOPMENT) flask_app.config[APP_CONFIG_JSON] = MappingProxyType(main_json_sql_backend_fixture) flask_app.config[CONFIG_FILE_FOLDER] = TEST_APP_DEPLOY_DATA - configure_backend(flask_app, models_path="test_app_deploy_data.models") + configure_backend(flask_app) # Flask provides a way to test your application by exposing the Werkzeug test Client # and handling the context locals for you. testing_client = flask_app @@ -151,21 +167,26 @@ def make_graphic_config_for_testing(): "type": "scatter", "x": "penguin_size:body_mass_g", "y": "penguin_size:flipper_length_mm", + HOVERTEXT: [ + "penguin_size:sex", + "penguin_size:culmen_length_mm", + ], + TRANSFORMS: { + GROUPBY: { + GROUPS: ["penguin_size:island", "penguin_size:sex",], + } + }, } ] }, - VISUALIZATION_OPTIONS: { - HOVER_DATA: { - COLUMN_NAME: ["penguin_size:sex", "penguin_size:culmen_length_mm",], - }, - GROUPBY: {COLUMN_NAME: ["penguin_size:island", "penguin_size:sex",],}, - }, SELECTABLE_DATA_DICT: { FILTER: [ {COLUMN_NAME: "penguin_size:sex", MULTIPLE: False,}, {COLUMN_NAME: "penguin_size:island", MULTIPLE: True,}, ], - NUMERICAL_FILTER: [{COLUMN_NAME: "penguin_size:culmen_length_mm",}], + NUMERICAL_FILTER: [ + {COLUMN_NAME: "penguin_size:culmen_length_mm", TYPE: "number"} + ], }, }, "graphic_1": { @@ -181,21 +202,10 @@ def make_graphic_config_for_testing(): }, }, SELECTABLE_DATA_DICT: { - AXIS: [ - { - OPTION_TYPE: "axis", - COLUMN_NAME: "x", - ENTRIES: [ - "penguin_size:culmen_length_mm", - "penguin_size:flipper_length_mm", - "penguin_size:body_mass_g", - "penguin_size:culmen_depth_mm", - ], - } - ], GROUPBY: { ENTRIES: ["penguin_size:sex", "penguin_size:island"], MULTIPLE: True, + DEFAULT_SELECTED: ["penguin_size:sex"], }, }, }, @@ -219,3 +229,19 @@ def sql_handler_fixture(rebuild_test_database): ], } return SqlHandler(data_sources) + + +@pytest.fixture() +def sql_data_inventory_fixture(rebuild_test_database): + data_sources = { + MAIN_DATA_SOURCE: {DATA_SOURCE_TYPE: "penguin_size"}, + } + return SqlDataInventory(data_sources) + + +@pytest.fixture +def penguin_size_csv_file(): + request_file = FileStorage( + stream=open(TEST_FILENAME, "rb"), filename=TEST_FILENAME, + ) + return request_file diff --git a/escalation/tests/test_controller.py b/escalation/tests/test_controller.py index f6775dc..2cf734b 100644 --- a/escalation/tests/test_controller.py +++ b/escalation/tests/test_controller.py @@ -1,160 +1,69 @@ # Copyright [2020] [Two Six Labs, LLC] # Licensed under the Apache License, Version 2.0 - +import copy import json from tests.conftest import PEARL_HARBOR from controller import ( create_labels_for_available_pages, - create_data_subselect_info_for_plot, - get_unique_set_of_columns_needed, - make_filter_dict, get_datasource_metadata_formatted_for_admin_panel, + make_dict_of_graphic_objects, ) -from graphics.plotly_plot import PlotlyPlot +from tests.conftest import graphic_json_fixture, addendum_dict from utility.constants import ( - JINJA_SELECT_HTML_FILE, - ENTRIES, ACTIVE_SELECTORS, SHOW_ALL_ROW, SELECTABLE_DATA_DICT, - TEXT, FILTER, - NUMERICAL_FILTER, - AXIS, - MULTIPLE, - DATA, - MAX, - VALUE, - MIN, - GROUPBY, - NO_GROUP_BY, + GRAPHIC_NUM, + DATA_FILTERS, ) -def test_extract_buttons(main_json_sql_backend_fixture): - aval_pg = main_json_sql_backend_fixture["available_pages"] - buttons = create_labels_for_available_pages(aval_pg) - - button1 = {"webpage_label": "PENGUINS!", "link": "penguin"} - assert button1 in buttons - - -def test_create_data_subselect_info(sql_handler_fixture, graphic_json_fixture): - select_dict = { - FILTER: [ - { - "column": "penguin_size:sex", - "multiple": False, - ACTIVE_SELECTORS: ["MALE"], - }, - { - "column": "penguin_size:island", - "multiple": True, - ACTIVE_SELECTORS: [SHOW_ALL_ROW], - }, - ], - AXIS: [ - { - "column": "x", - "entries": [ - "penguin_size:culmen_length_mm", - "penguin_size:flipper_length_mm", - "penguin_size:body_mass_g", - ], - ACTIVE_SELECTORS: ["penguin_size:culmen_length_mm"], - } - ], - } - graphic_json_fixture[SELECTABLE_DATA_DICT] = select_dict - select_info = create_data_subselect_info_for_plot( - graphic_json_fixture, sql_handler_fixture +def test_make_dict_of_graphic_classes(graphic_json_fixture, addendum_dict): + single_page_config_dict = graphic_json_fixture + single_page_config_dict_test = copy.deepcopy(single_page_config_dict) + graphic_class_dict = make_dict_of_graphic_objects( + single_page_config_dict_test, None ) + # add instructions should call the other two methods which I am already testing for. + # So I want to make sure it in actually doing something + assert ( + graphic_class_dict[GRAPHIC_NUM.format(0)].graphic_dict + != single_page_config_dict[GRAPHIC_NUM.format(0)] + ) + assert DATA_FILTERS not in graphic_class_dict[GRAPHIC_NUM.format(0)].graphic_dict - assert select_info[0][JINJA_SELECT_HTML_FILE] == "selector.html" - assert select_info[2][JINJA_SELECT_HTML_FILE] == "selector.html" - - assert "MALE" in select_info[1][ACTIVE_SELECTORS] - assert "MALE" in select_info[1][ENTRIES] - assert "FEMALE" in select_info[1][ENTRIES] - assert "." in select_info[1][ENTRIES] # yes this is a unique entry in the data set - assert SHOW_ALL_ROW in select_info[2][ACTIVE_SELECTORS] - assert "Biscoe" in select_info[2][ENTRIES] - assert select_info[2][MULTIPLE] - assert not select_info[1][MULTIPLE] - assert "penguin_size:culmen_length_mm" in select_info[0][ENTRIES] - assert "penguin_size:flipper_length_mm" in select_info[0][ENTRIES] - assert "penguin_size:body_mass_g" in select_info[0][ENTRIES] - assert "penguin_size:culmen_length_mm" in select_info[0][ACTIVE_SELECTORS] - - -def test_get_unique_set_of_columns_needed(): - culmen = "penguin_size:culmen_length_mm" - flipper = "penguin_size:flipper_length_mm" - flipper2 = "penguin_size:flipper_length_mm2" - island = "penguin_size:island" - sex = "penguin_size:sex" - ploty_test = PlotlyPlot() - test_cols_list = get_unique_set_of_columns_needed( - ploty_test.get_data_columns( - {DATA: [{"x": culmen, "y": flipper}, {"x": culmen, "y": flipper2},]} - ), - {"hover_data": {"column": [sex, culmen]}, "groupby": {"column": [island]},}, + single_page_config_dict_test = copy.deepcopy(single_page_config_dict) + graphic_class_dict = make_dict_of_graphic_objects( + single_page_config_dict_test, addendum_dict ) - assert culmen in test_cols_list - assert flipper in test_cols_list - assert flipper2 in test_cols_list - assert island in test_cols_list - assert sex in test_cols_list - assert len(test_cols_list) == 5 + assert DATA_FILTERS in graphic_class_dict[GRAPHIC_NUM.format(0)].graphic_dict -def test_create_data_subselect_info_for_plot_with_defaults( - sql_handler_fixture, graphic_json_fixture +def test_make_dict_of_graphic_classes_with_different_addendum( + graphic_json_fixture, addendum_dict ): - plot_specification = graphic_json_fixture["graphic_0"] - # add_active_selectors_to_selectable_data_list adds default SHOW_ALL_ROWS to selectors - for selector in plot_specification[SELECTABLE_DATA_DICT][FILTER]: - selector[ACTIVE_SELECTORS] = [SHOW_ALL_ROW] - numerical_filter_example_dict = {MAX: {VALUE: "3"}, MIN: {VALUE: ""}} - plot_specification[SELECTABLE_DATA_DICT][NUMERICAL_FILTER][0][ - ACTIVE_SELECTORS - ] = numerical_filter_example_dict - select_info = create_data_subselect_info_for_plot( - plot_specification, sql_handler_fixture + single_page_config_dict = graphic_json_fixture + single_page_config_dict_test = copy.deepcopy(single_page_config_dict) + new_addendum_dict = {"a_different_graph": addendum_dict[GRAPHIC_NUM.format(0)]} + graphic_class_dict = make_dict_of_graphic_objects( + single_page_config_dict_test, new_addendum_dict + ) + graphic_0_dict = graphic_class_dict[GRAPHIC_NUM.format(0)].graphic_dict + assert len(graphic_0_dict[SELECTABLE_DATA_DICT][FILTER][0][ACTIVE_SELECTORS]) == 1 + assert ( + SHOW_ALL_ROW + in graphic_0_dict[SELECTABLE_DATA_DICT][FILTER][0][ACTIVE_SELECTORS] ) - expected_select_info = [ - { - "select_html_file": "selector.html", - "type": "filter", - "name": "filter_0", - "active_selector": [SHOW_ALL_ROW], - "entries": [SHOW_ALL_ROW, ".", "FEMALE", "MALE"], - "multiple": False, - TEXT: "Filter by penguin_size:sex", - }, - { - "select_html_file": "selector.html", - "type": "filter", - "name": "filter_1", - "active_selector": [SHOW_ALL_ROW], - "entries": [SHOW_ALL_ROW, "Biscoe", "Dream", "Torgersen"], - "multiple": True, - TEXT: "Filter by penguin_size:island", - }, - { - "select_html_file": "numerical_filter.html", - "type": "numerical_filter", - "name": "numerical_filter_0", - "active_selector": numerical_filter_example_dict, - "entries": None, - "multiple": False, - TEXT: "Filter by penguin_size:culmen_length_mm", - }, - ] - assert select_info[0] == expected_select_info[0] - assert select_info[1] == expected_select_info[1] - assert select_info[2] == expected_select_info[2] + + +def test_extract_buttons(main_json_sql_backend_fixture): + aval_pg = main_json_sql_backend_fixture["available_pages"] + buttons = create_labels_for_available_pages(aval_pg) + + button1 = {"webpage_label": "PENGUINS!", "link": "penguin"} + assert button1 in buttons def test_get_data_for_page(): @@ -170,143 +79,25 @@ def test_assemble_plot_from_instructions(): def test_create_labels_for_available_pages(): - assert False - - -def test_make_filter_dict_filters(): - selector_dict = { - "filter": [ - { - "column": "penguin_size:sex", - "multiple": False, - "active_selector": ["Show All Rows"], - }, - { - "column": "penguin_size:island", - "multiple": True, - "default_selected": ["Dream"], - "active_selector": ["Dream"], - }, - ], - "numerical_filter": [ - { - "column": "penguin_size:culmen_length_mm", - "active_selector": {"max": {"value": ""}, "min": {"value": ""}}, - } - ], - } - - penguin_sexes = ([SHOW_ALL_ROW, "XX", "XY"],) # penguin sexes column entries - penguin_islands = [SHOW_ALL_ROW, "Dream"] # penguin islands column entries - - entries_for_columns = [penguin_sexes, penguin_islands] - created_filter_dicts = [] - for index, selector_items in enumerate(selector_dict[FILTER]): - created_filter_dicts.append( - make_filter_dict( - FILTER, - selector_items, - index, - selector_entries=entries_for_columns[index], - ) - ) - - expected_filter_dict_sexes = { - "type": "filter", - "select_html_file": "selector.html", - "name": "filter_0", - "text": "Filter by penguin_size:sex", - "active_selector": ["Show All Rows"], - "multiple": False, - "entries": penguin_sexes, - } - assert created_filter_dicts[0] == expected_filter_dict_sexes - - expected_filter_dict_islands = { - "type": "filter", - "select_html_file": "selector.html", - "name": "filter_1", - "text": "Filter by penguin_size:island", - "active_selector": ["Dream"], - "multiple": True, - "entries": penguin_islands, - } - assert created_filter_dicts[1] == expected_filter_dict_islands - - created_filter_dict = make_filter_dict( - NUMERICAL_FILTER, - selector_dict[NUMERICAL_FILTER][0], # get the entry from the list of filters - index=0, - ) - - expected_numerical_filter_dict = { - "type": "numerical_filter", - "select_html_file": "numerical_filter.html", - "name": "numerical_filter_0", - "text": "Filter by penguin_size:culmen_length_mm", - "active_selector": {"max": {"value": ""}, "min": {"value": ""}}, - "multiple": False, - "entries": None, - } - assert expected_numerical_filter_dict == created_filter_dict - - -def test_make_filter_dict_axis_groupby(): - axis_entries = [ - "penguin_size:body_mass_g", - "penguin_size:culmen_depth_mm", - "penguin_size:culmen_length_mm", - "penguin_size:flipper_length_mm", - ] - groupby_entries = [NO_GROUP_BY, "penguin_size:sex", "penguin_size:island"] - selector_dict = { - "axis": [ - { - "column": "x", - "entries": axis_entries, - "active_selector": "penguin_size:body_mass_g", - } - ], - "groupby": { - "entries": groupby_entries, - "multiple": True, - "active_selector": ["penguin_size:island"], + available_pages_list = [ + { + "webpage_label": "PENGUINS!", + "url_endpoint": "penguin", + "graphic_config_files": ["big_penguins.json", "hist_penguins.json"], }, - } - - created_axis_dict = make_filter_dict( - AXIS, - selector_dict[AXIS][0], # get the entry from the list of axes - index=0, - selector_entries=axis_entries, - ) - expected_axis_dict = { - "type": "axis", - "select_html_file": "selector.html", - "name": "axis_0", - "text": "x axis", - "active_selector": "penguin_size:body_mass_g", - "multiple": False, - "entries": axis_entries, - } - assert created_axis_dict == expected_axis_dict + { + "webpage_label": "Radio Penguins", + "url_endpoint": "radio_penguins", + "graphic_config_files": ["radio_penguins.json"], + }, + ] + labels = create_labels_for_available_pages(available_pages_list) + expected_labels = [ + {"webpage_label": "PENGUINS!", "link": "penguin"}, + {"webpage_label": "Radio Penguins", "link": "radio_penguins"}, + ] - created_groupby_dict = make_filter_dict( - GROUPBY, - selector_dict[GROUPBY], # get the entry from the list of filters - index=0, - selector_entries=groupby_entries, - ) - expected_groupby_dict = { - "type": "groupby", - "select_html_file": "selector.html", - "name": "groupby", - "text": "Group by:", - "active_selector": ["penguin_size:island"], - "multiple": True, - "entries": groupby_entries, - } - assert created_groupby_dict == expected_groupby_dict + assert labels == expected_labels def test_make_pages_dict(): diff --git a/escalation/tests/test_cytoscape.py b/escalation/tests/test_cytoscape.py new file mode 100644 index 0000000..3b407b9 --- /dev/null +++ b/escalation/tests/test_cytoscape.py @@ -0,0 +1,175 @@ +import copy +import json + +from graphics.cytoscape import Cytoscape +from utility.constants import ( + NODE_ID, + SOURCE, + TARGET, + EDGE_ID, + ELEMENT_PROPERTIES, + PROPERTY_NAME, + NODES, + GROUP, + COLUMN_NAME, + LAYOUT, + STYLE, + SELECTOR, + ELEMENT_STYLE, + DATA, + ID, + ELEMENTS, + STYLE_NAME, + EDGES, + PLOT_SPECIFIC_INFO, +) +import pytest + + +@pytest.fixture() +def make_data_cyto(): + data = { + "beings": ["Zeus", "Poseidon", "Hades", "Theseus", "Orion"], + "start": [ + "Zeus", + "Poseidon", + "Poseidon", + "Poseidon", + "Zeus", + "Poseidon", + "Hades", + "Hades", + ], + "end": [ + "Poseidon", + "Zeus", + "Theseus", + "Orion", + "Hades", + "Hades", + "Zeus", + "Poseidon", + ], + "numbers": ["0", "1", "2", "3", "4", "5", "6", "7"], + "color": ["yellow", "blue", "red", "blue", "blue"], + "class": ["god", "god,poseidon", "god", "poseidon", "poseidon"], + } + + return data + + +@pytest.fixture() +def make_graphic_dict_cyto(): + graphic_dict = { + PLOT_SPECIFIC_INFO: { + NODE_ID: "beings", + SOURCE: "start", + TARGET: "end", + EDGE_ID: "numbers", + ELEMENT_PROPERTIES: [ + {PROPERTY_NAME: "classes", GROUP: NODES, COLUMN_NAME: "class"} + ], + LAYOUT: {"name": "cose"}, + STYLE: [ + { + SELECTOR: "node", + STYLE: {"shape": "round-pentagon", "label": "data(id)"}, + }, + { + SELECTOR: "edge", + STYLE: { + "target-arrow-shape": "triangle", + "curve-style": "straight", + }, + }, + ], + ELEMENT_STYLE: [ + {STYLE_NAME: "background-color", GROUP: NODES, COLUMN_NAME: "color",} + ], + } + } + + return graphic_dict + + +def test_make_dict_for_html_plot(make_data_cyto, make_graphic_dict_cyto): + + expected_dict = { + ELEMENTS: [ + {GROUP: NODES, DATA: {ID: "Zeus"}, "classes": "god"}, + {GROUP: NODES, DATA: {ID: "Poseidon"}, "classes": "god,poseidon"}, + {GROUP: NODES, DATA: {ID: "Hades"}, "classes": "god"}, + {GROUP: NODES, DATA: {ID: "Theseus"}, "classes": "poseidon"}, + {GROUP: NODES, DATA: {ID: "Orion"}, "classes": "poseidon"}, + {DATA: {ID: "0", SOURCE: "Zeus", TARGET: "Poseidon"}, GROUP: EDGES}, + {DATA: {ID: "1", SOURCE: "Poseidon", TARGET: "Zeus"}, GROUP: EDGES}, + {DATA: {ID: "2", SOURCE: "Poseidon", TARGET: "Theseus"}, GROUP: EDGES}, + {DATA: {ID: "3", SOURCE: "Poseidon", TARGET: "Orion"}, GROUP: EDGES}, + {DATA: {ID: "4", SOURCE: "Zeus", TARGET: "Hades"}, GROUP: EDGES}, + {DATA: {ID: "5", SOURCE: "Poseidon", TARGET: "Hades"}, GROUP: EDGES}, + {DATA: {ID: "6", SOURCE: "Hades", TARGET: "Zeus"}, GROUP: EDGES}, + {DATA: {ID: "7", SOURCE: "Hades", TARGET: "Poseidon"}, GROUP: EDGES}, + ], + LAYOUT: {"name": "cose"}, + STYLE: [ + {SELECTOR: "node", STYLE: {"shape": "round-pentagon", "label": "data(id)"}}, + { + SELECTOR: "edge", + STYLE: {"target-arrow-shape": "triangle", "curve-style": "straight"}, + }, + {SELECTOR: "#Zeus", STYLE: {"background-color": "yellow"},}, + {SELECTOR: "#Poseidon", STYLE: {"background-color": "blue"},}, + {SELECTOR: "#Hades", STYLE: {"background-color": "red"},}, + {SELECTOR: "#Theseus", STYLE: {"background-color": "blue"},}, + {SELECTOR: "#Orion", STYLE: {"background-color": "blue"},}, + ], + } + + cyto_test = Cytoscape(make_graphic_dict_cyto) + cyto_test.data = make_data_cyto + cyto_test.make_dict_for_html_plot() + cyto_dict = json.loads(cyto_test.graph_json_str) + assert cyto_dict == expected_dict + + +def test_make_dict_for_html_plot_simple(make_data_cyto, make_graphic_dict_cyto): + del make_graphic_dict_cyto[PLOT_SPECIFIC_INFO][ELEMENT_PROPERTIES] + del make_graphic_dict_cyto[PLOT_SPECIFIC_INFO][STYLE] + expected_dict = { + ELEMENTS: [ + {GROUP: NODES, DATA: {ID: "Zeus"}}, + {GROUP: NODES, DATA: {ID: "Poseidon"}}, + {GROUP: NODES, DATA: {ID: "Hades"}}, + {GROUP: NODES, DATA: {ID: "Theseus"}}, + {GROUP: NODES, DATA: {ID: "Orion"}}, + {DATA: {ID: "0", SOURCE: "Zeus", TARGET: "Poseidon"}, GROUP: EDGES}, + {DATA: {ID: "1", SOURCE: "Poseidon", TARGET: "Zeus"}, GROUP: EDGES}, + {DATA: {ID: "2", SOURCE: "Poseidon", TARGET: "Theseus"}, GROUP: EDGES}, + {DATA: {ID: "3", SOURCE: "Poseidon", TARGET: "Orion"}, GROUP: EDGES}, + {DATA: {ID: "4", SOURCE: "Zeus", TARGET: "Hades"}, GROUP: EDGES}, + {DATA: {ID: "5", SOURCE: "Poseidon", TARGET: "Hades"}, GROUP: EDGES}, + {DATA: {ID: "6", SOURCE: "Hades", TARGET: "Zeus"}, GROUP: EDGES}, + {DATA: {ID: "7", SOURCE: "Hades", TARGET: "Poseidon"}, GROUP: EDGES}, + ], + LAYOUT: {"name": "cose"}, + STYLE: [ + {SELECTOR: "#Zeus", STYLE: {"background-color": "yellow"},}, + {SELECTOR: "#Poseidon", STYLE: {"background-color": "blue"},}, + {SELECTOR: "#Hades", STYLE: {"background-color": "red"},}, + {SELECTOR: "#Theseus", STYLE: {"background-color": "blue"},}, + {SELECTOR: "#Orion", STYLE: {"background-color": "blue"},}, + ], + } + + cyto_test = Cytoscape(make_graphic_dict_cyto) + cyto_test.data = make_data_cyto + cyto_test.make_dict_for_html_plot() + cyto_dict = json.loads(cyto_test.graph_json_str) + assert cyto_dict == expected_dict + + +def test_get_data_columns(make_data_cyto, make_graphic_dict_cyto): + cyto_test = Cytoscape(make_graphic_dict_cyto) + column_names = cyto_test.get_data_columns() + expected_columns = set(make_data_cyto.keys()) + assert column_names == expected_columns diff --git a/escalation/tests/test_dashboard.py b/escalation/tests/test_dashboard.py new file mode 100644 index 0000000..85d975b --- /dev/null +++ b/escalation/tests/test_dashboard.py @@ -0,0 +1,28 @@ +from werkzeug.datastructures import ImmutableMultiDict + +from views.dashboard import add_form_to_addendum_dict + + +def test_add_form_to_addendum_dict(): + addendum_dict = { + "graphic_0": { + "filter_0": ["MALE"], + "filter_1": ["Torgersen", "Dream"], + "numerical_filter_0_max_value": ["4"], + "numerical_filter_0_min_value": [""], + } + } + + form = ImmutableMultiDict( + [ + ("graphic_name", "graphic_0"), + ("filter_0", "MALE"), + ("filter_1", "Torgersen"), + ("filter_1", "Dream"), + ("numerical_filter_0_max_value", "4"), + ("numerical_filter_0_min_value", ""), + ] + ) + new_addendum_dict = {} + add_form_to_addendum_dict(form, new_addendum_dict) + assert new_addendum_dict == addendum_dict diff --git a/escalation/tests/test_file_uploads.py b/escalation/tests/test_file_uploads.py index 8f50050..38a1f87 100644 --- a/escalation/tests/test_file_uploads.py +++ b/escalation/tests/test_file_uploads.py @@ -1,7 +1,7 @@ # Copyright [2020] [Two Six Labs, LLC] # Licensed under the Apache License, Version 2.0 from base64 import b64encode -from io import BufferedReader, StringIO +from io import StringIO import os import pandas as pd @@ -35,28 +35,20 @@ def request_form(): } -@pytest.fixture -def request_file(): - request_file = FileStorage( - stream=open(TEST_FILENAME, "rb"), filename=TEST_FILENAME, - ) - return request_file - - -def test_validate_submission_content(request_file, test_app_client_sql_backed): +def test_validate_submission_content(penguin_size_csv_file, test_app_client_sql_backed): data_handler_class = test_app_client_sql_backed.config.data_handler( data_sources={MAIN_DATA_SOURCE: {DATA_SOURCE_TYPE: DATA_SOURCE_NAME}} ) data_source_schema = data_handler_class.get_schema_for_data_source() try: - _ = validate_submission_content(request_file, data_source_schema) + _ = validate_submission_content(penguin_size_csv_file, data_source_schema) except ValidationError: pytest.fail("validate_submission_content should run without error on good file") df = pd.read_csv(TEST_FILENAME) df["extra column not in schema"] = -99 - extra_column_file = StringIO(df.to_csv(index=False)) + extra_column_file = FileStorage(StringIO(df.to_csv(index=False)), filename=CSVFILE) try: validate_submission_content(extra_column_file, data_source_schema) except ValidationError: @@ -66,17 +58,21 @@ def test_validate_submission_content(request_file, test_app_client_sql_backed): df = pd.read_csv(TEST_FILENAME) df.drop("island", axis=1, inplace=True) - missing_column_file = StringIO(df.to_csv(index=False)) + missing_column_file = FileStorage( + StringIO(df.to_csv(index=False)), filename=CSVFILE + ) with pytest.raises(ValidationError): validate_submission_content(missing_column_file, data_source_schema) -def test_validate_data_form(test_app_client_sql_backed, request_file, request_form): +def test_validate_data_form( + test_app_client_sql_backed, penguin_size_csv_file, request_form +): for required_field in [USERNAME, DATA_SOURCE, NOTES]: missing_field_form = request_form.copy() _ = missing_field_form.pop(required_field) with pytest.raises(ValidationError): - validate_data_form(missing_field_form, request_file) + validate_data_form(missing_field_form, penguin_size_csv_file) # todo: validate file is a real file # for broken_files in [{}, {CSVFILE: None}]: # # require csvfile @@ -85,15 +81,16 @@ def test_validate_data_form(test_app_client_sql_backed, request_file, request_fo def test_submission_auth_prd_mode_failures( - test_app_client_sql_backed, request_form, request_file + test_app_client_sql_backed, request_form, penguin_size_csv_file ): # test authentication client = test_app_client_sql_backed.test_client() get_response = client.get("/upload") assert get_response.status_code == 200 - request_form[CSVFILE] = request_file + + request_form[CSVFILE] = penguin_size_csv_file post_response = client.post("/upload", data=request_form,) - # test responses in prd- auth required + # test responses in environment other than DEVELOPMENT- auth required assert post_response.status_code == 401 # todo: test client setup should include test-specific users @@ -106,11 +103,11 @@ def test_submission_auth_prd_mode_failures( def test_submission_auth_prd_mode( - test_app_client_sql_backed, request_form, request_file + test_app_client_sql_backed, request_form, penguin_size_csv_file ): client = test_app_client_sql_backed.test_client() credentials = b64encode(b"admin:escalation").decode("utf-8") - request_form[CSVFILE] = request_file + request_form[CSVFILE] = penguin_size_csv_file post_response = client.post( "/upload", headers={"Authorization": f"Basic {credentials}",}, @@ -123,13 +120,13 @@ def test_submission_auth_dev_mode( rebuild_test_database, test_app_client_sql_backed_development_env, request_form, - request_file, + penguin_size_csv_file, ): # test the file upload when the app is running in wizard/development mode- no auth required client = test_app_client_sql_backed_development_env.test_client() get_response = client.get("/upload") assert get_response.status_code == 200 - request_form[CSVFILE] = request_file + request_form[CSVFILE] = penguin_size_csv_file post_response = client.post( "/upload", data=request_form, content_type="multipart/form-data" @@ -142,9 +139,9 @@ def test_submission_validates_and_writes_to_db( rebuild_test_database, test_app_client_sql_backed_development_env, request_form, - request_file, + penguin_size_csv_file, ): - request_form[CSVFILE] = request_file + request_form[CSVFILE] = penguin_size_csv_file # upload_ids = PenguinSize.upload_id.all_() session = test_app_client_sql_backed_development_env.db_session client = test_app_client_sql_backed_development_env.test_client() diff --git a/escalation/tests/test_graphic_class.py b/escalation/tests/test_graphic_class.py new file mode 100644 index 0000000..5b307d0 --- /dev/null +++ b/escalation/tests/test_graphic_class.py @@ -0,0 +1,380 @@ +# Copyright [2020] [Two Six Labs, LLC] +# Licensed under the Apache License, Version 2.0 + +import copy + +import pytest +from werkzeug.datastructures import ImmutableMultiDict + +from graphics.graphic_class import get_key_for_form, make_filter_dict +from graphics.plotly_plot import PlotlyPlot +from tests.conftest import graphic_json_fixture, addendum_dict +from utility.constants import ( + AVAILABLE_PAGES, + DATA, + GRAPHIC_NUM, + GRAPHICS, + SELECTABLE_DATA_DICT, + SHOW_ALL_ROW, + UPPER_INEQUALITY, + VALUE, + OPERATION, + ACTIVE_SELECTORS, + DATA_FILTERS, + FILTER, + NUMERICAL_FILTER, + GROUPBY, + COLUMN_NAME, + PLOT_SPECIFIC_INFO, + DEFAULT_SELECTED, + MAX, + MIN, + TRANSFORMS, + GROUPS, + NO_GROUP_BY, + TEXT, + JINJA_SELECT_HTML_FILE, + ENTRIES, + MULTIPLE, + FILTERED_SELECTOR, +) + + +def test_add_active_selectors_to_selectable_data_list_with_addendum( + graphic_json_fixture, addendum_dict +): + graphic_0_dict = graphic_json_fixture["graphic_0"] + graphic_class = PlotlyPlot(graphic_0_dict, addendum_dict["graphic_0"],) + graphic_class.add_active_selectors_to_selectable_data_list() + filter_list = graphic_class.graphic_dict[SELECTABLE_DATA_DICT][FILTER] + assert len(filter_list[0][ACTIVE_SELECTORS]) == 1 + assert "MALE" in filter_list[0][ACTIVE_SELECTORS] + assert len(filter_list[1][ACTIVE_SELECTORS]) == 2 + assert "Torgersen" in filter_list[1][ACTIVE_SELECTORS] + assert "Dream" in filter_list[1][ACTIVE_SELECTORS] + + numerical_filter_list = graphic_class.graphic_dict[SELECTABLE_DATA_DICT][ + NUMERICAL_FILTER + ] + assert numerical_filter_list[0][ACTIVE_SELECTORS][MAX][VALUE] == "4" + assert numerical_filter_list[0][ACTIVE_SELECTORS][MIN][VALUE] == "" + + +def test_add_active_selectors_to_selectable_data_list_with_SHOW_ALL_ROWS_chosen( + graphic_json_fixture, +): + addendum_dict = { + "filter_0": ["MALE"], + "filter_1": [SHOW_ALL_ROW], + "numerical_filter_0_max_value": ["4"], + "numerical_filter_0_min_value": [""], + } + graphic_0_dict = graphic_json_fixture["graphic_0"] + graphic_class = PlotlyPlot(graphic_0_dict, addendum_dict,) + graphic_class.add_active_selectors_to_selectable_data_list() + graphic_dict = graphic_class.graphic_dict + assert len(graphic_dict[SELECTABLE_DATA_DICT][FILTER][1][ACTIVE_SELECTORS]) == 1 + assert ( + SHOW_ALL_ROW in graphic_dict[SELECTABLE_DATA_DICT][FILTER][1][ACTIVE_SELECTORS] + ) + + +def test_add_active_selectors_to_selectable_data_list_without_addendum( + graphic_json_fixture, +): + graphic_0_dict = graphic_json_fixture["graphic_0"] + graphic_0_dict[SELECTABLE_DATA_DICT][FILTER][1][DEFAULT_SELECTED] = ["Dream"] + + graphic_class = PlotlyPlot(graphic_0_dict,) + graphic_class.add_active_selectors_to_selectable_data_list() + + filter_list = graphic_class.graphic_dict[SELECTABLE_DATA_DICT][FILTER] + assert len(filter_list[0][ACTIVE_SELECTORS]) == 1 + assert SHOW_ALL_ROW in filter_list[0][ACTIVE_SELECTORS] + assert len(filter_list[1][ACTIVE_SELECTORS]) == 1 + assert "Dream" in filter_list[1][ACTIVE_SELECTORS] + + numerical_filter_list = graphic_class.graphic_dict[SELECTABLE_DATA_DICT][ + NUMERICAL_FILTER + ] + assert numerical_filter_list[0][ACTIVE_SELECTORS][MAX][VALUE] == "" + assert numerical_filter_list[0][ACTIVE_SELECTORS][MIN][VALUE] == "" + + graphic_1_dict = graphic_json_fixture["graphic_1"] + graphic_class = PlotlyPlot(graphic_1_dict,) + graphic_class.add_active_selectors_to_selectable_data_list() + groupby_list = graphic_class.graphic_dict[SELECTABLE_DATA_DICT][GROUPBY] + assert groupby_list[ACTIVE_SELECTORS] == ["penguin_size:sex"] + + +def test_add_operations_to_the_data(graphic_json_fixture, addendum_dict): + graphic_json_fixture = graphic_json_fixture + graphic_0_dict = graphic_json_fixture["graphic_0"] + graphic_class = PlotlyPlot(graphic_0_dict, addendum_dict["graphic_0"],) + graphic_class.add_operations_to_the_data_from_addendum() + operations_list = graphic_class.graphic_dict[DATA_FILTERS] + assert len(operations_list) == 3 + + assert operations_list[0] == { + "type": "filter", + "column": "penguin_size:sex", + "selected": ["MALE"], + FILTERED_SELECTOR: False, + } + assert operations_list[1] == { + "type": "filter", + "column": "penguin_size:island", + "selected": ["Torgersen", "Dream"], + FILTERED_SELECTOR: False, + } + assert operations_list[2] == { + "type": "numerical_filter", + "column": "penguin_size:culmen_length_mm", + "operation": "<=", + "value": 4.0, + } + + +def test_add_operations_to_the_data_from_defaults(graphic_json_fixture): + selectable_data_dict = graphic_json_fixture["graphic_0"][SELECTABLE_DATA_DICT] + selectable_data_dict[NUMERICAL_FILTER][0][MIN] = 2 + + graphic_class = PlotlyPlot(graphic_json_fixture["graphic_0"]) + graphic_class.add_operations_to_the_data_from_defaults() + operations_list = graphic_class.graphic_dict[DATA_FILTERS] + assert len(operations_list) == 1 + + assert operations_list[0] == { + "type": "numerical_filter", + "column": "penguin_size:culmen_length_mm", + "operation": ">=", + "value": 2.0, + } + + +def test_modify_config_based_on_json(graphic_json_fixture): + graphic_1_dict = graphic_json_fixture["graphic_1"] + addendum_dict = { + GROUPBY: ["penguin_size:island"], + } + assert TRANSFORMS not in graphic_1_dict[PLOT_SPECIFIC_INFO][DATA][0] + + graphic_class = PlotlyPlot(graphic_1_dict, addendum_dict,) + graphic_class.modify_graphic_dict_based_on_addendum_dict() + + assert graphic_class.graphic_dict[PLOT_SPECIFIC_INFO][DATA][0][TRANSFORMS][GROUPBY][ + GROUPS + ] == ["penguin_size:island"] + + # test defualt selected + graphic_class = PlotlyPlot(graphic_1_dict,) + graphic_class.modify_graphic_dict_based_on_addendum_dict() + + assert graphic_class.graphic_dict[PLOT_SPECIFIC_INFO][DATA][0][TRANSFORMS][GROUPBY][ + GROUPS + ] == ["penguin_size:sex"] + + +def test_get_key_for_form(): + assert "filter_1" == get_key_for_form(FILTER, 1) + assert "numerical_filter_4" == get_key_for_form(NUMERICAL_FILTER, 4) + assert "groupby" == get_key_for_form(GROUPBY, "") + + +def test_make_filter_dict_filters(): + selector_dict = { + "filter": [ + { + "column": "penguin_size:sex", + "multiple": False, + "active_selector": ["Show All Rows"], + }, + { + "column": "penguin_size:island", + "multiple": True, + "default_selected": ["Dream"], + "active_selector": ["Dream"], + }, + ], + "numerical_filter": [ + { + "column": "penguin_size:culmen_length_mm", + "type": "number", + "active_selector": {"max": {"value": ""}, "min": {"value": ""}}, + } + ], + } + + penguin_sexes = ([SHOW_ALL_ROW, "XX", "XY"],) # penguin sexes column entries + penguin_islands = [SHOW_ALL_ROW, "Dream"] # penguin islands column entries + + entries_for_columns = [penguin_sexes, penguin_islands] + created_filter_dicts = [] + for index, selector_items in enumerate(selector_dict[FILTER]): + created_filter_dicts.append( + make_filter_dict( + FILTER, + selector_items, + index, + selector_entries=entries_for_columns[index], + ) + ) + + expected_filter_dict_sexes = { + "type": "", + "select_html_file": "selector.html", + "name": "filter_0", + "text": "Filter by penguin_size:sex", + "active_selector": ["Show All Rows"], + "multiple": False, + "entries": penguin_sexes, + } + assert created_filter_dicts[0] == expected_filter_dict_sexes + + expected_filter_dict_islands = { + "type": "", + "select_html_file": "selector.html", + "name": "filter_1", + "text": "Filter by penguin_size:island", + "active_selector": ["Dream"], + "multiple": True, + "entries": penguin_islands, + } + assert created_filter_dicts[1] == expected_filter_dict_islands + + created_filter_dict = make_filter_dict( + NUMERICAL_FILTER, + selector_dict[NUMERICAL_FILTER][0], # get the entry from the list of filters + index=0, + ) + + expected_numerical_filter_dict = { + "type": "number", + "select_html_file": "numerical_filter.html", + "name": "numerical_filter_0", + "text": "Filter by penguin_size:culmen_length_mm", + "active_selector": {"max": {"value": ""}, "min": {"value": ""}}, + "multiple": False, + "entries": None, + } + assert expected_numerical_filter_dict == created_filter_dict + + +def test_make_filter_dict_groupby(): + groupby_entries = [NO_GROUP_BY, "penguin_size:sex", "penguin_size:island"] + selector_dict = { + "groupby": { + "entries": groupby_entries, + "multiple": True, + "active_selector": ["penguin_size:island"], + }, + } + + created_groupby_dict = make_filter_dict( + GROUPBY, + selector_dict[GROUPBY], # get the entry from the list of filters + index=0, + selector_entries=groupby_entries, + ) + expected_groupby_dict = { + "type": "", + "select_html_file": "selector.html", + "name": "groupby", + "text": "Group by:", + "active_selector": ["penguin_size:island"], + "multiple": True, + "entries": groupby_entries, + } + assert created_groupby_dict == expected_groupby_dict + + +def test_create_data_subselect_info(sql_handler_fixture, graphic_json_fixture): + select_dict = { + FILTER: [ + { + "column": "penguin_size:sex", + "multiple": False, + ACTIVE_SELECTORS: ["MALE"], + }, + { + "column": "penguin_size:island", + "multiple": True, + ACTIVE_SELECTORS: [SHOW_ALL_ROW], + }, + ] + } + graphic_0_dict = graphic_json_fixture["graphic_0"] + graphic_0_dict[SELECTABLE_DATA_DICT] = select_dict + graphic_class = PlotlyPlot(graphic_0_dict,) + data_filters = graphic_class.graphic_dict.get(DATA_FILTERS, []) + unique_entry_dict = sql_handler_fixture.get_column_unique_entries( + graphic_class.get_columns_that_need_unique_entries(), filters=data_filters + ) + graphic_class.unique_entry_dict = unique_entry_dict + graphic_class.create_data_subselect_info_for_plot() + select_info = graphic_class.select_info + + assert select_info[1][JINJA_SELECT_HTML_FILE] == "selector.html" + + assert "MALE" in select_info[0][ACTIVE_SELECTORS] + assert "MALE" in select_info[0][ENTRIES] + assert "FEMALE" in select_info[0][ENTRIES] + assert "." in select_info[0][ENTRIES] # yes this is a unique entry in the data set + assert SHOW_ALL_ROW in select_info[1][ACTIVE_SELECTORS] + assert "Biscoe" in select_info[1][ENTRIES] + assert select_info[1][MULTIPLE] + assert not select_info[0][MULTIPLE] + + +def test_create_data_subselect_info_for_plot_with_defaults( + sql_handler_fixture, graphic_json_fixture +): + graphic_0_dict = graphic_json_fixture["graphic_0"] + # add_active_selectors_to_selectable_data_list adds default SHOW_ALL_ROWS to selectors + for selector in graphic_0_dict[SELECTABLE_DATA_DICT][FILTER]: + selector[ACTIVE_SELECTORS] = [SHOW_ALL_ROW] + numerical_filter_example_dict = {MAX: {VALUE: "3"}, MIN: {VALUE: ""}} + graphic_0_dict[SELECTABLE_DATA_DICT][NUMERICAL_FILTER][0][ + ACTIVE_SELECTORS + ] = numerical_filter_example_dict + + graphic_class = PlotlyPlot(graphic_0_dict,) + data_filters = graphic_class.graphic_dict.get(DATA_FILTERS, []) + unique_entry_dict = sql_handler_fixture.get_column_unique_entries( + graphic_class.get_columns_that_need_unique_entries(), filters=data_filters + ) + graphic_class.unique_entry_dict = unique_entry_dict + graphic_class.create_data_subselect_info_for_plot() + select_info = graphic_class.select_info + expected_select_info = [ + { + "select_html_file": "selector.html", + "type": "", + "name": "filter_0", + "active_selector": [SHOW_ALL_ROW], + "entries": [SHOW_ALL_ROW, ".", "FEMALE", "MALE"], + "multiple": False, + TEXT: "Filter by penguin_size:sex", + }, + { + "select_html_file": "selector.html", + "type": "", + "name": "filter_1", + "active_selector": [SHOW_ALL_ROW], + "entries": [SHOW_ALL_ROW, "Biscoe", "Dream", "Torgersen"], + "multiple": True, + TEXT: "Filter by penguin_size:island", + }, + { + "select_html_file": "numerical_filter.html", + "type": "number", + "name": "numerical_filter_0", + "active_selector": numerical_filter_example_dict, + "entries": None, + "multiple": False, + TEXT: "Filter by penguin_size:culmen_length_mm", + }, + ] + assert select_info[0] == expected_select_info[0] + assert select_info[1] == expected_select_info[1] + assert select_info[2] == expected_select_info[2] diff --git a/escalation/tests/test_graphic_schema.py b/escalation/tests/test_graphic_schema.py new file mode 100644 index 0000000..e342333 --- /dev/null +++ b/escalation/tests/test_graphic_schema.py @@ -0,0 +1 @@ +from graphics.graphic_schema import GraphicsConfigInterfaceBuilder diff --git a/escalation/tests/test_plotly.py b/escalation/tests/test_plotly.py index 81ce2cc..eb2a567 100644 --- a/escalation/tests/test_plotly.py +++ b/escalation/tests/test_plotly.py @@ -8,22 +8,29 @@ TITLE, PLOT_AXIS, VISUALIZATION_TYPE, - AGGREGATIONS, - OPTIONS, - AGGREGATE, TRANSFORMS, ) import pytest import json import pandas as pd -from utility.constants import POINTS_NUM, DATA, OPTION_COL +from utility.constants import ( + DATA, + OPTION_COL, + AGGREGATIONS, + AGGREGATE, + HOVERTEXT, + TYPE, + GROUPS, + TARGET, + PLOT_SPECIFIC_INFO, +) TITLE1 = "random_num" TITLE2 = "another_rand" -@pytest.fixture() +@pytest.fixture(scope="module") def make_data(): data = { TITLE1: [3, 6, 7], @@ -34,13 +41,16 @@ def make_data(): def test_plotly_draw_scatter(make_data): - plot_options = { - DATA: [{"type": "scatter", "x": TITLE1, "y": TITLE2, "mode": "markers"}] + graphic_dict = { + PLOT_SPECIFIC_INFO: { + DATA: [{"type": "scatter", "x": TITLE1, "y": TITLE2, "mode": "markers"}] + } } - ploty_test = PlotlyPlot() - graph_json = ploty_test.make_dict_for_html_plot(make_data, plot_options) - graph_dict = json.loads(graph_json) + ploty_test = PlotlyPlot(graphic_dict) + ploty_test.data = make_data + ploty_test.make_dict_for_html_plot() + graph_dict = json.loads(ploty_test.graph_json_str) assert (graph_dict[DATA][0]["x"] == make_data[TITLE1]).all() assert (graph_dict[DATA][0]["y"] == make_data[TITLE2]).all() @@ -48,23 +58,38 @@ def test_plotly_draw_scatter(make_data): assert graph_dict[LAYOUT][PLOT_AXIS.format("y")][TITLE] == TITLE2 # assert graph_dict[LAYOUT][PLOT_AXIS.format("x")][AUTOMARGIN] # assert graph_dict[LAYOUT][PLOT_AXIS.format("y")][AUTOMARGIN] - assert len(graph_dict[DATA][0][TRANSFORMS]) == 0 + assert TRANSFORMS not in graph_dict[DATA][0] def test_plotly_visualization_options(make_data, test_app_client_sql_backed): - plot_options = { - DATA: [{"type": "scatter", "x": TITLE1, "y": TITLE2, "mode": "markers"}] - } - ploty_test = PlotlyPlot() - visualization_options = { - "hover_data": {OPTION_COL: [TITLE1],}, # need a flask app to run - AGGREGATE: {OPTION_COL: [TITLE2], AGGREGATIONS: {"x": "avg", "y": "avg"},}, + graphic_dict = { + PLOT_SPECIFIC_INFO: { + DATA: [ + { + "type": "scatter", + "x": TITLE1, + "y": TITLE2, + "mode": "markers", + HOVERTEXT: [TITLE1], + TRANSFORMS: { + AGGREGATE: [ + { + GROUPS: [TITLE2], + AGGREGATIONS: [ + {TARGET: "x", "func": "avg"}, + {TARGET: "y", "func": "avg"}, + ], + } + ] + }, + } + ] + } } - - graph_json = ploty_test.make_dict_for_html_plot( - make_data, plot_options, visualization_options - ) - graph_dict = json.loads(graph_json) + ploty_test = PlotlyPlot(graphic_dict) + ploty_test.data = make_data + ploty_test.make_dict_for_html_plot() + graph_dict = json.loads(ploty_test.graph_json_str) transform_dict = graph_dict[DATA][0][TRANSFORMS] assert len(transform_dict) == 1 diff --git a/escalation/tests/test_reformatting_functions.py b/escalation/tests/test_reformatting_functions.py deleted file mode 100644 index 3dc5d71..0000000 --- a/escalation/tests/test_reformatting_functions.py +++ /dev/null @@ -1,252 +0,0 @@ -# Copyright [2020] [Two Six Labs, LLC] -# Licensed under the Apache License, Version 2.0 - -import copy - -import pytest -from werkzeug.datastructures import ImmutableMultiDict - -from tests.conftest import make_graphic_config_for_testing -from utility.constants import ( - AVAILABLE_PAGES, - DATA, - GRAPHIC_NUM, - GRAPHICS, - SELECTABLE_DATA_DICT, - SHOW_ALL_ROW, - UPPER_INEQUALITY, - VALUE, - OPERATION, - ACTIVE_SELECTORS, - DATA_FILTERS, - VISUALIZATION_OPTIONS, - FILTER, - NUMERICAL_FILTER, - GROUPBY, - AXIS, - COLUMN_NAME, - PLOT_SPECIFIC_INFO, - GRAPHIC_NAME, - DEFAULT_SELECTED, - MAX, - MIN, -) -from utility.reformatting_functions import ( - add_operations_to_the_data_from_addendum, - add_active_selectors_to_selectable_data_list, - add_instructions_to_config_dict, - get_key_for_form, - add_form_to_addendum_dict, - add_operations_to_the_data_from_defaults, -) - - -@pytest.fixture() -def single_page_config_dict(): - config_dict = make_graphic_config_for_testing() - single_page_config_dict = copy.deepcopy(config_dict) - - return single_page_config_dict - - -@pytest.fixture() -def addendum_dict(): - addendum_dict = { - "graphic_0": { - "filter_0": ["MALE"], - "filter_1": ["Torgersen", "Dream"], - "numerical_filter_0_max_value": ["4"], - "numerical_filter_0_min_value": [""], - } - } - return addendum_dict - - -def test_add_form_to_addendum_dict(addendum_dict): - form = ImmutableMultiDict( - [ - ("graphic_name", "graphic_0"), - ("filter_0", "MALE"), - ("filter_1", "Torgersen"), - ("filter_1", "Dream"), - ("numerical_filter_0_max_value", "4"), - ("numerical_filter_0_min_value", ""), - ] - ) - new_addendum_dict = {} - add_form_to_addendum_dict(form, new_addendum_dict) - assert new_addendum_dict == addendum_dict - - -def test_add_active_selectors_to_selectable_data_list_with_addendum( - single_page_config_dict, addendum_dict -): - graphic_0_dict = single_page_config_dict["graphic_0"] - add_active_selectors_to_selectable_data_list( - graphic_0_dict[SELECTABLE_DATA_DICT], - graphic_0_dict[PLOT_SPECIFIC_INFO][DATA], - addendum_dict["graphic_0"], - ) - filter_list = graphic_0_dict[SELECTABLE_DATA_DICT][FILTER] - assert len(filter_list[0][ACTIVE_SELECTORS]) == 1 - assert "MALE" in filter_list[0][ACTIVE_SELECTORS] - assert len(filter_list[1][ACTIVE_SELECTORS]) == 2 - assert "Torgersen" in filter_list[1][ACTIVE_SELECTORS] - assert "Dream" in filter_list[1][ACTIVE_SELECTORS] - - numerical_filter_list = graphic_0_dict[SELECTABLE_DATA_DICT][NUMERICAL_FILTER] - assert numerical_filter_list[0][ACTIVE_SELECTORS][MAX][VALUE] == "4" - assert numerical_filter_list[0][ACTIVE_SELECTORS][MIN][VALUE] == "" - - -def test_add_active_selectors_to_selectable_data_list_with_SHOW_ALL_ROWS_chosen( - single_page_config_dict, -): - addendum_dict = { - "filter_0": ["MALE"], - "filter_1": [SHOW_ALL_ROW], - "numerical_filter_0_max_value": ["4"], - "numerical_filter_0_min_value": [""], - } - graphic_0_dict = single_page_config_dict["graphic_0"] - add_active_selectors_to_selectable_data_list( - graphic_0_dict[SELECTABLE_DATA_DICT], - graphic_0_dict[PLOT_SPECIFIC_INFO][DATA], - addendum_dict, - ) - assert len(graphic_0_dict[SELECTABLE_DATA_DICT][FILTER][1][ACTIVE_SELECTORS]) == 1 - assert ( - SHOW_ALL_ROW - in graphic_0_dict[SELECTABLE_DATA_DICT][FILTER][1][ACTIVE_SELECTORS] - ) - - -def test_add_active_selectors_to_selectable_data_list_without_addendum( - single_page_config_dict, -): - single_page_config_dict = single_page_config_dict - graphic_0_dict = single_page_config_dict["graphic_0"] - graphic_0_dict[SELECTABLE_DATA_DICT][FILTER][1][DEFAULT_SELECTED] = ["Dream"] - add_active_selectors_to_selectable_data_list( - graphic_0_dict[SELECTABLE_DATA_DICT], - graphic_0_dict[PLOT_SPECIFIC_INFO][DATA], - {}, - ) - - filter_list = graphic_0_dict[SELECTABLE_DATA_DICT][FILTER] - assert len(filter_list[0][ACTIVE_SELECTORS]) == 1 - assert SHOW_ALL_ROW in filter_list[0][ACTIVE_SELECTORS] - assert len(filter_list[1][ACTIVE_SELECTORS]) == 1 - assert "Dream" in filter_list[1][ACTIVE_SELECTORS] - - numerical_filter_list = graphic_0_dict[SELECTABLE_DATA_DICT][NUMERICAL_FILTER] - assert numerical_filter_list[0][ACTIVE_SELECTORS][MAX][VALUE] == "" - assert numerical_filter_list[0][ACTIVE_SELECTORS][MIN][VALUE] == "" - - -def test_add_operations_to_the_data(single_page_config_dict, addendum_dict): - single_page_config_dict = single_page_config_dict - graphic_0_dict = single_page_config_dict["graphic_0"] - operations_list, groupby_dict = add_operations_to_the_data_from_addendum( - graphic_0_dict[SELECTABLE_DATA_DICT], - graphic_0_dict[PLOT_SPECIFIC_INFO][DATA], - addendum_dict["graphic_0"], - ) - assert not groupby_dict - assert len(operations_list) == 3 - - assert operations_list[0] == { - "type": "filter", - "column": "penguin_size:sex", - "selected": ["MALE"], - } - assert operations_list[1] == { - "type": "filter", - "column": "penguin_size:island", - "selected": ["Torgersen", "Dream"], - } - assert operations_list[2] == { - "type": "numerical_filter", - "column": "penguin_size:culmen_length_mm", - "operation": "<=", - "value": 4.0, - } - - # test two - - graphic_1_dict = single_page_config_dict["graphic_1"] - addendum_dict = { - "axis_0": ["penguin_size:culmen_depth_mm"], - GROUPBY: ["penguin_size:island"], - } - - operations_list, groupby_dict = add_operations_to_the_data_from_addendum( - graphic_1_dict[SELECTABLE_DATA_DICT], - graphic_1_dict[PLOT_SPECIFIC_INFO][DATA], - addendum_dict, - ) - - assert ( - single_page_config_dict[GRAPHIC_NUM.format(1)][PLOT_SPECIFIC_INFO][DATA][0]["x"] - == "penguin_size:culmen_depth_mm" - ) - assert groupby_dict == { - COLUMN_NAME: ["penguin_size:island"], - } - - -def test_add_instructions_to_config_dict(single_page_config_dict, addendum_dict): - single_page_config_dict = single_page_config_dict - single_page_config_dict_test = copy.deepcopy(single_page_config_dict) - single_page_config_dict_test = add_instructions_to_config_dict( - single_page_config_dict_test, None - ) - # add instructions should call the other two methods which I am already testing for. - # So I want to make sure it in actually doing something - assert single_page_config_dict_test != single_page_config_dict - assert DATA_FILTERS not in single_page_config_dict_test[GRAPHIC_NUM.format(0)] - - single_page_config_dict_test = copy.deepcopy(single_page_config_dict) - single_page_config_dict_test = add_instructions_to_config_dict( - single_page_config_dict_test, addendum_dict - ) - assert DATA_FILTERS in single_page_config_dict_test[GRAPHIC_NUM.format(0)] - - -def test_add_instructions_to_config_dict_with_different_addendum( - single_page_config_dict, addendum_dict -): - single_page_config_dict = single_page_config_dict - single_page_config_dict_test = copy.deepcopy(single_page_config_dict) - new_addendum_dict = {"a_different_graph": addendum_dict["graphic_0"]} - single_page_config_dict_test = add_instructions_to_config_dict( - single_page_config_dict_test, new_addendum_dict - ) - graphic_0_dict = single_page_config_dict_test["graphic_0"] - assert len(graphic_0_dict[SELECTABLE_DATA_DICT][FILTER][0][ACTIVE_SELECTORS]) == 1 - assert ( - SHOW_ALL_ROW - in graphic_0_dict[SELECTABLE_DATA_DICT][FILTER][0][ACTIVE_SELECTORS] - ) - - -def test_get_key_for_form(): - assert "filter_1" == get_key_for_form(FILTER, 1) - assert "numerical_filter_4" == get_key_for_form(NUMERICAL_FILTER, 4) - assert "groupby" == get_key_for_form(GROUPBY, "") - assert "axis_0" == get_key_for_form(AXIS, 0) - - -def test_add_operations_to_the_data_from_defaults(single_page_config_dict): - selectable_data_dict = single_page_config_dict["graphic_0"][SELECTABLE_DATA_DICT] - selectable_data_dict[NUMERICAL_FILTER][0][MIN] = 2 - operations_list = add_operations_to_the_data_from_defaults(selectable_data_dict) - - assert len(operations_list) == 1 - - assert operations_list[0] == { - "type": "numerical_filter", - "column": "penguin_size:culmen_length_mm", - "operation": ">=", - "value": 2.0, - } diff --git a/escalation/tests/test_schema_valid.py b/escalation/tests/test_schema_valid.py index b4e1b78..27f0fd2 100644 --- a/escalation/tests/test_schema_valid.py +++ b/escalation/tests/test_schema_valid.py @@ -1,11 +1,8 @@ -from utility.build_schema import build_graphic_schema_with_plotly import jsonschema def test_if_schema_is_valid_schema(): # The input needs to be nonempty lists of strings for it to be a valid schema - schema = build_graphic_schema_with_plotly( - ["words"], ["words"], ["words"], ["words"], ["a", "b"] - ) + schema = {} jsonschema.Draft7Validator.check_schema(schema) - assert True + assert False diff --git a/escalation/tests/test_sql_handler.py b/escalation/tests/test_sql_handler.py index 1b1b860..0d426a9 100644 --- a/escalation/tests/test_sql_handler.py +++ b/escalation/tests/test_sql_handler.py @@ -3,10 +3,12 @@ import pandas as pd import pytest +import datetime from sqlalchemy.types import Integer, Text, Float, DateTime, ARRAY, Boolean from database.sql_handler import SqlDataInventory, SqlHandler from utility.constants import * +from test_app_deploy_data.models import PenguinSize, DataUploadMetadata PENGUIN_SIZE = "penguin_size" PENGUIN_SIZE_SMALL = "penguin_size_small" @@ -32,6 +34,11 @@ def get_sql_handler_fixture_small(rebuild_test_database): return SqlHandler({MAIN_DATA_SOURCE: {DATA_SOURCE_TYPE: "penguin_size_small"}}) +@pytest.fixture() +def get_sql_handler_fixture_temperature(rebuild_test_database): + return SqlHandler({MAIN_DATA_SOURCE: {DATA_SOURCE_TYPE: "temperature"}}) + + def test_sql_handler_init(sql_handler_fixture): data_sources = sql_handler_fixture.flat_data_sources assert "penguin_size" == data_sources[0][DATA_SOURCE_TYPE] @@ -40,10 +47,10 @@ def test_sql_handler_init(sql_handler_fixture): def test_get_column_data_no_filter(get_sql_handler_fixture_small): # also test apply filters to data - data_dict = [ + data_dict = { "penguin_size_small:body_mass_g", "penguin_size_small:flipper_length_mm", - ] + } test_dict = get_sql_handler_fixture_small.get_column_data(data_dict) assert (test_dict["penguin_size_small:body_mass_g"] == [3750, 3800, 3250]).all() assert (test_dict["penguin_size_small:flipper_length_mm"] == [181, 186, 195]).all() @@ -51,10 +58,10 @@ def test_get_column_data_no_filter(get_sql_handler_fixture_small): def test_get_column_data_filter(get_sql_handler_fixture_small): # also test apply filters to data - data_dict = [ + data_dict = { "penguin_size_small:body_mass_g", "penguin_size_small:flipper_length_mm", - ] + } test_dict = get_sql_handler_fixture_small.get_column_data( data_dict, [{"type": "filter", "column": "penguin_size_small:sex", "selected": ["MALE"]}], @@ -64,10 +71,10 @@ def test_get_column_data_filter(get_sql_handler_fixture_small): def test_get_column_data_numerical_filter(get_sql_handler_fixture_small): - data_dict = [ + data_dict = { "penguin_size_small:body_mass_g", "penguin_size_small:flipper_length_mm", - ] + } test_dict = get_sql_handler_fixture_small.get_column_data( data_dict, [ @@ -84,6 +91,29 @@ def test_get_column_data_numerical_filter(get_sql_handler_fixture_small): assert list(test_dict["penguin_size_small:flipper_length_mm"]) == [181, 186] +def test_get_column_data_numerical_filter_datetime(get_sql_handler_fixture_temperature): + data_dict = { + "temperature:Date", + "temperature:Temp", + } + test_dict = get_sql_handler_fixture_temperature.get_column_data( + data_dict, + [ + { + "type": "numerical_filter", + "column": "temperature:Date", + "operation": "<=", + "value": datetime.datetime(1981, 1, 2), + } + ], + ) + + assert list(test_dict["temperature:Date"]) == [ + datetime.datetime(1981, 1, 1), + datetime.datetime(1981, 1, 2), + ] + + def test_get_column_unique_entries(sql_handler_fixture): unique_entries = sql_handler_fixture.get_column_unique_entries( ["penguin_size:sex", "penguin_size:island",] @@ -110,7 +140,7 @@ def test_build_combined_data_table(sql_handler_fixture): penguin_size, penguin_mean, how="inner", on=["study_name", "sex", "species"] ) num_rows_in_inner_table = inner_join_table.shape[0] - rows = sql_handler_fixture.get_column_data([f"{PENGUIN_SIZE}:{CULMEN_DEPTH}"])[ + rows = sql_handler_fixture.get_column_data({f"{PENGUIN_SIZE}:{CULMEN_DEPTH}"})[ f"{PENGUIN_SIZE}:{CULMEN_DEPTH}" ] num_rows_in_combined_table = len(rows) @@ -130,11 +160,11 @@ def test_build_combined_data_table_with_filtered_data_source(sql_handler_fixture ) num_rows_in_inner_table = inner_join_table.shape[0] SqlDataInventory.update_data_upload_metadata_active( - PENGUIN_SIZE, {1: "ACTIVE", 2: "INACTIVE"} + PENGUIN_SIZE, {"id_1": "ACTIVE", "id_2": "INACTIVE"} ) num_rows_in_combined_table = len( sql_handler_fixture.get_column_data( - [f"{PENGUIN_SIZE}:{ISLAND}"], + {f"{PENGUIN_SIZE}:{ISLAND}"}, [{"type": FILTER, "column": f"{PENGUIN_SIZE}:upload_id", "selected": [1],}], )[f"{PENGUIN_SIZE}:{ISLAND}"] ) @@ -147,7 +177,8 @@ def test_get_available_data_sources(rebuild_test_database): assert "penguin_size" in file_names assert "mean_penguin_stat" in file_names assert "penguin_lter_small" in file_names - assert len(file_names) == 4 + assert "temperature" in file_names + assert len(file_names) == 7 def get_column_names_for_data_source(get_sql_handler_fixture): @@ -221,11 +252,51 @@ def test_get_schema_for_lter_data_source(get_sql_handler_fixture_lter_table): "penguin_lter_small:clutch_completion": Boolean, } for k, v in expected_column_types.items(): - assert isinstance(column_types_dict[k], v), k - + assert isinstance(column_types_dict[k], v) + + +def test_write_data_upload_to_backend( + rebuild_test_database, + penguin_size_csv_file, + sql_data_inventory_fixture, + test_app_client_sql_backed_development_env, +): + penguin_size_df = pd.read_csv(penguin_size_csv_file) + session = test_app_client_sql_backed_development_env.db_session + db_response = session.query(PenguinSize.upload_id).distinct() + upload_ids_in_db = sorted([x.upload_id for x in db_response]) + expected_upload_ids = [1, 2] + # check that the state of the db is as expected after setup + assert upload_ids_in_db == expected_upload_ids + + # write the upload and look for a new upload id + ignored_columns = sql_data_inventory_fixture.write_data_upload_to_backend( + penguin_size_df, "test_user", "test_write_data_upload_to_backend_notes" + ) + # check that no columns in the dataset were omitted from the db table write + assert ignored_columns == set() + db_response = session.query(PenguinSize.upload_id).distinct() + upload_ids_in_db = sorted([x.upload_id for x in db_response]) + expected_upload_ids = [1, 2, 3] + assert upload_ids_in_db == expected_upload_ids + + # check that extra columns in the csv not defined in table are ignored + penguin_size_df["column_in_upload_not_db_schema"] = -99 + # write the upload and look for a new upload id + ignored_columns = sql_data_inventory_fixture.write_data_upload_to_backend( + penguin_size_df, "test_user", "test_write_data_upload_to_backend_notes" + ) + assert ignored_columns == {"column_in_upload_not_db_schema"} -def test_write_data_upload_to_backend(): - assert False + # check that the metadata table has been updated + db_response = ( + session.query(DataUploadMetadata.upload_id) + .filter(DataUploadMetadata.table_name == "penguin_size") + .distinct() + ) + upload_ids_in_db = sorted([x.upload_id for x in db_response]) + expected_upload_ids = [1, 2, 3, 4] + assert upload_ids_in_db == expected_upload_ids def test_get_table_data(get_sql_handler_fixture_small): diff --git a/escalation/tests/test_wizard_utils.py b/escalation/tests/test_wizard_utils.py index 5846c7e..d0aa06b 100644 --- a/escalation/tests/test_wizard_utils.py +++ b/escalation/tests/test_wizard_utils.py @@ -25,14 +25,18 @@ POSTGRES, PLOT_SPECIFIC_INFO, SELECTABLE_DATA_DICT, - VISUALIZATION_OPTIONS, GROUPBY_SELECTOR, MAX_ENTRIES_FOR_FILTER_SELECTOR, + AVAILABLE_PAGES, + WEBPAGE_LABEL, + URL_ENDPOINT, + GRAPHIC_CONFIG_FILES, + GRAPHIC_PATH, + GRAPHIC, ) from utility.wizard_utils import ( invert_dict_lists, sanitize_string, - prune_visualization_dict, prune_selector_dict, make_empty_component_dict, extract_data_sources_from_config, @@ -40,7 +44,7 @@ graphic_dict_to_graphic_component_dict, graphic_component_dict_to_graphic_dict, generate_collapse_dict_from_graphic_component_dict, - divide_columns_into_type_of_filters, + get_layout_for_dashboard, ) from sqlalchemy.types import Integer, Text, Float, DateTime, ARRAY, Boolean @@ -54,9 +58,9 @@ def test_invert_dict_lists(): def test_get_layout_for_dashboard(main_json_sql_backend_fixture): - # Need to set up file system - # new_dict = get_layout_for_dashboard(main_json_csv_backend_fixture[AVAILABLE_PAGES]) - # + # app context + # new_dict = get_layout_for_dashboard(main_json_sql_backend_fixture[AVAILABLE_PAGES]) + # ground_truth = ( # [ # { @@ -69,11 +73,12 @@ def test_get_layout_for_dashboard(main_json_sql_backend_fixture): # }, # { # WEBPAGE_LABEL: "Radio Penguins", - # URL_ENDPOINT: "radio_penguins", + # URL_ENDPOINT: "radio_penguins", # GRAPHIC_CONFIG_FILES: ["radio_penguins.json"], # }, # ], # ) + # assert new_dict == ground_truth assert False @@ -83,24 +88,6 @@ def test_sanitize_string(): assert sanitize_string(test_string) == sanitized -def test_prune_visualization_dict(): - visualization_dict = { - HOVER_DATA: { - COLUMN_NAME: ["penguin_size:sex", "penguin_size:culmen_length_mm",], - }, - GROUPBY: {COLUMN_NAME: ["penguin_size:island", "penguin_size:sex",],}, - } - - visualization_dict_copy = copy.deepcopy(visualization_dict) - visualization_dict_copy[AGGREGATE] = {} - assert prune_visualization_dict(visualization_dict_copy) == visualization_dict - empty_dict = { - HOVER_DATA: {COLUMN_NAME: [],}, - GROUPBY: {COLUMN_NAME: [],}, - } - assert prune_visualization_dict(empty_dict) == {} - - def test_prune_selector_dict(): select_dict = { FILTER: [ @@ -110,7 +97,6 @@ def test_prune_selector_dict(): NUMERICAL_FILTER: [{COLUMN_NAME: "penguin_size:culmen_length_mm",}], } select_dict_copy = copy.deepcopy(select_dict) - select_dict_copy[AXIS] = [] select_dict_copy[GROUPBY] = {} assert prune_selector_dict(select_dict_copy) == select_dict empty_dict = { @@ -123,10 +109,9 @@ def test_prune_selector_dict(): def test_make_empty_component_dict(): component_dict = make_empty_component_dict() assert component_dict[GRAPHIC_META_INFO] == {} - assert component_dict[VISUALIZATION] == {} assert component_dict[SELECTOR] == {} - assert component_dict[PLOTLY] == {} - assert len(component_dict.keys()) == 4 + assert component_dict[GRAPHIC] == {} + assert len(component_dict.keys()) == 3 def test_extract_data_sources_from_config(graphic_json_fixture): @@ -170,12 +155,7 @@ def test_copy_data_from_form_to_config(main_json_sql_backend_fixture): def test_graphic_dict_to_graphic_component_dict(graphic_json_fixture): test_config = graphic_json_fixture["graphic_1"] component_dict = graphic_dict_to_graphic_component_dict(test_config) - assert component_dict[VISUALIZATION] == { - "aggregate": {}, - "groupby": {}, - "hover_data": {}, - } - assert component_dict[PLOTLY] == test_config[PLOT_SPECIFIC_INFO] + assert component_dict[GRAPHIC] == test_config[PLOT_SPECIFIC_INFO] selector_dict_expected = test_config[SELECTABLE_DATA_DICT] selector_dict_expected[NUMERICAL_FILTER] = [] selector_dict_expected[FILTER] = [] @@ -197,76 +177,25 @@ def test_graphic_component_dict_to_graphic_dict(graphic_json_fixture): assert new_test_config == test_config -def test_get_data_source_info(): - assert False - - def test_generate_collapse_dict_from_graphic_component_dict(graphic_json_fixture): test_config = graphic_json_fixture["graphic_0"] collapse_dict = generate_collapse_dict_from_graphic_component_dict(test_config) true_collapse_dict = { ADDITIONAL_DATA_SOURCES: True, - HOVER_DATA: False, - GROUPBY: False, - AGGREGATE: True, FILTER: False, NUMERICAL_FILTER: False, - AXIS: True, GROUPBY_SELECTOR: True, - VISUALIZATION_OPTIONS: False, SELECTABLE_DATA_DICT: False, } assert collapse_dict == true_collapse_dict -def test_divide_columns_into_type_of_filters(): - column_types_dict = { - "col_1": Integer(), - "col_2": Text(), - "col_3": Float(), - "col_4": DateTime(), - "col_5": ARRAY("string"), - "col_6": Boolean(), - "col_7": Integer(), - "col_8": Float(), - "col_9": DateTime(), - "col_10": Text(), - "col_11": Boolean(), - "col_12": ARRAY("string"), - } - unique_entries = { - "col_1": [1 for i in range(MAX_ENTRIES_FOR_FILTER_SELECTOR + 1)], - "col_2": ["Dream" for i in range(MAX_ENTRIES_FOR_FILTER_SELECTOR - 1)], - "col_3": [2.5 for i in range(MAX_ENTRIES_FOR_FILTER_SELECTOR + 1)], - "col_4": ["11/10/2013" for i in range(MAX_ENTRIES_FOR_FILTER_SELECTOR + 1)], - "col_5": [[1, 4, 5] for i in range(MAX_ENTRIES_FOR_FILTER_SELECTOR - 1)], - "col_6": [True for i in range(MAX_ENTRIES_FOR_FILTER_SELECTOR - 1)], - "col_7": [1 for i in range(MAX_ENTRIES_FOR_FILTER_SELECTOR - 1)], - "col_8": [3.5 for i in range(MAX_ENTRIES_FOR_FILTER_SELECTOR - 1)], - "col_9": ["11/10/2014" for i in range(MAX_ENTRIES_FOR_FILTER_SELECTOR - 1)], - "col_10": ["Dream" for i in range(MAX_ENTRIES_FOR_FILTER_SELECTOR + 1)], - "col_11": [True for i in range(MAX_ENTRIES_FOR_FILTER_SELECTOR + 1)], - "col_12": [[1, 4, 5] for i in range(MAX_ENTRIES_FOR_FILTER_SELECTOR + 1)], - } - ( - filter_column_names, - numerical_filter_column_names, - unique_entries_dict, - ) = divide_columns_into_type_of_filters(unique_entries, column_types_dict) - assert set(numerical_filter_column_names) == { - "col_1", - "col_3", - "col_4", - "col_7", - "col_8", - "col_9", - } - filter_column_names_set = {"col_2", "col_5", "col_6", "col_7", "col_8", "col_9"} - assert set(filter_column_names) == filter_column_names_set - assert set(unique_entries_dict.keys()) == filter_column_names_set - assert unique_entries_dict["col_2"] == unique_entries["col_2"] - assert unique_entries_dict["col_5"] == unique_entries["col_5"] - assert unique_entries_dict["col_6"] == unique_entries["col_6"] - assert unique_entries_dict["col_7"] == unique_entries["col_7"] - assert unique_entries_dict["col_8"] == unique_entries["col_8"] - assert unique_entries_dict["col_9"] == unique_entries["col_9"] +class test_GraphicsConfigInterfaceBuilder: + def test_get_data_source_info(self): + assert False + + def build_individual_plot_type_schemas(self): + assert False + + def build_full_plot_manager_schema(self): + assert False diff --git a/escalation/utility/available_selectors.py b/escalation/utility/available_selectors.py index 6e5228a..bb82ea7 100644 --- a/escalation/utility/available_selectors.py +++ b/escalation/utility/available_selectors.py @@ -2,7 +2,7 @@ # Licensed under the Apache License, Version 2.0 # selectors are dropdowns, checkboxes etc. -from utility.constants import SELECT_HTML_TEMPLATE, SELECTOR_TYPE, TEXT, SELECTOR_NAME +from utility.constants import SELECT_HTML_TEMPLATE, TEXT, SELECTOR_NAME AVAILABLE_SELECTORS = { "filter": { @@ -10,11 +10,6 @@ TEXT: "Filter by {}", SELECTOR_NAME: "filter_{}", }, - "axis": { - SELECT_HTML_TEMPLATE: "selector.html", - TEXT: "{} axis", - SELECTOR_NAME: "axis_{}", - }, "groupby": { SELECT_HTML_TEMPLATE: "selector.html", TEXT: "Group by:", diff --git a/escalation/utility/build_plotly_schema.py b/escalation/utility/build_plotly_schema.py deleted file mode 100644 index 93c258a..0000000 --- a/escalation/utility/build_plotly_schema.py +++ /dev/null @@ -1,400 +0,0 @@ -# Copyright [2020] [Two Six Labs, LLC] -# Licensed under the Apache License, Version 2.0 -import copy - -from graphics.plotly_plot import LAYOUT, X, Y, Z, ERROR_X, ERROR_Y, ERROR_Z, ARRAY -from utility.constants import * - -MODE = "mode" - -BAR = "bar" -HEATMAP = "heatmap" -HEATMAPGL = "heatmapgl" -CONTOUR = "contour" -BOX = "box" -VIOLIN = "violin" -HISTOGRAM = "histogram" -HISTOGRAM2D = "histogram2d" -HISTOGRAM2DCONTOUR = "histogram2dcontour" -SCATTER3D = "scatter3d" -# SURFACE = "surface" -MESH3D = "mesh3d" - -PLOT_TYPE = "plot_type" - -COLORS = [ - "#1f77b4", - "#ff7f0e", - "#2ca02c", - "#d62728", - "#9467bd", - "#8c564b", - "#e377c2", - "#7f7f7f", - "#bcbd22", - "#17becf", -] - -COLOR_NAMES = [ - "blue", - "orange", - "green", - "red", - "purple", - "brown", - "pink", - "gray", - "yellow-green", - "blue-teal", -] - -SELECTOR_DICT = { - "$schema": "http://json-schema.org/draft/2019-09/schema#", - "title": "Type of Plot", - REQUIRED: [PLOT_TYPE], - PROPERTIES: { - PLOT_TYPE: { - TYPE: "string", - TITLE: "Plot Type", - "enum": [ - SCATTER, - BAR, - HEATMAP, - CONTOUR, - BOX, - VIOLIN, - HISTOGRAM, - HISTOGRAM2D, - SCATTER3D, - MESH3D, - ], - OPTIONS: { - "enum_titles": [ - "Scatter or Line Plot", - "Bar Plot", - "Heatmap", - "Contour Plot", - "Box Plot", - "Violin Plot", - "Histogram", - "2D Histogram", - "3D Scatter/Line Plot", - "3D Mesh Plot", - ] - }, - } - }, - OPTIONS: {DISABLE_COLLAPSE: True, DISABLE_PROPERTIES: True}, -} - - -def build_plotly_schema(column_names): - if column_names: - column_names.sort() - schema = { - "$schema": "http://json-schema.org/draft/2019-09/schema#", - "title": "Plotly Graph Config", - "description": "dictionary that follows https://plotly.com/javascript/reference/", - "type": "object", - "required": [DATA], - OPTIONS: {DISABLE_COLLAPSE: True, REMOVE_EMPTY_PROPERTIES: True}, - "defaultProperties": [DATA, LAYOUT], - "properties": { - DATA: { - "type": "array", - "description": "list of graphs to be plotted on a single plot", - TITLE: "Data", - MIN_ITEMS: 1, - OPTIONS: {DISABLE_COLLAPSE: True}, - "items": { - "type": "object", - "title": "Data Dictionary", - OPTIONS: {DISABLE_COLLAPSE: True}, - "required": ["type"], - "properties": { - "type": { - "type": "string", - TITLE: "Render Mode", - "enum": [ - "scatter", - "scattergl", - "bar", - "pie", - "heatmap", - "heatmapgl", - "image", - "contour", - "table", - "box", - "violin", - "histogram", - "histogram2d", - "histogram2dcontour", - "scatter3d", - "surface", - "mesh3d", - ], - OPTIONS: {HIDDEN: True}, - }, - X: { - "type": "string", - TITLE: "Data on X Axis", - "enum": column_names, - }, - Y: { - "type": "string", - TITLE: "Data on Y Axis", - "enum": column_names, - }, - Z: { - TYPE: "string", - TITLE: "Data on Y Axis", - ENUM: column_names, - }, - ERROR_X: { - TYPE: "object", - "properties": { - ARRAY: { - "type": "string", - TITLE: "Data column to use for error bars", - ENUM: column_names, - }, - }, - TITLE: "Symmetric error bars in the Y axis", - }, - ERROR_Y: { - TYPE: "object", - "properties": { - ARRAY: { - "type": "string", - TITLE: "Data column to use for error bars", - ENUM: column_names, - }, - }, - TITLE: "Symmetric error bars in the Y axis", - }, - ERROR_Z: { - TYPE: "object", - "properties": { - ARRAY: { - "type": "string", - TITLE: "Data column to use for error bars", - ENUM: column_names, - }, - }, - TITLE: "Symmetric error bars in the Z axis", - }, - "mode": { - "type": "string", - TITLE: "Graph Style", - "description": "used for scatter or scattergl", - "enum": [ - "markers", - "lines", - "text", - "lines+markers", - "markers+text", - "lines+text", - "lines+markers+text", - "none", - "group", - ], - }, - "opacity": { - "type": "number", - TITLE: "Opacity", - "minimum": 0, - "maximum": 1, - }, - }, - }, - }, - LAYOUT: { - "title": "Graph Layout", - "description": "Determines how the graph looks (optional)", - "type": "object", - OPTIONS: {DISABLE_COLLAPSE: True, REMOVE_EMPTY_PROPERTIES: True}, - "properties": { - "height": {"type": "number", "minimum": 10}, - "width": {"type": "number", "minimum": 10}, - "xaxis": { - "type": "object", - "properties": { - "title": { - "type": "object", - "properties": {"text": {"type": "string"}}, - }, - "automargin": {"type": "boolean"}, - }, - }, - "yaxis": { - "type": "object", - "properties": { - "title": { - "type": "object", - "properties": {"text": {"type": "string"}}, - }, - "automargin": {"type": "boolean"}, - }, - }, - "hovermode": { - "type": "string", - "enum": [ - "x", - "y", - "closest", - "false", - "x unified", - "y unified", - ], - }, - "barmode": { - "type": "string", - "enum": ["stack", "group", "overlay", "relative"], - }, - "shapes": { - TYPE: ARRAY, - ITEMS: { - "type": "object", - "title": "Shape Dictionary", - REQUIRED: ["type", "x0", "y0", "x1", "y1"], - PROPERTIES: { - "type": { - TYPE: "string", - "enum": ["line", "rect", "circle", "path",], - }, - "layer": {TYPE: "string", "enum": ["above", "below",],}, - "xref": {TYPE: "string", "enum": [X, "paper"]}, - "yref": {TYPE: "string", "enum": [Y, "paper"]}, - "x0": {TYPE: "number"}, - "x1": {TYPE: "number"}, - "y0": {TYPE: "number"}, - "y1": {TYPE: "number"}, - "line": { - TYPE: "object", - PROPERTIES: { - "color": { - TYPE: "string", - ENUM: COLORS, - "options": {"enum_titles": COLOR_NAMES}, - }, - "width": { - TYPE: "number", - "default": 2, - "minimum": 0, - }, - "dash": { - TYPE: "string", - ENUM: [ - "solid", - "dot", - "dash", - "longdash", - "dashdot", - "longdashdot", - ], - }, - }, - }, - "fillcolor": { - TYPE: "string", - ENUM: ["#ffffff"] + COLORS, - "options": {"enum_titles": ["white"] + COLOR_NAMES}, - }, - }, - }, - }, - }, - }, - }, - } - return schema - - -def build_plotly_schema_individual_dicts(column_names): - """ - Seperate schemas for each plot - :param column_names: - :return: - """ - # If the element is not in required we will delete it - programmed_data_options = [TYPE, X, Y, Z, MODE] - - directions_for_building_schemas = { - SCATTER: { - ENUM: [SCATTERGL, SCATTER], - REQUIRED: [TYPE, X, Y, MODE], - DESCRIPTION: { - TYPE: "scattergl uses uses WebGL which is faster for lots of points", - MODE: "marker for scatter plot, line for line plot", - }, - }, - BAR: {ENUM: [BAR], REQUIRED: [TYPE, X, Y],}, - BOX: { - ENUM: [BOX], - REQUIRED: [TYPE, Y], - DESCRIPTION: { - TYPE: "To show more than one box in the plot, " - 'set "group by" in visualization options below', - }, - }, - VIOLIN: { - ENUM: [VIOLIN], - REQUIRED: [TYPE, Y], - DESCRIPTION: { - TYPE: "To show more than one violin in the plot, " - 'set "group by" in visualization options below', - }, - }, - HISTOGRAM: {ENUM: [HISTOGRAM], REQUIRED: [TYPE, X],}, - CONTOUR: {ENUM: [CONTOUR], REQUIRED: [TYPE, X, Y, Z],}, - HISTOGRAM2D: {ENUM: [HISTOGRAM2D, HISTOGRAM2DCONTOUR], REQUIRED: [TYPE, X, Y],}, - MESH3D: {ENUM: [MESH3D], REQUIRED: [TYPE, X, Y, Z],}, - HEATMAP: { - ENUM: [HEATMAPGL, HEATMAP], - REQUIRED: [TYPE, X, Y, Z], - DESCRIPTION: { - TYPE: "heatmapgl uses WebGL which may be faster for lots of points" - }, - }, - SCATTER3D: { - ENUM: [SCATTER3D], - REQUIRED: [TYPE, X, Y, Z, MODE], - DESCRIPTION: { - MODE: "if using line make sure your points are in a sensible order" - }, - }, - } - - # used to figure based on type what schema should - # be used type in config dict (used in wizard ui) - schema_to_type = { - schema: schema_directions[ENUM] - for schema, schema_directions in directions_for_building_schemas.items() - } - - dict_of_schemas = {} - main_schema = build_plotly_schema(column_names) - for plot_type, directions in directions_for_building_schemas.items(): - plot_schema = copy.deepcopy(main_schema) - plot_schema[PROPERTIES][DATA][ITEMS][PROPERTIES][TYPE][ENUM] = directions[ENUM] - if len(directions[ENUM]) > 1: - plot_schema[PROPERTIES][DATA][ITEMS][PROPERTIES][TYPE][OPTIONS][ - HIDDEN - ] = False - plot_schema[PROPERTIES][DATA][ITEMS][REQUIRED] = directions[REQUIRED] - # removing unnecessary elements from the general Plotly schema - # that are not needed for this plot type - elements_to_delete = [ - x for x in programmed_data_options if x not in directions[REQUIRED] - ] - for element in elements_to_delete: - del plot_schema[PROPERTIES][DATA][ITEMS][PROPERTIES][element] - description_dict = directions.get(DESCRIPTION, {}) - for key_in_schema, description in description_dict.items(): - plot_schema[PROPERTIES][DATA][ITEMS][PROPERTIES][key_in_schema][ - DESCRIPTION - ] = description - dict_of_schemas[plot_type] = plot_schema - - return dict_of_schemas, schema_to_type diff --git a/escalation/utility/build_schema.py b/escalation/utility/build_schema.py index 9cf1549..f451a13 100644 --- a/escalation/utility/build_schema.py +++ b/escalation/utility/build_schema.py @@ -1,23 +1,13 @@ # Copyright [2020] [Two Six Labs, LLC] # Licensed under the Apache License, Version 2.0 -import json -from collections import defaultdict - -from graphics.plotly_plot import STYLES -from utility.build_plotly_schema import build_plotly_schema from utility.constants import * -NO_DOTS = r"^[^\\.]*$" -ALPHA_NUMERIC_NO_SPACES = r"^[a-zA-Z0-9_]+$" -ONE_DOT = r"^[^\\.]*\\.[^\\.]*$" -ONE_LETTER = r"^[a-zA-Z]$" -NON_EMPTY_STRING = r"[\s\S]+" # json schema specific constants see https://json-schema.org/ -def build_settings_schema(): +def build_dashboard_layout_schema(): schema = { "$schema": "http://json-schema.org/draft/2019-09/schema#", "title": "Escalation Main Config Generator", @@ -77,442 +67,3 @@ def build_settings_schema(): }, } return schema - - -def build_graphic_schema( - data_source_names=None, - column_names=None, - filter_column_names=None, - numerical_filter_column_names=None, - unique_entries=None, - collapse_dict=None, -): - """ - - :param data_source_names: names from DATA_SOURCES, already checked against the file system - :param column_names: possible column names from files or database (format data_source_name.column_name) - :param filter_column_names: possible column names from files or database for filtering or group by - :param numerical_filter_column_names: possible column names from files or database for numerical filter - :param unique_entries: values from possible column names - :param collapse_dict: whether the element should be collapsed or not - :return: - """ - if not collapse_dict: - collapse_dict = defaultdict(lambda: True) - for info_list in [ - data_source_names, - column_names, - filter_column_names, - numerical_filter_column_names, - unique_entries, - ]: - if info_list: - info_list.sort() - schema = { - "$schema": "http://json-schema.org/draft/2019-09/schema#", - "type": "object", - "title": "Escalation Graphic Config Generator", - "description": "This form configures a single graphic", - "required": [ - PLOT_MANAGER, - GRAPHIC_TITLE, - GRAPHIC_DESC, - DATA_SOURCES, - PLOT_SPECIFIC_INFO, - ], - "additionalProperties": False, - OPTIONS: {DISABLE_COLLAPSE: True, DISABLE_PROPERTIES: True}, - PROPERTIES: { - PLOT_MANAGER: { - "type": "string", - TITLE: "Plot Backend", - "description": "plot library you would like to use" - " (only plotly is currently available)", - "enum": ["plotly"], - }, - GRAPHIC_TITLE: { - "type": "string", - TITLE: "Graph Title", - PATTERN: NON_EMPTY_STRING, - }, - GRAPHIC_DESC: { - "type": "string", - TITLE: "Graph Description", - "description": "Text caption shown above the graph (optional)", - }, - DATA_SOURCES: { - "type": "object", - TITLE: "Data Sources", - "description": "Define which data tables are used in this graphic," - " and on which columns the data tables are joined", - "required": [MAIN_DATA_SOURCE], - OPTIONS: {DISABLE_COLLAPSE: True, DISABLE_PROPERTIES: True}, - PROPERTIES: { - MAIN_DATA_SOURCE: { - TITLE: "Main Data Source", - "type": "object", - "additionalProperties": False, - REQUIRED: [DATA_SOURCE_TYPE], - PROPERTIES: { - DATA_SOURCE_TYPE: { - "type": "string", - TITLE: "Data Source Type", - "enum": data_source_names, - }, - }, - OPTIONS: {DISABLE_COLLAPSE: True, DISABLE_PROPERTIES: True}, - }, - ADDITIONAL_DATA_SOURCES: { - "type": "array", - TITLE: "Additional Data Sources", - OPTIONS: {COLLAPSED: collapse_dict[ADDITIONAL_DATA_SOURCES],}, - ITEMS: { - "type": "object", - TITLE: "Additional Data Source", - "additionalProperties": False, - REQUIRED: [DATA_SOURCE_TYPE, JOIN_KEYS], - PROPERTIES: { - DATA_SOURCE_TYPE: { - "type": "string", - TITLE: "Data Source Type", - "enum": data_source_names, - }, - JOIN_KEYS: { - "type": "array", - TITLE: "Join Keys", - "description": "Column names along which to join the tables" - " (in the case of 2 or more tables)", - ITEMS: { - TITLE: "Pairs of Keys", - DESCRIPTION: "Use the command/ctr key to select more than one entry", - "type": "array", - "uniqueItems": True, - "minItems": 2, - "maxItems": 2, - "items": { - "type": "string", - TITLE: "Key", - "enum": column_names, - }, - }, - }, - }, - }, - }, - }, - }, - PLOT_SPECIFIC_INFO: { - "type": "object", - "title": "Plot Dictionary", - "description": "this dictionary depends on the graphing library", - }, - VISUALIZATION_OPTIONS: { - "type": "object", - "title": "Visualization Options", - "description": "Graph options: hover tooltips, aggregation functions, group by", - "additionalProperties": False, - OPTIONS: { - COLLAPSED: collapse_dict[VISUALIZATION_OPTIONS], - REMOVE_EMPTY_PROPERTIES: True, - }, - PROPERTIES: { - HOVER_DATA: { - "type": "object", - "title": "Hover Data", - "description": "data shown on hover over by mouse", - "required": [COLUMN_NAME], - OPTIONS: {COLLAPSED: collapse_dict[HOVER_DATA]}, - "properties": { - COLUMN_NAME: { - "type": "array", - TITLE: "List of Column Names", - OPTIONS: {DISABLE_COLLAPSE: True}, - "items": { - "type": "string", - TITLE: "Column Name", - "enum": column_names, - }, - } - }, - }, - GROUPBY: { - "type": "object", - "title": "Group By", - "description": "Grouping of the data see https://plotly.com/javascript/group-by/", - "required": [COLUMN_NAME], - OPTIONS: { - COLLAPSED: collapse_dict[GROUPBY], - REMOVE_EMPTY_PROPERTIES: True, - }, - "properties": { - COLUMN_NAME: { - "type": "array", - TITLE: "List of Column Names", - OPTIONS: {DISABLE_COLLAPSE: True}, - "items": { - "type": "string", - TITLE: "Column Name", - "enum": filter_column_names, - }, - }, - STYLES: { - TYPE: "object", - TITLE: "Styles", - DESCRIPTION: "Optional, see https://plotly.com/javascript/group-by/ for examples", - OPTIONS: {COLLAPSED: True}, - }, - }, - }, - AGGREGATE: { - "type": "object", - "title": "Aggregate", - "description": "See https://plotly.com/javascript/aggregations/ for examples", - "required": [COLUMN_NAME, AGGREGATIONS], - OPTIONS: {COLLAPSED: collapse_dict[AGGREGATE]}, - "properties": { - COLUMN_NAME: { - "type": "array", - TITLE: "List of Column Names", - OPTIONS: {DISABLE_COLLAPSE: True}, - "items": { - "type": "string", - TITLE: "Column Name", - "enum": column_names, - }, - }, - AGGREGATIONS: { - "type": "object", - TITLE: "Aggregations", - "description": "axis to function on the data e.g. x:avg", - OPTIONS: {DISABLE_COLLAPSE: True}, - "patternProperties": { - ONE_LETTER: { - "type": "string", - "description": "function on the data", - "enum": [ - "avg", - "sum", - "min", - "max", - "mode", - "median", - "count", - "stddev", - "first", - "last", - ], - } - }, - }, - }, - }, - }, - }, - SELECTABLE_DATA_DICT: { - "type": "object", - "title": "Data Selector Options", - "description": "Interactive data selectors: filter data by values, change axes," - " change columns to group by", - ADDITIONAL_PROPERTIES: False, - OPTIONS: { - COLLAPSED: collapse_dict[SELECTABLE_DATA_DICT], - REMOVE_EMPTY_PROPERTIES: True, - }, - PROPERTIES: { - FILTER: { - "type": "array", - "title": "List of Filters", - DESCRIPTION: "a filter operation based on label", - OPTIONS: {COLLAPSED: collapse_dict[FILTER]}, - "items": { - "type": "object", - TITLE: "Filter", - "id": "filter_item", - "required": [COLUMN_NAME], - "additionalProperties": False, - OPTIONS: {DISABLE_COLLAPSE: True}, - PROPERTIES: { - COLUMN_NAME: { - "type": "string", - TITLE: "Column Name", - "description": "any data column with fewer than 200 unique entries can be filtered" - " by identity matching and is listed here", - "enum": filter_column_names, - }, - "multiple": { - "type": "boolean", - TITLE: "Allow Multiple Selections", - DESCRIPTION: "Allow multiple values to be selected in this filter", - }, - DEFAULT_SELECTED: { - "type": "array", - TITLE: "Default Selected", - "description": "Optional, Default value(s) selected in this filter," - " a list of values to include", - "items": { - "type": "string", - # watch will call the functions in enumSource (default_selected_filter, - # identity_callback; defined in the JS) whenever COLUMN_NAME is changed, - # it will also store the value of COLUMN_NAME to a variable called COLUMN_NAME - # to be used by the JS - "watch": { - COLUMN_NAME: ".".join( - ["filter_item", COLUMN_NAME] - ) - }, - "enumSource": [ - { - "source": unique_entries, - "filter": "default_selected_filter", - "title": "identity_callback", - "value": "identity_callback", - } - ], - }, - }, - UNFILTERED_SELECTOR: { - "type": "boolean", - TITLE: "Should Selector Be Filtered", - DESCRIPTION: "If selector is filtered, the user can only select values in this" - " field that are present in the data subsetted by the" - " currently-applied filters", - }, - }, - }, - }, - NUMERICAL_FILTER: { - "type": "array", - "title": "List of Numerical Filters", - DESCRIPTION: "a filter operation on numerical data", - OPTIONS: {COLLAPSED: collapse_dict[NUMERICAL_FILTER]}, - "items": { - TITLE: "Numerical Filters", - "type": "object", - "required": [COLUMN_NAME], - OPTIONS: {DISABLE_COLLAPSE: True}, - "additionalProperties": False, - PROPERTIES: { - COLUMN_NAME: { - "type": "string", - TITLE: "Column Name", - "description": "name in table", - "enum": numerical_filter_column_names, - }, - MAX: { - TITLE: "Default Maximum", - "description": "Optional, set null for no max", - "oneOf": [{"type": "null"}, {"type": "number"},], - }, - MIN: { - TITLE: "Default Minimum", - "description": "Optional, set null for no min", - "oneOf": [{"type": "null"}, {"type": "number"},], - }, - }, - }, - }, - AXIS: { - "type": "array", - "title": "List of Axis Selectors", - DESCRIPTION: "change what column data is shown on a axis", - OPTIONS: {COLLAPSED: collapse_dict[AXIS]}, - "items": { - "type": "object", - TITLE: "Axis Selector", - OPTIONS: {DISABLE_COLLAPSE: True}, - "required": [COLUMN_NAME, ENTRIES], - "additionalProperties": False, - PROPERTIES: { - COLUMN_NAME: { - "type": "string", - TITLE: "Axis Name", - ENUM: ["x", "y", "z"], - OPTIONS: { - "enum_titles": ["X Axis", "Y Axis", "Z Axis"] - }, - }, - ENTRIES: { - "type": "array", - TITLE: "Entries", - "items": {"type": "string", "enum": column_names,}, - }, - }, - }, - }, - GROUPBY: { - "type": "object", - "title": "Group By Selector", - "required": [ENTRIES], - OPTIONS: {COLLAPSED: collapse_dict[GROUPBY_SELECTOR]}, - "additionalProperties": False, - PROPERTIES: { - ENTRIES: { - "type": "array", - OPTIONS: {DISABLE_COLLAPSE: True}, - TITLE: "Entries", - "items": { - "type": "string", - "enum": filter_column_names, - }, - }, - "multiple": {"type": "boolean"}, - DEFAULT_SELECTED: { - "type": "array", - TITLE: "Default Selected", - "description": "optional, default filter, list of column values", - "items": { - "type": "string", - "enum": filter_column_names, - }, - }, - }, - }, - }, - }, - }, - } - return schema - - -def build_graphic_schema_with_plotly( - data_source_names=None, - column_names=None, - filter_column_names=None, - numerical_filter_column_names=None, - unique_entries=None, -): - """ - If you are using the app with plotly this puts the plotly schema into the graphic schema - :param data_source_names: - :param column_names: - :return: - """ - schema = build_graphic_schema( - data_source_names, - column_names, - filter_column_names, - numerical_filter_column_names, - unique_entries, - ) - plotly_schema = build_plotly_schema(column_names) - schema[PROPERTIES][PLOT_SPECIFIC_INFO] = plotly_schema - return schema - - -def convert_python_dict_to_schema_json_file(): - """ - Makes the schema dictionaries into files (currently only used for generating documentation) - :return: - """ - for file_name, schema_dict in zip( - ["main.schema.json", "graphic.schema.json"], - [build_settings_schema(), build_graphic_schema_with_plotly()], - ): - json_object = json.dumps(schema_dict, indent=4) - with open(file_name, "w") as outfile: - outfile.write(json_object) - - -if __name__ == "__main__": - # if the schema is needed as a json file - convert_python_dict_to_schema_json_file() diff --git a/escalation/utility/constants.py b/escalation/utility/constants.py index c98b4dc..d7b8038 100644 --- a/escalation/utility/constants.py +++ b/escalation/utility/constants.py @@ -37,10 +37,9 @@ AGGREGATE = "aggregate" HOVER_DATA = "hover_data" AGGREGATIONS = "aggregations" - - -# path to the file folder for LocalHandler or table name for SqlHandler -DATA_FILE_DIRECTORY = "data_file_directory" +TRANSFORMS = "transforms" +GROUPS = "groups" +PLOT_TYPE = "plot_type" DATA_SOURCES = "data_sources" DATA_UPLOAD_METADATA = "data_upload_metadata" NEW_DATA_SOURCE = "new_data_source" @@ -53,7 +52,6 @@ DATA = "data" DATA_FILTERS = "data_filters" PLOT_MANAGER = "plot_manager" -VISUALIZATION_OPTIONS = "visualization_options" DATA_TO_PLOT_PATH = "data_to_plot_path" PLOT_SPECIFIC_INFO = "plot_specific_info" FILTERS = "filters" @@ -61,12 +59,35 @@ SELECTABLE_DATA_DICT = "selectable_data_dict" OPTION_TYPE = "type" OPTION_COL = "column" - +HOVERTEXT = "hovertext" PAGE_NAME = "name" +X = "x" +Y = "y" +Z = "z" +ERROR_X = "error_x" +ERROR_Y = "error_y" +ERROR_Z = "error_z" + +SCATTER = "scatter" +SCATTERGL = "scattergl" +BAR = "bar" +HEATMAP = "heatmap" +HEATMAPGL = "heatmapgl" +CONTOUR = "contour" +BOX = "box" +VIOLIN = "violin" +HISTOGRAM = "histogram" +HISTOGRAM2D = "histogram2d" +HISTOGRAM2DCONTOUR = "histogram2dcontour" +SCATTER3D = "scatter3d" +SURFACE = "surface" +MESH3D = "mesh3d" + # Available graphics keys GRAPH_HTML_TEMPLATE = "graph_html_template" OBJECT = "object" +SCHEMA_CLASS = "schema_class" # Available selector keys SELECT_HTML_TEMPLATE = "select_html_template" @@ -79,7 +100,7 @@ GROUPBY_SELECTOR = "groupby_selector" SELECTOR_TYPE = "type" FILTER = "filter" -UNFILTERED_SELECTOR = "unfiltered_selector" +FILTERED_SELECTOR = "filtered_selector" NUMERICAL_FILTER = "numerical_filter" INEQUALITIES = "inequalities" OPERATION = "operation" @@ -94,7 +115,8 @@ MULTIPLE = "multiple" NO_GROUP_BY = "No Group By" SELECTOR_NAME = "name" - +VISIBLE = "visible" +DATETIME = "datetime" # ALL of row SHOW_ALL_ROW = "Show All Rows" @@ -112,6 +134,7 @@ INDEX_COLUMN = "row_index" UPLOAD_ID = "upload_id" UPLOAD_TIME = "upload_time" +UPLOAD_FILENAME = "upload_filename" USERNAME = "username" NOTES = "notes" INACTIVE = "inactive" @@ -148,10 +171,19 @@ PATTERN = "pattern" REQUIRED = "required" MIN_ITEMS = "minItems" +MAX_ITEMS = "maxItems" OPTIONS = "options" DEPENDENCIES = "dependencies" +DEFAULT = "default" ENUM = "enum" +MINIMUM = "minimum" +MAXIMUM = "maximum" +ARRAY_STRING = "array" # ARRAY collides with sqlalchemy class name ONEOF = "oneOf" +STRING = "string" +NUMBER = "number" +INTEGER = "integer" +BOOLEAN = "boolean" # schema constants, these come from https://github.com/json-editor/json-editor HIDDEN = "hidden" @@ -159,22 +191,71 @@ COLLAPSED = "collapsed" DISABLE_PROPERTIES = "disable_properties" REMOVE_EMPTY_PROPERTIES = "remove_empty_properties" - -# plotly constants - -SCATTER = "scatter" -SCATTERGL = "scattergl" +DEFAULTPROPERTIES = "defaultProperties" # schema constants GRAPHIC_SCHEMA = "graphic_schema" -PLOTLY_SCHEMA = "plotly_schema" +PLOT_MANAGER_SCHEMA = "plot_manager_schema" VISUALIZATION_SCHEMA = "visualization_schema" SELECTOR_SCHEMA = "selector_schema" - +COLUMN_NAMES = "column_names" VISUALIZATION = "visualization" SELECTOR = "selector" PLOTLY = "plotly" +SEABORN = "seaborn" +CYTOSCAPE = "cytoscape" +PLOT_MANAGERS = [PLOTLY, SEABORN, CYTOSCAPE] GRAPHIC_META_INFO = "graphic_meta_info" GRAPHIC_PATH = "graphic_path" MAX_ENTRIES_FOR_FILTER_SELECTOR = 200 + +# JS schema constants +# need to match functions names in default_entries_dict in wizard_graphic_config_editor.html +COLUMN_VALUE_FILTER = "column_value_filter" +CALLBACK = "identity_callback" + +# preview functionality +PREVIEW = "preview" + +# Plotly API +ATTRIBUTES = "attributes" +LAYOUT_ATTRIBUTES = "layoutAttributes" + +# Network constants +START = "start" +END = "end" +WEIGHTS = "weights" +DIRECTED = "directed" +LINE_PROPERTIES = "line propeties" +MARKER_PROPERTIES = "marker propeties" + + +# cytoscape +NODE_ID = "node_id" +GROUP = "group" +NODES = "nodes" +ID = "id" +EDGE_ID = "edge_id" +SOURCE = "source" +TARGET = "target" +EDGES = "edges" +ELEMENTS = "elements" +STYLE = "style" + +ELEMENT_STYLE = "element_style" +ELEMENT_PROPERTIES = "element_properties" +PROPERTY_NAME = "property_name" +STYLE_NAME = "style_name" + +# regex patterns +NO_DOTS = r"^[^\\.]*$" +ALPHA_NUMERIC_NO_SPACES = r"^[a-zA-Z0-9_]+$" +ONE_LETTER = r"^[a-zA-Z]$" +NON_EMPTY_STRING = r"[\s\S]+" +POSTGRES_TABLE_NAME_FORMAT_REGEX = r"^[a-zA-Z_]\w+$" + +# seaborn +SEABORN_PLOT_BYTES = "seaborn_plot_bytes" +ASPECT_RATIO = "aspect_ratio" +FIGSIZE = "figsize" diff --git a/escalation/utility/reformatting_functions.py b/escalation/utility/reformatting_functions.py deleted file mode 100644 index 1103bd9..0000000 --- a/escalation/utility/reformatting_functions.py +++ /dev/null @@ -1,271 +0,0 @@ -# Copyright [2020] [Two Six Labs, LLC] -# Licensed under the Apache License, Version 2.0 - -from collections import defaultdict - -from werkzeug.datastructures import ImmutableMultiDict - -from utility.available_selectors import AVAILABLE_SELECTORS -from utility.constants import * - -NUMERICAL_FILTER_DICT = {MAX: "<=", MIN: ">="} - - -def add_instructions_to_config_dict( - single_page_graphic_config_dict: dict, addendum_dict: dict = None -) -> dict: - """ - We build a page based on 2 dictonaries, what is in the config and what is submitted in the HTML form. - :param single_page_graphic_config_dict: Copy of part of the original config dict. - Outside of getting the data, only functions in reformatting_functions.py should modifies the copy of config dict. - :param addendum_dict: e.g ImmutableMultiDict([('graphic_name', 'graphic_0'), ('selection_0', 'SHOW_ALL_ROW'), - ('selection_2_upper_operation', '<='), ('selection_2_upper_value', '4'))]) - Should not pass an empty ImmutableMultiDict - :return: modified single_page_config_dict - """ - if addendum_dict is None: - addendum_dict = {} - - for graphic_name, graphic_dict in single_page_graphic_config_dict.items(): - if SELECTABLE_DATA_DICT in graphic_dict: - selector_dict = graphic_dict[SELECTABLE_DATA_DICT] - data_info_dict = graphic_dict[PLOT_SPECIFIC_INFO][DATA] - if graphic_name in addendum_dict: - add_active_selectors_to_selectable_data_list( - selector_dict, data_info_dict, addendum_dict[graphic_name] - ) - ( - graphic_dict[DATA_FILTERS], - groupby_dict, - ) = add_operations_to_the_data_from_addendum( - selector_dict, data_info_dict, addendum_dict[graphic_name], - ) - # Visualization options does not have to be in the dictionary - if groupby_dict: - visualization_info_dict = graphic_dict.get( - VISUALIZATION_OPTIONS, {} - ) - visualization_info_dict[GROUPBY] = groupby_dict - graphic_dict[VISUALIZATION_OPTIONS] = visualization_info_dict - else: - add_active_selectors_to_selectable_data_list( - selector_dict, data_info_dict - ) - data_filters = add_operations_to_the_data_from_defaults(selector_dict) - if data_filters: - graphic_dict[DATA_FILTERS] = data_filters - return single_page_graphic_config_dict - - -def add_active_selectors_to_selectable_data_list( - selectable_data_dict: dict, data_info_dict: dict, addendum_dict: dict = None, -): - """ - Sets which selectors are active based on user choices. - If none have been selected sets reasonable defaults - :param selectable_data_dict: each element of the dictionary is a list of dictionaries - on how to build the selector on the webpage - :param data_info_dict: Dictionary that has which data goes in which plot - :param addendum_dict: User selections form the webpage - :return: - """ - - if addendum_dict is None: - addendum_dict = {} - - axis_list = selectable_data_dict.get(AXIS, []) - for index, axis_dict in enumerate(axis_list): - # all values in addendum_dict are lists - axis_label = addendum_dict.get(get_key_for_form(AXIS, index)) - axis_dict[ACTIVE_SELECTORS] = ( - axis_label[0] if axis_label else data_info_dict[0][axis_dict[COLUMN_NAME]] - ) - - if GROUPBY in selectable_data_dict: - group_by_dict = selectable_data_dict[GROUPBY] - selected_group_by = addendum_dict.get(get_key_for_form(GROUPBY, "")) - if not selected_group_by or NO_GROUP_BY in selected_group_by: - group_by_dict[ACTIVE_SELECTORS] = [NO_GROUP_BY] - else: - group_by_dict[ACTIVE_SELECTORS] = selected_group_by or group_by_dict.get( - DEFAULT_SELECTED, [NO_GROUP_BY] - ) - - filter_list = selectable_data_dict.get(FILTER, []) - for index, filter_dict in enumerate(filter_list): - - selected_filters = addendum_dict.get(get_key_for_form(FILTER, index), []) - if SHOW_ALL_ROW in selected_filters: - filter_dict[ACTIVE_SELECTORS] = [SHOW_ALL_ROW] - else: - filter_dict[ACTIVE_SELECTORS] = selected_filters or filter_dict.get( - DEFAULT_SELECTED, [SHOW_ALL_ROW] - ) - - numerical_filter_list = selectable_data_dict.get(NUMERICAL_FILTER, []) - for index, numerical_filter_dict in enumerate(numerical_filter_list): - extrema = [MAX, MIN] - active_numerical_filter_dict = defaultdict(dict) - for extremum in extrema: - # pull the relevant filter info from the submitted form - # all values in addendum_dict are lists - numerical_filter_value = addendum_dict.get( - NUMERICAL_FILTER_NUM_LOC_TYPE.format(index, extremum, VALUE) - ) - active_numerical_filter_dict[extremum][VALUE] = ( - numerical_filter_value[0] - if numerical_filter_value - else numerical_filter_dict.get(extremum, "") - ) - - numerical_filter_dict[ACTIVE_SELECTORS] = active_numerical_filter_dict - - -def add_operations_to_the_data_from_addendum( - selectable_data_dict: dict, data_info_list: list, addendum_dict: dict, -) -> list: - """ - Adds operations to be passed to the data handlers for the data - Broken into two major parts read in info from selection_dict and addendum_dict and then - output a filter dict or change visualization_info_list or data_info_dict depending on the kind of filter - :param selectable_data_dict: each element of the dictionary on is how to build the selector on the webpage - :param data_info_list: Dictionary that has which data goes in which plot - :param visualization_info_dict: dict of visualization options - :param addendum_dict: User selections form the webpage - :return: - """ - groupby_dict = {} - operation_list = [] - - # modifies the axis shown in the config - axis_list = selectable_data_dict.get(AXIS, []) - for index, axis_dict in enumerate(axis_list): - new_column_for_axis = addendum_dict.get(get_key_for_form(AXIS, index))[0] - axis = axis_dict[COLUMN_NAME] - for axis_data_dict in data_info_list: - axis_data_dict[axis] = new_column_for_axis - - # adds a group by - if GROUPBY in selectable_data_dict: - selection = addendum_dict.get(get_key_for_form(GROUPBY, "")) - if len(selection) > 0 and NO_GROUP_BY not in selection: - groupby_dict = {COLUMN_NAME: selection} - - # creates an operations where only the values selected along a column will be shown in the plot - filter_list = selectable_data_dict.get(FILTER, []) - for index, filter_dict in enumerate(filter_list): - base_info_dict_for_selector = get_base_info_for_selector(filter_dict, FILTER) - selection = addendum_dict.get(get_key_for_form(FILTER, index)) - if len(selection) == 0 or SHOW_ALL_ROW in selection: - continue - base_info_dict_for_selector[SELECTED] = selection - operation_list.append(base_info_dict_for_selector) - - # creates an operations where only the values following an (in)equality - # along a column will be shown in the plot - numerical_filter_list = selectable_data_dict.get(NUMERICAL_FILTER, []) - for index, numerical_filter_dict in enumerate(numerical_filter_list): - base_info_dict_for_selector = get_base_info_for_selector( - numerical_filter_dict, NUMERICAL_FILTER - ) - # the numerical filter contains two filters so add them separately - for extremum in [MAX, MIN]: - # get the value submitted in the web form by using its name - # format specified in numeric_filter.html - # the value is a list of length one - numerical_value = addendum_dict[ - NUMERICAL_FILTER_NUM_LOC_TYPE.format(index, extremum, VALUE) - ][0] - if numerical_value == "": - continue - numerical_filter_info = { - VALUE: float(numerical_value), - OPERATION: NUMERICAL_FILTER_DICT[extremum], - } - operation_list.append( - {**base_info_dict_for_selector, **numerical_filter_info} - ) - return operation_list, groupby_dict - - -def add_operations_to_the_data_from_defaults(selectable_data_dict: dict) -> list: - """ - Adds operations to be passed to the data handlers for the data - Broken into two major parts read in info from selection_dict and then output a filter dict - :param selectable_data_dict: each filter element of the dict is a list of dictionary on how to build - the selector on the webpage - :return: - """ - operation_list = [] - filter_list = selectable_data_dict.get(FILTER, []) - for index, filter_dict in enumerate(filter_list): - if DEFAULT_SELECTED in filter_dict: - base_info_dict_for_selector = get_base_info_for_selector( - filter_dict, FILTER - ) - selection = filter_dict.get(DEFAULT_SELECTED) - # make sure we don't have an empty selection list, - # or a list that only contains an empty string - if selection and selection != [""]: - base_info_dict_for_selector[SELECTED] = ( - selection if isinstance(selection, list) else [selection] - ) - operation_list.append(base_info_dict_for_selector) - - numerical_filter_list = selectable_data_dict.get(NUMERICAL_FILTER, []) - for index, numerical_filter_dict in enumerate(numerical_filter_list): - for extremum in [MAX, MIN]: - numerical_value = numerical_filter_dict.get(extremum) - if numerical_value == "" or numerical_value is None: - continue - base_info_dict_for_selector = get_base_info_for_selector( - numerical_filter_dict, NUMERICAL_FILTER - ) - numerical_filter_info = { - VALUE: float(numerical_value), - OPERATION: NUMERICAL_FILTER_DICT[extremum], - } - operation_list.append( - {**base_info_dict_for_selector, **numerical_filter_info} - ) - - return operation_list - - -def get_base_info_for_selector(selection_dict, selector_type): - """ - Sets up the basic dictionary for data filters to be added on the data - :param selection_dict: - :param selector_type: - :return: - """ - base_info_dict_for_selector = { - OPTION_TYPE: selector_type, - COLUMN_NAME: selection_dict.get(COLUMN_NAME, ""), - } - if UNFILTERED_SELECTOR in selection_dict: - base_info_dict_for_selector[UNFILTERED_SELECTOR] = True - return base_info_dict_for_selector - - -def get_key_for_form(selector_type, index): - selection_index_str = AVAILABLE_SELECTORS[selector_type][SELECTOR_NAME].format( - index - ) - return selection_index_str - - -def add_form_to_addendum_dict(form: ImmutableMultiDict, addendum_dict: dict): - """ - Used to update the addendum_dict that contains the previous graphic - selection elements with a new set of selections from a posted form - :param form: - :param addendum_dict: - :return: - """ - graphic_dict = {} - for key, value in form.lists(): - if key in [GRAPHIC_NAME, PROCESS]: - continue - graphic_dict[key] = value - addendum_dict[form.get(GRAPHIC_NAME)] = graphic_dict diff --git a/escalation/utility/schemas_for_ui.py b/escalation/utility/schemas_for_ui.py deleted file mode 100644 index 94c8d61..0000000 --- a/escalation/utility/schemas_for_ui.py +++ /dev/null @@ -1,48 +0,0 @@ -import copy - -from utility.build_plotly_schema import build_plotly_schema_individual_dicts -from utility.build_schema import * -from utility.constants import PROPERTIES, REQUIRED, ENUM - - -def build_main_schemas_for_ui(): - schema_database = build_settings_schema() - schema_database[PROPERTIES][DATA_BACKEND][ENUM] = POSTGRES - return {POSTGRES: schema_database} - - -def build_graphic_schemas_for_ui( - data_source_names=None, - column_names=None, - filter_column_names=None, - numerical_filter_column_names=None, - unique_entries=None, - collapse_dict=None, -): - """ - Builds 4 separate schemas to be used by the UI - :param data_source_names: - :param column_names: - :return: - """ - graphic_schema = build_graphic_schema( - data_source_names, - column_names, - filter_column_names, - numerical_filter_column_names, - unique_entries, - collapse_dict, - ) - # update these dictionaries when we add other plotting libraries - plotly_schemas, schema_to_type = build_plotly_schema_individual_dicts(column_names) - visualization_schema = graphic_schema[PROPERTIES].pop(VISUALIZATION_OPTIONS) - selector_schema = graphic_schema[PROPERTIES].pop(SELECTABLE_DATA_DICT) - graphic_schema[REQUIRED].remove(PLOT_SPECIFIC_INFO) - del graphic_schema[PROPERTIES][PLOT_SPECIFIC_INFO] - graphic_schemas = { - GRAPHIC_SCHEMA: graphic_schema, - PLOTLY_SCHEMA: plotly_schemas, - VISUALIZATION_SCHEMA: visualization_schema, - SELECTOR_SCHEMA: selector_schema, - } - return graphic_schemas, schema_to_type diff --git a/escalation/utility/wizard_utils.py b/escalation/utility/wizard_utils.py index d21a2b9..774f9e3 100644 --- a/escalation/utility/wizard_utils.py +++ b/escalation/utility/wizard_utils.py @@ -4,14 +4,12 @@ import re from flask import current_app -from sqlalchemy import Integer, Float, DateTime from utility.constants import * UI_SCHEMA_PAIRS = { - VISUALIZATION: VISUALIZATION_OPTIONS, SELECTOR: SELECTABLE_DATA_DICT, - PLOTLY: PLOT_SPECIFIC_INFO, + GRAPHIC: PLOT_SPECIFIC_INFO, } @@ -67,18 +65,13 @@ def graphic_dict_to_graphic_component_dict(graphic_dict): component_dict[ui_name] = graphic_dict_copy.pop(schema_name, {}) component_dict[GRAPHIC_META_INFO] = graphic_dict_copy - visualization_components = {HOVER_DATA: {}, GROUPBY: {}, AGGREGATE: {}} - selector_components = {FILTER: [], NUMERICAL_FILTER: [], AXIS: [], GROUPBY: []} + selector_components = {FILTER: [], NUMERICAL_FILTER: [], GROUPBY: []} # add in missing elements so the options show up in the json editor component_dict[GRAPHIC_META_INFO][DATA_SOURCES][ ADDITIONAL_DATA_SOURCES ] = component_dict[GRAPHIC_META_INFO][DATA_SOURCES].get(ADDITIONAL_DATA_SOURCES, []) - for component, empty_element in visualization_components.items(): - component_dict[VISUALIZATION][component] = component_dict[VISUALIZATION].get( - component, empty_element - ) for component, empty_element in selector_components.items(): component_dict[SELECTOR][component] = component_dict[SELECTOR].get( component, empty_element @@ -100,11 +93,7 @@ def graphic_component_dict_to_graphic_dict(graphic_component_dict): ): del graphic_dict[DATA_SOURCES][ADDITIONAL_DATA_SOURCES] - graphic_dict[PLOT_SPECIFIC_INFO] = graphic_component_dict[PLOTLY] - - visualization_dict = prune_visualization_dict(graphic_component_dict[VISUALIZATION]) - if visualization_dict: - graphic_dict[VISUALIZATION_OPTIONS] = visualization_dict + graphic_dict[PLOT_SPECIFIC_INFO] = graphic_component_dict[GRAPHIC] selector_dict = prune_selector_dict(graphic_component_dict[SELECTOR]) if selector_dict: @@ -121,17 +110,12 @@ def generate_collapse_dict_from_graphic_component_dict(graphic_dict): """ HIGH_LEVEL_COLLAPSE = { ADDITIONAL_DATA_SOURCES: [DATA_SOURCES, ADDITIONAL_DATA_SOURCES], - HOVER_DATA: [VISUALIZATION_OPTIONS, HOVER_DATA], - GROUPBY: [VISUALIZATION_OPTIONS, GROUPBY], - AGGREGATE: [VISUALIZATION_OPTIONS, AGGREGATE], FILTER: [SELECTABLE_DATA_DICT, FILTER], NUMERICAL_FILTER: [SELECTABLE_DATA_DICT, NUMERICAL_FILTER], - AXIS: [SELECTABLE_DATA_DICT, AXIS], GROUPBY_SELECTOR: [SELECTABLE_DATA_DICT, GROUPBY], } FIRST_LEVEL_COLLAPSE = { - VISUALIZATION_OPTIONS: [HOVER_DATA, GROUPBY, AGGREGATE], - SELECTABLE_DATA_DICT: [FILTER, NUMERICAL_FILTER, AXIS, GROUPBY], + SELECTABLE_DATA_DICT: [FILTER, NUMERICAL_FILTER, GROUPBY_SELECTOR], } collapse_dict = {} @@ -153,34 +137,14 @@ def get_default_collapse_dict(): """ keys = [ SELECTABLE_DATA_DICT, - VISUALIZATION_OPTIONS, ADDITIONAL_DATA_SOURCES, - HOVER_DATA, - GROUPBY, - AGGREGATE, FILTER, NUMERICAL_FILTER, - AXIS, GROUPBY_SELECTOR, ] return dict.fromkeys(keys, True) -def prune_visualization_dict(visualization_dict): - """ - Get rid of empty entries in visualization dict - :param visualization_dict: - :return: - """ - new_visualization_dict = {} - # when the form is left blank the entries of visualization_dict have - # COLUMN_NAME key that points to an empty list - for vis_key, vis_dict in visualization_dict.items(): - if vis_dict.get(COLUMN_NAME): - new_visualization_dict[vis_key] = vis_dict - return new_visualization_dict - - def prune_selector_dict(selector_dict): """ Get rid of empty entries in selector dict @@ -191,8 +155,6 @@ def prune_selector_dict(selector_dict): for sel_key, sel_info in selector_dict.items(): if sel_key == GROUPBY and sel_info.get(ENTRIES): new_selector_dict[sel_key] = sel_info - if sel_key == AXIS and sel_info: - new_selector_dict[sel_key] = sel_info if sel_key == FILTER and sel_info: new_sel_info = [] @@ -246,106 +208,6 @@ def get_layout_for_dashboard(available_pages_list): return available_pages_list_copy -def get_possible_column_names_and_values( - data_source_names, data_handler_class, get_unique_values=True -): - """ - Used to populate a dropdown in the config wizard with any column from the data - sources included in a figure, unique_entries used to populate default selected. - :param data_source_names: list of data source name strings - :param data_handler_class: backend-specific data inventory class - :return: possible_column_names list, unique_entries dict. - :param get_unique_values: if true calculates unique values for each column - """ - possible_column_names = [] - unique_entries = {} - column_types_dict = {} - for data_source_name in data_source_names: - data_inventory = data_handler_class( - data_sources={MAIN_DATA_SOURCE: {DATA_SOURCE_TYPE: data_source_name}} - ) - column_names_for_data_source = data_inventory.get_column_names_for_data_source() - column_types_dict_for_data_source = data_inventory.get_schema_for_data_source() - - possible_column_names.extend(column_names_for_data_source) - column_types_dict.update(column_types_dict_for_data_source) - if get_unique_values: - unique_entries_for_data_source = data_inventory.get_column_unique_entries( - column_names_for_data_source - ) - unique_entries.update(unique_entries_for_data_source) - return possible_column_names, column_types_dict, unique_entries - - -def get_data_source_info(active_data_source_names=None): - """ - gets the available data sources and the possible column names based on the data source in the config - :param active_data_source_names: list of data source name strings - :return: - """ - - if active_data_source_names is None: - active_data_source_names = [] - data_inventory_class = current_app.config.data_backend_writer - data_source_names = data_inventory_class.get_available_data_sources() - active_data_source_names = [ - data_source_name - for data_source_name in active_data_source_names - if data_source_name in data_source_names - ] - if data_source_names and not active_data_source_names: - # default to the first in alphabetical order - active_data_source_names = [min(data_source_names)] - ( - possible_column_names, - column_types_dict, - unique_entries, - ) = get_possible_column_names_and_values( - active_data_source_names, current_app.config.data_handler - ) - - ( - filter_column_names, - numerical_filter_column_names, - unique_entries_dict, - ) = divide_columns_into_type_of_filters(unique_entries, column_types_dict) - return ( - data_source_names, - possible_column_names, - filter_column_names, - numerical_filter_column_names, - unique_entries_dict, - ) - - -def divide_columns_into_type_of_filters(unique_entries, column_types_dict): - """ - We only allow numerical data types to be filtered with a numerical inequality filter, - and any data column with fewer than MAX_ENTRIES_FOR_FILTER_SELECTOR unique entries - to be filtered by identity matching - :param unique_entries: - :param column_types_dict: - :return: - """ - numerical_types = [Integer, Float, DateTime] - filter_column_names = [] - numerical_filter_column_names = [] - unique_entries_dict = {} - for col_name, list_of_entries in unique_entries.items(): - if any( - [ - isinstance(column_types_dict[col_name], num_type) - for num_type in numerical_types - ] - ): - numerical_filter_column_names.append(col_name) - if len(list_of_entries) <= MAX_ENTRIES_FOR_FILTER_SELECTOR: - filter_column_names.append(col_name) - unique_entries_dict[col_name] = list_of_entries - - return filter_column_names, numerical_filter_column_names, unique_entries_dict - - def extract_data_sources_from_config(graphic_config): """ parses out the data source names from the graph definition @@ -354,7 +216,7 @@ def extract_data_sources_from_config(graphic_config): """ if DATA_SOURCES in graphic_config: data_source_dict = graphic_config[DATA_SOURCES] - data_sources = set([data_source_dict[MAIN_DATA_SOURCE].get(DATA_SOURCE_TYPE)]) + data_sources = {data_source_dict[MAIN_DATA_SOURCE].get(DATA_SOURCE_TYPE)} additional_data_source_list = data_source_dict.get(ADDITIONAL_DATA_SOURCES, []) for additional_data_source_dict in additional_data_source_list: data_sources.add(additional_data_source_dict.get(DATA_SOURCE_TYPE)) diff --git a/escalation/version.py b/escalation/version.py index a5e148c..3f9296f 100644 --- a/escalation/version.py +++ b/escalation/version.py @@ -1,4 +1,4 @@ # Copyright [2020] [Two Six Labs, LLC] # Licensed under the Apache License, Version 2.0 -VERSION = "1.0.0" +VERSION = "2.0.0" diff --git a/escalation/views/dashboard.py b/escalation/views/dashboard.py index 2c0fb45..aaa8ae1 100644 --- a/escalation/views/dashboard.py +++ b/escalation/views/dashboard.py @@ -3,6 +3,7 @@ import json from flask import current_app, render_template, Blueprint, request, make_response +from werkzeug.datastructures import ImmutableMultiDict from werkzeug.exceptions import BadRequest from utility.constants import ( @@ -15,9 +16,9 @@ CURRENT_PAGE, ADDENDUM_DICT, DEVELOPMENT, + GRAPHIC_NAME, ) from controller import get_data_for_page -from utility.reformatting_functions import add_form_to_addendum_dict DATA_LAYOUT = "data_layout.html" @@ -86,3 +87,19 @@ def create_jumbotron_info(): SITE_DESC: config_dict.get(SITE_DESC, ""), } return jumbotron_info + + +def add_form_to_addendum_dict(form: ImmutableMultiDict, addendum_dict: dict): + """ + Used to update the addendum_dict that contains the previous graphic + selection elements with a new set of selections from a posted form + :param form: + :param addendum_dict: + :return: + """ + graphic_dict = {} + for key, value in form.lists(): + if key in [GRAPHIC_NAME, PROCESS]: + continue + graphic_dict[key] = value + addendum_dict[form.get(GRAPHIC_NAME)] = graphic_dict diff --git a/escalation/views/download.py b/escalation/views/download.py index cfa7e7b..3757864 100644 --- a/escalation/views/download.py +++ b/escalation/views/download.py @@ -16,7 +16,6 @@ SELECTED, ACTIVE, ) -from utility.wizard_utils import get_possible_column_names_and_values DOWNLOAD_HTML = "view_uploaded_data.html" @@ -44,7 +43,7 @@ def download_data(): OPTION_TYPE: FILTER, OPTION_COL: f"{data_source_name}:{UPLOAD_ID}", SELECTED: [ - upload_id + upload_id.split("_")[1] # upload_id are of form f"id_{upload_id}" for upload_id, selected in download_data_dict.items() if selected == ACTIVE # reusing the active logic from the admin page, diff --git a/escalation/views/file_upload.py b/escalation/views/file_upload.py index 06fafe3..635afdd 100644 --- a/escalation/views/file_upload.py +++ b/escalation/views/file_upload.py @@ -1,8 +1,7 @@ # Copyright [2020] [Two Six Labs, LLC] # Licensed under the Apache License, Version 2.0 -from flask import current_app, render_template, Blueprint, request, jsonify -from io import BufferedReader +from flask import current_app, render_template, Blueprint, request import pandas as pd from app_deploy_data.authentication import auth @@ -38,12 +37,12 @@ def validate_data_form(request_form, request_files): ] # require this field to be here, but no other checks username = request_form[USERNAME] data_source_name = request_form[DATA_SOURCE] - csvfile = request_files[CSVFILE] + # todo: validate file types in request_files except KeyError as e: raise ValidationError(e) current_app.logger.info(f"POST {username} {data_source_name}") - return username, data_source_name, csvfile + return username, data_source_name, request_files def validate_submission_content(csvfile, data_source_schema): @@ -55,6 +54,7 @@ def validate_submission_content(csvfile, data_source_schema): :return: pandas dataframe of the uploaded csv """ try: + filename = csvfile.filename df = pd.read_csv(csvfile, sep=",", comment="#") existing_column_names = { x.split(TABLE_COLUMN_SEPARATOR)[-1] for x in data_source_schema @@ -66,7 +66,7 @@ def validate_submission_content(csvfile, data_source_schema): # all of the columns in the existing data source are specified in upload assert set(df.columns).issuperset( existing_column_names - ), f"Upload missing expected columns {existing_column_names - set(df.columns)}" + ), f"Upload {filename} missing expected columns {existing_column_names - set(df.columns)}" # todo: match data types # todo- additional file type specific content validation except (AssertionError, ValueError) as e: @@ -74,13 +74,30 @@ def validate_submission_content(csvfile, data_source_schema): return df -def upload_page(success_text=None): +def get_data_sources(): data_inventory = current_app.config.data_backend_writer existing_data_sources = data_inventory.get_available_data_sources() - data_sources = sorted(existing_data_sources) - return render_template( - UPLOAD_HTML, data_sources=data_sources, success_text=success_text - ) + return sorted(existing_data_sources) + + +def upload_page( + template: str, success_text: str = None, validation_error_message: str = None +): + """ + :param template: html template name to render + :param success_text: string that if present, triggers a success popup on the HTML + :param validation_error_message: string that if present, triggers a failuire popup + on the HTML + :return: jinja rendered template + """ + data_sources = get_data_sources() + alert_flag = {} + # include at most one success or failure message for rendering on template + if success_text: + alert_flag["success_text"] = success_text + elif validation_error_message: + alert_flag["failure_text"] = validation_error_message + return render_template(template, data_sources=data_sources, **alert_flag) @upload_blueprint.route("/upload", methods=("POST",)) @@ -88,9 +105,8 @@ def upload_page(success_text=None): def submission(): try: # check the submission form - username, data_source_name, csvfile = validate_data_form( - request.form, request.files - ) + files = request.files.getlist(CSVFILE) + username, data_source_name, csvfiles = validate_data_form(request.form, files) notes = request.form.get(NOTES) # if the form of the submission is right, let's validate the content of the submitted file @@ -102,20 +118,28 @@ def submission(): ) data_source_schema = data_handler_class.get_column_names_for_data_source() - df = validate_submission_content(csvfile, data_source_schema) + # validate each uploaded file before writing any to db + dfs = [] + for file in files: + df = validate_submission_content(file, data_source_schema) + dfs.append(df) + # by combining dfs before upload we get a single upload_id and metadata write + df = pd.concat(dfs).reset_index(drop=True) data_inventory.write_data_upload_to_backend(df, username, notes) # write upload history table record at the same time except ValidationError as e: current_app.logger.info(e, exc_info=True) - # check if POST comes from script instead of web UI - return jsonify({"error": str(e)}), 400 + # return bad request code with a rendered template + return upload_page(template=UPLOAD_HTML, validation_error_message=str(e)), 400 + # todo: check if POST comes from script instead of web UI and return json + # return jsonify({"error": str(e)}), 400 # todo: log information about what the submission is current_app.logger.info("Added submission") - return upload_page("Success") + return upload_page(template=UPLOAD_HTML, success_text="Success") @upload_blueprint.route("/upload", methods=("GET",)) def submission_view(): - return upload_page() + return upload_page(template=UPLOAD_HTML) diff --git a/escalation/views/wizard_view.py b/escalation/views/wizard_view.py index 49331d1..d7a06ff 100644 --- a/escalation/views/wizard_view.py +++ b/escalation/views/wizard_view.py @@ -1,67 +1,45 @@ # Copyright [2020] [Two Six Labs, LLC] # Licensed under the Apache License, Version 2.0 -import itertools from io import open as io_open import json import os +import re from flask import current_app, render_template, Blueprint, request +import pandas as pd from sqlacodegen.codegen import CodeGenerator +from controller import get_data_for_page from database.sql_handler import CreateTablesFromCSVs, REPLACE, SqlDataInventory -from utility.build_plotly_schema import SELECTOR_DICT -from utility.constants import ( - DATA_BACKEND, - AVAILABLE_PAGES, - PAGE_ID, - GRAPHIC, - CONFIG_FILE_FOLDER, - CONFIG_DICT, - GRAPHIC_STATUS, - GRAPHIC_CONFIG_FILES, - WEBPAGE_LABEL, - URL_ENDPOINT, - SCATTER, - POSTGRES, - DATA, - TYPE, - PLOT_SPECIFIC_INFO, - COPY, - OLD, - NEW, - GRAPHIC_PATH, - GRAPHIC_TITLE, - APP_DEPLOY_DATA, - SQLALCHEMY_DATABASE_URI, - USERNAME, - NOTES, - APP_CONFIG_JSON, - DATA_SOURCE, - DATA_SOURCES, - COLLAPSE_DICT, +from graphics.graphic_schema import GraphicsConfigInterfaceBuilder +from graphics.utils.available_graphics import ( + PLOT_DELIMITER, + get_all_available_graphics, + AVAILABLE_GRAPHICS, ) -from utility.schemas_for_ui import build_graphic_schemas_for_ui +from utility.constants import * +from utility.exceptions import ValidationError from utility.wizard_utils import ( load_graphic_config_dict, save_main_config_dict, load_main_config_dict_if_exists, sanitize_string, - invert_dict_lists, - make_empty_component_dict, graphic_dict_to_graphic_component_dict, graphic_component_dict_to_graphic_dict, get_layout_for_dashboard, - get_data_source_info, extract_data_sources_from_config, copy_data_from_form_to_config, make_page_dict_for_main_config, generate_collapse_dict_from_graphic_component_dict, get_default_collapse_dict, ) +from views.file_upload import upload_page + GRAPHIC_CONFIG_EDITOR_HTML = "wizard_graphic_config_editor.html" CONFIG_FILES_HTML = "wizard_configurer.html" CSV_TO_DATABASE_UPLOAD_HTML = "wizard_data_upload.html" +GRAPHIC_CONFIG_LANDING_PAGE = "wizard_config_landing_page.html" wizard_blueprint = Blueprint("wizard", __name__) @@ -127,57 +105,60 @@ def modify_layout(): return file_tree() -@wizard_blueprint.route("/wizard/graphic", methods=("POST",)) +@wizard_blueprint.route("/wizard/graphic/landing", methods=("POST",)) +def graphic_landing_setup(): + return render_template( + GRAPHIC_CONFIG_LANDING_PAGE, + page_id=request.form[PAGE_ID], + graphic_status=request.form[GRAPHIC_STATUS], + graphic_path=request.form[GRAPHIC], + plot_dict=get_all_available_graphics(), + data_source_schema=json.dumps( + GraphicsConfigInterfaceBuilder.get_wizard_data_source_schema() + ), + ) + + +@wizard_blueprint.route("/wizard/graphic/config", methods=("POST",)) def graphic_config_setup(): graphic_status = request.form[GRAPHIC_STATUS] + graphic_dict = {} - active_data_source_names = None collapse_dict = get_default_collapse_dict() if graphic_status in [COPY, OLD]: graphic_dict = json.loads(load_graphic_config_dict(request.form[GRAPHIC])) - active_data_source_names = extract_data_sources_from_config(graphic_dict) + plot_manager = graphic_dict[PLOT_MANAGER] + plot_type = graphic_dict[PLOT_TYPE] collapse_dict = generate_collapse_dict_from_graphic_component_dict(graphic_dict) + else: + plot_manager, plot_type = request.form[PLOT_TYPE].split(PLOT_DELIMITER) + graphic_dict[PLOT_MANAGER] = plot_manager + graphic_dict[PLOT_TYPE] = plot_type + graphic_dict[DATA_SOURCES] = json.loads(request.form[DATA_SOURCES]) + active_data_source_names = extract_data_sources_from_config(graphic_dict) + kwargs = { + "active_data_source_names": active_data_source_names, + "collapse_dict": collapse_dict, + } + plot_info = AVAILABLE_GRAPHICS[plot_manager] + if plot_manager in PLOT_MANAGERS: + graphic_config = plot_info[SCHEMA_CLASS](**kwargs) + else: + raise ValueError(f"plot_manager {plot_manager} not recognized") - ( - data_source_names, - all_column_names, - filter_column_names, - numerical_filter_column_names, - unique_entries_dict, - ) = get_data_source_info(active_data_source_names) - - # concatenating into one large list with no duplicates - unique_entries_list = list( - set(itertools.chain.from_iterable(unique_entries_dict.values())) - ) - graphic_schemas, schema_to_type = build_graphic_schemas_for_ui( - data_source_names, - all_column_names, - filter_column_names, - numerical_filter_column_names, - unique_entries_list, - collapse_dict, - ) - component_graphic_dict = make_empty_component_dict() - current_schema = SCATTER - - if graphic_status in [COPY, OLD]: - type_to_schema = invert_dict_lists(schema_to_type) - current_schema = type_to_schema[ - graphic_dict[PLOT_SPECIFIC_INFO][DATA][0].get(TYPE, SCATTER) - ] - component_graphic_dict = graphic_dict_to_graphic_component_dict(graphic_dict) - + ui_formatted_schema = graphic_config.build_graphic_schemas_for_ui(plot_type) + component_graphic_dict = graphic_dict_to_graphic_component_dict(graphic_dict) return render_template( GRAPHIC_CONFIG_EDITOR_HTML, - schema=json.dumps(graphic_schemas, indent=4,), + # todo: rename 'schema' + schema=json.dumps(ui_formatted_schema, indent=4,), page_id=request.form[PAGE_ID], current_config=json.dumps(component_graphic_dict), graphic_status=graphic_status, - schema_selector_dict=json.dumps(SELECTOR_DICT), - current_schema=current_schema, + schema_selector_dict=json.dumps(graphic_config.plot_selector_dict), graphic_path=request.form[GRAPHIC], - default_entries_dict=json.dumps(unique_entries_dict), + default_entries_dict=json.dumps(graphic_config.unique_entries_dict), + graph_html_template=plot_info[OBJECT].get_graph_html_template(), ) @@ -240,88 +221,124 @@ def update_graphic_json_config_with_ui_changes(): ) -@wizard_blueprint.route("/wizard/graphic/update_schemas", methods=("POST",)) -def get_updated_schemas(): - - ui_editor_info_dict = request.get_json() - ( - data_source_names, - all_column_names, - filter_column_names, - numerical_filter_column_names, - unique_entries_dict, - ) = get_data_source_info(ui_editor_info_dict[DATA_SOURCES]) - - # concatenating into one large list with no duplicates - unique_entries_list = list( - set(itertools.chain.from_iterable(unique_entries_dict.values())) - ) - graphic_schemas, schema_to_type = build_graphic_schemas_for_ui( - data_source_names, - all_column_names, - filter_column_names, - numerical_filter_column_names, - unique_entries_list, - ui_editor_info_dict[COLLAPSE_DICT], +@wizard_blueprint.route("/wizard/graphic/preview", methods=("POST",)) +def preview_graphic_json_config(): + config_information_dict = request.get_json() + graphic_dict = graphic_component_dict_to_graphic_dict( + config_information_dict[CONFIG_DICT] ) + # get_data_for_page needs a graphic label + plot_specs = get_data_for_page({PREVIEW: graphic_dict}) + # plot_specs is more nested than we want for the preveiw so get the plot_dict and pass that to the html return ( - json.dumps({"new_schemas": graphic_schemas, "new_default_entries_dict": unique_entries_dict}, indent=4,), + json.dumps({"success": True, PREVIEW: plot_specs[0][JINJA_PLOT_INFO]}), 200, {"ContentType": "application/json"}, ) -def data_upload_page(success_text=None): - data_inventory_class = current_app.config.data_backend_writer - data_source_names = data_inventory_class.get_available_data_sources() - data_sources = sorted(data_source_names) - return render_template( - CSV_TO_DATABASE_UPLOAD_HTML, - data_sources=data_sources, - success_text=success_text, - ) - - @wizard_blueprint.route("/wizard/upload", methods=("GET",)) def data_upload_page_view(): - return data_upload_page() - - -def validate_table_name(): - # todo: form validate table name, but in js for pre-submit warning? - # POSTGRES_TABLE_NAME_FORMAT_REGEX = r"^[a-zA-Z_]\w+$" - # if not re.match(POSTGRES_TABLE_NAME_FORMAT_REGEX, table_name): - # print( - # "Table names name must start with a letter or an underscore;" - # " the rest of the string can contain letters, digits, and underscores." - # ) - # exit(1) - # if len(table_name) > 31: - # print( - # "Postgres SQL only supports table names with length <= 31-" - # " additional characters will be ignored" - # ) - # exit(1) - # if re.match("[A-Z]", table_name): - # print( - # "Postgres SQL table names are case insensitive- " - # "tablename will be converted to lowercase letters" - # ) - # - pass - - -def sql_backend_file_upload(upload_form, csvfile): + return upload_page(template=CSV_TO_DATABASE_UPLOAD_HTML) + + +def validate_table_name(table_name: str): + """ + ensures that a user-entered table name is supported by Postgres + """ + # todo: check there is any name at all? + if not re.match(POSTGRES_TABLE_NAME_FORMAT_REGEX, table_name): + message = ( + f"Check formatting of {table_name}" + "Table names name must start with a letter or an underscore; " + "the rest of the string can contain letters, digits, and underscores." + ) + elif len(table_name) > 31: + message = f"Table name {table_name} too long. Postgres SQL only supports table names with length <= 31" + elif re.match("[A-Z]", table_name): + # sanitize_string now silently lowercases in background- + # this is currently not reachable + message = ( + "Postgres SQL table names are case insensitive- " + "please use only lowercase letters" + ) + else: + # there are no validation issues + return + raise ValidationError(message) + + +def validate_wizard_upload_submission( + table_name: str, csvfiles, csv_sql_creator: CreateTablesFromCSVs +): + """ + Checks that the name of the uploaded table is valid, + and in the case of multiple uploaded files, that the schemas agree. + Raises ValidationError on any problems + :param table_name: sanitized flask form string + :param csvfiles: flask request files list + :param csv_sql_creator: CreateTablesFromCSVs instance + :return: combined dataframe from multiple files. + """ + validate_table_name(table_name) + data_file_schemas = None + # for each file in the uploaded list of files, + # assert that the columns contained are the same name and datatype + dfs = [] + mismatches = [] + + for csvfile in csvfiles: + data = csv_sql_creator.get_data_from_csv(csvfile) + df, schema = csv_sql_creator.get_schema_from_df(data) + if not data_file_schemas: + data_file_schemas = schema + base_schema_file = csvfile.filename + else: + # raise error if column names don't match + if set(schema) - set(data_file_schemas): + mismatch_columns = f"columns in {csvfile.filename} not in {base_schema_file}: {set(schema) - set(data_file_schemas)}" + mismatches.append(mismatch_columns) + if set(data_file_schemas) - set(schema): + mismatch_columns = f"columns in {base_schema_file} not in {csvfile.filename}: {set(data_file_schemas) - set(schema)}" + mismatches.append(mismatch_columns) + # raise error if column types don't match + for column_name, column_type in schema.items(): + if ( + column_name in data_file_schemas + and data_file_schemas[column_name] != column_type + ): + mismatches.append( + f"Data type mismatch: {column_name}: {base_schema_file}:{data_file_schemas[column_name].__name__} and {csvfile.filename}:{column_type.__name__}" + ) + if len(mismatches) > 0: + raise ValidationError( + f"The following columns are inconsistent- upload failed: {mismatches}" + ) + dfs.append(df) + return pd.concat(dfs).reset_index(drop=True) + + +# todo: move to file_upload.py +def sql_backend_file_to_table_upload(upload_form, csvfiles): + """ + + :param upload_form: flask request form + :param csvfiles: flask request files list + :return: None. Raises a validation error if there are problems with the formatting + """ table_name = sanitize_string(upload_form.get(DATA_SOURCE)) username = upload_form.get(USERNAME) notes = upload_form.get(NOTES) - csv_sql_writer = CreateTablesFromCSVs(current_app.config[SQLALCHEMY_DATABASE_URI]) - data = csv_sql_writer.get_data_from_csv(csvfile) + + csv_sql_creator = CreateTablesFromCSVs(current_app.config[SQLALCHEMY_DATABASE_URI]) + data = validate_wizard_upload_submission( + table_name=table_name, csvfiles=csvfiles, csv_sql_creator=csv_sql_creator + ) ( upload_id, upload_time, table_name, - ) = csv_sql_writer.create_and_fill_new_sql_table_from_df(table_name, data, REPLACE) + ) = csv_sql_creator.create_and_fill_new_sql_table_from_df(table_name, data, REPLACE) # remove any existing metadata for this table name and write a new row SqlDataInventory.remove_metadata_rows_for_table_name(table_name) @@ -335,9 +352,9 @@ def sql_backend_file_upload(upload_form, csvfile): ) # Generate a new models.py # update the metadata to include all tables in the db - csv_sql_writer.meta.reflect() + csv_sql_creator.meta.reflect() # write the database schema to models.py - generator = CodeGenerator(csv_sql_writer.meta, noinflect=True) + generator = CodeGenerator(csv_sql_creator.meta, noinflect=True) # Write the generated model code to the specified file or standard output models_filepath = os.path.join(APP_DEPLOY_DATA, "models.py") # remove and write new file to trigger file watcher and refresh flask app @@ -350,10 +367,27 @@ def sql_backend_file_upload(upload_form, csvfile): @wizard_blueprint.route("/wizard/upload", methods=("POST",)) def upload_csv_to_database(): upload_form = request.form - csvfile = request.files.get("csvfile") + csvfiles = request.files.getlist(CSVFILE) data_backend = current_app.config[APP_CONFIG_JSON].get(DATA_BACKEND) if data_backend in [POSTGRES]: - sql_backend_file_upload(upload_form, csvfile) + try: + sql_backend_file_to_table_upload(upload_form, csvfiles) + except ValidationError as e: + current_app.logger.info(e, exc_info=True) + return ( + upload_page( + template=CSV_TO_DATABASE_UPLOAD_HTML, + validation_error_message=str(e), + ), + 400, + ) + else: - return data_upload_page("Failure- data backend not recognized") - return data_upload_page("success") + return ( + upload_page( + template=CSV_TO_DATABASE_UPLOAD_HTML, + validation_error_message="Failure- data backend not recognized", + ), + 400, + ) + return upload_page(template=CSV_TO_DATABASE_UPLOAD_HTML, success_text="success")