DEV: Update linting setup and fix issues (#179)
This commit is contained in:
parent
780232c902
commit
07e009e862
|
@ -1 +1,3 @@
|
||||||
node_modules
|
node_modules
|
||||||
|
/gems
|
||||||
|
/auto_generated
|
||||||
|
|
46
Gemfile.lock
46
Gemfile.lock
|
@ -1,34 +1,42 @@
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
ast (2.4.0)
|
ast (2.4.2)
|
||||||
jaro_winkler (1.5.4)
|
parallel (1.22.1)
|
||||||
parallel (1.19.1)
|
parser (3.1.2.0)
|
||||||
parser (2.7.1.2)
|
ast (~> 2.4.1)
|
||||||
ast (~> 2.4.0)
|
rainbow (3.1.1)
|
||||||
rainbow (3.0.0)
|
regexp_parser (2.5.0)
|
||||||
rexml (3.2.5)
|
rexml (3.2.5)
|
||||||
rubocop (0.82.0)
|
rubocop (1.30.1)
|
||||||
jaro_winkler (~> 1.5.1)
|
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
parser (>= 2.7.0.1)
|
parser (>= 3.1.0.0)
|
||||||
rainbow (>= 2.2.2, < 4.0)
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
rexml
|
regexp_parser (>= 1.8, < 3.0)
|
||||||
|
rexml (>= 3.2.5, < 4.0)
|
||||||
|
rubocop-ast (>= 1.18.0, < 2.0)
|
||||||
ruby-progressbar (~> 1.7)
|
ruby-progressbar (~> 1.7)
|
||||||
unicode-display_width (>= 1.4.0, < 2.0)
|
unicode-display_width (>= 1.4.0, < 3.0)
|
||||||
rubocop-discourse (2.1.2)
|
rubocop-ast (1.18.0)
|
||||||
rubocop (>= 0.69.0)
|
parser (>= 3.1.1.0)
|
||||||
rubocop-rspec (>= 1.39.0)
|
rubocop-discourse (2.5.0)
|
||||||
rubocop-rspec (1.39.0)
|
rubocop (>= 1.1.0)
|
||||||
rubocop (>= 0.68.1)
|
rubocop-rspec (>= 2.0.0)
|
||||||
ruby-progressbar (1.10.1)
|
rubocop-rspec (2.11.1)
|
||||||
unicode-display_width (1.7.0)
|
rubocop (~> 1.19)
|
||||||
|
ruby-progressbar (1.11.0)
|
||||||
|
unicode-display_width (2.1.0)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
|
arm64-darwin-20
|
||||||
ruby
|
ruby
|
||||||
|
x86_64-darwin-18
|
||||||
|
x86_64-darwin-19
|
||||||
|
x86_64-darwin-20
|
||||||
|
x86_64-linux
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
rubocop-discourse
|
rubocop-discourse
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.2.15
|
2.3.10
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
|
import Component from "@ember/component";
|
||||||
import loadScript from "discourse/lib/load-script";
|
import loadScript from "discourse/lib/load-script";
|
||||||
import { default as computed } from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import themeColor from "../lib/themeColor";
|
import themeColor from "../lib/themeColor";
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Component.extend({
|
||||||
barsColor: themeColor("--tertiary"),
|
barsColor: themeColor("--tertiary"),
|
||||||
barsHoverColor: themeColor("--tertiary-high"),
|
barsHoverColor: themeColor("--tertiary-high"),
|
||||||
gridColor: themeColor("--primary-low"),
|
gridColor: themeColor("--primary-low"),
|
||||||
labelsColor: themeColor("--primary-medium"),
|
labelsColor: themeColor("--primary-medium"),
|
||||||
chart: null,
|
chart: null,
|
||||||
|
|
||||||
@computed("data", "options")
|
@discourseComputed("data", "options")
|
||||||
config(data, options) {
|
config(data, options) {
|
||||||
return {
|
return {
|
||||||
type: "bar",
|
type: "bar",
|
||||||
|
@ -18,7 +19,7 @@ export default Ember.Component.extend({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("labels.[]", "values.[]", "datasetName")
|
@discourseComputed("labels.[]", "values.[]", "datasetName")
|
||||||
data(labels, values, datasetName) {
|
data(labels, values, datasetName) {
|
||||||
return {
|
return {
|
||||||
labels,
|
labels,
|
||||||
|
@ -33,7 +34,7 @@ export default Ember.Component.extend({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed
|
@discourseComputed
|
||||||
options() {
|
options() {
|
||||||
return {
|
return {
|
||||||
scales: {
|
scales: {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
import Component from "@ember/component";
|
||||||
import { observes } from "discourse-common/utils/decorators";
|
import { observes } from "discourse-common/utils/decorators";
|
||||||
import { schedule, throttle } from "@ember/runloop";
|
import { schedule, throttle } from "@ember/runloop";
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Component.extend({
|
||||||
@observes("hideSchema")
|
@observes("hideSchema")
|
||||||
_onHideSchema() {
|
_onHideSchema() {
|
||||||
this.appEvents.trigger("ace:resize");
|
this.appEvents.trigger("ace:resize");
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { default as computed } from "discourse-common/utils/decorators";
|
import Component from "@ember/component";
|
||||||
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Component.extend({
|
||||||
tagName: "ol",
|
tagName: "ol",
|
||||||
|
|
||||||
@computed("col.enum")
|
@discourseComputed("col.enum")
|
||||||
enuminfo(hash) {
|
enuminfo(hash) {
|
||||||
let result = [];
|
let result = [];
|
||||||
for (let key in hash) {
|
for (let key in hash) {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
import Component from "@ember/component";
|
||||||
import { on } from "discourse-common/utils/decorators";
|
import { on } from "discourse-common/utils/decorators";
|
||||||
import { reads } from "@ember/object/computed";
|
import { reads } from "@ember/object/computed";
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Component.extend({
|
||||||
classNameBindings: [":schema-table", "open"],
|
classNameBindings: [":schema-table", "open"],
|
||||||
tagName: "li",
|
tagName: "li",
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
import {
|
import Component from "@ember/component";
|
||||||
default as computed,
|
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||||
observes,
|
import discourseDebounce from "discourse-common/lib/debounce";
|
||||||
} from "discourse-common/utils/decorators";
|
import { isBlank, isEmpty } from "@ember/utils";
|
||||||
import { debounce } from "@ember/runloop";
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Component.extend({
|
||||||
actions: {
|
actions: {
|
||||||
collapseSchema() {
|
collapseSchema() {
|
||||||
this.set("hideSchema", true);
|
this.set("hideSchema", true);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("schema")
|
@discourseComputed("schema")
|
||||||
transformedSchema(schema) {
|
transformedSchema(schema) {
|
||||||
for (let key in schema) {
|
for (let key in schema) {
|
||||||
if (!schema.hasOwnProperty(key)) {
|
if (!schema.hasOwnProperty(key)) {
|
||||||
|
@ -51,9 +50,9 @@ export default Ember.Component.extend({
|
||||||
return schema;
|
return schema;
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("filter")
|
@discourseComputed("filter")
|
||||||
rfilter(filter) {
|
rfilter(filter) {
|
||||||
if (!Ember.isBlank(filter)) {
|
if (!isBlank(filter)) {
|
||||||
return new RegExp(filter);
|
return new RegExp(filter);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -100,7 +99,7 @@ export default Ember.Component.extend({
|
||||||
filterCols.push(col);
|
filterCols.push(col);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!Ember.isEmpty(filterCols)) {
|
if (!isEmpty(filterCols)) {
|
||||||
tables.push({
|
tables.push({
|
||||||
name: key,
|
name: key,
|
||||||
columns: filterCols,
|
columns: filterCols,
|
||||||
|
@ -114,14 +113,7 @@ export default Ember.Component.extend({
|
||||||
|
|
||||||
@observes("filter")
|
@observes("filter")
|
||||||
triggerFilter() {
|
triggerFilter() {
|
||||||
// TODO: Use discouseDebounce after the 2.7 release.
|
discourseDebounce(
|
||||||
let debounceFunc = debounce;
|
|
||||||
|
|
||||||
try {
|
|
||||||
debounceFunc = require("discourse-common/lib/debounce").default;
|
|
||||||
} catch (_) {}
|
|
||||||
|
|
||||||
debounceFunc(
|
|
||||||
this,
|
this,
|
||||||
function () {
|
function () {
|
||||||
this.set("filteredTables", this.filterTables(this.transformedSchema));
|
this.set("filteredTables", this.filterTables(this.transformedSchema));
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
|
import Component from "@ember/component";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { default as computed } from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import Category from "discourse/models/category";
|
import Category from "discourse/models/category";
|
||||||
import { dasherize } from "@ember/string";
|
import { dasherize } from "@ember/string";
|
||||||
|
import { isEmpty } from "@ember/utils";
|
||||||
|
import { computed } from "@ember/object";
|
||||||
|
|
||||||
const layoutMap = {
|
const layoutMap = {
|
||||||
int: "int",
|
int: "int",
|
||||||
|
@ -34,7 +37,7 @@ function allowsInputTypeTime() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Component.extend({
|
||||||
classNameBindings: ["valid:valid:invalid", ":param"],
|
classNameBindings: ["valid:valid:invalid", ":param"],
|
||||||
|
|
||||||
boolTypes: [
|
boolTypes: [
|
||||||
|
@ -52,7 +55,7 @@ export default Ember.Component.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
value: Ember.computed("params", "info.identifier", {
|
value: computed("params", "info.identifier", {
|
||||||
get() {
|
get() {
|
||||||
return this.params[this.get("info.identifier")];
|
return this.params[this.get("info.identifier")];
|
||||||
},
|
},
|
||||||
|
@ -62,7 +65,7 @@ export default Ember.Component.extend({
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
valueBool: Ember.computed("params", "info.identifier", {
|
valueBool: computed("params", "info.identifier", {
|
||||||
get() {
|
get() {
|
||||||
return this.params[this.get("info.identifier")] !== "false";
|
return this.params[this.get("info.identifier")] !== "false";
|
||||||
},
|
},
|
||||||
|
@ -73,9 +76,9 @@ export default Ember.Component.extend({
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@computed("value", "info.type", "info.nullable")
|
@discourseComputed("value", "info.type", "info.nullable")
|
||||||
valid(value, type, nullable) {
|
valid(value, type, nullable) {
|
||||||
if (Ember.isEmpty(value)) {
|
if (isEmpty(value)) {
|
||||||
return nullable;
|
return nullable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +134,7 @@ export default Ember.Component.extend({
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("info.type")
|
@discourseComputed("info.type")
|
||||||
layoutType(type) {
|
layoutType(type) {
|
||||||
if ((type === "time" || type === "date") && !allowsInputTypeTime()) {
|
if ((type === "time" || type === "date") && !allowsInputTypeTime()) {
|
||||||
return "string";
|
return "string";
|
||||||
|
@ -142,7 +145,7 @@ export default Ember.Component.extend({
|
||||||
return "generic";
|
return "generic";
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("layoutType")
|
@discourseComputed("layoutType")
|
||||||
layoutName(layoutType) {
|
layoutName(layoutType) {
|
||||||
return `admin/components/q-params/${layoutType}`;
|
return `admin/components/q-params/${layoutType}`;
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
import Component from "@ember/component";
|
||||||
import { findRawTemplate } from "discourse-common/lib/raw-templates";
|
import { findRawTemplate } from "discourse-common/lib/raw-templates";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
import getURL from "discourse-common/lib/get-url";
|
import getURL from "discourse-common/lib/get-url";
|
||||||
import Badge from "discourse/models/badge";
|
import Badge from "discourse/models/badge";
|
||||||
import { default as computed } from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import { capitalize } from "@ember/string";
|
import { capitalize } from "@ember/string";
|
||||||
import { alias, mapBy, notEmpty, reads } from "@ember/object/computed";
|
import { alias, mapBy, notEmpty, reads } from "@ember/object/computed";
|
||||||
import { schedule } from "@ember/runloop";
|
import { schedule } from "@ember/runloop";
|
||||||
|
@ -28,7 +29,7 @@ function transformedRelTable(table, modelClass) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QueryResultComponent = Ember.Component.extend({
|
const QueryResultComponent = Component.extend({
|
||||||
layoutName: "explorer-query-result",
|
layoutName: "explorer-query-result",
|
||||||
|
|
||||||
rows: alias("content.rows"),
|
rows: alias("content.rows"),
|
||||||
|
@ -40,7 +41,7 @@ const QueryResultComponent = Ember.Component.extend({
|
||||||
chartValues: mapBy("content.rows", "1"),
|
chartValues: mapBy("content.rows", "1"),
|
||||||
showChart: false,
|
showChart: false,
|
||||||
|
|
||||||
@computed("content.result_count")
|
@discourseComputed("content.result_count")
|
||||||
resultCount(count) {
|
resultCount(count) {
|
||||||
if (count === this.get("content.default_limit")) {
|
if (count === this.get("content.default_limit")) {
|
||||||
return I18n.t("explorer.max_result_count", { count });
|
return I18n.t("explorer.max_result_count", { count });
|
||||||
|
@ -51,14 +52,14 @@ const QueryResultComponent = Ember.Component.extend({
|
||||||
|
|
||||||
colCount: reads("content.columns.length"),
|
colCount: reads("content.columns.length"),
|
||||||
|
|
||||||
@computed("content.duration")
|
@discourseComputed("content.duration")
|
||||||
duration(contentDuration) {
|
duration(contentDuration) {
|
||||||
return I18n.t("explorer.run_time", {
|
return I18n.t("explorer.run_time", {
|
||||||
value: I18n.toNumber(contentDuration, { precision: 1 }),
|
value: I18n.toNumber(contentDuration, { precision: 1 }),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("params.[]")
|
@discourseComputed("params.[]")
|
||||||
parameterAry(params) {
|
parameterAry(params) {
|
||||||
let arr = [];
|
let arr = [];
|
||||||
for (let key in params) {
|
for (let key in params) {
|
||||||
|
@ -69,7 +70,7 @@ const QueryResultComponent = Ember.Component.extend({
|
||||||
return arr;
|
return arr;
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("content", "columns.[]")
|
@discourseComputed("content", "columns.[]")
|
||||||
columnDispNames(content, columns) {
|
columnDispNames(content, columns) {
|
||||||
if (!columns) {
|
if (!columns) {
|
||||||
return [];
|
return [];
|
||||||
|
@ -86,12 +87,12 @@ const QueryResultComponent = Ember.Component.extend({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed
|
@discourseComputed
|
||||||
fallbackTemplate() {
|
fallbackTemplate() {
|
||||||
return findRawTemplate("javascripts/explorer/text");
|
return findRawTemplate("javascripts/explorer/text");
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("content", "columns.[]")
|
@discourseComputed("content", "columns.[]")
|
||||||
columnTemplates(content, columns) {
|
columnTemplates(content, columns) {
|
||||||
if (!columns) {
|
if (!columns) {
|
||||||
return [];
|
return [];
|
||||||
|
@ -108,29 +109,29 @@ const QueryResultComponent = Ember.Component.extend({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("content.relations.user")
|
@discourseComputed("content.relations.user")
|
||||||
transformedUserTable(contentRelationsUser) {
|
transformedUserTable(contentRelationsUser) {
|
||||||
return transformedRelTable(contentRelationsUser);
|
return transformedRelTable(contentRelationsUser);
|
||||||
},
|
},
|
||||||
@computed("content.relations.badge")
|
@discourseComputed("content.relations.badge")
|
||||||
transformedBadgeTable(contentRelationsBadge) {
|
transformedBadgeTable(contentRelationsBadge) {
|
||||||
return transformedRelTable(contentRelationsBadge, Badge);
|
return transformedRelTable(contentRelationsBadge, Badge);
|
||||||
},
|
},
|
||||||
@computed("content.relations.post")
|
@discourseComputed("content.relations.post")
|
||||||
transformedPostTable(contentRelationsPost) {
|
transformedPostTable(contentRelationsPost) {
|
||||||
return transformedRelTable(contentRelationsPost);
|
return transformedRelTable(contentRelationsPost);
|
||||||
},
|
},
|
||||||
@computed("content.relations.topic")
|
@discourseComputed("content.relations.topic")
|
||||||
transformedTopicTable(contentRelationsTopic) {
|
transformedTopicTable(contentRelationsTopic) {
|
||||||
return transformedRelTable(contentRelationsTopic);
|
return transformedRelTable(contentRelationsTopic);
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("site.groups")
|
@discourseComputed("site.groups")
|
||||||
transformedGroupTable(groups) {
|
transformedGroupTable(groups) {
|
||||||
return transformedRelTable(groups);
|
return transformedRelTable(groups);
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed(
|
@discourseComputed(
|
||||||
"rows.[]",
|
"rows.[]",
|
||||||
"content.colrender.[]",
|
"content.colrender.[]",
|
||||||
"content.result_count",
|
"content.result_count",
|
||||||
|
@ -147,7 +148,7 @@ const QueryResultComponent = Ember.Component.extend({
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("content.rows.[]", "content.colrender.[]")
|
@discourseComputed("content.rows.[]", "content.colrender.[]")
|
||||||
chartLabels(rows, colRender) {
|
chartLabels(rows, colRender) {
|
||||||
const labelSelectors = {
|
const labelSelectors = {
|
||||||
user: (user) => user.username,
|
user: (user) => user.username,
|
||||||
|
|
|
@ -1,41 +1,40 @@
|
||||||
import Handlebars from "handlebars";
|
import Component from "@ember/component";
|
||||||
import { categoryLinkHTML } from "discourse/helpers/category-link";
|
import { categoryLinkHTML } from "discourse/helpers/category-link";
|
||||||
import { autoUpdatingRelativeAge } from "discourse/lib/formatter";
|
import { autoUpdatingRelativeAge } from "discourse/lib/formatter";
|
||||||
import { convertIconClass, iconHTML } from "discourse-common/lib/icon-library";
|
import { convertIconClass, iconHTML } from "discourse-common/lib/icon-library";
|
||||||
import getURL from "discourse-common/lib/get-url";
|
import getURL from "discourse-common/lib/get-url";
|
||||||
import { capitalize } from "@ember/string";
|
import { capitalize } from "@ember/string";
|
||||||
import { htmlSafe } from "@ember/template";
|
import { htmlSafe } from "@ember/template";
|
||||||
|
import { get } from "@ember/object";
|
||||||
|
import { isEmpty } from "@ember/utils";
|
||||||
|
import { escapeExpression } from "discourse/lib/utilities";
|
||||||
|
|
||||||
function icon_or_image_replacement(str, ctx) {
|
function icon_or_image_replacement(str, ctx) {
|
||||||
str = Ember.get(ctx.contexts[0], str);
|
str = get(ctx.contexts[0], str);
|
||||||
if (Ember.isEmpty(str)) {
|
if (isEmpty(str)) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (str.indexOf("fa-") > -1) {
|
if (str.indexOf("fa-") > -1) {
|
||||||
const icon = iconHTML(convertIconClass(str));
|
const icon = iconHTML(convertIconClass(str));
|
||||||
return new Handlebars.SafeString(icon);
|
return htmlSafe(icon);
|
||||||
} else {
|
} else {
|
||||||
return new Handlebars.SafeString("<img src='" + str + "'>");
|
return htmlSafe("<img src='" + str + "'>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function category_badge_replacement(str, ctx) {
|
function category_badge_replacement(str, ctx) {
|
||||||
const category = Ember.get(ctx.contexts[0], str);
|
const category = get(ctx.contexts[0], str);
|
||||||
return categoryLinkHTML(category, {
|
return categoryLinkHTML(category, {
|
||||||
allowUncategorized: true,
|
allowUncategorized: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function bound_date_replacement(str, ctx) {
|
function bound_date_replacement(str, ctx) {
|
||||||
const value = Ember.get(ctx.contexts[0], str);
|
const value = get(ctx.contexts[0], str);
|
||||||
return new Handlebars.SafeString(
|
return htmlSafe(autoUpdatingRelativeAge(new Date(value), { title: true }));
|
||||||
autoUpdatingRelativeAge(new Date(value), { title: true })
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const esc = Handlebars.Utils.escapeExpression;
|
|
||||||
|
|
||||||
// consider moving this elsewhere
|
// consider moving this elsewhere
|
||||||
function guessUrl(t) {
|
function guessUrl(t) {
|
||||||
let [dest, name] = [t, t];
|
let [dest, name] = [t, t];
|
||||||
|
@ -50,7 +49,7 @@ function guessUrl(t) {
|
||||||
return [dest, name];
|
return [dest, name];
|
||||||
}
|
}
|
||||||
|
|
||||||
const QueryRowContentComponent = Ember.Component.extend({
|
const QueryRowContentComponent = Component.extend({
|
||||||
tagName: "tr",
|
tagName: "tr",
|
||||||
rowContents: null,
|
rowContents: null,
|
||||||
|
|
||||||
|
@ -78,7 +77,7 @@ const QueryRowContentComponent = Ember.Component.extend({
|
||||||
if (row[idx] === null) {
|
if (row[idx] === null) {
|
||||||
return "NULL";
|
return "NULL";
|
||||||
} else if (t.name === "text") {
|
} else if (t.name === "text") {
|
||||||
return esc(row[idx]);
|
return escapeExpression(row[idx]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const lookupFunc = parentView[`lookup${capitalize(t.name)}`];
|
const lookupFunc = parentView[`lookup${capitalize(t.name)}`];
|
||||||
|
@ -98,7 +97,7 @@ const QueryRowContentComponent = Ember.Component.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return new Handlebars.SafeString((t.template || fallback)(ctx, params));
|
return htmlSafe((t.template || fallback)(ctx, params));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return "error";
|
return "error";
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
import { default as computed, on } from "discourse-common/utils/decorators";
|
import Component from "@ember/component";
|
||||||
|
import discourseComputed, { on } from "discourse-common/utils/decorators";
|
||||||
import getURL from "discourse-common/lib/get-url";
|
import getURL from "discourse-common/lib/get-url";
|
||||||
import { bind } from "@ember/runloop";
|
import { bind } from "@ember/runloop";
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Component.extend({
|
||||||
classNames: ["share-report"],
|
classNames: ["share-report"],
|
||||||
|
|
||||||
group: null,
|
group: null,
|
||||||
query: null,
|
query: null,
|
||||||
visible: false,
|
visible: false,
|
||||||
|
|
||||||
@computed("group", "query")
|
@discourseComputed("group", "query")
|
||||||
link() {
|
link() {
|
||||||
return getURL(`/g/${this.group}/reports/${this.query.id}`);
|
return getURL(`/g/${this.group}/reports/${this.query.id}`);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
|
import Controller from "@ember/controller";
|
||||||
import showModal from "discourse/lib/show-modal";
|
import showModal from "discourse/lib/show-modal";
|
||||||
import Query from "discourse/plugins/discourse-data-explorer/discourse/models/query";
|
import Query from "discourse/plugins/discourse-data-explorer/discourse/models/query";
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
import {
|
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||||
default as computed,
|
|
||||||
observes,
|
|
||||||
} from "discourse-common/utils/decorators";
|
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { Promise } from "rsvp";
|
import { Promise } from "rsvp";
|
||||||
import bootbox from "bootbox";
|
import bootbox from "bootbox";
|
||||||
|
import { get } from "@ember/object";
|
||||||
import { not, reads, sort } from "@ember/object/computed";
|
import { not, reads, sort } from "@ember/object/computed";
|
||||||
|
|
||||||
const NoQuery = Query.create({ name: "No queries", fake: true, group_ids: [] });
|
const NoQuery = Query.create({ name: "No queries", fake: true, group_ids: [] });
|
||||||
|
|
||||||
export default Ember.Controller.extend({
|
export default Controller.extend({
|
||||||
queryParams: { selectedQueryId: "id", params: "params" },
|
queryParams: { selectedQueryId: "id", params: "params" },
|
||||||
selectedQueryId: null,
|
selectedQueryId: null,
|
||||||
editDisabled: false,
|
editDisabled: false,
|
||||||
|
@ -34,17 +33,17 @@ export default Ember.Controller.extend({
|
||||||
sortBy: ["last_run_at:desc"],
|
sortBy: ["last_run_at:desc"],
|
||||||
sortedQueries: sort("model", "sortBy"),
|
sortedQueries: sort("model", "sortBy"),
|
||||||
|
|
||||||
@computed("params")
|
@discourseComputed("params")
|
||||||
parsedParams(params) {
|
parsedParams(params) {
|
||||||
return params ? JSON.parse(params) : null;
|
return params ? JSON.parse(params) : null;
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed
|
@discourseComputed
|
||||||
acceptedImportFileTypes() {
|
acceptedImportFileTypes() {
|
||||||
return ["application/json"];
|
return ["application/json"];
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("search", "sortBy")
|
@discourseComputed("search", "sortBy")
|
||||||
filteredContent(search) {
|
filteredContent(search) {
|
||||||
const regexp = new RegExp(search, "i");
|
const regexp = new RegExp(search, "i");
|
||||||
return this.sortedQueries.filter(
|
return this.sortedQueries.filter(
|
||||||
|
@ -52,12 +51,12 @@ export default Ember.Controller.extend({
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("newQueryName")
|
@discourseComputed("newQueryName")
|
||||||
createDisabled(newQueryName) {
|
createDisabled(newQueryName) {
|
||||||
return (newQueryName || "").trim().length === 0;
|
return (newQueryName || "").trim().length === 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("selectedQueryId")
|
@discourseComputed("selectedQueryId")
|
||||||
selectedItem(selectedQueryId) {
|
selectedItem(selectedQueryId) {
|
||||||
const id = parseInt(selectedQueryId, 10);
|
const id = parseInt(selectedQueryId, 10);
|
||||||
const item = this.model.findBy("id", id);
|
const item = this.model.findBy("id", id);
|
||||||
|
@ -73,7 +72,7 @@ export default Ember.Controller.extend({
|
||||||
return item || NoQuery;
|
return item || NoQuery;
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("selectedItem", "editing")
|
@discourseComputed("selectedItem", "editing")
|
||||||
selectedGroupNames() {
|
selectedGroupNames() {
|
||||||
const groupIds = this.selectedItem.group_ids || [];
|
const groupIds = this.selectedItem.group_ids || [];
|
||||||
const groupNames = groupIds.map((id) => {
|
const groupNames = groupIds.map((id) => {
|
||||||
|
@ -83,7 +82,7 @@ export default Ember.Controller.extend({
|
||||||
return groupNames.join(", ");
|
return groupNames.join(", ");
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("groups")
|
@discourseComputed("groups")
|
||||||
groupOptions(groups) {
|
groupOptions(groups) {
|
||||||
return groups
|
return groups
|
||||||
.filter((g) => g.id !== 0)
|
.filter((g) => g.id !== 0)
|
||||||
|
@ -92,7 +91,7 @@ export default Ember.Controller.extend({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("selectedItem", "selectedItem.dirty")
|
@discourseComputed("selectedItem", "selectedItem.dirty")
|
||||||
othersDirty(selectedItem) {
|
othersDirty(selectedItem) {
|
||||||
return !!this.model.find((q) => q !== selectedItem && q.dirty);
|
return !!this.model.find((q) => q !== selectedItem && q.dirty);
|
||||||
},
|
},
|
||||||
|
@ -106,7 +105,7 @@ export default Ember.Controller.extend({
|
||||||
|
|
||||||
addCreatedRecord(record) {
|
addCreatedRecord(record) {
|
||||||
this.model.pushObject(record);
|
this.model.pushObject(record);
|
||||||
this.set("selectedQueryId", Ember.get(record, "id"));
|
this.set("selectedQueryId", get(record, "id"));
|
||||||
this.selectedItem.set("dirty", false);
|
this.selectedItem.set("dirty", false);
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
showResults: false,
|
showResults: false,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
import Controller from "@ember/controller";
|
||||||
import { alias } from "@ember/object/computed";
|
import { alias } from "@ember/object/computed";
|
||||||
|
|
||||||
export default Ember.Controller.extend({
|
export default Controller.extend({
|
||||||
queries: alias("model.queries"),
|
queries: alias("model.queries"),
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import Controller from "@ember/controller";
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
import Bookmark, {
|
import Bookmark, {
|
||||||
|
@ -8,7 +9,7 @@ import { openBookmarkModal } from "discourse/controllers/bookmark";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import { alias, gt } from "@ember/object/computed";
|
import { alias, gt } from "@ember/object/computed";
|
||||||
|
|
||||||
export default Ember.Controller.extend({
|
export default Controller.extend({
|
||||||
showResults: false,
|
showResults: false,
|
||||||
explain: false,
|
explain: false,
|
||||||
loading: false,
|
loading: false,
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import {
|
import discourseComputed, {
|
||||||
default as computed,
|
|
||||||
observes,
|
observes,
|
||||||
on,
|
on,
|
||||||
} from "discourse-common/utils/decorators";
|
} from "discourse-common/utils/decorators";
|
||||||
|
@ -59,7 +58,7 @@ const Query = RestModel.extend({
|
||||||
this.set("params", newParams);
|
this.set("params", newParams);
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("id")
|
@discourseComputed("id")
|
||||||
downloadUrl(id) {
|
downloadUrl(id) {
|
||||||
// TODO - can we change this to use the store/adapter?
|
// TODO - can we change this to use the store/adapter?
|
||||||
return getURL(`/admin/plugins/explorer/queries/${id}.json?export=1`);
|
return getURL(`/admin/plugins/explorer/queries/${id}.json?export=1`);
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
{{#if info.nullable}}
|
{{#if info.nullable}}
|
||||||
{{combo-box valueAttribute="id" value=value nameProperty="name" content=boolTypes}}
|
{{combo-box
|
||||||
|
valueAttribute="id"
|
||||||
|
value=value
|
||||||
|
nameProperty="name"
|
||||||
|
content=boolTypes
|
||||||
|
}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{input type="checkbox" checked=valueBool}}
|
{{input type="checkbox" checked=valueBool}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<span class="param-name">{{info.identifier}}</span>
|
<span class="param-name">{{info.identifier}}</span>
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
{{email-group-user-chooser
|
{{email-group-user-chooser
|
||||||
value=value
|
value=value
|
||||||
options=(hash
|
options=(hash maximum=1)
|
||||||
maximum=1
|
|
||||||
)
|
|
||||||
onChange=(action (mut value))
|
onChange=(action (mut value))
|
||||||
}}
|
}}
|
||||||
|
|
||||||
<span class="param-name">{{info.identifier}}</span>
|
<span class="param-name">{{info.identifier}}</span>
|
||||||
|
|
|
@ -1,5 +1,2 @@
|
||||||
{{email-group-user-chooser
|
{{email-group-user-chooser value=value onChange=(action (mut value))}}
|
||||||
value=value
|
|
||||||
onChange=(action (mut value))
|
|
||||||
}}
|
|
||||||
<span class="param-name">{{info.identifier}}</span>
|
<span class="param-name">{{info.identifier}}</span>
|
||||||
|
|
|
@ -5,20 +5,33 @@
|
||||||
{{#unless selectedQueryId}}
|
{{#unless selectedQueryId}}
|
||||||
<div class="query-list">
|
<div class="query-list">
|
||||||
{{text-field value=search placeholderKey="explorer.search_placeholder"}}
|
{{text-field value=search placeholderKey="explorer.search_placeholder"}}
|
||||||
{{d-button action=(action "showCreate") icon="plus" class="no-text btn-right"}}
|
{{d-button
|
||||||
|
action=(action "showCreate")
|
||||||
|
icon="plus"
|
||||||
|
class="no-text btn-right"
|
||||||
|
}}
|
||||||
{{pick-files-button
|
{{pick-files-button
|
||||||
class="import-btn"
|
class="import-btn"
|
||||||
label="explorer.import.label"
|
label="explorer.import.label"
|
||||||
icon="upload"
|
icon="upload"
|
||||||
acceptedFormatsOverride=acceptedImportFileTypes
|
acceptedFormatsOverride=acceptedImportFileTypes
|
||||||
showButton=true
|
showButton=true
|
||||||
onFilesPicked=(action "import")}}
|
onFilesPicked=(action "import")
|
||||||
|
}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if showCreate}}
|
{{#if showCreate}}
|
||||||
<div class="query-create">
|
<div class="query-create">
|
||||||
{{text-field value=newQueryName placeholderKey="explorer.create_placeholder"}}
|
{{text-field
|
||||||
{{d-button action=(action "create") disabled=createDisabled label="explorer.create" icon="plus"}}
|
value=newQueryName
|
||||||
|
placeholderKey="explorer.create_placeholder"
|
||||||
|
}}
|
||||||
|
{{d-button
|
||||||
|
action=(action "create")
|
||||||
|
disabled=createDisabled
|
||||||
|
label="explorer.create"
|
||||||
|
icon="plus"
|
||||||
|
}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
@ -36,25 +49,40 @@
|
||||||
{{#if selectedItem}}
|
{{#if selectedItem}}
|
||||||
{{#if editing}}
|
{{#if editing}}
|
||||||
<div class="name">
|
<div class="name">
|
||||||
{{d-button action=(action "goHome") icon="chevron-left" class="previous"}}
|
{{d-button
|
||||||
|
action=(action "goHome")
|
||||||
|
icon="chevron-left"
|
||||||
|
class="previous"
|
||||||
|
}}
|
||||||
<div class="name-text-field">
|
<div class="name-text-field">
|
||||||
{{text-field value=selectedItem.name}}
|
{{text-field value=selectedItem.name}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="desc">
|
|
||||||
{{textarea value=selectedItem.description placeholder=(i18n "explorer.description_placeholder")}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<div class="desc">
|
||||||
|
{{textarea
|
||||||
|
value=selectedItem.description
|
||||||
|
placeholder=(i18n "explorer.description_placeholder")
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="name">
|
<div class="name">
|
||||||
{{d-button action=(action "goHome") icon="chevron-left" class="previous"}}
|
{{d-button
|
||||||
|
action=(action "goHome")
|
||||||
|
icon="chevron-left"
|
||||||
|
class="previous"
|
||||||
|
}}
|
||||||
|
|
||||||
<h1>
|
<h1>
|
||||||
{{selectedItem.name}}
|
{{selectedItem.name}}
|
||||||
{{#unless editDisabled}}
|
{{#unless editDisabled}}
|
||||||
<a href {{action "editName" class="edit-query-name"}}>{{d-icon "pencil-alt"}}</a>
|
<a href {{action "editName" class="edit-query-name"}}>
|
||||||
|
{{d-icon "pencil-alt"}}
|
||||||
|
</a>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="desc">
|
<div class="desc">
|
||||||
{{selectedItem.description}}
|
{{selectedItem.description}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -71,11 +99,20 @@
|
||||||
onSelect=(action (mut selectedItem.group_ids))
|
onSelect=(action (mut selectedItem.group_ids))
|
||||||
}}
|
}}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{{#if runDisabled}}
|
{{#if runDisabled}}
|
||||||
{{#unless editing}}
|
{{#unless editing}}
|
||||||
<span class="setting-controls">
|
<span class="setting-controls">
|
||||||
{{d-button class="ok" action=(action "save") icon="check"}}
|
{{d-button
|
||||||
{{d-button class="cancel" action=(action "discard") icon="times"}}
|
class="ok"
|
||||||
|
action=(action "save")
|
||||||
|
icon="check"
|
||||||
|
}}
|
||||||
|
{{d-button
|
||||||
|
class="cancel"
|
||||||
|
action=(action "discard")
|
||||||
|
icon="times"
|
||||||
|
}}
|
||||||
</span>
|
</span>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -91,10 +128,16 @@
|
||||||
<div class="editor-panel">
|
<div class="editor-panel">
|
||||||
{{ace-editor content=selectedItem.sql mode="sql"}}
|
{{ace-editor content=selectedItem.sql mode="sql"}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="right-panel">
|
<div class="right-panel">
|
||||||
{{#if hideSchema}}
|
{{#if hideSchema}}
|
||||||
{{d-button action=(action "expandSchema") icon="chevron-left" class="no-text unhide"}}
|
{{d-button
|
||||||
|
action=(action "expandSchema")
|
||||||
|
icon="chevron-left"
|
||||||
|
class="no-text unhide"
|
||||||
|
}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<div class="schema">
|
<div class="schema">
|
||||||
{{explorer-schema schema=schema hideSchema=hideSchema}}
|
{{explorer-schema schema=schema hideSchema=hideSchema}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -117,26 +160,61 @@
|
||||||
|
|
||||||
<div class="pull-left left-buttons">
|
<div class="pull-left left-buttons">
|
||||||
{{#if everEditing}}
|
{{#if everEditing}}
|
||||||
{{d-button action=(action "save") label="explorer.save" disabled=saveDisable}}
|
{{d-button
|
||||||
|
action=(action "save")
|
||||||
|
label="explorer.save"
|
||||||
|
disabled=saveDisable
|
||||||
|
}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#unless editDisabled}}
|
{{#unless editDisabled}}
|
||||||
{{d-button action=(action "editName") label="explorer.edit" icon="pencil-alt"}}
|
{{d-button
|
||||||
|
action=(action "editName")
|
||||||
|
label="explorer.edit"
|
||||||
|
icon="pencil-alt"
|
||||||
|
}}
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{d-button action=(action "download") label="explorer.export" disabled=runDisabled icon="download"}}
|
|
||||||
|
{{d-button
|
||||||
|
action=(action "download")
|
||||||
|
label="explorer.export"
|
||||||
|
disabled=runDisabled
|
||||||
|
icon="download"
|
||||||
|
}}
|
||||||
|
|
||||||
{{#if everEditing}}
|
{{#if everEditing}}
|
||||||
{{d-button action=(action "showHelpModal") label="explorer.help.label" icon="question-circle"}}
|
{{d-button
|
||||||
|
action=(action "showHelpModal")
|
||||||
|
label="explorer.help.label"
|
||||||
|
icon="question-circle"
|
||||||
|
}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pull-right right-buttons">
|
<div class="pull-right right-buttons">
|
||||||
{{#if selectedItem.destroyed}}
|
{{#if selectedItem.destroyed}}
|
||||||
{{d-button action=(action "recover") class="" icon="undo" label="explorer.recover"}}
|
{{d-button
|
||||||
|
action=(action "recover")
|
||||||
|
class=""
|
||||||
|
icon="undo"
|
||||||
|
label="explorer.recover"
|
||||||
|
}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#if everEditing}}
|
{{#if everEditing}}
|
||||||
{{d-button action=(action "discard") icon="undo" label="explorer.undo" disabled=saveDisabled}}
|
{{d-button
|
||||||
|
action=(action "discard")
|
||||||
|
icon="undo"
|
||||||
|
label="explorer.undo"
|
||||||
|
disabled=saveDisabled
|
||||||
|
}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{d-button action=(action "destroy") class="btn-danger" icon="trash-alt" label="explorer.delete"}}
|
|
||||||
|
{{d-button
|
||||||
|
action=(action "destroy")
|
||||||
|
class="btn-danger"
|
||||||
|
icon="trash-alt"
|
||||||
|
label="explorer.delete"
|
||||||
|
}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -159,15 +237,34 @@
|
||||||
|
|
||||||
{{#if runDisabled}}
|
{{#if runDisabled}}
|
||||||
{{#if saveDisabled}}
|
{{#if saveDisabled}}
|
||||||
{{d-button label="explorer.run" disabled="true" class="btn-primary"}}
|
{{d-button
|
||||||
|
label="explorer.run"
|
||||||
|
disabled="true"
|
||||||
|
class="btn-primary"
|
||||||
|
}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{d-button action=(action "saverun") icon="play" label="explorer.saverun" class="btn-primary"}}
|
{{d-button
|
||||||
|
action=(action "saverun")
|
||||||
|
icon="play"
|
||||||
|
label="explorer.saverun"
|
||||||
|
class="btn-primary"
|
||||||
|
}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{d-button action=(action "run") icon="play" label="explorer.run" disabled=runDisabled class="btn-primary" type="submit"}}
|
{{d-button
|
||||||
|
action=(action "run")
|
||||||
|
icon="play"
|
||||||
|
label="explorer.run"
|
||||||
|
disabled=runDisabled
|
||||||
|
class="btn-primary"
|
||||||
|
type="submit"
|
||||||
|
}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<label class="query-plan">{{input type="checkbox" checked=explain name="explain"}} {{i18n "explorer.explain_label"}}</label>
|
<label class="query-plan">
|
||||||
|
{{input type="checkbox" checked=explain name="explain"}}
|
||||||
|
{{i18n "explorer.explain_label"}}
|
||||||
|
</label>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
@ -194,13 +291,33 @@
|
||||||
<table class="recent-queries">
|
<table class="recent-queries">
|
||||||
<thead class="heading-container">
|
<thead class="heading-container">
|
||||||
<th class="col heading name">
|
<th class="col heading name">
|
||||||
<div role="button" class="heading-toggle" {{action "sortByProperty" "name"}}>
|
<div
|
||||||
{{table-header-toggle field="name" labelKey="explorer.query_name" order=order asc=asc automatic=true}}
|
role="button"
|
||||||
|
class="heading-toggle"
|
||||||
|
{{action "sortByProperty" "name"}}
|
||||||
|
>
|
||||||
|
{{table-header-toggle
|
||||||
|
field="name"
|
||||||
|
labelKey="explorer.query_name"
|
||||||
|
order=order
|
||||||
|
asc=asc
|
||||||
|
automatic=true
|
||||||
|
}}
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
<th class="col heading created-by">
|
<th class="col heading created-by">
|
||||||
<div role="button" class="heading-toggle" {{action "sortByProperty" "username"}}>
|
<div
|
||||||
{{table-header-toggle field="username" labelKey="explorer.query_user" order=order asc=asc automatic=true}}
|
role="button"
|
||||||
|
class="heading-toggle"
|
||||||
|
{{action "sortByProperty" "username"}}
|
||||||
|
>
|
||||||
|
{{table-header-toggle
|
||||||
|
field="username"
|
||||||
|
labelKey="explorer.query_user"
|
||||||
|
order=order
|
||||||
|
asc=asc
|
||||||
|
automatic=true
|
||||||
|
}}
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
<th class="col heading group-names">
|
<th class="col heading group-names">
|
||||||
|
@ -209,8 +326,18 @@
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
<th class="col heading created-at">
|
<th class="col heading created-at">
|
||||||
<div role="button" class="heading-toggle" {{action "sortByProperty" "last_run_at"}}>
|
<div
|
||||||
{{table-header-toggle field="last_run_at" labelKey="explorer.query_time" order=order asc=asc automatic=true}}
|
role="button"
|
||||||
|
class="heading-toggle"
|
||||||
|
{{action "sortByProperty" "last_run_at"}}
|
||||||
|
>
|
||||||
|
{{table-header-toggle
|
||||||
|
field="last_run_at"
|
||||||
|
labelKey="explorer.query_time"
|
||||||
|
order=order
|
||||||
|
asc=asc
|
||||||
|
automatic=true
|
||||||
|
}}
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -218,7 +345,10 @@
|
||||||
{{#each filteredContent as |query|}}
|
{{#each filteredContent as |query|}}
|
||||||
<tr class="query-row">
|
<tr class="query-row">
|
||||||
<td>
|
<td>
|
||||||
<a {{action "scrollTop"}} href="/admin/plugins/explorer/?id={{query.id}}">
|
<a
|
||||||
|
{{action "scrollTop"}}
|
||||||
|
href="/admin/plugins/explorer/?id={{query.id}}"
|
||||||
|
>
|
||||||
<b class="query-name">{{query.name}}</b>
|
<b class="query-name">{{query.name}}</b>
|
||||||
<medium class="query-desc">{{query.description}}</medium>
|
<medium class="query-desc">{{query.description}}</medium>
|
||||||
</a>
|
</a>
|
||||||
|
@ -249,7 +379,9 @@
|
||||||
</tr>
|
</tr>
|
||||||
{{else}}
|
{{else}}
|
||||||
<br>
|
<br>
|
||||||
<em class="no-search-results"> {{i18n "explorer.no_search_results"}}</em>
|
<em class="no-search-results">
|
||||||
|
{{i18n "explorer.no_search_results"}}
|
||||||
|
</em>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -6,12 +6,16 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{table.name}}
|
{{table.name}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="schema-table-cols">
|
<div class="schema-table-cols">
|
||||||
{{#if open}}
|
{{#if open}}
|
||||||
<dl>
|
<dl>
|
||||||
{{#each table.columns as |col|}}
|
{{#each table.columns as |col|}}
|
||||||
<div>
|
<div>
|
||||||
<dt class={{if col.sensitive "sensitive"}} title={{if col.sensitive (i18n "explorer.schema.sensitive")}}>
|
<dt
|
||||||
|
class={{if col.sensitive "sensitive"}}
|
||||||
|
title={{if col.sensitive (i18n "explorer.schema.sensitive")}}
|
||||||
|
>
|
||||||
{{#if col.sensitive}}
|
{{#if col.sensitive}}
|
||||||
{{d-icon "exclamation-triangle"}}
|
{{d-icon "exclamation-triangle"}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
<div class={{if hideSchema "hidden"}}>
|
<div class={{if hideSchema "hidden"}}>
|
||||||
<div class="schema-search inline-form full-width">
|
<div class="schema-search inline-form full-width">
|
||||||
{{text-field value=filter placeholderKey="explorer.schema.filter"}}
|
{{text-field value=filter placeholderKey="explorer.schema.filter"}}
|
||||||
{{d-button action=(action "collapseSchema") icon="chevron-right" class="no-text"}}
|
{{d-button
|
||||||
|
action=(action "collapseSchema")
|
||||||
|
icon="chevron-right"
|
||||||
|
class="no-text"
|
||||||
|
}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{conditional-loading-spinner condition=loading}}
|
{{conditional-loading-spinner condition=loading}}
|
||||||
|
|
||||||
<div class="schema-container">
|
<div class="schema-container">
|
||||||
<ul>
|
<ul>
|
||||||
{{#each filteredTables as |table|}}
|
{{#each filteredTables as |table|}}
|
||||||
|
|
|
@ -7,6 +7,13 @@
|
||||||
<div class="popup">
|
<div class="popup">
|
||||||
<label>{{i18n "explorer.link"}} {{group}}</label>
|
<label>{{i18n "explorer.link"}} {{group}}</label>
|
||||||
<input type="text" value={{link}}>
|
<input type="text" value={{link}}>
|
||||||
{{d-button action="close" class="btn-flat close" icon="times" aria-label="share.close" title="share.close"}}
|
|
||||||
|
{{d-button
|
||||||
|
action="close"
|
||||||
|
class="btn-flat close"
|
||||||
|
icon="times"
|
||||||
|
aria-label="share.close"
|
||||||
|
title="share.close"
|
||||||
|
}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -1,13 +1,35 @@
|
||||||
<article>
|
<article>
|
||||||
<header class="result-header">
|
<header class="result-header">
|
||||||
<div class="result-info">
|
<div class="result-info">
|
||||||
{{d-button action=(action "downloadResultJson") icon="download" label="explorer.download_json" group=group}}
|
{{d-button
|
||||||
{{d-button action=(action "downloadResultCsv") icon="download" label="explorer.download_csv" group=group}}
|
action=(action "downloadResultJson")
|
||||||
|
icon="download"
|
||||||
|
label="explorer.download_json"
|
||||||
|
group=group
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{d-button
|
||||||
|
action=(action "downloadResultCsv")
|
||||||
|
icon="download"
|
||||||
|
label="explorer.download_csv"
|
||||||
|
group=group
|
||||||
|
}}
|
||||||
|
|
||||||
{{#if canShowChart}}
|
{{#if canShowChart}}
|
||||||
{{#if showChart}}
|
{{#if showChart}}
|
||||||
{{d-button action=(action "showTable") icon="table" label="explorer.show_table" group=group}}
|
{{d-button
|
||||||
|
action=(action "showTable")
|
||||||
|
icon="table"
|
||||||
|
label="explorer.show_table"
|
||||||
|
group=group
|
||||||
|
}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{d-button action=(action "showChart") icon="chart-bar" label="explorer.show_graph" group=group}}
|
{{d-button
|
||||||
|
action=(action "showChart")
|
||||||
|
icon="chart-bar"
|
||||||
|
label="explorer.show_graph"
|
||||||
|
group=group
|
||||||
|
}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,8 +43,8 @@
|
||||||
|
|
||||||
{{~#if hasExplain}}
|
{{~#if hasExplain}}
|
||||||
<pre class="result-explain"><code>
|
<pre class="result-explain"><code>
|
||||||
{{~content.explain}}
|
{{~content.explain}}
|
||||||
</code></pre>
|
</code></pre>
|
||||||
{{~/if}}
|
{{~/if}}
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
@ -33,7 +55,8 @@
|
||||||
{{data-explorer-bar-chart
|
{{data-explorer-bar-chart
|
||||||
labels=chartLabels
|
labels=chartLabels
|
||||||
values=chartValues
|
values=chartValues
|
||||||
datasetName=chartDatasetName}}
|
datasetName=chartDatasetName
|
||||||
|
}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -48,10 +71,11 @@
|
||||||
{{query-row-content
|
{{query-row-content
|
||||||
row=row
|
row=row
|
||||||
fallbackTemplate=fallbackTemplate
|
fallbackTemplate=fallbackTemplate
|
||||||
columnTemplates=columnTemplates}}
|
columnTemplates=columnTemplates
|
||||||
|
}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</section>
|
</section>
|
||||||
</article>
|
</article>
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
{{! source: badge-button component }}
|
{{! source: badge-button component }}
|
||||||
<a href="{{baseuri}}/badges/{{badge.id}}/{{badge.name}}"
|
<a
|
||||||
|
href="{{baseuri}}/badges/{{badge.id}}/{{badge.name}}"
|
||||||
class="user-badge {{badge.badgeTypeClassName}}"
|
class="user-badge {{badge.badgeTypeClassName}}"
|
||||||
title={{badge.display_name}}
|
title={{badge.display_name}}
|
||||||
data-badge-name={{badge.name}}>
|
data-badge-name={{badge.name}}
|
||||||
|
>
|
||||||
{{icon-or-image badge.icon}}
|
{{icon-or-image badge.icon}}
|
||||||
<span class="badge-display-name">{{badge.display_name}}</span>
|
<span class="badge-display-name">{{badge.display_name}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -1,16 +1,28 @@
|
||||||
{{#if post}}
|
{{#if post}}
|
||||||
<aside class="quote" data-post={{post.post_number}} data-topic={{post.topic_id}}>
|
<aside
|
||||||
|
class="quote"
|
||||||
|
data-post={{post.post_number}}
|
||||||
|
data-topic={{post.topic_id}}
|
||||||
|
>
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<div class="quote-controls">
|
<div class="quote-controls">
|
||||||
<a href="/t/via-quote/{{post.topic_id}}/{{post.post_number}}"
|
{{! template-lint-disable no-invalid-link-text }}
|
||||||
|
<a
|
||||||
|
href="/t/via-quote/{{post.topic_id}}/{{post.post_number}}"
|
||||||
title="go to the quoted post"
|
title="go to the quoted post"
|
||||||
class="quote-other-topic">
|
class="quote-other-topic"
|
||||||
|
>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<a class="result-post-link" href="/t/{{post.topic_id}}/{{post.post_number}}">
|
|
||||||
|
<a
|
||||||
|
class="result-post-link"
|
||||||
|
href="/t/{{post.topic_id}}/{{post.post_number}}"
|
||||||
|
>
|
||||||
{{avatar post imageSize="tiny"}}{{post.username}}:
|
{{avatar post imageSize="tiny"}}{{post.username}}:
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<p>
|
<p>
|
||||||
{{html-safe post.excerpt}}
|
{{html-safe post.excerpt}}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
{{#if user}}
|
{{#if user}}
|
||||||
<a href="{{baseuri}}/u/{{user.username}}/activity" data-user-card={{user.username}}>
|
<a
|
||||||
{{avatar user imageSize="tiny"}} {{user.username}}
|
href="{{baseuri}}/u/{{user.username}}/activity"
|
||||||
|
data-user-card={{user.username}}
|
||||||
|
>
|
||||||
|
{{avatar user imageSize="tiny"}}
|
||||||
|
{{user.username}}
|
||||||
</a>
|
</a>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{id}}
|
{{id}}
|
||||||
|
|
|
@ -15,7 +15,9 @@
|
||||||
{{#each queries as |query|}}
|
{{#each queries as |query|}}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
{{#link-to "group.reports.show" group.name query.id}}{{query.name}}{{/link-to}}
|
{{#link-to "group.reports.show" group.name query.id}}
|
||||||
|
{{query.name}}
|
||||||
|
{{/link-to}}
|
||||||
</td>
|
</td>
|
||||||
<td>{{query.description}}</td>
|
<td>{{query.description}}</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<section class="user-content">
|
<section class="user-content">
|
||||||
<h1>{{model.name}}</h1>
|
<h1>{{model.name}}</h1>
|
||||||
<p>{{model.description}}</p>
|
<p>{{model.description}}</p>
|
||||||
|
|
||||||
<form class="query-run" {{action "run" on="submit"}}>
|
<form class="query-run" {{action "run" on="submit"}}>
|
||||||
{{#if hasParams}}
|
{{#if hasParams}}
|
||||||
<div class="query-params">
|
<div class="query-params">
|
||||||
|
@ -9,7 +10,14 @@
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{d-button action=(action "run") icon="play" label="explorer.run" class="btn-primary" type="submit"}}
|
|
||||||
|
{{d-button
|
||||||
|
action=(action "run")
|
||||||
|
icon="play"
|
||||||
|
label="explorer.run"
|
||||||
|
class="btn-primary"
|
||||||
|
type="submit"
|
||||||
|
}}
|
||||||
|
|
||||||
{{d-button
|
{{d-button
|
||||||
action=(action "toggleBookmark")
|
action=(action "toggleBookmark")
|
||||||
|
@ -17,9 +25,10 @@
|
||||||
icon=bookmarkIcon
|
icon=bookmarkIcon
|
||||||
class=bookmarkClassName
|
class=bookmarkClassName
|
||||||
}}
|
}}
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{{conditional-loading-spinner condition=loading}}
|
{{conditional-loading-spinner condition=loading}}
|
||||||
|
|
||||||
{{#if results}}
|
{{#if results}}
|
||||||
<div class="query-results">
|
<div class="query-results">
|
||||||
{{#if showResults}}
|
{{#if showResults}}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
en:
|
en:
|
||||||
site_settings:
|
site_settings:
|
||||||
data_explorer_enabled: "Enable the Data Explorer at /admin/plugins/explorer"
|
data_explorer_enabled: "Enable the Data Explorer at /admin/plugins/explorer"
|
||||||
|
|
||||||
|
|
14
lefthook.yml
14
lefthook.yml
|
@ -1,14 +0,0 @@
|
||||||
pre-commit:
|
|
||||||
parallel: true
|
|
||||||
commands:
|
|
||||||
rubocop:
|
|
||||||
glob: "*.rb"
|
|
||||||
run: bundle exec rubocop --parallel {staged_files}
|
|
||||||
prettier:
|
|
||||||
glob: "*.{scss,js,es6}"
|
|
||||||
include: "test/javascripts|assets/javascripts"
|
|
||||||
run: yarn prettier --list-different {staged_files}
|
|
||||||
eslint-js:
|
|
||||||
glob: "*.{js,es6}"
|
|
||||||
include: "test/javascripts|assets/javascripts"
|
|
||||||
run: yarn eslint --no-error-on-unmatched-pattern -f compact {staged_files}
|
|
|
@ -1,11 +1,10 @@
|
||||||
{
|
{
|
||||||
"name": "discourse-data-explorer",
|
"name": "discourse-data-explorer",
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"repository": "git@github.com:discourse/discourse-data-explorer.git",
|
"repository": "https://github.com/discourse/discourse-data-explorer",
|
||||||
"author": "Riking",
|
"author": "Discourse",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@arkweid/lefthook": "^0.7.2",
|
"eslint-config-discourse": "^3.2.0"
|
||||||
"eslint-config-discourse": "^2.0.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,8 +97,7 @@ acceptance("Data Explorer Plugin | List Queries", function (needs) {
|
||||||
queries: [
|
queries: [
|
||||||
{
|
{
|
||||||
id: -5,
|
id: -5,
|
||||||
sql:
|
sql: "-- [params]\n-- int :months_ago = 1\n\nWITH query_period AS\n(SELECT date_trunc('month', CURRENT_DATE) - INTERVAL ':months_ago months' AS period_start,\n date_trunc('month', CURRENT_DATE) - INTERVAL ':months_ago months' + INTERVAL '1 month' - INTERVAL '1 second' AS period_end)\nSELECT t.id AS topic_id,\n t.category_id,\n COUNT(p.id) AS reply_count\nFROM topics t\nJOIN posts p ON t.id = p.topic_id\nJOIN query_period qp ON p.created_at >= qp.period_start\nAND p.created_at <= qp.period_end\nWHERE t.archetype = 'regular'\nAND t.user_id > 0\nGROUP BY t.id\nORDER BY COUNT(p.id) DESC, t.score DESC\nLIMIT 100\n",
|
||||||
"-- [params]\n-- int :months_ago = 1\n\nWITH query_period AS\n(SELECT date_trunc('month', CURRENT_DATE) - INTERVAL ':months_ago months' AS period_start,\n date_trunc('month', CURRENT_DATE) - INTERVAL ':months_ago months' + INTERVAL '1 month' - INTERVAL '1 second' AS period_end)\nSELECT t.id AS topic_id,\n t.category_id,\n COUNT(p.id) AS reply_count\nFROM topics t\nJOIN posts p ON t.id = p.topic_id\nJOIN query_period qp ON p.created_at >= qp.period_start\nAND p.created_at <= qp.period_end\nWHERE t.archetype = 'regular'\nAND t.user_id > 0\nGROUP BY t.id\nORDER BY COUNT(p.id) DESC, t.score DESC\nLIMIT 100\n",
|
|
||||||
name: "Top 100 Active Topics",
|
name: "Top 100 Active Topics",
|
||||||
description:
|
description:
|
||||||
"based on the number of replies, it accepts a ‘months_ago’ parameter, defaults to 1 to give results for the last calendar month.",
|
"based on the number of replies, it accepts a ‘months_ago’ parameter, defaults to 1 to give results for the last calendar month.",
|
||||||
|
@ -119,8 +118,7 @@ acceptance("Data Explorer Plugin | List Queries", function (needs) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: -6,
|
id: -6,
|
||||||
sql:
|
sql: "-- [params]\n-- int :months_ago = 1\n\nWITH query_period AS (\n SELECT\n date_trunc('month', CURRENT_DATE) - INTERVAL ':months_ago months' as period_start,\n date_trunc('month', CURRENT_DATE) - INTERVAL ':months_ago months' + INTERVAL '1 month' - INTERVAL '1 second' as period_end\n )\n\n SELECT\n ua.user_id,\n count(1) AS like_count\n FROM user_actions ua\n INNER JOIN query_period qp\n ON ua.created_at >= qp.period_start\n AND ua.created_at <= qp.period_end\n WHERE ua.action_type = 1\n GROUP BY ua.user_id\n ORDER BY like_count DESC\n LIMIT 100\n",
|
||||||
"-- [params]\n-- int :months_ago = 1\n\nWITH query_period AS (\n SELECT\n date_trunc('month', CURRENT_DATE) - INTERVAL ':months_ago months' as period_start,\n date_trunc('month', CURRENT_DATE) - INTERVAL ':months_ago months' + INTERVAL '1 month' - INTERVAL '1 second' as period_end\n )\n\n SELECT\n ua.user_id,\n count(1) AS like_count\n FROM user_actions ua\n INNER JOIN query_period qp\n ON ua.created_at >= qp.period_start\n AND ua.created_at <= qp.period_end\n WHERE ua.action_type = 1\n GROUP BY ua.user_id\n ORDER BY like_count DESC\n LIMIT 100\n",
|
|
||||||
name: "Top 100 Likers",
|
name: "Top 100 Likers",
|
||||||
description:
|
description:
|
||||||
"returns the top 100 likers for a given monthly period ordered by like_count. It accepts a ‘months_ago’ parameter, defaults to 1 to give results for the last calendar month.",
|
"returns the top 100 likers for a given monthly period ordered by like_count. It accepts a ‘months_ago’ parameter, defaults to 1 to give results for the last calendar month.",
|
||||||
|
|
|
@ -97,8 +97,7 @@ acceptance("Data Explorer Plugin | Run Query", function (needs) {
|
||||||
queries: [
|
queries: [
|
||||||
{
|
{
|
||||||
id: -6,
|
id: -6,
|
||||||
sql:
|
sql: "-- [params]\n-- int :months_ago = 1\n\nWITH query_period AS (\n SELECT\n date_trunc('month', CURRENT_DATE) - INTERVAL ':months_ago months' as period_start,\n date_trunc('month', CURRENT_DATE) - INTERVAL ':months_ago months' + INTERVAL '1 month' - INTERVAL '1 second' as period_end\n )\n\n SELECT\n ua.user_id,\n count(1) AS like_count\n FROM user_actions ua\n INNER JOIN query_period qp\n ON ua.created_at >= qp.period_start\n AND ua.created_at <= qp.period_end\n WHERE ua.action_type = 1\n GROUP BY ua.user_id\n ORDER BY like_count DESC\n LIMIT 100\n",
|
||||||
"-- [params]\n-- int :months_ago = 1\n\nWITH query_period AS (\n SELECT\n date_trunc('month', CURRENT_DATE) - INTERVAL ':months_ago months' as period_start,\n date_trunc('month', CURRENT_DATE) - INTERVAL ':months_ago months' + INTERVAL '1 month' - INTERVAL '1 second' as period_end\n )\n\n SELECT\n ua.user_id,\n count(1) AS like_count\n FROM user_actions ua\n INNER JOIN query_period qp\n ON ua.created_at >= qp.period_start\n AND ua.created_at <= qp.period_end\n WHERE ua.action_type = 1\n GROUP BY ua.user_id\n ORDER BY like_count DESC\n LIMIT 100\n",
|
|
||||||
name: "Top 100 Likers",
|
name: "Top 100 Likers",
|
||||||
description:
|
description:
|
||||||
"returns the top 100 likers for a given monthly period ordered by like_count. It accepts a ‘months_ago’ parameter, defaults to 1 to give results for the last calendar month.",
|
"returns the top 100 likers for a given monthly period ordered by like_count. It accepts a ‘months_ago’ parameter, defaults to 1 to give results for the last calendar month.",
|
||||||
|
|
Loading…
Reference in New Issue