I have a script in Python which connects to SQL using pyodbc and returns a set of values from a calendar for the 30 days following today. I prototyped it by using the print('') function to generate the HTML for the file I was creating then copying and pasting it in to an HTML file with Notepad++ and I know the HTML is sound and will be good for its purpose. However when it comes to generating the file I'm running aground with including the SQL results in the variable that is passed to the file writer.
I have tried both {variable} and %v methods which just seem to be either erroring out with;
unsupported format character ';' (0x3b) at index 1744
in the case of %, or in the case of {inset} is just including the word rather than the var. below is the code I have in JN;
from os import getenv
import pyodbc
cnxn = pyodbc.connect('DRIVER={ODBC Driver 13 for SQL Server};SERVER=MYSERVER\SQLEXPRESS;DATABASE=MyTable;UID=test;PWD=t')
f = open('tes.html','w')
cursor = cnxn.cursor()
cursor.execute('DECLARE #today as date SET #today = GetDate() SELECT style112, day, month, year, dayofweek, showroom_name, isbusy from ShowroomCal where Date Between #today and dateadd(month,1,#today) ')
row = cursor.fetchone()
while row is not None:
inset = ('<div class="',row.isbusy,'">',row.day,'</div>')
row = cursor.fetchone()
html_str = """
<html lang="en" ><head><meta charset="UTF-8"><title>Calendar</title>
<link rel=\'stylesheet prefetch\' href=\'https://netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.css\'>
<style>
body{background-color: #ffffff;}
a{color:#462955; text-decoration: none; display: block;}a:hover{color:#ffffff; text-decoration: none; display: block;}#yes a {color:#ffffff !important; text-decoration: none; display: block;}#yes a:hover {color:#ffffff !important; text-decoration: none; display: block;}
#calendar{margin-left: auto;margin-right: auto;width: 800px;font-family: \'Lato\', sans-serif;}
#calendar_weekdays div{display:inline-block;vertical-align:top;}
#calendar_content, #calendar_weekdays, #calendar_header{position: relative;width: 800px;overflow: hidden;float: left;z-index: 10;}
#calendar_weekdays div, #calendar_content div{width: 25px;height: 25px;overflow: hidden;text-align: center;background-color: #FFFFFF;color: #787878;}
.Yes{background-color: #990000 !important;color: #CDCDCD !important;}
.None{background-color: #ffffff !Important;color: #462955 !important;}
.None:hover{background-color: #462955 !Important;color: #ffffff !important;}
.wend{background-color: #676767 !important;color: #999999 !important;}
#calendar_content{background-colour: #ff0000;-webkit-border-radius: 0px 0px 12px 12px;-moz-border-radius: 0px 0px 12px 12px; border-radius: 0px 0px 12px 12px;}
#calendar_content div{float: left;}
#yes {background-color: #ff0000 !important;}
#calendar_content div:hover{background-color: #F8F8F8;}
#calendar_content div.blank{background-color: #E8E8E8;}
#calendar_header, #calendar_content div.today{zoom: 1;filter: alpha(opacity=70);opacity: 0.7;}
#calendar_content div.today{color: #FFFFFF;}
#calendar_header{width: 100%;height: 25px;text-align: center;background-color: #FF6860;padding: 8px 0;-webkit-border-radius: 12px 12px 0px 0px;-moz-border-radius: 12px 12px 0px 0px; border-radius: 12px 12px 0px 0px;}
#calendar_header h1{font-size: 1.5em;color: #FFFFFF;float:left;width:70%;
i[class^=icon-chevron]{color: #FFFFFF;float: left;width:15%;border-radius: 50%;}
</style>
<link href=\'https://fonts.googleapis.com/css?family=Lato\' rel=\'stylesheet\' type=\'text/css\'>
</head><base target="_parent">
<div id="calendar"><div id="calendar_header"><h1>07 2018</h1></div><div id="calendar_weekdays"></div><div id="calendar_content">
{inset}
</div></div><script src=\'jquery.min.js\'></script>
<script>
$(function(){function c(){p();var e=h();var r=0;var u=false;l.empty();while(!u){if(s[r]==e[0].weekday){u=true}else{l.append(\'<div class="blank"></div>\');r++}}for(var c=0;c<42-r;c++){if(c>=e.length){l.append(\'<div class="blank"></div>\')}else{var v=e[c].day;var m=g(new Date(t,n-1,v))?\'<div class="today">\':"<div>";l.append(m+""+v+"</div>")}}var y=o[n-1];a.css("background-color",y).find("h1").text(i[n-1]+" "+t);f.find("div").css("color",y);l.find(".today").css("background-color",y);d()}function h(){var e=[];for(var r=1;r<v(t,n)+1;r++){e.push({day:r,weekday:s[m(t,n,r)]})}return e}function p(){f.empty();for(var e=0;e<7;e++){f.append("<div>"+s[e].substring(0,3)+"</div>")}}function d(){var t;var n=$("#calendar").css("width",e+"px");n.find(t="#calendar_weekdays, #calendar_content").css("width",e+"px").find("div").css({width:e/7+"px",height:e/14+"px","line-height":e/14+"px"});n.find("#calendar_header").css({height:e*(1/14)+"px"}).find(\'i[class^="icon-chevron"]\').css("line-height",e*(1/14)+"px")}function v(e,t){return(new Date(e,t,0)).getDate()}function m(e,t,n){return(new Date(e,t-1,n)).getDay()}function g(e){return y(new Date)==y(e)}function y(e){return e.getFullYear()+"/"+(e.getMonth()+1)+"/"+e.getDate()}function b(){var e=new Date;t=e.getFullYear();n=e.getMonth()+1}var e=700;var t=2018;var n=9;var r=[];var i=["JANUARY","FEBRUARY","MARCH","APRIL","MAY","JUNE","JULY","AUGUST","SEPTEMBER","OCTOBER","NOVEMBER","DECEMBER"];var s=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];var o=["#462955","#462955","#462955","#462955","#462955","#462955","#462955","#462955","#462955","#462955","#462955","#462955"];var u=$("#calendar");var a=u.find("#calendar_header");var f=u.find("#calendarweekdays");var l=u.find("#calendarcontent");b();c();a.find(\'i[class^="icon-chevron"]\').on("click",function(){var e=$(this);var r=function(e){n=e=="next"?n+1:n-1;if(n<1){n=12;t--}else if(n>12){n=1;t++}c()};if(e.attr("class").indexOf("left")!=-1){r("previous")}else{r("next")}})})
function updateValue(val, event) {document.getElementById("field17").value = val;event.preventDefault();}
</script>
</body></html><wehavechangedit>
"""
cnxn.close()
f.write(html_str)
f.close()
Can anyone point me in the direction of a better way to include the variables? Do I need to have the inset as an array for this model?
It's Py3.6, on Windows 10.
Have you tried to just save your html_str inside a template .html file, write your inset lines into a long string, then read your file into a string, do the replace, then re-write the file?
with open('C:\\template.html') as file:
wholefile = file.readlines()
use this to make a string of your results.
inset = inset + '<div class="'+ str(row.isbusy) + '">' + str(row.day) + '</div>' + '\n'
and then do the replace, so you will have the complete file in a string, then write it back out.
wholefile.replace('{inset}',inset)
Related
I'm trying to get the text Something here I want to get inside the div element from a html file using Python and BeautifulSoup.
This is how part of the code looks like in html:
<div xmlns="" id="idp46819314579224" style="box-sizing: border-box; width: 100%; margin: 0 0 10px 0; padding: 5px 10px; background: #d43f3a; font-weight: bold; font-size: 14px; line-height: 20px; color: #fff;" class="" onclick="toggleSection('idp46819314579224-container');" onmouseover="this.style.cursor='pointer'">Something here I want to get<div id="idp46819314579224-toggletext" style="float: right; text-align: center; width: 8px;">
-
</div>
</div>
And this is how I tried to do:
vu = soup.find_all("div", {"style" : "background: #d43f3a"})
for div in vu:
print(div.text)
I use loop because there are several div with different id but all of them has the same background colour. It has no errors, but I got no output.
How can I get the text using the background colour as the condition?
The style attribute has other content inside it
style="box-sizing: ....; ....;"
Your current code is asking if style == "background: #d43f3a" which it is not.
What you can do is ask if "background: #d43f3a" in style -- a sub-string check.
One approach is passing a regular expression.
>>> import re
>>> vu = soup.find_all("div", style=re.compile("background: #d43f3a"))
...
... for div in vu:
... print(div.text.strip())
Something here I want to get
You can also say the same thing using CSS Selectors
soup.select('div[style*="background: #d43f3a"]')
Or by passing a function/lambda
>>> vu = soup.find_all("div", style=lambda style: "background: #d43f3a" in style)
...
... for div in vu:
... print(div.text.strip())
Something here I want to get
Getting a red cross while trying to embed a png file created through pyplot in email
The code I am using is here below. What can I add in the current html to get rid of this
I need to add the image in the email body
fig, ax = plt.subplots(figsize=(10,6))
ax.plot(
query['dt'],
query['cnt'],
marker = 'o',
markerfacecolor = 'DeepSkyBlue',
linewidth = 3,
color = 'MediumSlateBlue' )
ax.set(xlabel = 'D A T E', ylabel = 'C O U N T')
plt.setp(ax.get_xticklabels(), rotation = 90)
plt.savefig("C:/Users/quratulain.zulfiqar/Desktop/f.png", bbox_inches = "tight")
body = """<html>
<head>
<style>
#customers {
font-family: "proxima-nova", sans-serif;
border-collapse: collapse;
width: 100%;
}
#customers td, #customers th {
border: 1px solid #CB3D57;
padding: 8px;
}
#customers tr:nth-child(even){background-color: #CB3D57;}
#customers tr:hover {background-color: #CB3D57;}
#customers th {
padding-top: 12px;
padding-bottom: 10px;
text-align: center;
background-color: #CB3D57;
color: red;
}
</style>
</head>
<body>
<span style="text-align:center;"> <u><h3>"""+"AGENT MAPPING COUNT"+"""</h3></u> </span>
<div><img src= "cid:C:/Users/quratulain.zulfiqar/Desktop/f.png" ></img></div>
<p><p>
<div style='vertical-align:middle; display:att;'>
<p><p>
<p><p>
<p>Regards,<p>
</body>
</html>
"""
# In[288]:
msg1 = MIMEMultipart('alternative')
msg1['Subject'] = email_subject
msg1["From"]=from_label
msg1["To"]=to_label
msg1["CC"]=CC
file_list=[]
for fl in file_list:
with open(fl, "rb") as fil:
part = MIMEApplication(fil.read(),Name=basename(fl))
part['Content-Disposition'] = 'attachment; filename="%s"' % basename(fl)
msg1.attach(part)
part1 = MIMEText(body, 'html')
msg1.attach(part1)
with open('C:/Users/quratulain.zulfiqar/Desktop/f.png', 'rb') as f:
# set attachment mime and file name, the image type is png
mime = MIMEBase('image','png', filename='f.png')
# add required header data:
mime.add_header('Content-Disposition', 'attachment', filename='f.png')
mime.add_header('X-Attachment-Id', '0')
mime.add_header('Content-ID', '<0>')
mime.set_payload(f.read())
encode_base64(mime)
msg1.attach(mime)
server = smtplib.SMTP(email_server, email_port)
server.sendmail(from_email,to_emails, msg1.as_string())
server.quit()
I know there are other ways but since we are already this format which we were following so I need to stick to that and update it here.
The "cid:" string that you send in the email has to exactly match the Content-ID: header in the image attachment (well, the Content-ID gets angle brackets). That's how the email app makes the connection. You are specifying a path on YOUR computer for the cid, which is going to mean absolutely nothing when it is rendered on someone else's computer.
embedding image in html email
Changing the img src to the attached image name worked as #tim mentioned
<div><img src= "cid:f.png"></img></div>
I'm trying to get the following span from this website:
https://www.indeed.com/jobs?q=data&l=New+York%2C+NY&explvl=entry_level
<span class="indeed-apply-widget indeed-apply-button-container js-IndeedApplyWidget indeed-apply-status-not-applied" aria-labelledby="indeed-apply-button-label" data-indeed-apply-jobtitle="Growth Associate" data-indeed-apply-apitoken="aa102235a5ccb18bd3668c0e14aa3ea7e2503cfac2a7a9bf3d6549899e125af4" data-indeed-apply-coverletter="optional" data-indeed-apply-resume="required" data-indeed-apply-jk="40da42b64688bda8" data-indeed-apply-jobid="19c5d6a1fff8d6ba9724" data-indeed-apply-joblocation="New York, NY" data-indeed-apply-jobcompanyname="Via" data-indeed-apply-joburl="https://www.indeed.com/viewjob?jk=40da42b64688bda8" data-indeed-apply-posturl="https://dradisindeedapply.sandbox.indeed.net/process-indeedapply" data-indeed-apply-jobmeta="{"vtk":"1csimi0m80g7f002", "tk":""}" data-indeed-apply-advnum="7404493598529036" data-indeed-apply-onapplied="indeedApplyHandleApply" data-indeed-apply-onclose="indeedApplyHandleModalClose" data-indeed-apply-onclick="indeedApplyHandleButtonClick" data-indeed-apply-oncontinueclick="indeedApplyHandleModalClose" data-indeed-apply-pingbackurl="https://gdc.indeed.com/conv/orgIndApp?trk.origin=unknown&jk=40da42b64688bda8&vjtk=1csimi0m80g7f002&advn=7404493598529036&co=US&acct_key=899c31afcc98f5e9&sj=0" data-indeed-apply-skipcontinue="false" data-acc-payload="1,2,22,1,144,1,552,1,3648,1,4392,1" style="padding: 0px !important; margin: 0px !important; text-indent: 0px !important; vertical-align: top !important; position: relative; zoom: 1 !important; display: inline-block;"><a class="indeed-apply-button" href="javascript:void(0);" id="indeed-ia-1542520898760-0"><span class="indeed-apply-button-inner" id="indeed-ia-1542520898760-0inner"><span class="indeed-apply-button-label" id="indeed-ia-1542520898760-0label">Apply Now</span><span class="indeed-apply-button-cm"><img src="https://d3fw5vlhllyvee.cloudfront.net/indeedapply/s/14096d1/check.png" style="border: 0px;"></span></span></a></span>
And I tried this code:
url = "https://www.indeed.com/jobs?q=data&l=New+York%2C+NY&explvl=entry_level"
html = urlopen(url).read().decode('utf-8')
soup = BeautifulSoup(html, features = 'lxml')
soup.find_all("span", {"class":"indeed-apply-widget indeed-apply-button-container js-IndeedApplyWidget indeed-apply-status-not-applied",
"aria-labelledby":"indeed-apply-button-label"})
But the result is [].
There is no such element on URL you mentioned above but it exist in /viewjob?jk=.. page.
The class in your code is generated by javascript, if you view page source the real class is indeed-apply-widget and it only has 1 element
# https://www.indeed.com/viewjob?jk=0ee200c5fc30ce02&from=recjobs&vjtk=1csj1b3nmbi4v800
soup.find("span", {"class":"indeed-apply-widget"})
I was following the style guide for pandas and it worked pretty well.
How can I keep these styles using the to_html command through Outlook? The documentation seems a bit lacking for me.
(df.style
.format(percent)
.applymap(color_negative_red, subset=['col1', 'col2'])
.set_properties(**{'font-size': '9pt', 'font-family': 'Calibri'})
.bar(subset=['col4', 'col5'], color='lightblue'))
import win32com.client as win32
outlook = win32.Dispatch('outlook.application')
mail = outlook.CreateItem(0)
mail.Subject = subject_name
mail.HTMLbody = ('<html><body><p><body style="font-size:11pt;
font-family:Calibri">Hello,</p> + '<p>Title of Data</p>' + df.to_html(
index=False, classes=????????) '</body></html>')
mail.send
The to_html documentation shows that there is a classes command that I can put inside of the to_html method, but I can't figure it out. It also seems like my dataframe does not carry the style that I specified up top.
If I try:
df = (df.style
.format(percent)
.applymap(color_negative_red, subset=['col1', 'col2'])
.set_properties(**{'font-size': '9pt', 'font-family': 'Calibri'})
.bar(subset=['col4', 'col5'], color='lightblue'))
Then df is now a Style object and you can't use to_html.
Edit - this is what I am currently doing to modify my tables. This works, but I can't keep the cool features of the .style method that pandas offers.
email_paragraph = """
<body style= "font-size:11pt; font-family:Calibri; text-align:left; margin: 0px auto" >
"""
email_caption = """
<body style= "font-size:10pt; font-family:Century Gothic; text-align:center; margin: 0px auto" >
"""
email_style = '''<style type="text/css" media="screen" style="width:100%">
table, th, td {border: 0px solid black; background-color: #eee; padding: 10px;}
th {background-color: #C6E2FF; color:black; font-family: Tahoma;font-size : 13; text-align: center;}
td {background-color: #fff; padding: 10px; font-family: Calibri; font-size : 12; text-align: center;}
</style>'''
Once you add style to your chained assignments you are operating on a Styler object. That object has a render method to get the html as a string. So in your example, you could do something like this:
html = (
df.style
.format(percent)
.applymap(color_negative_red, subset=['col1', 'col2'])
.set_properties(**{'font-size': '9pt', 'font-family': 'Calibri'})
.bar(subset=['col4', 'col5'], color='lightblue')
.render()
)
Then include the html in your email instead of a df.to_html().
It's not an extravagant / pythonic solution. I inserted the link to a direct css file before the html code made by the to_html () method, then I saved the whole string as an html file. This worked well for me.
dphtml = r'<link rel="stylesheet" type="text/css" media="screen" href="css-table.css" />' + '\n'
dphtml += dp.to_html()
with open('datatable.html','w') as f:
f.write(dphtml)
Selecting the table (the rendered, styled, dataframe widgets in jupyter) and copy-pasting to an email body worked for me (using outlook office).
No manual html extraction, saving, loading, or anything like that.
ReportLab/xhtml2pdf have worked perfectly until now when it crashes at this style bit in HTML:
<p style="border-style: initial; border-color: initial; border-image: initial;
font-family: Ubuntu-R; font-size: small; border-width: 0px; padding: 0px;
margin: 0px;">Done:</p>
with this error:
File "/usr/local/lib/python2.7/dist-packages/reportlab/lib/colors.py",
line 850, in __call__
raise ValueError('Invalid color value %r' % arg)
ValueError: Invalid color value 'initial'
I use it typically like this:
pdf = pisa.pisaDocument(StringIO.StringIO(html.encode('UTF-8')),
result, encoding='UTF-8', link_callback=fetch_resources)
Is there a way to overcome this other than patching it's original code?
The border-style: initial value is not correct. See the code as a reference for supported border-style values:
none
hidden
dotted
dashed
solid
double
groove
ridge
inset
outset