DEV: Update linting setup and fix issues (#179)

This commit is contained in:
Jarek Radosz 2022-06-17 15:01:34 +02:00 committed by GitHub
parent 780232c902
commit 07e009e862
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 1326 additions and 834 deletions

2
.gitignore vendored
View File

@ -1 +1,3 @@
node_modules node_modules
/gems
/auto_generated

View File

@ -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

View File

@ -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: {

View File

@ -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");

View File

@ -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) {

View File

@ -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",

View File

@ -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));

View File

@ -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}`;
}, },

View File

@ -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,

View File

@ -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";
} }

View File

@ -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}`);
}, },

View File

@ -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,

View File

@ -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"),
}); });

View File

@ -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,

View File

@ -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`);

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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}}

View File

@ -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|}}

View File

@ -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}}

View File

@ -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>

View File

@ -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>

View File

@ -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}}

View File

@ -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}}

View File

@ -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>

View File

@ -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}}

View File

@ -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"

View File

@ -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}

View File

@ -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"
} }
} }

View File

@ -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.",

View File

@ -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.",

1575
yarn.lock

File diff suppressed because it is too large Load Diff