Skip to content
Snippets Groups Projects
Commit 97bececf authored by Hugo NOUTS's avatar Hugo NOUTS
Browse files

fix(migrations): profileType migrations

parent 84188426
Branches
Tags
2 merge requests!441fix(migrations): profileType migrations,!435WIP: 1.4.0
Showing with 124 additions and 57 deletions
......@@ -73,7 +73,7 @@ const AnalysisConsumption: React.FC<AnalysisConsumptionProps> = ({
async function loadAverageComsumption() {
const profileTypeEntityService = new ProfileTypeEntityService(client)
const profileType: ProfileType | null = await profileTypeEntityService.getProfileType(
analysisDate
analysisDate.plus({ month: -1 })
)
if (profileType !== null) {
const profileTypeService: ProfileTypeService = new ProfileTypeService(
......
......@@ -41,7 +41,7 @@ const ProfileTypeView = () => {
}),
housingType: HousingType.INDIVIDUAL_HOUSE,
constructionYear: ConstructionYear.BETWEEN_1975_AND_1989,
area: 0,
area: '0',
occupantsNumber: 1,
outsideFacingWalls: OutsideFacingWalls.ONE,
floor: Floor.NOT_APPLICABLE,
......
......@@ -4,7 +4,7 @@
"updateDate": "0000-01-01T00:00:00.000Z",
"housingType": "individual_house",
"constructionYear": "between_1975_and_1989",
"area": 100,
"area": "100",
"occupantsNumber": 4,
"outsideFacingWalls": "2",
"floor": "not_applicable",
......
import { Migration } from './migration.type'
import { PROFILE_DOCTYPE } from 'doctypes'
import { Profile } from 'models'
import {
PROFILE_DOCTYPE,
PROFILETYPE_DOCTYPE,
USERCHALLENGE_DOCTYPE,
} from 'doctypes'
import { Profile, ProfileType, UserChallenge } from 'models'
import { Client, Q, QueryDefinition, QueryResult } from 'cozy-client'
import { DateTime } from 'luxon'
import { UserQuizState } from 'enum/userQuiz.enum'
export const SCHEMA_INITIAL_VERSION = 0
......@@ -12,14 +19,74 @@ export const migrations: Migration[] = [
{
baseSchemaVersion: SCHEMA_INITIAL_VERSION,
targetSchemaVersion: 1,
appVersion: '1.2.4',
description: 'Removing GCUApprovalDate from profil',
appVersion: '1.3.0',
description:
'Removes old profileType artifacts from users database : \n - Oldest profileType gets deleted \n - Removes insulation work form field prone to errors \n - Changes area & outsideFacingWalls form field to strings \n - Changes updateDate values of all existing profileType to match "created_at" entry (former updateDate values got corrupted and held no meaning).',
docTypes: PROFILETYPE_DOCTYPE,
run: async (_client: Client, docs: any[]): Promise<ProfileType[]> => {
docs.sort(function(a, b) {
const c = DateTime.fromISO(a.cozyMetadata.createdAt, {
zone: 'utc',
})
const d = DateTime.fromISO(b.cozyMetadata.createdAt, {
zone: 'utc',
})
return d.millisecond - c.millisecond
})
if (docs[0].area === 100) {
docs[0].deleteAction = true
}
return docs.map(doc => {
if (
doc.individualInsulationWork.includes(
'window_replacement_and_wall_insulation'
)
) {
doc.individualInsulationWork = 'window_replacement'
}
doc.outsideFacingWalls = doc.outsideFacingWalls.toString()
doc.area = doc.area.toString()
doc.updateDate = doc.cozyMetadata.createdAt
return doc
})
},
},
{
baseSchemaVersion: 1,
targetSchemaVersion: 2,
appVersion: '1.3.0',
description: 'Removes old profileType and GCUApprovalDate from profile.',
docTypes: PROFILE_DOCTYPE,
run: (docs: any[]): Profile[] => {
run: async (_client: Client, docs: any[]): Promise<Profile[]> => {
return docs.map(doc => {
if (doc.GCUApprovalDate) {
delete doc.GCUApprovalDate
}
if (doc.profileType) {
delete doc.profileType
}
return doc
})
},
},
{
baseSchemaVersion: 2,
targetSchemaVersion: 3,
appVersion: '1.3.0',
description:
'Updates userChallenges to make sure no quiz results are overflowing.',
docTypes: USERCHALLENGE_DOCTYPE,
run: async (_client: Client, docs: any[]): Promise<UserChallenge[]> => {
return docs.map(doc => {
if (doc.quiz.result > 5) {
doc.quiz.result = 5
doc.progress = {
actionProgress: 5,
explorationProgress: 5,
quizProgress: 5,
}
doc.quiz.state = UserQuizState.DONE
}
return doc
})
},
......
......@@ -19,7 +19,7 @@ describe('migration logger', () => {
appVersion: '1.2.4',
description: 'Removing mailToken from profil',
docTypes: PROFILE_DOCTYPE,
run: (docs: any[]): Profile[] => {
run: async (mockClient, docs: any[]): Promise<Profile[]> => {
return docs.map(doc => {
if (doc.mailToken) {
delete doc.mailToken
......@@ -61,7 +61,7 @@ describe('migration', () => {
appVersion: '1.2.4',
description: 'Removing mailToken from profil',
docTypes: PROFILE_DOCTYPE,
run: (docs: any[]): Profile[] => {
run: async (mockClient, docs: any[]): Promise<Profile[]> => {
return docs.map(doc => {
if (doc.GCUApprovalDate) {
delete doc.GCUApprovalDate
......@@ -155,7 +155,7 @@ describe('migration', () => {
appVersion: '1.2.4',
description: 'Removing mailToken from profil',
docTypes: PROFILE_DOCTYPE,
run: (docs: any[]): Profile[] => {
run: async (mockClient, docs: any[]): Promise<Profile[]> => {
return []
},
}
......
......@@ -46,7 +46,7 @@ async function updateSchemaVersion(
_client: Client,
targetSchemaVersion: number
): Promise<void> {
log.info('[Migartion] Update schema version')
log.info('[Migration] Update schema version')
const query: QueryDefinition = Q(SCHEMAS_DOCTYPE)
const data: QueryResult<Schema[]> = await _client.query(query.limitBy(1))
const doc = data.data[0]
......@@ -67,7 +67,11 @@ async function save(_client: Client, docs: any[]): Promise<MigrationResult> {
if (docs.length) {
log.info('[Migration] Saving docs...')
docs.forEach(async doc => {
await _client.save(doc)
if (doc.deleteAction) {
await _client.destroy(doc)
} else {
await _client.save(doc)
}
})
log.info('[Migration] Docs saved')
migrationResult.type = migrationResult.errors.length
......@@ -115,11 +119,13 @@ export async function migrate(
let result: MigrationResult
try {
const docToUpdate: any[] = await getDocs(_client, migration.docTypes)
const migratedDocs = migration.run(docToUpdate)
if (migratedDocs.length) {
result = await save(_client, migratedDocs)
if (docToUpdate.length) {
const migratedDocs = await migration.run(_client, docToUpdate)
if (migratedDocs.length) {
result = await save(_client, migratedDocs)
} else {
result = migrationNoop()
}
} else {
result = migrationNoop()
}
......
import { Client } from 'cozy-client'
type SchemaVersion = number
export type MigrationData = {
......@@ -24,5 +26,5 @@ export type Migration = {
description: string
docTypes: string
appVersion: string
run: (docs: any[]) => any[]
run: (_client: Client, docs: any[]) => Promise<any[]>
}
......@@ -33,7 +33,7 @@ export interface ProfileType extends ProfileTypeIndexableTypes {
updateDate: DateTime | null
housingType: HousingType
constructionYear: ConstructionYear
area: number
area: string
occupantsNumber: number
outsideFacingWalls: OutsideFacingWalls
floor: Floor
......
......@@ -38,7 +38,6 @@ import explorationEntityData from 'db/explorationEntity.json'
import ProfileService from 'services/profile.service'
import profileData from 'db/profileData.json'
import profileTypeData from 'db/profileTypeData.json'
import KonnectorStatusService from 'services/konnectorStatus.service'
import KonnectorService from 'services/konnector.service'
import AccountService from 'services/account.service'
......@@ -188,25 +187,8 @@ export default class InitializationService {
public async initProfileType(): Promise<ProfileType | null> {
const profileTypeEntityService = new ProfileTypeEntityService(this._client)
try {
let loadedProfileType = await profileTypeEntityService.getProfileType()
if (!loadedProfileType) {
const { data: newProfileType } = await this._client.create(
PROFILETYPE_DOCTYPE,
profileTypeData[0].profileType
)
if (newProfileType) {
log.info('[Initialization] ProfileType created')
loadedProfileType = await profileTypeEntityService.updateProfileType({
updateDate: DateTime.local().setZone('utc', {
keepLocalTime: true,
}),
})
} else {
throw new Error('initProfileType: ProfileType not created')
}
} else {
log.info('[Initialization] ProfileType loaded')
}
const loadedProfileType = await profileTypeEntityService.getProfileType()
log.info('[Initialization] ProfileType loaded')
return loadedProfileType
} catch (error) {
log.error('Initialization error - initProfileType: ', error)
......
......@@ -25,7 +25,8 @@ import {
} from 'enum/profileType.enum'
import { FluidType } from 'enum/fluid.enum'
import ConverterService from './converter.service'
import { Client } from 'cozy-client'
import { Client, Q, QueryDefinition, QueryResult } from 'cozy-client'
import log from 'utils/logger'
export default class ProfileTypeService {
private readonly profileType: ProfileType
......@@ -33,6 +34,10 @@ export default class ProfileTypeService {
private readonly year: number
constructor(profileType: ProfileType, _client: Client, year: number) {
log.info(
'[ProfileType] Analysis loaded profileType relative to : ',
profileType.updateDate.toString()
)
this.profileType = profileType
this._client = _client
this.year = year
......@@ -49,7 +54,8 @@ export default class ProfileTypeService {
const ratiosHeatingByHousingType = ratiosHeating[housingType]
const currentRatio: number = ratiosHeatingByHousingType[constructionYear]
const estimatedConsumption: number = this.profileType.area * currentRatio
const estimatedConsumption: number =
parseInt(this.profileType.area) * currentRatio
return estimatedConsumption
}
......
......@@ -2,6 +2,7 @@ import { Client, Q, QueryDefinition, QueryResult } from 'cozy-client'
import { ProfileType } from 'models'
import { PROFILETYPE_DOCTYPE } from 'doctypes'
import { DateTime } from 'luxon'
import profileTypeData from 'db/profileTypeData.json'
export default class ProfileTypeEntityService {
private readonly _client: Client
......@@ -12,14 +13,13 @@ export default class ProfileTypeEntityService {
/**
* Retrieve ProfileType from the PROFILETYPE_DOCTYPE
* When called with date parameter, fetch closes profileType to the date
* When called with date parameter, fetch closest profileType to the date
* When called without parameters, fetch last profileType in doctype
* @param {DateTime} date
* @returns {ProfileType}
*/
public async getProfileType(date?: DateTime): Promise<ProfileType | null> {
const query: QueryDefinition = Q(PROFILETYPE_DOCTYPE)
if (date) {
const {
data: [profileType],
......@@ -33,10 +33,14 @@ export default class ProfileTypeEntityService {
if (result) {
return this.parseProfileTypeEntityToProfileType(profileType)
} else {
// If no entry is return for a given date
// it means asked date is more recent than our last profile
// fetch last profile
return this.getProfileType()
// If no entry is returned for a given date
// it means asked date is older than our last profile
// return default profiletype
const loadedProfileType: any = {
...profileTypeData[0].profileType,
updateDate: date,
}
return loadedProfileType
}
} else {
const {
......
......@@ -21,7 +21,7 @@ import { ProfileTypeActionTypes } from './profileType.actions'
const initialState: ProfileType = {
housingType: HousingType.INDIVIDUAL_HOUSE,
constructionYear: ConstructionYear.BETWEEN_1975_AND_1989,
area: 100,
area: '100',
occupantsNumber: 4,
outsideFacingWalls: OutsideFacingWalls.TWO,
floor: Floor.NOT_APPLICABLE,
......
......@@ -22,7 +22,7 @@ export const profileTypeData: ProfileType = {
_id: 'ed8a160e06431be15c8fdbb428000f6a',
_rev: '16-f829d012bf290a3f9257be592d8c65d8',
id: 'ed8a160e06431be15c8fdbb428000f6a',
area: 64,
area: '64',
coldWater: IndividualOrCollective.INDIVIDUAL,
constructionYear: ConstructionYear.AFTER_1998,
cookingFluid: 0,
......@@ -47,7 +47,7 @@ export const profileTypeData: ProfileType = {
export const mockProfileType: ProfileType = {
housingType: HousingType.APPARTMENT,
constructionYear: ConstructionYear.AFTER_1998,
area: 43,
area: '43',
occupantsNumber: 1,
outsideFacingWalls: OutsideFacingWalls.TWO,
floor: Floor.GROUND_FLOOR,
......@@ -83,7 +83,7 @@ export const mockMonthEcsConsumptionThermo = 110
export const mockProfileType1: ProfileType = {
housingType: HousingType.APPARTMENT,
constructionYear: ConstructionYear.BETWEEN_1948_AND_1974,
area: 43,
area: '43',
occupantsNumber: 2,
outsideFacingWalls: OutsideFacingWalls.TWO,
floor: Floor.INTERMEDIATE_FLOOR,
......@@ -111,7 +111,7 @@ export const mockMonthEcsConsumption1Solar = 134
export const mockProfileType2: ProfileType = {
housingType: HousingType.INDIVIDUAL_HOUSE,
constructionYear: ConstructionYear.BETWEEN_1948_AND_1974,
area: 90,
area: '90',
occupantsNumber: 4,
outsideFacingWalls: OutsideFacingWalls.FOUR,
floor: Floor.GROUND_FLOOR,
......@@ -137,7 +137,7 @@ export const mockMonthConsumption2 = 3000
export const mockTestProfile1: ProfileType = {
housingType: HousingType.INDIVIDUAL_HOUSE,
constructionYear: ConstructionYear.BETWEEN_1948_AND_1974,
area: 110,
area: '110',
occupantsNumber: 5,
outsideFacingWalls: OutsideFacingWalls.FOUR,
floor: Floor.NOT_APPLICABLE,
......@@ -201,7 +201,7 @@ export const mockMonthlyForecastJanuaryTestProfile1: MonthlyForecast = {
export const mockTestProfile2: ProfileType = {
housingType: HousingType.APPARTMENT,
constructionYear: ConstructionYear.BETWEEN_1975_AND_1989,
area: 50,
area: '50',
occupantsNumber: 2,
outsideFacingWalls: OutsideFacingWalls.TWO,
floor: Floor.GROUND_FLOOR,
......@@ -266,7 +266,7 @@ export const mockMonthlyForecastJanuaryTestProfile2: MonthlyForecast = {
export const mockTestProfile3: ProfileType = {
housingType: HousingType.APPARTMENT,
constructionYear: ConstructionYear.BETWEEN_1948_AND_1974,
area: 50,
area: '50',
occupantsNumber: 2,
outsideFacingWalls: OutsideFacingWalls.ONE,
floor: Floor.GROUND_FLOOR,
......
......@@ -122,7 +122,7 @@ export const mockInitialProfileState: Profile = {
export const mockInitialProfileTypeState: ProfileType = {
housingType: HousingType.INDIVIDUAL_HOUSE,
constructionYear: ConstructionYear.BETWEEN_1975_AND_1989,
area: 100,
area: '100',
occupantsNumber: 4,
outsideFacingWalls: OutsideFacingWalls.TWO,
floor: Floor.NOT_APPLICABLE,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment