I would like to implement a live-update feature to the add column function based on the documentation on editable DataTable https://dash.plotly.com/datatable/editable such that a new column is added to the datatable when the CSV file is updated. I've got the callback working and I can add new columns as new CSV data is updated through live update but I've run into some problems. In my first attempt (labelled first code), I declared a global variable (which I know is bad in DASH) in an attempt to keep track of current CSV contents and the compare it to an updated CSV file to check for new data, but this version fails to load existing CVS data on initial start up and only adds new data. The second code, loads up existing CSV data into columns, but on live-update it simply adds duplicate columns. I cannot simply remake the whole datatable with a new CSV file because if there is user input in the cells, then that would be lost on update.
My question is therefore: How do I store the state of something (ie: number of lines of CSV data) without using global variables in Dash so that I have the ability to compare the existing state of a CSV file to a new state. I can't see how this is accomplished since assigned variables are reset on live-update. I've read about Hidden Div and storing data in a users browser session but I can't get it to work. Is that the direction I should be going or is there a more elegant solution?
html.Div([dash_table.DataTable(
id='editing-columns',
columns=[{
'name': 'Parameter',
'id': 'column1',
'deletable': True,
'renamable': True
}],
style_cell={
'whiteSpace': 'normal',
'height': 'auto',
'width': '20',
# 'minWidth': '50px',
'textAlign': 'left',
},
data=[
{'column1': j}
for j in table_contents
],
editable=True,
)], className="drug_input"),
#First code
#app.callback(Output('editing-columns', 'columns'),
[Input('graph-update', 'n_intervals')],
[State('editing-columns', 'columns')])
def update_columns(n, existing_columns):
check_length = []
global sierra
with open('assets' + '/' + 'record ' + str(current) + '.csv', 'r') as rf:
reader = csv.reader(rf)
for a in reader:
check_length.append(a[3])
if len(check_length) == 0:
return existing_columns
elif len(check_length) > sierra:
existing_columns.append({
'id': check_length[-1], 'name': check_length[-1],
'renamable': True, 'deletable': True
})
sierra = len(check_length)
return existing_columns
else:
return existing_columns
#Second code
#app.callback(Output('editing-columns', 'columns'),
Input('graph-update', 'n_intervals'),
State('editing-columns', 'columns'))
def update_columns(n, existing_columns):
counter = 0
with open('assets' + '/' + 'record ' + str(current) + '.csv', 'r') as rf:
reader = csv.reader(rf)
for a in reader:
counter = counter + 1
existing_columns.append({
'id': 'counter', 'name': a[3],
'renamable': True, 'deletable': True
})
return existing_columns
I've read about Hidden Div and storing data in a users browser session but I can't get it to work. Is that the direction I should be going or is there a more elegant solution?
Yes, you should use the store component for this.
You can store everything in a dict, with one key for your original file, and another key containing the modified one with user inputs. Make sure each callback reads in the store as a State. One should output to the table, and the other should output to the store.
Related
I have field in tarantool space I no longer need.
local space = box.schema.space.create('my_space', {if_not_exists = true})
space:format({
{'field_1', 'unsigned'},
{'field_2', 'unsigned'},
{'field_3', 'string'},
})
How to remove field_2 if it's indexed and if it's not indexed?
There is no any convenient way to do it.
The first way, just declare this field as nullable and insert NULL value to this field. Yes, it will be stored physically but you could hide them from users.
It's simple and not expensive.
The second way, write in-place migration. It's not possible if you have indexed fields after field you want to drop (in your example it's field_3).
And it's dangerous if you have a huge amount of data in this space.
local space = box.schema.space.create('my_space', {if_not_exists = true})
space:create_index('id', {parts = {{field = 1, type = 'unsigned'}}})
space:format({
{'field_1', 'unsigned'},
{'field_2', 'unsigned'},
{'field_3', 'string'},
})
-- Create key_def instance to simplify primary key extraction
local key_def = require('key_def').new(space.index[0].parts)
-- drop previous format
space:format({})
-- Migrate your data
for _, tuple in space:pairs() do
space:depete(key_def:extract_key(tuple))
space:replace({tuple[1], tuple[3]})
end
-- Setup new format
space:format({
{'field_1', 'unsigned'},
{'field_3', 'string'},
})
The third way is to create new space, migrate data into it and drop previous.
Still it's quite dangerous.
local space = box.schema.space.create('new_my_space', {if_not_exists = true})
space:create_index('id', {parts = {{field = 1, type = 'unsigned'}}})
space:format({
{'field_1', 'unsigned'},
{'field_3', 'string'},
})
-- Migrate your data
for _, tuple in box.space['my_space']:pairs() do
space:replace({tuple[1], tuple[3]})
end
-- Drop the old space
box.space['my_space']:drop()
-- Rename new space
local space_id = box.space._space.index.name:get({'my_new_space'}).id
-- In newer version of Tarantool (2.6+) space.alter method available
-- But in older versions you could update name via system "_space" space
box.space._space:update({space_id}, {{'=', 'name', 'my_space'}})
JSBIN Sample
I have a changeable set of child components (POJO object) that each have its own state stream. Each time a user triggers addChild/removeChild/clearChildren, a new set of children state streams is emitted with #switchMap. So far so good! (And so amazed by RxJS!)
With Rx.Observable.from(arrayOfStateStreams).combineAll() I get a good result as long as the arrayOfStateStreams isn't an empty array.
Since this is a partial state that is combined(Latest) on a higher level, I need to get an empty array emitted or the global state tree will contain old state data that is no longer true!
I can emit some reserved token like ['EMPTY-ARRAY-PLACEHOLDER-TOKEN'], but that's just weird.
A better way would be to always append one last stream into the array so the last index can be considered trash. Still confusing code and state though.
Using [null] is not OK, since we could have a child state of 'null'.
Anyone who can solve this in a good way? Can't this be supported since there should be no other representation of an empty array after #combineAll?
Credits go to github user trxcllnt who provided the following answer:
combineAll won't emit unless the combined Observables emit at least
one value, but you could check to ensure the collection you're
combining is empty or not, and either combine or emit an empty Array:
var arrayOfStreamsStream = Rx.Observable
.of(
[], [
Rx.Observable.of('blah-1'), // component state.
Rx.Observable.of('blah-2'),
Rx.Observable.of('blah-3')
], [], [
Rx.Observable.of('foo-1'),
Rx.Observable.of('qux-2')
]
)
.switchMap(function onMap(coll) {
return coll.length === 0 ?
Observable.of(coll) :
Observable.combineLatest(...coll);
})
.subscribe(function onSubscribe(data) {
console.log('onSubscribe START')
console.dir(data)
console.log('onSubscribe END')
})
This has nothing to do with combineAll. The problem is that Observable.from results in nothing (an empty observable) when passed an empty array.
The only viable solution that I can think of if you have to get a result from an empty array is to return something else in that case.
Ann example to illustrate the problem and a possible solution.
var data = [1, 2, 3, 4, 5];
log('With data: ');
Rx.Observable.from(data)
.subscribe(function (d) { log('data: ' + d); });
// Prints:
// With data:
// data: 1
// data: 2
// data: 3
// data: 4
// data: 5
var data = [];
log('Without data: ');
var nullDataObject = { msg: 'my null data object' };
Rx.Observable.from(data.length == 0 ? [nullDataObject] : data)
.subscribe(function (d) { log('data: ' + d); });
// Prints:
// With data:
// data: [object Object]
Runnable example on jsfiddle.
When consuming this you simply filter away the object representing an empty array where appropriate.
a possible workaround is to just pipe it with startWith();
combineLatest(potentiallyEmptyArray).pipe(
startWith<any>([])
);
Note: Similar issues exist with combineLatest() (the static version) which can be solved using defaultIfEmpty() - which works, but it screws up the typing of the output.
// array of Observables
const animals: Observable<{ species: 'dog' | 'cat' }>[] = [];
// Type '{ species: "dog" | "cat"; }[]' is not assignable to type 'never[]'.
combineLatest(animals).pipe(defaultIfEmpty([]));
In TypeScript you need to either know the type of the object or use <any>[] which means you then lose typing completely.
If you have a concrete type you can use one of these:
defaultIfEmpty<Animal[]>([])
defaultIfEmpty([] as Animal[])
I often don't have a concrete type for the return value of an observable. So I came up with an operator:
export const emptyArrayIfEmpty = () => <T>(observable: Observable<T[]>) =>
observable.pipe(defaultIfEmpty([] as T[]));
Then I can add the following and get out an empty array if animals === [] without losing any typing information:
combineLatest(animals).pipe(emptyArrayIfEmpty());
I am trying to make a page in my website where I want to use this select2 (http://ivaynberg.github.io/select2/) plugin. My requirement is to include same "words" multiple times.
My tags could be:
Monthly_Rent, Monthly_Rent, commission, mortgage, commission
Also when user loads the page, I want to maintain the order how user selected it before saving.
Currently when I add any option, its removed from the list. How can I add it again?
Another issue is, now if I want to remove "commission" which is before "mortgage", it should not delete another "commission" word which is at last.
Please help me understand how to achieve this.
just use a counter and a query function to provide data :
var fruits = ['apple', 'pear', 'orange', 'strawberry']
var counter = 0
$("#yourinput").select2({
query: function(query) {
var data = { results: []};
for (var i = 0; i < fruits.length; i++) {
data.results.push({"id": fruits[i] + "/" + counter, "text": fuits[i]});
}
counter += 1;
query.callback(data);
},
formatSelection: function(item) {
return item.text; // display apple, pear, ...
},
formatResult: function(item) {
return item.id; // display apple/1, pear/2, ... Return item.text to see apple, pear, ...
},
multiple: true,
closeOnSelect: true
}
So, the first time you click on your select box, the data are initialized with apple/1, pear/1, ... The next time, you get apple/2, pear/2, ..., next apple/3, ... and so on.
Each time you select a fruit, you get an different id, even you choose same fruit you have previously chosen. Keeping an unique counter you increment at each select allow you to delete a fruit without side effect : its id disappeared, and will not be reused.
closeOnSelect is set to true, to increment counter at each selection (more precisely, each time you open the select box). If you set it to false, when you select a fruit, it disappears from list, and you cannot select two times same fruit, except if you close the box.
When you validate your form, just remove trailing "/xx" to get the correct id.
I hope it's that you want.
Denis
I have local data in a grid. How can I get all of the rows or IDs that are not removed after a user uses the filter toolbar? I need to get all filtered rows, regardless of pagination.
For example, say I begin with 50 rows in the grid. The user uses the filter toolbar and the set of rows decreases to 10 rows. How can I get those ten rows?
There are no direct way to get the information which you need. Internally jqGrid uses $.jgrid.from to filter local data. The main code which uses $.jgrid.from in inside of addLocalData. To get results which you need without studying all the code I suggest to use the fact that all filtered data will be returned by select method of $.jgrid.from (see the line of code). My suggestion is to catch the data before the data will be cut to the page size.
To do this I suggest to use sub-classing: overwriting of the method select method of $.jgrid.from. I demonstrate the technique in the examples created for the answer and this one.
In your case the code will be
var oldFrom = $.jgrid.from,
lastSelected;
$.jgrid.from = function (source, initalQuery) {
var result = oldFrom.call(this, source, initalQuery),
old_select = result.select;
result.select = function (f) {
lastSelected = old_select.call(this, f);
return lastSelected;
};
return result;
};
Now the variable lastSelected will save the array of elements which are results of the last sorting or filtering operation. Because $.jgrid.from is global the data are not connected to the grid. If you have more as one grid on the page it will be uncomfortable. One can fix the small disadvantage with the following line in the code of loadComplate of every grid:
loadComplete: function () {
this.p.lastSelected = lastSelected; // set this.p.lastSelected
}
In the way we introduce new jqGrid parameter lastSelected which will have close structure as data parameter, but will hold only last filtered data.
The following code will display the ids of filtered data in alert message
$("#getIds").click(function () {
var filteredData = $grid.jqGrid('getGridParam', 'lastSelected'), i, n, ids = [],
idName = $grid.jqGrid('getGridParam', 'localReader').id;
if (filteredData) {
for (i = 0, n = filteredData.length; i < n; i++) {
ids.push(filteredData[i][idName]);
}
alert("tolal number of filtered data: " + n + "\n" +
"ids of filtered data:\n" + ids.join(', '));
}
});
I used localReader.id parameter because property name used for local data are typically id or _id_. The _id_ will be used in case of data loaded from the server if one uses loadonce: true option.
The demo demonstrate the approach. If one filter for example only the data from FedEx and then clicks on "Show Ids" button one will see information about all filtered and not only about the data displayed on the current page:
UPDATED: free jqGrid provides new lastSelectedData option. See the demo in the list of demos.
You colud use afterSearch option of the search toolbar:
var filteredIDs = new Array(); //Global variable
$("#"+gridId).jqGrid("filterToolbar", { stringResult:true, searchOnEnter:false,
afterSearch:function(){
filteredIDs = $("#"+gridId).getDataIDs();
}
});
If you want to get the filtered rows instead the filtered IDs, use getRowData() instead of getDataIDs().
All, I found another answer which is far easier to include
loadComplete: function (gridData) {
var isSearchPerformed = $grid.getGridParam("postData")._search;
if (isSearchPerformed) {
$("#spanFilterTotal").text(gridData.records);
}
All you want is below:
$.each($grid.getRowData(), function( index, value ) {
a.push(value["COLUMN_NAME"]); //Get the selected data you want
});
I currently use the statistics module (core) on my Drupal 6 install. This increments a count in the {node_counter} table every time the node is viewed, and this works.
My question is - can I programmatically increment this counter as well? I am looking to achieve this when users interact with content created from views (say click a lightbox), so being able to update the table with AJAX would be ideal.
I have done a quick search on d.o and there doesn't appear to be any modules that stick out straight away. Does anyone have any experience with this?
It shouldn't be hard to make a custom module for this.
The query that the statistics module runs is:
db_query('UPDATE {node_counter} SET daycount = daycount + 1, totalcount = totalcount + 1, timestamp = %d WHERE nid = %d', time(), arg(1));
// If we affected 0 rows, this is the first time viewing the node.
if (!db_affected_rows()) {
// We must create a new row to store counters for the new node.
db_query('INSERT INTO {node_counter} (nid, daycount, totalcount, timestamp) VALUES (%d, 1, 1, %d)', arg(1), time());
}
The only thing we need to do, is to replace arg(1) with the node id we want to add a count to this could be done in a custom module something like this.
function custom_module_menu() {
$items['custom/ajax/%node'] = array(
'title' => 'Update count',
'page callback' => 'custom_module_update_counter',
'page arguments' => array(2),
'access callback' => array('custom_module_access_control'),
);
function custom_module_update_counter($node) {
db_query('UPDATE {node_counter} SET daycount = daycount + 1, totalcount = totalcount + 1, timestamp = %d WHERE nid = %d', time(), $node->nid);
// If we affected 0 rows, this is the first time viewing the node.
if (!db_affected_rows()) {
// We must create a new row to store counters for the new node.
db_query('INSERT INTO {node_counter} (nid, daycount, totalcount, timestamp) VALUES (%d, 1, 1, %d)', $node->nid, time());
}
}
All that's left is to implement a custom access control function, you can check if the request is ajax or make whatever control you like, the function must just return TRUE or FALSE. You also need to make a ajax event with the node id in your setting, but that shouldn't be too hard either.
You need to hit the url custom/ajax/2 to update node with id 2 etc.