I have two templates (initial.html and index.html) containing (among others thing) a form. I use data from this form to generate a csv (data.csv) and then read it in the index.html thanks to d3 and parcoords.js. Each new submission generate a new data.csv
The thing is, on the first call, everything seems to work but the moment I try to submit the form with new values the visualization does not change (even if the file data.csv appears to change when I open it with TextEdit)
Moreover, the data (values in main.py ) I provide to the view does not change either.
I know it is not recommended to call the same view after a POST submission but even if I try to call a different view, both of the problems described below remains.
Relevant parts of code are as follows:
main.py
#app.route('/', methods=['POST','GET'])
def main():
global maxDepth
global numberOfRelated
if request.method=='POST':
url = request.form['url']
maxDepth = int(request.form['depth'])
numberOfRelated = int(request.form['numberOfRelated'])
values = crawling(url,maxDepth,numberOfRelated)
return render_template('index.html',var=values)
return render_template('initial.html')
the crawling function
def crawling(url,maxDepth,numberOfRelated):
start = time.time()
initialID = get_id(url)
videosId.append([0,initialID])
getAllID(1,initialID)
getVideoData(videosId)
if os.path.exists('static/csv/data.csv'):
os.remove('static/csv/data.csv')
with open('static/csv/data.csv','wb') as csvData:
dataWriter = csv.writer(csvData, delimiter='|', quotechar='|', quoting=csv.QUOTE_MINIMAL)
dataWriter.writerow(['Depth']+['ID']+['Title']+['Popularity']+['Occurency'])
for x in xrange(0,len(videosData)):
dataWriter.writerow([videosData[x].level]+[videosData[x].id]+[videosData[x].title.encode("utf-8")]+[videosData[x].popularite]+[videosData[x].occurence])
timeExecution = round(time.time() - start,1)
mostFrequent = mostFrequentVideo()
mostPopular = mostPopularVideo()
initialVideo = videosData[0]
return [initialVideo,mostPopular, mostFrequent, len(videosData), timeExecution]
The form
<nav class="navbar formSpace">
<div class="container">
<form class="form-inline" action="" method="POST">
<div class="service form-group">
<i class="fa fa-youtube fa-2x"></i>
</div>
<div class="form-group">
<input type="text" class="form-control" id="mainForm" placeholder="https://www.youtube.com/watch?v=2HIuN5lxMCI" name="url" />
</div>
<div class="form-group minorForm">
<input name='numberOfRelated' type="text" class="form-control" placeholder="Number of suggestion" />
</div>
<div class="form-group minorForm">
<input name='depth' type="text" class="form-control" placeholder="depth" />
</div>
<div class="form-group navbar-right">
<button class="btn btn-success minorForm generate" type="submit"> Generate</button>
</div>
</form>
</div>
</nav>
main.js
var pc0;
var blue_to_brown = d3.scale.linear()
.domain([0, 5])
.range(["red", "#3498db"])
.interpolate(d3.interpolateLab);
d3.csv('static/csv/data.csv?_='+ Math.random(), function(data) {
pc0 = d3.parcoords()("#example0")
.data(data)
.showControlPoints(false)
.hideAxis(["Title"])
.hideAxis(["ID"])
.composite("darker")
.width(860)
.color(function(d) { return blue_to_brown(d['Depth']);})
.render()
.alpha(0.35)
.brushMode("1D-axes")
.reorderable()
.interactive();
});
Flask serves static files that tell the browser to cache the file rather aggressively. The default is to tell the browser to cache the file for 12 hours.
You can bypass this cache by adding a random value to the URL;
d3.csv('static/csv/data.csv?_=' + Math.random(), function(data) {
Now the browser will see this as a new URL each time and not re-use the cached file.
Well, I finally found the solution
The trick advised by Martijn Pieters works perfectly fine, the problem was coming from the format of my CSV. (I was using '|' instead of ',' and D3 didn't like that)
Related
I have a from with three possible inputs to submit and send through a seperate script that then generates JSON data.
The problem is however while two inputs are actual inputs = one email and one being a nummer. The third one is not a really traditional input.
<form action="{ url_for('handle_data') }}" method="POST">
<div class="form-group">
<label for="Speryear">SPER jaar</label>
<input class="form-control" type="number" value="2" name="Speryear" min=0 max=10 />
</div>
<div class="form-group">
<div class="form-group">
<label for="inputEmail">Verzendings mail</label>
<input class="form-control" type="email" name="inputEmail" required />
</div>
</div>
<div class="form-group">
<div class="url-panel">
<p> <b>Url:</b></p>
<p id="api-url" name="api-url"></p>
</div>
</div>
<button id="search" type="submit" class="btn-primary">
Aanvraag indienen</button>
</form>
#app.route('/handle_data', methods=['POST'])
def handle_data():
sper_year = request.form["Speryear"]
email = request.form["inputEmail"]
url = request.form["api-url"]
Requested_data = GIPOD_converter.main(url, sper_year, email)
return Requested_data
The third input is actually a paragraph which is dynamically based on the values of a second form (the primary from) for the data requests. According to this post here:
Sending data from a html non-input to Flask
HTML forms only send along tagged values to the remote endpoint when a "submit" input is pressed.
I have tried to make this paragraph a data input but the thing is this will break the javascript I have for that specific id. Aka a the URL part that I want cannot be generated in the input field. So can my code get the paragraph from this?
Edits done as per answer.
I think you should end the app route with:
return Requested_data
Also, you do not define correctly to the url form, i.e.:
url = request.form["api-url"]
so far this is what I came up with
#html ask user to input information including an image
<div class="form-group">
<input autocomplete="off" autofocus class="form-control" name="name" placeholder="name" type="text">
</div>
<div class="form-group">
<input class="form-control" name="subject" placeholder="subject" type="text">
</div>
<div class="form-group">
<input class="form-control" name="experience" placeholder="experience" type="text">
</div>
<div class="form-group">
<input class="form-control" name="phone" placeholder="puone-number" type="number">
</div>
<div class="form-group">
<input type="file" name="pic" id="pic">
</div>
<button class="btn btn-primary" type="submit">Register</button>
</form>
flask
#app.route("/register", methods=["GET", "POST"])
def register():
"""Show teacher registering menu"""
if request.method == "GET":
return render_template("register.html")
else:
# get the user input
name = request.form.get("name")
sub = request.form.get("subject")
exp = request.form.get("experience")
phone = request.form.get("phone")
f = request.files['pic']
pic = f.save(secure_filename(f.filename))
if not name or not sub or not exp or not phone:
return "404"
# insert in the database
sql = "INSERT INTO teachers (name, sub, exp, phone, pic) VALUES (?, ?, ?, ?, ?)"
db.execute(sql, name, sub, exp, phone, pic)
# inform the user for the success of the process
return render_template("success.html")
showing the results on html
<div>
{% for i in query %}
<div class="media bg-primary text-white">
<img class="align-self-end mr-3" src={{ i['pic'] }} alt="Generic placeholder image">
<div class="media-body">
<h5 class="mt-0">Mr. {{ i['name'] }}</h5>
<ul class="list-group list-group-flush text-dark">
<li class="list-group-item">subject: {{ i['sub'] }},</li>
<li class="list-group-item">experience: {{ i['exp'] }},</li>
<li class="list-group-item">Contact number: {{ i['phone'] }}</li>
</ul>
</div>
</div>
<br>
{% endfor %}
</div>
but right now every time I try it I find the value of the image column in my sql table to be NULL.
How can I fix that
pic = f.save(secure_filename(f.filename))
The save method returns None, so here pic will be None.
I think you intended to write its filename to the database, so perhaps change this to:
pic = secure_filename(f.filename)
f.save(pic)
Now pic is the filename on your server, so you just need to reconstruct this wherever the file is viewed.
Of course be aware that this uses the filename of the file which was uploaded by the user. You may wish to avoid this, incase of duplicates or just for cleanliness. See this other answer I wrote regarding that.
EDIT: Regarding your template.
When it comes to loading the picture in the template, let's assume the filename came through as image.jpg, and you use your exisiting code:
<img src={{ i['pic'] }}>
You could view the source of the rendered page, and see:
<img src=image.jpg>
Two problems with this:
that attribute should have quotes (<img src="image.jpg">)
that's trying to load the file from whichever path is rendered in the browser, so if the URL was http://example.com/subdir/ it's looking for the image at http://example.com/subdir/image.jpg. This can also be verified in the Network tab of your browsers dev tools.
The solution, build the URL with flask's url_for function:
<img src="{{ url_for('static', filename=i['pic']) }}">
This, of course, assumes that you've saved the file to the static directory on your server. You may wish to ensure this in the python code:
import os
# ...
pic = secure_filename(f.filename)
f.save(os.path.join('static', pic))
The below code is in views.py .I want to take a variable from the user instead of the '360p' after user pastes the video link. Is there a way to show how to get the resolution from the user.I have to take input from the user.
The full code looks something like the below webpage
https://www.hexascholars.com/code-snippet/youtube-video-download-using-django-and-python/
def get_download(request):
if request.method == 'GET':
if(request.GET.get('url')):
url = request.GET['url']
try:
yt = YouTube(url)
title = yt.title
stream = yt.streams.filter(res='360p').first()
path = download_path()
stream.download(path)
message = "Download Complete!"
video = Video()```
If you want to do it the "plain vanilla" way, i.e. without using django Forms you can just add a grup of radio buttons or a select to the form in your html, e.g.
<form method="GET" action="/your/url/">
<div class="form-group">
<label>Enter Youtube URL</label>
<input type="url" class="form-control" required="required" name="link">
</div>
<div class="form-group">
<label>Resolution</label>
<fieldset>
<input type="radio" name="res" id="res1" value="360p">
<label for="res1">360p</label>
<input type="radio" name="res" id="res2" value="720p">
<label for="res1">720p</label>
</fieldset>
</div>
<button type="submit" class="save btn btn-success">Download</button>
</form>
In your view you can fetch the parameter the same way you are getting the url:
res = request.GET.get('res')
and then use "res" instead of your hard-coded string.
I was building a profile picture feature, it was working fine so I left it for a while, probably a week. I came back to it and ran the local server, but when I do there's a few lines that appear in the console. But do not exist on the source file.
Source file:
<script type='text/javascript'>
Dropzone.options.myDropzone = {
autoProcessQueue : false,
paramName: 'uploaded_image',
dictDefaultMessage: "Drag and drop files or click here to upload picture",
init: function() {
var submitButton = document.querySelector("#submitBtn")
myDropzone = this;
submitButton.addEventListener("click", function() {
myDropzone.processQueue();
});
// Automatically overwrites file so the user can only upload one
this.on("addedfile", function() {
document.getElementById('submitBtn').style.visibility = "visible";
});
this.on('addedfile', function(){
if (this.files[1]!=null){
this.removeFile(this.files[0]);
}
});
}
};
</script>
<!-- Modal -->
<div id="picModal" class="modal">
<!-- Modal content -->
<div class="modal-content">
<span class="close"></span>
<form action="{% url 'profile_test' %}" method='POST' enctype="multipart/form-data" class="dropzone" id="my-dropzone">{% csrf_token %}
<!-- submit button stays hidden by default, until user selects a picture -->
<button id='submitBtn' type='submit' class='pic-submit-button' style='visibility: hidden;'> Submit </button>
<input id='submit-all' type='file' name='uploaded_image'/>
{{form}}
</form>
</div>
</div>
Now the code I'm seeing when I run the server is only a few lines, and it's in the HTML that creates the modal:
<!-- Modal -->
<div id="picModal" class="modal" style="display: block;">
<!-- Modal content -->
<div class="modal-content">
<span class="close"></span>
<form action="/api/profile_test/" method="POST" enctype="multipart/form-data" class="dropzone dz-clickable" id="my-dropzone"><input type="hidden" name="csrfmiddlewaretoken" value="WDMihPq0zDhDQGaWxSFYyvxjtmxUxsBMpAzcDqVxDGUZj11O8wtqbCfCie1m81Tf">
<!-- submit button stays hidden by default, until user selects a picture -->
<button id="submitBtn" type="submit" class="pic-submit-button" style="visibility: hidden;"> Submit </button>
*****<input id="submit-all" type="file" name="uploaded_image">
<label for="id_user">User:</label><select name="user" id="id_user">
<option value="" selected="">---------</option>
<option value="2">Brian</option>
<option value="3">Charles</option>
</select>
<label for="id_img">Img:</label><input type="file" name="img" required="" id="id_img">
<div class="dz-default dz-message"><span>Drag and drop files or click here to upload picture</span></div></form>
</div>
</div>*****
The last chunk of code I put stars around is the code that is unknown to me. The Django project I cloned was using gulp, I talked to my friends and they said that it may have something to do with it, maybe it's doing something with Dropzone.js?. But why would it inject a random dropdown menu listing users in Django? I didn't use gulp myself because I just wanted to develop the feature, but that may have been a mistake.
Possibly the
{{form}}
is causing this issue.
I'm working on a simple UI to start and stop games by ID. The basic HTML I have written is as follows (game_id is populated by JS):
<div align="center" class="top">
<div align="left" class="game-id-input">
Game ID: <input type="text" name="game_id" id="game_id">
</div>
<div align="right" class="buttons">
<form action="{{ url_for('start_game', game_id=game_id) }}" method="get">
<input type="submit" name="start" value="Start game" class="btn btn-success"></input>
</form>
<form action="{{ url_for('end_game', game_id=game_id) }}" method="get">
<input type="submit" name="end" value="End game" class="btn btn-danger"></input>
</form>
</div>
</div>
which looks like
I also have Flask route functions defined for each of the forms:
#app.route("/start_game/<game_id>")
def start_game(game_id):
# ...
#app.route("/end_game/<game_id>")
def end_game(game_id):
# ...
In my forms, how can I make game_id correspond to the game_id from #game_id?
Currently when I submit start and end games, I get a File Not Found error because it's just appending the literal <game_id> to the route.
I'm new to web development. This should be trivial, but I don't know what to search for. Sorry in advance for such a simple question.
You are trying to generate a url based on user input, but user input isn't available when Jinja is rendering the template on the server side, it's only available on the client side. So if you wanted to post to URLs with the game id as a URL parameter, you would have to build that URL on the client side with JavaScript.
For what you're trying to do, that's not really necessary. You can get the submitted value of a named input with request.form['name']. Buttons are just like any other input, so you can name them to find out what action was taken.
#app.route('/manage_game', methods=['POST'])
def manage_game():
start = request.form['action'] == 'Start'
game_id = request.form['game_id']
if start:
start_game(game_id)
else:
stop_game(game_id)
return redirect(url_for('index'))
<form method="POST" action="{{ url_for('manage_game') }}">
<input type="text" name="game_id"/>
<input type="submit" name="action" value="Start"/>
<input type="submit" name="action" value="Stop"/>
</form>
Even that's more verbose than you need. Given that you'd know if a game was already in progress, just toggle the current status instead of picking an action. It would never make sense to start a game that's already started, only stop it.
I cannot comment, but I would like to correct davidism's code.
I believe that you need action within your form element with a value which corresponds to the function within the server python code for this to work. Minor, but an important correction. So it would be like this:
In your server.py:
#app.route('/manage_game', methods=['POST'])
def manage_game():
start = request.form['action'] == 'Start'
game_id = request.form['game_id']
if start:
start_game(game_id)
else:
stop_game(game_id)
return redirect(url_for('index'))
In your HTML:
<form method="POST" action=/manage_game>
<input type="text" name="game_id"/>
<input type="submit" name="action" value="Start"/>
<input type="submit" name="action" value="Stop"/>
</form>