Bottle Static files - python

I have tried reading the docs for Bottle, however, I am still unsure about how static file serving works. I have an index.tpl file, and within it it has a css file attached to it, and it works. However, I was reading that Bottle does not automatically serve css files, which can't be true if the page loads correctly.
I have, however, run into speed issues when requesting the page. Is that because I didn't use the return static_file(params go here)? If someone could clear up how they work, and how they are used when loading the page, it would be great.
Server code:
from Bottle import route,run,template,request,static_file
#route('/')
def home():
return template('Templates/index',name=request.environ.get('REMOTE_ADDR'))
run(host='Work-PC',port=9999,debug=True)
Index:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type"
content="text/html; charset=ISO-8859-1">
<title>index</title>
<link type="text/css"
href="cssfiles/mainpagecss.css"
rel="stylesheet">
</head>
<body>
<table
style="width: 100%; text-align: left; margin-left: auto; margin-right: auto;"
border="0" cellpadding="2" cellspacing="2">
<tbody>
<tr>
<td>
<h1><span class="headertext">
<center>Network
Website</center>
</span></h1>
</td>
</tr>
</tbody>
</table>
%if name!='none':
<p align="right">signed in as: {{name}}</p>
%else:
pass
%end
<br>
<table style="text-align: left; width: 100%;" border="0" cellpadding="2"
cellspacing="2">
<tbody>
<tr>
<td>
<table style="text-align: left; width: 100%;" border="0"
cellpadding="2" cellspacing="2">
<tbody>
<tr>
<td style="width: 15%; vertical-align: top;">
<table style="text-align: left; width: 100%;" border="1"
cellpadding="2" cellspacing="2">
<tbody>
<tr>
<td>Home<br>
<span class="important">Teamspeak Download</span><br>
<span class="important">Teamspeak Information</span></td>
</tr>
</tbody>
</table>
</td>
<td style="vertical-align: top;">
<table style="text-align: left; width: 100%;" border="1"
cellpadding="2" cellspacing="2">
<tbody>
<tr>
<td>
<h1><span style="font-weight: bold;">Network Website</span></h1>
To find all of the needed information relating to the network's social
capabilities, please refer to the links in the side bar.</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</body>
</html>

To serve static files using bottle you'll need to use the provided static_file function and add a few additional routes. The following routes direct the static file requests and ensure that only files with the correct file extension are accessed.
from bottle import get, static_file
# Static Routes
#get("/static/css/<filepath:re:.*\.css>")
def css(filepath):
return static_file(filepath, root="static/css")
#get("/static/font/<filepath:re:.*\.(eot|otf|svg|ttf|woff|woff2?)>")
def font(filepath):
return static_file(filepath, root="static/font")
#get("/static/img/<filepath:re:.*\.(jpg|png|gif|ico|svg)>")
def img(filepath):
return static_file(filepath, root="static/img")
#get("/static/js/<filepath:re:.*\.js>")
def js(filepath):
return static_file(filepath, root="static/js")
Now in your html, you can reference a file like so:
<link type="text/css" href="/static/css/main.css" rel="stylesheet">
Directory layout:
`--static
| `--css
| `--fonts
| `--img
| `--js

Just providing an answer here because a number of my students have used this code in an assignment and I have a bit of a concern about the solution.
The standard way to serve static files in Bottle is in the documentation:
from bottle import static_file
#route('/static/<filepath:path>')
def server_static(filepath):
return static_file(filepath, root='/path/to/your/static/files')
in this way, all of the files below your static folder are served from a URL starting with /static. In your HTML, you need to reference the full URL path for the resource, eg:
<link rel='stylesheet' type='text/css' href='/static/css/style.css'>
The answer from Sanketh makes it so that any reference to an image, css file etc anywhere in the URL space is served from a given folder inside the static folder. So /foo/bar/baz/picture.jpg and /picture.jpg would both be served from static/images/picture.jpg. This means that you don't need to worry about getting the path right in your HTML code and you can always use relative filenames (ie. just src="picture.jpg").
The problem with this approach comes when you try to deploy your application. In a production environment you want the static resources to be served by a web server like nginx, not by your Bottle application. To enable this, they should all be served from a single part of the URL space, eg. /static. If your code is littered with relative filenames, it won't translate easily to this model.
So, I'd advise using the three line solution from the Bottle tutorial rather than the more complex solution listed on this page. It's simpler code (so less likely to be buggy) and it allows you to seamlessly move to a production environment without code changes.

As indicated in the documentation, you should serve static files using the static function and css is a static file. The static function handles security and some other function which you can find out from the source. The path argument to the static function should point to the directory wherever you store the css files

Rather than use regular expression matching for serving files as in Sanketh's answer, I'd prefer not to modify my templates and provide a path to the static files explicitly, as in:
<script src="{{ get_url('static', filename='js/bootstrap.min.js') }}"></script>
You can do this simply by replacing the <filename> in the static route decorator with one of type :path - like so:
#app.route('/static/<filename:path>', name='static')
def serve_static(filename):
return static_file(filename, root=config.STATIC_PATH)
The :path matches an entire file path in a non-greedy way so you don't have to worry about changing templates when switching to production - just keep everything in the same relative folder structure.

I've used Sanketh's template in the past but over time condensed it to an extension agnostic function. You just have to add extension-folder mappings in the ext_map dictionary. It defaults to static/ folder if an extension is not mapped explicitly.
import os.path
# Static Routes
#get('/<filename>')
def serve_static_file(filename):
ext = os.path.splitext(filename)[1][1:]
ext_map = {'image':['png','gif','jpg','ico'],'js':['js']}
sub_folder = next((k for k, v in ext_map.items() if ext in v),'')
return static_file(filename, root='static/'+sub_folder)

Related

Simple Python and Flask question -- 404 error [duplicate]

This question already has an answer here:
Flask : href link to html not working
(1 answer)
Closed last year.
I'm trying to make a program to display some time and temperature numbers on a web page. I have two html templates, "index.html" and "set.html" in a "templates" directory under the main "furnace" directory. The index part works as it should when called by flask but the "set" link (to go to a page to enter a new time and date) results in a "404 Not found" error. I've stripped everything out of the Python program that isn't part of flask and displaying the html, and I still get the error. At this point, all that's left is about the same as simple examples I've found; I've compared my program to the examples and I just can't see what's going wrong. Can some sharp eyes spot my mistake?
furnace-9x.py
import time
import datetime
import RPi.GPIO as GPIO
import smbus
from gpiozero import Button
from flask import Flask, render_template
import SDL_DS3231_dev
import lcddriver
import threading
# Using a global here for simplicity in this demo
# Globals are generally bad practice, so you would maintain
# the status of "lcd_counter" in a more elegant way
# (e.g. through a shared module, through classes, etc.)
# A simple global serves the purpose for this demo
# of a flask server running in parallel to another process
# emulating some I/O, printing to the console, etc.
lcd_counter = 0
app = Flask(__name__)
#app.route("/")
def index():
print('Index')
global datetimestr, temperature
global minutes_24,minutes_30,heatOn
return render_template('index.html')
#app.route("/set")
def setTime():
print("Set time")
return render_template('set.html')
if __name__ == '__main__':
#=== Prelude ===
app.run(debug=False, host='0.0.0.0')
index.html
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
body {font-family:arial;
background-color: rgb(185,179,175);
text-align: left;}
table, tr {text-align: left;
font-size: 150%;
border-style: solid;}
</style>
</head>
<body>
<h1>Furnace</h1>
<table>
<tr>
<td colspan="3">{{dtstr}}</td>
</tr>
<tr>
<td>30 day: </td>
<td>{{m30}}</td>
<td>{{p30}}%</td>
</tr>
<tr>
<td>24 hour: </td>
<td>{{m24}}</td>
<td>{{p24}}%</td>
</tr>
<tr>
<td>Temp:</td>
<td> {{temper}}°F </td>
<td>Heat: {{heat}}</td>
</tr>
</table>
<p>Set time</p>
</body>
</html>
set.html
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
body {font-family:arial;
background-color: rgb(185,179,175);
text-align: left;}
table, tr {text-align: left;
font-size: 150%;
border-style: solid;}
</style>
</head>
<body>
<p>Enter the date and time:
<input type="text" name="datetime" size="15" maxlength="19" />
<input type="submit" value="Submit" />
</p>
</body>
</html>
The problem here is a mismatch between the link in index.html (set.html) and your route (/set).
To resolve the issue, you can update line 35 in your index.html like this:
<p>Set time</p>
The flask web server matches the route strings exactly, and not the file names of the templates. Even better, you can use the flask built in function url_for:
<p>Set time</p>

Locating an element using Selenium with Python

Edited based on the answers:
I am using Selenium with Python and trying to locate a button on an web application on Chrome. The block of code has an iframe as mentioned in the answer.
<iframe data-bind="attr: { src: src, foo: $root.registerTargetDisplayFrame($data, $element) }, event: {load: function() {loaded(true);}, focus: $root.blurredNavigationPane}" src="https://products.com/InfoShareAuthor/home">
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
<html>
<head>code here
<frameset id="IshTop" class="infoshareauthor" framespacing="0" border="0" bordercolor="#FFFFFF" frameborder="0" rows="31,25,*,0">
<frame id="MenuBar" scrolling="no" name="MenuBar" src="./MainMenuBar.asp">
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<body>
<div id="Top-Menu-Container">
<div id="top-menu-wrapper">
<div id="top-menu">
<form name="MainBar">
<script type="text/javascript" language="javascript">
<table cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td width="95" valign="bottom">
<td width="95" valign="bottom">
<div style="POSITION: relative;">
<div height="30" style="POSITION: absolute; z-index:0; top: 4px; margin-left: -5px">
<a href="javascript:TabSelect(1);">
<img border="0" src="./UIFramework/tab_active.png">
</a>
</div>
<div onclick="javascript:TabSelect(1);" style="POSITION: absolute; z-index:2; top: -8px">
<table cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td id="MenuButton1" class="tab_active" width="95" valign="bottom" height="30" align="center" style="cursor:pointer;padding-bottom:2px;" name="Repository">Repository</td>
</tr>
</tbody>
</table>
</div>
</div>
</td>
<td width="95" valign="bottom">
<td width="95" valign="bottom">
<td width="95" valign="bottom">
<td width="95" valign="bottom">
</tr>
</tbody>
</table>
</form>
</div>
<div id="top-help">
<div id="top-nav-links">
</div>
</div>
</body>
</html>
</frame>
<frame id="BreadCrumbs" frameborder="0" border="0" scrolling="no" name="BreadCrumbs" src="./BreadCrumbs.asp">
<frameset id="Application" bordercolor="#0099CC" frameborder="0" rows="0,*,0,0,0,0">
<frameset id="HiddenFrameSet" bordercolor="#0099CC" frameborder="0" rows="0,0,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1">
<noframes> It looks like your browser doesn't support frames. This page requires frames in order to function. <br><br>For more information, please <a href='http://www.trisoftcms.com/en/contact-us.html' target=_blank style='white-space:nowrap'>contact us</a>. </noframes>
</frameset>
</html>
</iframe>
I switched frames using this:
iframe = browser.find_element_by_xpath("//iframe[#src='https://products.com/InfoShareAuthor/home']")
browser.switch_to.frame(iframe)
The code that I wrote:
browser.find_element_by_xpath("//td[#id='MenuButton1'][#name='Repository'][contains(text(),'Repository')]")
I could find the element using this xpath when I did a Firebug search
I also tried:
browser.find_element_by_id("MenuButton1")
and
browser.find_element_by_name("Repository")
Note: When I click the button, the URL does not change. Just a list of items in the application expands. Also, IDs and the Names are unique for the seven five menu buttons. None of the menu buttons work.
Does any one have any idea about what might be wrong? I am very new to Python and Selenium.
This doesn't exactly answer your question, but it does address what you're trying to do: it is likely you can accomplish the same task (and many others) with SDL's API client ISHRemote.
https://github.com/sdl/ISHRemote
For example, if you're looking for all the directories under '\General':
Import-Module ISHRemote
# first authenticate
$session = New-IshSession -IshPassword $password -IshUserName $username -WsBaseUrl 'https://ccms.example.com/InfoShareWS/'
# get a list of all the child folders under General
Get-IshFolder -IshSession $session -FolderPath '\General' -Recurse -Depth 2
Or if you're trying to get a list of files in a particular directory:
Import-Module ISHRemote
# first authenticate
$session = New-IshSession -IshPassword $password -IshUserName $username -WsBaseUrl 'https://ccms.example.com/InfoShareWS/'
# get all content in this folder
Get-IshFolderContent -IshSession $session -FolderPath 'General\path\to\topics'
With ISHRemote, you can also find and update publications, move content, modify metadata, etc.
Hope that helps.
you can try load iframe url. It avoids issues with selenium waiting from the iframe to load

get_template_attribute not displaying data in html

i came across get_template_attribute in the flask api and thought of it as a great way to achieve partial page rendering.
so the goal was
create a 2 layer menu-page layout
menu should be static
on click of
menu, page should be shown dynamically without refreshing the entire
page
so i did something like so
LandingPage.html
<html>
<head>
<style>
html,body{
height:100%;
}
</style>
<script type="text/javascript">
function finalConfirmationToStartJob() {
alert('in here')
$.ajax({
type : 'GET',
url : 'home2',
data : ''
});
};
</script></head>
<body>
<table border=1 height=100% width=100%>
<tr><td colspan='2'/></tr>
<tr>
<td width=15%>
<ul>
<li>ABC</li>
<ul>
<li>ABC1</li>
<li>ABC2</li>
<li>ABC3</li>
</ul>
<li>DEF</li>
<li>GHI</li>
<li>JKL</li>
</ul>
</td>
<td>
{% macro hello(name) %}Hello {{ name }}!{% endmacro %}
how are you darlings!!! {{hello()}}
</td>
</tr>
<tr><td colspan='2'/></tr>
</table>
</body>
</html>
Controller
#app.route("/home")
def goto_home():
return render_template('/LandingPage.html')
#app.route("/home2")
def try_out_macro():
print 'hellooo elloo'
hello = get_template_attribute('/LandingPage.html', 'hello')
return hello('World')
However when i do click on the action link, even though the controller actually returns perfect data , the data is not displayed on the screen.
When i hit the url /home2 i do get perfect data, however on click on action link nothing happens on the html
Can someone please help me, i am not sure what i am doing wrong
PS : i got it to work using innr html and div combination, however i would still like to know why the above doesnt work.

Selenium Python : find element whose href attribute has required keyword

The page I'm working on is in this link.
This is the relevant portion of that page:
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>...</head>
<body>
...
<div id="searchResults">
<div class="box-related">...</div>
<img src="/ema/images/icon_download_spread.gif" />Download results to spreadsheet
<div class="table-holder">
<table class="table-epar eparResults" border="1" cellpadding="0" cellspacing="0" summary="Search results for EPARs ordered alphabetically">
<caption>EPAR Search results</caption>
<thead> ... </thead>
<tbody>
<tr>
<th scope="row" class="key-detail name word-wrap">
Abilify
</th>
...
</tr>
<tr>...</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>
This is the XPath location of the element I wish to select:
//*[#id="searchResults"]/div[2]/table/tbody/tr[1]/th/a
But there may be many results on the searchpage, so I want to click on the link whose URL has the product number that I'm searching for (which is 000471 in this case). I want to select the <a> element which contains that string in the href attribute.
Here's what I've tried:
inp = driver.find_element_by_xpath("//*[#id='searchResults']/div[2]/table/tbody/tr[1]/th/a[contains(#href,'"+str3+"')]")
inp.click()
where str3 has the value 000471 in this case. But I keep getting NoSuchElementException.
Any help would be appreciated!
The problem is probably cause by elements which are inserted into the source code viewer or inspector when rebuilding the table. The tbody tag is usually inserted in the code when it doesn't actually exist in the real source.
You can eliminate the unnecessary steps in your XPath, if you can still obtain a unique location path to the data you wish to select. This might be sufficient:
//*[#id='searchResults']//a[contains(#href,'000471')]
If the other steps are still necessary, you can try it without the tbody.
Update I also noticed that your search page declares a namespace:
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
...
Automatic registration of default namespaces is implementation dependent. XPath requires all selectors to be qualified with a namespace. If your selenium implementation doesn't do that, you need to either register a namespace/prefix mapping, and prefix all elements in the namespace (ex: //h:table/h:tr/h:td) or ignore the namespace, using wildcards and comparing the local name in a predicate.
If the namespace is keeping you from selecting the node, you can ignore it with this expression:
//*[#id='searchResults']//*[local-name() = 'a'][contains(#href,'000471')]

wkhtmltopdf adds extra spacing when multiple width, height : 100% elements

I'm using wkhtmltopdf via subprocess to generate PDFs in a python flask web app. It works great for most html input I give, but applies some very weird formatting in certain cases. For some cases, I need to append the html block together 5 times. In that case, the PDFs generated contain multiple blank pages in between content (which I would rather not have).
The format of the HTML I provide is the following:
<body>
<center>
<table height="100%" width="100%" id="backgroundTable" style="
height: 100%;
width: 100%;">
<!-- More content -->
</table>
</center>
</body>
I have found that the culprits are the height="100%" width="100%" and style="height: 100%; width: 100%;" calls for the <table> element.
Does anyone know why that would cause the issue?

Categories

Resources