Merge pull request #1774 from rmehta/list-settings

List Settings: Save user filters across sessions and list/report views
This commit is contained in:
Rushabh Mehta 2016-05-31 18:28:01 +05:30
commit fe6de7981d
23 changed files with 366 additions and 119 deletions

View file

@ -6,7 +6,6 @@ import json
import frappe
import frappe.handler
import frappe.client
import frappe.desk.reportview
from frappe.utils.response import build_response
from frappe import _

View file

@ -4,15 +4,14 @@
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.desk.reportview import execute as runreport
from frappe.utils import getdate
def execute(filters=None):
priority_map = {"High": 3, "Medium": 2, "Low": 1}
todo_list = runreport(doctype="ToDo", fields=["name", "date", "description",
todo_list = frappe.get_list('ToDo', fields=["name", "date", "description",
"priority", "reference_type", "reference_name", "assigned_by", "owner"],
filters=[["ToDo", "status", "=", "Open"]])
filters={'status': 'Open'})
todo_list.sort(key=lambda todo: (priority_map.get(todo.priority, 0),
todo.date and getdate(todo.date) or getdate("1900-01-01")), reverse=True)

View file

@ -7,6 +7,7 @@ import frappe.utils
import frappe.share
import frappe.defaults
import frappe.desk.form.meta
from frappe.model.utils.list_settings import get_list_settings
from frappe.permissions import get_doc_permissions
from frappe import _
@ -54,6 +55,8 @@ def getdoctype(doctype, with_parent=False, cached_timestamp=None):
"""load doctype"""
docs = []
parent_dt = None
# with parent (called from report builder)
if with_parent:
parent_dt = frappe.model.meta.get_parent_dt(doctype)
@ -65,6 +68,7 @@ def getdoctype(doctype, with_parent=False, cached_timestamp=None):
docs = get_meta_bundle(doctype)
frappe.response['user_permissions'] = get_user_permissions(docs)
frappe.response['list_settings'] = get_list_settings(parent_dt or doctype)
if cached_timestamp and docs[0].modified==cached_timestamp:
return "use_cache"

View file

@ -60,7 +60,6 @@ def add_comment(doc):
@frappe.whitelist()
def get_next(doctype, value, prev, filters=None, order_by="modified desc"):
import frappe.desk.reportview
prev = not int(prev)
sort_field, sort_order = order_by.split(" ")
@ -82,7 +81,7 @@ def get_next(doctype, value, prev, filters=None, order_by="modified desc"):
if not order_by[0] in [f[1] for f in filters]:
filters.append([doctype, sort_field, condition, value])
res = frappe.desk.reportview.execute(doctype,
res = frappe.get_list(doctype,
fields = ["name"],
filters = filters,
order_by = sort_field + " " + sort_order,

View file

@ -7,11 +7,34 @@ from __future__ import unicode_literals
import frappe, json
import frappe.permissions
from frappe.model.db_query import DatabaseQuery
from frappe.model.utils.list_settings import update_list_settings
from frappe.utils import cint
from frappe import _
@frappe.whitelist()
def get():
return compress(execute(**get_form_params()))
args = get_form_params()
save_list_settings_fields = False
if args.save_list_settings_fields:
save_list_settings_fields = True
del args['save_list_settings_fields']
data = compress(execute(**args))
# update list settings if new search
if not cint(args.limit_start) or cint(args.limit or args.limit_page_length) != 20:
list_settings = {
'filters': args.filters,
'limit': args.limit or args.limit_page_length,
'order_by': args.order_by
}
if save_list_settings_fields:
list_settings['fields'] = args.fields
update_list_settings(args.doctype, list_settings)
return data
def execute(doctype, *args, **kwargs):
return DatabaseQuery(doctype).execute(*args, **kwargs)

View file

@ -4,7 +4,6 @@
# Search
from __future__ import unicode_literals
import frappe
import frappe.desk.reportview
from frappe.utils import cstr, unique
# this is called by the Link Field
@ -83,7 +82,7 @@ def search_widget(doctype, txt, query=None, searchfield=None, start=0,
fields.append("""locate("{_txt}", `tab{doctype}`.`name`) as `_relevance`""".format(
_txt=frappe.db.escape((txt or "").replace("%", "")), doctype=frappe.db.escape(doctype)))
values = frappe.desk.reportview.execute(doctype,
values = frappe.get_list(doctype,
filters=filters, fields=fields,
or_filters = or_filters, limit_start = start,
limit_page_length=page_len,

View file

@ -113,6 +113,7 @@ scheduler_events = {
"frappe.email.doctype.email_account.email_account.notify_unreplied",
"frappe.utils.error.collect_error_snapshots",
"frappe.model.utils.link_count.update_link_count",
'frappe.model.utils.list_settings.sync_list_settings'
],
"daily": [
"frappe.email.bulk.clear_outbox",

View file

@ -39,6 +39,8 @@ def install_db(root_login="root", root_password=None, db_name=None, source_sql=N
remove_missing_apps()
create_auth_table()
create_list_settings_table()
frappe.flags.in_install_db = False
def get_current_host():
@ -72,6 +74,14 @@ def create_auth_table():
`password` VARCHAR(180) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8""")
def create_list_settings_table():
frappe.db.sql_ddl("""create table if not exists __ListSettings (
`user` VARCHAR(180) NOT NULL,
`doctype` VARCHAR(180) NOT NULL,
`data` TEXT,
UNIQUE(user, doctype)
) ENGINE=InnoDB DEFAULT CHARSET=utf8""")
def import_db_from_sql(source_sql, verbose):
if verbose: print "Starting database import..."
db_name = frappe.conf.db_name
@ -342,7 +352,7 @@ def check_if_ready_for_barracuda():
def extract_sql_gzip(sql_gz_path):
success = -1
try:
success = subprocess.check_output(['gzip', '-d', '-v', '-f', sql_gz_path])
subprocess.check_output(['gzip', '-d', '-v', '-f', sql_gz_path])
except Exception as subprocess.CalledProcessError:
print subprocess.CalledProcessError.output
finally:

View file

@ -0,0 +1,30 @@
import frappe, json
def get_list_settings(doctype, for_update=False):
list_settings = frappe.cache().hget('_list_settings',
'{0}::{1}'.format(doctype, frappe.session.user))
if list_settings is None:
list_settings = frappe.db.sql('''select * from __ListSettings
where user=%s and doctype=%s''', (frappe.session.user, doctype), as_dict=True)
list_settings = list_settings and list_settings[0] or '{}'
if not for_update:
update_list_settings(doctype, list_settings)
return list_settings
def update_list_settings(doctype, list_settings):
'''update list settings in cache'''
current = json.loads(get_list_settings(doctype, for_update = True))
current.update(list_settings)
frappe.cache().hset('_list_settings', '{0}::{1}'.format(doctype, frappe.session.user),
json.dumps(current))
def sync_list_settings():
'''Sync from cache to database (called asynchronously via the browser)'''
for key, data in frappe.cache().hgetall('_list_settings').iteritems():
doctype, user = key.split('::')
frappe.db.sql('''insert into __ListSettings (user, doctype, data) values (%s, %s, %s)
on duplicate key update data=%s''', (user, doctype, data, data))

View file

@ -126,3 +126,4 @@ frappe.patches.v7_0.set_user_fullname
frappe.patches.v7_0.desktop_icons_hidden_by_admin_as_blocked
frappe.patches.v7_0.add_communication_in_doc
frappe.patches.v7_0.update_send_after_in_bulk_email
frappe.patches.v7_0.setup_list_settings

View file

@ -0,0 +1,16 @@
from frappe.installer import create_list_settings_table
from frappe.model.utils.list_settings import update_list_settings
import frappe, json
def execute():
create_list_settings_table()
for user in frappe.db.get_all('User', {'user_type': 'System User'}):
defaults = frappe.defaults.get_defaults_for(user.name)
for key, value in defaults.iteritems():
if key.startswith('_list_settings:'):
doctype = key.replace('_list_settings:', '')
columns = ['`tab{1}`.`{0}`'.format(*c) for c in json.loads(value)]
update_list_settings(doctype, {'fields': columns})

View file

@ -1,3 +1,6 @@
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}
a {
cursor: pointer;
}

View file

@ -1,3 +1,6 @@
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}
a {
cursor: pointer;
}

View file

@ -1,3 +1,6 @@
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}
a {
cursor: pointer;
}

View file

@ -289,3 +289,4 @@ frappe.dom.set_box_shadow = function(ele, spread) {
return this;
}
})(jQuery);

View file

@ -118,6 +118,7 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
this.can_delete = frappe.model.can_delete(this.doctype);
this.meta = locals.DocType[this.doctype];
this.$page.find('.frappe-list-area').empty(),
this.init_list_settings();
this.setup_listview();
this.init_list(false);
this.init_menu();
@ -194,14 +195,19 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
init_filters: function() {
var me = this;
if(this.listview.settings.filters) {
$.each(this.listview.settings.filters, function(i, f) {
var set_filters = function(filters) {
$.each(filters, function(i, f) {
if(f.length===3) {
f = [me.doctype, f[0], f[1], f[2]]
}
me.filter_list.add_filter(f[0], f[1], f[2], f[3]);
});
}
if(this.list_settings.filters) {
set_filters(this.list_settings.filters);
} else if(this.listview.settings.filters) {
set_filters(this.listview.settings.filters);
}
},
init_sort_selector: function() {
@ -210,6 +216,25 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
if(this.listview.sort_selector) {
args = this.listview.sort_selector;
}
if(this.list_settings.order_by) {
// last saved settings
var order_by = this.list_settings.order_by
if(order_by.indexOf('`.`')!==-1) {
// scrub table name (separted by dot), like `tabTime Log`.`modified` desc`
order_by = order_by.split('.')[1];
}
parts = order_by.split(' ');
if(parts.length===2) {
var fieldname = strip(parts[0], '`');
args = {
sort_by: fieldname,
sort_order: parts[1]
}
}
}
this.sort_selector = new frappe.ui.SortSelector({
parent: this.wrapper.find('.list-filters'),
doctype: this.doctype,
@ -234,7 +259,7 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
setup_listview: function() {
this.listview = frappe.views.get_listview(this.doctype, this);
this.wrapper = this.$page.find('.frappe-list-area');
this.page_length = 20;
this.page_length = this.list_settings.limit || 20;
this.allow_delete = true;
},
init_list: function(auto_run) {
@ -242,6 +267,7 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
// init list
this.make({
method: 'frappe.desk.reportview.get',
save_list_settings: true,
get_args: this.get_args,
parent: this.wrapper,
freeze: true,
@ -278,10 +304,8 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
this.listview.settings.refresh(this);
}
if(frappe.route_options) {
this.set_route_options();
this.run();
} else if(this.dirty) {
this.set_filters_before_run();
if(this.dirty) {
this.run();
} else {
if(new Date() - (this.last_updated_on || 0) > 30000) {
@ -291,57 +315,69 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
}
},
set_route_options: function() {
set_filters_before_run: function() {
// set filters from frappe.route_options
// before switching pages, frappe.route_options can have pre-set filters
// for the list view
var me = this;
me.filter_list.clear_filters();
$.each(frappe.route_options, function(key, value) {
var doctype = null;
// if `Child DocType.fieldname`
if (key.indexOf(".")!==-1) {
doctype = key.split(".")[0];
key = key.split(".")[1];
}
if(frappe.route_options) {
this.filter_list.clear_filters();
$.each(frappe.route_options, function(key, value) {
var doctype = null;
// find the table in which the key exists
// for example the filter could be {"item_code": "X"}
// where item_code is in the child table.
// if `Child DocType.fieldname`
if (key.indexOf(".")!==-1) {
doctype = key.split(".")[0];
key = key.split(".")[1];
}
// we can search all tables for mapping the doctype
if(!doctype) {
if(in_list(frappe.model.std_fields_list, key)) {
// standard
doctype = me.doctype;
} else if(frappe.meta.has_field(me.doctype, key)) {
// found in parent
doctype = me.doctype;
} else {
frappe.meta.get_table_fields(me.doctype).every(function(d) {
if(frappe.meta.has_field(d.options, key)) {
doctype = d.options;
return false;
// find the table in which the key exists
// for example the filter could be {"item_code": "X"}
// where item_code is in the child table.
// we can search all tables for mapping the doctype
if(!doctype) {
if(in_list(frappe.model.std_fields_list, key)) {
// standard
doctype = me.doctype;
} else if(frappe.meta.has_field(me.doctype, key)) {
// found in parent
doctype = me.doctype;
} else {
frappe.meta.get_table_fields(me.doctype).every(function(d) {
if(frappe.meta.has_field(d.options, key)) {
doctype = d.options;
return false;
}
});
if(!doctype) {
frappe.msgprint(__('Warning: Unable to find {0} in any table related to {1}', [
key, __(me.doctype)]));
}
});
if(!doctype) {
frappe.msgprint(__('Warning: Unable to find {0} in any table related to {1}', [
key, __(me.doctype)]));
}
}
}
if(doctype) {
if($.isArray(value)) {
me.filter_list.add_filter(doctype, key, value[0], value[1]);
} else {
me.filter_list.add_filter(doctype, key, "=", value);
if(doctype) {
if($.isArray(value)) {
me.filter_list.add_filter(doctype, key, value[0], value[1]);
} else {
me.filter_list.add_filter(doctype, key, "=", value);
}
}
}
});
frappe.route_options = null;
});
frappe.route_options = null;
this.dirty = true;
} else if(this.list_settings && this.list_settings.filters
&& this.list_settings.updated_on != this.list_settings_updated_on) {
// update remembered list settings
this.filter_list.clear_filters();
this.list_settings.filters.forEach(function(f) {
me.filter_list.add_filter(f[0], f[1], f[2], f[3]);
});
this.dirty = true;
}
},
run: function(more) {
@ -390,6 +426,8 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
if(this.listview.settings.post_render) {
this.listview.settings.post_render(this);
}
this.list_settings_updated_on = this.list_settings.updated_on;
},
make_no_result: function() {

View file

@ -325,6 +325,26 @@ frappe.utils = {
return list.reduce(function(previous_value, current_value) { return flt(previous_value) + flt(current_value); }, 0.0);
},
arrays_equal: function(arr1, arr2) {
if (!arr1 || !arr2) {
return false;
}
if (arr1.length != arr2.length) {
return false;
}
for (var i = 0; i < arr1.length; i++) {
if ($.isArray(arr1[i])) {
if (!frappe.utils.arrays_equal(arr1[i], arr2[i])) {
return false;
}
}
else if (arr1[i] !== arr2[i]) {
return false;
}
}
return true;
},
intersection: function(a, b) {
// from stackoverflow: http://stackoverflow.com/questions/1885557/simplest-code-for-array-intersection-in-javascript
/* finds the intersection of

View file

@ -33,6 +33,7 @@ $.extend(frappe.model, {
new_names: {},
events: {},
list_settings: {},
init: function() {
// setup refresh if the document is updated somewhere else
@ -113,6 +114,12 @@ $.extend(frappe.model, {
}
frappe.model.init_doctype(doctype);
frappe.defaults.set_user_permissions(r.user_permissions);
if(r.list_settings) {
// remember filters and other settings from last view
frappe.model.list_settings[doctype] = JSON.parse(r.list_settings);
frappe.model.list_settings[doctype].updated_on = moment().toString();
}
callback && callback(r);
}
});

View file

@ -52,7 +52,7 @@ frappe.ui.Listing = Class.extend({
this.opts.no_result_message = __('Nothing to show');
}
if(!this.opts.page_length) {
this.opts.page_length = 20;
this.opts.page_length = this.list_settings ? (this.list_settings.limit || 20) : 20;
}
this.opts._more = __("More");
},
@ -174,14 +174,18 @@ frappe.ui.Listing = Class.extend({
if(this.onreset) this.onreset();
}
if(!me.opts.no_loading)
if(!me.opts.no_loading) {
me.set_working(true);
}
var args = this.get_call_args();
this.save_list_settings_locally(args);
return frappe.call({
method: this.opts.method || 'frappe.desk.query_builder.runquery',
type: "GET",
freeze: (this.opts.freeze != undefined ? this.opts.freeze : true),
args: this.get_call_args(),
args: args,
callback: function(r) {
if(!me.opts.no_loading)
me.set_working(false);
@ -191,6 +195,39 @@ frappe.ui.Listing = Class.extend({
no_spinner: this.opts.no_loading
});
},
save_list_settings_locally: function(args) {
if(this.opts.save_list_settings && this.doctype && !this.docname) {
// save list settings locally
list_settings = frappe.model.list_settings[this.doctype];
var different = false;
if(!frappe.utils.arrays_equal(args.filters, list_settings.filters)) {
// settings are dirty if filters change
list_settings.filters = args.filters || [];
different = true;
}
if(list_settings.order_by !== args.order_by) {
list_settings.order_by = args.order_by;
different = true;
}
if(list_settings.limit != args.limit_page_length) {
list_settings.limit = args.limit_page_length || 20
different = true;
}
// save fields in list settings
if(args.save_list_settings_fields) {
list_settings.fields = args.fields;
};
if(different) {
list_settings.updated_on = moment().toString();
}
}
},
set_working: function(flag) {
this.$w.find('.img-load').toggle(flag);
},
@ -325,5 +362,12 @@ frappe.ui.Listing = Class.extend({
}
}
return this;
}
},
init_list_settings: function() {
if(frappe.model.list_settings[this.doctype]) {
this.list_settings = frappe.model.list_settings[this.doctype];
} else {
this.list_settings = {};
}
},
});

View file

@ -42,11 +42,12 @@ frappe.ui.SortSelector = Class.extend({
},
prepare_args: function() {
var me = this;
// make from doctype if not given
if(!this.args && this.doctype) {
this.setup_from_doctype();
if(!this.args) {
this.args = {};
}
this.setup_from_doctype();
// if label is missing, add from options
if(this.args.sort_by && !this.args.sort_by_label) {
this.args.options.every(function(o) {
@ -57,67 +58,73 @@ frappe.ui.SortSelector = Class.extend({
}
},
setup_from_doctype: function() {
var args = {};
var me = this;
var meta = frappe.get_meta(this.doctype);
if(meta.sort_field) {
if(meta.sort_field.indexOf(',')!==-1) {
parts = meta.sort_field.split(',')[0].split(' ');
args.sort_by = parts[0];
args.sort_order = parts[1];
if(!this.args.sort_by) {
if(meta.sort_field) {
if(meta.sort_field.indexOf(',')!==-1) {
parts = meta.sort_field.split(',')[0].split(' ');
this.args.sort_by = parts[0];
this.args.sort_order = parts[1];
} else {
this.args.sort_by = meta.sort_field;
this.args.sort_order = meta.sort_order.toLowerCase();
}
} else {
args.sort_by = meta.sort_field;
args.sort_order = meta.sort_order.toLowerCase();
// default
this.args.sort_by = 'modified';
this.args.sort_order = 'desc';
}
} else {
// default
args.sort_by = 'modified';
args.sort_order = 'desc';
}
args.sort_by_label = this.get_label(args.sort_by);
// default options
args._options = [
{'fieldname': 'modified'},
]
// title field
if(meta.title_field) {
args._options.push({'fieldname': meta.title_field});
}
// bold or mandatory
meta.fields.forEach(function(df) {
if(df.mandatory || df.bold) {
args._options.push({fieldname: df.fieldname, label: df.label});
}
});
if(!this.args.sort_by_label) {
this.args.sort_by_label = this.get_label(this.args.sort_by);
}
args._options.push({'fieldname': 'name'});
args._options.push({'fieldname': 'creation'});
args._options.push({'fieldname': 'idx'});
if(!this.args.options) {
// default options
var _options = [
{'fieldname': 'modified'},
]
// de-duplicate
var added = [];
args.options = [];
args._options.forEach(function(o) {
if(added.indexOf(o.fieldname)===-1) {
args.options.push(o);
added.push(o.fieldname);
// title field
if(meta.title_field) {
_options.push({'fieldname': meta.title_field});
}
});
// add missing labels
args.options.forEach(function(o) {
if(!o.label) {
o.label = me.get_label(o.fieldname);
}
});
// bold or mandatory
meta.fields.forEach(function(df) {
if(df.mandatory || df.bold) {
_options.push({fieldname: df.fieldname, label: df.label});
}
});
_options.push({'fieldname': 'name'});
_options.push({'fieldname': 'creation'});
_options.push({'fieldname': 'idx'});
// de-duplicate
var added = [];
this.args.options = [];
_options.forEach(function(o) {
if(added.indexOf(o.fieldname)===-1) {
me.args.options.push(o);
added.push(o.fieldname);
}
});
// add missing labels
this.args.options.forEach(function(o) {
if(!o.label) {
o.label = me.get_label(o.fieldname);
}
});
}
// set default
this.args = args;
this.sort_by = args.sort_by;
this.sort_order = args.sort_order;
this.sort_by = this.args.sort_by;
this.sort_order = this.args.sort_order;
},
get_label: function(fieldname) {
if(fieldname==='idx') {

View file

@ -30,7 +30,7 @@ frappe.views.ReportViewPage = Class.extend({
me.parent.reportview.run();
});
} else {
me.parent.reportview.set_route_filters();
me.parent.reportview.set_route_filters(true);
me.parent.reportview.run();
}
});
@ -89,10 +89,12 @@ frappe.views.ReportView = frappe.ui.Listing.extend({
var me = this;
this.page = this.parent.page;
this.page_title = __('Report')+ ': ' + __(this.docname ? (this.doctype + ' - ' + this.docname) : this.doctype);
this.page.set_title(this.page_title)
this.page.set_title(this.page_title);
this.init_list_settings();
this.make({
page: this.parent.page,
method: 'frappe.desk.reportview.get',
save_list_settings: true,
get_args: this.get_args,
parent: this.page.main,
start: 0,
@ -130,8 +132,17 @@ frappe.views.ReportView = frappe.ui.Listing.extend({
set_init_columns: function() {
// pre-select mandatory columns
var columns = frappe.defaults.get_default("_list_settings:" + this.doctype);
if(!columns) {
var me = this;
var columns = [];
if(this.list_settings.fields) {
this.list_settings.fields.forEach(function(field) {
var coldef = me.get_column_info_from_field(field);
if(!in_list(['_seen', '_comments', '_user_tags', '_assign', '_liked_by', 'docstatus'], coldef[0])) {
columns.push(coldef);
}
});
};
if(!columns.length) {
var columns = [['name', this.doctype],];
$.each(frappe.meta.docfield_list[this.doctype], function(i, df) {
if((df.in_filter || df.in_list_view) && df.fieldname!='naming_series'
@ -168,7 +179,7 @@ frappe.views.ReportView = frappe.ui.Listing.extend({
if(opts.sort_order_next) this.sort_order_next_select.val(opts.sort_order_next);
},
set_route_filters: function() {
set_route_filters: function(first_load) {
var me = this;
if(frappe.route_options) {
me.filter_list.clear_filters();
@ -177,7 +188,16 @@ frappe.views.ReportView = frappe.ui.Listing.extend({
});
frappe.route_options = null;
return true;
} else if(this.list_settings && this.list_settings.filters &&
(this.list_settings.updated_on != this.list_settings_updated_on)) {
// list settings (previous settings)
this.filter_list.clear_filters();
$.each(this.list_settings.filters, function(i, f) {
me.filter_list.add_filter(f[0], f[1], f[2], f[3]);
});
return true;
}
this.list_settings_updated_on = this.list_settings.updated_on;
},
setup_print: function() {
@ -196,6 +216,7 @@ frappe.views.ReportView = frappe.ui.Listing.extend({
fields: $.map(this.columns, function(v) { return me.get_full_column_name(v) }),
order_by: this.get_order_by(),
filters: this.filter_list.get_filters(),
save_list_settings_fields: 1,
with_childnames: 1
}
},
@ -229,6 +250,15 @@ frappe.views.ReportView = frappe.ui.Listing.extend({
return (v[1] ? ('`tab' + v[1] + '`') : this.tab_name) + '.`' + v[0] + '`';
},
get_column_info_from_field: function(t) {
if(t.indexOf('.')===-1) {
return [strip(t, '`'), this.doctype];
} else {
var parts = t.split('.');
return [strip(parts[1], '`'), strip(parts[0], '`').substr(3)];
}
},
// build columns for slickgrid
build_columns: function() {
var me = this;
@ -706,7 +736,6 @@ frappe.ui.ColumnPicker = Class.extend({
: null;
});
frappe.defaults.set_default("_list_settings:" + this.doctype, columns);
this.list.columns = columns;
this.list.run();
}

View file

@ -7,6 +7,13 @@
// font-family: "Open Sans", "Helvetica", Arial, "sans-serif";
// }
body {
font-family: -apple-system, BlinkMacSystemFont,
"Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell",
"Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
}
a {
cursor: pointer;
}

View file

@ -2,7 +2,7 @@
# MIT License. See license.txt
from __future__ import unicode_literals
import redis, frappe, re, copy
import redis, frappe, re
import cPickle as pickle
from frappe.utils import cstr
@ -126,6 +126,10 @@ class RedisWrapper(redis.Redis):
except redis.exceptions.ConnectionError:
pass
def hgetall(self, name):
return {key: pickle.loads(value) for key, value in
super(redis.Redis, self).hgetall(self.make_key(name)).iteritems()}
def hget(self, name, key, generator=None):
if not name in frappe.local.cache:
frappe.local.cache[name] = {}