Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stateful notifications #2077

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions manifest.webapp
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@
"balance-lower": {
"description": "Alert the user when his account balance is lower than a certain value",
"collapsible": true,
"stateful": false,
"multiple": false,
"stateful": true,
"multiple": true,
"default_priority": "normal",
"templates": {}
},
Expand Down Expand Up @@ -154,9 +154,9 @@
},
"budget-alerts": {
"description": "Alert the user when sum of expenses goes higher than defined in settings",
"collapsible": false,
"stateful": false,
"multiple": false,
"collapsible": true,
"stateful": true,
"multiple": true,
"default_priority": "normal",
"templates": {}
},
Expand Down
5 changes: 3 additions & 2 deletions src/ducks/appSuggestions/services.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logger from 'cozy-logger'
import { findMatchingBrand, getNotInstalledBrands } from 'ducks/brandDictionary'
import { getLabel } from 'ducks/transactions/helpers'
import { getKonnectorFromTrigger } from 'utils/triggers'
import { trigger as triggerLibs } from 'cozy-client/dist/models'
import { BankTransaction } from 'cozy-doctypes'
import AppSuggestion from './AppSuggestion'
import Trigger from './Trigger'
Expand All @@ -11,6 +11,7 @@ import get from 'lodash/get'
import set from 'lodash/set'

const log = logger.namespace('app-suggestions')
const { getKonnector } = triggerLibs.triggers

export const findSuggestionForTransaction = (
transaction,
Expand Down Expand Up @@ -90,7 +91,7 @@ export const findAppSuggestions = async setting => {
set(setting, 'appSuggestions.lastSeq', transactionsToCheck.newLastSeq)

log('info', 'Get not installed brands')
const installedSlugs = triggers.map(getKonnectorFromTrigger)
const installedSlugs = triggers.map(getKonnector)
const brands = getNotInstalledBrands(installedSlugs)

log('info', `${brands.length} not installed brands`)
Expand Down
23 changes: 22 additions & 1 deletion src/ducks/appSuggestions/services.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
import { findSuggestionForTransaction, normalizeSuggestions } from './services'
import {
findSuggestionForTransaction,
normalizeSuggestions,
findAppSuggestions
} from './services'
import { TRANSACTION_DOCTYPE } from '../../doctypes'
import { getBrands } from '../brandDictionary'
import { Document } from 'cozy-doctypes'
import CozyClient from 'cozy-client'
import fetch from 'node-fetch'
global.fetch = fetch

const client = new CozyClient({
uri: 'http://localhost:8080'
})
Document.registerClient(client)

describe('findSuggestionForTransaction', () => {
const brands = getBrands()
Expand Down Expand Up @@ -87,3 +100,11 @@ describe('normalizeSuggestions', () => {
expect(normalizeSuggestions(suggestions)).toMatchSnapshot()
})
})

describe('findAppSuggestions', () => {
it('should work with empty data', async () => {
Document.fetchAll = jest.fn().mockResolvedValue([])
Document.fetchChanges = jest.fn().mockResolvedValue({ documents: [] })
await findAppSuggestions({})
})
})
24 changes: 23 additions & 1 deletion src/ducks/budgetAlerts/CategoryBudgetNotificationView.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import sumBy from 'lodash/sumBy'
import keyBy from 'lodash/keyBy'
import merge from 'lodash/merge'
import sortBy from 'lodash/sortBy'
import formatDate from 'date-fns/format'

import { ACCOUNT_DOCTYPE, GROUP_DOCTYPE } from 'doctypes'
import NotificationView from 'ducks/notifications/BaseNotificationView'
Expand Down Expand Up @@ -67,6 +69,9 @@ const transformForTemplate = (budgetAlert, t, accountsById, groupsById) => {
}
}

const formatBudgetAlertToCategoryId = budgetAlert =>
`${budgetAlert.categoryId}:${budgetAlert.maxThreshold}`

class CategoryBudget extends NotificationView {
constructor(options) {
super(options)
Expand Down Expand Up @@ -112,6 +117,8 @@ class CategoryBudget extends NotificationView {
: null
}

this.templateData = data

return data
}

Expand Down Expand Up @@ -149,10 +156,25 @@ class CategoryBudget extends NotificationView {
}

getExtraAttributes() {
if (!this.templateData) {
return
}
const { budgetAlerts } = this.templateData
return merge(super.getExtraAttributes(), {
data: {
route: '/analysis/categories'
}
},
categoryId: budgetAlerts.map(formatBudgetAlertToCategoryId).join(','),
state: JSON.stringify({
budgetAlerts: sortBy(
budgetAlerts,
budgetAlert => budgetAlert.categoryId
).map(budgetAlert => ({
categoryId: budgetAlert.categoryId,
date: formatDate(new Date(), 'YYYY-MM-DD'),
maxThreshold: budgetAlert.maxThreshold
}))
})
})
}
}
Expand Down
45 changes: 40 additions & 5 deletions src/ducks/notifications/BalanceLower/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import flatten from 'lodash/flatten'
import uniqBy from 'lodash/uniqBy'
import groupBy from 'lodash/groupBy'
import map from 'lodash/map'
import mapValues from 'lodash/mapValues'
import merge from 'lodash/merge'
import log from 'cozy-logger'
import { getAccountBalance } from 'ducks/account/helpers'
import { getCurrencySymbol } from 'utils/currencySymbol'
import { getCurrentDate } from 'ducks/notifications/utils'
import { isNew as isNewTransaction } from 'ducks/transactions/helpers'
import template from './template.hbs'
import { toText } from 'cozy-notifications'
import { ruleAccountFilter } from 'ducks/settings/ruleUtils'
Expand Down Expand Up @@ -53,6 +55,8 @@ const customToText = cozyHTMLEmail => {
return toText(cozyHTMLEmail, getContent)
}

const byIdSorter = (acc1, acc2) => (acc1._id > acc2._id ? 1 : -1)

class BalanceLower extends NotificationView {
constructor(config) {
super(config)
Expand All @@ -74,13 +78,22 @@ class BalanceLower extends NotificationView {
* Rules that do not match any accounts are discarded
*/
findMatchingRules() {
const nbNewTransactionsByAccountId = mapValues(
groupBy(
this.data.transactions.filter(
process.env.NODE_ENV === 'test' ? () => true : isNewTransaction
),
tr => tr.account
),
transactions => transactions.length
)
return this.rules
.filter(rule => rule.enabled)
.map(rule => ({
rule,
accounts: this.data.accounts.filter(acc =>
this.filterForRule(rule, acc)
)
accounts: this.data.accounts
.filter(account => nbNewTransactionsByAccountId[account._id] > 0)
.filter(acc => this.filterForRule(rule, acc))
}))
.filter(({ accounts }) => accounts.length > 0)
}
Expand Down Expand Up @@ -111,20 +124,42 @@ class BalanceLower extends NotificationView {

log('info', `BalanceLower: ${accounts.length} accountsFiltered`)

return {
this.templateData = {
matchingRules,
accounts,
institutions: groupAccountsByInstitution(accounts),
date: getCurrentDate(),
...this.urls
}

return this.templateData
}

getExtraAttributes() {
return merge(super.getExtraAttributes(), {
data: {
route: '/balances'
}
},

// If there are new transactions for the account but the account balance
// does not change, there will be no alerts
state: JSON.stringify({
accounts: this.templateData.accounts
.map(account => ({
_id: account._id,
balance: account.balance
}))
.sort(byIdSorter)
}),

// The category of the alert is made of the rule doc + the threshold
categoryId: this.templateData.matchingRules
.map(({ rule }) =>
rule.accountOrGroup
? `${rule.accountOrGroup._type}:${rule.accountOrGroup._id}:${rule.value}`
: `all:${rule.value}`
)
.join(',')
})
}

Expand Down
16 changes: 16 additions & 0 deletions src/ducks/notifications/BalanceLower/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ describe('balance lower', () => {
}
],
data: {
transactions: operations,
accounts: fixtures['io.cozy.bank.accounts'],
groups: fixtures['io.cozy.bank.groups']
},
Expand Down Expand Up @@ -141,6 +142,21 @@ describe('balance lower', () => {
expect(minValueBy(accounts, getAccountBalance)).toBeLessThan(500)
expect(maxValueBy(accounts, getAccountBalance)).toBe(325.24)
})

it('should compute correct state', async () => {
const { notification } = setup({
value: 500,
accountOrGroup: isabelleGroup
})
await notification.buildData()
const extraAttributes = await notification.getExtraAttributes()
expect(extraAttributes).toEqual(
expect.objectContaining({
categoryId: 'io.cozy.bank.groups:isabelle:500',
state: '{"accounts":[{"_id":"comptelou1","balance":325.24}]}'
})
)
})
})

describe('notification content', () => {
Expand Down
21 changes: 12 additions & 9 deletions src/ducks/notifications/LateHealthReimbursement/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ const customToText = cozyHTMLEmail => {
}

class LateHealthReimbursement extends NotificationView {
constructor(config) {
super(config)
this.interval = config.value
constructor(options) {
super(options)
this.interval = options.value
}

async getTransactions() {
async fetchTransactions() {
const DATE_FORMAT = 'YYYY-MM-DD'
const today = new Date()
const lt = formatDate(subDays(today, this.interval), DATE_FORMAT)
Expand Down Expand Up @@ -123,22 +123,22 @@ class LateHealthReimbursement extends NotificationView {
!isAlreadyNotified(lateReimbursement, LateHealthReimbursement)
)

log('info', `${toNotify} need to be notified`)
log('info', `${toNotify.length} need to be notified`)

this.toNotify = toNotify

return toNotify
}

getAccounts(transactions) {
fetchAccounts(transactions) {
const accountIds = uniq(
transactions.map(transaction => transaction.account)
)
return BankAccount.getAll(accountIds)
}

async fetchData() {
const transactions = await this.getTransactions()
const transactions = await this.fetchTransactions()

if (transactions.length === 0) {
log('info', 'No late health reimbursement')
Expand All @@ -148,7 +148,7 @@ class LateHealthReimbursement extends NotificationView {
log('info', `${transactions.length} late health reimbursements`)

log('info', 'Fetching accounts for late health reimbursements')
const accounts = await this.getAccounts(transactions)
const accounts = await this.fetchAccounts(transactions)
log(
'info',
`${accounts.length} accounts fetched for late health reimbursements`
Expand Down Expand Up @@ -186,10 +186,13 @@ class LateHealthReimbursement extends NotificationView {
}

/**
* Saves last notification date to transactions for which there was
* the notification.
*
* Executed by `Notification` when the notification has been successfuly sent
* See `Notification::sendNotification`
*/
async onSendNotificationSuccess() {
async onSuccess() {
this.toNotify.forEach(reimb => {
if (!reimb.cozyMetadata) {
reimb.cozyMetadata = {}
Expand Down
Loading