Commit 06e0ab32 authored by LE GAC Renaud's avatar LE GAC Renaud
Browse files

Merge branch '83-speed-up-graph' into 'master'

Resolve "Speed up graph"

Closes #83

See merge request !84
parents 8fe8156a 6e5452ff
......@@ -7,19 +7,14 @@ import matplotlib
import matplotlib.pyplot as plt
from datetime import datetime
from gluon import current
from import Storage
from graph_tools import (do_labels,
from pandas import DataFrame
from plugin_dbui import Selector
from reporting_tools import repr_team_project
TITLE_Y = "Number of publications"
from graph_tools import (FROM_TO,
from model_selector import YEAR_SUBMIT
def dashboard():
......@@ -28,73 +23,70 @@ def dashboard():
the current year.
current_year =
cfg = Storage()
cfg.Graph_selectorCumulative = "True"
cfg.Graph_selectorId = ""
cfg.Graph_selectorId_authors_roles = ""
cfg.Graph_selectorId_graphs = ""
cfg.Graph_selectorId_projects = ""
cfg.Graphs_selectorId_teams = ""
cfg.Graph_selectorTime = ""
cfg.Graph_selectorYear_start = ""
cfg.Graph_selectorYear_end = ""
fields = ("cumulative", "id_graphs", "time", "year_start", "year_end")
selector = Selector(virtdb.graph_selector, exclude_fields=fields)
current_year =
dashboard_start =
selector = Storage()
selector.Graph_selectorCumulative = "true"
selector.Graph_selectorId = ""
selector.Graph_selectorId_authors_roles = ""
selector.Graph_selectorId_graphs = ""
selector.Graph_selectorId_projects = ""
selector.Graphs_selectorId_teams = ""
selector.Graph_selectorTime = ""
selector.Graph_selectorYear_start = ""
selector.Graph_selectorYear_end = ""
# figure layout
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, sharey=True)
# histogram of the number of publications per year
selector.cumulative = False
selector.time = T("year")
selector.year_start = ""
selector.year_end = current_year - 1
# the number of publications per year except for the current year
# the starting value is defined in the preferences of the application
min_year = ("" if dashboard_start is None else str(dashboard_start))
max_year = str(current_year - 1)
selector.Graph_selectorCumulative = "false"
selector.Graph_selectorTime = T(YEAR_SUBMIT)
selector.Graph_selectorYear_start = min_year
selector.Graph_selectorYear_end = max_year
do_linechart(db.publications, selector, target=ax1)
do_labels(ax1, "", T(TITLE_Y))
title = (T(FROM_TO) % (min_year, max_year)).decode("utf-8")
ylabel = T(LABELY_YEAR).decode("utf-8")
# the cumulative sum of publications for the current year
selector.cumulative = True
selector.time = T("month")
selector.year_start = current_year
selector.year_end = ""
do_linechart(db.publications, selector, target=ax2)
# the cumulative sum of publications for the current year on a month
year = str(current_year)
selector.Graph_selectorCumulative = "true"
selector.Graph_selectorTime = T(YEAR_SUBMIT)
selector.Graph_selectorYear_start = year
selector.Graph_selectorYear_end = ""
title = (T("In %s") % year).decode("utf-8")
linechart(db, selector, target=ax2, title=title)
# delegate the rendering to the view
response.view = "graphs/index.html"
return dict(data=savefig(fig, "svg"))
def index():
def publications_versus_time():
"""Generate graph showing the number of publication per month / year
either as a linechart or as a stacked histograms.
either as a line chart or as a stacked histograms.
fields = \
("author", "cumulative", "id_graphs", "time", "year_start", "year_end")
selector = Selector(virtdb.graph_selector, exclude_fields=fields)
# graph configuration
graph = db.graphs[selector.id_graphs]
# stacked chart
if graph.stack_axis:
ax = do_stackedchart(db.publications, selector, graph)
# line char
ax = do_linechart(db.publications, selector)
# user criteria and graph configuration
selector = request.vars
graph = db.graphs[selector.Graph_selectorId_graphs]
axis = graph.stack_axis
do_labels(ax, "", T(TITLE_Y))
do_title(ax, db, selector)
# instantiate the graph
ax = (stackchart(db, selector) if axis else linechart(db, selector))
# delegate the rendering to the view
extension = request.extension
......@@ -105,4 +97,5 @@ def index():
if fmt in ("pdf", "png"):
data = base64.b64encode(data)
response.view = "graphs/index.%s" % extension
return dict(data=data)
......@@ -19,6 +19,7 @@
'about': 'à propos',
'ACL': 'ACL',
'acte de conférence': 'acte de conférence',
'Action': 'Action',
......@@ -26,6 +27,8 @@
'Added': 'Ajouté',
'Address of the invenio store where the search is performed.': 'Addresse du site invenio où les recherches sont effectuées.',
'administrators, librairians,...': 'administrateurs, documentalistes,...',
'AFF': 'AFF',
'affiche': 'affiche',
'Affiliation': 'Affiliation',
'affiliation keys': "clés définissant l'affiliation",
'Affiliation keys are not defined !!!': "Les clés définissant votre affiliation n'existe pas !!!",
......@@ -85,6 +88,8 @@
'Binary files': 'fichiers binaires',
'Book': 'Ouvrage',
'book': 'ouvrage',
'BRE': 'BRE',
'brevet': 'brevet',
'but with different category: %s': 'mais avec une catégorie différente : %s',
'cache': 'cache',
'Can be applied on any field of the table using the SQL WHERE syntax. Be aware that foreign key are not resolved (see smart_query in the web2py)': 'Peu être appliqué à tous le champs de la table publications en utilisant la syntaxe SQL WHERE. Attention, les clés étrangères ne sont pas résolues (voir le chapitre smart_query dans la documentation web2py)',
......@@ -166,6 +171,8 @@
'Created On': 'Created On',
'Cumulative': 'Cumulé',
'Cumulative sum of publications': 'Somme cumulée des publications',
'Cumulative sum of publications / month': 'Somme cumulée des publications / mois',
'Cumulative sum of publications / year': 'Somme cumulée des publications / année',
'dashboard': 'tableau de bord',
'Data base scheme': 'Schéma base de donnée',
'Data institute': 'Les données du laboratoire',
......@@ -201,6 +208,7 @@
'Direction Row': 'Direction Row',
'Direction Vertical': 'Direction Vertical',
'Directors': 'Directeurs',
'DO': 'DO',
'Documentation for developers': 'Documentation développeurs',
'documentations': 'documentations',
'Documentations': 'Documentations',
......@@ -266,8 +274,8 @@
'from %s to %s': 'de %s à %s',
'Function disabled': 'Fonction desactivée',
'General': 'Général',
'granularity': 'granularité',
'Granularity': 'Granularité',
'granularity': 'granularité',
'Granularity Column': 'Granularity Column',
'Granularity Horizontal': 'Granularity Horizontal',
'Granularity Level 1': 'Granularity Level 1',
......@@ -294,6 +302,7 @@
'Harvester(s)': 'Moissonneur(s)',
'harvesters': 'moissonneurs',
'Harvesters are ran automatically when the value is equal to true.': 'Les moissonneurs sont éxécuté automatiquement quand cette valuer est égale à vraie.',
'HDR': 'HDR',
'Header': 'En tête',
'Header / Footer': 'Header / Footer',
'Header/Footer': 'Header/Footer',
......@@ -305,6 +314,8 @@
'Id': 'Id',
'Identical harvester already exists.': 'Ce moissonneur existe dans la base de données.',
'Import/Export': 'Importer/Exporter',
'In %s': 'En %s',
'indéfini': 'indéfini',
'insert MARCXML': 'insérer MARCXML',
'insert new': 'insert new',
'insert new %s': 'insert new %s',
......@@ -313,11 +324,13 @@
'Institute identifier in': 'Identifiant du laboratoire dans',
'Institute not found in the inspirehep database!': "Le laboratoire n'a pas été trouvé !",
'Institute number associated to CPPM authors': "Numéro de l'Institut associé aux auteurs du CPPM",
'INV': 'INV',
'Invalid': 'Non conforme',
"Invalid database table '%s'": "Invalid database table '%s'",
'Invalid email': 'Invalid email',
'invalid expression': 'expression non conforme',
'Invalid expression': 'Expression non conforme',
'Invalid host': 'Invalid host',
'Invalid keyword argument': 'Invalid keyword argument',
'Invalid list of record identifiers': "La liste des identifiants n'est pas valide",
'Invalid login': 'Invalid login',
......@@ -384,8 +397,10 @@
'Modified On': 'Modified On',
'Modify': 'Modifier',
'month': 'mois',
'month of submission': 'mois de soumission',
'More than one affiliation for the selected author!': "Plus d'une affiliation pour l'auteur sélectionné !",
'my_authors': 'autheurs du laboratoire',
'mémoire': 'mémoire',
'Name': 'Nom',
'Name of a function located in the modules list_postprocessing. Can be a list of name separated by comma.': 'Liste de fonction du module list_postprocessing, séparé par une virgule. Les fonctions disponibles sont : clean, highlight_my_authors, highlight_my_speaker, remove_undef.',
'Name of the database table containing the publications shown in this section.': 'Nom de la table qui contient les publications qui seront présentées dans cette section.',
......@@ -398,6 +413,7 @@
'Niveau 4': 'Niveau 4',
'No harvesters for your selection !!!': 'Pas de moisonneurs pour votre sélection !!!',
'Nom': 'Nom',
'Nombre de publications / année': 'Nombre de publications / année',
'not install': 'pas installé',
'Note': 'Note',
'Notes and reports to committees': 'Notes et Rapports à des Comités',
......@@ -406,6 +422,8 @@
'Number of invalid records': "Nombre d'enregistrement non valide",
'Number of invalid records: %s': "Nombre d'enregistrement non valide : %s",
'Number of publications': 'Nombre de publications',
'Number of publications / month': 'Nombre de publications / mois',
'Number of publications / year': 'Nombre de publications / année',
'Number of records added in the database': "Nombre d'enregistrements ajoutés à la base de donnée",
'Number of records already registered': "Nombre d'enregistrements déjà enregistrés dans la base de donnée",
'Number of records already validated': "Nombre d'enregistrements déjà validé",
......@@ -431,6 +449,9 @@
'organisations': 'organisations',
'organizations': 'organizations',
'Origin': 'Origine',
'OS': 'OS',
'ouvrage': 'ouvrage',
'OV': 'OV',
'Page generated in %s seconds': 'Page générée en %s secondes',
'pages': 'pages',
'Pages': 'Pages',
......@@ -443,6 +464,7 @@
'patent': 'brevet',
'PDF file url': 'URL du pdf',
'Period': 'Période',
'PHD ': 'PHD ',
'PhD Thesis, ...': 'Doctorat, habilitation à diriger les recherches, ...',
'PhDs': 'PhDs',
'please input your password again': 'please input your password again',
......@@ -456,9 +478,10 @@
'poster': 'affiche',
'Posters': 'Affiches',
'Postprocessing': 'Postprocessing',
'PRE': 'PRE',
'Preference REG_INSTITUTE is not defined.': 'La préférence REG_INSTITUTE est indéfinie.',
'Preferences': 'Préférences',
'preferences': 'préférences',
'Preferences': 'Préférences',
'Preprint': 'Preprint',
'preprint': 'preprint',
'Preprint identifier separated by comma: arXiv:0906.1516': 'Numéro(s) du preprint séparé par des virgules: arXiv:0906.1516',
......@@ -476,8 +499,10 @@
'Projets': 'Projets',
'properties': 'propriétés',
'Property': 'Propriété',
'présentation orale': 'présentation orale',
'Publication': 'Publication',
'Publication category associated to the found records.': 'Catégorie associée aux enregistrements.',
'Publication date': 'Date de publication',
'Publication not found!': "La publication n'a pas été trouvé !",
'publications': 'publications',
'Publications': 'Publications',
......@@ -488,6 +513,7 @@
'Publishers is not defined': "La revue n'est pas définie",
'Python API': 'Python API',
'Query:': 'Query:',
'rapport': 'rapport',
'Ratio': 'Ratio',
'Record': 'Enregistrement',
'record id': 'record id',
......@@ -617,6 +643,7 @@
'Stacked Axis': 'Stacked Axis',
'stacked chart': 'stacked chart',
'Stacked Granularity': 'Stacked Granularity',
'Starting year for the dashboard': 'Année de début du tableau de bord',
'state': 'state',
'Statistics': 'Statistique',
'Status': 'Status',
......@@ -626,6 +653,7 @@
'String containing blabla and the database fields to be displayed. The substitution mechanism is: {tablename.fieldname} or {foreigntablename.fieldname}': 'Chaîne de caractère contenant du texte et des champs de la base de donnée. Les régles de substitution sont : {tablename.fieldname} ou {foreigntablename.fieldname}',
'String containing the author names with their institutes number.': 'Chaîne de caractère contenant le nom des auteurs avec le numéro de leurs instituts.',
'string indices must be integers, not str': 'string indices must be integers, not str',
'Submission date': 'Date de soumission',
'Submitted': 'Soumis',
'Submitted date is not defined': "La date de soumission n'est pas défini",
'Submitted date is not valid': "La date de soumission n'est pas valide",
......@@ -653,6 +681,7 @@
'The <i>record id</i> is not well formed.': 'Le <i>record id</i> est mal formé.',
'The category is used twice !': 'La catégorie est utilisée deux fois !',
'The field "%s" is missing ...': 'Le champ "%s" est manquant ...',
"The field '%s' is missing ...": "The field '%s' is missing ...",
'The generation of the ODT file failed:<br><br>': 'La génération du fichier ODT a échoué :<br><br>',
'The generation of the PDF file failed.<br>Please, check the LaTeX file.': 'La génération du fichier PDF a échoué.<br>Vérifier le fichier LaTeX...',
'The graph can be rendered as line or stacked chart. The latter is used when the stacked fields are defined. ': 'The graph can be rendered as line or stacked chart. The latter is used when the stacked fields are defined. ',
......@@ -744,6 +773,8 @@
'year': 'année',
'Year': 'Année',
'Year End': 'Année de fin',
'year of publication': 'année de publication',
'year of submission': 'année de soumission',
'Year of the publication': 'Année de la publication',
'Year Start': 'Année de début',
'You can not delete the row containing the undefined value.': 'Vous ne pouvez pas détruire la ligne contenant la valeur "indéfini".',
This diff is collapsed.
......@@ -19,6 +19,10 @@ FORMATS = ["bib", "html", "odt", "pdf", "tex"]
MODE_CHANGE_STATUS = "change status"
MODE_LOAD_IN_DB = "load in the database"
MONTH_SUBMIT = "month of submission"
YEAR_PUBLISH = "year of publication"
YEAR_SUBMIT = "year of submission"
class Selector(object):
"""Create virtual tables used to generate selector interfaces.
......@@ -220,6 +224,7 @@ class Selector(object):
year =
month_submit = T(MONTH_SUBMIT)
table = virtdb.define_table(
......@@ -232,7 +237,7 @@ class Selector(object):
Field("id_authors_roles", "reference authors_roles", label="Role"),
Field("id_graphs", "reference graphs", label="Graph"),
Field("cumulative", "boolean", default=True),
Field("time", "string", default=T("month")),
Field("time", "string", default=month_submit),
Field("format", "string", default="html", label="Format"))
table.id_authors_roles.requires = IS_IN_DB(db, "authors_roles.role")
......@@ -242,7 +247,9 @@ class Selector(object):
table.id_teams.requires = IS_IN_DB(db, "")
table.format.requires = IS_IN_SET(["html", "pdf", "png"])
table.time.requires = IS_IN_SET([T("month"), T("year")])
table.time.requires = \
return table
......@@ -415,7 +415,7 @@ class ViewportUi(object):
graph_panel = to_panelWithUrlSelector(
baseUrl=URL("graphs", "index"),
baseUrl=URL("graphs", "publications_versus_time"),
list_panel = to_panelWithUrlSelector(
# -*- coding: utf-8 -*-
""" NAME
fix_preference_0965 -- add dashboard_start_year
fix_preference_0965 [options]
In the limbra release, the preference dashboard_start_year
has been added.
This script add the this preference if it does not exist and with
its value to 2009
> cd limbra/scripts
> run script -S limbra_cppm
R. Le Gac -- May 2017
if __name__ == "__main__":
import sys
from plugin_dbui import get_id
DEF = "Starting year for the dashboard"
PROPERTY = "dashboard_start_year"
VALUE = 2009
idrec = get_id(db.preferences, property=PROPERTY)
if idrec is None:
print "\n\tAdd the property", PROPERTY
di = {
"property": PROPERTY,
"value": VALUE,
"definition": T(DEF)}
db.preferences[0] = di
rep = raw_input("\nCommit database changes [y/N]: ")
if rep == 'y':
print "\n\tProperty %s already defined. Exit.\n" % PROPERTY
# exit gently
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment