Accessing UpdateLinks() in COM Object using Python - python

I am working on automating an Excel file which is linked to certain .csv files.
Those .csv files are created from a SAS Code which is run every Quarter.
The files created are timestamped accordingly for example XYZ_201603.csv and XYZ_201606.csv and so on.
I need to update the links on my Excel File so that it automatically changes the link to the file from next quarter. I am trying to do this using Python win32com.client and my code looks like
from win32com import Dispatch
xl_app = Dispatch("Excel.Application")
xl_app.Visible = True
xl_app.DisplayAlerts = False
wb = xl_app.workbooks.open(r"C:\Users\XYZ\Desktop\Test\Summary.xlsx")
xl_app.AskToUpdateLinks = False
try:
wb.UpdateLink(Name=r"C:\Users\XYZ\Desktop\Test\XYZ_201606.csv")
except Exception as e:
print(e)
finally:
wb.Close(True)
wb = None
return True
xl_app.Quit()
xl = None
Whenever I run this, I get the following error
(-2147352567,'Exception occured.',(0,'Microsoft Excel','UpdateLink method of
Workbook class failed','xlmain11.chm',0,-2146827284),None)
Can Somebody tell me what is going wrong here. Also, incase I have multiple links, how do I tell which link needs to be changed to what? Can I pass a dictionary of directories of updated datasets
The code and the approach has been taken from this answer on Stack Overflow
Update Links in for Excel Spreadsheet Using Python

If you review the Microsoft Documentation, it seems that the UpdateLink method can be called without any parameters. Therefore this program should work:
import win32com.client as win32
xl_app = win32.gencache.EnsureDispatch("Excel.Application")
xl_app.Visible = True
xl_app.DisplayAlerts = False
wb = xl_app.workbooks.open(r"C:\Users\XYZ\Desktop\Test\Summary.xlsx")
wb.UpdateLink()
wb.Save()
wb.Close()
xl_app.Quit()

I'm not sure if my solution solves your issue, but I had the same problem and I used LinkSources() and ChangeLink() instead
newSource = r"C:\Users\XYZ\Desktop\Test\XYZ_201606.csv"
oldSource = wb.LinkSources()
wb.ChangeLink(Name = oldSource[0], NewName = newSource, Type = 1)
Hope it helps!

Related

update links using win32com where excel is linked with multiple sources

I am using win32com.client to open Excel with
excel = win32com.client.Dispatch("Excel.Application")
workbook = excel.Workbooks.Open(filename,UpdateLinks=False, ReadOnly=True)
Sometimes this generates an alert with the following text:
"This workbook contains one or more links that cannot be
updated." & options are: "Continue", and "Edit Links").
This stops the script until I handle the dialogue box manually which is not preferred.
I want to continue as if "Continue" had been
clicked, or to suppress the whole dialogue box, I can't find a way to do this.
Searching the Stackoverflow forums, I've seen the following suggestions:
excel.DisplayAlerts = False
excel.AskToUpdateLinks = False
I've tried each of these (immediately after the excel variable is
assigned) and neither has the desired effect, separately or together.
As suggested in other forums I have also tried to open excel & save & then again reopen with python, which is also not working.
wb = xlApp.Workbooks.Open(Path to file,UpdateLinks = False)
ws = wb.Worksheets('XYZ')
wb.Close(SaveChanges=1)
excel.Visible = False
excel.ScreenUpdating = False
excel.DisplayAlerts = False
excel.AskToUpdateLinks = False
wb = xlApp.Workbooks.Open(Path to file)
ws = wb.Worksheets('XYZ')
Does anyone know a solution that works? anything will do either break link or continue.
Thank you in advance
excel.AskToUpdateLinks = False
As far as I know, the above turns off the prompt and automatically update external links as a file is open. Try use below:
excel = win32com.client.Dispatch("Excel.Application")
excel.DisplayAlerts = False
workbook = excel.Workbooks.Open(Path to file,UpdateLinks=0)

How to extract OLE objects from Excel table using Python?

I would like to use Python to extract OLE-objects from an Excel table into the Windows clipboard.
This post didn't help further since it is for VBA.
And this post is still unanswered.
Assuming the given Excel table (with ChemDraw or ChemSketch OLE objects):
There are some Python modules which can handle Excel files, e.g. openpyxl, xlrd.
The module win32clipboard can put data into the clipboard.
My Problems:
I don't see how to get the embedded OLE object to the clipboard. Probably, openpyxl and xlrd together with win32clipboard are not suited for this?
There is a Python module oletools which maybe can do it but I don't understand how it works.
https://pypi.org/project/oletools/
From this page:
oleobj: to extract embedded objects from OLE files.
This seems to be exactly what I am looking for, however, I couldn't find any MCVEs. And unfortunately, the documentation of oleobj is basically reduced to: "read the source code and find out yourself". I would be grateful for hints and assistance.
My code so far:
### trying to extract OLE objects from Excel table into clipboard
from openpyxl import load_workbook
import win32clipboard as clpbd
def set_clipboard(data):
clpbd.OpenClipboard()
clpbd.EmptyClipboard()
clpbd.SetClipboardText(data) # I'm aware, this is only for text, is there anything for OLEs?
clpbd.CloseClipboard()
def print_clipboard():
clpbd.OpenClipboard()
data = clpbd.GetClipboardData()
clpbd.CloseClipboard()
print(data)
wb = load_workbook(filename = 'tbChemOLE.xlsx')
ws = wb.active
myName = ws['A3'].value # result: napthalene
myImage = ws['B3'].value # result: None
myObject = ws['C3'].value # result: None
set_clipboard(myName)
print_clipboard() # result: Naphtalene
# set_clipboard(myImage) # crash, because myImage is None
print_clipboard()
# set_clipboard(myObject) # crash, because myObject is None
print_clipboard()
wb.close()
### end of code
I built a python module to do exactly this check it out over here. https://pypi.org/project/AttachmentsExtractor/ also the module can be run on any os.
after installing the library use the following code snippet:
from AttachmentsExtractor import extractor
abs_path_to_file='Please provide absolute path here '
path_to_destination_directory = 'Please provide path of the directory where the extracted attachments should be stored'
extractor.extract(abs_path_to_file,path_to_destination_directory) # returns true if one or more attachments are found else returns false.
In the meantime I found this post, where the OP actually didn't want the OLE objects on the clipboard, but for me it is fine. Actually, no need for openpyxl or xlrd, but win32com.client is required.
I can get all OLE objects, however, they are indexed (probably) in the sequence of their addition.
So I need to create a dictionary with the row index as key and a set of OLE object index of and name as value.
Code:
### copy OLE object in certain cell to clipboard
import win32com.client as win32
import win32clipboard
excel = win32.gencache.EnsureDispatch('Excel.Application')
ffname = r'C:\Test\tbChemOLE.xlsx'
wb = excel.Workbooks.Open(ffname)
ws = wb.Worksheets.Item(1)
objs = ws.OLEObjects()
def get_all_OLEs():
oleNo_dict = {} # dictionary for all OLE objects
for i in range(1,len(objs)+1): # loop all OLE objects
obj = objs.Item(i)
myRow = obj.TopLeftCell.Row # row of OLE object
myName = ws.Cells(myRow,1).Value # corresponding name
oleNo_dict[myRow] = (i, myName)
return oleNo_dict
def get_OLE(row):
try:
objs[oleNo_dict[row][0]].Copy()
win32clipboard.OpenClipboard()
data = win32clipboard.GetClipboardData(0xC004) # Binary access
win32clipboard.CloseClipboard()
except Exception as e:
print(e)
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.CloseClipboard()
return oleNo_dict[row]
# and OLE is on clipboard if found
oleNo_dict = get_all_OLEs()
row = 4
myMolecule = get_OLE(row)
print(myMolecule[1], "OLE object is now on the clipboard.")
wb.Close()
excel.Application.Quit()
### end of code
Result:
Anthracene OLE object is now on the clipboard.

Getting file exists overwrite alert when trying to refresh and save .xlsm file via python even with DisplayAlerts set to False

I am trying to refresh a macro enabled excel file through a python script. I have used win32com.client package for launching an excel instance and refreshing the file and trying to save it. I have also set DisplayAlerts = False. I am getting an alert like:
A file named 'F:\User\output\testfile.xlsm' already exists. Do you want to overwrite it?
I am not getting this alert for other xlsm files that I am trying to refresh even though there are files present with the same name in the destination folder. Here is a sample of the code I used to refresh the files:
import win32com.client as win32
xl = win32.DispatchEx('Excel.Application')
xl.DisplayAlerts = False
xl.Visible = False
xl.Interactive = False
xl.EnableEvents=False
wb = xl.Workbooks.Open(os.path.join(excel_path),ReadOnly=False, IgnoreReadOnlyRecommended =True)
xl.DeferAsyncQueries = True
wb.RefreshAll()
xl.CalculateUntilAsyncQueriesDone()
xl.DeferAsyncQueries = False
wb.SaveAs(output_filepath,ReadOnlyRecommended =False)
wb.Close(False)
xl.Quit()
Can anyone help me figure out why am I getting the alert only for that particular file?
Thanks in advance.

How do I download an xlsm file and read every sheet in python?

Right now I am doing the following.
import xlrd
resp = requests.get(url, auth=auth).content
output = open(r'temp.xlsx', 'wb')
output.write(resp)
output.close()
xl = xlrd.open_workbook(r'temp.xlsx')
sh = 1
try:
for sheet in xl.sheets():
xls.append(sheet.name)
except:
xls = ['']
It's extracting the sheets but I don't know how to read the file or if saving the file as an .xlsx is actually working for macros. All I know is that the code is not working right now and I need to be able to catch the data that is being generated in a macro. Please help! Thanks.
I highly recommend using xlwings if you want to open, modify, and save .xlsm files without corrupting them. I have tried a ton of different methods (using other modules like openpyxl) and the macros always end up being corrupted.
import xlwings as xw
app = xw.App(visible=False) # IF YOU WANT EXCEL TO RUN IN BACKGROUND
xlwb = xw.Book('PATH\\TO\\FILE.xlsm')
xlws = {}
xlws['ws1'] = xlwb.sheets['Your Worksheet']
print(xlws['ws1'].range('B1').value) # get value
xlws['ws1'].range('B1').value = 'New Value' # change value
yourMacro = xlwb.macro('YourExcelMacro')
yourMacro()
xlwb.save()
xlwb.close()
Edit - I added an option to keep Excel invisible at users request

dynamically read and/or overwrite an excel file with python without the overwrite alert appearing

For some reason the following code runs fine but the file overwrite alert keeps coming up even though I have set xl.EnableEvents = False, the code won't execute further unless I manually click the overwrite file popup. Does anyone know how to fix this?
The code opens an excel file which contains a string which allows the excel file to connect to the bloomberg api, I used this solution here to get this to work. As long as the file is open for enough time the data is pulled into the file and then saves and exits. It takes around ~35 seconds to get the data and the pandas table starts displaying the content I'm requesting
The problem is the popups! - I need to see when the string '#N/A Requesting Data...' is no longer in the file and can't see a way to do it without periodically saving the file. A solution that allows me to see the file contents dynamically without having to save would be great.
The solution here didn't work for me to stop the popups, I could probably make a new file each time and then delete them all at the end but this seems a bit clunky. This question extends this problem here if anyone wants to see the code and the problem in a more full context.
WB = 'C:/path/to/my/file.xlsx'
location = "EGLL"
def run_VWA(WB, location):
"""open the excel file, allow enough time to pull the data, then close and save"""
bb = 'C:/blp/API/Office Tools/BloombergUI.xla'
xl=win32com.client.DispatchEx("Excel.Application")
xl.Workbooks.Open(bb)
xl.AddIns("Bloomberg Excel Tools").Installed = True
wb = xl.Workbooks.Open(Filename=WB) #opens workbook in readonly mode.
xl.Visible = False
xl.EnableEvents = False
xl.DisplayAlerts = False
total=0
colstring='#N/A Requesting Data...'
while total < 40:
wb.Save()
df = df_from_excel(WB, location)
if colstring not in df:
break
time.sleep(3)
total+=3
wb.Close(SaveChanges=1)
xl.DisplayAlerts = True
xl.Quit()
#Cleanup the com reference.
del xl
return
Any help with this is much appreciated, I have very limited experience with the win32com library.
After a good few hours digging I've found how to solve this dynamically without the need for saving the file each iteration. If anyone else comes up against this problem most of the solution was found here. Many thanks assylias for some useful pointers.
def run_VWA(WB, location):
"""open the excel file, allow enough time to pull the data, then close and save"""
bb = 'C:/blp/API/Office Tools/BloombergUI.xla'
xl=win32com.client.DispatchEx("Excel.Application")
xl.Workbooks.Open(bb)
xl.AddIns("Bloomberg Excel Tools").Installed = True
wb = xl.Workbooks.Open(Filename=WB) #opens workbook in readonly mode.
xl.Visible = False
xl.EnableEvents = False
xl.DisplayAlerts = False
count=0
while True:
readData = wb.Worksheets(location)
allData = readData.UsedRange
if allData.Rows.Count > 1 or allData.Columns.Count > 1:
print('rows: {}'.format(allData.Rows.Count))
print('cols: {}'.format(allData.Columns.Count))
break
print(wb)
print(count)
time.sleep(3)
count+=3
wb.Close(SaveChanges=1)
xl.DisplayAlerts = True
xl.Quit()
#Cleanup the com reference.
del xl
return

Categories