Skip to content

Commit

Permalink
Merge pull request #107 from AplinkosMinisterija/stats-endpoint
Browse files Browse the repository at this point in the history
fixing stats endpoint
  • Loading branch information
ambrazasp committed Aug 6, 2024
2 parents 9eb1e97 + aa5e8b0 commit e3f3183
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 85 deletions.
119 changes: 117 additions & 2 deletions services/events.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

import moleculer, { Context } from 'moleculer';
import { Method, Service } from 'moleculer-decorators';
import { Action, Method, Service } from 'moleculer-decorators';
import PostgisMixin, { intersectsQuery } from 'moleculer-postgis';
import DbConnection from '../mixins/database.mixin';
import {
Expand All @@ -15,10 +15,12 @@ import {
UserAuthMeta,
QueryObject,
} from '../types';
import { App } from './apps.service';
import { App, APP_TYPE } from './apps.service';
import { LKS_SRID, parseToJsonIfNeeded } from '../utils';
import { Subscription } from './subscriptions.service';
import { Tag } from './tags.service';
import { Knex } from 'knex';
import _ from 'lodash';

interface Fields extends CommonFields {
app: number;
Expand Down Expand Up @@ -181,6 +183,109 @@ export function applyEventsQueryBySubscriptions(query: QueryObject, subscription
},
})
export default class EventsService extends moleculer.Service {
@Action({
rest: {
method: 'GET',
path: '/',
basePath: '/stats',
},
auth: EndpointType.PUBLIC,
})
async stats(ctx: Context<{ query: any }>) {
const adapter = await this.getAdapter(ctx);
const table = adapter.getTable();
const knex: Knex = adapter.client;

const query = await this.getComputedQuery(ctx);
const eventsQuery = adapter.computeQuery(table, query);
const tagsById: { [key: string]: Tag } = await ctx.call('tags.find', { mapping: 'id' });

const appTypeCaseWhenClause = Object.keys(APP_TYPE).reduce((acc: string[], key: string) => {
if (key && APP_TYPE[key]) {
acc.push(`WHEN apps.key = '${key}' THEN '${APP_TYPE[key]}'`);
}
return acc;
}, []);

const appTypeCaseClause = `CASE ${appTypeCaseWhenClause.join(' ')} END AS app_type`;

const eventsCountByAppType = await knex
.select('ecat.appType')
.count('ecat.id')
.from(
knex
.select('events.id', knex.raw(appTypeCaseClause))
.from(eventsQuery.as('events'))
.leftJoin('apps', 'events.appId', 'apps.id')
.as('ecat'),
)
.groupBy('ecat.appType');

const eventsCountByTagId = await knex
.select(knex.raw('jsonb_array_elements(events.tags)::numeric as tag_id'))
.count('events.id')
.from(eventsQuery.as('events'))
.groupBy('tagId');

const eventsCountByTagData = await knex
.select(knex.raw('td.tag_id::numeric'), 'td.tagName')
.sum(knex.raw('td.tag_value::numeric'))
.from(
knex
.select(
knex.raw(`jsonb_array_elements(events.tags_data)->>'id' as tag_id`),
knex.raw(`jsonb_array_elements(events.tags_data)->>'name' as tag_name`),
knex.raw(`jsonb_array_elements(events.tags_data)->>'value' as tag_value`),
)
.from(eventsQuery.as('events'))
.whereNotNull('events.tagsData')
.as('td'),
)
.groupBy(['tagId', 'tagName']);

const stats: {
count: number;

byApp: {
[key: App['key']]: {
count: number;
byTag?: { [key: Tag['name']]: { count: number; [key: string]: number } };
};
};
} = {
byApp: {},
count: 0,
};

eventsCountByAppType?.forEach((item) => {
const count = Number(item.count);
const path = `byApp.${item.appType}.count`;
const existingCount = _.get(stats, path, 0);
_.set(stats, path, existingCount + count);
stats.count += count;
});

eventsCountByTagId?.forEach((item) => {
const tag = tagsById[item.tagId];
const count = Number(item.count);

const path = `byApp.${tag.appType}.byTag.${tag.name}.count`;
const existingCount = _.get(stats, path, 0);
_.set(stats, path, existingCount + count);
});

eventsCountByTagData?.forEach((item) => {
const tag = tagsById[item.tagId];
const count = Number(item.sum);

const path = `byApp.${tag.appType}.byTag.${tag.name}.${item.tagName}`;
const existingCount = _.get(stats, path, 0);
_.set(stats, path, existingCount + count);
});

return stats;
}

@Method
async applyFilters(ctx: Context<any, UserAuthMeta>) {
ctx.params.query = parseToJsonIfNeeded(ctx.params.query) || {};
Expand All @@ -196,4 +301,14 @@ export default class EventsService extends moleculer.Service {

return ctx;
}

@Method
async getComputedQuery(ctx: Context<{ query: any }>) {
let { params } = ctx;
params = this.sanitizeParams(params);
params = await this._applyScopes(params, ctx);
params = this.paramsFieldNameConversion(params);

return parseToJsonIfNeeded(params.query) || {};
}
}
83 changes: 0 additions & 83 deletions services/stats.service.ts

This file was deleted.

0 comments on commit e3f3183

Please sign in to comment.