Planning Analytics

 View Only

 TM1 - Counting Rules and Feeders per Cube

Fausto Capellari's profile image
Fausto Capellari posted Tue May 27, 2025 11:18 AM

Hello,

I need to collect statistics from a TM1 database. Specifically, I would like to count how many rules and feeder formulas exist in each cube (there are about 50 cubes).

It would be great if anyone could share ideas or suggestions on how to achieve this.

Kind regards,
Fausto

Vlad Didenko's profile image
Vlad Didenko IBM Champion

Hi @Fausto Capellari, is this what you're looking for?

Fausto Capellari's profile image
Fausto Capellari

Hi @Vlad Didenko,

Yes, that would help me a lot. Thank you!

Could you please share that Python code with me? I believe I can adapt it to read rules from files, since I have all the ".rux" files.

Kind regards,

Fausto

Vlad Didenko's profile image
Vlad Didenko IBM Champion

Sure, here's the code:

import configparser
from TM1py import TM1Service
config = configparser.ConfigParser()
config.read("../space.cfg")
tm1_connection = {
    "base_url": config.get("TM1", "base_url"),
    "port": config.getint("TM1", "port", fallback=443),
    "user": config.get("TM1", "user"),
    "password": config.get("TM1", "password"),
    "namespace": config.get("TM1", "namespace", fallback="")
}
try:
    with TM1Service(**tm1_connection) as tm1:
        print(f"Connected to TM1 Server: {tm1.server.get_server_name()}")
        cubes = tm1.cubes.get_all()
        data = []
        for cube in cubes:
            try:
                rule_text = cube.rules or ""
                if cube.name.startswith('}') or not rule_text:
                    continue
                cleaned_lines = []
                for line in rule_text.body.splitlines():
                    line = line.strip()
                    if line and not line.startswith('#'):
                        cleaned_lines.append(line)
                joined_rules = ' '.join(cleaned_lines)

                # Count feeder blocks
                feeder_count = joined_rules.upper().count('FEEDERS')
                # Remove feeders block (conservatively from FEEDERS to next semicolon)
                joined_rules_no_feeders = re.sub(r'FEEDERS.*?;', '', joined_rules, flags=re.IGNORECASE)
                # Count rules as number of semicolons
                rule_count = joined_rules_no_feeders.count(';')
                data.append({
                    "Cube": cube.name,
                    "Rules": rule_count,
                    "Feeders": feeder_count
                })

            except Exception as e:
                print(f"Error reading rules for cube {cube.name}: {e}")

        # Output the results as a DataFrame
        df = pd.DataFrame(data)
        print(df.to_string(index=False))

except Exception as e:
    print(f"Error connecting to TM1: {e}")
Fausto Capellari's profile image
Fausto Capellari

Thank you, @Vlad Didenko! Your code was very helpful.

I’ll post my code here as an alternative approach.

Kind regards,

Fausto

import re
import pathlib

# Folder containing the rule files; Replace with your actual folder path
folder = r'your/rules/folder/path'

for file_obj in sorted(pathlib.Path(folder).glob('*.txt')):
    with open(file_obj.absolute(), 'r', encoding='utf-8', errors='ignore') as file:
        rules_original_txt = file.read()
        
    # Cleaning the rules text: removing empty lines, comments, and specific keywords
    cleaned_rules_list = []
    for line in rules_original_txt.splitlines():
        line = line.strip()
        if line and not line.startswith('#') and not line.startswith('FORMAT') and line.strip().upper() != "FEEDSTRINGS;" and line.strip().upper() != "SKIPCHECK;":
            cleaned_rules_list.append(line)
    
    cleaned_rules_txt = '\n'.join(cleaned_rules_list)

    # Lists to hold rules and feeders separately
    rules_list = []
    feeders_list = []

    # Boolean to track if we are in the FEEDERS section
    found_feeders = False

    # Loop through the cleaned lines to separate rules and feeders
    for line in cleaned_rules_list:
        if line.strip().upper() == "FEEDERS;":
            found_feeders = True
            #feeders_list.append(line)  # Append the FEEDERS line to feeders_list
            continue

        if not found_feeders:
            rules_list.append(line)
        else:
            feeders_list.append(line)

    # Convert lists to strings, joining with newlines
    rules_txt = "\n".join(rules_list)
    feeders_txt = "\n".join(feeders_list)

    # Counting rules and feeders by counting semicolons
    # Assuming each rule ends with a semicolon and each feeder also ends with a semicolon
    rule_count = rules_txt.count(';')
    feeder_count = feeders_txt.count(';')
    print (f'{file_obj.stem};{rule_count};{feeder_count}')
Emil Malmberg Fosdal's profile image
Emil Malmberg Fosdal IBM Champion

If you want to do it within TM1, you could:

  1. Create a process that loops all cubes (use the }Cubes dimension)
  2. Use CubeRuleGet to get the rule
  3. Count then number of ";" in the rules text (loop the text). Each rule or feeder will always end with ;
  4. Keep an eye for when you get the "FEEDERS;" and change from counting as rules to feeders