[IMP] gamification: corrections with feedbacks and usage of templates
bzr revid: mat@openerp.com-20130227165600-0majyllrmy452hwb
This commit is contained in:
parent
d9cee091ea
commit
79206a10e3
|
@ -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
|
||||
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue