# v.0.17

{% hint style="danger" %}
The package shimoku-api-python is no longer maintained
{% endhint %}

{% hint style="info" %}
To get the new version 🤖
{% endhint %}

```
pip install --upgrade shimoku-api-python
```

This is a major version that comes with a new module and a whole new infrastructure!

### Improvements

* Annotated Chart - A new type of chart has been added! This chart allows users to interactively change their data across a timeline. In the frontend, users can click inside the chart to display a modal with options to add a point or two to the timeline, as well as add an annotation for that point.

  The chart requires a separate dataset for each series to plot, and it needs the y-field-name specified for each of them. All datasets must have the same name for the x-field and for the annotations field. The x-field values must be of type Date or convertible to it, the y-field must be integers, and the annotation field values must be strings.&#x20;

  The modal that appears includes a slider to select the value that the user wants to add. This can be configured with the `slider_config` parameter, where the `max` value can be defined, and also the `defaultValue`. Labels can be added to the slider values using the `slider_marks` parameter.

  An example on how to use it would be:&#x20;

  ```python
  data1 = [
      {'date': '2022-01-01', 'Value [1]': 3},
      {'date': '2022-03-01', 'Value [1]': 7, 'Annotation': 'Value [1] rapid increase'},
      {'date': '2022-04-01', 'Value [1]': 7},
      {'date': '2022-07-01', 'Value [1]': 8},
      {'date': '2022-08-01', 'Value [1]': 9},
      {'date': '2022-12-01', 'Value [1]': 5},
  ]
  data2 = [
      {'date': '2021-12-31', 'Value [2]': 6},
      {'date': '2022-01-02', 'Value [2]': 9},
      {'date': '2022-02-01', 'Value [2]': 7},
      {'date': '2022-03-01', 'Value [2]': 8},
      {'date': '2022-04-01', 'Value [2]': 9},
      {'date': '2022-05-01', 'Value [2]': 7},
      {'date': '2022-06-01', 'Value [2]': 8, 'Annotation': 'Value [2] oscillation'},
      {'date': '2022-07-01', 'Value [2]': 9},
      {'date': '2022-08-01', 'Value [2]': 8},
  ]

  s.plt.annotated_chart(
      menu_path='Annotated Chart',
      order=7, x='date', y=['Value [1]', 'Value [2]'], annotation='Annotation',
      data=[data1, data2], slider_config={'max': 100, 'defaultValue': 50},
      slider_marks=[('Low', 15), ('Medium', 50), ('High', 85)]
  )
  ```

  resulting in:

  <figure><img src="/files/aFOVontC3LZvagkhGWOb" alt=""><figcaption><p>Annotated chart with two datasets</p></figcaption></figure>

  When clicking in the chart the modal appears like so:

  <figure><img src="/files/OoLtIFEcTnpNwLyq7Dq0" alt=""><figcaption><p>Annotation modal</p></figcaption></figure>

  It lets the user select a date range and the value of the point. Also it lets the user write an annotation in the bottom text field.

  If we use the following values:&#x20;

  <figure><img src="/files/8LNyDqlykWhg0GAb9yHK" alt=""><figcaption><p>Form ready to send</p></figcaption></figure>

  The result in the chart is:

  <figure><img src="/files/PJskL3Iwk8bcwi8Vz1WM" alt=""><figcaption><p>Annotation added</p></figcaption></figure>

* Before getting the logs of an activities run was done in a very cumbersome way, now it has a dedicated method:

  ```python
  s.activity.get_run_logs(
      menu_path: str, 
      activity_name: str, 
      run_id: str
  ) -> Dict
  ```

* A new module has been added that introduces a new navigation element: dashboards! A dashboard is an element that groups apps together and it will show as the first path element in the sidebar:&#x20;

  <figure><img src="/files/nz4M6xfOAQOsASoCY1aA" alt=""><figcaption><p>Dashboards hierarchy</p></figcaption></figure>

  This new element not only serves as an extended hierarchy, but it also permits a lot of new features, one of the most flashy ones being dashboards embedding, which in the future will permit inserting public dashboards in external pages.

  The module provides the following methods:

  <pre class="language-python"><code class="lang-python"># Sets the business for the whole module
  s.dashboard.set_business(business_id: str)

  # Creates a dashboard and return it's dictionary representation
  s.dashboard.create_dashboard(
      dashboard_name: str, order: Optional[int] = None, 
      public_permission: Optional[Dict] = None, is_disabled: bool = False
  ) -> Dict

  # Gets a dashboard, if the dashboard does not exit it returns None
  s.dashboard.get_dashboard(
      dashboard_name: Optional[str] = None, dashboard_id: Optional[str] = None
  ) -> Optional[Dict]

  # Deletes the specified dashboard, if it exists
  s.dashboard.delete_dashboard(
      dashboard_name: Optional[str] = None, dashboard_id: Optional[str] = None
  )

  # Updates the specified dashboard fields, if it exists
  s.dashboard.update_dashboard(
      dashboard_name: Optional[str] = None, dashboard_id: Optional[str] = None,
      name: Optional[str] = None, order: Optional[int] = None,
      public_permission: Optional[str] = None, is_disabled: Optional[bool] = None
  )

  # Adds an app to a specified dashboard, this creates a link object
  <strong>s.dashboard.add_app_in_dashboard(
  </strong>    app_id: str, dashboard_name: Optional[str] = None,
      dashboard_id: Optional[str] = None
  )

  # Gets all the business dashboards
  s.dashboard.get_dashboards() -> List[Dict]

  # Gets all the app_id's for a specified dashboard
  s.dashboard.get_dashboard_app_ids(
      dashboard_name: Optional[str] = None, dashboard_id: Optional[str] = None
  ) -> List[str]

  # Deletes the link between a given app and a given dashboard
  s.dashboard.remove_app_from_dashboard(
      app_id: str, dashboard_name: Optional[str] = None,
      dashboard_id: Optional[str] = None
  )

  # Deletes all business dashboards, id any dashboard has apps it returns an error
  s.dashboard.delete_all_business_dashboards()
  </code></pre>

  When referencing a dashboard, it can be done by its name or by its UUID. That's why all functions have optional parameters for the dashboard's name or its ID, but one of them must be provided, or an error will be returned.

  The module is useful for managing dashboards at a low level, in case some customization is needed or more information is required, but the most common use case is through the plotting module, which takes care of the apps' inclusion in dashboards automatically. For an app to be seen, it needs to be included in a dashboard, so if the user does not specify which dashboard to use, the SDK will take the name of the app being created and add the app to the dashboard: `{app_name} dashboard`.

  To specify the dashboard's name, the plotting module provides the method:

  ```python
                       s.plt.set_dashboard(dashboard_name: str)
  ```

  This way, all apps created through the plotting module after calling this method will be included in the same dashboard.

  An example on how to use this function is:

  ```python
  s.plt.set_dashboard('Dashboard groupping apps')
  data_ = {
          "footer": "Hello random people",
          "header": "True",
          "val": "359K",
          "alignment": "left",
          "icon": "Line/subscriptions",
          "color": "warning"
  }

  s.plt.indicator(
      data=data_,
      menu_path='SubGroup 1/sublevel 1',
      order=14,
      value='val',
      header='header',
      footer='footer',
      align='alignment',
      icon='icon',
      color='color',
      rows_size=1, cols_size=4,
  )

  s.plt.indicator(
      data=data_,
      menu_path='SubGroup 2/sublevel 1',
      order=14,
      value='val',
      header='header',
      footer='footer',
      align='alignment',
      icon='icon',
      color='color',
      rows_size=1, cols_size=4,
  )
  ```

  The result is:

  <figure><img src="/files/KLJQ6f7petXbMzN8GDDq" alt=""><figcaption><p>Dashboard with two apps created </p></figcaption></figure>

  It is possible that when using the set\_dashboard function the dashboard name is not show correctly in the sidebar, that might be because the code is reusing a previous app which allready belonged to a dashboard. The SDK will only create a dashboard when the app is being created so when an app is modified the dashboard will have the same name, to solve this we present two options:

  * One way would be deleting the app (or app link) of the created app and then deleting the dashboard, because dashboards with apps can not be deleted. The fastest way would be to use:

    ```python
    s.app.delete_all_business_apps()
    s.dashboard.delete_all_business_dashboards()
    ```

    This way you can delete all contents of a business, in case your business has lots of apps or dashboards. Beware of deleting apps with activities, as it will return an error.
  * The other way would be to rename the dashboard, with the update function:

    ```python
    s.dashboard.update_dashboard(
        dashboard_name=previous_dashboard_name, name=new_dashboard_name
    )
    ```

    &#x20;

* New methods to manage roles have been added! A role can be created from the SDK and can be later added to the desired users, providing a customization level to the platform's security. Some users might have restricted access to business apps, while others may have access to data modifications or activity executions.

  The roles can be managed from the following modules:

  &#x20;                    `s.app, s.dashboard, s.business`                     &#x20;

  The module from which the method is called will define the level of permission that the user with that role will have. If a user is granted a superior level of permission, they will have access to the lower levels too.

  The fields of the role object and it's permited values are:

  * 'role': str (it's the role's name)
    * 'permission': \['READ', 'WRITE']
    * 'target': \['GROUP', 'USER']
    * 'resource': \['DATA', 'DATA\_EXECUTION', 'USER\_MANAGEMENT', 'BUSINESS\_INFO']

  Each module has the following methods:

  * Business:

    ```python
    s.business.create_role(
        business_id: str, role_name: str, 
        resource: Optional[str] = None, permission: Optional[str] = None, 
        target: Optional[str] = None
    )
    s.business.get_roles(business_id: str)
    s.business.get_roles_by_name(business_id: str, role_name: str)
    s.business.delete_role(business_id: str, role_id: str)
    ```

  * Dashboard:

    (The dashboards can be referenced either by name or id)

    ```python
    s.dashboard.create_role(
      dashboard_name: Optional[str] = None, dashboard_id: Optional[str] = None,
      resource: Optional[str] = None, role_name: Optional[str] = None,
      permission: Optional[str] = None, target: Optional[str] = None
    )
    s.dashboard.get_roles(
      dashboard_name: Optional[str] = None, dashboard_id: Optional[str] = None
    )
    s.business.get_roles_by_name(role_name: str, 
      dashboard_name: Optional[str] = None, dashboard_id: Optional[str] = None
    )
    s.business.delete_role(role_id: str, 
      dashboard_name: Optional[str] = None, dashboard_id: Optional[str] = None
    )
    ```

  * App:

    ```python
    s.app.create_role(
        business_id: str, app_id: str,
        role_name: str, resource: Optional[str] = None, 
        permission: Optional[str] = None, target: Optional[str] = None
    )
    s.app.get_roles(business_id: str, app_id: str)
    s.app.get_roles_by_name(business_id: str, app_id: str, role_name: str)
    s.app.delete_role(business_id: str, app_id: str, role_id: str)
    ```

  An example on how to create, read and delete a role using the dashboards module is:

  ```python
  dashboard_name = 'roles_dashboard'
  s.dashboard.create_dashboard(dashboard_name=dashboard_name)

  role = s.dashboard.create_role(dashboard_name=dashboard_name, role_name='test_role')

  assert role['role'] == 'test_role'
  assert role['permission'] == 'READ'
  assert role['resource'] == 'BUSINESS_INFO'
  assert role['target'] == 'GROUP'

  roles = s.dashboard.get_roles(dashboard_name=dashboard_name)

  assert len(roles) == 1
  assert roles[0]['role'] == 'test_role'

  s.dashboard.delete_role(dashboard_name=dashboard_name, role_id=role['id'])

  assert len(s.dashboard.get_roles(dashboard_name=dashboard_name)) == 0

  s.dashboard.create_role(
      dashboard_name=dashboard_name, 
      role_name='test_role', permission='WRITE',
      resource='DATA', target='USER'
  )
  ```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.shimoku.com/dev/releases/2023/v.0.17.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
