Ordering a Rails DataTable column by another value - ruby-on-rails

I am building a helpdesk application. I have a model called TicketDetail, with a table which uses datatables to get its data via JSON. This is in order to periodically recalculate the time a ticket has been open. The time taken is formatted by a simple helper so it's in the format "dd:hh:mm", but it should be sorted by the time (stored as a decimal) multiplied by a weighting. Here's the datatables definition
var table = $('#ticket_details').DataTable({
order: [[ 8, "desc" ], [ 9, "desc" ], [ 2, "asc" ]],
stateSave: true,
deferRender: true,
ajax: $('#ticket_details').data('source'),
"columns": [
{ "data": "reference_number" },
{ "data": "location" },
{ "data": "title" },
{ "data": "parent", className: "hidden-md hidden-sm hidden-xs" },
{ "data": { _:"time_display.time", sort: "time_display.decimal_time"}},
{ "data": "created_by", className: "hidden-md hidden-sm hidden-xs" }
]
} );
setInterval( function () {
table.ajax.reload( null, false ); }, 60000 );
Here's a simplified sample record, where the ticket has been open 3 days and 6 hours, with a weighting of x2 (i.e. 3.25 * 2 = 6.5:
{
data: [
{
id: 140,
parent: null,
title: "[",
location: "Bond St",
ticket_sla: "16 Hours",
reference_number: "1606210001",
ticket_sla_weighting: 2,
time_display: {
time: "<span class = "label label-danger">03:06:00</span>",
decimal_time: 6.5
}
]
}
The problem is that the datatable sorts correctly if I display the decimal_time, but as soon as I put the formatted time in the class, it sorts simply by the number of days, immediately to the left of the colon. (So 03:06:00 and 03:18:00 would not get sorted properly).

For Date/Time sorting in DataTable You need to use it's Sorting plug-ins
For Example,
You need to include this js files :
//cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.4/moment.min.js
//cdn.datatables.net/plug-ins/1.10.12/sorting/datetime-moment.js
and then, In your jQuery use this as
$.fn.dataTable.moment( 'HH:mm MMM D, YY' ); // Pass your date time format as param
For Deeper reference please check :
Sorting Plugins
Ultimate date / time sorting plugin

Related

Filtering chloropleth data using a slider for annual data in vega-lite

I have some geojson grids that map through to some annualised sales data over a period of 25 years. I am really struggling to filter this sales data by year to show the trends in a chloropleth map.
d3 = require('d3-dsv');
map_json = FileAttachment("Time_line#2.geojson").json()
sales_data = FileAttachment("Timeline_test#1.csv").csv()
vegalite = require('#observablehq/vega-lite')
vegalite ({
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"width": 600,
"height": 350,
"data": {
"name": "mapdata",
"values": map_json,
"format": {"property": "features"},
},
"params": [{
"name" : "AnnualPeriod",
"value": 1995,
"bind" : {"input": "range", "min":1995, "max":2020,"step":1 }
}],
"transform" : [{
"lookup": "properties.id",
"from": {
"data": { "values": sales_data,},
"format":"csv",
"key": "derived_boundary_id",
"fields": ["sales_volume"],
},
},],
"layer": [
{
"mark": "geoshape",
"encoding": {
"color": {
"field": "sales_volume",
"type": "quantitative",
"scale": {"scheme": "Oranges"},
},
"stroke": { "value": "#ff75"},
},
},
]
})
I have tried to add transform.filter and cannot get it to work. At the moment it appears to be taking the first sales_data record for each of the boundary_ids.
I would like the data to be filtered according to the setting of the AnnualPeriod slider.
I think I need to include something like
"transform" :[{"filter": "datum.year == AnnualPeriod"}]
I have tried it in the transform section, with the lookup between the sales_data and the geojosn objects.
I have also tried to filter in and around the geoshape mark but neither work.
Does anyone have any ideas?
The is a sample of the sales_data:
sales_volume,year,derived_boundary_id
5,2015,602212
2,2016,602212
2,2019,602212
5,1995,602213
7,1996,602213
6,1997,602213
7,1998,602213
9,1999,602213
10,2000,602213
7,2001,602213
5,2002,602213
5,2003,602213
9,2004,602213
5,2005,602213
...
where the last column maps to an "id" in the geojson data.
and this is the 'map' that I get. Always the same, irrespective of the slider setting.
I have eventually worked out how to get this to work.
Create the main data source as the sales data and attach the maps/geojson to this via a transform/lookup.
It seems so simple now, but I thought I would post the result so others can see how it can be achieved.
These observations may help others:
The transform can take the "year" filter as well as the map_json lookup.
The map/json lookup is referenced as "geo" to make it easier to understand
The geoshape mark then references, via the encoding, this geojson "geo" object.
Thanks to Mike Bostock and the Observable and Vega-Lite team for their excellent work.
vegalite ({
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"width": 600,
"height": 600,
"data": {
"values": sales_data,
},
"params": [{
"name" : "AnnualPeriod",
"value": 1995,
"bind" : {"input": "range", "min":1995, "max":2020,"step":1 }
}],
"transform" : [
{"filter" : "year(datum.year) === AnnualPeriod"},
{
"lookup": "derived_boundary_id",
"from" : {
"data": {
"values": map_json,
"format": {"property": "features"},
},
"key":"properties.id",
},
"as":"geo",
}
],
"mark":"geoshape",
"encoding": {
"shape": {
"field":"geo",
"type":"geojson",
},
"color":{
"field":"sales_volume",
"type":"quantitative",
"scale": {
"scheme":"Oranges",
"domain": [0,15],
},
}
}
})
How's your progress? I am not familiar with Choropleths, but by comparing with the Vega Examples, I spot 2 differences you may wanna take a look:
Choropleth needs a projection
Map data should be placed in lookup transform, and sale data in normal data. Ref 1 Ref 2
Do feel free to correct me if I am wrong. If you need more help, please share an editor with dummy data because I found it hard to make good use of the sale data you provided :'(

How to setup date and fuzzy title search on elasticsearch

I am building an Rails 5 app with an Angular 7 frontent.
In this app I am using Searchkick (an Elasticsearch gem) and I have indexed a model called Event that got attributes title (string) and starts_at (datetime).
I want to be able to build a query in the search controller where I am able to do the following:
Search the title with a fuzzy search meaning it do not have to match 100% (which it now require).
Search with a date range matching starts_at for the indexed Events.
This is my controller index method
def index
args = {}
args[:eventable_id] = params[:id]
args[:eventable_type] = params[:type]
args[:title] = params[:title] if params[:title].present?
if params[:starts_at].present?
args[:starts_at] = {}
args[:starts_at][:gte] = params[:starts_at].to_date.beginning_of_day
args[:starts_at][:lte] = params[:ends_at].to_date.end_of_day
end
#events = Event.search where: args, page: params[:page], per_page: params[:per_page]
end
I have added this line to my Event model
searchkick text_middle: [:title]
This is the actual query that is run
{
"query": {
"bool": {
"must": {
"match_all": {}
},
"filter": [{
"term": {
"eventable_id": "2"
}
}, {
"term": {
"eventable_type": "Space"
}
}, {
"term": {
"title": "nice event"
}
}, {
"range": {
"starts_at": {
"from": "2020-02-01T00:00:00.000Z",
"include_lower": true,
"to": "2020-02-29T23:59:59.999Z",
"include_upper": true
}
}
}]
}
},
"timeout": "11s",
"_source": false,
"size": 10000
}
The date search does not work (but I get no errors) and the title search must match 100% (even the case).
Thankful for all help!
Rather than using Fuzzy queries, I would recommend an ngram analyzer.
Here is an example of an ngram analyzer:
analyzer: {
ngram_analyzer: {
type: "custom",
tokenizer: "standard",
filter: ["lowercase", "ngram_filter"],
char_filter: [
"replace_dots"
]
}
},
filter: {
ngram_filter: {
type: "ngram",
min_gram: "3",
max_gram: "20",
}
}
You will also have to add this code to your settings index:
max_ngram_diff: 17
Then on your mapping, make sure you create two fields. 1 mapping for your regular field such as name and then another mapping for your ngram field such as name.ngram.
In my query, I like to give my name field a boost of 10 and my name.ngram field a boost of 5 so that the exact matches will be rendered first. You will have to play with this though.
In regard to your range query, I am using gte and lte. Here is an example:
query:{
bool: {
must: {
range: {date: {gte: params[:date], lte: params[:date], boost: 10}}
}
}
}
I hope this helps.

Jquery datatable - Inline edit columns and save all details on clicking 'save' button

I am using following Jquery Datatables code:
$(document).ready(function() {
table = DataTable({
"ajax": {
"url" : '/Book/list/' + book_id,
"dataSrc": function(data) {
return data.book_details;
},
"searching": false
"columns": [
{ "data": "id"},
{ "data": "book_name" },
{ "data": "price"},
{ "data": "rank"},
]
});
I want to edit the 'Price' column - Tried editor plugin but since that is not an opensouce plugin, I am not able to use it.
And the second thing i need is :
After editing 'price' column for every row, by clicking save button, it has to save every modified value in datatable.
Anyone please help?

How do I add JSON data in the child table against the parent table in Ruby on Rails?

I have been struggling with something in Ruby on Rails.
I have four tables which are interlinked: A, B, C, and D. A is the parent for B and B is the parent for C and D.
I have a records already existing in table B and want to add multiple entries against a particular record, for example "3", in the 'C' and 'D' tables against this id.
The data format is:
[{\"waypoint\":{\"latitude\":37.3645616666667,\"timestamp\":\"2012-10-16T09:58:50Z\",\"background\":false,\"estimated_speed\":17.4189262390137,\"journey_id\":null,\"longitude\":-112.850676666667}},{\"waypoint\":{\"latitude\":37.3648733333333,\"timestamp\":\"2012-10-16T09:58:54Z\",\"background\":false,\"estimated_speed\":17.076057434082,\"journey_id\":null,\"longitude\":-112.85077}},{\"waypoint\":{\"latitude\":37.3651116666667,\"timestamp\":\"2012-10-16T09:58:57Z\",\"background\":false,\"estimated_speed\":15.4269437789917,\"journey_id\":null,\"longitude\":-112.850766666667}},{\"waypoint\":{\"latitude\":37.36547,\"timestamp\":\"2012-10-16T09:59:02Z\",\"background\":false,\"estimated_speed\":17.1007328033447,\"journey_id\":null,\"longitude\":-112.85072}},{\"waypoint\":{\"latitude\":37.3658433333333,\"timestamp\":\"2012-10-16T09:59:11Z\",\"background\":false,\"estimated_speed\":10.3052024841309,\"journey_id\":null,\"longitude\":-112.850738333333}}]"
I get this data from a web service. But I see journey_id as null, whereas I want it to be 3, as I want to make the entry against this id.
How can I save this data in a child table using this id?
Your JSON string isn't opened correctly in your sample, as it's missing the leading '"'. Fixing that and moving on, here's what the JSON looks like "prettified":
[
{
"waypoint": {
"latitude": 37.3645616666667,
"timestamp": "2012-10-16T09:58:50Z",
"background": false,
"estimated_speed": 17.4189262390137,
"journey_id": null,
"longitude": -112.850676666667
}
},
{
"waypoint": {
"latitude": 37.3648733333333,
"timestamp": "2012-10-16T09:58:54Z",
"background": false,
"estimated_speed": 17.076057434082,
"journey_id": null,
"longitude": -112.85077
}
},
{
"waypoint": {
"latitude": 37.3651116666667,
"timestamp": "2012-10-16T09:58:57Z",
"background": false,
"estimated_speed": 15.4269437789917,
"journey_id": null,
"longitude": -112.850766666667
}
},
{
"waypoint": {
"latitude": 37.36547,
"timestamp": "2012-10-16T09:59:02Z",
"background": false,
"estimated_speed": 17.1007328033447,
"journey_id": null,
"longitude": -112.85072
}
},
{
"waypoint": {
"latitude": 37.3658433333333,
"timestamp": "2012-10-16T09:59:11Z",
"background": false,
"estimated_speed": 10.3052024841309,
"journey_id": null,
"longitude": -112.850738333333
}
}
]
You have an array of waypoint objects. Parsing that JSON into a Ruby object:
obj = JSON["[{\"waypoint\":..."] # purposely truncated for brevity
returns an array of hashes:
[{"waypoint"=>
{"latitude"=>37.3645616666667,
"timestamp"=>"2012-10-16T09:58:50Z",
"background"=>false,
"estimated_speed"=>17.4189262390137,
"journey_id"=>nil,
"longitude"=>-112.850676666667}},
{"waypoint"=>
{"latitude"=>37.3648733333333,
"timestamp"=>"2012-10-16T09:58:54Z",
"background"=>false,
"estimated_speed"=>17.076057434082,
"journey_id"=>nil,
"longitude"=>-112.85077}},
{"waypoint"=>
{"latitude"=>37.3651116666667,
"timestamp"=>"2012-10-16T09:58:57Z",
"background"=>false,
"estimated_speed"=>15.4269437789917,
"journey_id"=>nil,
"longitude"=>-112.850766666667}},
{"waypoint"=>
{"latitude"=>37.36547,
"timestamp"=>"2012-10-16T09:59:02Z",
"background"=>false,
"estimated_speed"=>17.1007328033447,
"journey_id"=>nil,
"longitude"=>-112.85072}},
{"waypoint"=>
{"latitude"=>37.3658433333333,
"timestamp"=>"2012-10-16T09:59:11Z",
"background"=>false,
"estimated_speed"=>10.3052024841309,
"journey_id"=>nil,
"longitude"=>-112.850738333333}}]
You can walk through that array and access, or change, the value for journey_id:
row = 3
obj = obj.map{ |h| h['waypoint']['journey_id'] = row }
obj.first
Looking at the first hash shows the value was changed, as were all the rest:
{
"waypoint" => {
"latitude" => 37.3645616666667,
"timestamp" => "2012-10-16T09:58:50Z",
"background" => false,
"estimated_speed" => 17.4189262390137,
"journey_id" => 3,
"longitude" => -112.850676666667
}
}
At that point, you need to recreate the JSON string. You can figure that out by reading the JSON documentation.
You could do all this by modifying the received string directly, but you don't want to get into the habit of directly modifying JSON strings because you can inadvertently damage the payload. It's better to let the parser give you the structure, modify that, then let JSON recreate the string.
How you store it to your database is left as an exercise for you also.

Jstree - Add nodes to tree by user input (button click)

I have a ASP.NET MVC 4.0 project using JsTree. The JsTree is being populated thought a model that returns a JSON.
Now my problem is this, i have a huge tree that makes the user experience pretty bad. What i need is to load a few items (let'say 20), and have a button that when clicked by the user add's the next 20 records to the tree.
I have been searching in Google JsTree documentation and SO, but i haven't found a example that works for me.
Can anyone help me?
Thanks in advanced.
Ok, some break trought. I kind of got this working. In my view the user input call this function:
function getNextRecords() {
var new_data = { "attr": { "ID": "999999999999", "NodeText": "999999999999" },
"data": "999999999999",
"children": [{
"attr": { "ID": "ACT99", "NodeText": "969994222" },
"data": "969994222 - PPP",
"children": [{
"attr": { "ID": "TRFps800", "rel": "disabled" },
"data": "Voz com unlimited 800 fidelização até 01/11/2019",
"state": "open"
}],
"state": "open"
}], "state": "open"
};
var retDom = $.jstree._reference("#demoTree")._parse_json(new_data, -1, true);
$("#demoTree").jstree("move_node", retDom, -1, "inside", false, false, true);
}
This is working fine, expect that the parse json creates a "ul" instead of a "li" any ideas in how to change that?
OK, i finally got this working, this is the final version of my Function i hope this helps someone.
function getNextRecords() {
var new_data = { "attr": { "ID": "999999999999", "NodeText": "999999999999" },
"data": "999999999999",
"children": [{
"attr": { "ID": "ACT99", "NodeText": "969994222" },
"data": "969994222 - PPP",
"children": [{
"attr": { "ID": "TRFps800", "rel": "disabled" },
"data": "Voz com unlimited 800 fidelização até 01/11/2019",
"state": "open"
}], "state": "open"
}], "state": "open"
};
var ref = $("li ul:first")
var retDom = $.jstree._reference("#demoTree")._parse_json(new_data, ref, true);
$("#demoTree").jstree("move_node", retDom, ref, "first", false, false, true);
}
This add a new child after the first UL.
The second part "load part of the branch and with another load of the same branch
load more" is exactly what i'm trying to do but i have not yet succeeded, is it
possible to have an example?
Such task must be taken care of on server side. Take is this way: jsTree only displays whatever is passed to it. So you make a decision what to load in a first go and you can have a button (to reload the whole tree or a branch only) to load more or replace whatever was loaded. Don't forget the logic has (almost) nothing to do with jsTree.

Resources