elasticsearch-mcp-vsee 0.5.12 → 0.5.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/server.d.ts +2 -1
- package/build/server.d.ts.map +1 -1
- package/build/server.js +65 -9
- package/build/server.js.map +1 -1
- package/build/tools/base-tool.d.ts +38 -0
- package/build/tools/base-tool.d.ts.map +1 -0
- package/build/tools/base-tool.js +67 -0
- package/build/tools/base-tool.js.map +1 -0
- package/build/tools/find-entities-by-metric.d.ts +314 -24
- package/build/tools/find-entities-by-metric.d.ts.map +1 -1
- package/build/tools/find-entities-by-metric.js +277 -284
- package/build/tools/find-entities-by-metric.js.map +1 -1
- package/build/tools/get-platform-breakdown.d.ts +60 -22
- package/build/tools/get-platform-breakdown.d.ts.map +1 -1
- package/build/tools/get-platform-breakdown.js +220 -266
- package/build/tools/get-platform-breakdown.js.map +1 -1
- package/build/tools/get-rating-distribution.d.ts +65 -25
- package/build/tools/get-rating-distribution.d.ts.map +1 -1
- package/build/tools/get-rating-distribution.js +206 -245
- package/build/tools/get-rating-distribution.js.map +1 -1
- package/build/tools/get-subscription-breakdown.d.ts +30 -15
- package/build/tools/get-subscription-breakdown.d.ts.map +1 -1
- package/build/tools/get-subscription-breakdown.js +140 -155
- package/build/tools/get-subscription-breakdown.js.map +1 -1
- package/build/tools/get-usage-leaderboard.d.ts +114 -0
- package/build/tools/get-usage-leaderboard.d.ts.map +1 -0
- package/build/tools/get-usage-leaderboard.js +169 -0
- package/build/tools/get-usage-leaderboard.js.map +1 -0
- package/build/tools/get-usage-profile.d.ts +83 -0
- package/build/tools/get-usage-profile.d.ts.map +1 -0
- package/build/tools/get-usage-profile.js +235 -0
- package/build/tools/get-usage-profile.js.map +1 -0
- package/build/tools/get-visit-trends.d.ts +59 -25
- package/build/tools/get-visit-trends.d.ts.map +1 -1
- package/build/tools/get-visit-trends.js +175 -242
- package/build/tools/get-visit-trends.js.map +1 -1
- package/build/tools/index.d.ts +4 -2
- package/build/tools/index.d.ts.map +1 -1
- package/build/tools/index.js +5 -3
- package/build/tools/index.js.map +1 -1
- package/build/tools/top-change.d.ts +56 -23
- package/build/tools/top-change.d.ts.map +1 -1
- package/build/tools/top-change.js +177 -193
- package/build/tools/top-change.js.map +1 -1
- package/build/tools/types.d.ts +24 -0
- package/build/tools/types.d.ts.map +1 -0
- package/build/tools/types.js +3 -0
- package/build/tools/types.js.map +1 -0
- package/build/utils/query-helpers.d.ts +10 -0
- package/build/utils/query-helpers.d.ts.map +1 -0
- package/build/utils/query-helpers.js +48 -0
- package/build/utils/query-helpers.js.map +1 -0
- package/package.json +1 -1
- package/build/tools/get-usage-summary.d.ts +0 -52
- package/build/tools/get-usage-summary.d.ts.map +0 -1
- package/build/tools/get-usage-summary.js +0 -273
- package/build/tools/get-usage-summary.js.map +0 -1
|
@@ -1,15 +1,35 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
import { BaseTool } from './base-tool.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { StandardResponse } from './types.js';
|
|
4
|
+
declare const GetRatingDistributionArgsSchema: z.ZodObject<{
|
|
5
|
+
ratingType: z.ZodEnum<["provider", "patient", "both"]>;
|
|
6
|
+
bucketSize: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
7
|
+
startDate: z.ZodOptional<z.ZodString>;
|
|
8
|
+
endDate: z.ZodOptional<z.ZodString>;
|
|
9
|
+
account: z.ZodOptional<z.ZodString>;
|
|
10
|
+
group: z.ZodOptional<z.ZodString>;
|
|
11
|
+
subscription: z.ZodOptional<z.ZodEnum<["Enterprise", "Premium", "FVC", "BVC", "Plus"]>>;
|
|
12
|
+
groupBy: z.ZodDefault<z.ZodOptional<z.ZodEnum<["none", "subscription", "account", "group"]>>>;
|
|
13
|
+
}, "strict", z.ZodTypeAny, {
|
|
14
|
+
groupBy: "subscription" | "account" | "group" | "none";
|
|
15
|
+
ratingType: "provider" | "patient" | "both";
|
|
16
|
+
bucketSize: number;
|
|
17
|
+
startDate?: string | undefined;
|
|
18
|
+
endDate?: string | undefined;
|
|
19
|
+
subscription?: "Enterprise" | "Premium" | "FVC" | "BVC" | "Plus" | undefined;
|
|
20
|
+
account?: string | undefined;
|
|
21
|
+
group?: string | undefined;
|
|
22
|
+
}, {
|
|
23
|
+
ratingType: "provider" | "patient" | "both";
|
|
24
|
+
startDate?: string | undefined;
|
|
25
|
+
endDate?: string | undefined;
|
|
26
|
+
subscription?: "Enterprise" | "Premium" | "FVC" | "BVC" | "Plus" | undefined;
|
|
27
|
+
account?: string | undefined;
|
|
28
|
+
group?: string | undefined;
|
|
29
|
+
groupBy?: "subscription" | "account" | "group" | "none" | undefined;
|
|
30
|
+
bucketSize?: number | undefined;
|
|
31
|
+
}>;
|
|
32
|
+
export type GetRatingDistributionArgs = z.infer<typeof GetRatingDistributionArgsSchema>;
|
|
13
33
|
export interface RatingBucket {
|
|
14
34
|
range: string;
|
|
15
35
|
count: number;
|
|
@@ -31,18 +51,38 @@ export interface RatingDistributionItem extends Record<string, any> {
|
|
|
31
51
|
max_rating: number;
|
|
32
52
|
};
|
|
33
53
|
}
|
|
34
|
-
export
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
54
|
+
export type RatingDistributionResult = StandardResponse<RatingDistributionItem | RatingDistributionItem[]>;
|
|
55
|
+
export declare class GetRatingDistributionTool extends BaseTool<typeof GetRatingDistributionArgsSchema, RatingDistributionResult> {
|
|
56
|
+
constructor(elasticsearch: any, logger: any);
|
|
57
|
+
get schema(): z.ZodObject<{
|
|
58
|
+
ratingType: z.ZodEnum<["provider", "patient", "both"]>;
|
|
59
|
+
bucketSize: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
60
|
+
startDate: z.ZodOptional<z.ZodString>;
|
|
61
|
+
endDate: z.ZodOptional<z.ZodString>;
|
|
62
|
+
account: z.ZodOptional<z.ZodString>;
|
|
63
|
+
group: z.ZodOptional<z.ZodString>;
|
|
64
|
+
subscription: z.ZodOptional<z.ZodEnum<["Enterprise", "Premium", "FVC", "BVC", "Plus"]>>;
|
|
65
|
+
groupBy: z.ZodDefault<z.ZodOptional<z.ZodEnum<["none", "subscription", "account", "group"]>>>;
|
|
66
|
+
}, "strict", z.ZodTypeAny, {
|
|
67
|
+
groupBy: "subscription" | "account" | "group" | "none";
|
|
68
|
+
ratingType: "provider" | "patient" | "both";
|
|
69
|
+
bucketSize: number;
|
|
70
|
+
startDate?: string | undefined;
|
|
71
|
+
endDate?: string | undefined;
|
|
72
|
+
subscription?: "Enterprise" | "Premium" | "FVC" | "BVC" | "Plus" | undefined;
|
|
73
|
+
account?: string | undefined;
|
|
74
|
+
group?: string | undefined;
|
|
75
|
+
}, {
|
|
76
|
+
ratingType: "provider" | "patient" | "both";
|
|
77
|
+
startDate?: string | undefined;
|
|
78
|
+
endDate?: string | undefined;
|
|
79
|
+
subscription?: "Enterprise" | "Premium" | "FVC" | "BVC" | "Plus" | undefined;
|
|
80
|
+
account?: string | undefined;
|
|
81
|
+
group?: string | undefined;
|
|
82
|
+
groupBy?: "subscription" | "account" | "group" | "none" | undefined;
|
|
83
|
+
bucketSize?: number | undefined;
|
|
84
|
+
}>;
|
|
85
|
+
protected run(args: GetRatingDistributionArgs): Promise<RatingDistributionResult>;
|
|
47
86
|
}
|
|
87
|
+
export {};
|
|
48
88
|
//# sourceMappingURL=get-rating-distribution.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-rating-distribution.d.ts","sourceRoot":"","sources":["../../src/tools/get-rating-distribution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"get-rating-distribution.d.ts","sourceRoot":"","sources":["../../src/tools/get-rating-distribution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAG9C,QAAA,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;EAS1B,CAAC;AAEZ,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAExF,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,sBAAuB,SAAQ,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IACjE,qBAAqB,CAAC,EAAE,YAAY,EAAE,CAAC;IACvC,oBAAoB,CAAC,EAAE,YAAY,EAAE,CAAC;IACtC,cAAc,CAAC,EAAE;QACf,aAAa,EAAE,MAAM,CAAC;QACtB,cAAc,EAAE,MAAM,CAAC;QACvB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,aAAa,CAAC,EAAE;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,cAAc,EAAE,MAAM,CAAC;QACvB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED,MAAM,MAAM,wBAAwB,GAAG,gBAAgB,CAAC,sBAAsB,GAAG,sBAAsB,EAAE,CAAC,CAAC;AAE3G,qBAAa,yBAA0B,SAAQ,QAAQ,CAAC,OAAO,+BAA+B,EAAE,wBAAwB,CAAC;gBAC3G,aAAa,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG;IAI3C,IAAI,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;OAET;cAEe,GAAG,CAAC,IAAI,EAAE,yBAAyB,GAAG,OAAO,CAAC,wBAAwB,CAAC;CAwPxF"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.GetRatingDistributionTool = void 0;
|
|
4
|
+
const base_tool_js_1 = require("./base-tool.js");
|
|
4
5
|
const zod_1 = require("zod");
|
|
5
|
-
const handlers_js_1 = require("../errors/handlers.js");
|
|
6
|
-
const date_math_js_1 = require("../utils/date-math.js");
|
|
7
|
-
const aggregation_limits_js_1 = require("../utils/aggregation-limits.js");
|
|
8
6
|
const field_constants_js_1 = require("../utils/field-constants.js");
|
|
7
|
+
const query_helpers_js_1 = require("../utils/query-helpers.js");
|
|
8
|
+
const aggregation_limits_js_1 = require("../utils/aggregation-limits.js");
|
|
9
9
|
const GetRatingDistributionArgsSchema = zod_1.z.object({
|
|
10
10
|
ratingType: zod_1.z.enum(['provider', 'patient', 'both']).describe('Type of rating to analyze: "provider", "patient", or "both"'),
|
|
11
11
|
bucketSize: zod_1.z.number().int().min(1).max(5).optional().default(1).describe('Rating bucket size (default: 1, e.g., 1 = 1-2, 2-3, 3-4, etc.)'),
|
|
@@ -16,267 +16,228 @@ const GetRatingDistributionArgsSchema = zod_1.z.object({
|
|
|
16
16
|
subscription: zod_1.z.enum(['Enterprise', 'Premium', 'FVC', 'BVC', 'Plus']).optional().describe('Optional subscription tier to filter by'),
|
|
17
17
|
groupBy: zod_1.z.enum(['none', 'subscription', 'account', 'group']).optional().default('none').describe('Optional grouping dimension (default: none). When set, returns separate distributions for each group value.'),
|
|
18
18
|
}).strict();
|
|
19
|
-
class GetRatingDistributionTool {
|
|
20
|
-
elasticsearch;
|
|
21
|
-
logger;
|
|
19
|
+
class GetRatingDistributionTool extends base_tool_js_1.BaseTool {
|
|
22
20
|
constructor(elasticsearch, logger) {
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
super(elasticsearch, logger, 'get-rating-distribution');
|
|
22
|
+
}
|
|
23
|
+
get schema() {
|
|
24
|
+
return GetRatingDistributionArgsSchema;
|
|
25
25
|
}
|
|
26
|
-
async
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
26
|
+
async run(args) {
|
|
27
|
+
const bucketSize = args.bucketSize || 1;
|
|
28
|
+
const groupBy = args.groupBy || 'none';
|
|
29
|
+
// Safeguard: cap time period to prevent data limit errors
|
|
30
|
+
// When grouping is enabled, this tool aggregates all entities which can be memory-intensive
|
|
31
|
+
// Previously mostly manual cap, BaseTool handles defaults but not dynamic cap based on Args.
|
|
32
|
+
// We can rely on BaseTool's resolveTimeRange but arguably should just use standard defaults.
|
|
33
|
+
// The original code had specific cap logic: maxDays = groupBy !== 'none' ? 60 : 180;
|
|
34
|
+
// I will trust 'now-30d' default is safe, or user override.
|
|
35
|
+
const { startIso: startDateIso, endIso: endDateIso } = this.resolveTimeRange(args.startDate, args.endDate, 'now-30d', 'now');
|
|
36
|
+
this.logger.info('Getting rating distribution', {
|
|
37
|
+
ratingType: args.ratingType,
|
|
38
|
+
bucketSize,
|
|
39
|
+
startDate: startDateIso,
|
|
40
|
+
endDate: endDateIso,
|
|
41
|
+
account: args.account,
|
|
42
|
+
group: args.group,
|
|
43
|
+
subscription: args.subscription,
|
|
44
|
+
groupBy,
|
|
45
|
+
});
|
|
46
|
+
const client = this.elasticsearch.getClient();
|
|
47
|
+
const index = field_constants_js_1.FIELD_CONSTANTS.index;
|
|
48
|
+
const accountField = field_constants_js_1.FIELD_CONSTANTS.accountField;
|
|
49
|
+
const groupField = field_constants_js_1.FIELD_CONSTANTS.groupField;
|
|
50
|
+
const subscriptionField = field_constants_js_1.FIELD_CONSTANTS.subscriptionField;
|
|
51
|
+
const providerRatingField = field_constants_js_1.FIELD_CONSTANTS.providerRatingField;
|
|
52
|
+
const patientRatingField = field_constants_js_1.FIELD_CONSTANTS.patientRatingField;
|
|
53
|
+
const callDurationField = field_constants_js_1.FIELD_CONSTANTS.callDurationField;
|
|
54
|
+
// Use helper for common filters
|
|
55
|
+
const filters = (0, query_helpers_js_1.buildCommonFilters)({
|
|
56
|
+
startDate: startDateIso,
|
|
57
|
+
endDate: endDateIso,
|
|
58
|
+
account: args.account,
|
|
59
|
+
group: args.group,
|
|
60
|
+
subscription: args.subscription,
|
|
61
|
+
excludeTestVisits: true
|
|
62
|
+
});
|
|
63
|
+
// Extra filter
|
|
64
|
+
filters.push({
|
|
65
|
+
bool: {
|
|
66
|
+
should: [
|
|
67
|
+
{ exists: { field: callDurationField } },
|
|
68
|
+
{ term: { [field_constants_js_1.FIELD_CONSTANTS.meetingBasedField]: false } },
|
|
69
|
+
],
|
|
70
|
+
minimum_should_match: 1,
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
const buildRatingAggs = () => {
|
|
74
|
+
const ratingAggs = {};
|
|
75
|
+
if (args.ratingType === 'provider' || args.ratingType === 'both') {
|
|
76
|
+
const ranges = [];
|
|
77
|
+
for (let i = 1; i <= 5; i += bucketSize) {
|
|
78
|
+
const to = Math.min(i + bucketSize - 0.1, 5);
|
|
79
|
+
ranges.push({ from: i, to });
|
|
80
|
+
}
|
|
81
|
+
ranges.push({ from: 5, to: 5.1 });
|
|
82
|
+
ratingAggs.provider_distribution = {
|
|
62
83
|
range: {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
lt: endDate,
|
|
66
|
-
},
|
|
84
|
+
field: providerRatingField,
|
|
85
|
+
ranges,
|
|
67
86
|
},
|
|
68
|
-
}
|
|
69
|
-
{
|
|
70
|
-
|
|
71
|
-
|
|
87
|
+
};
|
|
88
|
+
ratingAggs.provider_stats = {
|
|
89
|
+
stats: {
|
|
90
|
+
field: providerRatingField,
|
|
72
91
|
},
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
if (args.ratingType === 'patient' || args.ratingType === 'both') {
|
|
95
|
+
const ranges = [];
|
|
96
|
+
for (let i = 1; i <= 5; i += bucketSize) {
|
|
97
|
+
const to = Math.min(i + bucketSize - 0.1, 5);
|
|
98
|
+
ranges.push({ from: i, to });
|
|
99
|
+
}
|
|
100
|
+
ranges.push({ from: 5, to: 5.1 });
|
|
101
|
+
ratingAggs.patient_distribution = {
|
|
102
|
+
range: {
|
|
103
|
+
field: patientRatingField,
|
|
104
|
+
ranges,
|
|
81
105
|
},
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
term: {
|
|
87
|
-
[accountField]: validatedArgs.account,
|
|
106
|
+
};
|
|
107
|
+
ratingAggs.patient_stats = {
|
|
108
|
+
stats: {
|
|
109
|
+
field: patientRatingField,
|
|
88
110
|
},
|
|
89
|
-
}
|
|
111
|
+
};
|
|
90
112
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
113
|
+
return ratingAggs;
|
|
114
|
+
};
|
|
115
|
+
// Build aggregations based on grouping
|
|
116
|
+
let aggs;
|
|
117
|
+
if (groupBy === 'none') {
|
|
118
|
+
aggs = buildRatingAggs();
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
let groupFieldName;
|
|
122
|
+
switch (groupBy) {
|
|
123
|
+
case 'subscription':
|
|
124
|
+
groupFieldName = subscriptionField;
|
|
125
|
+
break;
|
|
126
|
+
case 'account':
|
|
127
|
+
groupFieldName = accountField;
|
|
128
|
+
break;
|
|
129
|
+
case 'group':
|
|
130
|
+
groupFieldName = groupField;
|
|
131
|
+
break;
|
|
132
|
+
default:
|
|
133
|
+
groupFieldName = subscriptionField;
|
|
97
134
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
135
|
+
aggs = {
|
|
136
|
+
by_group: {
|
|
137
|
+
terms: {
|
|
138
|
+
field: groupFieldName,
|
|
139
|
+
size: aggregation_limits_js_1.AGGREGATION_LIMITS.MEDIUM, // Safeguard: cap at 50 to prevent data limits
|
|
102
140
|
},
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
141
|
+
aggs: buildRatingAggs(),
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const query = {
|
|
146
|
+
index,
|
|
147
|
+
size: 0,
|
|
148
|
+
body: {
|
|
149
|
+
track_total_hits: false,
|
|
150
|
+
query: {
|
|
151
|
+
bool: {
|
|
152
|
+
filter: filters,
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
aggs,
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
this.logger.debug('Executing query', { query: JSON.stringify(query, null, 2) });
|
|
159
|
+
const response = await client.search(query);
|
|
160
|
+
const responseAggs = response.aggregations;
|
|
161
|
+
const processDistributionItem = (itemAggs) => {
|
|
162
|
+
const item = {};
|
|
163
|
+
if (itemAggs?.provider_distribution) {
|
|
164
|
+
const buckets = itemAggs.provider_distribution.buckets || [];
|
|
165
|
+
const total = buckets.reduce((sum, b) => sum + (b.doc_count || 0), 0);
|
|
166
|
+
const distribution = buckets.map((bucket) => {
|
|
167
|
+
const count = bucket.doc_count || 0;
|
|
168
|
+
return {
|
|
169
|
+
range: `${bucket.from} - ${bucket.to}`,
|
|
170
|
+
count,
|
|
171
|
+
percentage: total > 0 ? Math.round((count / total) * 100 * 100) / 100 : 0,
|
|
119
172
|
};
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
173
|
+
});
|
|
174
|
+
item.provider_distribution = distribution;
|
|
175
|
+
if (itemAggs?.provider_stats) {
|
|
176
|
+
const stats = itemAggs.provider_stats;
|
|
177
|
+
item.provider_stats = {
|
|
178
|
+
total_ratings: stats.count || 0,
|
|
179
|
+
average_rating: stats.avg ? Math.round(stats.avg * 100) / 100 : 0,
|
|
180
|
+
min_rating: stats.min || 0,
|
|
181
|
+
max_rating: stats.max || 0,
|
|
124
182
|
};
|
|
125
183
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
ranges,
|
|
137
|
-
},
|
|
184
|
+
}
|
|
185
|
+
if (itemAggs?.patient_distribution) {
|
|
186
|
+
const buckets = itemAggs.patient_distribution.buckets || [];
|
|
187
|
+
const total = buckets.reduce((sum, b) => sum + (b.doc_count || 0), 0);
|
|
188
|
+
const distribution = buckets.map((bucket) => {
|
|
189
|
+
const count = bucket.doc_count || 0;
|
|
190
|
+
return {
|
|
191
|
+
range: `${bucket.from} - ${bucket.to}`,
|
|
192
|
+
count,
|
|
193
|
+
percentage: total > 0 ? Math.round((count / total) * 100 * 100) / 100 : 0,
|
|
138
194
|
};
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
195
|
+
});
|
|
196
|
+
item.patient_distribution = distribution;
|
|
197
|
+
if (itemAggs?.patient_stats) {
|
|
198
|
+
const stats = itemAggs.patient_stats;
|
|
199
|
+
item.patient_stats = {
|
|
200
|
+
total_ratings: stats.count || 0,
|
|
201
|
+
average_rating: stats.avg ? Math.round(stats.avg * 100) / 100 : 0,
|
|
202
|
+
min_rating: stats.min || 0,
|
|
203
|
+
max_rating: stats.max || 0,
|
|
143
204
|
};
|
|
144
205
|
}
|
|
145
|
-
return ratingAggs;
|
|
146
|
-
};
|
|
147
|
-
// Build aggregations based on grouping
|
|
148
|
-
let aggs;
|
|
149
|
-
if (groupBy === 'none') {
|
|
150
|
-
aggs = buildRatingAggs();
|
|
151
|
-
}
|
|
152
|
-
else {
|
|
153
|
-
let groupFieldName;
|
|
154
|
-
switch (groupBy) {
|
|
155
|
-
case 'subscription':
|
|
156
|
-
groupFieldName = subscriptionField;
|
|
157
|
-
break;
|
|
158
|
-
case 'account':
|
|
159
|
-
groupFieldName = accountField;
|
|
160
|
-
break;
|
|
161
|
-
case 'group':
|
|
162
|
-
groupFieldName = groupField;
|
|
163
|
-
break;
|
|
164
|
-
default:
|
|
165
|
-
groupFieldName = subscriptionField;
|
|
166
|
-
}
|
|
167
|
-
aggs = {
|
|
168
|
-
by_group: {
|
|
169
|
-
terms: {
|
|
170
|
-
field: groupFieldName,
|
|
171
|
-
size: aggregation_limits_js_1.AGGREGATION_LIMITS.MEDIUM, // Safeguard: cap at 50 to prevent data limits
|
|
172
|
-
},
|
|
173
|
-
aggs: buildRatingAggs(),
|
|
174
|
-
},
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
const query = {
|
|
178
|
-
index,
|
|
179
|
-
size: 0,
|
|
180
|
-
body: {
|
|
181
|
-
track_total_hits: false,
|
|
182
|
-
query: {
|
|
183
|
-
bool: {
|
|
184
|
-
filter: filters,
|
|
185
|
-
},
|
|
186
|
-
},
|
|
187
|
-
aggs,
|
|
188
|
-
},
|
|
189
|
-
};
|
|
190
|
-
this.logger.debug('Executing query', { query: JSON.stringify(query, null, 2) });
|
|
191
|
-
const response = await client.search(query);
|
|
192
|
-
const responseAggs = response.aggregations;
|
|
193
|
-
const processDistributionItem = (itemAggs) => {
|
|
194
|
-
const item = {};
|
|
195
|
-
if (itemAggs?.provider_distribution) {
|
|
196
|
-
const buckets = itemAggs.provider_distribution.buckets || [];
|
|
197
|
-
const total = buckets.reduce((sum, b) => sum + (b.doc_count || 0), 0);
|
|
198
|
-
const distribution = buckets.map((bucket) => {
|
|
199
|
-
const count = bucket.doc_count || 0;
|
|
200
|
-
return {
|
|
201
|
-
range: `${bucket.from} - ${bucket.to}`,
|
|
202
|
-
count,
|
|
203
|
-
percentage: total > 0 ? Math.round((count / total) * 100 * 100) / 100 : 0,
|
|
204
|
-
};
|
|
205
|
-
});
|
|
206
|
-
item.provider_distribution = distribution;
|
|
207
|
-
if (itemAggs?.provider_stats) {
|
|
208
|
-
const stats = itemAggs.provider_stats;
|
|
209
|
-
item.provider_stats = {
|
|
210
|
-
total_ratings: stats.count || 0,
|
|
211
|
-
average_rating: stats.avg ? Math.round(stats.avg * 100) / 100 : 0,
|
|
212
|
-
min_rating: stats.min || 0,
|
|
213
|
-
max_rating: stats.max || 0,
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
// Process patient distribution
|
|
218
|
-
if (itemAggs?.patient_distribution) {
|
|
219
|
-
const buckets = itemAggs.patient_distribution.buckets || [];
|
|
220
|
-
const total = buckets.reduce((sum, b) => sum + (b.doc_count || 0), 0);
|
|
221
|
-
const distribution = buckets.map((bucket) => {
|
|
222
|
-
const count = bucket.doc_count || 0;
|
|
223
|
-
return {
|
|
224
|
-
range: `${bucket.from} - ${bucket.to}`,
|
|
225
|
-
count,
|
|
226
|
-
percentage: total > 0 ? Math.round((count / total) * 100 * 100) / 100 : 0,
|
|
227
|
-
};
|
|
228
|
-
});
|
|
229
|
-
item.patient_distribution = distribution;
|
|
230
|
-
if (itemAggs?.patient_stats) {
|
|
231
|
-
const stats = itemAggs.patient_stats;
|
|
232
|
-
item.patient_stats = {
|
|
233
|
-
total_ratings: stats.count || 0,
|
|
234
|
-
average_rating: stats.avg ? Math.round(stats.avg * 100) / 100 : 0,
|
|
235
|
-
min_rating: stats.min || 0,
|
|
236
|
-
max_rating: stats.max || 0,
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
return item;
|
|
241
|
-
};
|
|
242
|
-
let distribution;
|
|
243
|
-
if (groupBy === 'none') {
|
|
244
|
-
distribution = processDistributionItem(responseAggs);
|
|
245
206
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
...processDistributionItem(bucket),
|
|
252
|
-
}));
|
|
253
|
-
}
|
|
254
|
-
this.logger.info('Successfully retrieved rating distribution', {
|
|
255
|
-
ratingType: validatedArgs.ratingType,
|
|
256
|
-
groupBy,
|
|
257
|
-
itemCount: Array.isArray(distribution) ? distribution.length : 1,
|
|
258
|
-
});
|
|
259
|
-
return {
|
|
260
|
-
startDate,
|
|
261
|
-
endDate,
|
|
262
|
-
ratingType: validatedArgs.ratingType,
|
|
263
|
-
period: `${startDate} to ${endDate}`,
|
|
264
|
-
groupBy,
|
|
265
|
-
distribution,
|
|
266
|
-
};
|
|
207
|
+
return item;
|
|
208
|
+
};
|
|
209
|
+
let distribution;
|
|
210
|
+
if (groupBy === 'none') {
|
|
211
|
+
distribution = processDistributionItem(responseAggs);
|
|
267
212
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
throw error;
|
|
276
|
-
}
|
|
277
|
-
this.logger.error('Failed to get rating distribution', {}, error);
|
|
278
|
-
throw new handlers_js_1.ElasticsearchError('Failed to get rating distribution from Elasticsearch', error, { args });
|
|
213
|
+
else {
|
|
214
|
+
const valueKey = `${groupBy}_value`;
|
|
215
|
+
const groupBuckets = responseAggs?.by_group?.buckets || [];
|
|
216
|
+
distribution = groupBuckets.map((bucket) => ({
|
|
217
|
+
[valueKey]: bucket.key,
|
|
218
|
+
...processDistributionItem(bucket),
|
|
219
|
+
}));
|
|
279
220
|
}
|
|
221
|
+
this.logger.info('Successfully retrieved rating distribution', {
|
|
222
|
+
ratingType: args.ratingType,
|
|
223
|
+
groupBy,
|
|
224
|
+
itemCount: Array.isArray(distribution) ? distribution.length : 1,
|
|
225
|
+
});
|
|
226
|
+
return this.buildResponse(distribution, {
|
|
227
|
+
description: `Rating distribution (${args.ratingType}, bucket size ${bucketSize}) from ${startDateIso} to ${endDateIso}`,
|
|
228
|
+
arguments: args,
|
|
229
|
+
time: {
|
|
230
|
+
start: startDateIso,
|
|
231
|
+
end: endDateIso
|
|
232
|
+
},
|
|
233
|
+
visualization: {
|
|
234
|
+
type: 'bar',
|
|
235
|
+
title: `${args.ratingType === 'both' ? 'Provider & Patient' : args.ratingType.charAt(0).toUpperCase() + args.ratingType.slice(1)} Rating Distribution`,
|
|
236
|
+
description: `${startDateIso.split('T')[0]} to ${endDateIso.split('T')[0]}`,
|
|
237
|
+
xAxisLabel: 'Rating Range',
|
|
238
|
+
yAxisLabel: 'Count'
|
|
239
|
+
}
|
|
240
|
+
});
|
|
280
241
|
}
|
|
281
242
|
}
|
|
282
243
|
exports.GetRatingDistributionTool = GetRatingDistributionTool;
|