Skip to content

Commit

Permalink
Merge branch 'develop' into SumedhAndAlice/NotificationMessaging
Browse files Browse the repository at this point in the history
  • Loading branch information
hdemusg authored Mar 30, 2021
2 parents 465e73b + 9af28c8 commit 0da9631
Show file tree
Hide file tree
Showing 14 changed files with 601 additions and 78 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ local.settings.json
node_modules
dist


__blobstorage__
__queuestorage__
__azurite_db_blob__.json
Expand Down
20 changes: 20 additions & 0 deletions InputData/function.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"bindings": [
{
"authLevel": "function",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": [
"get",
"post"
]
},
{
"type": "http",
"direction": "out",
"name": "res"
}
],
"scriptFile": "../dist/InputData/index.js"
}
200 changes: 200 additions & 0 deletions InputData/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import { AzureFunction, Context, HttpRequest } from '@azure/functions'
import Mongo from '../ReceiveMessage/Scripts/db'
import xlsxFile from 'read-excel-file/node'
import ChatbotMessage, { IMessage } from '../ReceiveMessage/models/ChatbotMessage'

type MessageToBeCreated = {
record: IMessage
keywords: string[]
nextMessages: number[]
previousMessage: number
}

//https://medium.com/javascript-in-plain-english/how-to-read-an-excel-file-in-node-js-6e669e9a3ce1
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
context.log('HTTP trigger function processed a request.')

const success = true

const path = req.query.path || (req.body && req.body.path)

//from the root dir, for whatever reason
let defaultPath = './InputData/messages.xlsx'
if (path) {
defaultPath = path
}
//CHANGE THIS TO MATCH THE CHATBOTMESSAGE SCHEMA
//TODO: FIgure out keywords
const schema = {
ID: {
prop: 'messageId',
type: Number,
required: true,
},
BODY: {
prop: 'body',
type: String,
required: true,
},
MULTIMEDIA: {
prop: 'multimedia',
type: String,
},
'NEXT MESSAGES': {
prop: 'nextMessages',
type: String,
},
'PREVIOUS MESSAGE': {
prop: 'prevMessage',
type: Number,
},
MODULE: {
prop: 'module',
type: Number,
},
'LOW DATA': {
prop: 'lowDataBody',
type: String, // it is possible to validate each of these values.
required: true,
},
KEYWORDS: {
prop: 'keywords',
type: String,
},
MESSAGETYPE: {
prop: 'messageType',
type: String,
},
}
await Mongo()
const readXlsx = await xlsxFile(defaultPath, { schema })
const rows = readXlsx.rows
const errors = readXlsx.errors
if (errors.length != 0) {
context.log(errors)
let errorBody = `There were ${errors.length} number of errors:\n`
for (const error of errors) {
errorBody += `Row ${error.row}; Col ${error.column}; Error: ${error.error}`
}
context.res = {
// status: 200, /* Defaults to 200 */
body: errorBody,
}
return
}
const messagesToInsert = new Map<number, MessageToBeCreated>()
// 1.) Make a map based upon all of the messageIds, with the value being a chatbotMessage
for (const [i, row] of rows.entries()) {
// 0.) Convert nextMessages into an array; same with keywords:
const nextMessagesArray: number[] = row.nextMessages
.toString()
.split(',')
.map((x: string) => parseInt(x.trim()))
const nextKeywordsArray: string[] = row.keywords
? row.keywords
.toString()
.split(',')
.map((x: string) => x.trim())
: ['default']

if (nextMessagesArray.length != nextKeywordsArray.length) {
context.res = {
// status: 200, /* Defaults to 200 */
body: `on probably line ${i + 2} for the message with id ${
row.messageId
} there was a mismatch between the next messages and number of corresponding keywords.`,
}
return
}

// 1.) query for the messageId
const existingMessage = await ChatbotMessage.findOne({
messageId: row.messageId,
}).exec()

// 2.) If that record exists, set the value to be that chatbotMessage
if (existingMessage) {
existingMessage.body = row.body
existingMessage.image = row.multimedia ? row.multimedia.toString() : ''
existingMessage.module = row.module
existingMessage.messageType = row.messageType
existingMessage.lowData = row.lowDataBody
messagesToInsert.set(row.messageId, {
record: existingMessage,
keywords: nextKeywordsArray,
nextMessages: nextMessagesArray,
previousMessage: row.prevMessage,
})
}
// 3.) else, create a new one
else {
messagesToInsert.set(row.messageId, {
record: new ChatbotMessage({
//put stuff here
messageId: row.messageId,
body: row.body,
nextMessages: {},
image: row.multimedia ? row.multimedia.toString() : '',
module: row.module,
messageType: row.messageType,
lowData: row.lowDataBody,
previousMessage: 'not yet',
}),
keywords: nextKeywordsArray,
nextMessages: nextMessagesArray,
previousMessage: row.prevMessage,
})
}
}

// 2.) Iterate through every entry of the map, and make connections between all of the messages.
for (const [key, record] of messagesToInsert) {
for (let i = 0; i < record.nextMessages.length; i++) {
const nextMessage = messagesToInsert.get(record.nextMessages[i])
const prevMessage = messagesToInsert.get(record.previousMessage)
if (!nextMessage) {
context.res = {
// status: 200, /* Defaults to 200 */
body:
record.nextMessages[i] +
' is not a valid messageId. Please check to make sure this id was put in properly',
}
return
}
if (!prevMessage) {
context.res = {
// status: 200, /* Defaults to 200 */
body:
record.previousMessage +
' is not a valid messageId. Please check to make sure this id was put in properly',
}
return
}
record.record.nextMessages.set(record.keywords[i], nextMessage.record['_id'])
record.record.previousMessage = prevMessage.record['_id']
}
}

// 3.) Save every record. This could be done in conjunction with (2), but save() is safer than insertMany(), and this function
// won't be constantly called, so we can take the hit in efficiency.
// The one caveat is that if there is an error somehow with saving, then it will keep saving everything else, which probably
// isn't desirable. If we do a good job error checking above, though, that would be fine.
for (const [key, record] of messagesToInsert) {
//context.log(record.record)
await record.record.save()
}

let responseMessage = path
? 'Input, ' + path + '. This HTTP triggered function executed successfully.'
: 'This HTTP triggered function executed successfully. Pass a path for not the default excel sheet.'
if (!success) {
responseMessage = 'Your upload was incorrect. Please verify the xlsx spreadsheet format matches.'
}

context.res = {
// status: 200, /* Defaults to 200 */
body: responseMessage,
}
}

export default httpTrigger
Binary file added InputData/messages.xlsx
Binary file not shown.
3 changes: 3 additions & 0 deletions InputData/sample.dat
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "Azure"
}
36 changes: 15 additions & 21 deletions ReceiveMessage/Scripts/readRequest.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,25 @@
import { AzureFunction, Context, HttpRequest } from '@azure/functions';
import * as twilio from 'twilio';
import qs from 'qs';
import MongoConnect from './db';
import UserState from '../models/UserState';

const MessagingResponse = twilio.twiml.MessagingResponse;
import { AzureFunction, Context, HttpRequest } from '@azure/functions'
import * as twilio from 'twilio'
import qs from 'qs'
import MongoConnect from './db'
import UserState from '../models/UserState'

//change to Promise<UserState> later
const getUserState = async function (req: HttpRequest) {
const body = qs.parse(req.body);
const body = qs.parse(req.body)
// do necessary processing on the request (nothing at this point)
await MongoConnect();
const userStateResult = await UserState.find({ userId: body.From as string });
if (userStateResult.length === 0) {
const newUser = new UserState({ userId: body.From });
await MongoConnect()
const userStateResult = await UserState.findOne({ userId: body.From as string })
if (!userStateResult) {
const newUser = new UserState({ userId: body.From })
newUser.save(function (err) {
if (err) {
console.log(err);
console.log(err)
}
``;
});
})
}
// retrieve and return the corresponding state
const id = body.AccountSid;
await MongoConnect();
const result = await UserState.find({ userId: id as string });
return result[0];
};
return userStateResult
}

export default getUserState;
export default getUserState
2 changes: 1 addition & 1 deletion ReceiveMessage/Scripts/sendMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const formResponse = async function (state, message) {
await MongoConnect()
const prev = await ChatbotMessage.findById(p)
const map = prev.nextMessages
let nextChatbotMessageId = map.get(prev.isQuestion ? message : 'default')
let nextChatbotMessageId = map.get(map.size > 1 ? message : 'default')
if (nextChatbotMessageId == null) {
nextChatbotMessageId = '6022178429efc055c8e74e50' //change this to whatever error message we want to send (for now it is welcome message)
}
Expand Down
33 changes: 33 additions & 0 deletions ReceiveMessage/fixedMessages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import ChatbotMessage, { IMessage } from './models/ChatbotMessage'
import db from './Scripts/db'

interface templateFixedMessageHandler {
(messageId: number): Promise<IMessage>
}

const generalHandler: templateFixedMessageHandler = async function (messageId: number): Promise<IMessage> {
db()
return ChatbotMessage.findOne({ messageId: messageId })
}

const fixedMessages: Map<string | qs.ParsedQs | string[] | qs.ParsedQs[], Promise<IMessage>> = new Map<
string | qs.ParsedQs | string[] | qs.ParsedQs[],
Promise<IMessage>
>()

fixedMessages.set('MessagePermission', generalHandler(-1))
fixedMessages.set('DataPermission', generalHandler(0))
fixedMessages.set('Welcome', generalHandler(1))
fixedMessages.set('ModuleOne', generalHandler(100))
fixedMessages.set('ModuleTwo', generalHandler(200))
fixedMessages.set('ModuleThree', generalHandler(300))
fixedMessages.set('ModuleFour', generalHandler(400))
fixedMessages.set('ModuleFive', generalHandler(500))
fixedMessages.set('commands', generalHandler(911))
fixedMessages.set('Exercise Diagnostic', generalHandler(1100))
fixedMessages.set('WASH Diagnostic', generalHandler(1200))
fixedMessages.set('Nutrition Diagnostic', generalHandler(1300))
fixedMessages.set('Maternal Infant Care Diagnostic', generalHandler(1400))
fixedMessages.set('Mental Health Diagnostic', generalHandler(1500))

export default fixedMessages
Loading

0 comments on commit 0da9631

Please sign in to comment.