Python XML trying to store elements in variables - python

I am trying to display elements from an XML file in tkinter Entry and Text widgets.
Example XML:
<notes>
<note id="5/10/22/14:20">
<index>5/10/22, 14:20 > Mr Anderson</index>
<date>5/10/22</date>
<time>14:20</time>
<from>Agt Smith</from>
<to>Mr Anderson</to>
<subject>App Test 3</subject>
<body>Do you hear that, Mr. Anderson? That is the sound of inevitability.</body>
</note>
</notes>
I was going to try getchildren() but I am told that is deprecated. I want to use the note id to pull up that element and its children, and insert various children into the correlating Entry and Text widgets. However, I cannot seem to get the correct values into variables. It feels like it should be simple and easy, but my instincts are leading me astray. What I am getting from this code is all of the entries displaying, rather than one selected by the note id. This is my code:
def displayNote(self):
# Clear the boxes first
clearBoxes()
# get index value
for i in noteList.curselection():
noteIndex = noteList.get(i)
# process index value to get id
subStrip = ' >.*'
noteStrip = noteIndex.replace(", " , "/").replace(":" , "")
noteID = re.sub(subStrip , "" , noteStrip)
# parse xml for box display
fileForDisplay = ET.parse(notefile)
rootd = fileForDisplay.getroot()
# and pull the info for that note id into variables
for note in rootd.findall('note'):
dateInfo = note.find("date").text
toInfo = note.find("to").text
fromInfo = note.find("from").text
subjectInfo = note.find("subject").text
bodyInfo = note.find("body").text
timeInfo = note.find("time").text
bodyDisplay = timeInfo + "\n" + bodyInfo

Related

Spotipy: 'Index out of range' when trying to retrieve album_ids so that I can get their release dates

I am working with this data, which I extracted from a public playlist and shows all of the number 1's since 1953 with their audio features: https://raw.githubusercontent.com/StanWaldron/StanWaldron.github.io/main/FinalData.csv
I am now trying to loop through and find their album ids so that I can retrieve their release date and plot their audio features against other time series data, using this code:
def find_album_release(name):
album_ids = []
for x in name:
results = sp.search(q="album:" + x, type="album")
if not results["albums"]["items"]:
return []
album_id = results['albums']['items'][0]['uri']
album_ids.append(album_id)
print(album_id)
return album_ids
final = pd.read_csv('FinalData.csv')
albumlist = final['album']
finalalbums = find_album_release(albumlist)
It works for the first 7 and then returns nothing. Without the if statement, it returns that the index is out of range. I have tested the 8th element by hard coding in its album name and it returns the correct result, this is the same for the next 4 in the list so it isn't an issue with the searching of these album names. I have played around with the lists but I am not entirely sure what is out of range of what.
Any help is greatly appreciated
The 8th row album name has single quotes in its name (Don't Stop Me Eatin'). I tried to remove the quotes and it worked. Maybe you should check what characters are allowed in the query parameters.
def find_album_release(name):
album_ids = []
for x in name:
x = x.replace("'", "") # Remove the quotes from album name
results = sp.search(q="album:" + x, type="album")
....
....
final = pd.read_csv('FinalData.csv')
albumlist = final['album']
finalalbums = find_album_release(albumlist)
The output for me:
spotify:album:31lHUoHC3P6BRFzKYLyRJO
spotify:album:6s84u2TUpR3wdUv4NgKA2j
spotify:album:4OyzQQJHEfKXRfyN4QyLR7
spotify:album:2Hjcfw8zHN4dJDZJGOzLd6
spotify:album:1zEBi4O4AaY5M55dUcUp3z
spotify:album:0Hi8bTOS35xZM0zZ6S89hT
spotify:album:5GGIgiGtxIgcVJQnsKQW94
spotify:album:3rLjiJI34bHFNIFqeK3y9s
spotify:album:6q1MiYTIE28nFzjkvLLt0I
spotify:album:61ulfFSmmxMhc2wCdmdMkN
spotify:album:3euz4vS7ezKGnNSwgyvKcd
spotify:album:1pFaEu56zqpzSviJc3htZN
spotify:album:4PTxbJPI4jj0Kx8hwr1v0T
spotify:album:2ogiazbrNEx0kQHGl5ZBTQ
spotify:album:5glfCPECXSHzidU6exW8wO
spotify:album:1XMw3pBrYeXzNXZXc84DNw
spotify:album:623PL2MBg50Br5dLXC9E9e
spotify:album:4TqgXMSSTwP3RCo3MMSR6t
spotify:album:3xIwVbGJuAcovYIhzbLO3J
spotify:album:3h2xv1tJgDnJZGy5Roxs5A
spotify:album:66xP0vUo8to8ALVpkyKc41
spotify:album:6XcYTEonLIpg9NpAbJnqrC
spotify:album:5sXSHscDjBez8VF20cSyad
spotify:album:6pQZPa398NswBXGYyqHH7y
spotify:album:0488X5veBK6t3vSmIiTDJY

Writing to databases on Python

I have been trying to write to a database and am having trouble setting data using two different classes in the one function.
Firstly, all values are being passed through a GUI and in this case, only the following entries were passed: 'Categories' = C, 'Usage' = Charter, 'DispTHR' = 5000.
Here you can see that I have two classes I am wanting to access (airport and runway) where the function set_opsdatabase_details() will go to the appropriate class and write to our database. This is all well and good when the airport and runway are separate from each other; however, when integrating them in the same function I can't seem to get the corresponding airportvalueDict = {} and runwayvalueDict = {] to display the values I want. Could someone help me understand how to write the correct entry box values into the corresponding valueDict dictionaries?
Thank you in advance! (a screenshot of the output from the print statements is attached)
Function in python
Output of function with the print statements
Example of code in text format:
#edit function for first part of operational window
def edit_details1(self):
airport = self.manager.get_chosen_airport()
runway = airport.get_chosen_runway()
airportlabels = ['Category', 'Usage', 'Curfew',
'Surveyor', 'LatestSurvey',
'FuelAvail', 'FuelPrice', 'TankerPort', 'RFF']
runwaylabels = ['DispTHR', 'VASI', 'TCH']
airportvalueDict = {}
runwayvalueDict = {}
print(self.entryDict['Category']["state"])
if self.entryDict['Category']["state"] == 'disabled':
for entry in self.entryDict.values():
entry.config(state=tk.NORMAL)
self.editButton.config(text="Confirm")
elif self.entryDict['Category']['state'] == 'normal':
airport = self.manager.get_chosen_airport()
runway = airport.get_chosen_runway()
values = [x.get() for x in self.varDict.values()]
for label, value in zip(airportlabels, values):
airportvalueDict[label] = value
airport.set_opsdatabase_details(airportvalueDict)
print(f'airport: {airportvalueDict}')
for label, value in zip(runwaylabels, values):
runwayvalueDict[label] = value
runway.set_opsdatabase_details(runwayvalueDict)
print(f'runway: {runwayvalueDict}')
for entry in self.entryDict.values():
entry.config(state=tk.DISABLED)
self.editButton.config(text="Edit...")

Catia select a feature from a specific instance in an assembly

Lets say I have an assembly like this:
MainProduct:
-Product1 (Instance of Part1)
-Product2 (Instance of Part2)
-Product3 (Instance of Part2)
-Product4 (Instance of Part3)
...
Now, I want to copy/paste a feature from Product3 into another one.
But I run into problems when selecting the feature programmatically, because there are 2 instances of the part of that feature.
I can't control which feature will be selected by CATIA.ActiveDocument.Selection.Add(myExtractReference)
Catia always selects the feature from Product2 instead of the feature from Product3. So the position of the pasted feature will be wrong!
Does anybody know this problem and has a solution to it?
Edit:
The feature reference which I want to copy already exists as a variable because it was newly created (an extract of selected geometry)
I could get help else where. Still want to share my solution. It's written in Python but in VBA its almost the same.
The clue is to access CATIA.Selection.Item(1).LeafProduct in order to know where the initial selection was made.
import win32com.client
import pycatia
CATIA = win32com.client.dynamic.DumbDispatch('CATIA.Application')
c_doc = CATIA.ActiveDocument
c_sel = c_doc.Selection
c_prod = c_doc.Product
# New part where the feature should be pasted
new_prod = c_prod.Products.AddNewComponent("Part", "")
new_part_doc = new_prod.ReferenceProduct.Parent
# from user selection
sel_obj = c_sel.Item(1).Value
sel_prod_by_user = c_sel.Item(1).LeafProduct # reference to the actual product where the selection was made
doc_from_sel = sel_prod_by_user.ReferenceProduct.Parent # part doc from selection
hb = doc_from_sel.Part.HybridBodies.Add() # new hybrid body for the extract. will be deleted later on
extract = doc_from_sel.Part.HybridShapeFactory.AddNewExtract(sel_obj)
hb.AppendHybridShape(extract)
doc_from_sel.Part.Update()
# Add the extract to the selection and copy it
c_sel.Clear()
c_sel.Add(extract)
sel_prod_by_catia = c_sel.Item(1).LeafProduct # reference to the product where Catia makes the selection
c_sel_copy() # will call Selection.Copy from VBA. Buggy in Python.
# Paste the extract into the new part in a new hybrid body
c_sel.Clear()
new_hb = new_part_doc.Part.HybridBodies.Item(1)
c_sel.Add(new_hb)
c_sel.PasteSpecial("CATPrtResultWithOutLink")
new_part_doc.Part.Update()
new_extract = new_hb.HybridShapes.Item(new_hb.HybridShapes.Count)
# Redo changes in the part, where the selection was made
c_sel.Clear()
c_sel.Add(hb)
c_sel.Delete()
# Create axis systems from Position object of sel_prd_by_user and sel_prd_by_catia
prod_list = [sel_prod_by_user, sel_prod_by_catia]
axs_list = []
for prod in prod_list:
pc_pos = pycatia.in_interfaces.position.Position(prod.Position) # conversion to pycata's Position object, necessary
# in order to use Position.GetComponents
ax_comp = pc_pos.get_components()
axs = new_part_doc.Part.AxisSystems.Add()
axs.PutOrigin(ax_comp[9:12])
axs.PutXAxis(ax_comp[0:3])
axs.PutYAxis(ax_comp[3:6])
axs.PutZAxis(ax_comp[6:9])
axs_list.append(axs)
new_part_doc.Part.Update()
# Translate the extract from axis system derived from sel_prd_by_catia to sel_prd_by_user
extract_ref = new_part_doc.Part.CreateReferenceFromObject(new_extract)
tgt_ax_ref = new_part_doc.Part.CreateReferenceFromObject(axs_list[0])
ref_ax_ref = new_part_doc.Part.CreateReferenceFromObject(axs_list[1])
new_extract_translated = new_part_doc.Part.HybridShapeFactory.AddNewAxisToAxis(extract_ref, ref_ax_ref, tgt_ax_ref)
new_hb.AppendHybridShape(new_extract_translated)
new_part_doc.Part.Update()
I would suggest a differed approach. Instead of adding references you get from somewhere (by name probably) add the actual instance of part to selection while iterating trough all the products. Or use instance Names to get the correct part.
Here is a simple VBA example of iterating one lvl tree and select copy paste scenario.
If you want to copy features, you have to dive deeper on the Instance objects.
Public Sub CatMain()
Dim ActiveDoc As ProductDocument
Dim ActiveSel As Selection
If TypeOf CATIA.ActiveDocument Is ProductDocument Then 'of all the checks that people are using I think this one is most elegant and reliable
Set ActiveDoc = CATIA.ActiveDocument
Set ActiveSel = ActiveDoc.Selection
Else
Exit Sub
End If
Dim Instance As Product
For Each Instance In ActiveDoc.Product.Products 'object oriented for ideal for us in this scenario
If Instance.Products.Count = 0 Then 'beware that products without parts have also 0 items and are therefore mistaken for parts
Call ActiveSel.Add(Instance)
End If
Next
Call ActiveSel.Copy
Call ActiveSel.Clear
Dim NewDoc As ProductDocument
Set NewDoc = CATIA.Documents.Add("CATProduct")
Set ActiveSel = NewDoc.Selection
Call ActiveSel.Add(NewDoc.Product)
Call ActiveSel.Paste
Call ActiveSel.Clear
End Sub

python + objectlistview + updatelist

I have an objectlistview. I remove a line from it and then I want to update the list without the removed line. I fill the list with data from a database. I tried repopulatelist, but then it seems to use the data that is already in the list.
I think I can solve it with clearAll (clearing the list) and then addobjects and add the database again. But it seems that it should be possible to just update the list. This is my code:
def deletemeas(self):
MAid = self.objectma.id
MAname = self.pagename
objectsRemList = self.tempmeasurements.GetCheckedObjects()
print 'objectremlist', objectsRemList
for measurement in objectsRemList:
print measurement
Measname = measurement.filename
Measid = database.Measurement.select(database.Measurement.q.filename == Measname)[0].id
deleteMeas = []
deleteMeas.append(MAid)
deleteMeas.append(Measid)
pub.sendMessage('DELETE_MEAS', Container(data=deleteMeas)) #to microanalyse controller
#here I get the latest information from the database what should be viewed in the objectlist self.tempmeasurements
MeasInListFromDB = list(database.Microanalysismeasurement.select(database.Microanalysismeasurement.q.microanalysisid == MAid))
print 'lijstmetingen:', MeasInListFromDB
#this doesn't work
self.tempmeasurements.RefreshObjects(MeasInListFromDB)
Ok, this was actually easier than I thought ...
I added this line:
self.tempmeasurements.RemoveObject(measurement)
So I first removed the data from my database table and then I just removed the line in my objectlistview.

How do you keep table rows together in python-docx?

As an example, I have a generic script that outputs the default table styles using python-docx (this code runs fine):
import docx
d=docx.Document()
type_of_table=docx.enum.style.WD_STYLE_TYPE.TABLE
list_table=[['header1','header2'],['cell1','cell2'],['cell3','cell4']]
numcols=max(map(len,list_table))
numrows=len(list_table)
styles=(s for s in d.styles if s.type==type_of_table)
for stylenum,style in enumerate(styles,start=1):
label=d.add_paragraph('{}) {}'.format(stylenum,style.name))
label.paragraph_format.keep_with_next=True
label.paragraph_format.space_before=docx.shared.Pt(18)
label.paragraph_format.space_after=docx.shared.Pt(0)
table=d.add_table(numrows,numcols)
table.style=style
for r,row in enumerate(list_table):
for c,cell in enumerate(row):
table.row_cells(r)[c].text=cell
d.save('tablestyles.docx')
Next, I opened the document, highlighted a split table and under paragraph format, selected "Keep with next," which successfully prevented the table from being split across a page:
Here is the XML code of the non-broken table:
You can see the highlighted line shows the paragraph property that should be keeping the table together. So I wrote this function and stuck it in the code above the d.save('tablestyles.docx') line:
def no_table_break(document):
tags=document.element.xpath('//w:p')
for tag in tags:
ppr=tag.get_or_add_pPr()
ppr.keepNext_val=True
no_table_break(d)
When I inspect the XML code the paragraph property tag is set properly and when I open the Word document, the "Keep with next" box is checked for all tables, yet the table is still split across pages. Am I missing an XML tag or something that's preventing this from working properly?
Ok, I also needed this. I think we were all making the incorrect assumption that the setting in Word's table properties (or the equivalent ways to achieve this in python-docx) was about keeping the table from being split across pages. It's not -- instead, it's simply about whether or not a table's rows can be split across pages.
Given that we know how successfully do this in python-docx, we can prevent tables from being split across pages by putting each table within the row of a larger master table. The code below successfully does this. I'm using Python 3.6 and Python-Docx 0.8.6
import docx
from docx.oxml.shared import OxmlElement
import os
import sys
def prevent_document_break(document):
"""https://github.com/python-openxml/python-docx/issues/245#event-621236139
Globally prevent table cells from splitting across pages.
"""
tags = document.element.xpath('//w:tr')
rows = len(tags)
for row in range(0, rows):
tag = tags[row] # Specify which <w:r> tag you want
child = OxmlElement('w:cantSplit') # Create arbitrary tag
tag.append(child) # Append in the new tag
d = docx.Document()
type_of_table = docx.enum.style.WD_STYLE_TYPE.TABLE
list_table = [['header1', 'header2'], ['cell1', 'cell2'], ['cell3', 'cell4']]
numcols = max(map(len, list_table))
numrows = len(list_table)
styles = (s for s in d.styles if s.type == type_of_table)
big_table = d.add_table(1, 1)
big_table.autofit = True
for stylenum, style in enumerate(styles, start=1):
cells = big_table.add_row().cells
label = cells[0].add_paragraph('{}) {}'.format(stylenum, style.name))
label.paragraph_format.keep_with_next = True
label.paragraph_format.space_before = docx.shared.Pt(18)
label.paragraph_format.space_after = docx.shared.Pt(0)
table = cells[0].add_table(numrows, numcols)
table.style = style
for r, row in enumerate(list_table):
for c, cell in enumerate(row):
table.row_cells(r)[c].text = cell
prevent_document_break(d)
d.save('tablestyles.docx')
# because I'm lazy...
openers = {'linux': 'libreoffice tablestyles.docx',
'linux2': 'libreoffice tablestyles.docx',
'darwin': 'open tablestyles.docx',
'win32': 'start tablestyles.docx'}
os.system(openers[sys.platform])
Have been straggling with the problem for some hours and finally found the solution worked fine for me. I just changed the XPath in the topic starter's code so now it looks like this:
def keep_table_on_one_page(doc):
tags = self.doc.element.xpath('//w:tr[position() < last()]/w:tc/w:p')
for tag in tags:
ppr = tag.get_or_add_pPr()
ppr.keepNext_val = True
The key moment is this selector
[position() < last()]
We want all but the last row in each table to keep with the next one
Would have left this is a comment under #DeadAd 's answer, but had low rep.
In case anyone is looking to stop a specific table from breaking, rather than all tables in a doc, change the xpath to the following:
tags = table._element.xpath('./w:tr[position() < last()]/w:tc/w:p')
where table refers to the instance of <class 'docx.table.Table'> which you want to keep together.
"//" will select all nodes that match the xpath (regardless of relative location), "./" will start selection from current node

Categories

Resources