2022-12-20 12:09:37 -06:00
import Component from "@glimmer/component" ;
2020-09-04 13:23:11 +02:00
import I18n from "I18n" ;
2018-10-10 17:26:23 +05:30
import { ajax } from "discourse/lib/ajax" ;
2020-07-06 15:25:19 -04:00
import getURL from "discourse-common/lib/get-url" ;
2018-10-10 17:26:23 +05:30
import Badge from "discourse/models/badge" ;
2022-05-13 12:33:38 -05:00
import { capitalize } from "@ember/string" ;
2022-06-17 07:18:36 -05:00
import { schedule } from "@ember/runloop" ;
2022-12-20 12:09:37 -06:00
import { action } from "@ember/object" ;
import { tracked } from "@glimmer/tracking" ;
import { inject as service } from "@ember/service" ;
import { findRawTemplate } from "discourse-common/lib/raw-templates" ;
export default class QueryResult extends Component {
@ service site ;
@ tracked chartDisplayed = false ;
get colRender () {
return this . args . content . colrender || {};
}
get rows () {
return this . args . content . rows ;
}
get columns () {
return this . args . content . columns ;
}
get params () {
return this . args . content . params ;
}
get explainText () {
return this . args . content . explain ;
}
get chartDatasetName () {
return this . columnNames [ 1 ];
}
get columnNames () {
if ( ! this . columns ) {
return [];
}
return this . columns . map (( colName ) => {
if ( colName . endsWith ( "_id" )) {
return colName . slice ( 0 , - 3 );
}
const dIdx = colName . indexOf ( "$" );
if ( dIdx >= 0 ) {
return colName . substring ( dIdx + 1 );
}
return colName ;
});
}
get columnTemplates () {
if ( ! this . columns ) {
return [];
}
return this . columns . map (( _ , idx ) => {
let viewName = "text" ;
if ( this . colRender [ idx ]) {
viewName = this . colRender [ idx ];
}
const template = findRawTemplate ( `javascripts/explorer/ ${ viewName } ` );
return { name : viewName , template };
});
}
get chartValues () {
// return an array with the second value of this.row
return this . rows . mapBy ( 1 );
}
get colCount () {
return this . columns . length ;
}
get resultCount () {
const count = this . args . content . result_count ;
if ( count === this . args . content . default_limit ) {
return I18n . t ( "explorer.max_result_count" , { count });
} else {
return I18n . t ( "explorer.result_count" , { count });
}
}
get duration () {
return I18n . t ( "explorer.run_time" , {
value : I18n . toNumber ( this . args . content . duration , { precision : 1 }),
});
}
get parameterAry () {
let arr = [];
for ( let key in this . params ) {
if ( this . params . hasOwnProperty ( key )) {
arr . push ({ key , value : this . params [ key ] });
}
}
return arr ;
}
get transformedUserTable () {
return transformedRelTable ( this . args . content . relations . user );
}
get transformedBadgeTable () {
return transformedRelTable ( this . args . content . relations . badge , Badge );
}
get transformedPostTable () {
return transformedRelTable ( this . args . content . relations . post );
}
get transformedTopicTable () {
return transformedRelTable ( this . args . content . relations . topic );
}
get transformedGroupTable () {
return transformedRelTable ( this . site . groups );
}
get canShowChart () {
const hasTwoColumns = this . colCount === 2 ;
const secondColumnContainsNumber =
this . resultCount . length && typeof this . rows [ 0 ][ 1 ] === "number" ;
const secondColumnContainsId = this . colRender [ 1 ];
return (
hasTwoColumns && secondColumnContainsNumber && ! secondColumnContainsId
);
}
get chartLabels () {
const labelSelectors = {
user : ( user ) => user . username ,
badge : ( badge ) => badge . name ,
topic : ( topic ) => topic . title ,
group : ( group ) => group . name ,
category : ( category ) => category . name ,
};
const relationName = this . colRender [ 0 ];
if ( relationName ) {
const lookupFunc = this [ `lookup ${ capitalize ( relationName ) } ` ];
const labelSelector = labelSelectors [ relationName ];
if ( lookupFunc && labelSelector ) {
return this . rows . map (( r ) => {
const relation = lookupFunc . call ( this , r [ 0 ]);
const label = labelSelector ( relation );
return this . _cutChartLabel ( label );
});
}
}
return this . rows . map (( r ) => this . _cutChartLabel ( r [ 0 ]));
}
lookupUser ( id ) {
return this . transformedUserTable [ id ];
}
lookupBadge ( id ) {
return this . transformedBadgeTable [ id ];
}
lookupPost ( id ) {
return this . transformedPostTable [ id ];
}
lookupTopic ( id ) {
return this . transformedTopicTable [ id ];
}
lookupGroup ( id ) {
return this . transformedGroupTable [ id ];
}
lookupCategory ( id ) {
return this . site . categoriesById [ id ];
}
_cutChartLabel ( label ) {
const labelString = label . toString ();
if ( labelString . length > 25 ) {
return ` ${ labelString . substring ( 0 , 25 ) } ...` ;
} else {
return labelString ;
}
}
@ action
downloadResultJson () {
this . _downloadResult ( "json" );
}
@ action
downloadResultCsv () {
this . _downloadResult ( "csv" );
}
@ action
showChart () {
this . chartDisplayed = true ;
}
@ action
hideChart () {
this . chartDisplayed = false ;
}
_download_url () {
return this . group
? `/g/ ${ this . group . name } /reports/`
: "/admin/plugins/explorer/queries/" ;
}
_downloadResult ( format ) {
// Create a frame to submit the form in (?)
// to avoid leaving an about:blank behind
let windowName = randomIdShort ();
const newWindowContents =
"<style>body{font-size:36px;display:flex;justify-content:center;align-items:center;}</style><body>Click anywhere to close this window once the download finishes.<script>window.onclick=function(){window.close()};</script>" ;
window . open ( "data:text/html;base64," + btoa ( newWindowContents ), windowName );
let form = document . createElement ( "form" );
form . setAttribute ( "id" , "query-download-result" );
form . setAttribute ( "method" , "post" );
form . setAttribute (
"action" ,
getURL (
this . _download_url () +
this . get ( "query.id" ) +
"/run." +
format +
"?download=1"
)
);
form . setAttribute ( "target" , windowName );
form . setAttribute ( "style" , "display:none;" );
function addInput ( name , value ) {
let field ;
field = document . createElement ( "input" );
field . setAttribute ( "name" , name );
field . setAttribute ( "value" , value );
form . appendChild ( field );
}
addInput ( "params" , JSON . stringify ( this . params ));
addInput ( "explain" , this . explainText );
addInput ( "limit" , "1000000" );
ajax ( "/session/csrf.json" ). then (( csrf ) => {
addInput ( "authenticity_token" , csrf . csrf );
document . body . appendChild ( form );
form . submit ();
schedule ( "afterRender" , () => document . body . removeChild ( form ));
});
}
}
2015-06-30 15:12:12 -07:00
2015-07-09 12:02:47 -07:00
function randomIdShort () {
2019-07-16 12:46:32 +02:00
return "xxxxxxxx" . replace ( /[xy]/g , () => {
2015-09-14 15:34:57 -07:00
/*eslint-disable*/
2018-10-10 17:26:23 +05:30
return (( Math . random () * 16 ) | 0 ). toString ( 16 );
2015-09-14 15:34:57 -07:00
/*eslint-enable*/
2015-07-09 12:02:47 -07:00
});
}
2015-06-30 15:12:12 -07:00
2015-08-25 21:36:39 -07:00
function transformedRelTable ( table , modelClass ) {
2015-08-25 20:48:19 -07:00
const result = {};
2020-09-04 13:23:11 +02:00
table . forEach (( item ) => {
2015-08-25 21:36:39 -07:00
if ( modelClass ) {
result [ item . id ] = modelClass . create ( item );
} else {
result [ item . id ] = item ;
}
2015-08-25 20:48:19 -07:00
});
return result ;
}