Accessing and changing Schedule Options, ShowGrandTotal and IsItemized, through Python

Hello, I’ve been trying to extend the usage capablities of the Archi-Lab Custom Node to include the option to toggle Grand Totals and Itemize every instance but haven’t had any luck. I thought I could access these properties similarly to how Archi-Lab accesses Blank line/ShowHeader/Etc. But all I seem to be doing it breaking the script…

My edits are between the two ################ lines.

I’m also a bit confused by how Archi-Lab calls inputs…

Blank for example: _showBlankLine = IN[2] this is where the variable is first set but as _showBlankLine, but later it’s called inside

def createSortingField(fieldId, blank, fCount, fTitle, header, sOrder):

how did _showBlankLine become linked to blank?

Thanks for any help you can offer! Very novice “coder” but learning every day!

# Copyright(c) 2015, Konrad Sobon
# @arch_laboratory, http://archi-lab.net

import clr

clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

# Import DocumentManager and TransactionManager
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

# Import RevitAPI
clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application

from System.Collections.Generic import *

# Import ToDSType(bool) extension method
clr.AddReference("RevitNodes")
import Revit

clr.ImportExtensions(Revit.Elements)

# The inputs to this node will be stored as a list in the IN variable.
dataEnteringNode = IN

_keySchedule = UnwrapElement(IN[0])
_paramName = IN[1]
_showBlankLine = IN[2]
_footerCount = IN[3]
_footerTitle = IN[4]
_header = IN[5]
_sortOrder = IN[6]
showGrandTotal = IN[7]
itemize = IN[8]

# "Start" the transaction
TransactionManager.Instance.EnsureInTransaction(doc)


def getFieldId(schedule, name):
    definition = schedule.Definition
    count = definition.GetFieldCount()
    for i in range(0, count, 1):
        if definition.GetField(i).GetName() == name:
            fieldId = definition.GetField(i).FieldId
    return fieldId


def addSortingField(schedule, ssgf):
    message = None
    definition = schedule.Definition
    try:
        definition.AddSortGroupField(ssgf)
    except:
        message = "You can add Max of 4 sorting/grouping parameters."
        pass
    return message


def createSortingField(fieldId, blank, fCount, fTitle, header, sOrder):
    message = None
    ssgf = ScheduleSortGroupField()
    ssgf.FieldId = fieldId

    if blank != None:
    ssgf.ShowBlankLine = blank

checkList = [fCount, fTitle]
if any(item == True for item in checkList):
    ssgf.ShowFooter = True
    if fCount != None:
        ssgf.ShowFooterCount = fCount
    if fTitle != None:
        ssgf.ShowFooterTitle = fTitle
else:
    ssgf.ShowFooter = False

if header != None:
    ssgf.ShowHeader = header
if sOrder != None:
    if sOrder == "Ascending":
        sortO = ScheduleSortOrder.Ascending
        ssgf.SortOrder = sortO
    elif sOrder == "Descending":
        sortO = ScheduleSortOrder.Descending
        ssgf.SortOrder = sortO
    else:
        message = "Schedule Sort Order can only be \nset to Ascending or Descending.\nCheck your spelling please."
if message == None:
    return ssgf
else:
    return message


if type(_paramName) == list:
    definition = _keySchedule.Definition
    if definition.GetSortGroupFieldCount() != 0:
        definition.ClearSortGroupFields()
    for i, j, k, l, m, n in zip(_paramName, _showBlankLine, _footerCount, _footerTitle, _header, _sortOrder):
        fieldId = getFieldId(_keySchedule, i)
        ssgf = createSortingField(fieldId, j, k, l, m, n)
        if type(ssgf) == str:
            message = "Schedule Sort Order can only be \nset to Ascending or Descending.\nCheck your spelling please."
        else:
            message = addSortingField(_keySchedule, ssgf)
    else:
    definition = _keySchedule.Definition
    if definition.GetSortGroupFieldCount() != 0:
        definition.ClearSortGroupFields()
    fieldId = getFieldId(_keySchedule, _paramName)
    ssgf = createSortingField(fieldId, _showBlankLine, _footerCount, _footerTitle, _header, _sortOrder)
    if type(ssgf) == str:
        message = "Schedule Sort Order can only be \nset to Ascending or Descending.\nCheck your spelling please."
    else:
        message = addSortingField(_keySchedule, ssgf)

######################################################################################
def createScheduleAlts(showGrandTotal, itemize)
    message = None
    sd = ScheduleDefinition()

    if showGrandTotal != None:
        sd.ShowGrandTotal = ShowGrandTotal


if itemize != None:

    sd.IsItemized = IsItemized

    if message == None:
        return sd
else:
    return message
#################################################################################
# "End" the transaction
TransactionManager.Instance.TransactionTaskDone()

# Assign your output to the OUT variable

if message == None:
   OUT = 0
else:
    OUT = '\n'.join('{:^35}'.format(s) for s in message.split('\n'))

Not sure how to properly format this but

if message == None:
OUT = 0
else:
OUT = ‘\n’.join(’{:^35}’.format(s) for s in message.split(’\n’))

is not commented out

Well it has to do with how that method is defined:

def createSortingField(fieldId, blank, fCount, fTitle, header, sOrder):
    message = None
    ssgf = ScheduleSortGroupField()
    ssgf.FieldId = fieldId

    if blank != None:
    ssgf.ShowBlankLine = blank

When you are creating a method/definition like this, you can assign your own names to inputs. So I have simply named mine “blank”.

Now, your code. You can’t create a new ScheduleDefinition() and expect it to have any impact on Schedule Definition of an existing schedule. You should do what I was doing there, which is extract the schedule definition from the schedule and then make changes to it. :slight_smile:

1 Like

Thank you for your response Konrad! I’m trying to work that out now, I’ve noticed a few things that I’ve touched up from what I posted.

Namely making a variable sd

sd = ScheduleDefinition()

and updating my portion of the code to this:

if grandTotal != None:
        if grandTotal == True or grandTotal == False:
            sd.ShowGrandTotal = grandTotal
        else:
            message = "Schedule Show Grand Total can only be \nset to True or False"
    else:
        sd.ShowGrandTotal = False

if itemize != None:
    if itemize == True or itemize == False:
        sd.IsItemized = itemize
    else:
        message = "Schedule Itemize can only be \nset to True or False"
else:
    sd.IsItemized = True

if message = None:
    return (ssgf, sd)
else:
    return message

I think I understand what you’re saying, and hopefully I addressed that in the code by pulling ScheduleDefinition() into sd and then modifying those values with the user inputs grandTotal and itemize (though I’m still unsure of how the user inputs like your _sortOrder become sOrder - when does sOrder take the value of sortOrder from the user?

I just got why this works, I think, is it from calling the definition later in the script? When you do "for i, j, k, l… in zip(fieldId, _showBlankLine,…), so then _showBlankLine becomes the value of whatever the second variable in the definition is? in this case blank but if you were to put 123 then 123 == _showBlankLine?

I’m also unsure of how the ScheduleDefinition() properties get pushed back into the schedule in revit. I can see through your method that ssgf from ScheduleSortGroupField() is drawn in through addSortingField(_keySchedule, ssgf)

I don’t know if ScheduleDefinition has a similar “add” function through the API but I haven’t managed to find it…

From the original code Konrad created (with some interjections of my own which broke it):

if type(_parameterName) == list:
definition = _keySchedule.Definition
if definition.GetSortGroupFieldCount() != 0:
    definition.ClearSortGroupFields()
for i, j, k, l, m, n, o, p in zip(_parameterName, _showBlankLine, _footerCount, _header, _sortOrder, _grandTotal, _itemize):
    fieldId = getFieldId(_keySchedule, i)
    ssgf = createSortingField(fieldId, j, k, l, m, n)
    sd = createGroupingField(fieldIdo, p)
    if type(ssgf) == str or type(sd) == str:
        message = "Schedule Sort Order can only be \nset to Ascending or Descending."
    else:
        message = addSortingField(_keySchedule, ssgf)
        message = addGroupingField(_keySchedule, sd)
        
else:
    definition = _keySchedule.Definition
    if definition.GetSortGroupFieldCount() != 0:
        definition.ClearSortGroupFields()
        fieldId = getFieldId(_keySchedule, _parameterName)
        ssgf = createSortingField(fieldId, _showBlankLine, _footerCount, _footerTitle, _header, _sortOrder)
        sd = (_grandTotal, _itemize)
    if type(ssgf) == str or type(sd) == str:
        message = "Schedule Sort Order can only be \nset to Ascending or Descending."
    else:
        message = addSortingField(_keySchedule, ssgf, sd)

I’m assuming the “ssgf = createSortingField(fieldId, j, k, l, m, n)” is something Konard created - I’ve looked through the revit API and found no references to it so I don’t know if it comes from somewhere or if it is a new method created for this script. At any rate I took a swing at making “sd = createGroupingField(fieldId, o, p)”. Am I on the right track with this or are createSortingField and addSortingField predefined methods and there is a comparable (but yet to be discovered by me) method to do the same for Grouping components (such as grand totals and itemize every instance

Should I be making a new def for createGroupingField?

# Copyright(c) 2015, Konrad Sobon
# @arch_laboratory, http://archi-lab.net 

import clr

clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

# Import DocumentManager and TransactionManager
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

# Import RevitAPI
clr.AddReference("RevitAPI")
import Autodesk
from autodesk.Revit.DB import *

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application

from System.Collections.Generic import *

# Import ToDSType(bool) extension method
clr.AddReference("RevitNodes")
import Revit

clr.ImportExtensions(Revit.Elements)

#The inputs to this node will be stored as a list in the IN variables.
dataEnteringNode = IN

_keySchedule = UnwrapElement(IN[0])
_parameterName = IN[1]
_showBlankLine = IN[2]
_footerCount = IN[3]
_footerTitle = IN[4]
_header = IN[5]
_sortOrder = IN[6]
_grandTotal = IN[7]
_itemize = IN[8]

# "Start" the transaction
TransactionManager.Instance.EnsureInTransation(doc)


def getFieldId(schedule, name):
	definition = schedule.definition
	count = definition.GetFieldCount()
	for i in range(0, count, 1):
		if definition.GetField(i).GetName() == name:
			fieldId = definition.GetField(i).FieldId
	return fieldId


def addSortingField(schedule, ssgf):
message = None
definition = schedule.Definition
try:
	definition.AddSortingGroupField(ssgf)
except:
	message = "You can add a maximum of 4 sorting/grouping parameters."
	pass
return message

def addGrouping(schedule, sd):
message = None
definition = schedule.Definition
try:
	definition.AddGrouping(sd)
except:
	message = "Bad Entry."
	pass
return message


def createSortingField(fieldId, blank, fCount, fTitle, header, sOrder):
message = None
ssgf = ScheduleSortGroupField()
ssgf.FieldId = fieldId

if blank != None:
	ssgf.ShowBlankLine = blank
	
checkList = [fCount, fTitle]
if any(item == True for item in checkList):
	ssgf.ShowFooter = True
	if fCount != None:
		ssgf.ShowFooterCount = fCount
	if fTitle != None:
		ssgf.ShowFooterTitle = fTitle
else:
	ssgf.ShowFooter = False
	
if header != None:
	ssgf.ShowHeader = header
if sOrder != None:
	if sOrder == "Ascending":
		sortO = ScheduleSortOrder.Ascending
		ssgf.SortOrder = sortO
	elif sOrder == "Descending":
		sortO = ScheduleSortOrder.Descending
		ssgf.SortOrder = sortO
	else:
		message = "Schedule Sort Order can only be \nset to Ascending or Descending."
	
if message == None:
    return (ssgf)
else:
    return message
	
if type(_parameterName) == list:
definition = _keySchedule.Definition
if definition.GetSortGroupFieldCount() != 0:
    definition.ClearSortGroupFields()
for i, j, k, l, m, n in zip(_parameterName, _showBlankLine, _footerCount, _header, _sortOrder):
    fieldId = getFieldId(_keySchedule, i)
    ssgf = createSortingField(fieldId, j, k, l, m, n)
    if type(ssgf) == str:
        message = "Schedule Sort Order can only be \nset to Ascending or Descending."
    else:
        message = addSortingField(_keySchedule, ssgf)
        
else:
    definition = _keySchedule.Definition
    if definition.GetSortGroupFieldCount() != 0:
        definition.ClearSortGroupFields()
        fieldId = getFieldId(_keySchedule, _parameterName)
        ssgf = createSortingField(fieldId, _showBlankLine, _footerCount, _footerTitle, _header, _sortOrder)
    if type(ssgf) == str:
        message = "Schedule Sort Order can only be \nset to Ascending or Descending."
    else:
        message = addSortingField(_keySchedule, ssgf)
        
def createGrouping(fieldId, grandTotal, itemize):
message = None
sd = ScheduleDefinition()
sd.FieldId = fieldId

if grandTotal != None:
    if grandTotal == True or grandTotal == False:
        sd.ShowGrandTotal = grandTotal
    else:
        message = "Show Grand Total can only be \nset to True or False"
else:
    sd.ShowGrandTotal = False

if itemize != None:
    if itemize == True or itemize == False:
        sd.IsItemized = itemize
    else:
        message = "Itemize can only be \nset to True or False"
else:
    sd.IsItemized = True

if message == None:
    return (sd)
else:
    return message
		
if type(_parameterName) == list:
definition = _keySchedule.Definition
for i, o, p in zip(_parameterName, _grandTotal, _itemize):
	fieldId = getFieldId(_keySchedule, i)
    sd = createGrouping(fieldId, o, p)
    if type(sd) == str:
        message = "Schedule Sort Order can only be \nset to Ascending or Descending."
    else:
        message = addGrouping(_keySchedule, sd)
        
else:
    definition = _keySchedule.Definition
        fieldId = getFieldId(_keySchedule, _parameterName)
        sd = createGrouping(fieldId, _grandTotal, _itemize)
    if type(sd) == str:
        message = "Schedule Sort Order can only be \nset to Ascending or Descending."
    else:
        message = addGrouping(_keySchedule, sd)
# "End" the transaction
TransactionManager.Instance.TransactionTaskDone()

#Assign your output to the OUT variable.
if message == None:
OUT = 0
else:
OUT = message

Had similar problem. To show grand totals I’ve did this. The first input is schedule and the second one is string. Wrap it in a node and you get yourself a function.

import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application

from System.Collections.Generic import *

clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)

dataEnteringNode = IN

TransactionManager.Instance.EnsureInTransaction(doc)

unwrapped = UnwrapElement(IN[0])
unwrapped.Definition.ShowGrandTotal = True
unwrapped.Definition.ShowGrandTotalCount = True
unwrapped.Definition.ShowGrandTotalTitle = True
unwrapped.Definition.GrandTotalTitle = IN[1]

TransactionManager.Instance.TransactionTaskDone()

OUT = "potato"