fix(analytics.gblib): Fixes in database storage.

This commit is contained in:
Rodrigo Rodriguez 2020-01-27 16:19:09 -03:00
parent 5d6dacc910
commit 22f4250831
12 changed files with 177 additions and 128 deletions

11
.vscode/tasks.json vendored
View file

@ -14,6 +14,17 @@
"kind": "build",
"isDefault": true
}
},
{
"type": "typescript",
"tsconfig": "tsconfig.json",
"problemMatcher": [
"$tsc"
],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

View file

@ -6,15 +6,9 @@ const { exec } = require('child_process');
// Displays version of Node JS being used at runtime and others attributes.
console.log(`[GB Runtime] version = ${process.version}`);
console.log(`[GB Runtime] env = ${process.env}`);
console.log(`[GB Runtime] NodeJS = ${process.version}`);
console.log(`[GB Runtime] platform = ${process.platform}`);
console.log(`[GB Runtime] release = ${process.release}`);
console.log(`[GB Runtime] argv = ${process.argv}`);
console.log(`[GB Runtime] env.USER = ${process.env.USER}`);
console.log(`[GB Runtime] env.PATH = ${process.env.PATH.split(':').join('\n')}`);
console.log(`[GB Runtime] env.PWD = ${process.env.PWD}`);
console.log(`[GB Runtime] env.HOME = ${process.env.HOME}`);
console.log(`[GB Runtime] debugPort = ${process.debugPort}`);
var now = () => {

View file

@ -38,6 +38,7 @@
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
import { Sequelize } from 'sequelize-typescript';
import { GuaribasConversation, GuaribasConversationMessage } from './models';
/**
* .gblib Package handler.
@ -49,6 +50,8 @@ export class GBAnalyticsPackage implements IGBPackage {
}
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
GBLog.verbose(`loadPackage called.`);
core.sequelize.addModels([GuaribasConversation, GuaribasConversationMessage]);
}
public unloadPackage(core: IGBCoreService): void {
GBLog.verbose(`unloadPackage called.`);

View file

@ -58,6 +58,50 @@ import { GuaribasChannel, GuaribasInstance } from '../../core.gbapp/models/GBMod
import { GuaribasSubject } from '../../kb.gbapp/models';
import { GuaribasUser } from '../../security.gblib/models';
/**
* A conversation that groups many messages.
*/
@Table
export class GuaribasConversation extends Model<GuaribasConversation> {
@PrimaryKey
@AutoIncrement
@Column
public conversationId: number;
@ForeignKey(() => GuaribasSubject)
@Column
public startSubjectId: number;
@BelongsTo(() => GuaribasSubject)
public startSubject: GuaribasSubject;
@ForeignKey(() => GuaribasChannel)
@Column
public channelId: string;
@Column public rateDate: Date;
@Column(DataType.FLOAT)
@Column
public rate: number;
@Column
@CreatedAt
public createdAt: Date;
@Column public text: string;
@ForeignKey(() => GuaribasUser)
@Column
public startedByUserId: number;
@BelongsTo(() => GuaribasUser)
public startedBy: GuaribasUser;
}
/**
* A single message in a conversation.
*/
@ -104,48 +148,3 @@ export class GuaribasConversationMessage extends Model<GuaribasConversationMessa
@BelongsTo(() => GuaribasUser)
public user: GuaribasUser;
}
/**
* A conversation that groups many messages.
*/
@Table
export class GuaribasConversation extends Model<GuaribasConversation> {
@PrimaryKey
@AutoIncrement
@Column
public conversationId: number;
@ForeignKey(() => GuaribasSubject)
@Column
public startSubjectId: number;
@BelongsTo(() => GuaribasSubject)
public startSubject: GuaribasSubject;
@ForeignKey(() => GuaribasChannel)
@Column
public channelId: string;
@Column public rateDate: Date;
@Column(DataType.FLOAT)
@Column
public rate: number;
@Column
@CreatedAt
public createdAt: Date;
@Column public text: string;
@HasMany(() => GuaribasConversationMessage)
public conversationMessage: GuaribasConversationMessage[];
@ForeignKey(() => GuaribasUser)
@Column
public startedByUserId: number;
@BelongsTo(() => GuaribasUser)
public startedBy: GuaribasUser;
}

View file

@ -52,15 +52,34 @@ export class AnalyticsService {
return await conversation.save();
}
public async updateConversationRate(
instanceId: number,
conversationId: number,
rate: number
): Promise<GuaribasConversation> {
const options = { where: {} };
// TODO: Filter by instanceId: instanceId
options.where = { conversationId: conversationId };
const item = await GuaribasConversation.findOne(options);
item.rate = rate;
item.rateDate = new Date();
return item.save();
}
public async createMessage(
instanceId: number,
conversation: GuaribasConversation,
user: GuaribasUser,
content: string
): Promise<GuaribasConversationMessage> {
const message = GuaribasConversationMessage.build();
message.conversation = conversation;
message.user = user;
message.content = content;
message.instanceId = instanceId;
message.userId = user.userId;
message.conversationId = conversation.conversationId;
return await message.save();
}
}

View file

@ -382,11 +382,11 @@ STORAGE_SYNC=true
const sysPackages: IGBPackage[] = [];
[
GBAdminPackage,
GBAnalyticsPackage,
GBCorePackage,
GBSecurityPackage,
GBKBPackage,
GBCustomerSatisfactionPackage,
GBAnalyticsPackage,
GBWhatsappPackage,
GBAzureDeployerPackage,
GBSharePointPackage,

View file

@ -58,19 +58,12 @@ import {
import { MicrosoftAppCredentials } from 'botframework-connector';
import { GBServer } from '../../../src/app';
import { GBAnalyticsPackage } from '../../analytics.gblib';
import { GBCorePackage } from '../../core.gbapp';
import { GBCustomerSatisfactionPackage } from '../../customer-satisfaction.gbapp';
import { GBKBPackage } from '../../kb.gbapp';
import { AskDialogArgs } from '../../kb.gbapp/dialogs/AskDialog';
import { GBSecurityPackage } from '../../security.gblib';
import { GBWhatsappPackage } from '../../whatsapp.gblib';
import { Messages } from '../strings';
import { GBAdminPackage } from './../../admin.gbapp/index';
import { GBConfigService } from './GBConfigService';
import { GBDeployer } from './GBDeployer';
import { SecService } from '../../security.gblib/services/SecService';
import { isBreakOrContinueStatement } from 'typescript';
import { AnalyticsService } from '../../analytics.gblib/services/AnalyticsService';
/**
* Minimal service layer for a bot.
@ -124,7 +117,7 @@ export class GBMinService {
if (process.env.DISABLE_WEB !== 'true') {
GBServer.globals.server.get('/instances/:botId', (req, res) => {
(async () => {
await this.handleGetInstanceFroClient(req, res);
await this.handleGetInstanceForClient(req, res);
})();
});
}
@ -189,7 +182,7 @@ export class GBMinService {
public async mountBot(instance: IGBInstance) {
// Build bot adapter.
const { min, adapter, conversationState } = await this.buildBotAdapter(instance, GBServer.globals.publicAddress, GBServer.globals.sysPackages);
const { min, adapter, conversationState } = await this.buildBotAdapter(instance, GBServer.globals.sysPackages);
if (GBServer.globals.minInstances.length === 0) {
GBServer.globals.minBoot = min;
@ -201,7 +194,7 @@ export class GBMinService {
// this.deployer.deployPackage(min, 'packages/default.gbdialog');
// Call the loadBot context.activity for all packages.
this.invokeLoadBot(GBServer.globals.appPackages, GBServer.globals.sysPackages, min, GBServer.globals.server);
this.invokeLoadBot(GBServer.globals.appPackages, GBServer.globals.sysPackages, min);
// Serves individual URL for each bot conversational interface...
const url = `/api/messages/${instance.botId}`;
@ -280,7 +273,7 @@ export class GBMinService {
/**
* Returns the instance object to clients requesting bot info.
*/
private async handleGetInstanceFroClient(req: any, res: any) {
private async handleGetInstanceForClient(req: any, res: any) {
let botId = req.params.botId;
if (botId === '[default]' || botId === undefined) {
botId = GBConfigService.get('BOT_ID');
@ -365,7 +358,7 @@ export class GBMinService {
}
}
private async buildBotAdapter(instance: any, proxyAddress: string, sysPackages: IGBPackage[]) {
private async buildBotAdapter(instance: any, sysPackages: IGBPackage[]) {
const adapter = new BotFrameworkAdapter({
appId: instance.marketplaceId,
appPassword: instance.marketplacePassword
@ -404,11 +397,9 @@ export class GBMinService {
return { min, adapter, conversationState };
}
private invokeLoadBot(appPackages: IGBPackage[], sysPackages: IGBPackage[], min: GBMinInstance, server: any) {
let index = 0;
private invokeLoadBot(appPackages: IGBPackage[], sysPackages: IGBPackage[], min: GBMinInstance) {
sysPackages.forEach(e => {
e.loadBot(min);
index++;
}, this);
appPackages.forEach(p => {
@ -456,15 +447,24 @@ export class GBMinService {
user.loaded = true;
user.subjects = [];
user.cb = undefined;
await min.userProfile.set(step.context, user);
if (context.activity.membersAdded !== undefined) {
let sec = new SecService();
const member = context.activity.membersAdded[0];
await sec.ensureUser(instance.instanceId, member.id,
const persistedUser = await sec.ensureUser(instance.instanceId, member.id,
min.botId, member.id, "", "web", member.name, member.id);
const analytics = new AnalyticsService();
user.systemUser = persistedUser;
user.conversation = await analytics.createConversation(persistedUser);
}
await min.userProfile.set(step.context, user);
}
GBLog.info(
@ -534,7 +534,17 @@ export class GBMinService {
}
private async processMessageActivity(context, min: GBMinInstance, step: GBDialogStep) {
// Direct script invoking by itent name.
// Adds message to the analytics layer.
const analytics = new AnalyticsService();
const user = await min.userProfile.get(context, {});
analytics.createMessage(min.instance.instanceId,
user.conversation, user.systemUser,
context.activity.text);
// Checks for global exit kewywords cancelling any active dialogs.
const globalQuit = (locale, utterance) => {
return utterance.match(Messages[locale].global_quit);
}
@ -546,7 +556,7 @@ export class GBMinService {
if (hasBadWord) {
await step.beginDialog('/pleaseNoBadWords');
}else if (isVMCall) {
} else if (isVMCall) {
await GBMinService.callVM(context.activity.text, min, step);
} else if (context.activity.text.charAt(0) === '/') {
await step.beginDialog(context.activity.text);
@ -562,7 +572,6 @@ export class GBMinService {
await step.beginDialog('/menu', JSON.parse(context.activity.text));
// Otherwise, continue to the active dialog in the stack.
} else {
const user = await min.userProfile.get(context, {});
if (step.activeDialog !== undefined) {
await step.continueDialog();
} else {

View file

@ -42,6 +42,7 @@ import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs';
import { CSService } from '../services/CSService';
import { Messages } from '../strings';
import { AnalyticsService } from '../../analytics.gblib/services/AnalyticsService';
/**
* Dialog for collecting quality of answer.
@ -78,6 +79,14 @@ export class QualityDialog extends IGBDialog {
user.lastQuestion,
user.lastQuestionId
);
// Updates values to perform Bot Analytics.
const analytics = new AnalyticsService();
analytics.updateConversationRate(min.instance.instanceId, user.conversation, score);
// Goes to the ask loop.
await step.replaceDialog('/ask', { isReturning: true });
}

View file

@ -2818,6 +2818,15 @@
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8="
},
"browser-id": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/browser-id/-/browser-id-1.1.0.tgz",
"integrity": "sha512-MuvthhJ8pDjIYpJzHwYOrlFtTPhEOF3wk9AVvy19pLQwmS/OdGWptuhzjkgLnDJS3WgV2m7bLDvlCwufg1jWBA==",
"requires": {
"uuid": "^3.3.3",
"versioned-storage": "^1.1.0"
}
},
"browser-process-hrtime": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz",
@ -6058,6 +6067,11 @@
"locate-path": "^3.0.0"
}
},
"fingerprintjs2": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fingerprintjs2/-/fingerprintjs2-2.1.0.tgz",
"integrity": "sha512-H1k/ESTD2rJ3liupyqWBPjZC+LKfCGixQzz/NDN4dkgbmG1bVFyMOh7luKSkVDoyfhgvRm62pviNMPI+eJTZcQ=="
},
"flat-cache": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
@ -7639,8 +7653,7 @@
},
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
"optional": true
"bundled": true
},
"aproba": {
"version": "1.2.0",
@ -7677,8 +7690,7 @@
},
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"optional": true
"bundled": true
},
"concat-map": {
"version": "0.0.1",
@ -7687,8 +7699,7 @@
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"optional": true
"bundled": true
},
"core-util-is": {
"version": "1.0.2",
@ -7791,8 +7802,7 @@
},
"inherits": {
"version": "2.0.4",
"bundled": true,
"optional": true
"bundled": true
},
"ini": {
"version": "1.3.5",
@ -7802,7 +7812,6 @@
"is-fullwidth-code-point": {
"version": "1.0.0",
"bundled": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@ -7822,13 +7831,11 @@
},
"minimist": {
"version": "0.0.8",
"bundled": true,
"optional": true
"bundled": true
},
"minipass": {
"version": "2.9.0",
"bundled": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@ -7845,7 +7852,6 @@
"mkdirp": {
"version": "0.5.1",
"bundled": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@ -7926,8 +7932,7 @@
},
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"optional": true
"bundled": true
},
"object-assign": {
"version": "4.1.1",
@ -7937,7 +7942,6 @@
"once": {
"version": "1.4.0",
"bundled": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@ -8013,8 +8017,7 @@
},
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
"optional": true
"bundled": true
},
"safer-buffer": {
"version": "2.1.2",
@ -8044,7 +8047,6 @@
"string-width": {
"version": "1.0.2",
"bundled": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@ -8062,7 +8064,6 @@
"strip-ansi": {
"version": "3.0.1",
"bundled": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@ -8101,13 +8102,11 @@
},
"wrappy": {
"version": "1.0.2",
"bundled": true,
"optional": true
"bundled": true
},
"yallist": {
"version": "3.1.1",
"bundled": true,
"optional": true
"bundled": true
}
}
}
@ -14283,6 +14282,11 @@
"extsprintf": "^1.2.0"
}
},
"versioned-storage": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/versioned-storage/-/versioned-storage-1.1.0.tgz",
"integrity": "sha512-oS+unMiWJjaVAFCkNJvmHiu6LDb8KrzT8YmfrQpAmn8/yKnW4Roq3lcVMxQofT9Oidqg8oWChbGYl4FGQJyPPg=="
},
"vfile": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/vfile/-/vfile-3.0.1.tgz",

View file

@ -1,6 +1,6 @@
{
"name": "default.gbui",
"version": "0.0.12",
"version": "0.0.13",
"private": false,
"repository": "https://github.com/pragmatismo-io/BotServer",
"description": "Default web interface for General Bots open-core",
@ -12,6 +12,7 @@
"botframework-webchat": "^4.7.1",
"deep-extend": "0.6.0",
"fetch": "1.1.0",
"fingerprintjs2": "^2.1.0",
"msal": "^1.2.0",
"powerbi-client": "2.10.2",
"react": "^16.12.0",

View file

@ -41,8 +41,8 @@ import GBCss from './components/GBCss.js';
import { DirectLine } from 'botframework-directlinejs';
import { ConnectionStatus } from 'botframework-directlinejs';
import ReactWebChat from 'botframework-webchat';
// import GBPowerBIPlayer from './players/GBPowerBIPlayer.js';
import { UserAgentApplication } from 'msal';
import Fingerprint2 from 'fingerprintjs2';
class GBUIApp extends React.Component {
constructor() {
@ -90,7 +90,8 @@ class GBUIApp extends React.Component {
}
getUser() {
return { id: 'webUser@gb', name: 'You' };
return { id: 'web@gb', name: 'You' };
}
postEvent(name, value) {
@ -145,7 +146,7 @@ class GBUIApp extends React.Component {
let userAgentApplication = new UserAgentApplication(
this.state.instanceClient.authenticatorClientId,
authority,
function(errorDesc, token, error, tokenType) {
function (errorDesc, token, error, tokenType) {
if (error) {
console.log(error);
}
@ -157,10 +158,10 @@ class GBUIApp extends React.Component {
var user = userAgentApplication.getUser();
if (user) {
userAgentApplication.acquireTokenSilent(graphScopes).then(
function(accessToken) {
function (accessToken) {
_this_.sendToken(accessToken);
},
function(error) {
function (error) {
console.log(error);
}
);

View file

@ -65,8 +65,7 @@ export class SecService extends GBService {
user.email = userName;
user.phone = phone;
user.defaultChannel = channelName;
user.save();
return user;
return await user.save();
}
/**