Django PDF: How to display progress in browser? - python

The file will not display until the whole file is loaded. How can i display the progress in browser?
from io import BytesIO
from reportlab.pdfgen import canvas
from django.http import HttpResponse
def some_view(request):
# Create the HttpResponse object with the appropriate PDF headers.
response = HttpResponse(mimetype='application/pdf')
response['Content-Disposition'] = 'attachment; filename="somefilename.pdf"'
buffer = BytesIO()
# Create the PDF object, using the BytesIO object as its "file."
p = canvas.Canvas(buffer)
p.drawString(**, **, "Hello world.") # draw pdf, size > 10M
# Close the PDF object cleanly.
p.showPage()
p.save()
# Get the value of the BytesIO buffer and write it to the response.
pdf = buffer.getvalue()
buffer.close()
response.write(pdf)
return response

Well, in your example I think it would be pretty simple (and almost instantaneous). You would just print out/return a progress indicator for drawString, showPage, etc. Assuming the actual PDF you're generating is more involved, you could use the setProgressCallBack method of the BaseDocTemplate class. Of course, this would require that you use the reportlab platypus engine.
There are a few methods/properties of note (see the comments), let's say you have a custom template class which overrides BaseDocTemplate:
from reportlab.platypus import BaseDocTemplate
class MyDocTemplate(BaseDocTemplate):
"""Override BaseDocTemplate for "progress bar" information"""
def __init__(self, *args, **kwargs):
BaseDocTemplate.__init__(self, *args, **kwargs)
# BaseDocTemplate keeps a "progress" dictionary for its own
# internal use, which is updated as various drawings are done.
# This directs reportlab to use your own custom method
# as the "callback" function when updates are made.
# Notice the use of the __ prefix for the method, which ensures
# that it calls *your* custom class's method, and not the default.
# Should match the signature of the original callback: (self, type, value)
self.setProgressCallBack(self.__onProgress)
def __onProgress(self, prog_type, value):
"""Progress monitoring"""
# One example "progress type" is the "PAGE" key. Which indicates
# which page reportlab is working on.
if prog_type == "PAGE":
print "Drawing page: %s" % value
elif prog_type == "FINISHED":
print "Drawing complete!"
Here are some more values (you can see the code in reportlab.platypus.doctemplate):
def progressCB(typ, value):
"""Example prototype for progress monitoring.
This aims to provide info about what is going on
during a big job. It should enable, for example, a reasonably
smooth progress bar to be drawn. We design the argument
signature to be predictable and conducive to programming in
other (type safe) languages. If set, this will be called
repeatedly with pairs of values. The first is a string
indicating the type of call; the second is a numeric value.
typ 'STARTING', value = 0
typ 'SIZE_EST', value = numeric estimate of job size
typ 'PASS', value = number of this rendering pass
typ 'PROGRESS', value = number between 0 and SIZE_EST
typ 'PAGE', value = page number of page
type 'FINISHED', value = 0
The sequence is
STARTING - always called once
SIZE_EST - always called once
PROGRESS - called often
PAGE - called often when page is emitted
FINISHED - called when really, really finished
some juggling is needed to accurately estimate numbers of
pages in pageDrawing mode.
NOTE: the SIZE_EST is a guess. It is possible that the
PROGRESS value may slightly exceed it, or may even step
back a little on rare occasions. The only way to be
really accurate would be to do two passes, and I don't
want to take that performance hit.
"""
print 'PROGRESS MONITOR: %-10s %d' % (typ, value)

Related

Python Click: make option depend on previous option

Is there an idiomatic way, using the Python Click library, to create a command where one option depends on a value set by a previous option?
A concrete example (my use case) would be that a command takes an option of type click.File as input, but also an encoding option which specifies the encoding of the input stream:
import click
#click.command()
#click.option("--encoding", type=str, default="utf-8")
#click.option("--input",
type=click.File("r", encoding="CAN I SET THIS DYNAMICALLY BASED ON --encoding?"))
def cli(encoding, input):
pass
I guess it would have to involve some kind of deferred evaluation using a callable, but I'm not sure if it's even possible given the current Click API.
I've figured out I can do something along the following lines:
import click
#click.command()
#click.pass_context
#click.option("--encoding", type=str, default="utf-8")
#click.option("--input", type=str, default="-")
def cli(ctx, encoding, input):
input = click.File("r", encoding=encoding)(input, ctx=ctx)
But it somehow feels less readable / maintainable to decouple the option decorator from the semantically correct type constraint that applies to it, and put str in there instead as a dummy. So if there's a way to keep these two together, please enlighten me.
A proposed workaround:
I guess I could use the click.File type twice, making it lazy in the decorator so that the file isn't actually left opened, the first time around:
#click.option("--input", type=click.File("r", lazy=True), default="-")
This feels semantically more satisfying, but also redundant.
It is possible to inherit from the click.File class and override the .convert() method to allow it to gather the encoding value from the context.
Using a Custom Class
It should look something like:
#click.command()
#click.option("--my_encoding", type=str, default="utf-8")
#click.option("--in_file", type=CustomFile("r", encoding_option_name="my_encoding"))
def cli(my_encoding, in_file):
....
CustomFile should allow the user to specify whichever name they want for the parameter from which the encoding value should be collected, but there can be a reasonable default such as "encoding".
Custom File Class
This CustomFile class can be used in association with an encoding option:
import click
class CustomFile(click.File):
"""
A custom `click.File` class which will set its encoding to
a parameter.
:param encoding_option_name: The 'name' of the encoding parameter
"""
def __init__(self, *args, encoding_option_name="encoding", **kwargs):
# enforce a lazy file, so that opening the file is deferred until after
# all of the command line parameters have been processed (--encoding
# might be specified after --in_file)
kwargs['lazy'] = True
# Python 3 can use just super()
super(CustomFile, self).__init__(*args, **kwargs)
self.lazy_file = None
self.encoding_option_name = encoding_option_name
def convert(self, value, param, ctx):
"""During convert, get the encoding from the context."""
if self.encoding_option_name not in ctx.params:
# if the encoding option has not been processed yet, wrap its
# convert hook so that it also retroactively modifies the encoding
# attribute on self and self.lazy_file
encoding_opt = [
c for c in ctx.command.params
if self.encoding_option_name == c.human_readable_name]
assert encoding_opt, \
"option '{}' not found for encoded_file".format(
self.encoding_option_name)
encoding_type = encoding_opt[0].type
encoding_convert = encoding_type.convert
def encoding_convert_hook(*convert_args):
encoding_type.convert = encoding_convert
self.encoding = encoding_type.convert(*convert_args)
self.lazy_file.encoding = self.encoding
return self.encoding
encoding_type.convert = encoding_convert_hook
else:
# if it has already been processed, just use the value
self.encoding = ctx.params[self.encoding_option_name]
# Python 3 can use just super()
self.lazy_file = super(CustomFile, self).convert(value, param, ctx)
return self.lazy_file

Why is a website's response in python's `urllib.request` different to a request sent directly from a web-browser?

I have a program that takes a URL and gets a response from the server using urllib.request. It all works fine, but I tested it a little more and realised that when I put in a URL such as http://google.com into my browser, I got a different page (which had a doodle and a science fair promotion etc.) but with my program it was just plain Google with nothing special on it.
It is probably due to redirection, but if the request from my program goes through the same router and DNS, surely the output should be exactly the same?
Here is the code:
"""
This is a simple browsing widget that handles user requests, with the
added condition that all proxy settings are ignored. It outputs in the
default web browser.
"""
# This imports some necessary libraries.
import tkinter as tk
import webbrowser
from tempfile import NamedTemporaryFile
import urllib.request
def parse(data):
"""
Removes junk from the data so it can be easily processed.
:rtype : list
:param data: A long string of compressed HTML.
"""
data = data.decode(encoding='UTF-8') # This makes data workable.
lines = data.splitlines() # This clarifies the lines for writing.
return lines
class Browser(object):
"""This creates an object for getting a direct server response."""
def __init__(self, master):
"""
Sets up a direct browsing session and a GUI to manipulate it.
:param master: Any Tk() window in which the GUI is displayable.
"""
# This creates a frame within which widgets can be stored.
frame = tk.Frame(master)
frame.pack()
# Here we create a handler that ignores proxies.
proxy_handler = urllib.request.ProxyHandler(proxies=None)
self.opener = urllib.request.build_opener(proxy_handler)
# This sets up components for the GUI.
tk.Label(frame, text='Full Path').grid(row=0)
self.url = tk.Entry(frame) # This takes the specified path.
self.url.grid(row=0, column=1)
tk.Button(frame, text='Go', command=self.browse).grid(row=0, column=2)
# This binds the return key to calling the method self.browse.
master.bind('<Return>', self.browse)
def navigate(self, query):
"""
Gets raw data from the queried server, ready to be processed.
:rtype : str
:param query: The request entered into 'self.url'.
"""
# This contacts the domain and parses it's response.
response = self.opener.open(query)
html = response.read()
return html
def browse(self, event=None):
"""
Wraps all functionality together for data reading and writing.
:param event: The argument from whatever calls the method.
"""
# This retrieves the input given by the user.
location = self.url.get()
print('\nUser inputted:', location)
# This attempts to access the server and gives any errors.
try:
raw_data = self.navigate(location)
except Exception as e:
print(e)
# This executes assuming there are no errors.
else:
clean_data = parse(raw_data)
# This creates and executes a temporary HTML file.
with NamedTemporaryFile(suffix='.html', delete=False) as cache:
cache.writelines(line.encode('UTF-8') for line in clean_data)
webbrowser.open_new_tab(cache.name)
print('Done.')
def main():
"""Using a main function means not doing everything globally."""
# This creates a window that is always in the foreground.
root = tk.Tk()
root.wm_attributes('-topmost', 1)
root.title('DirectQuery')
# This starts the program.
Browser(root)
root.mainloop()
# This allows for execution as well as for importing.
if __name__ == '__main__':
main()
Note: I don't know if it is something to do with the fact that it is instructed to ignore proxies? My computer doesn't have any proxy settings turned on by the way. Also, if there is a way that I can get the same response/output as a web browser such as chrome would, I would love to hear it.
In order to answer your general question you need to understand how the web site in question operates, so this isn't really a Python question. Web sites frequently detect the browser's "make and model" with special detection code, often (as indicated in the comment on your question) starting with the User-Agent: HTTP header.
It would therefor make sense for Google's home page not to include any JavaScript-based functionality if the User-Agent identifies itself as a program.

difference between Class().property and self.class.property

The following code is my original attempt at getting a gui app to update its font etc,, when the user changed it in the config.ini settings:
def on_config_change(self, config, section, key, value):
"""
sets font,size colour etc..
when user changes in settings.
"""
if config is self.config:
token = (section, key)
if token == ('Font', 'button_font'):
print('Our button font has been changed to', value)
GetInformation().lay_button.font_size = str(value)
GetInformation().bet_button.font_size = str(value)
def build(self):
self.config.write()
return GetInformation()
My code updated the config but the screen never updated without restarting the app.
The following code works:
def on_config_change(self, config, section, key, value):
"""
sets font,size colour etc..
when user changes in settings.
"""
if config is self.config:
token = (section, key)
if token == ('Font', 'button_font'):
print('Our button font has been changed to', value)
self.getInformation.lay_button.font_size = str(value)
self.getInformation.bet_button.font_size = str(value)
def build(self):
self.config.write()
self.getInformation = GetInformation()
return self.getInformation
What is the difference between calling GetInformation().lay_button.font_size
and self.getInformation.lay_button.font_size?
Unless you design your code specifically (typically a Singleton pattern), Class().method() creates a new object instantiated from Class and calls on it the method method. The object is then destroyed as there is no receiving variable specified.
self.object.method() on the other hand calls the method method on the existing object self.object. This object is persistent as it is saved as a member of your top level class (self).
In your first example, you are actually calling different methods on three different objects. In the method on_config_change, the two objects are immediately destroyed. In the second example, all the calls apply on the same object, that then keeps its modified properties.
Setting GetInformation().lay_button.font_size changes the lay_button's font size for a brand new GetInformation that you just created and didn't hook into anything.
Setting self.getInformation.lay_button.font_size changes the lay_button's font size for the current GetInformation that on_config_change fired for. It's that GetInformation that's hooked into your system so it's that GetInformation you need to do your work on.

How to format an integer widget on the controlpanel using plone.app.registry

I have an add-on configuration page/form generated by plone.app.registry.browser.controlpanel
Using these Docs:
http://plone.org/documentation/kb/how-to-create-a-plone-control-panel-with-plone.app.registry
https://pypi.python.org/pypi/plone.app.registry#control-panel-widget-settings
On this form, I have an integer field:
from zope import schema
from plone.app.registry.browser import controlpanel
class MyAddonSettings(Interface):
partnerId = schema.Int(title=u"Partner Id",
description=u"enter your Partner ID",
required=True,
default=54321)
class SettingsEditForm(controlpanel.RegistryEditForm):
schema = MyAddonSettings
label = u"My settings"
description = u""""""
def updateFields(self):
super(SettingsEditForm, self).updateFields()
def updateWidgets(self):
super(SettingsEditForm, self).updateWidgets()
class SettingsControlPanel(controlpanel.ControlPanelFormWrapper):
form = SettingsEditForm
When the form renders, I get the integer field auto-filled with '54,321' I don't want the comma.
How to I specify "Don't do that!"
So, I think I went pretty deep down the rabbit hole, but here is what I came up with.
1) The default widget for zope.schema.Int is the TextWidget
2) z3c.form.converter.IntegerDataConverter Adapts itself to zope.schema.interfaces.IInt and ITextWidget
3) the IntegerDataConverter calls upon the locale to 'format the integer' for you, giving you a nice pretty representation of an int - with commas.
My choice was to create a new widget 'IntWidget' and a new converter 'NoFormatIntegerDataConverter', adapt these. Then manually set the field in question to my new widget:
I'm sure there is a less 'rabbit hole' way to do this, but I found myself at the bottom, so I completed the journey. I'll let a zope guru follow up with the 'right' way to do it.
=========================
create the new widget based on TextWidget
so we don't tie our new converter to everyone's TextWidget and break someone else's stuff
import zope.interface
import zope.component
import zope.schema.interfaces
import z3c.form.interfaces
from z3c.form.widget import FieldWidget
from z3c.form.browser.text import TextWidget
from z3c.form import converter
class IIntWidget(z3c.form.interfaces.ITextWidget):
"""Int Widget"""
class IntWidget(TextWidget):
zope.interface.implementsOnly(IIntWidget)
klass = u'int-widget'
value = u''
#zope.component.adapter(zope.schema.interfaces.IField,
z3c.form.interfaces.IFormLayer)
#zope.interface.implementer(z3c.form.interfaces.IFieldWidget)
def IntFieldWidget(field, request):
"""IFieldWidget factory for IntWidget."""
return FieldWidget(field, IntWidget(request))
zope.component.provideAdapter(IntFieldWidget)
Create the 'dumb' converter, and adapt it to our new widget 'IntWidget'
class NoFormatIntegerDataConverter(converter.IntegerDataConverter):
""" data converter that ignores the formatter,
simply returns the unicode representation of the integer value
The base class for this calls upon the locale for a formatter.
This completely avoids calling the locale.
"""
zope.component.adapts(zope.schema.interfaces.IInt, IIntWidget)
def toWidgetValue(self, value):
if value is self.field.missing_value:
return u''
#go look at z3c.form.converter.IntegerDataConverter
#to see what it used to return here.
return unicode(value)
zope.component.provideAdapter(NoFormatIntegerDataConverter)
Finally, update the field widget factory to use our new widget
class SettingsEditForm(controlpanel.RegistryEditForm):
...
def updateFields(self):
super(SettingsEditForm, self).updateFields()
self.fields['partnerId'].widgetFactory = IntFieldWidget #<----- Here
...

Web.py pass parameter to GET/POST function via ajax call on form submit

I have a main page that has a GET and a POST function. The POST function gets data from a search screen and should pass this information, via an ajax call, to the worldMarkers class. This is separate because it will be needed for other aspects of the application.
The goal of this, is to have a user press submit on index and during the POST call, it is able to limit the results retrieved. This logic exists in the worldMarkers class.
class index(object):
def GET(self):
# do things to generate the page
return html
def POST(self):
continents = web.input(search_continents=[])
countries = web.input(search_countries=[])
searchDict = {}
if continents['search_continents']:
searchDict['continents'] = continents['search_continents']
if countries['search_countries']:
searchDict['countries'] = countries['search_countries']
markers = worldMarkers()
# Yes, this just spits out the results, nothing fancy right now
return markers.GET()
#alternatively,
#return markers.GET(searchDict)
class worldMarkers(object):
def __init__(self, **kargs):
self.searchDict = None
if 'searchDict' in kargs:
self.searchDict = kargs['searchDict']
def GET(self):
print "SearchDict: %s" % (self.searchDict)
# No searchDict data exists
The first option, with no parameters to markers.GET() means that none of my search criteria has been passed. If I do markers.GET(searchDict), I receive this error:
<type 'exceptions.TypeError'> at /
GET() takes exactly 1 argument (2 given)
How can I pass my search parameters to the worldMarkers class?
It looks like you should actually create an instance of worldMarkers as follows in order for your searchDict to exist:
markers = worldMarkers(searchDict=searchDict)
Right now, you're creating it without the argument:
markers = worldMarkers()
and in that case, the condition if 'searchDict' in kargs is false and self.searchDict = kargs['searchDict'] is not run.
And, as #TyrantWave points out, your GET is not really prepared to take any arguments since it is only declared as def GET(self). See the last sample code of this section of the docs.

Categories