Image Thumbnail in Web2py: Unable to display the thumbnail - python

I have used the suggestion here http://www.web2pyslices.com/slice/show/1387/upload-image-and-make-a-thumbnail
to make a thumbnail of an image.
I have got the thumbnail but I am unable to display it.
The following are my functions:
db.py :
db.define_table('uploads', Field('dataset', 'reference dataset'),
Field('filename', represent = lambda x, row: "None" if x == None else [:45]),
Field('image', 'upload', uploadseparate=True, requires=IS_NOT_EMPTY() and IS_IMAGE(extensions=('jpeg', 'png','jpg','tif')) ),
Field('thumb', 'upload', uploadseparate=True, requires=IS_NOT_EMPTY() and IS_IMAGE(extensions=('jpeg', 'png', 'jpg', 'tif'))))
default.py :
def makeThumbnail(dbtable,ImageID,size=(150,150)):
try:
thisImage=db(dbtable.id==ImageID).select()[0]
import os, uuid
from PIL import Image
except: return
im=Image.open(request.folder + 'uploads/' + thisImage.image)
im.thumbnail(size,Image.ANTIALIAS)
thumbName='uploads.thumb.%s.jpg' % (uuid.uuid4())
im.save(request.folder + 'uploads/' + thumbName,'jpeg')
thisImage.update_record(thumb=thumbName)
return
def insertImage():
response.menu = [
(T('Home'),False,URL('default','experimenter')),
(T('Manage Data Set'),False,URL('default','MDS')),
(T('Manage Experiment'),False,URL('default','ME')),
(T('Manage Workflow Element'),False,URL('default','MWE'))]
dbtable = db.uploads
record = None
record = db(db.dataset.id == request.args[0],ignore_common_filters=True).select().first()
form = FORM(dbtable, INPUT(_name='up_files', _type='file',
_multiple=True, requires=IS_NOT_EMPTY()),INPUT(_type='submit'))
# The multiple param lets us choose multiple files.
if form.process().accepted:
#onvalidation checks the uploaded files to make sure they are only txt, config, or log.
makeThumbnail(dbtable,form.vars.id,(300,300))
response.flash = 'files uploaded'
files = request.vars['up_files']
if not isinstance(files, list):
#convert files to a list if they are not one already.
files = [files]
for file in files:
db.uploads.insert(dataset=record.id, filename=file.filename, image=db.uploads.image.store(file, file.filename))
#store is a FIELD method that let's you save a file to disk. you can choose the directory if you want using the 'path' param.
else:
response.flash = 'Choose the Files you would like to upload'
return dict(form=form, record=record)
And then the view:
{{extend 'layout.html'}}
<h4>Manage Image of dataset: {{=record.name}}</h4>
{{if images:}}
<div style="overflow: auto;" width="80%">
<table>
<tr> <th> Image </th> </tr>
{{
for image in images:
=TR(TD(image.filename), IMG(_src=URL('default', 'download', args=image.thumb)), A(str(T('View')),_href=URL("show", args=[image.id,rowId])), A(str(T('Delete')),_href=URL('deleteImage',args=image.id)))}}
{{pass}}
</table>
</div>
{{pass}}
Note: I am trying to display the the thumbnails for a each image in a list of images.(see the View).
I am not getting the thumbnail but rather small question marks in its place.
PS: i am unable to upload the image.
I want images in place of question mark. I am doing something wrong in insertImage() function and also in the view.
Thanks in Advance for the help!

First, you appear to be conflating FORM and SQLFORM. The former is for creating custom forms (not connected with any database tables), and the latter is for building a form based on a database table (and therefore automatically handling inserts). You cannot pass a DAL Table object to FORM as in your code -- that will simply serialize the Table object to its string name, which will be included in the HTML form DOM to no effect. Further, in this case, form.vars.id will simply be None (FORM does not generate record IDs, as it does not do any database inserts).
Also, rather than directly saving the file in makeThumbnail, a better option would be to save the image to a StringIO object and then pass that object to db.uploads.thumbnail.store() (as you do for storing the original image). In that case, the .store() method of the thumbnail field will handle the file naming and saving automatically.
from cStringIO import StringIO
tmp = StringIO()
im.save(tmp, 'jpeg')
tmp.seek(0)
thisImage.update_record(thumb=db.uploads.thumb.store(tmp, filename='thumbnail.jpg'))
For more details, see http://web2py.com/books/default/chapter/29/06/the-database-abstraction-layer.

Related

Displaying image from directory to html page using Flask Python

Here, I want to inster an image from the directory to html page. I know how to do it using the exact file name but now I'm trying to do it using a variable which refers to the image name.
This is my python file where I save the image name to the database. The picture is already saved in the directory before.
//code.py
#app.route('/add_picture', methods=['POST'])
def add_picture():
if 'user_id' in session:
session['logged_in'] = True
photo = request.form.get('photo')
with sqlite3.connect('memory.db') as conn:
cursor = conn.cursor()
name = session['name']
cursor.execute('''UPDATE users SET photo=? WHERE name=?''', (photo, name))
conn.commit()
return redirect("/settings")
else:
return redirect('/')
//code.html
<img src="{{ url_for('.static', filename='images/IMG_2165.JPG') }}" width="200" height="200">
//we inster the picture by using the image name and it works
<img src="{{ url_for('.static', filename='images/{{ photo }}') }}" width="200" height="200">
//displaying the picture using the variable which refers to the image name but it returns nothing...
1- The image is already saved before in the directory.
2- I saved the image name to the database with the variable "photo".
3- The variable "photo" is being returned from another function.
4- In html I want to display the image using the variable but not the image name "IMG_2165.JPG".
Shouldn't your function return the image's path as a variable (as in return , photo, redirect('/')) ?

Render dynamically changing images with same filenames in Flask

I have a flask view function as below:
#app.route('/myfunc', methods = ['POST', 'GET'])
def myfunc():
var = request.form["samplename"]
selected_ecg=ecg.loc[ecg['Patient ID'].isin([var])]
selected_ecg = selected_ecg.drop('Patient ID', 1)
arr = np.array(selected_ecg)
y = arr.T
x=np.array(range(1,189))
plot.plot(x,y)
#Remove the old file
os.remove("static\graph.png")
#Now save the new image file
plot.savefig("static\graph.png")
return render_template("outputs.html")
Outputs.html:
<html>
<head>
</head>
<body>
<h1>Output page</h1>
<img src="static/graph.png" />
</body>
</html>
I use the flask view function to display an image through the outputs.html file. The catch here is that the static image file that is served keeps changing every time based on user inputs. In other words, I keep overwriting the image file based on the inputs the user has selected.
But the problem is that the changing image file is not served. The old image file that was used for first time render is only displayed for every new input of the user.
I have already referred to old posts regarding serving dynamic content in flask. But none of them served useful.
thebjorn's solution is valid. I have found multiple posts on Stack Overflow which suggest identical solutions. To view them, search for how to not cache images on Google. link link2 link3
Below is my solution to your problem. This will delete graph file and create new one with plot.savefig on every GET request to /myfunc. I was not sure on which request you wanted this behavior.
#app.route('/myfunc', methods = ['POST', 'GET'])
def myfunc():
var = request.form["samplename"]
selected_ecg=ecg.loc[ecg['Patient ID'].isin([var])]
selected_ecg = selected_ecg.drop('Patient ID', 1)
arr = np.array(selected_ecg)
y = arr.T
x=np.array(range(1,189))
plot.plot(x,y)
new_graph_name = "graph" + str(time.time()) + ".png"
for filename in os.listdir('static/'):
if filename.startswith('graph_'): # not to remove other images
os.remove('static/' + filename)
plot.savefig('static/' + new_graph_name)
return render_template("outputs.html", graph=new_graph_name)
Outputs.html
<html>
<head>
</head>
<body>
<h1>Output page</h1>
<img src="{{ url_for('static', filename=graph) }}" />
</body>
</html>
You're running into a caching issue. Static resources, like images, are cached at every point in the chain between your server and the browser. This is a good thing. Most reasonable systems are set up to cache images for at least 1 year at the server (and that's if they're not cached in the browser).
To bust through this cache issue, you'll need to either (i) give the files new names, (ii) reconfigure Vary headers to indicate they shouldn't be cached, or (iii) add a uniqueness fragment -- e.g. instead of using static/graph.png, add a timestamp 'static/graph.png?v=' + (new Date()).valueOf() or a md5 hash.
update: Dinko has given you a fine answer (do read the links he provides). To add cache-busting on the server side, without creating new files, you can calculate an md5 checksum (disadvantage: you'll need to read the entire file):
from hashlib import md5
fname = 'static/graph.png'
with open(fname, 'rb') as fp:
checksum = md5.new(fp.read()).hexdigest()
fname += "?v" + checksum
or use the last-modified attribute (not always reliable):
from hashlib import md5
fname = 'static/graph.png'
modified_tstamp = str(int(os.stat(fname).st_mtime * 10**6))
fname += "?v" + checksum
both of these methods will serve a cached version as long as the file doesn't change.

File responses in Python Pyramid 'cancelling' each other out?

Here are three Pyramid view_callables which setup a page with data and two image files. Trouble is, only one of the images (file responses) is returned. It seems I can only return one of the images at a time. If I take one of the file response vc's away, the other images is returned. However, if I have both file respone vc's there, only the second image is returned. Is there some object in the first vc I'm overwriting with the second vc?
Is there a better way to return both images (files), even within the first # title vc? As it is now, even if it worked, I have to retrieve the same document from the database 3 times for the one template. Any advice or clues would be greatly appreciated.
# title
#view_config(
route_name='title',
renderer='templates/titles/title.jinja2')
def title(request):
title = Title().find_one({'_id':ObjectId(request.matchdict['_id'])})
result = dict(
user = request.user,
title = title)
return result
# view title image
#view_config(route_name="view_title_image")
def jpg(request):
fd = Title().find_one({'_id':ObjectId(request.matchdict['title_id'])}).TitleImage
response = Response(content_type='application/jpg')
response.app_iter = fd.File
print fd
return response
# view trailer thumbnail
#view_config(route_name="view_trailer_thumbnail")
def jpg(request):
fd = Title().find_one({'_id':ObjectId(request.matchdict['title_id'])}).TrailerThumbnail
response = Response(content_type='application/jpg')
response.app_iter = fd.File
print fd
return response
Here are the route configs from __init__:
# title
config.add_route('title', '/title/{_id}')
# view title image
config.add_route('view_title_image', '/view/title_image/{title_id}')
# view title image
config.add_route('view_trailer_thumbnail', '/view/trailer_thumbnail/{title_id}')
This is how its used in the Jinja2 template:
<img src="/view/title_image/{{ title._id }}">
<img src="/view/trailer_thumbnail/{{ title._id }}">
I think your problem is that both views have the function named jpg.
Although it's not a great idea to overwrite functions like that, I would have thought that this would be no problem at all for the view_config decorator. The only thing I can think of is that rather recording a reference to the function, view_config works out what the dotted path would be and records that.
Anyway, give the view functions different names and you should be fine.

How to convert one content type to another using Archetypes

I have a content type which which is a non-folderish content type, and I want to convert this content type into a folderish. Inside my content type there is a multifilefield. I read this link http://developer.plone.org/content/archetypes/converting-content-types.html about converting one content type to another. However, as I run it using browser view, the new content types were created, also the values of the old contents were copied except for the uploaded files handled by multifilefield, they are empty.
Here's my code inside browser view:
Updated:
def migrateaction(self):
items=self.context.listFolderContents(contentFilter={"portal_type": 'myoldcontent'})
for item in items:
id = "%s-new" % item.getId()
service = self.context.invokeFactory(
'mynewcontent',
id,
rp_category=item.getRp_category(),
familyname=item.getFamilyname(),
firstname=item.getFirstname(),
file=item.getField('file').getRaw(item))
return 'Successfully migrated.'
My field definition for multifilefield:
MultiFileField('file',
primary=True,
languageIndependent=True,
widget = MultiFileWidget(
label= "File Uploads",
show_content_type = False,)),
All field definitions are the same for both old and new content types.
Is there lacking in my code that caused the files not to be copied?

Performing text processing on flatpage content to include handling of custom tag

I'm using flatpages app in my project to manage some html content. That content will include images, so I've made a ContentImage model allowing user to upload images using admin panel.
The user should then be able to include those images in content of the flatpages. He can of course do that by manually typing image url into <img> tag, but that's not what I'm looking for.
To make including images more convenient, I'm thinking about something like this:
User edits an additional, let's say pre_content field of CustomFlatPage model (I'm using custom flatpage model already)
instead of defining <img> tags directly, he uses a custom tag, something like [img=...] where ... is name of the ContentImage instance
now the hardest part: before CustomFlatPage is saved, pre_content field is checked for all [img=...] occurences and they are processed like this:
ContentImage model is searched if there's image instance with given name and if so, [img=...] is replaced with proper <img> tag.
flatpage actual content is filled with processed pre_content and then flatpage is saved (pre_content is leaved unchanged, as edited by user)
The part that I can't cope with is text processing. Should I use regular expressions? Apparently they can be slow for large strings.
And how to organize logic? I assume it's rather algorithmic question, but I'm not familliar with text processing in Python enough, to do it myself.
Can somebody give me any clues?
I finally imlemented this using regular expressions. I decided, that spaces are not allowed inside the custom tag. Main text processing function looks like this:
import re
from django.utils.translation import ugettext as _
def process_image_tags(text, ImageModel):
'''image tag usage:
... some text ... [img=image_name:image_class(optional)] ... some text ...
'''
t1 = re.split(r'(\[img=[a-z0-9\-_\:]+\])', text)
t2 = []
for i in t1:
if i[:5] == '[img=':
attrs = i[5:-1].split(':')
name_attr = attrs[0] #name attribute
error = None
try:
image = ImageModel.objects.get(name=name_attr)
except ImageModel.DoesNotExist:
error = '<span class="image_tag_error">%s</span>' % _('Image with given name not found')
except ImageModel.MultipleObjectsReturned:
error = '<span class="image_tag_error">%s</span>' % _('More than one image found')
if not error:
p = ['<img']
p.append('src="%s"' % image.image.url)
if len(attrs) > 1:
p.append('class="%s"' % attrs[1]) #class attribute
if image.description:
p.append('title="%s"' % image.description)
p.append('alt="%s"' % image.name)
p.append('/>')
t2.append(' '.join(p))
else:
t2.append(error)
else:
t2.append(i)
return ''.join(t2)
Above function is then used in save method of CustomFlatPage model, like this:
def save(self, *args, **kwargs):
self.content = process_image_tags(self.pre_content, ContentImage)
super(CustomFlatPage, self).save(*args, **kwargs)
It seems to work, so I'll probably end up using that solution. Maybe I'll add some javascript for user to insert image tags by picking images from generated list of images, but even now it's better than manually typing urls, I think.

Categories

Resources