2022-12-20 12:09:37 -06:00
import Component from "@glimmer/component" ;
import { tracked } from "@glimmer/tracking" ;
2023-12-01 16:47:54 +01:00
import { action } from "@ember/object" ;
import { schedule } from "@ember/runloop" ;
2024-11-19 10:20:51 +00:00
import { service } from "@ember/service" ;
2023-12-01 16:47:54 +01:00
import { capitalize } from "@ember/string" ;
import { ajax } from "discourse/lib/ajax" ;
import Badge from "discourse/models/badge" ;
2024-03-27 18:44:02 +02:00
import Category from "discourse/models/category" ;
2023-12-01 16:47:54 +01:00
import getURL from "discourse-common/lib/get-url" ;
import I18n from "I18n" ;
2023-02-07 12:26:47 -06:00
import BadgeViewComponent from "./result-types/badge" ;
2023-12-01 16:47:54 +01:00
import CategoryViewComponent from "./result-types/category" ;
2023-02-07 12:26:47 -06:00
import GroupViewComponent from "./result-types/group" ;
import HtmlViewComponent from "./result-types/html" ;
2023-11-02 09:50:05 +10:00
import JsonViewComponent from "./result-types/json" ;
2023-12-01 16:47:54 +01:00
import PostViewComponent from "./result-types/post" ;
import ReltimeViewComponent from "./result-types/reltime" ;
import TextViewComponent from "./result-types/text" ;
import TopicViewComponent from "./result-types/topic" ;
import UrlViewComponent from "./result-types/url" ;
import UserViewComponent from "./result-types/user" ;
2023-02-07 12:26:47 -06:00
const VIEW_COMPONENTS = {
topic : TopicViewComponent ,
text : TextViewComponent ,
post : PostViewComponent ,
reltime : ReltimeViewComponent ,
badge : BadgeViewComponent ,
url : UrlViewComponent ,
user : UserViewComponent ,
group : GroupViewComponent ,
html : HtmlViewComponent ,
2023-11-02 09:50:05 +10:00
json : JsonViewComponent ,
2023-02-07 12:26:47 -06:00
category : CategoryViewComponent ,
};
2022-12-20 12:09:37 -06:00
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 ;
});
}
2023-02-07 12:26:47 -06:00
get columnComponents () {
2022-12-20 12:09:37 -06:00
if ( ! this . columns ) {
return [];
}
return this . columns . map (( _ , idx ) => {
2023-02-07 12:26:47 -06:00
let type = "text" ;
2022-12-20 12:09:37 -06:00
if ( this . colRender [ idx ]) {
2023-02-07 12:26:47 -06:00
type = this . colRender [ idx ];
2022-12-20 12:09:37 -06:00
}
2023-02-07 12:26:47 -06:00
return { name : type , component : VIEW_COMPONENTS [ type ] };
2022-12-20 12:09:37 -06:00
});
}
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 =
2022-12-20 16:55:11 -06:00
this . resultCount [ 0 ] > 0 && typeof this . rows [ 0 ][ 1 ] === "number" ;
2022-12-20 12:09:37 -06:00
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 ];
}
2024-11-20 14:45:48 +00:00
2022-12-20 12:09:37 -06:00
lookupBadge ( id ) {
return this . transformedBadgeTable [ id ];
}
2024-11-20 14:45:48 +00:00
2022-12-20 12:09:37 -06:00
lookupPost ( id ) {
return this . transformedPostTable [ id ];
}
2024-11-20 14:45:48 +00:00
2022-12-20 12:09:37 -06:00
lookupTopic ( id ) {
return this . transformedTopicTable [ id ];
}
2024-11-20 14:45:48 +00:00
2022-12-20 12:09:37 -06:00
lookupGroup ( id ) {
return this . transformedGroupTable [ id ];
}
lookupCategory ( id ) {
2024-03-27 18:44:02 +02:00
return Category . findById ( id );
2022-12-20 12:09:37 -06:00
}
_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 () {
2022-12-30 10:26:14 -06:00
return this . args . group
? `/g/ ${ this . args . group . name } /reports/`
2022-12-20 12:09:37 -06:00
: "/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 () +
2022-12-29 14:47:18 -06:00
this . args . query . id +
2022-12-20 12:09:37 -06:00
"/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 = {};
2024-08-28 13:51:37 +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 ;
}