0.8.0 (June 02, 2023)#



User interface#

  • Upgraded the app to Atoti UI 5.1.4. The dashboards and widgets saved in previous versions of Atoti need to be migrated. Follow this guide with the following modifications:

    Make sure to connect as a user with access to all the content that must be migrated. We recommend connecting as a user with the role ROLE_CS_ROOT.

    • No roles are required when using Atoti Community Edition.

    • Instead of ROLE_CS_ROOT, the ROLE_ADMIN role is required when migrating a secured Session. To not have to deal with roles and security, the migration can be performed from a local session with user_content_storage set to the one to migrate and authentication set to None.

    Create a file in the migration folder and name it servers.json

    To create that file, run the following code, adapting the logic to create a QuerySession targetting the session holding the user content storage to migrate:

    import json
    from pathlib import Path
    import atoti as tt
    # Replace the URL below with the one of the session to migrate.
    query_session = tt.QuerySession("http://localhost:56035")
    data_model = query_session._fetch_discovery()
    servers_json = json.dumps(
        {"default": {"url": query_session.url, "dataModel": data_model}},


  • Support for pandas 1. Update to pandas >= 2.0.0 instead.

  • UserServiceClient.basic.create_user(). Use credentials instead:

    - from atoti_plus import UserServiceClient
    - user_service_client = UserServiceClient.from_session(session)
    - user_service_client.basic.create_user(
    -     "Jean", password="mot de passe", roles=["ROLE_FRANCE"]
    - )
    - user_service_client.basic.create_user(
    -     "John", password="password", roles=["ROLE_UK"]
    - )
    + session.security.basic.credentials.update({
    +     "Jean": "mot de passe",
    +     "John": "password",
    + })
    + session.security.individual_roles.update({
    +     "Jean": {"ROLE_FRANCE", "ROLE_USER"},
    +     "John": {"ROLE_UK", "ROLE_USER"},
    + })
  • UserServiceClient.create_role and UserServiceClient.roles. Use restrictions instead:

    - from atoti_plus import UserServiceClient
    - user_service_client = UserServiceClient.from_session(session)
      table = session.create_table(
        "Example", types={"Country": tt.STRING, "Language": tt.STRING}
    - user_service_client.create_role(
    -     "ROLE_FRANCE",
    -     restrictions={
    -         (table.name, "Country"): {"France"},
    -         (table.name, "Language"): {"French"},
    -     },
    - )
    - user_service_client.create_role(
    -     "ROLE_USA",
    -     restrictions={
    -         (table.name, "Country"): {"USA"},
    -         (table.name, "Language"): {"English", "Spanish"},
    -     },
    - )
    + session.security.restrictions.update(
    +     {
    +         "ROLE_FRANCE": (table["Country"] == "France")
    +             & (table["Language"] == "French"),
    +         "ROLE_USA": (table["Country"] == "USA")
    +             & (table["Language"].isin("English", "Spanish")),
    +     }
    + )
  • Ability to mutate individual and mapped roles. Reassign them instead:

    - from atoti_plus import UserServiceClient
    - user_service_client = UserServiceClient.from_session(session)
    - user_service_client.individual_roles["John"].update({"ROLE_A", "ROLE_B"})
    + session.security.individual_roles["John"] |= {"ROLE_A", "ROLE_B"}
    - user_service_client.individual_roles["John"].remove("ROLE_A")
    - user_service_client.individual_roles["John"].remove("ROLE_B")
    + session.security.individual_roles["John"] -= {"ROLE_A", "ROLE_B"}


  • Support for Python 3.8.

  • Support for ACTIVEPIVOT_LICENSE environment variable. Use ATOTI_LICENSE instead.

  • Support for setting a default_value of a different type than the column’s data_type.

        types={"Date": "LocalDate"},
    -   default_values={"Date": "N/A"},
    +   default_values={"Date": None}

Previously deprecated#

  • Comparing a Column, Level, or Measure against None. Use the corresponding isnull() method instead.

  • Passing a Level to shift()’s on parameter. Pass a Hierarchy instead.

  • atoti.value(). Use atoti.agg.single_value() instead.

  • Passing a Mapping to at() and drop()’s coordinates parameter, and to join(). Pass a Condition instead.

  • atoti.Cube.query(), atoti_query.QueryCube.query() and explain_query()’s condition parameter. Use the filter parameter instead.

  • Scope factory functions atoti.scope.cumulative(), atoti.scope.origin(), and atoti.scope.siblings(). Instantiate their corresponding class: CumulativeScope, OriginScope, and SiblingsScope instead.

  • atoti.hide_new_license_agreement_message(). Use atoti.hide_new_eula_message() instead.

  • Automatic assignment of the ROLE_USER role when using atoti_plus.BasicSecurity.create_user(). Assign the role manually through the individual_roles instead.

  • UserServiceClient.kerberos.create_user(). It was unnecessary as users authenticating through Kerberos will automatically be able to access the application if they are granted the ROLE_USER role through default_roles or individual_roles:

    - user_service_client.kerberos.create_user("Jean", roles={"ROLE_FRANCE"})
    + user_service_client.individual_roles["Jean"] = {"ROLE_FRANCE", "ROLE_USER"}