mirror of
https://github.com/tutao/tutanota.git
synced 2025-10-19 16:03:43 +00:00
wip: tests2
This commit is contained in:
parent
1bc8ebdc4e
commit
3f09b0f5db
5 changed files with 53 additions and 11 deletions
|
@ -242,6 +242,12 @@ export class MailModel {
|
||||||
mailFolderAfterInboxRuleAndSpamProcessing.then((targetFolder) => {
|
mailFolderAfterInboxRuleAndSpamProcessing.then((targetFolder) => {
|
||||||
this._showNotification(targetFolder ?? initialMailFolder, mail)
|
this._showNotification(targetFolder ?? initialMailFolder, mail)
|
||||||
})
|
})
|
||||||
|
} else if (isUpdateForTypeRef(MailTypeRef, update) && update.operation === OperationType.DELETE) {
|
||||||
|
const mailId: IdTuple = [update.instanceListId, update.instanceListId]
|
||||||
|
|
||||||
|
// todo: how can we get the group in case of delete events?
|
||||||
|
const mailOwnerGroup = this.logins.getUserController().getMailGroupMemberships().at(0)!.group
|
||||||
|
await this.spamHandler().dropClassificationData(mailOwnerGroup, mailId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,10 @@ export class SpamClassificationHandler {
|
||||||
return assertNotNull(folderSystem.getSystemFolderByType(classifierMailSetTarget), `Could not get System folder for owner: ${mail._ownerGroup}`)
|
return assertNotNull(folderSystem.getSystemFolderByType(classifierMailSetTarget), `Could not get System folder for owner: ${mail._ownerGroup}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async dropClassificationData(mailOwnerGroup: Id, mailId: IdTuple) {
|
||||||
|
await this.spamClassifier?.deleteSpamClassification(mailOwnerGroup, mailId)
|
||||||
|
}
|
||||||
|
|
||||||
public async updateSpamClassificationData(events: ReadonlyArray<EntityUpdateData>, mail: Mail, folderSystem: FolderSystem) {
|
public async updateSpamClassificationData(events: ReadonlyArray<EntityUpdateData>, mail: Mail, folderSystem: FolderSystem) {
|
||||||
// TODO:
|
// TODO:
|
||||||
// would be nice to still update spam classification data even if spam classifier is not there yet,
|
// would be nice to still update spam classification data even if spam classifier is not there yet,
|
||||||
|
|
|
@ -200,6 +200,18 @@ export class OfflineStoragePersistence {
|
||||||
await this.sqlCipherFacade.run(query, params)
|
await this.sqlCipherFacade.run(query, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async deleteSpamClassificationData(owner: Id, mailId: IdTuple): Promise<void> {
|
||||||
|
const mailListId = listIdPart(mailId)
|
||||||
|
const mailElementId = elementIdPart(mailId)
|
||||||
|
const { query, params } = sql`
|
||||||
|
DELETE
|
||||||
|
FROM spam_classification_training_data
|
||||||
|
where ownerGroup = ${owner}
|
||||||
|
AND listId = ${mailListId}
|
||||||
|
AND elementId = ${mailElementId}`
|
||||||
|
await this.sqlCipherFacade.run(query, params)
|
||||||
|
}
|
||||||
|
|
||||||
async updateSpamClassificationData(id: IdTuple, isSpam: boolean, isSpamConfidence: number): Promise<void> {
|
async updateSpamClassificationData(id: IdTuple, isSpam: boolean, isSpamConfidence: number): Promise<void> {
|
||||||
const { query, params } = sql`
|
const { query, params } = sql`
|
||||||
UPDATE spam_classification_training_data
|
UPDATE spam_classification_training_data
|
||||||
|
|
|
@ -399,6 +399,10 @@ export class SpamClassifier {
|
||||||
return this.offlineStorage.storeSpamClassification(spamTrainMailDatum)
|
return this.offlineStorage.storeSpamClassification(spamTrainMailDatum)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public deleteSpamClassification(ownerGroup: Id, mailId: IdTuple) {
|
||||||
|
return this.offlineStorage.deleteSpamClassificationData(ownerGroup, mailId)
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO: Only for internal release
|
* TODO: Only for internal release
|
||||||
*
|
*
|
||||||
|
@ -598,17 +602,17 @@ export class SpamClassifier {
|
||||||
return concatenated.length > 0 ? concatenated : " "
|
return concatenated.length > 0 ? concatenated : " "
|
||||||
}
|
}
|
||||||
|
|
||||||
private async retrainModelFromScratch(storage: CacheStorage, ownerGroup: Id, cutoffTimestamp: number) {
|
private async retrainModelFromScratch(storage: CacheStorage, ownerGroup: Id, cutoffTimestamp: number) {
|
||||||
console.log("Model is being re-trained from scratch, deleting old data")
|
console.log("Model is being re-trained from scratch, deleting old data")
|
||||||
try {
|
try {
|
||||||
await assertNotNull(this.offlineStorage).deleteSpamClassificationTrainingDataBeforeCutoff(cutoffTimestamp, ownerGroup)
|
await assertNotNull(this.offlineStorage).deleteSpamClassificationTrainingDataBeforeCutoff(cutoffTimestamp, ownerGroup)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed delete old training data: ", e)
|
console.error("Failed delete old training data: ", e)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.trainFromScratch(storage, ownerGroup)
|
await this.trainFromScratch(storage, ownerGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Testing methods
|
// === Testing methods
|
||||||
public async cloneClassifier(): Promise<SpamClassifier> {
|
public async cloneClassifier(): Promise<SpamClassifier> {
|
||||||
|
|
|
@ -190,7 +190,7 @@ o.spec("MailModelTest", function () {
|
||||||
const inboxRuleTargetFolder = createTestEntity(MailFolderTypeRef, { _id: ["folderListId", "inboxRuleTarget"] })
|
const inboxRuleTargetFolder = createTestEntity(MailFolderTypeRef, { _id: ["folderListId", "inboxRuleTarget"] })
|
||||||
|
|
||||||
when(spamClassificationHandler.downloadMail(anything())).thenResolve(mail)
|
when(spamClassificationHandler.downloadMail(anything())).thenResolve(mail)
|
||||||
when(inboxRuleHandler.findAndApplyMatchingRule(anything(), anything(), anything())).thenResolve(inboxRuleTargetFolder)
|
when(inboxRuleHandler.findAndApplyMatchingRule(anything(), mail, anything())).thenResolve(inboxRuleTargetFolder)
|
||||||
|
|
||||||
const mailCreateEvent = makeUpdate({ instanceListId: "mailListId", instanceId: "mailId", operation: OperationType.CREATE })
|
const mailCreateEvent = makeUpdate({ instanceListId: "mailListId", instanceId: "mailId", operation: OperationType.CREATE })
|
||||||
await model.entityEventsReceived([mailCreateEvent])
|
await model.entityEventsReceived([mailCreateEvent])
|
||||||
|
@ -198,6 +198,22 @@ o.spec("MailModelTest", function () {
|
||||||
verify(spamClassificationHandler.predictSpamForNewMail(anything(), mail, anything()), { times: 1 })
|
verify(spamClassificationHandler.predictSpamForNewMail(anything(), mail, anything()), { times: 1 })
|
||||||
verify(spamClassifier.predict(anything()), { times: 0 })
|
verify(spamClassifier.predict(anything()), { times: 0 })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
o("deletes a training datum for deleted mail event", async () => {
|
||||||
|
const mail = createTestEntity(MailTypeRef, {
|
||||||
|
_id: ["mailListId", "mailId"],
|
||||||
|
_ownerGroup: "owner",
|
||||||
|
mailDetailsDraft: ["draftListId", "draftId"],
|
||||||
|
mailDetails: null,
|
||||||
|
})
|
||||||
|
when(spamClassificationHandler.downloadMail(anything())).thenResolve(mail)
|
||||||
|
when(inboxRuleHandler.findAndApplyMatchingRule(anything(), anything(), anything())).thenResolve(null)
|
||||||
|
|
||||||
|
const mailDeleteEvent = makeUpdate({ instanceListId: "mailListId", instanceId: "mailId", operation: OperationType.DELETE })
|
||||||
|
await model.entityEventsReceived([mailDeleteEvent])
|
||||||
|
|
||||||
|
verify(spamClassifier.deleteSpamClassification("owner", mail._id), { times: 0 })
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
function makeUpdate({
|
function makeUpdate({
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue