[IMP] gamification: corrections with feedbacks and usage of templates

bzr revid: mat@openerp.com-20130227165600-0majyllrmy452hwb
This commit is contained in:
Martin Trigaux 2013-02-27 17:56:00 +01:00
parent d9cee091ea
commit 79206a10e3
8 changed files with 166 additions and 140 deletions

View File

@ -22,11 +22,50 @@
from openerp.osv import fields, osv
from openerp.tools.safe_eval import safe_eval
from mako.template import Template as MakoTemplate
from datetime import date, datetime, timedelta
from mako.template import Template as MakoTemplate
from urllib import urlencode, quote as quote
import calendar
import itertools
import logging
import os.path
_logger = logging.getLogger(__name__)
GAMIFICATION_PATH = os.path.dirname(os.path.abspath(__file__))
try:
# We use a jinja2 sandboxed environment to render mako templates.
# Note that the rendering does not cover all the mako syntax, in particular
# arbitrary Python statements are not accepted, and not all expressions are
# allowed: only "public" attributes (not starting with '_') of objects may
# be accessed.
# This is done on purpose: it prevents incidental or malicious execution of
# Python code that may break the security of the server.
from jinja2.sandbox import SandboxedEnvironment
from jinja2 import FileSystemLoader
mako_template_env = SandboxedEnvironment(
loader=FileSystemLoader(os.path.join(GAMIFICATION_PATH,'templates/')),
block_start_string="<%",
block_end_string="%>",
variable_start_string="${",
variable_end_string="}",
comment_start_string="<%doc>",
comment_end_string="</%doc>",
line_statement_prefix="%",
line_comment_prefix="##",
trim_blocks=True, # do not output newline after blocks
autoescape=True, # XML/HTML automatic escaping
)
mako_template_env.globals.update({
'str': str,
'quote': quote,
'urlencode': urlencode,
})
except ImportError:
_logger.warning("jinja2 not available, templating features will not work!")
def start_end_date_for_period(period):
@ -221,12 +260,13 @@ class gamification_goal(osv.Model):
if goal.remind_update_delay and goal.last_update:
delta_max = timedelta(days=goal.remind_update_delay)
last_update = datetime.strptime(goal.last_update,'%Y-%m-%d').date()
if date.today() - last_update > delta_max:
if date.today() - last_update > delta_max and goal.state == 'inprogress':
towrite['state'] = 'inprogress_update'
# generate a remind report
template_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'gamification', 'email_template_goal_reminder')[1]
self.pool.get('email.template').send_mail(cr, uid, template_id, goal.id, context=context)
body_html = mako_template_env.get_template('reminder.mako').render({'object':goal})
self.pool.get('mail.thread').message_post(cr, goal.user_id.id, False, body=body_html, context=context)
#self.pool.get('email.template').send_mail(cr, uid, template_id, goal.id, context=context)
else: # count or sum
obj = self.pool.get(goal.type_id.model_id.model)
@ -280,7 +320,9 @@ class gamification_goal(osv.Model):
def write(self, cr, uid, ids, vals, context=None):
"""Overwrite the write method to update the last_update field to today"""
for goal in self.browse(cr, uid, ids, vals):
vals['last_update'] = fields.date.today()
# TODO if current in vals
#vals['last_update'] = fields.date.today()
pass
write_res = super(gamification_goal, self).write(cr, uid, ids, vals, context=context)
return write_res
@ -553,10 +595,9 @@ class gamification_goal_plan(osv.Model):
template_context = dict(context)
if plan.visibility_mode == 'board':
# generate a shared report
template_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'gamification', 'email_template_gamification_leaderboard')[1]
template_id = self.pool.get('ir.model.data').get_object(cr, uid, 'gamification', 'email_template_gamification_leaderboard')
# unsorted list of planline
template_context['planlines'] = []
planlines_boards = []
for planline in plan.planline_ids:
(start_date, end_date) = start_end_date_for_period(plan.period)
@ -567,12 +608,12 @@ class gamification_goal_plan(osv.Model):
]
if start_date:
domain.append(('start_date', '=', start_date.isoformat()))
goal_ids = goal_obj.search(cr, uid, domain, context=context)
planlines_stats = []
board_goals = []
goal_ids = goal_obj.search(cr, uid, domain, context=context)
for goal in goal_obj.browse(cr, uid, goal_ids, context=context):
planlines_stats.append({
board_goals.append({
'user': goal.user_id,
'current':goal.current,
'target_goal':goal.target_goal,
@ -580,23 +621,30 @@ class gamification_goal_plan(osv.Model):
})
# most complete first, current if same percentage (eg: if several 100%)
sorted_planline_goals = enumerate(sorted(planlines_stats, key=lambda k: (k['completeness'], k['current']), reverse=True))
template_context['planlines'].append({'goal_type':planline.type_id.name, 'list':sorted_planline_goals})
sorted_board = enumerate(sorted(board_goals, key=lambda k: (k['completeness'], k['current']), reverse=True))
planlines_boards.append({'goal_type':planline.type_id.name, 'board_goals':sorted_board})
self.pool.get('email.template').send_mail(cr, uid, template_id, plan.id, context=template_context)
body_html = mako_template_env.get_template('group_progress.mako').render({'object':plan, 'planlines_boards':planlines_boards})
self.pool.get('mail.thread').message_post(cr, [user.id for user in plan.user_ids], False, body=body_html, context=context)
#self.pool.get('email.template').send_mail(cr, uid, template_id, plan.id, context=template_context)
else:
# generate individual reports
template_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'gamification', 'email_template_gamification_individual')[1]
for user in plan.user_ids:
goal_ids = self.get_current_related_user_goals(cr, uid, plan.id, user.id, context)
goal_ids = self.get_current_related_goals(cr, uid, plan.id, user.id, context=context)
if len(goal_ids) == 0:
continue
template_context['goals'] = goal_obj.browse(cr, uid, goal_ids, context=context)
template_context['user'] = user
self.pool.get('email.template').send_mail(cr, uid, template_id, plan.id, context=template_context)
variables = {
'object':plan,
'user':user,
'goals':goal_obj.browse(cr, uid, goal_ids, context=context)
}
body_html = mako_template_env.get_template('personal_progress.mako').render(variables)
self.pool.get('mail.thread').message_post(cr, user.id, False, body=body_html, context=context)
#self.pool.get('email.template').send_mail(cr, uid, template_id, plan.id, context=template_context)
return True
@ -616,19 +664,14 @@ class gamification_goal_plan(osv.Model):
for planline in plan.planline_ids:
domain = [('planline_id', '=', planline.id),
('user_id', '=', user_id)]
('user_id', '=', user_id),
('state','in',('inprogress','inprogress_update','reached'))]
if start_date:
domain.append(('start_date', '=', start_date.isoformat()))
goal_ids = goal_obj.search(cr, uid, domain, context=context)
related_goal_ids.append(goal_ids[0])
if len(goal_ids) == 0:
# this goal has been deleted
raise osv.except_osv('Warning!','Planline {0} has no goal present for user {1} at date {2}'.format(planline.id, user.id, start_date))
else: # more than one goal ?
raise osv.except_osv('Warning!', 'Duplicate goals for planline {0}, user {1}, date {2}'.format(planline.id, user.id, start_date))
related_goal_ids.extend(goal_ids)
related_goal_ids.extend(goal_ids)
return related_goal_ids

View File

@ -1,23 +0,0 @@
<?xml version="1.0" ?>
<openerp>
<data>
<record id="email_template_goal_reminder" model="email.template">
<field name="name">Goal Update Reminder - Send by Email</field>
<field name="email_from">noreply@localhost</field>
<field name="subject">Goal Update Reminder</field>
<field name="email_recipients">${object.user_id.alias_id.name_get()[0][1]}</field>
<field name="model_id" ref="gamification.model_gamification_goal"/>
<field name="auto_delete" eval="True"/>
<field name="body_html"><![CDATA[
<div>
<p>Hello ${object.user_id.name}</p>
<p>You have not updated your progress for the goal ${object.type_id.name} (currently reached at ${object.completeness}%) for at least ${object.remind_update_delay}. Do not forget to do it.</p>
<p>If you have not changed your score yet, you can use the button "The current value is up to date" to indicate so.</p>
</div>
]]></field>
</record>
</data>
</openerp>

View File

@ -1,81 +0,0 @@
<?xml version="1.0" ?>
<openerp>
<data>
<!--Email template for individual report -->
<record id="email_template_gamification_individual" model="email.template">
<field name="name">Individual Goal Report - Send by Email</field>
<field name="email_from">noreply@localhost</field>
<field name="subject">Individual report - ${object.name}</field>
<field name="email_recipients">${object.report_message_group_id.alias_id.name_get()[0][1]}</field>
<field name="model_id" ref="gamification.model_gamification_goal_plan"/>
<field name="auto_delete" eval="True"/>
<field name="body_html"><![CDATA[
<div>
<h1>Individual report - ${object.name}</h1>
<p>Hello, bellow are the latest results for the plan ${object.name} for ${ctx['user'].name}.</p>
<table width="100%" border="1">
<tr>
<th>Goal</th>
<th>Target</th>
<th>Current</th>
<th>Completeness</th>
</tr>
% for goal in ctx['goals']:
<tr>
<td>${goal.type_id.name}</td>
<td>${goal.target_goal}</td>
<td>${goal.current}</td>
<td>${goal.completeness} %</td>
</tr>
% endfor
</table>
</div>
]]></field>
</record>
<!-- Email template for leaderboard -->
<record id="email_template_gamification_leaderboard" model="email.template">
<field name="name">Leaderboard Goal Report - Send by Email</field>
<field name="email_from">noreply@localhost</field>
<field name="subject">${object.name} group report</field>
<field name="email_recipients">${object.report_message_group_id.alias_id.name_get()[0][1]}</field>
<field name="model_id" ref="gamification.model_gamification_goal_plan"/>
<field name="auto_delete" eval="True"/>
<field name="body_html"><![CDATA[
<div>
<h1>Group report - ${object.name}</h1>
<p>Hello,
Bellow are the latest results for the plan ${object.name} for the group ${object.report_message_group_id.name}</p>
% for planline in ctx['planlines']:
<h2>${planline['goal_type']}</h2>
<table width="100%" border="1">
<th>#</th>
<th>User</th>
<th>Completeness</th>
<th>Current</th>
% for idx, goal in planline['list']:
<tr
% if goal.completeness >= 100:
style="font-weight:bold;""
% endif
>
<td>${idx+1}</td>
<td>${goal.user.name}</td>
<td>${goal.completeness}%</td>
<td>${goal.current}/${goal.target_goal}</td>
</tr>
% endfor
</table>
% endfor
</div>
]]></field>
</record>
</data>
</openerp>

View File

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<style type="text_css">${css}</style>
</head>
<body>
<header>
% if object.report_header
${object.report_header}
% endif
</header>
Below are the latest results for the plan <strong>${object.name}</strong> for the group <em>${object.report_message_group_id.name}</em>.</p>
!${planlines_boards}!
% for planline in planlines_boards:
<h2>${planline['goal_type']}</h2>
<table width="100%" border="1">
<tr>
<th>#</th>
<th>User</th>
<th>Completeness</th>
<th>Current</th>
</tr>
% for idx, goal in planline['board_goals']:
<tr
% if goal.completeness >= 100:
style="font-weight:bold;"
% endif
>
<td>${idx+1}</td>
<td>${goal.user.name}</td>
<td>${goal.completeness}%</td>
<td>${goal.current}/${goal.target_goal}</td>
</tr>
% endfor
</table>
% endfor
</body>

View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head>
<style type="text_css">${css}</style>
</head>
<body>
<header>
% if object.report_header
${object.report_header}
% endif
</header>
<p>Below are the latest results for the plan ${object.name} for ${user.name}.</p>
<table width="100%" border="1">
<tr>
<th>Goal</th>
<th>Target</th>
<th>Current</th>
<th>Completeness</th>
</tr>
% for goal in goals:
<tr>
<td>${goal.type_id.name}</td>
<td>${goal.target_goal}</td>
<td>${goal.current}</td>
<td>${goal.completeness} %</td>
</tr>
% endfor
</table>
</body>

View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<style type="text_css">${css}</style>
</head>
<body>
<header>
% if object.report_header
${object.report_header}
% endif
</header>
<p>You have not updated your progress for the goal ${object.type_id.name} (currently reached at ${object.completeness}%) for at least ${object.remind_update_delay}. Do not forget to do it.</p>
<p>If you have not changed your score yet, you can use the button "The current value is up to date" to indicate so.</p>
</body>

View File

@ -41,16 +41,13 @@
<form string="Goal" version="7.0">
<header>
<button string="Start goal" type="object" name="action_start" states="draft" class="oe_highlight"/>
<button string="Refresh state" type="object" name="update" states="inprogress,inprogress_update,failed,reached" class="oe_highlight"/>
<button string="Goal Reached" type="object" name="action_reach" states="inprogress,inprogress_update" />
<button string="Goal Failed" type="object" name="action_fail" states="inprogress,inprogress_update"/>
<button string="Reset Completion" type="object" name="action_cancel" states="failed,reached"/>
<field name="state" widget="statusbar" statusbar_visible="inprogress" />
</header>
<sheet>
<div class="oe_right oe_button_box" attrs="{'invisible':[('computation_mode','!=', 'manually')]}">
<button name="write" type="object" string="The current value is up to date" help="Indicate that the current value of the manual goal is still correct and avoid reminders"/>
</div>
<group>
<group string="Reference">
<field name="type_id" on_change="on_change_type_id(type_id)" attrs="{'readonly':[('state','!=','draft')]}"/>
@ -69,9 +66,14 @@
</div>
<field name="last_update" groups="base.group_no_one"/>
</group>
<group string="Data">
<group string="Data" colspan="4">
<field name="target_goal" attrs="{'readonly':[('state','!=','draft')]}"/>
<field name="current" attrs="{'readonly':[('computation_mode','!=','manually')]}"/>
<label for="current" />
<div>
<field name="current" attrs="{'readonly':[('computation_mode','!=','manually')]}" class="oe_inline"/>
<button string="Refresh state" type="object" name="update" states="inprogress,inprogress_update,failed,reached" class="oe_highlight oe_inline"/>
<button name="write" type="object" string="The current value is up to date" help="Indicate that the current value of the manual goal is still correct and avoid reminders" attrs="{'invisible':[('computation_mode','!=', 'manually')]}"/>
</div>
</group>
</group>
</sheet>

View File

@ -47,8 +47,8 @@
<button string="Start Plan" type="object" name="action_start" states="draft" class="oe_highlight"/>
<button string="Check Plan" type="object" name="action_check" states="inprogress"/>
<button string="Close Plan" type="object" name="action_close" states="inprogress" class="oe_highlight"/>
<button string="Cancel Plan" type="object" name="action_cancel" states="inprogress"/>
<button string="Reset Plan" type="object" name="action_reset" states="done"/>
<button string="Reset to Draft" type="object" name="action_cancel" states="inprogress"/>
<button string="Reset Completion" type="object" name="action_reset" states="done"/>
<button string="Report Progress" type="object" name="report_progress" states="inprogress,done" groups="base.group_no_one"/>
<field name="state" widget="statusbar"/>
</header>