QTOUtility Class

Hi,
is anyone here familiar with the QTOUtility Methods?
My goal is to get Takeoff for every alignment in separate named .csv.
I managed to create the xml report itself, but cannot understand the following:

  • the reportfile is created in the temp folder, regardless of my input (maybe “reportFilePath” doesn’t mean output file path, I don’t know)
  • therefore only one file is created, overriden with the data of the last one in the for loop.
  • therefore the QTOUtility.TransformXMLReport() method can only create one file in the temp folder regardless of the given output path
  • QTOUtility.TransformXMLReport() creates txt even if SummaryCSV.xsl sheet file is used

Also, I guess there is an error in the documentation as QTOUtility.TransformXMLReport() asks for
strPayItemFilePath, but it must be the file path that QTOUtility.GenerateXMLReport() returns.

I’m aware that there is a node for this in the C3D Toolkit, but I could not really figure out how it works, and not qualified enough to get data from the XML as @Paolo_Emilio_Serra1 did.

So, can anyone explain me how the above should work?
Shall I save as the generated txt within the for loop? Or am I missing something?

Thanks.

# Load the Python Standard and DesignScript Libraries
import sys
import clr

# Add Assemblies for AutoCAD and Civil3D
clr.AddReference('AcMgd')
clr.AddReference('AcCoreMgd')
clr.AddReference('AcDbMgd')
clr.AddReference('AecBaseMgd')
clr.AddReference('AecPropDataMgd')
clr.AddReference('AeccDbMgd')

# Import references from AutoCAD
from Autodesk.AutoCAD.Runtime import *
from Autodesk.AutoCAD.ApplicationServices import *
from Autodesk.AutoCAD.EditorInput import *
from Autodesk.AutoCAD.DatabaseServices import *
from Autodesk.AutoCAD.Geometry import *

# Import references from Civil3D
from Autodesk.Civil.ApplicationServices import *
from Autodesk.Civil.DatabaseServices import *

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

adoc = Application.DocumentManager.MdiActiveDocument
editor = adoc.Editor

alignments = IN[0]
reportdirpath = IN[1]
payitemids = IN[2]

from System import Array

payitemidsarray = Array[str](payitemids)

with adoc.LockDocument():
    with adoc.Database as db:

        with db.TransactionManager.StartTransaction() as t:
            # Place your code below
            try:
                alignmentobidlist =[]
                for alignment in alignments:
                    #get alignment id
                    alignmentId = alignment.InternalObjectId
                    #open alignment object
                    alignmentob = t.GetObject(alignmentId, OpenMode.ForRead)
                    #generate QTODetail
                    detail = QTOGenerateDetail()
                    #QTODetail props
                    detail.AlignmentId = alignmentId
                    detail.StartStation = alignmentob.StartingStation
                    detail.EndStation = alignmentob.EndingStation
                    detail.ReportType = QTOReportType.SummaryReport
                    #Filename template
                    filename = "\QTOReport-"+ alignmentob.Name
                    reportpath = reportdirpath + filename + ".XLS"
                    #Genererating report
                    report = QTOUtility.GenerateXMLReport(detail,reportpath,payitemidsarray)
                    payitemfilepath = QTOUtility.PayItemFilePath
                    
                    #Transforming it to CSV
                    reportformat = QTOReportFormat.SummaryCSV
                    
                    outpath = reportdirpath + filename + ".CSV"
                    outpath2 = reportdirpath + '\test' + ".CSV"
                    reportfilepath = report[1]
                    #out = QTOUtility.TransformXMLReport(xslpath,reportfilepath,payitemidsarray,outpath2)
                    out = QTOUtility.TransformXMLReport(reportformat,reportfilepath,payitemidsarray,outpath2)
            except Exception as ex:
                MessageBox.Show('Dont be sad, be better.')
            #
            # Commit before end transaction
            t.Commit()
            pass

# Assign your output to the OUT variable.
OUT = out

Have a try at this if you get the same issues.

Though i will highlight that you are replacing the “out” with a new utility each time, where as you should append this to then get a list of outputs that match the alignments

# Load the Python Standard and DesignScript Libraries
import sys
import clr

# Add Assemblies for AutoCAD and Civil3D
clr.AddReference('AcMgd')
clr.AddReference('AcCoreMgd')
clr.AddReference('AcDbMgd')
clr.AddReference('AecBaseMgd')
clr.AddReference('AecPropDataMgd')
clr.AddReference('AeccDbMgd')

# Import references from AutoCAD
from Autodesk.AutoCAD.Runtime import *
from Autodesk.AutoCAD.ApplicationServices import *
from Autodesk.AutoCAD.EditorInput import *
from Autodesk.AutoCAD.DatabaseServices import *
from Autodesk.AutoCAD.Geometry import *

# Import references from Civil3D
from Autodesk.Civil.ApplicationServices import *
from Autodesk.Civil.DatabaseServices import *

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

adoc = Application.DocumentManager.MdiActiveDocument
editor = adoc.Editor

alignments = IN[0]
reportdirpath = IN[1]
payitemids = IN[2]
OUT=[]

from System import Array

payitemidsarray = Array[str](payitemids)

with adoc.LockDocument():
    with adoc.Database as db:

        with db.TransactionManager.StartTransaction() as t:
            # Place your code below
            try:
                alignmentobidlist =[]
                for alignment in alignments:
                    #get alignment id
                    alignmentId = alignment.InternalObjectId
                    #open alignment object
                    alignmentob = t.GetObject(alignmentId, OpenMode.ForRead)
                    #generate QTODetail
                    detail = QTOGenerateDetail()
                    #QTODetail props
                    detail.AlignmentId = alignmentId
                    detail.StartStation = alignmentob.StartingStation
                    detail.EndStation = alignmentob.EndingStation
                    detail.ReportType = QTOReportType.SummaryReport
                    #Filename template
                    filename = "\QTOReport-"+ alignmentob.Name
                    reportpath = reportdirpath + filename + ".XLS"
                    #Genererating report
                    report = QTOUtility.GenerateXMLReport(detail,reportpath,payitemidsarray)
                    payitemfilepath = QTOUtility.PayItemFilePath
                    
                    #Transforming it to CSV
                    reportformat = QTOReportFormat.SummaryCSV
                    
                    outpath = reportdirpath + filename + ".CSV"
                    outpath2 = reportdirpath + '\test' + ".CSV"
                    reportfilepath = report[1]
                    #out = QTOUtility.TransformXMLReport(xslpath,reportfilepath,payitemidsarray,outpath2)
                    OUT.append(QTOUtility.TransformXMLReport(reportformat,reportfilepath,payitemidsarray,outpath2))
            except Exception as ex:
                OUT.append("The Code had a issue")

            t.Commit()
            pass

Well, as for the Python Script it creates the right number of outputs, but it isn’t my issue.
My main issue is with the QTOUtility Methods: regardless of my input they create files with the same name in the %TEMP% folder.
So it doesn’t matter if I append the result in a list or not, the files are always overriden.
Thus my assumption to maybe saveas the results within the loop. But then what is the outFormatFilePath Parameter for?

Thats fine but at least you can see visually that it is not using the same paths.

I would tell you to check that the new/existing file name/paths are being built correctly, then this might highlight that it is the string paths that is the issue.

Eg change
OUT.append(QTOUtility.TransformXMLReport(reportformat,reportfilepath,payitemidsarray,outpath2))

to

OUT.append([outpath,outpath2,reportfilepath, reportpath, filename])

Then this will give you all the path data, because it could be a issue with the generated paths giving it issues.

1 Like

Thanks, good idea. There were some minor errors, but that did not have an effect on the results.

  • "/test" string returned "<tab>est" :melting_face:, changed the file name, but nothing happened

I looked into the toolkit node and in my understanding it copies the files from the temp folder to a working folder, so I tried and I guess that might be the way.

So it seems to be working, but I don’t understand how the C3D API functions should properly work.

Perhaps part of the issue… Are you intending to overwrite the output file (one file path is generated by the DWG name) with each alignment? Or are your files only one alignment per file?

filename variable uses alignment name, and the for loop goes through all alignments.

So it should name first the xml if reportFilePath means output name, but the result has a general name and placed in %TEMP%
https://help.autodesk.com/view/CIV3D/2024/ENU/?guid=a1a33410-91b4-6d3a-5020-4a29bdfe6ad3

After that, using said xml the TransformXMLReport() should return a .csv (it generates it in .txt though)
and place it to outFormatFilePath, but it is placed in %TEMP% as well
https://help.autodesk.com/view/CIV3D/2024/ENU/?guid=1310e19e-8b31-0352-06a4-3a2fcf7f720e

But as the documentation is not that infromative, maybe I misinterpret something and the input parameters have totally different meaning.

Luckily now it works, but it would be cool if someone smarter than me could explain what is happening :sweat_smile: