I am using win32com.client in Python to send an email.
However I want the body of the email to be a table (HTML- formatted table), I can do it in an Excel first and then copy and paste (but how?), or directly edit the corresponding Pandas data frame.
newMail.body = my_table which is a Pandas data frame didn't work.
So I'm wondering if there is smarter ways for example, to combine Excel with Outlook apps within Python?
Cheers,
There are solutions regarding how to convert your Excel table to HTML here: How do I save Excel Sheet as HTML in Python?, and then you just drop the HTML into the body of your email.
Per request in the comments:
Once you have the HTML-formatted version of your table in a file called mytable.html, you can drop it into the email with: newMail.body = open("mytable.html").read()
I m replying on old quest , but it comes on the top in search,may be helpful.
import win32com.client as win32
import pandas as pd
# Read A to D column and first 5 rows
df = pd.read_excel('yourfilepath.xlsx', index_col=False, nrows = 5, usecols = "A:D")
body = df.to_html()
outlook = win32.gencache.EnsureDispatch('Outlook.Application')
new_mail = outlook.CreateItem(0)
new_mail.To = 'person#email.com'
new_mail.HTMLBody = (body)
new_mail.Send()
Related
data1=[218031839]
data3=[204394266]
# dataframe Name and Age columns
df = pd.DataFrame(data1)
#Adding the header
df.set_axis(["Bans"],axis=1,inplace=True)
df.head()
# Create a Pandas Excel writer using XlsxWriter as the engine.
writer = pd.ExcelWriter(r'C:\user\file.xlsx', engine='xlsxwriter')
# Convert the dataframe to an XlsxWriter Excel object.
df.to_excel(writer, sheet_name='Sheet1',startrow=1,index=False)
# Close the Pandas Excel writer and output the Excel file.
writer.save()
reader = pd.read_excel(r'file.xlsx')
print(reader)
#ss_body = ss_df1.to_html(index=False)
outlook = win32.Dispatch('outlook.application')
mail = outlook.CreateItem(0)
mail.To = 'xyz#gmail.com';
'
mail.Subject = 'Report'
mail.Body = 'testing purpose now ..Pl ignore the mail'
mail.attachmensts.add(r'C:\user\file.xlsx')
mail.send()
#remove the file
Os.remove(r'C:\user\file.xlsx')
getting the below:
os.remove(r'C:\Users\file.xlsx'')
PermissionError: [WinError 32] The process cannot access the file because it is being used by another process:
I successfully sent the mail from Python and next step is to remove the excel from the location where it is saved on my pc. But getting the above error and can anyone look into this.
You need to use the Attachments.Add method to attach a file. The source of the attachment can be a file (represented by the full file system path with a file name) or an Outlook item that constitutes the attachment. The file name is not enough. For example, the following VBA macro shows how to use the method (the Outlook object model is common for all kind of programming languages):
Sub AddAttachment()
Dim myItem As Outlook.MailItem
Dim myAttachments As Outlook.Attachments
Set myItem = Application.CreateItem(olMailItem)
Set myAttachments = myItem.Attachments
myAttachments.Add "D:\Test.xlsx", _
olByValue, 1, "Test"
myItem.Display
End Sub
Also there is no need to call the Display method right before Send.
I'm using the simple-smartsheet library for read data from a sheet in Smartsheet and download existing attachments on each row of the sheet.
I can already read the data for each row, however I cannot download existing attachments.
import config
from simple_smartsheet import Smartsheet
sheet = smartsheet.sheets.get(id=config.SHEET_ID)
for row in sheet.rows:
attachments = row.attachments
print(attachments)
when executing the above command I get as a result:
[]
simple-smartsheet
I use the simple-smartsheet library as it is the only one that supports python versions 3.6+
my python version 3.7.5
You can use list_row_attachments to find information of the attachments that belongs to a row.
The code might look like this:
import config
from simple_smartsheet import Smartsheet
sheet = smartsheet.sheets.get(id=config.SHEET_ID)
for row in sheet.rows:
response = smartsheet_client.Attachments.list_row_attachments(
config.SHEET_ID,
row.id,
include_all=True
)
attachments = response.data
print(attachments)
my solution is not very pythonic, but works, it consist of 2 steps
Get the attachment links
Save the file to a local HDD (I'm doing backups too) as a pivot place
1. to get the list of attachments:
import smartsheet
import urllib.request
smart = smartsheet.Smartsheet()
att_list = smart.Attachments.list_all_attachments(<sheet_id>, include_all=True)
2. Downloading the attachments to local disk, you need to create a loop to go through the list of attachments, you can also add your own conditions to discriminate which ones to download:
for attach in att_list:
att_id = attach.id #get the id of the attachment
att_name = attach.name # get the name of the attachment
retrieve_att = smart.Attachments.get_attachment(<sheet id>, att_id) #downloads the atachment
dest_dir = "C:\\path\\to\\folder\\"
dest_file = destd+str(att_name) # parsing the destination path
dwnld_url = retrieve_att.url # this link gives you access to download the file for about 5 to 10 min. before expire
urllib.request.urlretrieve(dwnld_url, dest_file) ## retrieving attachement and saving locally
Now you have the file and you can do whatever you need with it
It looks like that library has not implemented logic for dealing with the attachments yet.
as an alternative to solving this problem I implemented a solution with the code below:
import requests
#token = 'Your smartsheet Token'
#sheetId = 'Your sheet id'
r = requests.get('https://api.smartsheet.com/2.0/sheets/{sheetId}/rows/{rowId}/attachments', headers={'Authorization': f'Bearer {token}'})
response_json = r.json()
print(response_json)
see Get Attachments for more details on handling attachments Smartsheets
r = requests.get(projectsExportURL, auth=(username, password), verify=False,stream=True)
r.raw.decode_content = True
#add snapshot date column
df = pd.read_excel(r.raw,sheet_name='Tasks',Headers=0)
This worked just fine until 0.25.0 and xlrd 1.2.0
I recently had to re-do my entire environment and opted to update. The above code is now resulting in the following error:
File "d:\python\python37\lib\site-packages\pandas\io\excel\_base.py", line 356, in __init__
filepath_or_buffer.seek(0)
UnsupportedOperation: seek
if I remove xlrd from the equation pandas throws an error about an optional library missing (like if it is optional, why are you complaining).
So the incoming data is xlsx file format and I have to add a snapshot date to the file and then I send it to a MySQL database.
How can I fix my code to read the excel file with the changes to pandas, I can't seem to find anything in the docs that are specifically jumping out at me about this.
Here is my current replacement code that seems to be working:
wb = load_workbook(filename=BytesIO(r.raw.read()))
ws = wb['Tasks']
data = ws.values
columns = next(data)[0:]
df = pd.DataFrame(data, columns=columns)
This is how I solved this problem to download an xlsx excel file to pandas 1.0 DataFrame. This works on pandas >= 1.0
xl = requests.get(EXCEL_URL)
df = pd.read_excel(BytesIO(xl.content), sheet_name="Worksheet Name")
if sheet_name not given 1st sheet will be loaded.
Formatted Excel range copied to a Word file
This copies a range of cells from Excel and pastes them into a Word document with formatting preserved. The code works for this. However, I also want to paste the data into the body of an email with the cell styles.
import sys
from pathlib import Path
import win32com.client as win32
excel_path = str(Path.cwd() / 'input.xlsx')
excel = win32.gencache.EnsureDispatch('Excel.Application')
excel.Visible = False
excel.DisplayAlerts = False
wb = excel.Workbooks.Open(excel_path)
ws = wb.Worksheets(1)
ws.Range("A1:B2").Copy()
wb.Close()
word_path = str(Path.cwd() / 'output.docx')
word = win32.gencache.EnsureDispatch('Word.Application')
doc = word.Documents.Open(word_path)
doc.Content.PasteExcelTable(False, False, True)
doc.Close()
# That works, but now I want to paste the copied Excel range into the body of
# an email. The solution may look something like the following with the "X"
# as a "Selection" or "Range" object for the PasteExcelTable method.
outlook = win32.gencache.EnsureDispatch('Outlook.Application')
new_mail = outlook.CreateItem(0)
new_mail.To = 'person#email.com'
new_mail.Body = ws.Range("A1:B2").PasteExcelTable(False, False, True)
new_mail.Send()
sys.exit()
I believe [PasteExcelTable]2 can be used to make this happen with Outlook as it does for Word files and perhaps someone knows how this can be done.
I'm not defining the range correctly. Here's an example of one of my attempts:
new_mail.Body = ws.Range("A1:B2").PasteExcelTable(False, False, True)
I know this could be accomplished with VBA, but I want to use Python and to see whether PasteExcelTable will work for this. If it is not possible, I will capture an image of the data range and paste that to the email.
After hours of research and experimentation I found out how to use PasteExcelTable in Outlook. I hope this can be helpful. You only need to have the Excel Table in the clipboard.
import win32com.client
outlook = win32com.client.Dispatch('Outlook.Application')
mail = outlook.CreateItem(0)
mail.To = "person#email.com"
mail.Subject = "Subject"
mail.Display()
inspector = outlook.ActiveInspector()
word_editor = inspector.WordEditor
word_range = word_editor.Application.ActiveDocument.Content
word_range.PasteExcelTable(False, False, True)
Hope this will help...
import win32com.client as win32
import pandas as pd
df = pd.read_excel('fullexelpath.xlsx', index_col=False, nrows = 5, usecols = "A:D")
body = df.to_html()
outlook = win32.gencache.EnsureDispatch('Outlook.Application')
mail = outlook.CreateItem(0)
mail.To = 'abc#gmail.com'
mail.HTMLBody = (body)
mail.Send()
This copies a range of cells from Excel and pastes them into a Word document with formatting preserved. The code works for this. However, I also want to paste the data into the body of an email with the cell styles.
There are three main ways for working with bodies in Outlook:
Body - a plain text representing the message body.
HTMLBody.
Word editor. Outlook uses Word as an email editor. The Inspector class provides the WordEditor property which returns an instance of the Document class from the Word object model which represents the message body. Or just use the [GetInspector] method of the MailItem class to retrieve an instance of the Inspector class. You can read more about that in the Chapter 17: Working with Item Bodies .
So, basically you can re-use the same codebase used for Word in case of Outlook.
I was unable to use PasteExcelTable, but I was able to accomplish this with HTMLBody using the following code:
import sys
from pathlib import Path
import win32com.client as win32
from PIL import ImageGrab
excel_path = str(Path.cwd() / 'input.xlsx')
excel = win32.gencache.EnsureDispatch('Excel.Application')
excel.Visible = False
excel.DisplayAlerts = False
wb = excel.Workbooks.Open(excel_path)
ws = wb.Worksheets(1)
win32c = win32.constants
ws.Range("A1:B2").CopyPicture(Format=win32c.xlBitmap)
img = ImageGrab.grabclipboard()
image_path = str(Path.cwd() / 'test.png')
img.save(image_path)
outlook = win32.gencache.EnsureDispatch('Outlook.Application')
new_mail = outlook.CreateItem(0)
new_mail.To = 'person#email.com'
new_mail.Attachments.Add(Source=image_path)
body = "<h1>Email text...</h1><br><br> <img src=test.png>"
new_mail.HTMLBody = (body)
new_mail.Send()
wb.Close()
sys.exit()
views.py
def export_to_excel(request):
lists = MyModel.objects.all()
# your excel html format
template_name = "sample_excel_format.html"
response = render_to_response(template_name, {'lists': lists})
# this is the output file
filename = "model.csv"
response['Content-Disposition'] = 'attachment; filename='+filename
response['Content-Type'] = 'application/vnd.ms-excel; charset=utf-16'
return response
urls.py
from django.conf.urls.defaults import *
urlpatterns = patterns('app_name.views',
url(r'^export/$', 'export_to_excel', name='export_to_excel'),
)
Last, in your page create a button or link that will point in exporting.
page.html
Export
Nothing getting file option for download and not giving any error but i can see all result in log its working fine.
I think that the solution to export excel files is :
if 'excel' in request.POST:
response = HttpResponse(content_type='application/vnd.ms-excel')
response['Content-Disposition'] = 'attachment; filename=Report.xlsx'
xlsx_data = WriteToExcel(weather_period, town)
response.write(xlsx_data)
return response
In this example the library used for exporting is xlsxWriter.
Here is a very complete and practical solution for this, and many others: http://assist-software.net/blog/how-export-excel-files-python-django-application .
In addition to the options shown in the other answers you can also use XlsxWriter to create Excel files.
See this example.
It seems that you are trying to generate an excel workbook with HTML content. I don't know if Excel (or LibreOffice) is able to open such file but I think it is not the right approach.
You should fist generate a excel file : you can use csv, xlwt for xls and openpyxl for xlsx
The content of the file can be passed to the HttpResponse
for example, if you work with xlwt:
import xlwt
wb = xlwt.Workbook()
#use xlwt to fill the workbook
#
#ws = wb.add_sheet("sheet")
#ws.write(0, 0, "something")
response = HttpResponse(mimetype='application/vnd.ms-excel')
response['Content-Disposition'] = 'attachment; filename=the-file.xls'
wb.save(response)
return response
You can also look at
django-excel-response which does all the work for you. (I think it doesn't support xlsx format)
django-excel-export
I hope it helps
This seems to be based on the practice of tricking excel into opening an HTML table by changing the file name and MIME type. In order to make this work, the HTML file has to assemble an HTML table, and this is likely to trigger a warning that the real content of the file is different from the declared content.
IMHO it is a crude hack and should be avoided. Instead you can create a real excel file using the xlwt module, or you can create a real CSV file using the csv module.
[update]
After looking the blog post you refered, I see it is recommending another bad practice: using anything but the csv module to produce CSV files is dangerous because if the data contains the delimiter character, quotes or line breaks, you may end up with a bad CSV.
The csv module will take care of all corner cases and produce a proper formatted output.
I've seen people use a Django template naming the file "something.xls" and using HTML tables instead of the CSV format, but this has some corner cases as well.
Export Data to XLS File
Use it if you really need to export to a .xls file. You will be able to add formating as bold font, font size, define column size, etc.
First of all, install the xlwt module. The easiest way is to use pip.
pip install xlwt
views.py
import xlwt
from django.http import HttpResponse
from django.contrib.auth.models import User
def export_users_xls(request):
response = HttpResponse(content_type='application/ms-excel')
response['Content-Disposition'] = 'attachment; filename="users.xls"'
wb = xlwt.Workbook(encoding='utf-8')
ws = wb.add_sheet('Users')
# Sheet header, first row
row_num = 0
font_style = xlwt.XFStyle()
font_style.font.bold = True
columns = ['Username', 'First name', 'Last name', 'Email address', ]
for col_num in range(len(columns)):
ws.write(row_num, col_num, columns[col_num], font_style)
# Sheet body, remaining rows
font_style = xlwt.XFStyle()
rows = User.objects.all().values_list('username', 'first_name', 'last_name', 'email')
for row in rows:
row_num += 1
for col_num in range(len(row)):
ws.write(row_num, col_num, row[col_num], font_style)
wb.save(response)
return response
urls.py
import views
urlpatterns = [
...
url(r'^export/xls/$', views.export_users_xls, name='export_users_xls'),
]
template.html
Export all users
Learn more about the xlwt module reading its official documentation. https://simpleisbetterthancomplex.com/tutorial/2016/07/29/how-to-export-to-excel.html