INSERT INTO using Postgres on PYTHON DB-API - postgresql

I'm trying to learn to add backend to a simple web app using postgreSQL and Python DB-API.
When running the app, why do I get an error if the function get_posts() in forumdb. python uses c. execute ("SELECT * FROM posts ORDER BY time;) instead of SELECT content, time FROM posts ORDER BY time;)?
Secondly, can anyone explain why c.execute("INSERT INTO posts VALUES (content)") doesn't work and we have to use the ('%s') % content thing in the function add_post(content) in forumdb.py?
Below is forum.py
from flask import Flask, request, redirect, url_for
# Using a module called forumdb
from forumdb import get_posts, add_post
app = Flask(__name__)
# HTML template for the forum page
HTML_WRAP = '''\
<!DOCTYPE html>
<html>
<head>
<title>DB Forum</title>
<style>
h1, form { text-align: center; }
textarea { width: 400px; height: 100px; }
div.post { border: 1px solid #999;
padding: 10px 10px;
margin: 10px 20%%; }
hr.postbound { width: 50%%; }
em.date { color: #999 }
</style>
</head>
<body>
<h1>DB Forum</h1>
<form method=post>
<div><textarea id="content" name="content"></textarea></div>
<div><button id="go" type="submit">Post message</button></div>
</form>
<!-- post content will go here -->
%s
</body>
</html>
'''
# HTML template for an individual comment
POST = '''\
<div class=post><em class=date>%s</em><br>%s</div>
'''
#app.route('/', methods=['GET'])
def main():
'''Main page of the forum.'''
posts = "".join(POST % (date, text) for text, date in get_posts())
html = HTML_WRAP % posts
return html
#app.route('/', methods=['POST'])
def post():
'''New post submission.'''
message = request.form['content']
add_post(message)
return redirect(url_for('main'))
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)
Below is forumdb.py
# "Database code" for the DB Forum.
import psycopg2
import datetime
def get_posts():
con = psycopg2.connect(dbname="forum")
c = con.cursor()
"""Return all posts from the 'database', most recent first."""
c.execute("SELECT content, time FROM posts ORDER BY time;")
return c.fetchall()
con.close()
def add_post(content):
con = psycopg2.connect(dbname="forum")
c = con.cursor()
"""Add a post to the 'database' with the current timestamp."""
c.execute("INSERT INTO posts VALUES ('%s')" % content)
con.commit()
con.close()
Thank you!

Because you are using  parameterized query is a query in which placeholders are used for parameters and the parameter values are supplied at execution time.
When we want to use a variable inside a SQL query you need to use a placeholder for it.
Example
query = """Update table set column_a = %s where column_b = %s"""

Related

socketio emit not working from gpio callback

I am pulling my hair out trying to figure out what I am doing wrong.
Server shows emit is being sent, but the browser never gets it. After about a minute, the client disconnects and re-connects.
However, emit after on.connect works perfect. It's almost like the client has to send the server a request and immediately get a response to work.
For some reason, StackOverflow wants me to type more info because it looks like all code, but I'm not really sure what else I can add. I googled and tried different versions and tutorials to get this working, and I believe it should be good. It must be a typo somewhere.
Okay, that still isn't enough detail, so let's go through the entire process.
I have a Raspberry Pi 3 with an SD card, running Bullseye x64 that is up to date.
I am also using a logic level shifter to drive a few 5v transistors that turn on 4 different LEDs on 4 different buttons. The buttons are grounded when pressed, and using the Raspberry Pi's internal pull up resistor. There are also 2 relays that operate solenoid air valves, the relays are separated from the Raspberry Pi/ logic level with an optocoupler.
Essentially, I am trying to use flask-socket io to display a question on a tablet. The answer is then entered using 1 of the 4 colored buttons. If the answer is wrong then relays/transistors are activated to let air out. If the answer is correct then different relays/transistors are activated to trigger the compressor and build pressure again.
The LEDs, buttons presses, and compressors all work. I just can't seem to get socketio to receive emit messages outside of an on event.
As a workaround, I may just have Javascript poll for button status every second, until I can figure out what I'm doing wrong. At least I can get that part to work.
Sorry for the long intro. Maybe it helps, to me it seems like a lot of boring text to read through while I'm sure someone with more knowledge only needed to look at my code to see what was wrong.
Nope, still not enough text.... so let's see. I'm new to Python, and not sure what else to put in here. Actually, I'll write more boring stuff at the bottom so you don't actually have to read any of this.
Raspberry Pi3 Bullseye x64
Dependancies
bidict==0.22.0
cachelib==0.9.0
click==8.1.3
Flask==2.2.1
Flask-Login==0.6.2
Flask-Session==0.4.0
Flask-SocketIO
importlib-metadata==4.12.0
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.1
python-engineio
python-socketio
six==1.11.0
Werkzeug==2.2.1
zipp==3.8.1
RPi.GPIO==0.7.1
Python code:
from flask import Flask, render_template, request
from flask_socketio import SocketIO, emit
from random import random
from threading import Lock
from datetime import datetime
import RPi.GPIO as GPIO
import time
"""
define gpio class
this holds all info for pins
"""
class rio:
def __init__(self, label, pin, state, btnPin=None):
self.label = label
self.pin = pin
self.state = state
self.btnPin = btnPin
self.checking = False
self.timeLast = time.time()
GPIO.setup(self.pin, GPIO.OUT)
if (btnPin!=None):
GPIO.setup(btnPin,GPIO.IN,pull_up_down=GPIO.PUD_UP)
GPIO.add_event_detect(self.btnPin, GPIO.FALLING, callback=self.stateChange)
GPIO.output(self.pin, self.state)
def stateChange(self,channel):
global count
duration = time.time() - self.timeLast
self.timeLast = time.time()
print(self.label + " was pressed")
count+=1
print("Sending counter event...")
socketio.emit('updateSensorData', {'value': count, "date": get_current_datetime()})
"""
Define Globals
"""
count = 0
"""
label and define what gpio
we want to use
"""
GPIO.setmode(GPIO.BCM) #enable GPIO BCM Module
yellow = rio("yellow", 22, GPIO.LOW, 14)
green = rio("green", 10, GPIO.LOW, 16)
blue = rio("blue", 9, GPIO.LOW, 20)
red = rio("red", 11, GPIO.LOW, 21)
compressor = rio("compressor", 13, GPIO.LOW)
LogicShift = rio("shifter", 26, GPIO.HIGH)
valve1 = rio("valve in", 27, GPIO.HIGH)
valve2 = rio("valve out", 17, GPIO.HIGH)
"""
create websocket
"""
app = Flask(__name__)
app.config['SECRET_KEY'] = 'donsky!'
socketio = SocketIO(app, cors_allowed_origins='*', logger=True)
"""
Get current date time
"""
def get_current_datetime():
now = datetime.now()
return now.strftime("%m/%d/%Y %H:%M:%S")
"""
Serve root index file
"""
#app.route('/')
def index():
return render_template('index.html')
"""
Decorator for connect
"""
#socketio.on('connect')
def connect():
global thread
print('Client connected')
emit('after connect', {'data':'Lets dance'})
"""
Decorator for disconnect
"""
#socketio.on('disconnect')
def disconnect():
print('Client disconnected', request.sid)
if __name__ == '__main__':
# Access app in all IP not just localhost
socketio.run(app, host="0.0.0.0")
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Synchronized</title>
<link rel="stylesheet" href='/static/style.css' />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css">
<script src="https://code.jquery.com/jquery-3.5.1.js"></script>
<!--script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.5.3/socket.io.js"></script-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/3.0.4/socket.io.js" integrity="sha512-aMGMvNYu8Ue4G+fHa359jcPb1u+ytAF+P2SCb+PxrjCdO3>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
// sending a connect request to the server.
var socket = io.connect('http://10.100.180.15:5000');
// An event handler for a change of value
$('#btn1').on('input', function(event) {
console.log('button changed');
socket.emit('Button value changed', {
who:$(this).attr('id'),
data: $(this).val()
});
return false;
});
socket.on('after connect', function(msg){
console.log('After connect', msg);
});
socket.on('update value', function(msg) {
console.log('Slider value updated');
$('#'+msg.who).val(msg.data);
});
socket.on('message', function(msg) {
console.log(msg);
});
socket.on('updateSensorData', function (msg) {
console.log('Received sensorData :: ' + msg.date + ' :: ' + msg.value);
$('#current-count').text(msg.value);
});
});
</script>
</head>
<body>
<h1>Data:</h1>
<div class="">
<h1>Demo</h1>
<form class="mt-5">
<div class="form-group">
<label for"formControlRange">Btn1</label>
<input type="checkbox" class"form-control-range sync" id="btn1" value="{{btn1}}">
</div>
</form>
</div>
<button class="btn-close btn btn-sm">×</button>
<div class="container">
<div class="hero">
<h1 class="title">Real-Time Conveyor Counter Using Raspberry Pi</h1>
<div class="counter">
<h1 id="current-count">0</h1>
</div>
</div>
</div>
</body>
</html>
I tried different dependencies, and different JavaScript versions of socketio, but all of them respond the same way. The browser is Chrome 107.0.5304.110. Safari responds the same way, as well.

TYPO3 11 content element Plain HTML with <style>

TYPO3 11.5.4
I want to add a Plain HTML content element to a page, e.g.:
<style type="text/css">
h1#testheader { font-weight: bold; }
</style>
<h1 id="testheader">
Header
</h1>
(just as a simplified example)
But rendered output is:
<style type="text/css"> h1#testheader { font-weight: bold; } </style>
Header
I thought that style is allowed because I set:
lib.parseFunc.allowTags = ...,style,...
lib.parseFunc_RTE.allowTags = ...,style,...
but it doesn't work.
Setting
lib.parseFunc.htmlSanitize = 0
helps but is there something better to allow <style> in Plain HTML content elements?
EDIT:
Looking into the DB entry the html tags are not encoded, so no "<" or ">" instead of "<" and ">", so it's not an issue of the RTE.
After some more research (hours of hours again invested) I come to the conclusion that the only way is to extend the HTMLSanitizer.
The following sources have been used for this solution:
https://punkt.de/de/blog/2021/htmlsanitizer-in-typo3.html
https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog/9.5.x/Important-94484-IntroduceHTMLSanitizer.html
https://gist.github.com/ohader/2239dab247e18d23e677fd1b816f4fd5
The complete solution to allow <style>,<script> and <iframe> is shown below.
Still I am not able to allow <font> with this approach!
It's implemented in a local site package (gpcf_theme) and composer is used.
Add <style>,<script> and <iframe> to
lib.parseFunc.allowTags = ... style, ..., iframe, script, ...
in the TypoScript part of the site package.
style seems to be standard and is already part of this list.
The path to the local site package is:
local_packages/gpcf_theme
with a symbolic link to it from:
public/typo3conf/ext/gpcf_theme -> ../../../local_packages/gpcf_theme
These are the important file changes:
local_packages/gpcf_theme/composer.json:
{
"name": "oheil/gpcf_theme",
...
"autoload": {
"psr-4": {
"Oheil\\GpcfTheme\\": "Classes/"
}
},
...
}
local_packages/gpcf_theme/ext_localconf.php:
<?php
defined('TYPO3_MODE') || die();
...
$GLOBALS['TYPO3_CONF_VARS']['SYS']['htmlSanitizer']['default'] = \Oheil\GpcfTheme\MyDefaultBuilder::class;
...
local_packages/gpcf_theme/Classes/MyDefaultBuilder.php
<?php
namespace Oheil\GpcfTheme;
use TYPO3\CMS\Core\Html\DefaultSanitizerBuilder;
use TYPO3\HtmlSanitizer\Behavior;
use TYPO3\HtmlSanitizer\Behavior\Attr;
use TYPO3\HtmlSanitizer\Behavior\Tag;
class MyDefaultBuilder extends \TYPO3\CMS\Core\Html\DefaultSanitizerBuilder
{
protected function createBehavior(): \TYPO3\HtmlSanitizer\Behavior
{
// https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog/9.5.x/Important-94484-IntroduceHTMLSanitizer.html
return parent::createBehavior()
->withName('common')
->withTags(
(new Tag(
'style',
Tag::ALLOW_CHILDREN + Behavior::ENCODE_INVALID_TAG
))->addAttrs(
(new Attr('type')),
...$this->globalAttrs
),
(new Tag(
'iframe',
Tag::ALLOW_CHILDREN
))->addAttrs(
...array_merge(
$this->globalAttrs,
[$this->srcAttr],
$this->createAttrs('scrolling', 'marginwidth', 'marginheight', 'frameborder', 'vspace', 'hspace', 'height', 'width')
)
),
(new Tag(
'script',
Tag::ALLOW_CHILDREN
))->addAttrs(
...array_merge(
$this->globalAttrs,
[$this->srcAttr]
)
),
// more tags...
);
}
}
After these changes of course you need to clear the TYPO3 cache and (not sure here) you need to do:
composer remove "oheil/gpcf_theme"
composer require "oheil/gpcf_theme"
The above is working now, but I am still happy for any expert to give some more insights in what is wrong or can be done better, perhaps easier?
And why does this not work for <font> ?
Example:
<font class="font713099">Some Text</font>

web.py markdown global name 'markdown' is not defined

Im trying to use markdown together with Templetor in web.py but I can't figure out what Im missing
Documentation is here http://webpy.org/docs/0.3/templetor#builtins
import markdown
t_globals = {
'datestr': web.datestr,
'markdown': markdown.markdown
}
render = web.template.render(globals=t_globals)
class Blog:
def GET(self, post_slug):
""" Render single post """
post = BlogPost.get(BlogPost.slug == post_slug)
render = web.template.render(base="layout")
return render.post({
"blogpost_title": post.title,
"blogpost_content": post.content,
"blogpost_teaser": post.teaser
})
here is how I try to use markdown inside the post.html template
$def with (values)
$var title: $values['blogpost_title']
<article class="post">
<div class="post-meta">
<h1 class="post-title">$values['blogpost_title']</h1>
</div>
<section class="post-content">
<a name="topofpage"></a>
$:markdown(values['blogpost_content'])
</section>
But Im getting this exception
type 'exceptions.NameError' at
/blog/he-ll-want-to-use-your-yacht-and-i-don-t-want-this-thing-smelling-like-fish/
global name 'markdown' is not defined
You're re-initializing render, once in global scope setting globals and once within Blog.GET setting base. Do it only once!

Show local HTML file with JavaScript in SWT Browser widget in RAP

For my RAP-project I need to show some charts. Because I haven't found any widget for this purpose, my plan was to use the browser widget, so I can use JavaScript-Plugins like Highcharts or Chartsjs. But I can't get it working. If I set an HTML-File in browser.setUrl, the browser widget don't show anything, not even simple HTML. The JavaScript-Console in Chrome says
Not allowed to load local resource
If I enter the HTML-Code with the setText method it shows the HTML, but JavaScript is not working, it don't load external JS-File like the jQuery-library.
Can't this be done this way? Or where is my failure? (Sorry for my bad englisch, I'm not native speaker.)
Here's the Java-Code I tried:
browser = new Browser(composite, SWT.NONE);
browser.setTouchEnabled(true);
browser.setBounds(10, 10, 358, 200);
browser.setUrl("D:\\STATS\\statistiken.html");
Or this:
File file = new File("D:\\STATS\\statistiken.html");
browser = new Browser(composite, SWT.NONE);
browser.setTouchEnabled(true);
browser.setBounds(10, 10, 358, 200);
browser.setUrl(file.toURI().toString());
I tried also some other things, there were not working to.
With HTML in setText-method (I tried external libraries and local libraries in same folder):
browser = new Browser(composite, SWT.NONE);
browser.setBounds(10, 10, 358, 200);
browser.setText(
"<html>" +
"<head>" +
"<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js\"></script>" +
"<script src=\"http://code.highcharts.com/highcharts.js\"></script>" +
"<script src=\"http://code.highcharts.com/modules/exporting.js\"></script>" +
"</head>" +
"<body>" +
"<p>Test</p>" +
"<div id=\"container\" style=\"min-width: 400px; height: 400px; margin: 0 auto\"></div>" +
"</body>" +
"</htm>");
Hope someone can help me with this problem.
Local links will not be resolved and external links will not be loaded(Cross Domain problem) in your case.
I could suggest you 2 Solutions.
Solution 1:
This is useful when you have very few resources(html, javascript, css) to render on Browser and no Hyperlinks(which when cliked will load a different page).
You can use Velocity. Read this to start using Velocity.
You can have all the static content in Velocity Template and inject Dynamic content into it at Runtime.
Here is the excerpt from one of my Projects.
init.vm
<html dir="ltr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style type="text/css">
.transcript {
background-color: #d2d2d2;
}
.messageBlock {
margin-left: 4px;
margin-bottom: -15px;
}
.message {
margin-left: 115px;
word-wrap: break-word;
white-space: -moz-pre-wrap;
_white-space: pre;
white-space: pre-wrap;
}
</style>
</head>
<script type="text/javascript">
function resizeChatWindow() { var divT = document.getElementById("divTranscript"); divT.style.height = (document.body.clientHeight - getTopAreaHeight()) + "px"; divT.style.width = (document.body.clientWidth) + "px"; divT.style.overflow = "auto"; divT.style.position = "absolute"; divT.style.left = "0px"; divT.style.top = getTopAreaHeight() + "px";}
function getTopAreaHeight() { var chatAlert = document.getElementById("chatAlert"); if (chatAlert) { return chatAlert.clientHeight; } return document.getElementById("divBody").clientHeight;}
isChat=false; window.onresize=resizeChatWindow;
</script>
<script type="text/javascript">
$scriptText
</script>
<script type="text/javascript">
function addChat(chatText){
$("#divTranscript").append(chatText);
$("#divTranscript").animate({ scrollTop: $("#divTranscript")[0].scrollHeight }, "slow");
}
</script>
<body onload="resizeChatWindow();">
<div id="divBody"></div>
<div id="divTranscript">$history</div>
</body>
</html>
VelocityUtils
private void init() throws Exception {
ve = new VelocityEngine();
Properties velocityProperties = new Properties();
velocityProperties.put("resource.loader", "class");
velocityProperties.put("class.resource.loader.description", "Velocity Classpath Resource Loader");
velocityProperties.put("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
ve.init(velocityProperties);
//ve.init();
}
public String getInitHtml(String history) throws ResourceNotFoundException, ParseErrorException, Exception {
/* now render the template into a StringWriter */
StringWriter writer = null;
/* next, get the Template */
Template t = ve.getTemplate("templates/init.vm","UTF-8");
/* create a context and add data */
VelocityContext context = new VelocityContext();
String script = IOUtils.toString(VelocityUtils.class.getResourceAsStream("script/jquery.min.js"), "UTF-8");
context.put("scriptText", script); //You can even have all the script content in init.vm rather than injecting it at runtime.
context.put("history", StringUtils.defaultIfBlank(history, StringPool.BLANK));
writer = new StringWriter();
t.merge(context, writer);
/* show the World */
String returnMe = writer.toString();
return returnMe;
}
set the returned String in Browser.setText()
Solution 2:
I explained it here.

Is it possible to create a 'view all' page in Tumblr?

I've tried creating pages using the 'standard layout' and 'custom layout' but neither allows the use of the {block:Posts} variable(s). I need to re-create essentially the archive page but with some custom css. Is there any way to accomplish this?
If I try $("#someDiv").load("/archive", "#content"); the whole page formatting gets screwed up. Is there a way to load just the <a> tags into a div on my custom page?
Or would it be possible to use the API entirely client side to accomplish this?
Any ideas on this would be appreciated.
I came up with two possible solutions if anyone else finds themselves stuck on this. I abandoned this first one before finalizing it, so it's a bit rough but a good start. It uses the API to load photos (that was all I needed) as you scroll down the page.
<script>
function getPhotos(offset){
$.ajax({
url: "http://api.tumblr.com/v2/blog/[tumblr].tumblr.com/posts?api_key=[key]&offset="+offset,
dataType: 'jsonp',
success: function(results){
loaded += 20;
total = results.response.blog.posts;
if(total > loaded){
results.response.posts.forEach(function(post){
post.photos.forEach(function(photo){
$("#photos ul").append("<li class='"+post.tags.join(" ")+"'><img src='"+photo.alt_sizes[0].url+"'></li>");
$("#photos").imagesLoaded(function(){
$("#photos").masonry({
itemSelector: 'li'
});
});
});
});
if($("#photos ul").height() < $(window).height()){
getPhotos(loaded);
}
}
}
});
}
loaded = 0;
getPhotos(loaded);
$(window).scroll(function() {
if($(window).scrollTop() + $(window).height() > $(document).height() - 100) {
getPhotos(loaded);
}
});
</script>
What I've ended up doing is just using an iframe with a custom stylesheet appended to the head.
html:
<head>
<script src="http://[remote location]/frame.js"></script>
</head>
<body>
<div id="photos">
<iframe name="frame1" id="frame1" src="http://[tumblr]/archive" frameBorder="0"></iframe>
</div>
</body>
frame.js:
$(function(){
function insertCSS(){
var frm = frames['frame1'].document;
var otherhead = frm.getElementsByTagName("head")[0];
if(otherhead.length != 0){
var link = frm.createElement("link");
link.setAttribute("rel", "stylesheet");
link.setAttribute("type", "text/css");
link.setAttribute("href", "http://[remote location]/frame.css");
otherhead.appendChild(link);
setTimeout(function(){$("#frame1").show();}, 200);
clearInterval(cssInsertion);
}
}
cssInsertion = setInterval(insertCSS, 500);
//setTimeout(insertCSS, 1000);
$(window).scroll(function() {
if($(window).scrollTop() + $(window).height() > $(document).height() - 100 && $("#frame1").height() < 50000) {
$("#frame1").css("height", "+=1000px");
}
});
});
frame.css (stylesheet appended into iframe)
body{
overflow: hidden;
}
#nav_archive{
display: none;
}
.heading{
display: block !important;
}
.old_archive #content{
margin: 0 auto 0;
}
style.css (stylesheet on page where iframe is located)
#frame1{
border: none;
width: 100%;
height: 3000px;
overflow: hidden;
display: none;
}