I want to get a view count for every time a photo is viewed. This is my show method, not sure why this doesn't work, seems pretty straight forward. The branch is called every time, but the database is not bing update...
def show
if current_user.nil? || #photo.profile != current_user.profile
#photo.views += 1
#photo.save!
end
render layout: 'layouts/photo'
end
The problem here is that #profile is simply not specified.
You may want to do something like the following:
def show
#profile = Profile.find(params[:id])
if #profile && current_user.nil? || #photo.profile != current_user.profile
#profile.views += 1
#profile.save
end
render layout: 'layouts/photo'
end
Besides that it's probably best to use a gem for that matter. I suggest impressionist. It will handle the most important stuff for you and block bots. It also offers additional features you may want.
Related
I'm trying to create an invoice with the python reportlab library in a Django view.
But now my question:
How can I write/draw something at the bottom (that's important!) of the last page? (Which may also be the first one, depending on how long the table is.)
My idea was to just get the last page's canvas and draw the footer on it (I added a Spacer so I can be sure that there's enough space for it. This would work, but I can't get the last page's canvas.)
buffer = BytesIO()
doc = SimpleDocTemplate(buffer, rightMargin=10*mm, leftMargin=10*mm, topMargin=10*mm, bottomMargin=0*mm)
elements = [Spacer(1,75*mm),get_table(),Spacer(1,108*mm)]
# get_table() returns a Table object
doc.build(elements, onFirstPage=draw_header)
# draw_header draws the header on the canvas
draw_invoice(CANVAS) # here's my problem
buffer.seek(0)
return FileResponse(buffer, as_attachment=False, filename='invoice.pdf')
Is there a way to get the canvas of the last page after building the doc and modify it? Or is there an other way to solve my problem?
Here's a scratch of what I'm trying to do: (The table could also go over more than 2 pages, the footer just has to be on the bottom of the earliest possible page, but after the table.)
I had several problems with reportlab, yes, it is a great tool to generate pdf but most of the times I had to override functions and creating subclass from basedoctemplate.
What I would do is create a pagetemplate that include that info, then swith to it before to finish the document with a Pagebreak().
class MyDocTemplate(BaseDocTemplate):
def __init__(self, data, filename, **kw):
self.data = data
self.allowSplitting = 0
BaseDocTemplate.__init__(self, filename, **kw)
#Frame bottom, limited to your work area
frame_bottom = Frame(self.leftMargin, self.bottomMargin, self.width/2, self.height/2 - 20*mm,
page_height_landscape - 2 * self.bottomMargin,
id='landscape', showBoundary=0)
# Definicion de templates
template_last_page = PageTemplate('last_page', frames=frame_b,
onPage=lastpage)
After that you can add your template_last_page to your doc. Then after to reach the final of the document use the method nextpagetemplate("your template"), then Pagebreak().
If you donĀ“t use subclasses from the library is very limitated sometimes. I bought a reportlab book, Reportlab: Processing with Python, help me a lot with these kind of problems.
Regards.
I was able to find my own solution:
I created my own Flowable and added it with a TopPadder to the other Flowables. I had to put a Spacer between because otherways my Flowable was sometimes overlapping with the Table.
Say an application has many Products and searching capabilities.
1. How can I track page views on each product(a simple counter) and
2. how can I track search queries as well?
Most importantly, I need to be able to find/order products by number of page views.
The (simpler / best performance / least outside dependency) the better!
Why this question? The solutions I've seen so far are either out of date gems or don't work well with searching.
Here's a simple way to do it. In the model:
def increment(by = 1)
self.views ||= 0 #Prevents error if views can be nil
self.views += by
self.save
end
Then in the controller:
#model.increment
Faster if made into an asynchronous job which I still need to do.
To prevent users spamming refresh or something I made it a bit more complicated:
def show
product_itemcode = params[:itemcode]
#product = Product.find_by(itemcode: product_itemcode)
unless session[product_itemcode.to_s]
#product.increment
session[product_itemcode.to_s] = true
end
end
Any thoughts?
EDIT:
An increment method already exists:
http://apidock.com/rails/ActiveRecord/Persistence/increment
You still have to call object.save though
I'm trying to alter the order of the steps in wicked wizard based on the selections form a previous selection.
So currently I have all the steps:
class WWTestController < ApplicationController
include Wicked::Wizard
steps :first_page,:optional_page,:second_page
def show
#event_object = EventObject.find(params[:event_object_id])
render_wizard
end
def update
#event_object = EventObject.find(params[:event_object_id])
#event_object.update_attributes(event_object_params)
render_wizard #event_object
end
private
def event_entry_params
params.fetch(:event_object, {}).permit(:choice_a)
end
end
I want to only include the step :optional_page if they have selection :choice_a to equal 2. I've tried various configs but the real issue I run into is if they go back to the :firstpage and change the steps aren't always correct. I'm sure someone has a good approach to this, any help would be greatly appreciated!!!
def show
#event_object = EventObject.find(params[:event_object_id])
# Extra logic based on flow steps - when to skip sth.
case step
when :optional_page
skip_step unless #event_object.choice_a == 2
end
render_wizard
end
How can I show a please wait loading message from a django view?
I have a Django view that takes significant time to perform calculations on a large dataset.
While the process loads, I would like to present the user with a feedback message e.g.: spinning loading animated gif or similar.
After trying the two different approaches suggested by Brandon and Murat, Brandon's suggestion proved the most successful.
Create a wrapper template that includes the javascript from http://djangosnippets.org/snippets/679/. The javascript has been modified: (i) to work without a form (ii) to hide the progress bar / display results when a 'done' flag is returned (iii) with the JSON update url pointing to the view described below
Move the slow loading function to a thread. This thread will be passed a cache key and will be responsible for updating the cache with progress status and then its results. The thread renders the original template as a string and saves it to the cache.
Create a view based on upload_progress from http://djangosnippets.org/snippets/678/ modified to (i) instead render the original wrapper template if progress_id='' (ii) generate the cache_key, check if a cache already exists and if not start a new thread (iii) monitor the progress of the thread and when done, pass the results to the wrapper template
The wrapper template displays the results via document.getElementById('main').innerHTML=data.result
(* looking at whether step 4 might be better implemented via a redirect as the rendered template contains javascript that is not currently run by document.getElementById('main').innerHTML=data.result)
Another thing you could do is add a javascript function that displays a loading image before it actually calls the Django View.
function showLoaderOnClick(url) {
showLoader();
window.location=url;
}
function showLoader(){
$('body').append('<div style="" id="loadingDiv"><div class="loader">Loading...</div></div>');
}
And then in your template you can do:
This will take some time...
Here's a quick default loadingDiv : https://stackoverflow.com/a/41730965/13476073
Note that this requires jQuery.
a more straightforward approach is to generate a wait page with your gif etc. and then use the javascript
window.location.href = 'insert results view here';
to switch to the results view which starts your lengthy calculation. The page wont change until the calculation is finished. When it finishes, then the results page will be rendered.
Here's an oldie, but might get you going in the right direction: http://djangosnippets.org/snippets/679/
A workaround that I chose was to use beforunload and unload events to show the loading image. This can be used with or without window.load. In my case, it's the view that is taking a great amount of time and not the page loading, hence I am not using window.load (because it's already a lot of time by the time window.load comes into picture, and at that point of time, I do not need the loading icon to be shown anymore).
The downside is that there is a false message that goes out to the user that the page is loading even when when the request has not even reached the server or it's taking much time. Also, it doesn't work for requests coming from outside my website. But I'm living with this for now.
Update: Sorry for not adding code snippet earlier, thanks #blockhead. The following is a quick and dirty mix of normal JS and JQuery that I have in the master template.
Update 2: I later moved to making my view(s) lightweight which send the crucial part of the page quickly, and then using ajax to get the remaining content while showing the loading icon. It needed quite some work, but the end result is worth it.
window.onload=function(){
$("#load-icon").hide(); // I needed the loading icon to hide once the page loads
}
var onBeforeUnLoadEvent = false;
window.onunload = window.onbeforeunload= function(){
if(!onBeforeUnLoadEvent){ // for avoiding dual calls in browsers that support both events
onBeforeUnLoadEvent = true;
$("#load-icon").show();
setTimeout(function(){
$("#load-icon").hide();},5000); // hiding the loading icon in any case after
// 5 seconds (remove if you do not want it)
}
};
P.S. I cannot comment yet hence posted this as an answer.
Iterating HttpResponse
https://stackoverflow.com/a/1371061/198062
Edit:
I found an example to sending big files with django: http://djangosnippets.org/snippets/365/ Then I look at FileWrapper class(django.core.servers.basehttp):
class FileWrapper(object):
"""Wrapper to convert file-like objects to iterables"""
def __init__(self, filelike, blksize=8192):
self.filelike = filelike
self.blksize = blksize
if hasattr(filelike,'close'):
self.close = filelike.close
def __getitem__(self,key):
data = self.filelike.read(self.blksize)
if data:
return data
raise IndexError
def __iter__(self):
return self
def next(self):
data = self.filelike.read(self.blksize)
if data:
return data
raise StopIteration
I think we can make a iterable class like this
class FlushContent(object):
def __init__(self):
# some initialization code
def __getitem__(self,key):
# send a part of html
def __iter__(self):
return self
def next(self):
# do some work
# return some html code
if finished:
raise StopIteration
then in views.py
def long_work(request):
flushcontent = FlushContent()
return HttpResponse(flushcontent)
Edit:
Example code, still not working:
class FlushContent(object):
def __init__(self):
self.stop_index=2
self.index=0
def __getitem__(self,key):
pass
def __iter__(self):
return self
def next(self):
if self.index==0:
html="loading"
elif self.index==1:
import time
time.sleep(5)
html="finished loading"
self.index+=1
if self.index>self.stop_index:
raise StopIteration
return html
Here is another explanation on how to get a loading message for long loading Django views
Views that do a lot of processing (e.g. complex queries with many objects, accessing 3rd party APIs) can take quite some time before the page is loaded and shown to the user in the browser. What happens is that all that processing is done on the server and Django is not able to serve the page before it is completed.
The only way to show a show a loading message (e.g. a spinner gif) during the processing is to break up the current view into two views:
First view renders the page with no processing and with the loading message
The page includes a AJAX call to the 2nd view that does the actual processing. The result of the processing is displayed on the page once its done with AJAX / JavaScript
How would I go about adding the "Spent Time" as a column to be displayed in the issues list?
Consolidating Eric and Joel's answers, this is what I needed to do to get a 'Spent time' column added to Redmine 1.0.3. Not sure if there's a better way to get the translation text added.
To give the new field a localised name, added to config/locales/en.yml around line 299 at the end of the field definitions:
field_spent_hours: Spent time
To add the new column, created lib/spent_time_query_patch.rb with content:
# Based on http://github.com/edavis10/question_plugin/blob/master/lib/question_query_patch.rb
require_dependency 'query'
module QueryPatch
def self.included(base) # :nodoc:
base.extend(ClassMethods)
# Same as typing in the class
base.class_eval do
unloadable # Send unloadable so it will not be unloaded in development
base.add_available_column(QueryColumn.new(:spent_hours))
end
end
module ClassMethods
unless Query.respond_to?(:available_columns=)
# Setter for +available_columns+ that isn't provided by the core.
def available_columns=(v)
self.available_columns = (v)
end
end
unless Query.respond_to?(:add_available_column)
# Method to add a column to the +available_columns+ that isn't provided by the core.
def add_available_column(column)
self.available_columns << (column)
end
end
end
end
To get the spent_time_query_patch above to actually load, created config/initializers/spent_time_query_patch.rb with content:
require 'spent_time_query_patch'
Query.class_eval do
include QueryPatch
end
You can also do this by adding the column at runtime. This will add the spent hours column without modifying the Redmine core. Just drop the following code into a file in lib/
Adapted from:
Redmine Budget Plugin
Redmine Question Plugin
require_dependency 'query'
module QueryPatch
def self.included(base) # :nodoc:
base.extend(ClassMethods)
# Same as typing in the class
base.class_eval do
unloadable # Send unloadable so it will not be unloaded in development
base.add_available_column(QueryColumn.new(:spent_hours))
end
end
module ClassMethods
unless Query.respond_to?(:available_columns=)
# Setter for +available_columns+ that isn't provided by the core.
def available_columns=(v)
self.available_columns = (v)
end
end
unless Query.respond_to?(:add_available_column)
# Method to add a column to the +available_columns+ that isn't provided by the core.
def add_available_column(column)
self.available_columns
Also, it would be cool, if the column "Spent time" was sortable.
After looking up the produced SQL, I just implemented the sortable feature this in this way:
base.add_available_column(QueryColumn.new(:spent_hours,
:sortable => "(select sum(hours) from time_entries where time_entries.issue_id = t0_r0)")
)
Replace the respective line. I just hope the issue_id column's name is always "t0_r0" ...
PS: You can find lots of examples in app/models/query.rb lines 122++
2-Digits Problem:
Unfortunatly, I had to hack one of the core files: app/helpers/queries_helper.rb
Around line 44, change this:
when 'Fixnum', 'Float'
if column.name == :done_ratio
progress_bar(value, :width => '80px')
else
value.to_s
end
into:
when 'Fixnum', 'Float'
if column.name == :done_ratio
progress_bar(value, :width => '80px')
elsif column.name == :spent_hours
sprintf "%.2f", value
else
value.to_s
end
EDIT: Using a patch instead manipulating the source Recently, we did an update of the redmine system, so the above mentioned Fix also was removed.
This time, we decided to implement that as a patch.
Open up any plugin (We created a plugin for our monkey-patch changes on core). open up vendor/plugins/redmine_YOURPLUGIN/app/helpers/queries_helper.rb
module QueriesHelper
def new_column_content(column, issue)
value = column.value(issue)
if value.class.name == "Float" and column.name == :spent_hours
sprintf "%.2f", value
else
__column_content(column, issue)
end
end
alias_method :__column_content, :column_content
alias_method :column_content, :new_column_content
end
This feature build in from 1.4.0 version
by using AgileDwarf plugin. You can have spent time & you can say for what you spent this time (developement - design -...)
Since no one answered, I just poked the source until it yielded results. Then I started a blog to explain how I did it.
Add spent time column to default issues list in Redmine