Skip to content

Commit 3691aba

Browse files
authored
Weekly emails receipt history (#479)
1 parent 9d4ed13 commit 3691aba

16 files changed

Lines changed: 632 additions & 165 deletions

backend/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
"format": "npx prettier --write .",
2929
"script:process-integration": "SERVICE=script ts-node ./src/bin/scripts/process-integration.ts",
3030
"script:change-tenant-plan": "SERVICE=script ts-node ./src/bin/scripts/change-tenant-plan.ts",
31-
"script:process-webhook": "SERVICE=script ts-node ./src/bin/scripts/process-webhook.ts"
31+
"script:process-webhook": "SERVICE=script ts-node ./src/bin/scripts/process-webhook.ts",
32+
"script:send-weekly-analytics-email": "SERVICE=script ts-node ./src/bin/scripts/send-weekly-analytics-email.ts"
3233
},
3334
"dependencies": {
3435
"@aws-sdk/client-comprehend": "^3.159.0",

backend/src/bin/jobs/weeklyAnalyticsEmailsCoordinator.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import TenantService from '../../services/tenantService'
33
import { sendNodeWorkerMessage } from '../../serverless/utils/nodeWorkerSQS'
44
import { NodeWorkerMessageBase } from '../../types/mq/nodeWorkerMessageBase'
55
import { NodeWorkerMessageType } from '../../serverless/types/workerTypes'
6+
import { timeout } from '../../utils/timing'
67

78
const job: CrowdJob = {
89
name: 'Weekly Analytics Emails coordinator',
@@ -16,6 +17,9 @@ const job: CrowdJob = {
1617
tenant: tenant.id,
1718
service: 'weekly-analytics-emails',
1819
} as NodeWorkerMessageBase)
20+
21+
// Wait 1 second between messages to potentially reduce spike load on cube between each tenant runs
22+
await timeout(1000)
1923
}
2024
},
2125
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import commandLineArgs from 'command-line-args'
2+
import commandLineUsage from 'command-line-usage'
3+
import * as fs from 'fs'
4+
import moment from 'moment'
5+
import path from 'path'
6+
import { createServiceLogger } from '../../utils/logging'
7+
import SequelizeRepository from '../../database/repositories/sequelizeRepository'
8+
import { timeout } from '../../utils/timing'
9+
import { sendNodeWorkerMessage } from '../../serverless/utils/nodeWorkerSQS'
10+
import { NodeWorkerMessageType } from '../../serverless/types/workerTypes'
11+
import { NodeWorkerMessageBase } from '../../types/mq/nodeWorkerMessageBase'
12+
import WeeklyAnalyticsEmailsHistoryRepository from '../../database/repositories/weeklyAnalyticsEmailsHistoryRepository'
13+
14+
const banner = fs.readFileSync(path.join(__dirname, 'banner.txt'), 'utf8')
15+
16+
const log = createServiceLogger()
17+
18+
const options = [
19+
{
20+
name: 'tenant',
21+
alias: 't',
22+
type: String,
23+
description: 'The unique ID of tenant that you would like to send weekly emails to.',
24+
},
25+
{
26+
name: 'help',
27+
alias: 'h',
28+
type: Boolean,
29+
description: 'Print this usage guide.',
30+
},
31+
]
32+
const sections = [
33+
{
34+
content: banner,
35+
raw: true,
36+
},
37+
{
38+
header: 'Send weekly analytics email to given tenant.',
39+
content:
40+
'Sends weekly analytics email to given tenant. The daterange will be from previous week.',
41+
},
42+
{
43+
header: 'Options',
44+
optionList: options,
45+
},
46+
]
47+
48+
const usage = commandLineUsage(sections)
49+
const parameters = commandLineArgs(options)
50+
51+
if (parameters.help || !parameters.tenant) {
52+
console.log(usage)
53+
} else {
54+
setImmediate(async () => {
55+
const options = await SequelizeRepository.getDefaultIRepositoryOptions()
56+
const tenantIds = parameters.tenant.split(',')
57+
const weekOfYear = moment().utc().startOf('isoWeek').subtract(7, 'days').isoWeek().toString()
58+
const waeRepository = new WeeklyAnalyticsEmailsHistoryRepository(options)
59+
60+
for (const tenantId of tenantIds) {
61+
const tenant = await options.database.tenant.findByPk(tenantId)
62+
const isEmailAlreadySent =
63+
(await waeRepository.findByWeekOfYear(tenantId, weekOfYear)) !== null
64+
65+
if (!tenant) {
66+
log.error({ tenantId }, 'Tenant not found! Skipping.')
67+
} else if (isEmailAlreadySent) {
68+
log.info(
69+
{ tenantId },
70+
'Analytics email for this week is already sent to this tenant. Skipping.',
71+
)
72+
} else {
73+
log.info({ tenantId }, `Tenant found - sending weekly email message!`)
74+
await sendNodeWorkerMessage(tenant.id, {
75+
type: NodeWorkerMessageType.NODE_MICROSERVICE,
76+
tenant: tenant.id,
77+
service: 'weekly-analytics-emails',
78+
} as NodeWorkerMessageBase)
79+
80+
if (tenantIds.length > 1) {
81+
await timeout(1000)
82+
}
83+
}
84+
}
85+
86+
process.exit(0)
87+
})
88+
}

backend/src/cubejs/metrics/activeMembers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default async (cjs: CubeJsService, startDate: moment.Moment, endDate: mom
1818
timeDimensions: [
1919
{
2020
dimension: CubeDimensions.ACTIVITY_DATE,
21-
dateRange: [startDate.toISOString(), endDate.toISOString()],
21+
dateRange: [startDate.format('YYYY-MM-DD'), endDate.format('YYYY-MM-DD')],
2222
},
2323
],
2424
limit: 1,

backend/src/cubejs/metrics/activeOrganizations.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default async (cjs: CubeJsService, startDate: moment.Moment, endDate: mom
1818
timeDimensions: [
1919
{
2020
dimension: CubeDimensions.ACTIVITY_DATE,
21-
dateRange: [startDate.toISOString(), endDate.toISOString()],
21+
dateRange: [startDate.format('YYYY-MM-DD'), endDate.format('YYYY-MM-DD')],
2222
},
2323
],
2424
limit: 1,

backend/src/cubejs/metrics/newActivities.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default async (cjs: CubeJsService, startDate: moment.Moment, endDate: mom
1818
timeDimensions: [
1919
{
2020
dimension: CubeDimensions.ACTIVITY_DATE,
21-
dateRange: [startDate.toISOString(), endDate.toISOString()],
21+
dateRange: [startDate.format('YYYY-MM-DD'), endDate.format('YYYY-MM-DD')],
2222
},
2323
],
2424
limit: 1,

backend/src/cubejs/metrics/newConversations.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default async (cjs: CubeJsService, startDate: moment.Moment, endDate: mom
1818
timeDimensions: [
1919
{
2020
dimension: CubeDimensions.CONVERSATION_FIRST_ACTIVITY_TIME,
21-
dateRange: [startDate.toISOString(), endDate.toISOString()],
21+
dateRange: [startDate.format('YYYY-MM-DD'), endDate.format('YYYY-MM-DD')],
2222
},
2323
],
2424
limit: 1,

backend/src/cubejs/metrics/newMembers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default async (cjs: CubeJsService, startDate: moment.Moment, endDate: mom
1818
timeDimensions: [
1919
{
2020
dimension: CubeDimensions.MEMBER_JOINED_AT,
21-
dateRange: [startDate.toISOString(), endDate.toISOString()],
21+
dateRange: [startDate.format('YYYY-MM-DD'), endDate.format('YYYY-MM-DD')],
2222
},
2323
],
2424
limit: 1,

backend/src/cubejs/metrics/newOrganizations.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default async (cjs: CubeJsService, startDate: moment.Moment, endDate: mom
1818
timeDimensions: [
1919
{
2020
dimension: CubeDimensions.ORGANIZATIONS_JOINED_AT,
21-
dateRange: [startDate.toISOString(), endDate.toISOString()],
21+
dateRange: [startDate.format('YYYY-MM-DD'), endDate.format('YYYY-MM-DD')],
2222
},
2323
],
2424
limit: 1,
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
drop table "weeklyAnalyticsEmailsHistory";

0 commit comments

Comments
 (0)