Commit a27595a8 authored by LE GAC Renaud's avatar LE GAC Renaud
Browse files

Deploy the new class Selector in list and metric controllers. Simplify the views.

parent b7348573
......@@ -6,52 +6,44 @@ import re
from gluon.dal import Field
from list_postprocessing import *
from reporting_tools import (Report,
from plugin_dbui import Selector
from reporting_tools import (get_converter,
get_sections,
get_selector)
repr_team_project)
MSG_NO_LIST = T("Please select a list....")
MSG_NO_SECTIONS = T('Please, define sections for the list "%s"')
def index():
"""Main Controller handling list.
"""
# get the list identifier
(id_list,) = get_selector("id_lists")
# protection
if not id_list:
# get the user constraint.
# the selector fields year_start, year_end, id_lists are excluded
# from the selector query.
selector = Selector(virtdb.list_selector,
exclude_fields=('year_start', 'year_end', 'id_lists'),
extfield='format')
# protection
if not selector.id_lists:
return MSG_NO_LIST
# retrieve the list data
list = db.lists[id_list]
# get the user requirements
# download the list as a file with a given format
tool = Report(db)
tool.decode_request()
iframe = tool.download()
# download list as a file with a given format
iframe = selector.download()
if iframe:
return iframe
# retrieve the sections belonging to the list
# and add parameter query and orderby
sections = get_sections(db, list)
# build the converter dictionary for axis label
# RULE: value: "blabla, ....", value2: "foo", ....
cvt_labels = {}
if list.axis_label_converters:
s = re.sub(r'" *, *', '"|', list.axis_label_converters)
for el in s.split('|'):
m = re.match(r' *(.+) *: *"(.+)"', el)
if m:
cvt_labels[m.group(1)] = m.group(2)
# retrieve the list data
list = db.lists[selector.id_lists]
# retrieve the section records belonging to the list
# and add to each section the parameters query and orderby
sections = get_sections(db, selector, list)
# get the converter dictionary for axes labels
cvt_labels = get_converter(list)
# retrieve the publications for each section
# organise them according to the level requirements, for example
......@@ -120,5 +112,6 @@ def index():
return dict(list=list,
paths_keys=paths_keys,
paths_data=paths_data,
tool=tool)
paths_data=paths_data,
selector=selector,
team_project=repr_team_project(db, selector))
......@@ -3,9 +3,8 @@
"""
import re
from reporting_tools import (Diplomas,
Publications,
get_selector)
from plugin_dbui import Selector
from reporting_tools import get_converter, repr_team_project
MSG_NO_METRIC = T("Please select a metric....")
......@@ -16,33 +15,30 @@ def index():
"""Main Controller handling metrics.
"""
# get the metric identifier
(id_metric,) = get_selector("id_metrics")
# get the user constraint.
# the selector fields year_start, year_end, id_lists are excluded
# from the selector query.
selector = Selector(virtdb.metric_selector,
exclude_fields=('year_start', 'year_end', 'id_metrics'),
extfield='format')
# protection
if not id_metric:
if not selector.id_metrics:
return MSG_NO_METRIC
# the metric data
metric = db.metrics[id_metric]
# download metric as a file with a given format
iframe = selector.download()
if iframe:
return iframe
# selector requirements
# retrieve metric data
metric = db.metrics[selector.id_metrics]
# protection
tablename = metric.tablename
if tablename == 'diplomas':
tool = Diplomas(db)
elif tablename == 'publications':
tool = Publications(db)
else:
if tablename not in ('diplomas', 'publications'):
return MSG_NO_TABLE % tablename
# download the metric as a file with a given format
tool.decode_request()
iframe = tool.download()
if iframe:
return iframe
# database fields corresponding to the axes
fields, orderby = [], []
for axis in (metric.axis_columns, metric.axis_rows):
......@@ -60,9 +56,20 @@ def index():
field = ~field
orderby.append(field)
# build the table
query = tool.query()
# query directive to count publications including
# foreign key constraints and user requirements
# related to team, project and year
query = selector.query(db[tablename])
if selector.year_start and not selector.year_end:
query = (query) & (db[tablename].year == selector.year_start)
elif selector.year_start and selector.year_end:
q_start = db[tablename].year >= selector.year_start
q_end = db[tablename].year <= selector.year_end
query = (query) & ((q_start) & (q_end))
# build the table object
header_columns, field = [], fields[0]
for row in db(query).select(fields[0], distinct=True, orderby=orderby[0]):
header_columns.append(row[field])
......@@ -81,11 +88,11 @@ def index():
# fill the table with value from the database
count = db[tablename].id.count()
rows = db(tool.query()).select(fields[0],
fields[1],
count,
groupby=fields,
orderby=orderby)
rows = db(query).select(fields[0],
fields[1],
count,
groupby=fields,
orderby=orderby)
for row in rows:
icol = header_columns.index(row[fields[0]]) + 1
......@@ -93,16 +100,7 @@ def index():
table[icol][irow] = row[count]
# Substitute axis label
# RULE: value: "blabla, ....", value2: "foo", ....
cvt_labels = {}
if metric.axis_label_converters:
s = re.sub(r'" *, *', '"|', metric.axis_label_converters)
for el in s.split('|'):
m = re.match(r' *(.+) *: *"(.+)"', el)
if m:
cvt_labels[m.group(1)] = m.group(2)
cvt_labels = get_converter(metric)
for i in range(len(table[0])):
if table[0][i] in cvt_labels:
......@@ -112,4 +110,6 @@ def index():
if table[i][0] in cvt_labels:
table[i][0] = cvt_labels[table[i][0]]
return dict(table=table, tool=tool)
return dict(table=table,
team_project=repr_team_project(db, selector),
selector=selector)
......@@ -9,44 +9,64 @@ from gluon.html import IFRAME, URL
from plugin_dbui import decode_field
def get_sections(db, list):
"""Helper function returning a list of section.
Build the query and orderby directive for each section taking into
account list and user directive. The corresponding keyword are added
to the section storage.
def get_converter(list):
"""Helper function returning the converter dictionary for axis label.
RULE: value: "blabla, ....", value2: "foo", ....
"""
# get the basic query for a diplomas or publications
# taking into account foreign keys and user requirements
tool = Diplomas(db)
tool.decode_request()
q_diplomas = tool.query()
di = {}
if list.axis_label_converters:
tool = Publications(db)
tool.decode_request()
q_publications = tool.query()
s = re.sub(r'" *, *', '"|', list.axis_label_converters)
for el in s.split('|'):
m = re.match(r' *(.+) *: *"(.+)"', el)
if m:
di[m.group(1)] = m.group(2)
return di
# build the list of sections embedded in the list
def get_sections(db, selector, list):
"""Helper function returning the list of section records.
Add to each record, the query and orderby directive taking into
account user directive.
The query and orderby keyword are added to the each section record.
They take into account user and section constraints.
"""
sections = []
for el in list.sections.split(','):
# retrieve the section record
el = el.strip()
rows = db(db.sections.section == el).select()
# go to the next section if nothing is found in the database
if not rows:
continue
section = rows.first()
sections.append(section)
# build the query
# the basic query depends on the database table
q_section = q_publications
if section.tablename == 'diplomas':
q_section = q_diplomas
# query directive to extract publications for this section
# It includes foreign key constraints and user requirements
# related to team, project, authors and year
tablename = section.tablename
query = selector.query(db[tablename])
if selector.year_start and not selector.year_end:
query = (query) & (db[tablename].year == selector.year_start)
elif selector.year_start and selector.year_end:
q_start = db[tablename].year >= selector.year_start
q_end = db[tablename].year <= selector.year_end
query = (query) & ((q_start) & (q_end))
# add to the query the category codes processed by the section
# add to the query the directive coming from the section itself
# the publication category and dedicated conditions
if section.category_codes:
q_cat = None
codes = section.category_codes.replace(' ','').split(',')
......@@ -57,17 +77,16 @@ def get_sections(db, list):
else:
q_cat = q
q_section = (q_section) & (q_cat)
query = (query) & (q_cat)
# Add to the query the section conditions
if section.conditions:
q_conditions = smart_query(db[section.tablename], section.conditions)
q_section = (q_section) & (q_conditions)
section.query = q_section
# build the order by directive taking into account
# directive from the list and from the section
q_conditions = smart_query(db[tablename], section.conditions)
query = (query) & (q_conditions)
section.query = query
# order by directive to order publication for this section
# taking into account directive from the list and from the section
orderby = []
for i in range(1,5):
......@@ -94,221 +113,39 @@ def get_sections(db, list):
return sections
def get_selector(*fields):
"""Helper function returning a tuple with the selector value
corresponding to the list arguments fields.
They are search in request.vars
Note that the field is equal to None when not found in vars.
def ref_url(name='List'):
"""Reference URL to build the current request.
"""
di = {}
for el in fields:
di[el] = None
vars = []
for (k, v) in request.vars.iteritems():
if k.startswith(name) and len(v):
vars.append("%s=%s" % (k, v))
pass
pass
t = (request.env.http_host, request.env.path_info, '&'.join(vars))
url = "http://%s%s?%s" % t
# keep in mind that the field are encode ad TablenameFieldName
# where tablename is the name of the selector
for key in current.request.vars:
t = decode_field(key)
if len(t) != 2:
continue
if t[1] in fields:
di[t[1]] = current.request.vars[key]
return (di[el] for el in fields)
class Report(object):
"""Basic tool to build a report.
It analyses data send by the selector widget in order
to determine the user constraints.
"""
def __init__(self, db, extfield='format'):
self.author = None
self.db = db
self.ext_field = extfield
self.extension = None
self.id_project = None
self.id_team = None
self.y1 = None
self.y2 = None
def decode_request(self):
"""Decode the current selector request in order to extract the
parameters to build the report.
"""
for key in current.request.vars:
t = decode_field(key)
if 'author' in t:
self.author = current.request.vars[key]
elif self.ext_field in t:
self.extension = current.request.vars[key]
elif 'id_projects' in t:
self.id_project = current.request.vars[key]
elif 'id_teams' in t:
self.id_team = current.request.vars[key]
elif 'year_start' in t:
self.y1 = current.request.vars[key]
elif 'year_end' in t:
self.y2 = current.request.vars[key]
def download(self):
"""The report can be download and receive as a file with a given format.
this method initiate this process by return and IFRAME.
The IFRAME is used by the browser to download the file.
The file format is defined by one of the field of the list selector.
By default is it 'format'. It can be changed via the argument extfield
of the constructor.
The IFRAME contain and URL. it it the same as the one defined in the
current request (application/controller/function) but the extension
is defined by the format field.
In this process all data conversion are performed by the view.
"""
if self.extension and self.extension != 'html':
return url
# remove the field use to select the file format
# in order to avoid circular loop
for key in current.request.vars:
t = decode_field(key)
if self.ext_field in t:
del current.request.vars[key]
break
# The new URL is equal to the one defined in the current
# request but with the proper extension
url = URL(current.request.function, extension=self.extension,
vars=current.request.vars)
# Return the IFRAME
# the browser will request the file locate at the URL of the IFRAME
# The IFRAME occupy all the space in the receiving container
# Useful for embedded pdf application.
return IFRAME(_frameborder=0,
_width="100%",
_height="100%",
_src=url)
return None
def query(self):
"""Build the database query for a given table
resolving foreign keys and taking into account request constraints.
Return the query.
To implement for each inherited class.
"""
return None
def years(self):
"""Return the list of years corresponding to the selection criteria.
"""
if self.y1 and not self.y2:
y1 = int(self.y1)
return [y1]
elif self.y1 and self.y2:
y1, y2 = int(self.y1), int(self.y2)
return range(y1, y2+1)
else:
# interrogate the database in order to find the
# year range corresponding to the current selection criteria
min = self.db.publications.year.min()
max = self.db.publications.year.max()
row = self.db(self.query()).select(min, max).first()
y1, y2 = int(row[min]), int(row[max])
return range(y1, y2+1)
class Diplomas(Report):
def repr_team_project(db, selector):
""" Helper function to build the team/project string.
def query(self):
"""Build the database query for the diploma table
resolving foreign keys and taking into account request constraints.
"""
db = self.db
# query resolving foreign keys
query = db.diplomas.id_levels == db.levels.id
query = (query) & (db.diplomas.id_projects == db.projects.id)
query = (query) & (db.diplomas.id_teams == db.teams.id)
"""
s = ''
# add selector constraints
if self.id_project:
query = (query) & (db.diplomas.id_projects == self.id_project)
if self.id_team:
query = (query) & (db.diplomas.id_teams == self.id_team)
if self.author:
query = (query) & (db.diplomas.author.contains(self.author))
if self.y1 and not self.y2:
query = (query) & (db.diplomas.year == self.y1)
elif self.y1 and self.y2:
cnd = (db.diplomas.year >= self.y1) & (db.diplomas.year <= self.y2)
query = (query) & (cnd)
if selector.id_teams and not selector.id_projects:
s = db.teams[selector.id_teams].team
return query
elif not selector.id_teams and selector.id_projects:
s = db.projects[selector.id_projects].project
class Publications(Report):
def query(self):
"""Build the database query for the publication table
resolving foreign keys and taking into account request constraints.
"""
db = self.db
# query resolving foreign keys
query = db.publications.id_collaborations == db.collaborations.id
query = (query) & (db.publications.id_categories == db.categories.id)
query = (query) & (db.publications.id_countries == db.countries.id)
query = (query) & (db.publications.id_projects == db.projects.id)
query = (query) & (db.publications.id_publishers == db.publishers.id)
query = (query) & (db.publications.id_teams == db.teams.id)
# add selector constraints
if self.id_project:
query = (query) & (db.publications.id_projects == self.id_project)
if self.id_team:
query = (query) & (db.publications.id_teams == self.id_team)
if self.author:
query = (query) & (db.publications.authors_cppm.contains(self.author))
if self.y1 and not self.y2:
query = (query) & (db.publications.year == self.y1)
elif self.y1 and self.y2:
cnd = (db.publications.year >= self.y1) & (db.publications.year <= self.y2)
query = (query) & (cnd)
elif selector.id_teams and selector.id_projects:
team = db.teams[selector.id_teams].team
project = db.projects[selector.id_projects].project
s = "%s/%s" % (team, project)
return query
return s
--------------------------------- CHANGELOG ----------------------------------
HEAD
- Migrate to plugin_dbui 0.4.9.6.
- New tree structure in the viewport.
- Add a wizard check and validate.
- Deploy the new class Selector in lists and metrics.
- Deploy linked comboBox in the harvester selector.
0.6.0 (Oct 2012)
- Redesign the metric to use the axes approach.
- Remove obsolete code for lists and metrics reporting.
......
{{
#
# Serie of helper functions to builds views
#
def get_title(selector, undef_id):
title = ''
if selector.id_team and not selector.id_project:
title = db.teams[selector.id_team].team
elif not selector.id_team and selector.id_project:
title = db.projects[selector.id_project].project
elif selector.id_team and selector.id_project:
team = db.teams[selector.id_team].team
project = db.projects[selector.id_project].project
title = "%s/%s" % (team, project)
pass
return title
def get_url(name='List'):
# the url used for the current request
vars = []
for (k, v) in request.vars.iteritems():
if k.startswith(name) and len(v):
vars.append("%s=%s" % (k, v))
pass
pass
t = (request.env.http_host, request.env.path_info, '&'.join(vars))
url = "http://%s%s?%s" % t