0.8.0 (June 02, 2023)#
Added#
atoti-directquery-mssqlplugin for Microsoft SQL Server.Support for exclusion filter in
atoti.Cube.query()andatoti_query.QueryCube.query(issue #742).atoti.parent_value()’s dense parameter.
Changed#
The
atoti-plusplugin has been merged with the mainatotipackage. The DirectQuery plugins can be installed without configuring a private PyPI/Conda repository. The DirectQuery plugins and the features that were part of theatoti-plusplugin still require a license key to be used. See how to Unlock all features.atoti_plus.ADVANCED_APP_EXTENSIONhas moved toatoti.app_extension.ADVANCED_APP_EXTENSIONand has changed from atupleto aMappingfor simpler use.atoti_plus.UserServiceClienthas moved back toatoti.Session.security.Graphviz is not declared as a dependency of the
atotiConda package for consistency with the Python wheel package. Install it before usingatoti.tables.Tables.schemaoratoti.Cube.schema.The
atoti-jupyterlabplugin has been renamedatoti-jupyterlab3. It is compatible with JupyterLab 3. This will allowatoti-jupyterlabto target JupyterLab 4 in a future non-breaking Atoti release.The dtypes of the pandas DataFrames returned by
atoti.Table.head(),atoti.Cube.query(),atoti.Session.query_mdx(),atoti_query.QueryCube.query, andatoti_query.QuerySession.query_mdxmatch theDataTypeof the correspondingColumn,Level, andMeasure. Here are some examples:Int64for aColumnwith alongdata_typeand aNonedefault_value.float32for aColumnwith afloatdata_typeand a non-Nonedefault_value.int32for aLevelwith anintdata_type. The dtype will become nullable (i.e.Int32) ifatoti.Cube.query()’s include_totals oratoti.Session.query_mdx()’s keep_totals are set toTrue.
The attributes of
AggregateProviderhave been made immutable. They have also been made private because some of them are instances of classes that are, for now, private.
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 toNone.
Create a file in the migration folder and name it
servers.jsonTo create that file, run the following code, adapting the logic to create a
atoti_query.QuerySessiontargetting 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}}, indent=2, ) Path("servers.json").write_text(servers_json)
Deprecated#
Support for pandas 1. Update to pandas >= 2.0.0 instead.
UserServiceClient.basic.create_user(). Useatoti_query.security.basic_security.BasicSecurity.credentialsinstead:- 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_roleandUserServiceClient.roles. Useatoti_query.security.Security.restrictionsinstead:- 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"}
Removed#
Support for Python 3.8.
Support for
ACTIVEPIVOT_LICENSEenvironment variable. UseATOTI_LICENSEinstead.Support for setting a
default_valueof a different type than the column’sdata_type.session.create_table( "Example", types={"Date": "LocalDate"}, - default_values={"Date": "N/A"}, + default_values={"Date": None} )
Previously deprecated#
Comparing a
Column,Level, orMeasureagainstNone. Use the correspondingisnull()method instead.Passing a
Leveltoshift()’s on parameter. Pass aHierarchyinstead.atoti.value(). Useatoti.agg.single_value()instead.Passing a
Mappingtoat()anddrop()’s coordinates parameter, and tojoin(). Pass aConditioninstead.atoti.Cube.query(),atoti_query.QueryCube.query()andatoti.Cube.explain_query()’s condition parameter. Use the filter parameter instead.Scope factory functions
atoti.scope.cumulative(),atoti.scope.origin(), andatoti.scope.siblings(). Instantiate their corresponding class:CumulativeScope,OriginScope, andSiblingsScopeinstead.atoti.hide_new_license_agreement_message(). Useatoti.hide_new_eula_message()instead.Automatic assignment of the ROLE_USER role when using
atoti_plus.BasicSecurity.create_user(). Assign the role manually through theatoti_query.security.Security.individual_rolesinstead.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 throughatoti_query.security.kerberos_security.KerberosSecurity.default_rolesoratoti_query.security.Security.individual_roles:- user_service_client.kerberos.create_user("Jean", roles={"ROLE_FRANCE"}) + user_service_client.individual_roles["Jean"] = {"ROLE_FRANCE", "ROLE_USER"}