1

Desired Outcome: I am attempting to generate a scalable vector graphic of the locations of our ambulances and a color indicating if the ambulance is busy, out of service, at quarters, etc. Our paramedics on the road can see this as a web page and see the disposition of all our ambulances as a map. At a glance they can see which ambulances are busy and which are free.

My Methodology: I am using python 2.7 with pyodbc and xml.etree. I am querying our our database to ascertain the most recent ambulance status, looping through the array and generating svg of the data.

Problem: When the output is written out to text and I open up the .svg file using Notepad, I see that there is one long line of output. I would like for it to be nested output. If you look at my code below, you will see that I am testing only for two ambulances. But I have fifty ambulances in my system. I can't have one long line of thousands of characters.

Question: How do I have python / ElementTree write out line breaks in the output?

Code shown below:

import pyodbc
from xml.etree import ElementTree as et

cnxn = pyodbc.connect('DRIVER={SQL Server}; SERVER=my server; DATABASE=my database; UID=my user id; PWD=my password')
cursor = cnxn.cursor()
AmbulanceStatus = """
WITH Maxtimes AS
(
SELECT
  MAX(M_Manpower_PK) AS [FirstRecord]
, PUN_UnitID
FROM MManpower
INNER JOIN PUnit ON M_kUnit = PUN_Unit_PK
WHERE PUN_UnitID LIKE 'M__'
OR PUN_UnitID LIKE 'FBA__'
AND M_tTime > DATEADD(HOUR, -24, GETDATE())
GROUP BY PUN_UnitID
)
,
MaxAttributes AS
(
SELECT
  M_Manpower_PK
, M_tTime
, M_Code
, CASE 
  WHEN M_Code IN ('USTA', 'USER', 'USOL', 'USAR', 'USIZ', 'UF', 'USTR', 'USDP') THEN 'Working'
  WHEN M_Code IN ('USAQ', 'USRQ') THEN 'Free'
  WHEN M_Code IN ('USES', 'USSB', 'USAS', 'USRS', 'USLC') THEN 'Standby'
  WHEN M_Code IN ('USOS') THEN 'OutofService'
  ELSE 'NA'
  END AS [Disposition]
FROM MManpower
INNER JOIN PUnit ON M_kUnit = PUN_Unit_PK
WHERE PUN_UnitID LIKE 'M__'
OR PUN_UnitID LIKE 'FBA__'
AND M_tTime > DATEADD(HOUR, -24, GETDATE())
)

SELECT
  MaxTimes.firstRecord AS [RecordNumber]
, MaxTimes.PUN_UnitID AS [Ambulance]
, CONVERT(VARCHAR(20), MaxAttributes.M_tTime, 108) AS [FinalTimeStamp]
, MaxAttributes.M_Code AS [UnitStatus]
, MaxAttributes.Disposition AS [Disposition]
FROM Maxtimes
LEFT OUTER JOIN MaxAttributes ON Maxtimes.FirstRecord=MaxAttributes.M_Manpower_PK
"""

cursor.execute(AmbulanceStatus)

Ambulances = cursor.fetchall()

cursor.close()

for ambulance in Ambulances:
    print ambulance

doc = et.Element('svg', width='792', height='612', version='1.1', xmlns='http://www.w3.org/2000/svg')
#===============================================================================
# I have no idea how to implement the following 15 lines of code. I downloaded them from the internet. I have googled this question and someone believes the following lines of code can help write out carriage returns.
#===============================================================================
# def indent(elem, level=0):
#    i = "\n" + level * "  "
#    if len(elem):
#        if not elem.text or not elem.text.strip():
#            elem.text = i + "  "
#        if not elem.tail or not elem.tail.strip():
#            elem.tail = i
#        for elem in elem:
#            indent(elem, level + 1)
#        if not elem.tail or not elem.tail.strip():
#            elem.tail = i
#    else:
#        if level and (not elem.tail or not elem.tail.strip()):
#            elem.tail = i
#===============================================================================
j = 0
#===============================================================================
# medic 19
#===============================================================================
while j < len(Ambulances):
    if Ambulances[j][1] == 'M19' and Ambulances[j][4] == 'Free':
        fillColor = 'aliceblue'
        fillText = 'black'
        et.SubElement(doc, 'circle', cx='520', cy='85', r='10', stroke='black', fill=fillColor)
        text = et.Element('text', x='520', y='85', fill=fillText, style='font-family:Sans;font-size:12px;text-anchor:middle;dominant-baseline:top')
        text.text = Ambulances[j][1]
        doc.append(text)

    elif Ambulances[j][1] == 'M19' and Ambulances[j][4] == 'Working':
        fillColor = 'red'
        fillText = 'white'
        et.SubElement(doc, 'circle', cx='520', cy='85', r='10', fill=fillColor)
        text = et.Element('text', x='520', y='85', fill=fillText, style='font-family:Sans;font-size:12px;text-anchor:middle;dominant-baseline:top')
        text.text = Ambulances[j][1]
        doc.append(text)

    elif Ambulances[j][1] == 'M19' and Ambulances[j][4] == 'Standby':
        fillColor = 'green'
        fillText = 'white'
        et.SubElement(doc, 'circle', cx='520', cy='85', r='10', fill=fillColor)
        text = et.Element('text', x='520', y='85', fill=fillText, style='font-family:Sans;font-size:12px;text-anchor:middle;dominant-baseline:top')
        text.text = Ambulances[j][1]
        doc.append(text)

    elif Ambulances[j][1] == 'M19' and Ambulances[j][4] == 'OutofService':
        fillColor = 'black'
        fillText = 'white'
        et.SubElement(doc, 'circle', cx='520', cy='85', r='10', fill=fillColor)
        text = et.Element('text', x='520', y='85', fill=fillText, style='font-family:Sans;font-size:12px;text-anchor:middle;dominant-baseline:top')
        text.text = Ambulances[j][1]
        doc.append(text)

    elif Ambulances[j][1] == 'M19' and Ambulances[j][4] == 'NA':
        fillColor = 'pink'
        fillText = 'blue'
        et.SubElement(doc, 'circle', cx='520', cy='85', r='10', fill=fillColor)
        text = et.Element('text', x='520', y='85', fill=fillText, style='font-family:Sans;font-size:12px;text-anchor:middle;dominant-baseline:top')
        text.text = Ambulances[j][1]
        doc.append(text)
#===============================================================================
# medic 16
#===============================================================================
    elif Ambulances[j][1] == 'M16' and Ambulances[j][4] == 'Free':
        fillColor = 'aliceblue'
        fillText = 'black'
        et.SubElement(doc, 'circle', cx='28.80024', cy='45.8372', r='10', stroke='black', fill=fillColor)
        text = et.Element('text', x='28.80024', y='45.8372', fill=fillText, style='font-family:Sans;font-size:12px;text-anchor:middle;dominant-baseline:top')
        text.text = Ambulances[j][1]
        doc.append(text)

    elif Ambulances[j][1] == 'M16' and Ambulances[j][4] == 'Working':
        fillColor = 'red'
        fillText = 'blue'
        et.SubElement(doc, 'circle', cx='28.80024', cy='45.8372', r='10', fill=fillColor)
        text = et.Element('text', x='28.80024', y='45.8372', fill=fillText, style='font-family:Sans;font-size:12px;text-anchor:middle;dominant-baseline:top')
        text.text = Ambulances[j][1]
        doc.append(text)

    elif Ambulances[j][1] == 'M16' and Ambulances[j][4] == 'Standby':
        fillColor = 'green'
        fillText = 'white'
        et.SubElement(doc, 'circle', cx='28.80024', cy='45.8372', r='10', fill=fillColor)
        text = et.Element('text', x='28.80024', y='45.8372', fill=fillText, style='font-family:Sans;font-size:12px;text-anchor:middle;dominant-baseline:top')
        text.text = Ambulances[j][1]
        doc.append(text)

    elif Ambulances[j][1] == 'M16' and Ambulances[j][4] == 'OutofService':
        fillColor = 'black'
        fillText = 'white'
        et.SubElement(doc, 'circle', cx='28.80024', cy='45.8372', r='10', fill=fillColor)
        text = et.Element('text', x='28.80024', y='45.8372', fill=fillText, style='font-family:Sans;font-size:12px;text-anchor:middle;dominant-baseline:top')
        text.text = Ambulances[j][1]
        doc.append(text)

    elif Ambulances[j][1] == 'M16' and Ambulances[j][4] == 'NA':
        fillColor = 'pink'
        fillText = 'blue'
        et.SubElement(doc, 'circle', cx='28.80024', cy='45.8372', r='10', fill=fillColor)
        text = et.Element('text', x='28.80024', y='45.8372', fill=fillText, style='font-family:Sans;font-size:12px;text-anchor:middle;dominant-baseline:top')
        text.text = Ambulances[j][1]
        doc.append(text)

#===============================================================================
# loop through the array
#===============================================================================
    j = j + 1

# ElementTree 1.2 doesn't write the SVG file header errata, so do that manually
f = open('C:\sample.svg', 'w')
f.write('<?xml version=\'1.0\' standalone=\'no\'?>\n')
f.write('<!DOCTYPE svg PUBLIC \'-//W3C//DTD SVG 1.1//EN\'\n')
f.write('\'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\'>\n')
f.write(et.tostring(doc))
f.close()
David Fort Myers
  • 333
  • 1
  • 5
  • 17
  • Have you tried adding a new line character to text just before you append it? – AlexLordThorsen Mar 14 '13 at 19:39
  • Does this answer your question? [Pretty printing XML in Python](https://stackoverflow.com/questions/749796/pretty-printing-xml-in-python) – mzjn Jul 21 '22 at 15:51

2 Answers2

2

Check out this link;

http://broadcast.oreilly.com/2010/03/pymotw-creating-xml-documents.html

from xml.dom import minidom

def prettify(elem):
    """Return a pretty-printed XML string for the Element.
    """
    rough_string = ET.tostring(elem, 'utf-8')
    reparsed = minidom.parseString(rough_string)
    return reparsed.toprettyxml(indent="  ")
Clay Morton
  • 333
  • 2
  • 15
0

I use the solution proposed in this answer https://stackoverflow.com/a/4590052/2132157

from xml.etree import ElementTree

def indent(elem, level=0):
    i = "\n" + level*"  "
    j = "\n" + (level-1)*"  "
    if len(elem):
        if not elem.text or not elem.text.strip():
            elem.text = i + "  "
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
        for subelem in elem:
            indent(subelem, level+1)
        if not elem.tail or not elem.tail.strip():
            elem.tail = j
    else:
        if level and (not elem.tail or not elem.tail.strip()):
            elem.tail = j
    return elem        

root = doc.getroot()
indent(root)
ElementTree.dump(root)
G M
  • 20,759
  • 10
  • 81
  • 84
  • There is a built-in `indent()` function since Python 3.9. See https://stackoverflow.com/a/68618047/407651, https://stackoverflow.com/a/63373633/407651 – mzjn Jul 21 '22 at 15:41
  • @mzjn thanks maybe you should add this solution to the other answer – G M Jul 22 '22 at 06:34
  • Not sure what you mean. What should I add? What other answer? There are so many answers already. – mzjn Jul 22 '22 at 06:37
  • @mzjn I meant the question that I linked, don't worry was just an idea – G M Jul 22 '22 at 06:39