Include a pdf file in urls in Django - python

I've a pdf file in one of my main project directory. How should I add this file in the main urls.py file so as to link this file in <a href> tag.
EDIT
I get 2 dates, start and end date, via AJAX. I process the data b/w those 2 dates, generate a report and then returns an HttpResponse. The PDF report is now saved in my main project directory. Now I get a response back in AJAX. So, now how should I process the response in the success function, sent back from the sever and open a PDF file.
Thanks.
jQuery
$(function() {
$("#report_submit").click(function(){
$.ajax({
type : "POST",
url: "/reports/",
data : { 'start_date' : $("#startDate").val() , 'end_date' : $("#endDate").val() },
success : function(result){
},
error : function(result){
}
});
});
});
Django view code
def generate_report(request):
ctx = {}
if request.is_ajax():
if request.POST.has_key('start_date'):
start_date = datetime.strptime(request.POST[ 'start_date'] , '%m/%d/%Y')
end_date = datetime.strptime(request.POST[ 'end_date'] , '%m/%d/%Y')
......
# PDF GENERATED in MAIN PROJECT DIRECTORY
with open(os.path.join(os.path.dirname(__file__),'../../../../gui','Report.pdf')) as pdf:
response = HttpResponse(pdf.read(), content_type='application/pdf')
response['Content-Disposition'] = 'inline;filename=Report.pdf'
return response # so, now when I send a response back, how should I process it in AJAX success function?
pdf.closed
return render(request, 'generate_report/reports.html', ctx)

You can do this in two ways depending upon the requirements of your app
Add the pdf file to the static directory and serve it from there. After all pdf file is really just a static resource.
However, in case you need to do some checks before serving the file to the user eg. allowing only the authenticated users to access it, then write a view that will do the necessary checking and then serve the contents of the file with appropriate response headers. See example here
Edit after the OP updated their question
Is ajax absolutely necessary here? In case it is I can think of following:
Since you mention a report is being generated, I will assume that non authenticated and no authorized users shouldn't be able to access the report.
One way is to store the generated pdf file at some location (outside the static dir) and also save a record of the name of the generated file and the id of the user who can access it in a database table. The request can then simply respond with the name of the file.
In the success callback of ajax, open the url of a view along with the filename as get param in a separate tab. This view will check if the user can access the file and serve it.
Again, all this complexity can be avoided if ajax is not a requirement

Related

Streaming & downloading Large CSV files using StreamingHttpResponse

So the issue I am facing is trying to get our webservers export to CSV button working, one requirement is the file cannot be saved locally and must be streamed straight to the client. At the moment I am just trying to get a basic CSV file streamed and saved on the local machine from the server before I worry about rendering actual data.
So, I have, on button click a post to my views where I wish to render then return a response. This was a $.ajax function but I have since changed it during testing and it now looks like this looks like:
$("#exportbutton").click(function(e){
$.post("{% url 'doner:download' %}", function(data)
{
console.log(data);
});
})
The code in the view looks like this (which happens to be a CTRL+V of an example from SO):
def download(request):
def stream():
buffer_ = io.StringIO()
writer = csv.writer(buffer_)
for row in rows:
writer.writerow(row)
buffer_.seek(0)
data = buffer_.read()
buffer_.seek(0)
buffer_.truncate()
yield data
response = StreamingHttpResponse(
stream(), content_type='text/csv'
)
disposition = "attachment; filename=file.csv"
response['Content-Disposition'] = disposition
return response
So after a lot of tinkering this is as far as I have got, it will 'stream'(?) the data back into the HTML page, the console.log will output the 'files' data however I do not get any form of a file or a download, just the log in the console of the data.
I don't know if I have missunderstood how StreamingHttpResponse works and I'm meant to do something more on the client side in order to download it past the stream but I've read the docs and I'm no more clear.
Any help would be greatly appreciated as I'm stumped, cheers guys!
The issue I was facing was posting using jQuery, apparently the browser is not able to download a file using AJAX, so in order to fix this part and get the file download, all I did was inside the AJAX (making sure it get generated alright before it's downloaded) add the line location.replace('doner/download'); and that was it...
Now the jQuery looks like this:
$("#exportbutton").click(function(e){
$.post("{% url 'doner:download' %}", function(data)
{
console.log(data);
location.replace("{% url 'doner:download' %}");
});
})
And everything works as needed!

How to upload files to slack using file.upload and requests

I've been searching a lot and I haven't found an answer to what I'm looking for.
I'm trying to upload a file from /tmp to slack using python requests but I keep getting {"ok":false,"error":"no_file_data"} returned.
file={'file':('/tmp/myfile.pdf', open('/tmp/myfile.pdf', 'rb'), 'pdf')}
payload={
"filename":"myfile.pdf",
"token":token,
"channels":['#random'],
"media":file
}
r=requests.post("https://slack.com/api/files.upload", params=payload)
Mostly trying to follow the advice posted here
Sending files through http requires a bit more extra work than sending other data. You have to set content type and fetch the file and all that, so you can't just include it in the payload parameter in requests.
You have to give your file information to the files parameter of the .post method so that it can add all the file transfer information to the request.
my_file = {
'file' : ('/tmp/myfile.pdf', open('/tmp/myfile.pdf', 'rb'), 'pdf')
}
payload={
"filename":"myfile.pdf",
"token":token,
"channels":['#random'],
}
r = requests.post("https://slack.com/api/files.upload", params=payload, files=my_file)
Writing this post, to potentially save you all the time I've wasted. I did try to create a new file and upload it to Slack, without actually creating a file (just having it's content). Because of various and not on point errors from the Slack API I wasted few hours to find out that in the end, I had good code from the beginning and simply missed a bot in the channel.
This code can be used also to open an existing file, get it's content, modify and upload it to Slack.
Code:
from io import StringIO # this library will allow us to
# get a csv content, without actually creating a file.
sio = StringIO()
df.to_csv(sio) # save dataframe to CSV
csv_content = sio.getvalue()
filename = 'some_data.csv'
token=os.environ.get("SLACK_BOT_TOKEN")
url = "https://slack.com/api/files.upload"
request_data = {
'channels': 'C123456', # somehow required if you want to share the file
# it will still be uploaded to the Slack servers and you will get the link back
'content': csv_content, # required
'filename': filename, # required
'filetype': 'csv', # helpful :)
'initial_comment': comment, # optional
'text': 'File uploaded', # optional
'title': filename, # optional
#'token': token, # Don't bother - it won't work. Send a header instead (example below).
}
headers = {
'Authorization': f"Bearer {token}",
}
response = requests.post(
url, data=request_data, headers=headers
)
OFFTOPIC - about the docs
I just had a worst experience (probably of this year) with Slack's file.upload documentation. I think that might be useful for you in the future.
Things that were not working in the docs:
token - it cannot be a param of the post request, it must be a header. This was said in one of github bug reports by actual Slack employee.
channel_not_found - I did provide an existing, correct channel ID and got this message. This is somehow OK, because of security reasons (obfuscation), but why there is this error message then: not_in_channel - Authenticated user is not in the channel. After adding bot to the channel everything worked.
Lack of examples for using content param (that's why I am sharing my code with you.
Different codding resulted with different errors regarding form data and no info in the docs helped to understand what might be wrong, what encoding is required in which upload types.
The main issue is they do not version their API, change it and do not update docs, so many statements in the docs are false/outdated.
Base on the Slack API file.upload documentation
What you need to have are:
Token : Authentication token bearing required scopes.
Channel ID : Channel to upload the file
File : File to upload
Here is the sample code. I am using WebClient method in #slack/web-api package to upload it in slack channel.
import { createReadStream } from 'fs';
import { WebClient } from '#slack/web-api';
const token = 'token'
const channelId = 'channelID'
const web = new WebClient(token);
const uploadFileToSlack = async () => {
await web.files.upload({
filename: 'fileName',
file: createReadStream('path/file'),
channels: channelId,
});
}

python class on html button click

I'm totally new to all this python things, I've searched on my problem but withiout results, so I count on your help guys :)
I have a template file with button:
<button class="btn" value="{{invoice.number}}">Send</button>
In different file I have a class which runs function from another file:
class ReminderManual(webapp2.RequestHandler):
...
for inv in invoices:
is_send = reminder.send(inv)
And I'd like to:
run this class when the button is pushed
display is_send value
Any ideas how I can do that?
If you don't want to reload the complete page to get the result, you could to the following using JQuery and Ajax:
In HTML, you have
<button class="btn" value="{{invoice.number}}" id="myButton">Send</button>
and later in the file (before the closing </body> tag):
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script type="text/javascript">
jQuery(document).ready(function() {
$('#myButton').on('click', function (e) {
var invoice_number = $('#myButton').val();
var data = { invoice_number:invoice_number };
var args = { dataType: "json", type:"POST", url:"/my_ajax_url/",
data:data, complete:ajax_call_complete };
$.ajax(args);
});
});
var ajax_call_complete = function(res, status) {
data = jQuery.parseJSON(res.responseText);
// use data as a json response object here and
// do something with the response data
}
</script>
In Javascript, the /my_ajax_url/ would be the URL on which your request handler on the server runs. It is called via Ajax, the parameters are provided as POST parameters (in the code example, the value of the button is send as parameter invoice_number).
I typically set up my server Ajax requests to return JSON, which is then handled by the callback method (in the example, ajax_call_complete). Here you can check for errors and indicating the result to the user by displaying a message, modifying certain HTML elements, etc.
If you want to submit the complete page instead of using Ajax, you should use the Django forms mechanism:
https://docs.djangoproject.com/en/1.6/topics/forms/
The button needs an onclick handler, so it knows what to do. The onclick handler is a javascript function that sends a new request via a url. So, your app needs to handle that url, and execute the Class when a request comes to the url.
With jquery, you could easily make the onclick handler process an Ajax request to the url, and respond back with the value of is_send. Still need a url handler in the app, which calls the class.
I see you added django as a tag. With django, you could make the button part of a form, and use django's form handling to execute the class, and push back the value of is_send.

How to upload multipart/form-data with enabled xsrf throw AJAX in tornadoweb

I'm trying to upload file by means POST HTTP request with enabled XSRF protection as AJAX request, but I get each time:
WARNING:root:403 POST /path/to/uploader/ (127.0.0.1): '_xsrf' argument missing from POST
I checked data of request:
_xsrf=01f86a98fe2346f9baec589dc8af3027&id=2
As I can see I send _xsrf to handler, but It can't find this argument.
If I disable multipart/form-data as contentTypem, bug will be gone and self.request.files is empty too.
Does anybody know how to fix that?
It may help if you include the code you have for the form.
You'll need to include the output of this in your <form> tag (generated in tornado.web.RequestHandler.get()):
self.xsrf_form_html()
If the input field is named upload_file, you can do this in the handler which handles the POST:
file = self.request.files['upload_file'][0]
uploaded_content_type = file['content_type']
uploaded_filename = file['filename']
local_file_path = '/some/path/on/server/uploaded.file'
output_file = open(local_file_path, 'w')
output_file.write(file['body'])
output_file.close()

How do I post a lot of data to Django?

I am trying to send of list of files to my Django Website. Each set is transmitted with the following info:
File name,
File size,
File location,
File type
Now, suppose I have 100 such sets of data, and I want to send it to my Django Website, what is the best method I Should use?
PS: I was thinking of using JSON, and then POST that JSON data to my Django URL. The data might then look like this:
{
"files": [
{ "filename":"Movie1" , "filesize":"702", "filelocation":"C:/", "filetype":"avi" },
{ "filename":"Movie2" , "filesize":"800", "filelocation":"C:/", "filetype":"avi" },
{ "filename":"Movie3" , "filesize":"900", "filelocation":"C:/", "filetype":"avi" }
]
}
I think sending json data to your server makes sense. Now to actually implement it, you would need your server to accept the http POST request that you will use to send the data about the files.
So, the server code may look like this:
urls.py:
import myapp
# ...
urlpatterns = patterns('', url(r'^json/$',myapp.serve_json), #http://<site_url>/json/ will accept your post requests, myapp is the app containing view functions
#add other urls
)
#other code
views.py
import json
def serve_json(request):
if request.method == 'POST':
if 'files' in request.POST:
file_list = json.loads(request.POST['files'])
for file in file_list:
#do something with each file dictionary in file_list
#...
return HttpResponse("Sample message") #You may return a message
raise Http404
Now in your Desktop application, once you have the list of the dictionaries of files, you may do this:
import urllib,json
data = urllib.urlencode({'files':json.dumps(file_dict)}) #file_dict has the list of stats about the files
response = urllib.urlopen('http://example.com/json/', data)
print response.read()
You may also look into urllib2 and httplib and use them in place of urllib.

Categories