Python Forum
XML minidom "Pretty Print" Lost Data
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
XML minidom "Pretty Print" Lost Data
#2
(Jun-14-2024, 08:36 PM)marksy95 Wrote: I have code that successfully creates a desired XML file from a provided CSV file. However, I'm trying to format it to be easier to read. I want the list of "triggers" and list of "messages" to be separated by a blank line and each "trigger" and each "message" should have its own line. Trying to use minidom, but the "pretty" file loses the triggers for some reason.

Code:

import csv
import os
import tkinter as tk
from tkinter import filedialog, simpledialog
import xml.etree.ElementTree as ET
from xml.dom import minidom

def process_csv_to_xml():
    # Initialize tkinter
    root = tk.Tk()
    root.withdraw()  # Hide main window

    # Prompt for CSV file
    csv_file_path = filedialog.askopenfilename(title="Select 'Tag Export' CSV file", filetypes=[("CSV files", "*.csv")])
    if not csv_file_path:
        print("No file selected.")
        return

    # Prompt for PLC Shortcut with default value
    plc_shortcut = simpledialog.askstring("Input", "Enter PLC Shortcut (STRING):", initialvalue="SCP", parent=root)
    if not plc_shortcut:
        print("PLC Shortcut not provided.")
        return

    # Prompt for Starting Trigger ID with default value
    starting_trigger_id = simpledialog.askinteger("Input", "Enter Starting Trigger ID (INT):", initialvalue=1, parent=root)
    if starting_trigger_id is None:
        print("Starting Trigger ID not provided.")
        return

    # Prompt for Starting Message ID with default value
    starting_message_id = simpledialog.askinteger("Input", "Enter Starting Message ID (INT):", initialvalue=1, parent=root)
    if starting_message_id is None:
        print("Starting Message ID not provided.")
        return
        
    # Prompt for XML file name with default value
    xml_file_path = filedialog.asksaveasfilename(
        title="Save XML file",
        defaultextension=".xml",
        filetypes=[("XML files", "*.xml")],
        initialfile="Alarms",
        initialdir=os.path.dirname(csv_file_path)
    )
    if not xml_file_path:
        print("XML file not saved.")
        return

    triggers = []
    messages = []
    trigger_number = starting_trigger_id
    message_number = starting_message_id

    with open(csv_file_path, 'r', newline='') as csv_file:
        csv_reader = csv.reader(csv_file)
        for _ in range(6):
            next(csv_reader)  # Skip first 6 lines
        headers = next(csv_reader)  # Line 7 is the headers

        for row in csv_reader:
            tag_type = row[headers.index('TYPE')]
            tag_name = row[headers.index('NAME')]
            data_type = row[headers.index('DATATYPE')]

            # Debug info
            print(f"Processing tag: Name='{tag_name}', DataType='{data_type}'")

            if tag_type == 'TAG':
                if data_type == 'All_DigitalAlm_ActiveResumeDelay':
                    trigger = ET.Element('trigger', id=f"T{trigger_number}", type="value",
                                         ack_all_value="0", use_ack_all="false", ack_tag="",
                                         exp=f"{{[{plc_shortcut}]{tag_name}.ALM_IND}}",
                                         message_tag="", message_handshake_exp="",
                                         message_notification_tag="", remote_ack_exp="",
                                         remote_ack_handshake_tag="", label=f"Label{trigger_number}",
                                         handshake_tag="")
                    triggers.append(trigger)
                    trigger_number += 1

                elif data_type == 'All_ScaleSignal_2SPFailAlm':
                    trigger = ET.Element('trigger', id=f"T{trigger_number}", type="value",
                                         ack_all_value="0", use_ack_all="false", ack_tag="",
                                         exp=f"{{[{plc_shortcut}]{tag_name}.ALM_Display}}",
                                         message_tag="", message_handshake_exp="",
                                         message_notification_tag="", remote_ack_exp="",
                                         remote_ack_handshake_tag="", label=f"Label{trigger_number}",
                                         handshake_tag="")
                    triggers.append(trigger)
                    trigger_number += 1

                elif data_type == 'Wtr_Motor_Alm':
                    trigger1 = ET.Element('trigger', id=f"T{trigger_number}", type="bit",
                                          ack_all_value="0", use_ack_all="false", ack_tag="",
                                          exp=f"{{[{plc_shortcut}]{tag_name}.Thermal_Seal_Status}}",
                                          message_tag="", message_handshake_exp="",
                                          message_notification_tag="", remote_ack_exp="",
                                          remote_ack_handshake_tag="", label=f"Label{trigger_number}",
                                          handshake_tag="")
                    triggers.append(trigger1)
                    trigger_number += 1

                    trigger2 = ET.Element('trigger', id=f"T{trigger_number}", type="value",
                                          ack_all_value="0", use_ack_all="false", ack_tag="",
                                          exp=f"{{[{plc_shortcut}]{tag_name}.Run_Fail_Status}}",
                                          message_tag="", message_handshake_exp="",
                                          message_notification_tag="", remote_ack_exp="",
                                          remote_ack_handshake_tag="", label=f"Label{trigger_number}",
                                          handshake_tag="")
                    triggers.append(trigger2)
                    trigger_number += 1

                elif data_type == 'More_Scale_Signal_For_Valve':
                    trigger = ET.Element('trigger', id=f"T{trigger_number}", type="value",
                                         ack_all_value="0", use_ack_all="false", ack_tag="",
                                         exp=f"{{[{plc_shortcut}]{tag_name}.Open_Close_Status}}",
                                         message_tag="", message_handshake_exp="",
                                         message_notification_tag="", remote_ack_exp="",
                                         remote_ack_handshake_tag="", label=f"Label{trigger_number}",
                                         handshake_tag="")
                    triggers.append(trigger)
                    trigger_number += 1

            # Handle messages
            if data_type == 'All_DigitalAlm_ActiveResumeDelay':
                message = ET.Element('message', id=f"M{message_number}", trigger_value="1",
                                     identifier=str(message_number), trigger=f"#T{trigger_number-1}",
                                     backcolor="#800000", forecolor="#FFFFFF", audio="false",
                                     display="true", print="false", message_to_tag="false",
                                     text=tag_name)
                messages.append(message)
                message_number += 1

            elif data_type == 'All_ScaleSignal_2SPFailAlm':
                for i in range(5):
                    message = ET.Element('message', id=f"M{message_number}", trigger_value=str(i+1),
                                         identifier=str(message_number), trigger=f"#T{trigger_number-1}",
                                         backcolor="#800000", forecolor="#FFFFFF", audio="false",
                                         display="true", print="false", message_to_tag="false",
                                         text=f"{tag_name}{'Low Float' if i == 0 else ('Low' if i == 1 else ('Signal Fail' if i == 2 else ('High' if i == 3 else 'High Float')))}")
                    messages.append(message)
                    message_number += 1

            elif data_type == 'Wtr_Motor_Alm':
                for i in range(5):
                    if i < 3:
                        message = ET.Element('message', id=f"M{message_number}", trigger_value=str(i+1),
                                             identifier=str(message_number), trigger=f"#T{trigger_number-2+i}",
                                             backcolor="#800000", forecolor="#FFFFFF", audio="false",
                                             display="true", print="false", message_to_tag="false",
                                             text=f"{tag_name}{'Thermal Fail' if i == 0 else ('Seal Fail' if i == 1 else ('Fail to Run' if i == 2 else ('RVSS Fault' if i == 3 else 'VFD Fault')))}")
                    else:
                        message = ET.Element('message', id=f"M{message_number}", trigger_value=str(i+1),
                                             identifier=str(message_number), trigger=f"#T{trigger_number-2+i}",
                                             backcolor="#800000", forecolor="#FFFFFF", audio="false",
                                             display="true", print="false", message_to_tag="false",
                                             text=f"{tag_name}{'VFD Fault'}")
                    messages.append(message)
                    message_number += 1

            elif data_type == 'More_Scale_Signal_For_Valve':
                for i in range(3):
                    message = ET.Element('message', id=f"M{message_number}", trigger_value=str(i+3),
                                         identifier=str(message_number), trigger=f"#T{trigger_number-1}",
                                         backcolor="#800000", forecolor="#FFFFFF", audio="false",
                                         display="true", print="false", message_to_tag="false",
                                         text=f"{tag_name}{'Position Fail' if i == 0 else ('Fail to Open' if i == 1 else 'Fail to Close')}")
                    messages.append(message)
                    message_number += 1

    # Debugging: Check contents of triggers and messages
    print(f"Total Triggers: {len(triggers)}")
    for t in triggers:
        print(f"Trigger: {ET.tostring(t, 'unicode')}")
    print(f"Total Messages: {len(messages)}")
    for m in messages:
        print(f"Message: {ET.tostring(m, 'unicode')}")

    # Create XML structure
    root_elem = ET.Element('root')

    # Append triggers
    triggers_elem = ET.SubElement(root_elem, 'triggers')
    for trigger in triggers:
        triggers_elem.append(trigger)

    # Append messages
    messages_elem = ET.SubElement(root_elem, 'messages')
    for message in messages:
        messages_elem.append(message)

    # Convert XML to string with proper formatting
    rough_string = ET.tostring(root_elem, 'utf-8')

    # Write the raw XML to a file
    raw_xml_file_path = xml_file_path.replace(".xml", "_RAW.xml")
    with open(raw_xml_file_path, 'wb') as raw_file:
        raw_file.write(rough_string)
    
    print(f"Raw XML file has been saved successfully at {raw_xml_file_path}")

    # Pretty print the XML
    try:
        reparsed = minidom.parseString(rough_string)
        pretty_xml = reparsed.toprettyxml(indent="  ")

        # Insert a blank line between <triggers> and <messages>
        lines = pretty_xml.split('\n')
        for i, line in enumerate(lines):
            if "<triggers>" in line:
                triggers_end = i
            if "<messages>" in line:
                messages_start = i
                break

        formatted_xml = "\n".join(lines[:triggers_end+1] + [""] + lines[messages_start:])
    except Exception as e:
        print(f"Error during pretty printing: {e}")
        formatted_xml = rough_string.decode('utf-8')

    # Write the pretty-printed XML to a file
    pretty_xml_file_path = xml_file_path.replace(".xml", "_PRETTY.xml")
    with open(pretty_xml_file_path, 'w', encoding='utf-8') as pretty_file:
        pretty_file.write(formatted_xml)
    
    print(f"Pretty XML file has been saved successfully at {pretty_xml_file_path}")

if __name__ == "__main__":
    process_csv_to_xml()
Test file and resultant XML files are attached.

Seems like the only real issue is trying to join them back together with the newline.
Reply


Messages In This Thread
XML minidom "Pretty Print" Lost Data - by marksy95 - Jun-14-2024, 08:36 PM
RE: XML minidom "Pretty Print" Lost Data - by marksy95 - Jun-14-2024, 08:52 PM

Possibly Related Threads…
Thread Author Replies Views Last Post
  i tried to install python for the first time today and pretty certain im being remote brianlj 2 734 Oct-03-2023, 11:15 AM
Last Post: snippsat
  Lost Control over VLC jrockow 8 1,314 Jul-18-2023, 06:04 PM
Last Post: jrockow
  Lost Modules standenman 2 866 Jun-22-2023, 12:18 PM
Last Post: standenman
  print(data) is suddenly invalid syntax db042190 6 1,471 Jun-14-2023, 02:55 PM
Last Post: deanhystad
  Lost Module standenman 10 3,118 Oct-30-2021, 05:11 PM
Last Post: deanhystad
  XML Editing formatting lost ateestructural 2 2,021 Apr-08-2021, 04:41 AM
Last Post: ndc85430
  how to print all data from all data array? korenron 3 2,604 Dec-30-2020, 01:54 PM
Last Post: korenron
  List structure lost when multiplying Protonn 2 2,399 Apr-23-2020, 04:16 AM
Last Post: buran
  How do I install the dom.minidom module? Johno 3 7,424 Apr-17-2020, 09:46 PM
Last Post: Larz60+
  How to print counter without bracket in python and sort data. phob0s 1 2,891 Jul-25-2019, 05:33 PM
Last Post: ichabod801

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020