Compare commits

...
2 Commits
Author SHA1 Message Date
Jonas Lochmann e8cc9fd5e6 Improve handling of manually added time 2020-09-07 02:00:00 +02:00
Jonas Lochmann 6518159069 Fix adding time when not using sqlite 2020-08-31 02:00:00 +02:00
5 changed files with 58 additions and 7 deletions
+3 -1
View File
@@ -56,6 +56,7 @@ export interface Database {
user: UserModelStatic
userLimitLoginCategory: UserLimitLoginCategoryModelStatic
transaction: <T> (autoCallback: (t: Sequelize.Transaction) => Promise<T>) => Promise<T>
dialect: string
}
const createDatabase = (sequelize: Sequelize.Sequelize): Database => ({
@@ -79,7 +80,8 @@ const createDatabase = (sequelize: Sequelize.Sequelize): Database => ({
userLimitLoginCategory: createUserLimitLoginCategoryModel(sequelize),
transaction: <T> (autoCallback: (transaction: Sequelize.Transaction) => Promise<T>) => (sequelize.transaction({
isolationLevel: Sequelize.Transaction.ISOLATION_LEVELS.READ_COMMITTED
}, autoCallback) as any) as Promise<T>
}, autoCallback) as any) as Promise<T>,
dialect: sequelize.getDialect()
})
export const sequelize = new Sequelize.Sequelize(process.env.DATABASE_URL || 'sqlite://test.db', {
@@ -64,9 +64,12 @@ export async function dispatchAddUsedTime ({ deviceId, action, cache }: {
currentExtraTime: number
}) => {
if (action.timeToAdd !== 0) {
const maxOperator = cache.database.dialect === 'sqlite' ? 'MAX' : 'GREATEST'
const minOperator = cache.database.dialect === 'sqlite' ? 'MIN' : 'LEAST'
// try to update first
const [updatedRows] = await cache.database.usedTime.update({
usedTime: Sequelize.literal(`MAX(0, MIN(usedTime + ${action.timeToAdd}, ${dayLengthInMs}))`) as any,
usedTime: Sequelize.literal(`${maxOperator}(0, ${minOperator}(usedTime + ${action.timeToAdd}, ${dayLengthInMs}))`) as any,
lastUpdate: roundedTimestamp
}, {
where: {
@@ -17,6 +17,7 @@
import * as Sequelize from 'sequelize'
import { AddUsedTimeActionVersion2 } from '../../../../action'
import { EventHandler } from '../../../../monitoring/eventhandler'
import { MinuteOfDay } from '../../../../util/minuteofday'
import { Cache } from '../cache'
import { getRoundedTimestamp as getRoundedTimestampForUsedTime } from './addusedtime'
@@ -27,14 +28,34 @@ export const getRoundedTimestampForSessionDuration = () => {
return now - (now % (1000 * 60 * 60 * 12 /* 12 hours */))
}
export async function dispatchAddUsedTimeVersion2 ({ deviceId, action, cache }: {
export async function dispatchAddUsedTimeVersion2 ({ deviceId, action, cache, eventHandler }: {
deviceId: string
action: AddUsedTimeActionVersion2
cache: Cache
eventHandler: EventHandler
}) {
const deviceEntryUnsafe = await cache.database.device.findOne({
where: {
familyId: cache.familyId,
deviceId: deviceId
},
attributes: ['currentUserId'],
transaction: cache.transaction
})
if (!deviceEntryUnsafe) {
throw new Error('source device not found')
}
const deviceEntry = {
currentUserId: deviceEntryUnsafe.currentUserId
}
const roundedTimestampForUsedTime = getRoundedTimestampForUsedTime().toString(10)
const roundedTimestampForSessionDuration = getRoundedTimestampForSessionDuration().toString(10)
let addUsedTimeForADifferentUserThanTheCurrentUserOfTheDevice = false
for (let i = 0; i < action.items.length; i++) {
const item = action.items[i]
@@ -62,14 +83,21 @@ export async function dispatchAddUsedTimeVersion2 ({ deviceId, action, cache }:
extraTimeInMillis: categoryEntryUnsafe.extraTimeInMillis
}
if (categoryEntry.childId !== deviceEntry.currentUserId) {
addUsedTimeForADifferentUserThanTheCurrentUserOfTheDevice = true
}
// tslint:disable-next-line:no-inner-declarations
async function handle (start: number, end: number) {
const lengthInMinutes = (end - start) + 1
const lengthInMs = lengthInMinutes * 1000 * 60
const maxOperator = cache.database.dialect === 'sqlite' ? 'MAX' : 'GREATEST'
const minOperator = cache.database.dialect === 'sqlite' ? 'MIN' : 'LEAST'
// try to update first
const [updatedRows] = await cache.database.usedTime.update({
usedTime: Sequelize.literal(`MAX(0, MIN(usedTime + ${item.timeToAdd}, ${lengthInMs}))`) as any,
usedTime: Sequelize.literal(`${maxOperator}(0, ${minOperator}(usedTime + ${item.timeToAdd}, ${lengthInMs}))`) as any,
lastUpdate: roundedTimestampForUsedTime
}, {
where: {
@@ -171,5 +199,20 @@ export async function dispatchAddUsedTimeVersion2 ({ deviceId, action, cache }:
cache.categoriesWithModifiedBaseData.push(item.categoryId)
}
if (addUsedTimeForADifferentUserThanTheCurrentUserOfTheDevice) {
// there are two possible causes for this:
// - a device user was changed remotely while it was used by this user for
// limited Apps (rarely)
// - a parent added time manually (rarely)
//
// For the second case, it's important to sync this change.
// As it should occur not too often, a full sync should be no problem.
// To keep an eye on it, it is counted.
cache.areChangesImportant = true
eventHandler.countEvent('add used time for a different user than the current user of the device')
}
}
}
@@ -26,6 +26,7 @@ import {
UpdateAppActivitiesAction,
UpdateDeviceStatusAction
} from '../../../../action'
import { EventHandler } from '../../../../monitoring/eventhandler'
import { Cache } from '../cache'
import { dispatchAddInstalledApps } from './addinstalledapps'
import { dispatchAddUsedTime } from './addusedtime'
@@ -36,17 +37,18 @@ import { dispatchTriedDisablingDeviceAdmin } from './trieddisablingdeviceadmin'
import { dispatchUpdateAppActivities } from './updateappactivities'
import { dispatchUpdateDeviceStatus } from './updatedevicestatus'
export const dispatchAppLogicAction = async ({ action, deviceId, cache }: {
export const dispatchAppLogicAction = async ({ action, deviceId, cache, eventHandler }: {
action: AppLogicAction
deviceId: string
cache: Cache
eventHandler: EventHandler
}) => {
if (action instanceof AddInstalledAppsAction) {
await dispatchAddInstalledApps({ deviceId, action, cache })
} else if (action instanceof AddUsedTimeAction) {
await dispatchAddUsedTime({ deviceId, action, cache })
} else if (action instanceof AddUsedTimeActionVersion2) {
await dispatchAddUsedTimeVersion2({ deviceId, action, cache })
await dispatchAddUsedTimeVersion2({ deviceId, action, cache, eventHandler })
} else if (action instanceof RemoveInstalledAppsAction) {
await dispatchRemoveInstalledApps({ deviceId, action, cache })
} else if (action instanceof SignOutAtDeviceAction) {
+2 -1
View File
@@ -183,7 +183,8 @@ export const applyActionsFromDevice = async ({ database, request, websocket, con
await dispatchAppLogicAction({
action: parsedAction,
cache,
deviceId: deviceEntry.deviceId
deviceId: deviceEntry.deviceId,
eventHandler
})
} catch (ex) {
eventHandler.countEvent('applyActionsFromDevice actionWithError:' + parsedSerializedAction.type)