Dieser Artikel beschreibt, wie Sie ein Python-Skript erstellen, mit dem sich ein aus dem Werkzeug Progress Monitoring extrahierter CIPM-Bericht umwandeln lässt in:
- BCF-Dateien zur Verwendung mit einem Revit-Modell oder einem beliebigen anderen Werkzeug
- formatierte Anmerkungen in einem Cintoo-Projekt, die anhand des Parameters Coverage mit problematischen Modellelementen verknüpft sind
Berechtigungen: Nutzer mit Rollen, die die Berechtigung Anmerkungen verwalten umfassen, können mit diesem Skript Anmerkungen erstellen.
Ein Beispiel für ein Skript finden Sie im Anhang. Alle ergänzenden Skripte sind ebenfalls angehängt.
INHALTSVERZEICHNIS
- Voraussetzungen
- Eingabeinformationen
- Autorisierung
- Einschränkungen
- Ergebnisse
- Erläuterung des Skripts CreateBCF.py
- Erläuterung des Skripts CreateAnnotation.py
Voraussetzungen
Installieren Sie Python anhand der Anleitung eines KI-Chatbots Ihrer Wahl. Bitten Sie um eine Anleitung zum Ausführen von Python-Skripten über die Eingabeaufforderung, falls keine dedizierte Umgebung installiert ist.
In diesem Python-Skript werden die folgenden Bibliotheken verwendet, um den Workflow auszuführen.
Installieren Sie diese, bevor Sie das Skript verwenden:
- png - Führen Sie den folgenden Befehl aus, um die Bibliothek einzubinden
pip install pypng- requests - Führen Sie den folgenden Befehl aus, um die Bibliothek einzubinden
pip install requestsEingabeinformationen
Führen Sie das Skript createBCF.py in der Eingabeaufforderung aus. Alle anderen Skripte sind ergänzende Skripte.
python createBCF.py
Nach dem Start fordert das Skript den Nutzer auf, die folgenden Informationen anzugeben:
- Insert the path to the CIPM .csv report - vollständiger Pfad zum CIPM-Bericht im CSV-Format einschließlich Dateierweiterung
Beispiel: C:\Projects\ProgressMonitoring\Walls_0.0500(Meters).csv - Insert the path where BCF files should be created - vollständiger Pfad, unter dem die BCF-Dateien lokal erstellt und gespeichert werden
Beispiel: C:\Projects\ProgressMonitoring\BCF - Insert the minimum coverage - benutzerdefinierter Grenzwert (in Prozent): Es werden alle Coverage-Werte berücksichtigt, die strikt darunter liegen.
Beispiel: Um BCF/Anmerkungen für Modellelemente mit weniger als 25 % Scanabdeckung zu erstellen, geben Sie den Wert 25 ein - Insert the path to the workzone where to create annotations - vollständige URL der Arbeitszone im Cintoo-Projekt.
Hinweis: Es wird dringend empfohlen, für die mit diesem Skript erstellten Anmerkungen eine eigene Arbeitszone anzulegen, da dies mehr Kontrolle über das Ergebnis und bei Bedarf ein einfacheres Aufräumen ermöglicht.
Beispiel: https://aec.cintoo.com/accounts/0a000a00-000a-000a-a0000aa0a00000a0/
projects/a0a0000a-aaa0000a-000a-aaaa00aaa000/workzones/aaaaaaaaaa0aaaaaaaa/data
Kopieren Sie diese URL direkt aus dem Browser.
Autorisierung
Sobald alle Parameter eingegeben sind, wird der Nutzer aufgefordert, die Verwendung eines Cintoo-Kontos auf einer sich öffnenden Browserseite zu autorisieren. Klicken Sie auf Allow, um fortzufahren.
Nach der Autorisierung erscheint die folgende Meldung, die eine erfolgreiche Authentifizierung anzeigt. Dieser Browser-Tab kann gefahrlos geschlossen werden.

Cintoo-tokens.json - vom Skript erstellte JSON-Authentifizierungsdatei zum Herstellen der Verbindung. Es wird empfohlen, sie jedes Mal zu löschen, wenn ein Nutzer seine Arbeit beendet.
Hinweis: Die Zeit, die für die Erstellung von BCF und Anmerkungen benötigt wird, hängt vom Wert der Abdeckung und von der Anzahl der Modellelemente ab.
Einschränkungen
Warnung: Bitte beachten Sie, dass mit dem Skript über die APIs maximal 3000 Anmerkungen erstellt werden können.
Ergebnisse
- BCF-Dateien, die lokal unter dem vom Nutzer festgelegten Pfad gespeichert werden und beispielsweise in Revit verwendet werden können, um problematische Modellelemente zu erkennen und zu korrigieren

- Cintoo-Anmerkungen im Projekt, die visuell mit Modellelementen verknüpft sind und alle erforderlichen Daten in der (bearbeitbaren) Beschreibung enthalten.

Erläuterung des Skripts CreateBCF.py
Nachfolgend finden Sie das Skript mit Kommentaren zu jedem Abschnitt.
Warnung: Anpassungen am Code sind möglich, Cintoo übernimmt jedoch keine Verantwortung für die Funktionalität dieses Codes, sobald er geändert wurde.
import csv
import os
import zipfile
import png
from createAnnotation import createAnnotation
import login
from cintooUtils import parse_cintoo_workzone_context_url
import datetime
import uuid
def create_png(path):
width = 255
height = 255
img = []
for y in range(height):
row = ()
for x in range(width):
row = row + (x, max(0, 255 - x - y), y)
img.append(row)
with open(path, 'wb') as f:
w = png.Writer(width, height, greyscale=False)
w.write(f, img)Importieren aller erforderlichen Bibliotheken und ergänzenden Skripte zur Unterstützung des Hauptskripts. Erstellen einer Vorlage im PNG-Format, die zur Erstellung einer BCF-Datei erforderlich ist.
def create_bcfzip(input_csv,
output_dir,
min_coverage,
tenant=None,
account_id=None,
project_id=None,
workzone_id=None,
camera_state=None,
headers=None):
if not os.path.exists(output_dir):
os.makedirs(output_dir)
report_name = input_csv.split("\\")[-1]
with open(input_csv, 'r', encoding='utf-8') as file:
reader = csv.DictReader(file)
elements_to_be_created = [row for row in reader if float(row['Coverage by Scan Data (%)']) < min_coverage]
if len(elements_to_be_created) > 1000:
print("More than 1000 elements have a lower score than the minimum coverage score. Only the top 1000 will be created.")
elements_to_be_created = sorted(
elements_to_be_created,
key=lambda x: float(x['Coverage by Scan Data (%)']),
reverse=True
)[0:999]
for row in elements_to_be_created:
if (row.get('IfcGUID') or row.get('GlobalId')) and row.get('Coverage by Scan Data (%)'):
print(row)
try:
coverage = float(row['Coverage by Scan Data (%)'])
print(coverage)
except ValueError:
continue
guid = row.get('IfcGUID') or row.get('GlobalId')
element_name = row.get('Model Element Name', 'Unknown')
element_id = list(row.values())[0]
print(element_id)
create_bcf_file(guid, element_name, output_dir, coverage, report_name)
if all([tenant, account_id, project_id, workzone_id, camera_state]):
createAnnotation(tenant,
account_id,
project_id,
workzone_id,
f'Low Coverage Issue: {element_name}',
f"The element {element_name} has a coverage score of {coverage}, according to the report {report_name}",
{"x": 0, "y": 0, "z": 0},
camera_state,
headers,
modelElementId=element_id)Funktion, die BCF-Dateien zu jedem Element erstellt, das anhand des vom Nutzer definierten Parameters Coverage ausgewählt wird.
Sie liest alle erforderlichen Daten aus den vom Nutzer angegebenen Parametern wie Ausgabeverzeichnis, minimale Abdeckung, Tenant usw. sowie die vorhandenen GUIDs in den Zeilen des CIPM-Berichts aus, um sie anhand des Abdeckungsvergleichs zu extrahieren.
Warnung: Es wird nicht empfohlen, die Funktion create_bcfzip zu ändern, außer wenn die Bedingung für die Datenauswahl angepasst werden muss (zum Beispiel coverage<min_coverage). Für Änderungen an Text, Beschreibung und Beschriftung der Anmerkungen verweisen wir auf die Funktion create_issue_xml.
def create_bcf_file(guid, element_name, output_dir, coverage, report_name):
bcf_dir = os.path.join(output_dir, guid)
os.makedirs(bcf_dir, exist_ok=True)
viewpoint_guid = str(uuid.uuid4())
comment_guid = str(uuid.uuid4())
topic_guid = str(uuid.uuid4())
project_guid = str(uuid.uuid4())
viewpoint_filename = os.path.join(bcf_dir, 'viewpoint.bcfv')
viewpoint_xml = create_viewpoint_xml(guid, viewpoint_guid)
with open(viewpoint_filename, 'w', encoding='utf-8') as f:
f.write(viewpoint_xml)
issue_filename = os.path.join(bcf_dir, 'markup.bcf')
issue_xml = create_issue_xml(element_name, coverage, report_name, viewpoint_guid, comment_guid, topic_guid)
with open(issue_filename, 'w', encoding='utf-8') as f:
f.write(issue_xml)
project_filename = os.path.join(output_dir, 'project.bcfp')
project_xml = create_project_xml(project_guid)
with open(project_filename, 'w', encoding='utf-8') as f:
f.write(project_xml)
version_filename = os.path.join(output_dir, 'bcf.version')
with open(version_filename, 'w', encoding='utf-8') as f:
f.write(create_bcf_version())
snapshot_filename = os.path.join(output_dir, 'snapshot.png')
create_png(snapshot_filename)
zip_filename = os.path.join(output_dir, f'{guid}.bcf')
with zipfile.ZipFile(zip_filename, 'w') as zf:
zf.write(viewpoint_filename, os.path.join(topic_guid, 'viewpoint.bcfv'))
zf.write(issue_filename, os.path.join(topic_guid, 'markup.bcf'))
zf.write(snapshot_filename, os.path.join(topic_guid, 'snapshot.png'))
zf.write(project_filename, os.path.join('project.bcfp'))
zf.write(version_filename, os.path.join('bcf.version'))
# Bereinigung
os.remove(viewpoint_filename)
os.remove(issue_filename)
os.remove(project_filename)
os.remove(version_filename)
os.remove(snapshot_filename)
os.rmdir(bcf_dir)Erstellen einzelner BCF-Dateien.
def create_viewpoint_xml(guid, viewpoint_guid):
return f"""<?xml version="1.0" encoding="UTF-8"?>
<VisualizationInfo Guid="{viewpoint_guid}">
<Components>
<ViewSetupHints SpacesVisible="false" SpaceBoundariesVisible="false" OpeningsVisible="false" />
<Selection>
<Component IfcGuid="{guid}" />
</Selection>
<Visibility DefaultVisibility="true" />
</Components>
<OrthogonalCamera>
<CameraViewPoint>
<X>129.93820633961636</X>
<Y>-124.61204504554462</Y>
<Z>104.47418360973809</Z>
</CameraViewPoint>
<CameraDirection>
<X>-0.58963662529065941</X>
<Y>0.5647967039409042</Y>
<Z>-0.57735026918962584</Z>
</CameraDirection>
<CameraUpVector>
<X>-0.41693605617897656</X>
<Y>0.39937157934842415</Y>
<Z>0.81649658092772603</Z>
</CameraUpVector>
<ViewToWorldScale>44.256083397559856</ViewToWorldScale>
</OrthogonalCamera>
</VisualizationInfo>"""Festlegen der Standardkameraposition.
def create_issue_xml(element_name, coverage, report_name, viewpoint_guid, comment_guid, topic_guid):
return f"""<?xml version="1.0" encoding="UTF-8"?>
<Markup>
<Topic Guid="{topic_guid}" TopicType="Issue" TopicStatus="Active">
<Title>Low Coverage Issue: {element_name}</Title>
<Priority>Normal</Priority>
<Index>1</Index>
<CreationDate>2025-03-05T09:22:02+00:00</CreationDate>
<CreationAuthor>youremail@domain.com</CreationAuthor>
<ModifiedDate>2025-03-05T09:22:03+00:00</ModifiedDate>
<ModifiedAuthor>youremail@domain.com</ModifiedAuthor>
<DueDate>2025-03-05T09:00:00+00:00</DueDate>
<AssignedTo>youremail@domain.com</AssignedTo>
<Description>The element {element_name} has a coverage score of {coverage}, according to the report {report_name}</Description>
</Topic>
<Comment Guid="{comment_guid}">
<Date>2025-03-04T09:53:03+00:00</Date>
<Author>youremail@domain.com</Author>
<Comment>The element {element_name} has a coverage score of {coverage}, according to the report {report_name}</Comment>
<Viewpoint Guid="{viewpoint_guid}" />
<ModifiedDate>2025-03-04T09:53:03+00:00</ModifiedDate>
<ModifiedAuthor>youremail@domain.com</ModifiedAuthor>
</Comment>
<Viewpoints Guid="{viewpoint_guid}">
<Viewpoint>viewpoint.bcfv</Viewpoint>
<Snapshot>snapshot.png</Snapshot>
</Viewpoints>
</Markup>
"""Erstellen von XML-Dateien für die Metadaten zu den Problemen.
Geben Sie die erforderlichen Angaben zum Autor in die folgenden Felder ein:
- CreationAuthor - Zeile 154
- ModifiedAuthor - Zeile 156
- AssignedTo - Zeile 158
- Author - Zeile 163
- ModifiedAuthor - Zeile 167
def create_project_xml(projectGuid):
return f"""<?xml version="1.0" encoding="UTF-8"?>
<ProjectExtension>
<Project ProjectId="{projectGuid}" />
<ExtensionSchema></ExtensionSchema>
</ProjectExtension>"""Erstellen einer Datei, die auf ein Projekt verweist.
def create_extensions_xsd():
return """<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<schema>
<redefine schemaLocation="markup.xsd">
<simpleType name="Priority">
<restriction base="Priority">
<enumeration value="Aucune"/>
<enumeration value="Faible"/>
<enumeration value="Moyenne"/>
<enumeration value="Haute"/>
<enumeration value="Critique"/>
</restriction>
</simpleType>
<simpleType name="TopicStatus">
<restriction base="TopicStatus">
<enumeration value="Assignée"/>
<enumeration value="En cours"/>
<enumeration value="Terminée"/>
<enumeration value="Bloquée"/>
<enumeration value="Validée"/>
<enumeration value="Archivée"/>
</restriction>
</simpleType>
<simpleType name="TopicLabel">
<restriction base="TopicLabel"/>
</simpleType>
</redefine>
</schema>
"""Beschreibung der BCF-Struktur (Kategorien).
def create_bcf_version():
return """<?xml version="1.0" encoding="UTF-8"?>
<Version VersionId="2.1" xsi:noNamespaceSchemaLocation="version.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DetailedVersion>2.1 KUBUS BV</DetailedVersion>
</Version>"""Definition der BCF-Version (2.1).
def main():
input_csv = input("Insert the path to the CIPM .csv report :")
output_dir = input("Insert the path where BCF files should be created :")
min_coverage = float(input("Insert the minimum coverage"))
context_url = input("Insert the path to the workzone where to create annotations (e.g: "
"https://aec.cintoo.com/accounts/0a000a00-000a-000a-a000-0aa0a00000a0/projects/a0a0000a-aaa0"
"-000a-000a-aaaa00aaa000/workzones/aaaaaaaaaa0aaaaaaaa/data ")
context_details = parse_cintoo_workzone_context_url(context_url)
tenant, account_id, project_id, workzone_id = context_details['tenant'], context_details['accountId'], \
context_details['projectId'], context_details['workzoneId']
# Authentifizieren und Zugriffstoken abrufen
login.load_tokens()
token = login.get_token(tenant)
headers = {"Authorization": f"Bearer {token}"}
camera_state = {"type": "legacy",
"camerastate": {
"position": [0, 0, 1],
"rotationQuaternion": [0, 0, 0, 1],
"fov": 1.5708,
"ortho": False,
"overviewScale": 10,
"camType": "scan"},
}
create_bcfzip(input_csv,
output_dir,
min_coverage,
tenant,
account_id,
project_id,
workzone_id,
camera_state,
headers)
main()Definition des Hauptteils des Skripts: Er stellt dem Nutzer die Eingabefragen, ruft das Zugriffstoken ab und authentifiziert sich gemäß Cintoo API Authentication beim Cintoo-Konto. Hinzu kommen die Standardkameraposition, die nach dem Import in Cintoo geändert wird, die Erstellung der BCFzip-Ausgabedateien und die Ausführung der Hauptfunktion.
Erläuterung des Skripts CreateAnnotation.py
Nachfolgend finden Sie das Skript mit Kommentaren zu jedem Abschnitt.
Warnung: Anpassungen am Code sind möglich, Cintoo übernimmt jedoch keine Verantwortung für die Funktionalität dieses Codes, sobald er geändert wurde.
import login
import requests
from cintooUtils import parse_cintoo_workzone_context_url
TIMEOUT = 10Importieren aller erforderlichen Bibliotheken und Daten aus ergänzenden Skripten zur Unterstützung des Hauptskripts. Festlegen eines Timeout-Parameters.
def getWorkzoneGuid(tenant, account_id, project_id, workzone_idv1, headers):
url = f"{tenant}/api/2/accounts/{account_id}/projects/{project_id}/workzones"
response = requests.get(url, headers=headers, timeout=TIMEOUT)
response.raise_for_status()
return [wz for wz in response.json() if wz["api1Id"] == workzone_idv1]Extrahieren der Arbeitszonen-ID aus der Nutzereingabe.
def createAnnotation(tenant,
account_id,
project_id,
workzone_id,
title,
description,
position,
saved_view,
headers,
modelElementId=None,):
"""Creates a project in the specified account."""
workzone_guid = getWorkzoneGuid(tenant, account_id, project_id, workzone_id, headers)[0]['id'].split(':')[-1]
url = f"{tenant}/api/2/accounts/{account_id}/projects/{project_id}/annotations"
print(f"modelElementId : {modelElementId}")
if modelElementId:
body = {
'workzoneId': workzone_guid,
'annotationType': "Note",
'title': title,
'description': description,
'position': position,
'normal': {"x": 0, "y": 0, "z": 1},
'savedView': {**saved_view, 'modelElementId': modelElementId},
}
else:
body = {
'workzoneId': workzone_guid,
'annotationType': "Note",
'title': title,
'description': description,
'position': position,
'normal': {"x": 0, "y": 0, "z": 1},
'savedView': saved_view,
'elementId': "Toto"
}
print(url)
print(body)
response = requests.post(url, headers=headers, json=body, timeout=TIMEOUT)
print(response.text)
response.raise_for_status()
return response.json()Auslesen der Cintoo-Standortinformationen aus der Nutzereingabe und Zuordnen jeder Anmerkung zu einem entsprechenden Modellelement.
def main(context_url):
context_details = parse_cintoo_workzone_context_url(context_url)
tenant, account_id, project_id, workzone_id = context_details['tenant'], context_details['accountId'], \
context_details['projectId'], context_details['workzoneId']
# Authentifizieren und Zugriffstoken abrufen
login.load_tokens()
token = login.get_token(tenant)
headers = {"Authorization": f"Bearer {token}"}
camera_state = {"type": "legacy",
"camerastate": {
"position": [0, 0, 1],
"rotationQuaternion": [0, 0, 0, 1],
"fov": 1.5708,
"ortho": False,
"overviewScale": 10,
"camType": "scan"}}
print(workzone_id)
createAnnotation(tenant, account_id, project_id, workzone_id, "Test API", "Created with API",
{"x": 0, "y": 0, "z": 0}, camera_state, headers)Hauptfunktion mit Authentifizierungsprozess, Festlegung der Kameraposition und Erstellung von Anmerkungen im Cintoo-Projekt.
War dieser Artikel hilfreich?
Das ist großartig!
Vielen Dank für das Feedback
Leider konnten wir nicht helfen
Vielen Dank für das Feedback
Feedback gesendet
Wir wissen Ihre Bemühungen zu schätzen und werden versuchen, den Artikel zu korrigieren