From dd317c07e0848064986a30a21107f6159cfd48ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-L=C3=A9o=20Bourbonnais?= Date: Mon, 8 Sep 2025 14:51:11 -0400 Subject: [PATCH] Remove deprecated Base classes no longer used These classes are not used anymore in the actual and future surveys. Also update documentation to reflect changes. Move BaseInterview old Device import to new InterviewParadataAttributes --- .../src/services/baseObjects/BaseAddress.ts | 98 ------ .../src/services/baseObjects/BaseHousehold.ts | 207 ------------- .../src/services/baseObjects/BaseInterview.ts | 238 --------------- .../src/services/baseObjects/BaseJourney.ts | 151 --------- .../src/services/baseObjects/BaseJunction.ts | 167 ---------- .../services/baseObjects/BaseOrganization.ts | 187 ------------ .../src/services/baseObjects/BasePerson.ts | 271 ---------------- .../src/services/baseObjects/BasePlace.ts | 221 -------------- .../src/services/baseObjects/BaseSegment.ts | 104 ------- .../src/services/baseObjects/BaseTrip.ts | 98 ------ .../src/services/baseObjects/BaseTripChain.ts | 150 --------- .../src/services/baseObjects/BaseVehicle.ts | 145 --------- .../services/baseObjects/BaseVisitedPlace.ts | 197 ------------ .../baseObjects/__tests__/BaseAddress.test.ts | 68 ----- .../__tests__/BaseHousehold.test.ts | 190 ------------ .../__tests__/BaseInterview.test.ts | 172 ----------- .../baseObjects/__tests__/BaseJourney.test.ts | 129 -------- .../__tests__/BaseJunction.test.ts | 187 ------------ .../__tests__/BaseOrganization.test.ts | 163 ---------- .../baseObjects/__tests__/BasePerson.test.ts | 251 --------------- .../baseObjects/__tests__/BasePlace.test.ts | 289 ------------------ .../baseObjects/__tests__/BaseSegment.test.ts | 103 ------- .../baseObjects/__tests__/BaseTrip.test.ts | 115 ------- .../__tests__/BaseTripChain.test.ts | 147 --------- .../baseObjects/__tests__/BaseVehicle.test.ts | 138 --------- .../__tests__/BaseVisitedPlace.test.ts | 224 -------------- .../baseObjectsAndCompositionDoc.md | 16 +- 27 files changed, 7 insertions(+), 4419 deletions(-) delete mode 100644 packages/evolution-common/src/services/baseObjects/BaseAddress.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/BaseHousehold.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/BaseInterview.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/BaseJourney.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/BaseJunction.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/BaseOrganization.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/BasePerson.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/BasePlace.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/BaseSegment.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/BaseTrip.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/BaseTripChain.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/BaseVehicle.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/BaseVisitedPlace.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/__tests__/BaseAddress.test.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/__tests__/BaseHousehold.test.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/__tests__/BaseInterview.test.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/__tests__/BaseJourney.test.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/__tests__/BaseJunction.test.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/__tests__/BaseOrganization.test.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/__tests__/BasePerson.test.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/__tests__/BasePlace.test.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/__tests__/BaseSegment.test.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/__tests__/BaseTrip.test.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/__tests__/BaseTripChain.test.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/__tests__/BaseVehicle.test.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/__tests__/BaseVisitedPlace.test.ts diff --git a/packages/evolution-common/src/services/baseObjects/BaseAddress.ts b/packages/evolution-common/src/services/baseObjects/BaseAddress.ts deleted file mode 100644 index b14583829..000000000 --- a/packages/evolution-common/src/services/baseObjects/BaseAddress.ts +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -import { Uuidable } from './Uuidable'; -import { Optional } from '../../types/Optional.type'; -import { IValidatable } from './IValidatable'; - -export type BaseAddressAttributes = { - _uuid?: string; - - civicNumber: number; - civicNumberSuffix?: string; - unitNumber?: number; - streetName: string; - streetNameHomogenized?: string; - streetNameId?: string; - streetNameInternalId?: string; - municipalityName: string; - municipalityCode?: string; - postalMunicipalityName?: string; - region: string; - country: string; - postalCode?: string; - addressId?: string; - internalId?: string; -}; - -export type ExtendendAddressAttributes = BaseAddressAttributes & { [key: string]: any }; - -export class BaseAddress extends Uuidable implements IValidatable { - _isValid: Optional; - - civicNumber: number; - civicNumberSuffix?: string; // A, B, C, etc. - unitNumber?: number; // appartment number, office number, etc. - streetName: string; // non abbreviated if possible - - /** homogenized street name: - * - replace latin characters with their non-latin equivalent - * - lowercase - * - no dash - * - includes non-abbreviated street type/prefix and/or suffix (e.g. "rue", "avenue", "boulevard", etc.) - * - includes non-abbreviated orientation - * - non-abbreviated saint/sainte - * - trimmed and trimmed start - * example: rue de la gauchetiere ouest | 5e avenue nord | rang du petit saint jean | 30e rue - */ - streetNameHomogenized?: string; // homogenized street name, without latin characters, lowercase, no dash. - streetNameId?: string; // official street name id - streetNameInternalId?: string; // official street name id used as the primary key for addresses by the survey administrator - municipalityName: string; // should be taken from an official list if possible - municipalityCode?: string; // in Quebec, this is always a number - postalMunicipalityName?: string; // in some places, postal municipality names can be different from official names - region: string; // province, region or state - country: string; // should be standardized or ISO 3166-1 alpha-2 code - postalCode?: string; // should be validated if local postal code format is known - addressId?: string; // official address id - internalId?: string; // internal id used as the primary key for addresses by the survey administrator - - constructor(params: BaseAddressAttributes | ExtendendAddressAttributes) { - super(params._uuid); - - this.civicNumber = params.civicNumber; - this.civicNumberSuffix = params.civicNumberSuffix; - this.unitNumber = params.unitNumber; - this.streetName = params.streetName; - this.streetNameHomogenized = params.streetNameHomogenized; - this.streetNameId = params.streetNameId; - this.streetNameInternalId = params.streetNameInternalId; - this.municipalityName = params.municipalityName; - this.municipalityCode = params.municipalityCode; - this.postalMunicipalityName = params.postalMunicipalityName; - this.region = params.region; - this.country = params.country; - this.postalCode = params.postalCode; - this.addressId = params.addressId; - this.internalId = params.internalId; - } - - // params must be sanitized and must be valid: - static unserialize(params: BaseAddressAttributes): BaseAddress { - return new BaseAddress(params); - } - - validate(): Optional { - // TODO: implement: - this._isValid = true; - return true; - } - - isValid(): Optional { - return this._isValid; - } -} diff --git a/packages/evolution-common/src/services/baseObjects/BaseHousehold.ts b/packages/evolution-common/src/services/baseObjects/BaseHousehold.ts deleted file mode 100644 index 2523f73c4..000000000 --- a/packages/evolution-common/src/services/baseObjects/BaseHousehold.ts +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -import { Optional } from '../../types/Optional.type'; -import { IValidatable } from './IValidatable'; -import { Weightable, Weight, validateWeights } from './Weight'; -import * as HAttr from './attributeTypes/HouseholdAttributes'; -import { Uuidable } from './Uuidable'; - -type BaseHouseholdAttributes = { - _uuid?: string; - - size?: number; - carNumber?: number; - twoWheelNumber?: number; - pluginHybridCarNumber?: number; - electricCarNumber?: number; - category?: HAttr.HouseholdCategory; - homeCarParkings?: HAttr.HomePrivateCarParkingType[]; - - // must be anonymized: - contactPhoneNumber?: string; // TODO: normalize and/or validate phone numbers - contactEmail?: string; -} & Weightable; - -type ExtendedHouseholdAttributes = BaseHouseholdAttributes & { [key: string]: any }; - -class BaseHousehold extends Uuidable implements IValidatable { - _isValid: Optional; - _weights?: Weight[]; - - /** - * Here it would be better to just calculate from household members, - * but in most surveys, we first ask the respondent for the household - * size and then we create the persons using this value. However, in the - * questionnaire, it is often possible to increase the number of persons - * after having declared the household size at the beginning. So the size - * attribute and the persons array must be checked for consistency. - * See documentation (baseObjectsDoc.md) for more details. - */ - size?: number; - carNumber?: number; - twoWheelNumber?: number; - pluginHybridCarNumber?: number; - electricCarNumber?: number; - category?: HAttr.HouseholdCategory; - homeCarParkings?: HAttr.HomePrivateCarParkingType[]; - - // must be anonymized: - contactPhoneNumber?: string; - contactEmail?: string; - - _confidentialAttributes: string[] = [ - // these attributes should be hidden when exporting - 'contactPhoneNumber', - 'contactEmail' - ]; - - /** - * TODO: make the constructor protected for all surveyed classes. - * Only the factory create method should be able to generate a valid object - * */ - constructor(params: BaseHouseholdAttributes | ExtendedHouseholdAttributes) { - super(params._uuid); - - this._isValid = undefined; - this._weights = params._weights; - - this.size = params.size; - this.carNumber = params.carNumber; - this.twoWheelNumber = params.twoWheelNumber; - this.pluginHybridCarNumber = params.pluginHybridCarNumber; - this.electricCarNumber = params.electricCarNumber; - this.category = params.category; - this.homeCarParkings = params.homeCarParkings; - this.contactPhoneNumber = params.contactPhoneNumber; - this.contactEmail = params.contactEmail; - } - - // params must be sanitized and must be valid: - static unserialize(params: BaseHouseholdAttributes): BaseHousehold { - return new BaseHousehold(params); - } - - validate(): Optional { - // TODO: implement: - this._isValid = true; - return true; - } - - isValid(): Optional { - return this._isValid; - } - - /** - * Factory that validates input from an interview and makes - * sure types and required fields are valid before returning a new object - * @param dirtyParams - * @returns BaseHousehold | Error[] - */ - static create(dirtyParams: { [key: string]: any }): BaseHousehold | Error[] { - const errors = BaseHousehold.validateParams(dirtyParams); - return errors.length > 0 ? errors : new BaseHousehold(dirtyParams as ExtendedHouseholdAttributes); - } - - /** - * validates attributes types - * @param dirtyParams the params input - * @returns Error[] TODO: specialize this error class - */ - static validateParams(dirtyParams: { [key: string]: any }): Error[] { - const errors: Error[] = []; - - // Validate params object: - if (!dirtyParams || typeof dirtyParams !== 'object') { - errors.push(new Error('BaseHousehold validateParams: params is undefined or invalid')); - return errors; // stop now otherwise it will crash because params are not valid - } - - // Validate uuid: - const uuidErrors = Uuidable.validateParams(dirtyParams); - if (uuidErrors.length > 0) { - errors.push(...uuidErrors); - } - - // Validate weights: - const weightsErrors = validateWeights(dirtyParams._weights); - if (weightsErrors.length > 0) { - errors.push(...weightsErrors); - } - - // Validate size: - if (dirtyParams.size !== undefined && (!Number.isInteger(dirtyParams.size) || dirtyParams.size < 0)) { - errors.push(new Error('BaseHousehold validateParams: size should be a positive integer')); - } - - // Validate carNumber: - if ( - dirtyParams.carNumber !== undefined && - (!Number.isInteger(dirtyParams.carNumber) || dirtyParams.carNumber < 0) - ) { - errors.push(new Error('BaseHousehold validateParams: carNumber should be a positive integer')); - } - - // Validate twoWheelNumber: - if ( - dirtyParams.twoWheelNumber !== undefined && - (!Number.isInteger(dirtyParams.twoWheelNumber) || dirtyParams.twoWheelNumber < 0) - ) { - errors.push(new Error('BaseHousehold validateParams: twoWheelNumber should be a positive integer')); - } - - // Validate pluginHybridCarNumber: - if ( - dirtyParams.pluginHybridCarNumber !== undefined && - (!Number.isInteger(dirtyParams.pluginHybridCarNumber) || dirtyParams.pluginHybridCarNumber < 0) - ) { - errors.push(new Error('BaseHousehold validateParams: pluginHybridCarNumber should be a positive integer')); - } - - // Validate electricCarNumber: - if ( - dirtyParams.electricCarNumber !== undefined && - (!Number.isInteger(dirtyParams.electricCarNumber) || dirtyParams.electricCarNumber < 0) - ) { - errors.push(new Error('BaseHousehold validateParams: electricCarNumber should be a positive integer')); - } - - // Validate category: - if (dirtyParams.category !== undefined && typeof dirtyParams.category !== 'string') { - errors.push(new Error('BaseHousehold validateParams: category should be a string')); - } - - // Validate homeCarParkings: - if (dirtyParams.homeCarParkings !== undefined) { - if (!Array.isArray(dirtyParams.homeCarParkings)) { - errors.push(new Error('BaseHousehold validateParams: homeCarParkings should be an array')); - } else { - for (let i = 0, countI = dirtyParams.homeCarParkings.length; i < countI; i++) { - if (typeof dirtyParams.homeCarParkings[i] !== 'string') { - errors.push( - new Error(`BaseHousehold validateParams: homeCarParkings index ${i} should be a string`) - ); - } - } - } - } - - // Validate contactPhoneNumber: - if (dirtyParams.contactPhoneNumber !== undefined && typeof dirtyParams.contactPhoneNumber !== 'string') { - errors.push(new Error('BaseHousehold validateParams: contactPhoneNumber should be a string')); - } - - // Validate contactEmail: - if (dirtyParams.contactEmail !== undefined && typeof dirtyParams.contactEmail !== 'string') { - errors.push(new Error('BaseHousehold validateParams: contactEmail should be a string')); - } - - return errors; - } -} - -export { BaseHousehold, BaseHouseholdAttributes, ExtendedHouseholdAttributes }; diff --git a/packages/evolution-common/src/services/baseObjects/BaseInterview.ts b/packages/evolution-common/src/services/baseObjects/BaseInterview.ts deleted file mode 100644 index 951bee80a..000000000 --- a/packages/evolution-common/src/services/baseObjects/BaseInterview.ts +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -import { Optional } from '../../types/Optional.type'; -import { IValidatable } from './IValidatable'; -import { Surveyable, SurveyableAttributes } from './Surveyable'; -import { Survey, SurveyAttributes } from './Survey'; -import { Sample, SampleAttributes } from './Sample'; -import { Uuidable } from './Uuidable'; -import { parseDate } from '../../utils/DateUtils'; - -/** - * @deprecated use Interview instead - */ -export const devices = ['tablet', 'mobile', 'desktop', 'other', 'unknown'] as const; -export type Device = (typeof devices)[number]; - -/** - * @deprecated use Interview instead - */ -export type BaseInterviewAttributes = { - _uuid?: string; - - accessCode?: string; - assignedDate?: string; // string, YYYY-MM-DD, the assigned date for the survey (trips date most of the time) - contactPhoneNumber?: string; // phone number - contactEmail?: string; // email - - _startedAt?: Date | string; - _updatedAt?: Date | string; - _completedAt?: Date | string; - - _language?: string; // two-letter ISO 639-1 code - _source?: string; // source for the interview (web, phone, social, etc.) - _isCompleted?: boolean; - - _device?: Device; -}; - -/** - * @deprecated use Interview instead - */ -export type ExtendedInterviewAttributes = BaseInterviewAttributes & { [key: string]: any }; - -/** - * @deprecated use Interview instead - */ -export class BaseInterview extends Surveyable implements IValidatable { - _isValid: Optional; - - accessCode?: string; - assignedDate?: string; // string YYYY-MM-DD, the assigned date for the survey (trips date most of the time) - contactPhoneNumber?: string; // phone number - contactEmail?: string; // email - - _startedAt?: Date; - _updatedAt?: Date; - _completedAt?: Date; - - _language?: string; // two-letter ISO 639-1 code - _source?: string; // source for the interview (web, phone, social, etc.) - _isCompleted?: boolean; - - _device?: Device; - - _confidentialAttributes: string[] = [ - // these attributes should be hidden when exporting - 'contactPhoneNumber', - 'contactEmail', - 'accessCode' - ]; - - constructor(params: (BaseInterviewAttributes | ExtendedInterviewAttributes) & SurveyableAttributes) { - super(params.survey, params.sample, params.sampleBatchNumber, params._uuid); - - this.accessCode = params.accessCode; - this.assignedDate = params.assignedDate; // parseDate(params.assignedDate); - this.contactPhoneNumber = params.contactPhoneNumber; - this.contactEmail = params.contactEmail; - - this._startedAt = parseDate(params._startedAt); - this._updatedAt = parseDate(params._updatedAt); - this._completedAt = parseDate(params._completedAt); - - this._language = params._language; - this._source = params._source; - this._isCompleted = params._isCompleted; - - this._device = params._device; - } - - // params must be sanitized and must be valid: - static unserialize( - params: BaseInterviewAttributes & { - survey: SurveyAttributes; - sample: SampleAttributes; - sampleBatchNumber?: number; - } - ): BaseInterview { - const survey = Survey.unserialize(params.survey); - const sample = Sample.unserialize(params.sample); - return new BaseInterview({ ...params, survey, sample, sampleBatchNumber: params.sampleBatchNumber }); - } - - validate(): Optional { - // TODO: implement: - this._isValid = true; - return true; - } - - isValid(): Optional { - return this._isValid; - } - - /** - * Factory that validates input from an interview and makes - * sure types and required fields are valid before returning a new object - * @param dirtyParams - * @returns BaseInterview | Error[] - */ - static create(dirtyParams: { [key: string]: any }): BaseInterview | Error[] { - const errors = BaseInterview.validateParams(dirtyParams); - return errors.length > 0 - ? errors - : new BaseInterview(dirtyParams as ExtendedInterviewAttributes & SurveyableAttributes); - } - - /** - * Validates attribute types for BaseInterview. - * Any failing validation should prevent the creation of the object. - * Validations that should not prevent the creation of the object - * should be moved to survey audits. - * @param dirtyParams The parameters to validate. - * @returns Error[] TODO: specialize this error class - */ - static validateParams(dirtyParams: { [key: string]: any }): Error[] { - const errors: Error[] = []; - - dirtyParams._startedAt = parseDate(dirtyParams._startedAt); - dirtyParams._updatedAt = parseDate(dirtyParams._updatedAt); - dirtyParams._completedAt = parseDate(dirtyParams._completedAt); - - // Validate params object: - if (!dirtyParams || typeof dirtyParams !== 'object') { - errors.push(new Error('BaseInterview validateParams: params is undefined or invalid')); - return errors; // Stop now, as the params are not valid. - } - - // Validate UUID - const uuidErrors: Error[] = Uuidable.validateParams(dirtyParams); - if (uuidErrors.length > 0) { - errors.push(...uuidErrors); - } - - // Validate accessCode (if provided) - if (dirtyParams.accessCode !== undefined && typeof dirtyParams.accessCode !== 'string') { - errors.push(new Error('BaseInterview validateParams: accessCode should be a string')); - } - - // Validate _language (if provided) - if ( - dirtyParams._language !== undefined && - (typeof dirtyParams._language !== 'string' || /^[a-zA-Z]{2}$/.test(dirtyParams._language) === false) - ) { - errors.push(new Error('BaseInterview validateParams: _language should be a string of two letters')); - } - - // Validate _source (if provided) - if (dirtyParams._source !== undefined && typeof dirtyParams._source !== 'string') { - errors.push(new Error('BaseInterview validateParams: _source should be a string')); - } - - // Validate _isCompleted (if provided) - if (dirtyParams._isCompleted !== undefined && typeof dirtyParams._isCompleted !== 'boolean') { - errors.push(new Error('BaseInterview validateParams: _isCompleted should be a boolean')); - } - - const assignedDateObj = parseDate(dirtyParams.assignedDate); - // Validate assignedDate (if provided) - if ( - dirtyParams.assignedDate !== undefined && - (!(assignedDateObj instanceof Date) || (assignedDateObj !== undefined && isNaN(assignedDateObj.getDate()))) - ) { - errors.push(new Error('BaseInterview validateParams: assignedDate should be a valid date string')); - } - - // Validate _startedAt (if provided) - if ( - dirtyParams._startedAt !== undefined && - (!(dirtyParams._startedAt instanceof Date) || isNaN(dirtyParams._startedAt.getDate())) - ) { - errors.push(new Error('BaseInterview validateParams: invalid _startedAt')); - } - - // Validate _completedAt (if provided) - if ( - dirtyParams._completedAt !== undefined && - (!(dirtyParams._completedAt instanceof Date) || isNaN(dirtyParams._completedAt.getDate())) - ) { - errors.push(new Error('BaseInterview validateParams: invalid _completedAt')); - } - - // Validate _updatedAt (if provided) - if ( - dirtyParams._updatedAt !== undefined && - (!(dirtyParams._updatedAt instanceof Date) || isNaN(dirtyParams._updatedAt.getDate())) - ) { - errors.push(new Error('BaseInterview validateParams: invalid _updatedAt')); - } - - // Validate contactPhoneNumber (if provided): - // Precise phone number validation must be done in audits, because incorrect phone number should not prevent the interview from being created. - if (dirtyParams.contactPhoneNumber !== undefined && typeof dirtyParams.contactPhoneNumber !== 'string') { - errors.push(new Error('BaseInterview validateParams: contactPhoneNumber should be a string')); - } - - // Validate contactEmail (if provided): - // Regex email validation must be done in audits, because incorrect email should not prevent the interview from being created. - if (dirtyParams.contactEmail !== undefined && typeof dirtyParams.contactEmail !== 'string') { - errors.push(new Error('BaseInterview validateParams: contactEmail should be a string')); - } - - // Validate _device (if provided): - if (dirtyParams._device !== undefined) { - if (!devices.includes(dirtyParams._device)) { - errors.push(new Error('BaseInterview validateParams: _device is invalid')); - } - } - - return errors; - } - - // TODO: methods to get started and completed date/time with any format using moment or DateTime -} diff --git a/packages/evolution-common/src/services/baseObjects/BaseJourney.ts b/packages/evolution-common/src/services/baseObjects/BaseJourney.ts deleted file mode 100644 index 1ec8f4568..000000000 --- a/packages/evolution-common/src/services/baseObjects/BaseJourney.ts +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -/** - * A journey is a group of trips/trip chains used for a long-distance travel, or a surveyed day of travel. - * Journeys must be used for travel surveys when a specific household is part of a panel and is interviewed - * multiple times in the survey period. It can also be used if in a single survey, the questionnaire asks - * for trips made on mutiple distinct days. - */ - -import { Optional } from '../../types/Optional.type'; -import { IValidatable } from './IValidatable'; -import { WeightableAttributes, Weight, validateWeights } from './Weight'; -import { Uuidable, UuidableAttributes } from './Uuidable'; -import { ExcludeFunctionPropertyNames } from '../../utils/TypeUtils'; -import { parseDate } from '../../utils/DateUtils'; - -export type BaseJourneyAttributes = ExcludeFunctionPropertyNames & - WeightableAttributes & - UuidableAttributes; -export type ExtendedJourneyAttributes = BaseJourneyAttributes & { [key: string]: unknown }; - -export class BaseJourney extends Uuidable implements IValidatable { - _isValid?: Optional; - _weights?: Optional; - - startDate?: Optional; // string, YYYY-MM-DD - startTime?: Optional; - endDate?: Optional; // string, YYYY-MM-DD - endTime?: Optional; - name?: Optional; - - static _confidentialAttributes: string[] = [ - // these attributes should be hidden when exporting - ]; - - constructor(params: BaseJourneyAttributes | ExtendedJourneyAttributes) { - super(params._uuid); - - this._isValid = undefined; - this._weights = params._weights; - - this.startDate = params.startDate; - this.startTime = params.startTime; - this.endDate = params.endDate; - this.endTime = params.endTime; - this.name = params.name; - } - - // params must be sanitized and must be valid: - static unserialize(params: BaseJourneyAttributes): BaseJourney { - return new BaseJourney(params); - } - - validate(): Optional { - // TODO: implement: - this._isValid = true; - return true; - } - - isValid(): Optional { - return this._isValid; - } - - /** - * Factory that validates input from an interview and makes - * sure types and required fields are valid before returning a new object - * @param dirtyParams - * @returns BaseJourney | Error[] - */ - static create(dirtyParams: { [key: string]: any }): BaseJourney | Error[] { - const errors = BaseJourney.validateParams(dirtyParams); - return errors.length > 0 ? errors : new BaseJourney(dirtyParams as ExtendedJourneyAttributes); - } - - /** - * Validates attributes types for BaseJourney. - * @param dirtyParams The parameters to validate. - * @returns Error[] TODO: specialize this error class - */ - static validateParams(dirtyParams: { [key: string]: any }): Error[] { - const errors: Error[] = []; - - const startDateObj = parseDate(dirtyParams.startDate); - const endDateObj = parseDate(dirtyParams.endDate); - - // Validate params object: - if (!dirtyParams || typeof dirtyParams !== 'object') { - errors.push(new Error('BaseJourney validateParams: params is undefined or invalid')); - return errors; // stop now otherwise it will crash because params are not valid - } - - // Validate UUID - const uuidErrors: Error[] = Uuidable.validateParams(dirtyParams); - if (uuidErrors.length > 0) { - errors.push(...uuidErrors); - } - - // Validate weights: - const weightsErrors = validateWeights(dirtyParams._weights); - if (weightsErrors.length > 0) { - errors.push(...weightsErrors); - } - - // Validate startDate - if ( - dirtyParams.startDate !== undefined && - (!(startDateObj instanceof Date) || (startDateObj !== undefined && isNaN(startDateObj.getDate()))) - ) { - errors.push( - new Error('BaseJourney validateParams: startDate is required and should be a valid date string') - ); - } - - // Validate startTime - if ( - dirtyParams.startTime !== undefined && - (typeof dirtyParams.startTime !== 'number' || dirtyParams.startTime < 0) - ) { - errors.push( - new Error('BaseJourney validateParams: startTime is required and should be a non-negative number') - ); - } - - // Validate endDate - if ( - dirtyParams.endDate !== undefined && - (!(endDateObj instanceof Date) || (endDateObj !== undefined && isNaN(endDateObj.getDate()))) - ) { - errors.push(new Error('BaseJourney validateParams: endDate is required and should be a valid date string')); - } - - // Validate endTime - if (dirtyParams.endTime !== undefined && (typeof dirtyParams.endTime !== 'number' || dirtyParams.endTime < 0)) { - errors.push( - new Error('BaseJourney validateParams: endTime is required and should be a non-negative number') - ); - } - - // Validate name (if provided) - if (dirtyParams.name !== undefined && typeof dirtyParams.name !== 'string') { - errors.push(new Error('BaseJourney validateParams: name should be a string')); - } - - return errors; - } -} diff --git a/packages/evolution-common/src/services/baseObjects/BaseJunction.ts b/packages/evolution-common/src/services/baseObjects/BaseJunction.ts deleted file mode 100644 index 85cf80bdb..000000000 --- a/packages/evolution-common/src/services/baseObjects/BaseJunction.ts +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright 2024, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -/** - * A junction is a place used to transfer between segments/modes by a person during a trip - * Usually, junctions are used as origin and/or destination for segments - * Junctions are optional in most surveys - */ - -import { Optional } from '../../types/Optional.type'; -import { Uuidable } from './Uuidable'; -import { IValidatable } from './IValidatable'; -import { BasePlace, BasePlaceAttributes } from './BasePlace'; -import { BaseAddressAttributes } from './BaseAddress'; -import { ExcludeFunctionPropertyNames } from '../../utils/TypeUtils'; -import { parseDate } from '../../utils/DateUtils'; -import { UuidableAttributes } from './Uuidable'; - -export type BaseJunctionAttributes = ExcludeFunctionPropertyNames & UuidableAttributes; -export type ExtendedJunctionAttributes = BaseJunctionAttributes & { [key: string]: unknown }; - -export class BaseJunction extends Uuidable implements IValidatable { - _isValid?: Optional; - - basePlace?: BasePlace; - - arrivalDate?: Optional; // string, YYYY-MM-DD - departureDate?: Optional; // string, YYYY-MM-DD - arrivalTime?: Optional; // seconds since midnight - departureTime?: Optional; // seconds since midnight - - static _confidentialAttributes: string[] = [ - // these attributes should be hidden when exporting - ]; - - constructor(params: (BaseJunctionAttributes | ExtendedJunctionAttributes) & { basePlace: BasePlace }) { - super(params._uuid); - - this._isValid = undefined; - - this.basePlace = params.basePlace; - this.arrivalDate = params.arrivalDate; - this.departureDate = params.departureDate; - this.arrivalTime = params.arrivalTime; - this.departureTime = params.departureTime; - } - - // params must be sanitized and must be valid: - static unserialize( - params: BaseJunctionAttributes & { - basePlace: BasePlaceAttributes & { address?: Optional }; - } - ): BaseJunction { - return new BaseJunction({ ...params, basePlace: BasePlace.unserialize(params.basePlace) }); - } - - validate(): Optional { - // TODO: implement: - this._isValid = true; - return true; - } - - isValid(): Optional { - return this._isValid; - } - - /** - * Factory that validates input from an interview and makes - * sure types and required fields are valid before returning a new object - * @param dirtyParams - * @returns BaseJunction | Error[] - */ - static create(dirtyParams: { [key: string]: any }): BaseJunction | Error[] { - const basePlaceParams = { - ...dirtyParams, - geography: dirtyParams.geography, - geocodingQueryString: dirtyParams.geography?.properties?.geocodingQueryString, - lastAction: dirtyParams.geography?.properties?.lastAction, - deviceUsed: dirtyParams.geography?.properties?.platform, - zoom: dirtyParams.geography?.properties?.zoom - }; - - // validate params for both baseJunction and basePlace: - const errors = [...BasePlace.validateParams(basePlaceParams), ...BaseJunction.validateParams(dirtyParams)]; - if (errors.length > 0) { - return errors; - } else { - const basePlace = BasePlace.create(basePlaceParams) as BasePlace; - const baseJunction = new BaseJunction({ - basePlace, - ...dirtyParams - } as ExtendedJunctionAttributes & { basePlace: BasePlace }); - return baseJunction; - } - } - - /** - * Validates attributes types for BaseJunction - * @param dirtyParams The params input - * @returns Error[] TODO: specialize this error class - */ - static validateParams(dirtyParams: { [key: string]: any }): Error[] { - const errors: Error[] = []; - - const arrivalDateObj = parseDate(dirtyParams.arrivalDate); - const departureDateObj = parseDate(dirtyParams.departureDate); - - // Validate params object: - if (!dirtyParams || typeof dirtyParams !== 'object') { - errors.push(new Error('BaseJunction validateParams: params is undefined or invalid')); - return errors; // stop now otherwise it will crash because params are not valid - } - - // Validate uuid: - const uuidErrors = Uuidable.validateParams(dirtyParams); - if (uuidErrors.length > 0) { - errors.push(...uuidErrors); - } - - // Validate base place: - if (dirtyParams.basePlace !== undefined) { - const basePlaceErrors = BasePlace.validateParams(dirtyParams.basePlace); - if (basePlaceErrors.length > 0) { - errors.push(...basePlaceErrors); - } - } - - // Validate arrivalDate (if provided): - if ( - dirtyParams.arrivalDate !== undefined && - (!(arrivalDateObj instanceof Date) || (arrivalDateObj !== undefined && isNaN(arrivalDateObj.getDate()))) - ) { - errors.push(new Error('BaseJunction validateParams: arrivalDate should be a valid date string')); - } - - // Validate departureDate (if provided): - if ( - dirtyParams.departureDate !== undefined && - (!(departureDateObj instanceof Date) || - (departureDateObj !== undefined && isNaN(departureDateObj.getDate()))) - ) { - errors.push(new Error('BaseJunction validateParams: departureDate should be a valid date string')); - } - - // Validate arrivalTime (if provided): - if ( - dirtyParams.arrivalTime !== undefined && - (!Number.isInteger(dirtyParams.arrivalTime) || dirtyParams.arrivalTime < 0) - ) { - errors.push(new Error('BaseJunction validateParams: arrivalTime should be a positive integer')); - } - - // Validate departureTime (if provided): - if ( - dirtyParams.departureTime !== undefined && - (!Number.isInteger(dirtyParams.departureTime) || dirtyParams.departureTime < 0) - ) { - errors.push(new Error('BaseJunction validateParams: departureTime should be a positive integer')); - } - - return errors; - } -} diff --git a/packages/evolution-common/src/services/baseObjects/BaseOrganization.ts b/packages/evolution-common/src/services/baseObjects/BaseOrganization.ts deleted file mode 100644 index d64b6701b..000000000 --- a/packages/evolution-common/src/services/baseObjects/BaseOrganization.ts +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -/** - * An organization is a company, an NGO, an institutiona, - * a government entity, or any group of people that can be surveyed. - */ - -import { Optional } from '../../types/Optional.type'; -import { IValidatable } from './IValidatable'; -import { Weightable, Weight, validateWeights } from './Weight'; -import * as OAttr from './attributeTypes/OrganizationAttributes'; -import { Uuidable } from './Uuidable'; - -type BaseOrganizationAttributes = { - _uuid?: string; - - name?: string; - shortname?: string; - size?: number; // can be the number of employees or members or count of basePersons related to the organization - category?: OAttr.OrganizationCategory; - vehicleNumber?: number; - pluginHybridVehicleNumber?: number; - electricVehicleNumber?: number; - contactPhoneNumber?: string; - contactEmail?: string; -} & Weightable; - -type ExtendedOrganizationAttributes = BaseOrganizationAttributes & { [key: string]: any }; - -class BaseOrganization extends Uuidable implements IValidatable { - _isValid: Optional; - _weights?: Weight[]; - - name?: string; - shortname?: string; - size?: number; - category?: OAttr.OrganizationCategory; - vehicleNumber?: number; - pluginHybridVehicleNumber?: number; - electricVehicleNumber?: number; - contactPhoneNumber?: string; - contactEmail?: string; - - _confidentialAttributes: string[] = [ - // these attributes should be hidden when exporting - 'contactPhoneNumber', - 'contactEmail', - 'contactPerson' - ]; - - constructor(params: BaseOrganizationAttributes | ExtendedOrganizationAttributes) { - super(params._uuid); - - this._isValid = undefined; - this._weights = params._weights; - - this.name = params.name; - this.shortname = params.shortname; - this.size = params.size; - this.category = params.category; - this.vehicleNumber = params.vehicleNumber; - this.pluginHybridVehicleNumber = params.pluginHybridVehicleNumber; - this.electricVehicleNumber = params.electricVehicleNumber; - this.contactPhoneNumber = params.contactPhoneNumber; - this.contactEmail = params.contactEmail; - } - - // params must be sanitized and must be valid: - static unserialize(params: BaseOrganizationAttributes): BaseOrganization { - return new BaseOrganization(params); - } - - validate(): Optional { - // TODO: implement: - this._isValid = true; - return true; - } - - isValid(): Optional { - return this._isValid; - } - - /** - * Factory that validates input from an interview and makes - * sure types and required fields are valid before returning a new object - * @param dirtyParams - * @returns BaseOrganization | Error[] - */ - static create(dirtyParams: { [key: string]: any }): BaseOrganization | Error[] { - const errors = BaseOrganization.validateParams(dirtyParams); - return errors.length > 0 ? errors : new BaseOrganization(dirtyParams as ExtendedOrganizationAttributes); - } - - /** - * Validates attributes types for BaseOrganization. - * @param dirtyParams The parameters to validate. - * @returns Error[] TODO: specialize this error class - */ - static validateParams(dirtyParams: { [key: string]: any }): Error[] { - const errors: Error[] = []; - - // Validate params object: - if (!dirtyParams || typeof dirtyParams !== 'object') { - errors.push(new Error('BaseOrganization validateParams: params is undefined or invalid')); - return errors; // Stop now; further validation depends on valid params object. - } - - // Validate UUID - const uuidErrors: Error[] = Uuidable.validateParams(dirtyParams); - if (uuidErrors.length > 0) { - errors.push(...uuidErrors); - } - - // Validate weights: - const weightsErrors = validateWeights(dirtyParams._weights); - if (weightsErrors.length > 0) { - errors.push(...weightsErrors); - } - - // Validate name (if provided) - if (dirtyParams.name !== undefined && typeof dirtyParams.name !== 'string') { - errors.push(new Error('BaseOrganization validateParams: name should be a string')); - } - - // Validate shortname (if provided) - if (dirtyParams.shortname !== undefined && typeof dirtyParams.shortname !== 'string') { - errors.push(new Error('BaseOrganization validateParams: shortname should be a string')); - } - - // Validate size (if provided) - if (dirtyParams.size !== undefined && (!Number.isInteger(dirtyParams.size) || dirtyParams.size < 0)) { - errors.push(new Error('BaseOrganization validateParams: size should be a positive integer')); - } - - // Validate category (if provided) - if (dirtyParams.category !== undefined && typeof dirtyParams.category !== 'string') { - errors.push(new Error('BaseOrganization validateParams: category is not a valid organization category')); - } - - // Validate vehicleNumber (if provided) - if ( - dirtyParams.vehicleNumber !== undefined && - (!Number.isInteger(dirtyParams.vehicleNumber) || dirtyParams.vehicleNumber < 0) - ) { - errors.push(new Error('BaseOrganization validateParams: vehicleNumber should be a positive integer')); - } - - // Validate pluginHybridVehicleNumber (if provided) - if ( - dirtyParams.pluginHybridVehicleNumber !== undefined && - (!Number.isInteger(dirtyParams.pluginHybridVehicleNumber) || dirtyParams.pluginHybridVehicleNumber < 0) - ) { - errors.push( - new Error('BaseOrganization validateParams: pluginHybridVehicleNumber should be a positive integer') - ); - } - - // Validate electricVehicleNumber (if provided) - if ( - dirtyParams.electricVehicleNumber !== undefined && - (!Number.isInteger(dirtyParams.electricVehicleNumber) || dirtyParams.electricVehicleNumber < 0) - ) { - errors.push( - new Error('BaseOrganization validateParams: electricVehicleNumber should be a positive integer') - ); - } - - // Validate contactPhoneNumber (if provided) - if (dirtyParams.contactPhoneNumber !== undefined && typeof dirtyParams.contactPhoneNumber !== 'string') { - errors.push(new Error('BaseOrganization validateParams: contactPhoneNumber should be a string')); - } - - // Validate contactEmail (if provided) - if (dirtyParams.contactEmail !== undefined && typeof dirtyParams.contactEmail !== 'string') { - errors.push(new Error('BaseOrganization validateParams: contactEmail should be a string')); - } - - return errors; - } -} - -export { BaseOrganization, BaseOrganizationAttributes, ExtendedOrganizationAttributes }; diff --git a/packages/evolution-common/src/services/baseObjects/BasePerson.ts b/packages/evolution-common/src/services/baseObjects/BasePerson.ts deleted file mode 100644 index 221d8d83d..000000000 --- a/packages/evolution-common/src/services/baseObjects/BasePerson.ts +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -import { Optional } from '../../types/Optional.type'; -import { IValidatable } from './IValidatable'; -import { WeightableAttributes, Weight, validateWeights } from './Weight'; -import { Uuidable } from './Uuidable'; -import * as PAttr from './attributeTypes/PersonAttributes'; - -type BasePersonAttributes = { - _uuid?: string; - - age?: PAttr.Age; - ageGroup?: PAttr.AgeGroup; - gender?: PAttr.Gender; - drivingLicenseOwnership?: PAttr.DrivingLicenseOwnership; - transitPassOwnership?: PAttr.TransitPassOwnership; - carsharingMember?: PAttr.CarsharingMember; - carsharingUser?: PAttr.CarsharingUser; - bikesharingMember?: PAttr.BikesharingMember; - bikesharingUser?: PAttr.BikesharingUser; - ridesharingMember?: PAttr.RidesharingMember; - ridesharingUser?: PAttr.RidesharingUser; - occupation?: PAttr.Occupation; - jobCategory?: PAttr.JobCategory; - jobName?: PAttr.JobName; - isOnTheRoadWorker?: PAttr.IsOnTheRoadWorker; - isJobTelecommuteCompatible?: PAttr.HasTelecommuteCompatibleJob; - educationalAttainment?: PAttr.EducationalAttainment; - - // must be anonymized: - nickname?: string; - contactPhoneNumber?: string; - contactEmail?: string; -} & WeightableAttributes; - -type ExtendedPersonAttributes = BasePersonAttributes & { [key: string]: any }; - -/* eslint-disable @typescript-eslint/no-empty-interface, @typescript-eslint/no-empty-object-type */ -interface IBasePersonAttributes extends BasePersonAttributes {} - -class BasePerson extends Uuidable implements IBasePersonAttributes, IValidatable { - _isValid: Optional; - _weights?: Weight[]; - - age?: PAttr.Age; - ageGroup?: PAttr.AgeGroup; - gender?: PAttr.Gender; - drivingLicenseOwnership?: PAttr.DrivingLicenseOwnership; - transitPassOwnership?: PAttr.TransitPassOwnership; - carsharingMember?: PAttr.CarsharingMember; - carsharingUser?: PAttr.CarsharingUser; - bikesharingMember?: PAttr.BikesharingMember; - bikesharingUser?: PAttr.BikesharingUser; - ridesharingMember?: PAttr.RidesharingMember; - ridesharingUser?: PAttr.RidesharingUser; - occupation?: PAttr.Occupation; - jobCategory?: PAttr.JobCategory; - jobName?: PAttr.JobName; - isOnTheRoadWorker?: PAttr.IsOnTheRoadWorker; - isJobTelecommuteCompatible?: PAttr.HasTelecommuteCompatibleJob; - educationalAttainment?: PAttr.EducationalAttainment; - - // must be anonymized: - nickname?: string; - contactPhoneNumber?: string; - contactEmail?: string; - - _confidentialAttributes: string[] = [ - // these attributes should be hidden when exporting - 'contactPhoneNumber', - 'contactEmail', - 'nickname' - ]; - - constructor(params: BasePersonAttributes | ExtendedPersonAttributes) { - super(params._uuid); - - this._isValid = undefined; - this._weights = params._weights; - - this.age = params.age; - this.ageGroup = params.ageGroup; - this.gender = params.gender; - this.drivingLicenseOwnership = params.drivingLicenseOwnership; - this.transitPassOwnership = params.transitPassOwnership; - this.carsharingMember = params.carsharingMember; - this.carsharingUser = params.carsharingUser; - this.bikesharingMember = params.bikesharingMember; - this.bikesharingUser = params.bikesharingUser; - this.ridesharingMember = params.ridesharingMember; - this.ridesharingUser = params.ridesharingUser; - this.occupation = params.occupation; - this.jobCategory = params.jobCategory; - this.jobName = params.jobName; - this.isOnTheRoadWorker = params.isOnTheRoadWorker; - this.isJobTelecommuteCompatible = params.isJobTelecommuteCompatible; - this.educationalAttainment = params.educationalAttainment; - - this.nickname = params.nickname; - this.contactPhoneNumber = params.contactPhoneNumber; - this.contactEmail = params.contactEmail; - } - - // params must be sanitized and must be valid: - static unserialize(params: BasePersonAttributes): BasePerson { - return new BasePerson(params); - } - - /** - * Factory that validates input from an interview and makes - * sure types and required fields are valid before returning a new object - * @param dirtyParams - * @returns BasePerson | Error[] - */ - static create(dirtyParams: { [key: string]: any }): BasePerson | Error[] { - const errors = BasePerson.validateParams(dirtyParams); - return errors.length > 0 ? errors : new BasePerson(dirtyParams as ExtendedPersonAttributes); - } - - validate(): Optional { - // TODO: implement: - this._isValid = true; - return true; - } - - isValid(): Optional { - return this._isValid; - } - - /** - * Validates attributes types for BasePerson. - * @param dirtyParams The parameters to validate. - * @returns Error[] TODO: specialize this error class - */ - static validateParams(dirtyParams: { [key: string]: any }): Error[] { - const errors: Error[] = []; - - // Validate params object: - if (!dirtyParams || typeof dirtyParams !== 'object') { - errors.push(new Error('BasePerson validateParams: params is undefined or invalid')); - return errors; // Stop now; further validation depends on valid params object. - } - - // Validate UUID - const uuidErrors: Error[] = Uuidable.validateParams(dirtyParams); - if (uuidErrors.length > 0) { - errors.push(...uuidErrors); - } - - // Validate weights: - const weightsErrors = validateWeights(dirtyParams._weights); - if (weightsErrors.length > 0) { - errors.push(...weightsErrors); - } - - // Validate age (if provided) - if (dirtyParams.age !== undefined && (!Number.isInteger(dirtyParams.age) || dirtyParams.age < 0)) { - errors.push(new Error('BasePerson validateParams: age must be a positive integer')); - } - - // Validate ageGroup (if provided) - if (dirtyParams.ageGroup !== undefined && typeof dirtyParams.ageGroup !== 'string') { - errors.push(new Error('BasePerson validateParams: ageGroup is not a valid value')); - } - - // Validate gender (if provided) - if (dirtyParams.gender !== undefined && typeof dirtyParams.gender !== 'string') { - errors.push(new Error('BasePerson validateParams: gender is not a valid value')); - } - - // Validate drivingLicenseOwnership (if provided) - if ( - dirtyParams.drivingLicenseOwnership !== undefined && - typeof dirtyParams.drivingLicenseOwnership !== 'string' - ) { - errors.push(new Error('BasePerson validateParams: drivingLicenseOwnership is not a valid value')); - } - - // Validate transitPassOwnership (if provided) - if (dirtyParams.transitPassOwnership !== undefined && typeof dirtyParams.transitPassOwnership !== 'string') { - errors.push(new Error('BasePerson validateParams: transitPassOwnership is not a valid value')); - } - - // Validate carsharingMember (if provided) - if (dirtyParams.carsharingMember !== undefined && typeof dirtyParams.carsharingMember !== 'string') { - errors.push(new Error('BasePerson validateParams: carsharingMember is not a valid value')); - } - - // Validate carsharingUser (if provided) - if (dirtyParams.carsharingUser !== undefined && typeof dirtyParams.carsharingUser !== 'string') { - errors.push(new Error('BasePerson validateParams: carsharingUser is not a valid value')); - } - - // Validate bikesharingMember (if provided) - if (dirtyParams.bikesharingMember !== undefined && typeof dirtyParams.bikesharingMember !== 'string') { - errors.push(new Error('BasePerson validateParams: bikesharingMember is not a valid value')); - } - - // Validate bikesharingUser (if provided) - if (dirtyParams.bikesharingUser !== undefined && typeof dirtyParams.bikesharingUser !== 'string') { - errors.push(new Error('BasePerson validateParams: bikesharingUser is not a valid value')); - } - - // Validate ridesharingMember (if provided) - if (dirtyParams.ridesharingMember !== undefined && typeof dirtyParams.ridesharingMember !== 'string') { - errors.push(new Error('BasePerson validateParams: ridesharingMember is not a valid value')); - } - - // Validate ridesharingUser (if provided) - if (dirtyParams.ridesharingUser !== undefined && typeof dirtyParams.ridesharingUser !== 'string') { - errors.push(new Error('BasePerson validateParams: ridesharingUser is not a valid value')); - } - - // Validate occupation (if provided) - if (dirtyParams.occupation !== undefined && typeof dirtyParams.occupation !== 'string') { - errors.push(new Error('BasePerson validateParams: occupation is not a valid value')); - } - - // Validate jobCategory (if provided) - if (dirtyParams.jobCategory !== undefined && typeof dirtyParams.jobCategory !== 'string') { - errors.push(new Error('BasePerson validateParams: jobCategory is not a valid value')); - } - - // Validate jobName (if provided) - if (dirtyParams.jobName !== undefined && typeof dirtyParams.jobName !== 'string') { - errors.push(new Error('BasePerson validateParams: jobName should be a string')); - } - - // Validate isOnTheRoadWorker (if provided) - if (dirtyParams.isOnTheRoadWorker !== undefined && typeof dirtyParams.isOnTheRoadWorker !== 'boolean') { - errors.push(new Error('BasePerson validateParams: isOnTheRoadWorker should be a boolean')); - } - - // Validate isJobTelecommuteCompatible (if provided) - if ( - dirtyParams.isJobTelecommuteCompatible !== undefined && - typeof dirtyParams.isJobTelecommuteCompatible !== 'boolean' - ) { - errors.push(new Error('BasePerson validateParams: isJobTelecommuteCompatible should be a boolean')); - } - - // Validate educationalAttainment (if provided) - if (dirtyParams.educationalAttainment !== undefined && typeof dirtyParams.educationalAttainment !== 'string') { - errors.push(new Error('BasePerson validateParams: educationalAttainment is not a valid value')); - } - - // Validate nickname (if provided) - if (dirtyParams.nickname !== undefined && typeof dirtyParams.nickname !== 'string') { - errors.push(new Error('BasePerson validateParams: nickname should be a string')); - } - - // Validate contactPhoneNumber (if provided) - if (dirtyParams.contactPhoneNumber !== undefined && typeof dirtyParams.contactPhoneNumber !== 'string') { - errors.push(new Error('BasePerson validateParams: contactPhoneNumber should be a string')); - } - - // Validate contactEmail (if provided) - if (dirtyParams.contactEmail !== undefined && typeof dirtyParams.contactEmail !== 'string') { - errors.push(new Error('BasePerson validateParams: contactEmail should be a string')); - } - - return errors; - } -} - -export { BasePerson, BasePersonAttributes, ExtendedPersonAttributes }; diff --git a/packages/evolution-common/src/services/baseObjects/BasePlace.ts b/packages/evolution-common/src/services/baseObjects/BasePlace.ts deleted file mode 100644 index dafb7fa6c..000000000 --- a/packages/evolution-common/src/services/baseObjects/BasePlace.ts +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -/** - * A place is a specific location on the map, usually stored as a geojson object. - * When possible, it should also contains some way to associated it with official databases - * like postal data, landrole data or OpenStreetMap ids. - */ - -import { Optional } from '../../types/Optional.type'; -import { isFeature, isPoint } from 'geojson-validation'; -import { Uuidable } from './Uuidable'; -import { GeocodingPrecisionCategory, LastAction } from './attributeTypes/PlaceAttributes'; -import { BaseAddress, BaseAddressAttributes } from './BaseAddress'; -import { IValidatable } from './IValidatable'; -import { Device } from './BaseInterview'; - -// TODO: move these to chaire-lib - -export type BasePlaceAttributes = { - _uuid?: string; - - geography?: GeoJSON.Feature | undefined; - name?: string; - shortname?: string; - osmId?: string; - landRoleId?: string; - postalId?: string; - buildingId?: string; - internalId?: string; - geocodingPrecisionCategory?: GeocodingPrecisionCategory; - geocodingPrecisionMeters?: number; - geocodingQueryString?: string; - lastAction?: LastAction; - deviceUsed?: Device; - zoom?: number; -}; - -export type ExtendedPlaceAttributes = BasePlaceAttributes & { [key: string]: any }; - -export class BasePlace extends Uuidable implements IValidatable { - _isValid: Optional; - - geography?: GeoJSON.Feature; - name?: string; - shortname?: string; - address?: BaseAddress; - osmId?: string; // OpenStreetMap id (can be a node, way or relation). This is the short version: n1234, w2345, r3456 - landRoleId?: string; // official land role building/location id - postalId?: string; // official postal id - buildingId?: string; // official building id - internalId?: string; // internal id used as the primary key for places by the survey administrator - geocodingPrecisionCategory?: GeocodingPrecisionCategory; - geocodingPrecisionMeters?: number; // the max distance in meters between the geocoded point and the real location - geocodingQueryString?: string; // in most surveys, this would be the query used to geocode the location - lastAction?: LastAction; // how the location was geocoded or pinpointed on map - deviceUsed?: Device; // which device was used to pinpoint the location (only useful if not geocoded) - zoom?: number; // the zoom used when pinpointing the location on the map (only useful if not geocoded) - - _confidentialAttributes: string[] = [ - // these attributes should be hidden when exporting - ]; - - constructor(params: (BasePlaceAttributes | ExtendedPlaceAttributes) & { address?: BaseAddress }) { - super(params._uuid); - - this._isValid = undefined; - - this.geography = params.geography; - this.name = params.name; - this.shortname = params.shortname; - this.address = params.address; - this.osmId = params.osmId; - this.landRoleId = params.landRoleId; - this.postalId = params.postalId; - this.buildingId = params.buildingId; - this.internalId = params.internalId; - this.geocodingPrecisionCategory = params.geocodingPrecisionCategory; - this.geocodingPrecisionMeters = params.geocodingPrecisionMeters; - this.geocodingQueryString = params.geocodingQueryString; - this.lastAction = params.lastAction; - this.deviceUsed = params.deviceUsed; - this.zoom = params.zoom; - } - - // params must be sanitized and must be valid: - static unserialize(params: BasePlaceAttributes & { address?: BaseAddressAttributes }): BasePlace { - const address = params.address ? BaseAddress.unserialize(params.address) : undefined; - const geography = params.geography ? (params.geography as GeoJSON.Feature) : undefined; - return new BasePlace({ ...params, address, geography }); - } - - validate(): Optional { - // TODO: implement: - this._isValid = true; - return true; - } - - isValid(): Optional { - return this._isValid; - } - - /** - * Factory that validates input from an interview and makes - * sure types and required fields are valid before returning a new object - * @param dirtyParams - * @returns BasePlace | Error[] - */ - static create(dirtyParams: { [key: string]: any }): BasePlace | Error[] { - const errors = BasePlace.validateParams(dirtyParams); - return errors.length > 0 - ? errors - : new BasePlace(dirtyParams as ExtendedPlaceAttributes & { address: BaseAddress }); - } - - /** - * Validates attributes types - * @param dirtyParams The params input - * @returns Error[] TODO: specialize this error class - */ - static validateParams(dirtyParams: { [key: string]: any }): Error[] { - const errors: Error[] = []; - - // Validate params object: - if (!dirtyParams || typeof dirtyParams !== 'object') { - errors.push(new Error('BasePlace validateParams: params is undefined or invalid')); - return errors; // stop now otherwise it will crash because params are not valid - } - - // Validate UUID - const uuidErrors: Error[] = Uuidable.validateParams(dirtyParams); - if (uuidErrors.length > 0) { - errors.push(...uuidErrors); - } - - // Validate geography - if (dirtyParams.geography !== undefined) { - if (!isFeature(dirtyParams.geography) || !isPoint(dirtyParams.geography.geometry)) { - errors.push(new Error('BasePlace validateParams: geography should be a GeoJSON Point feature')); - } - } - - // Validate name (if provided) - if (dirtyParams.name !== undefined && typeof dirtyParams.name !== 'string') { - errors.push(new Error('BasePlace validateParams: name should be a string')); - } - - // Validate shortname (if provided) - if (dirtyParams.shortname !== undefined && typeof dirtyParams.shortname !== 'string') { - errors.push(new Error('BasePlace validateParams: shortname should be a string')); - } - - // Validate osmId (if provided) - // TODO: validate valid osmId - if (dirtyParams.osmId !== undefined && typeof dirtyParams.osmId !== 'string') { - errors.push(new Error('BasePlace validateParams: osmId should be a string')); - } - - // Validate landRoleId (if provided) - if (dirtyParams.landRoleId !== undefined && typeof dirtyParams.landRoleId !== 'string') { - errors.push(new Error('BasePlace validateParams: landRoleId should be a string')); - } - - // Validate postalId (if provided) - if (dirtyParams.postalId !== undefined && typeof dirtyParams.postalId !== 'string') { - errors.push(new Error('BasePlace validateParams: postalId should be a string')); - } - - // Validate buildingId (if provided) - if (dirtyParams.buildingId !== undefined && typeof dirtyParams.buildingId !== 'string') { - errors.push(new Error('BasePlace validateParams: buildingId should be a string')); - } - - // Validate internalId (if provided) - if (dirtyParams.internalId !== undefined && typeof dirtyParams.internalId !== 'string') { - errors.push(new Error('BasePlace validateParams: internalId should be a string')); - } - - // Validate geocodingPrecisionCategory (if provided) - if ( - dirtyParams.geocodingPrecisionCategory !== undefined && - typeof dirtyParams.geocodingPrecisionCategory !== 'string' - ) { - errors.push(new Error('BasePlace validateParams: geocodingPrecisionCategory should be a string')); - } - - // Validate geocodingPrecisionMeters (if provided) - if ( - dirtyParams.geocodingPrecisionMeters !== undefined && - (typeof dirtyParams.geocodingPrecisionMeters !== 'number' || isNaN(dirtyParams.geocodingPrecisionMeters)) - ) { - errors.push(new Error('BasePlace validateParams: geocodingPrecisionMeters should be a number')); - } - - // Validate geocodingQueryString (if provided) - if (dirtyParams.geocodingQueryString !== undefined && typeof dirtyParams.geocodingQueryString !== 'string') { - errors.push(new Error('BasePlace validateParams: geocodingQueryString should be a string')); - } - - // Validate lastAction (if provided) - if (dirtyParams.lastAction !== undefined && typeof dirtyParams.lastAction !== 'string') { - errors.push(new Error('BasePlace validateParams: lastAction should be a string')); - } - - // Validate internalId (if provided) - if (dirtyParams.deviceUsed !== undefined && typeof dirtyParams.deviceUsed !== 'string') { - errors.push(new Error('BasePlace validateParams: deviceUsed should be a string')); - } - - // Validate zoom (if provided) - if (dirtyParams.zoom !== undefined && (!Number.isInteger(dirtyParams.zoom) || dirtyParams.zoom < 0)) { - errors.push(new Error('BasePlace validateParams: zoom should be a positive number')); - } - - return errors; - } -} diff --git a/packages/evolution-common/src/services/baseObjects/BaseSegment.ts b/packages/evolution-common/src/services/baseObjects/BaseSegment.ts deleted file mode 100644 index d34a5f1e6..000000000 --- a/packages/evolution-common/src/services/baseObjects/BaseSegment.ts +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -/** - * A segment is the part of a trip using a single mode of transport - */ - -import { Optional } from '../../types/Optional.type'; -import { Uuidable } from './Uuidable'; -import { IValidatable } from './IValidatable'; -import * as SAttr from './attributeTypes/SegmentAttributes'; - -export type BaseSegmentAttributes = { - _uuid?: string; - - modeCategory?: SAttr.ModeCategory; // TODO: remove this an include the mode category in the mode itself - mode?: SAttr.Mode; -}; - -export type ExtendedSegmentAttributes = BaseSegmentAttributes & { [key: string]: any }; - -export class BaseSegment extends Uuidable implements IValidatable { - _isValid: Optional; - - modeCategory?: SAttr.ModeCategory; - mode?: SAttr.Mode; - - _confidentialAttributes: string[] = [ - // these attributes should be hidden when exporting - ]; - - constructor(params: BaseSegmentAttributes | ExtendedSegmentAttributes) { - super(params._uuid); - - this._isValid = undefined; - - this.modeCategory = params.modeCategory; - this.mode = params.mode; - } - - // params must be sanitized and must be valid: - static unserialize(params: BaseSegmentAttributes): BaseSegment { - return new BaseSegment(params); - } - - validate(): Optional { - // TODO: implement: - this._isValid = true; - return true; - } - - isValid(): Optional { - return this._isValid; - } - - /** - * Factory that validates input from an interview and makes - * sure types and required fields are valid before returning a new object - * @param dirtyParams - * @returns BaseSegment | Error[] - */ - static create(dirtyParams: { [key: string]: any }): BaseSegment | Error[] { - const errors = BaseSegment.validateParams(dirtyParams); - return errors.length > 0 ? errors : new BaseSegment(dirtyParams as ExtendedSegmentAttributes); - } - - /** - * Factory that validates input from an interview and makes - * sure types and required fields are valid before returning a new object - * @param dirtyParams - * @returns Error[] TODO: specialize this error class - */ - static validateParams(dirtyParams: { [key: string]: any }): Error[] { - const errors: Error[] = []; - - // Validate params object: - if (!dirtyParams || typeof dirtyParams !== 'object') { - errors.push(new Error('BaseSegment validateParams: params is undefined or invalid')); - return errors; // stop now otherwise it will crash because params are not valid - } - - // Validate UUID - const uuidErrors: Error[] = Uuidable.validateParams(dirtyParams); - if (uuidErrors.length > 0) { - errors.push(...uuidErrors); - } - - // Validate modeCategory (if provided) - if (dirtyParams.modeCategory !== undefined && typeof dirtyParams.modeCategory !== 'string') { - errors.push(new Error('BaseSegment validateParams: modeCategory should be a string')); - } - - // Validate mode (if provided) - if (dirtyParams.mode !== undefined && typeof dirtyParams.mode !== 'string') { - errors.push(new Error('BaseSegment validateParams: mode should be a string')); - } - - return errors; - } -} diff --git a/packages/evolution-common/src/services/baseObjects/BaseTrip.ts b/packages/evolution-common/src/services/baseObjects/BaseTrip.ts deleted file mode 100644 index 732590e1e..000000000 --- a/packages/evolution-common/src/services/baseObjects/BaseTrip.ts +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -/** - * A trip is has an origin and a destination, which are visited places, and is made by a person - */ - -import { Optional } from '../../types/Optional.type'; -import { Uuidable } from './Uuidable'; -import { IValidatable } from './IValidatable'; -import { Weightable, Weight, validateWeights } from './Weight'; - -export type BaseTripAttributes = { - _uuid?: string; - - /** - * Departure and arrival time must be calculated from visited places, - * so shortcuts must be converted to places and each visited place - * cloned for each occurence before creating trips here - * */ -} & Weightable; - -export type ExtendedTripAttributes = BaseTripAttributes & { [key: string]: any }; - -export class BaseTrip extends Uuidable implements IValidatable { - _isValid: Optional; - _weights?: Weight[]; - - _confidentialAttributes: string[] = [ - // these attributes should be hidden when exporting - ]; - - constructor(params: BaseTripAttributes | ExtendedTripAttributes) { - super(params._uuid); - - this._isValid = undefined; - this._weights = params._weights; - } - - // params must be sanitized and must be valid: - static unserialize(params: BaseTripAttributes): BaseTrip { - return new BaseTrip(params); - } - - validate(): Optional { - // TODO: implement: - this._isValid = true; - return true; - } - - isValid(): Optional { - return this._isValid; - } - - /** - * Factory that validates input from an interview and makes - * sure types and required fields are valid before returning a new object - * @param dirtyParams - * @returns BaseTrip | Error[] - */ - static create(dirtyParams: { [key: string]: any }): BaseTrip | Error[] { - const errors = BaseTrip.validateParams(dirtyParams); - return errors.length > 0 ? errors : new BaseTrip(dirtyParams as ExtendedTripAttributes); - } - - /** - * Validates attributes types - * @param dirtyParams The params input - * @returns Error[] TODO: specialize this error class - */ - static validateParams(dirtyParams: { [key: string]: any }): Error[] { - const errors: Error[] = []; - - // Validate params object: - if (!dirtyParams || typeof dirtyParams !== 'object') { - errors.push(new Error('BaseTrip validateParams: params is undefined or invalid')); - return errors; // stop now otherwise it will crash because params are not valid - } - - // Validate UUID - const uuidErrors: Error[] = Uuidable.validateParams(dirtyParams); - if (uuidErrors.length > 0) { - errors.push(...uuidErrors); - } - - // Validate weights: - const weightsErrors = validateWeights(dirtyParams._weights); - if (weightsErrors.length > 0) { - errors.push(...weightsErrors); - } - - return errors; - } -} diff --git a/packages/evolution-common/src/services/baseObjects/BaseTripChain.ts b/packages/evolution-common/src/services/baseObjects/BaseTripChain.ts deleted file mode 100644 index 9248092a0..000000000 --- a/packages/evolution-common/src/services/baseObjects/BaseTripChain.ts +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -// TODO: implement. We must be able to auto-generate trip chains from declared trips. - -/** - * A trip chain is a group of trips that create a closed-loop or an open-loop. - * Examples of closed-loop: Home-Work-Home, Home-Grocery-Work-Home, Home-School-Visiting friends-Home, etc. - * Examples of open-loop: Home-Leisure (staying there until the next day), Work-Home (worked a night shift), etc. - */ - -import { Optional } from '../../types/Optional.type'; -import { Uuidable } from './Uuidable'; -import { IValidatable } from './IValidatable'; -import { BaseTrip } from './BaseTrip'; -import { Weightable, Weight, validateWeights } from './Weight'; -import * as TCAttr from './attributeTypes/TripChainAttributes'; -import * as VPAttr from './attributeTypes/VisitedPlaceAttributes'; - -export type BaseTripChainAttributes = { - _uuid?: string; - - isMultiloop?: boolean; - isConstrained?: boolean; - category?: TCAttr.TripChainCategory; - mainActivityCategory?: VPAttr.ActivityCategory; // TODO: This should maybe removed and included in the activity object - mainActivity?: VPAttr.Activity; - - baseTrips?: BaseTrip[]; - - /** - * Departure and arrival time must be calculated from baseTrips - * */ -} & Weightable; - -export type ExtendedTripChainAttributes = BaseTripChainAttributes & { [key: string]: any }; - -export class BaseTripChain extends Uuidable implements IValidatable { - _isValid: Optional; - _weights?: Weight[]; - - category?: TCAttr.TripChainCategory; - isMultiloop?: boolean; - isConstrained?: boolean; - mainActivityCategory?: VPAttr.ActivityCategory; // TODO: This should maybe removed and included in the activity object - mainActivity?: VPAttr.Activity; - - baseTrips?: BaseTrip[]; - - _confidentialAttributes: string[] = [ - // these attributes should be hidden when exporting - ]; - - constructor(params: BaseTripChainAttributes | ExtendedTripChainAttributes) { - super(params._uuid); - - this._isValid = undefined; - this._weights = params._weights; - - this.category = params.category; - this.isMultiloop = params.isMultiloop; - this.isConstrained = params.isConstrained; - this.mainActivityCategory = params.mainActivityCategory; - this.mainActivity = params.mainActivity; - } - - // params must be sanitized and must be valid: - static unserialize(params: BaseTripChainAttributes): BaseTripChain { - return new BaseTripChain(params); - } - - validate(): Optional { - // TODO: implement: - this._isValid = true; - return true; - } - - isValid(): Optional { - return this._isValid; - } - - /** - * Factory that validates input from an interview and makes - * sure types and required fields are valid before returning a new object - * @param dirtyParams - * @returns BaseTripChain | Error[] - */ - static create(dirtyParams: { [key: string]: any }): BaseTripChain | Error[] { - const errors = BaseTripChain.validateParams(dirtyParams); - return errors.length > 0 ? errors : new BaseTripChain(dirtyParams as ExtendedTripChainAttributes); - } - - /** - * Validates attributes types and required fields for BaseTripChain - * @param dirtyParams The params input - * @returns Error[] TODO: specialize this error class - */ - static validateParams(dirtyParams: { [key: string]: any }): Error[] { - const errors: Error[] = []; - - // Validate params object: - if (!dirtyParams || typeof dirtyParams !== 'object') { - errors.push(new Error('BaseTripChain validateParams: params is undefined or invalid')); - return errors; // Stop now, as params are not valid - } - - // Validate UUID - const uuidErrors: Error[] = Uuidable.validateParams(dirtyParams); - if (uuidErrors.length > 0) { - errors.push(...uuidErrors); - } - - // Validate weights: - const weightsErrors = validateWeights(dirtyParams._weights); - if (weightsErrors.length > 0) { - errors.push(...weightsErrors); - } - - // Validate isMultiloop (if provided) - if (dirtyParams.isMultiloop !== undefined && typeof dirtyParams.isMultiloop !== 'boolean') { - errors.push(new Error('BaseTripChain validateParams: isMultiloop should be a boolean')); - } - - // Validate isConstrained (if provided) - if (dirtyParams.isConstrained !== undefined && typeof dirtyParams.isConstrained !== 'boolean') { - errors.push(new Error('BaseTripChain validateParams: isConstrained should be a boolean')); - } - - // Validate category (if provided) - if (dirtyParams.category !== undefined && typeof dirtyParams.category !== 'string') { - errors.push(new Error('BaseTripChain validateParams: category should be a string')); - } - - // Validate mainActivityCategory (if provided) - if (dirtyParams.mainActivityCategory !== undefined && typeof dirtyParams.mainActivityCategory !== 'string') { - errors.push(new Error('BaseTripChain validateParams: mainActivityCategory should be a string')); - } - - // Validate mainActivity (if provided) - if (dirtyParams.mainActivity !== undefined && typeof dirtyParams.mainActivity !== 'string') { - errors.push(new Error('BaseTripChain validateParams: mainActivity should be a string')); - } - - return errors; - } -} diff --git a/packages/evolution-common/src/services/baseObjects/BaseVehicle.ts b/packages/evolution-common/src/services/baseObjects/BaseVehicle.ts deleted file mode 100644 index f1b599c58..000000000 --- a/packages/evolution-common/src/services/baseObjects/BaseVehicle.ts +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -/** - * A vehicle can be used to make trips or can be surveyed for a specific household or a specific company. - */ - -import { Optional } from '../../types/Optional.type'; -import { Uuidable } from './Uuidable'; -import { IValidatable } from './IValidatable'; -import { Weightable, Weight } from './Weight'; -import * as VAttr from './attributeTypes/VehicleAttributes'; - -export type BaseVehicleAttributes = { - _uuid?: string; - - make?: VAttr.Make; // TODO: remove and use Make inside model instead - model?: VAttr.Model; - licensePlateNumber?: string; - capacitySeated?: number; - capacityStanding?: number; - - /** - * Departure and arrival time must be calculated from trips - * */ -} & Weightable; - -export type ExtendedVehicleAttributes = BaseVehicleAttributes & { [key: string]: any }; - -export class BaseVehicle extends Uuidable implements IValidatable { - _isValid: Optional; - _weights?: Weight[]; - - make?: VAttr.Make; - model?: VAttr.Model; - licensePlateNumber?: string; - capacitySeated?: number; - capacityStanding?: number; - - _confidentialAttributes: string[] = [ - // these attributes should be hidden when exporting - 'licensePlateNumber' - ]; - - constructor(params: BaseVehicleAttributes | ExtendedVehicleAttributes) { - super(params._uuid); - - this._isValid = undefined; - this._weights = params._weights; - - this.make = params.make; - this.model = params.model; - this.licensePlateNumber = params.licensePlateNumber; - this.capacitySeated = params.capacitySeated; - this.capacityStanding = params.capacityStanding; - } - - // params must be sanitized and must be valid: - static unserialize(params: BaseVehicleAttributes): BaseVehicle { - return new BaseVehicle(params); - } - - validate(): Optional { - // TODO: implement: - this._isValid = true; - return true; - } - - isValid(): Optional { - return this._isValid; - } - - /** - * Factory that validates input from an interview and makes - * sure types and required fields are valid before returning a new object - * @param dirtyParams - * @returns BaseVehicle | Error[] - */ - static create(dirtyParams: { [key: string]: any }): BaseVehicle | Error[] { - const errors = BaseVehicle.validateParams(dirtyParams); - return errors.length > 0 ? errors : new BaseVehicle(dirtyParams as ExtendedVehicleAttributes); - } - - /** - * Validates attributes types for BaseVehicle - * @param dirtyParams The params input - * @returns Error[] TODO: specialize this error class - */ - static validateParams(dirtyParams: { [key: string]: any }): Error[] { - const errors: Error[] = []; - - // Validate params object: - if (!dirtyParams || typeof dirtyParams !== 'object') { - errors.push(new Error('BaseVehicle validateParams: params is undefined or invalid')); - return errors; // Stop now; other validations may crash if params are not valid - } - - // Validate UUID - const uuidErrors: Error[] = Uuidable.validateParams(dirtyParams); - if (uuidErrors.length > 0) { - errors.push(...uuidErrors); - } - - // Validate make (if provided) - if (dirtyParams.make !== undefined && typeof dirtyParams.make !== 'string') { - errors.push(new Error('BaseVehicle validateParams: make should be a string')); - } - - // Validate model (if provided) - if (dirtyParams.model !== undefined && typeof dirtyParams.model !== 'string') { - errors.push(new Error('BaseVehicle validateParams: model should be a string')); - } - - // Validate licensePlateNumber (if provided) - if (dirtyParams.licensePlateNumber !== undefined && typeof dirtyParams.licensePlateNumber !== 'string') { - errors.push(new Error('BaseVehicle validateParams: licensePlateNumber should be a string')); - } - - // Validate capacitySeated (if provided) - if ( - dirtyParams.capacitySeated !== undefined && - (typeof dirtyParams.capacitySeated !== 'number' || - !Number.isInteger(dirtyParams.capacitySeated) || - dirtyParams.capacitySeated < 0) - ) { - errors.push(new Error('BaseVehicle validateParams: capacitySeated should be a positive integer')); - } - - // Validate capacityStanding (if provided) - if ( - dirtyParams.capacityStanding !== undefined && - (typeof dirtyParams.capacityStanding !== 'number' || - !Number.isInteger(dirtyParams.capacityStanding) || - dirtyParams.capacityStanding < 0) - ) { - errors.push(new Error('BaseVehicle validateParams: capacityStanding should be a positive integer')); - } - - return errors; - } -} diff --git a/packages/evolution-common/src/services/baseObjects/BaseVisitedPlace.ts b/packages/evolution-common/src/services/baseObjects/BaseVisitedPlace.ts deleted file mode 100644 index f808419aa..000000000 --- a/packages/evolution-common/src/services/baseObjects/BaseVisitedPlace.ts +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -/** - * A visited place is a place visited by a person during a journey or specific day - * Usually, visited places are used as origin and/or destination for trips - */ - -import { Optional } from '../../types/Optional.type'; -import { Uuidable } from './Uuidable'; -import { IValidatable } from './IValidatable'; -import { BasePlace, BasePlaceAttributes } from './BasePlace'; -import { BaseAddressAttributes } from './BaseAddress'; -import { Weightable, Weight, validateWeights } from './Weight'; -import * as VPAttr from './attributeTypes/VisitedPlaceAttributes'; -import { parseDate } from '../../utils/DateUtils'; - -export type BaseVisitedPlaceAttributes = { - _uuid?: string; - - arrivalDate?: string; - departureDate?: string; - arrivalTime?: number; - departureTime?: number; - activityCategory?: VPAttr.ActivityCategory; - activity?: VPAttr.Activity; -} & Weightable; - -export type ExtendedVisitedPlaceAttributes = BaseVisitedPlaceAttributes & { [key: string]: any }; - -export class BaseVisitedPlace extends Uuidable implements IValidatable { - _isValid: Optional; - _weights?: Weight[]; - - basePlace?: BasePlace; - - arrivalDate?: string; // string, YYYY-MM-DD - departureDate?: string; // string, YYYY-MM-DD - arrivalTime?: number; // seconds since midnight - departureTime?: number; // seconds since midnight - activityCategory?: VPAttr.ActivityCategory; // TODO: This should maybe removed and included in the activity object - activity?: VPAttr.Activity; - - _confidentialAttributes: string[] = [ - // these attributes should be hidden when exporting - ]; - - constructor(params: (BaseVisitedPlaceAttributes | ExtendedVisitedPlaceAttributes) & { basePlace: BasePlace }) { - super(params._uuid); - - this._isValid = undefined; - this._weights = params._weights; - - this.basePlace = params.basePlace; - this.arrivalDate = params.arrivalDate; - this.departureDate = params.departureDate; - this.arrivalTime = params.arrivalTime; - this.departureTime = params.departureTime; - this.activityCategory = params.activityCategory; - this.activity = params.activity; - } - - // params must be sanitized and must be valid: - static unserialize( - params: BaseVisitedPlaceAttributes & { basePlace: BasePlaceAttributes & { address?: BaseAddressAttributes } } - ): BaseVisitedPlace { - return new BaseVisitedPlace({ ...params, basePlace: BasePlace.unserialize(params.basePlace) }); - } - - validate(): Optional { - // TODO: implement: - this._isValid = true; - return true; - } - - isValid(): Optional { - return this._isValid; - } - - /** - * Factory that validates input from an interview and makes - * sure types and required fields are valid before returning a new object - * @param dirtyParams - * @returns BaseVisitedPlace | Error[] - */ - static create(dirtyParams: { [key: string]: any }): BaseVisitedPlace | Error[] { - const basePlaceParams = { - ...dirtyParams, - geography: dirtyParams.geography, - geocodingQueryString: dirtyParams.geography?.properties?.geocodingQueryString, - lastAction: dirtyParams.geography?.properties?.lastAction, - deviceUsed: dirtyParams.geography?.properties?.platform, - zoom: dirtyParams.geography?.properties?.zoom - }; - - // validate params for both baseVisitedPlace and basePlace: - const errors = [...BasePlace.validateParams(basePlaceParams), ...BaseVisitedPlace.validateParams(dirtyParams)]; - if (errors.length > 0) { - return errors; - } else { - const basePlace = BasePlace.create(basePlaceParams) as BasePlace; - const baseVisitedPlace = new BaseVisitedPlace({ - basePlace, - ...dirtyParams - } as ExtendedVisitedPlaceAttributes & { basePlace: BasePlace }); - return baseVisitedPlace; - } - } - - /** - * Validates attributes types for BaseVisitedPlace - * @param dirtyParams The params input - * @returns Error[] TODO: specialize this error class - */ - static validateParams(dirtyParams: { [key: string]: any }): Error[] { - const errors: Error[] = []; - - const arrivalDateObj = parseDate(dirtyParams.arrivalDate); - const departureDateObj = parseDate(dirtyParams.departureDate); - - // Validate params object: - if (!dirtyParams || typeof dirtyParams !== 'object') { - errors.push(new Error('BaseVisitedPlace validateParams: params is undefined or invalid')); - return errors; // stop now otherwise it will crash because params are not valid - } - - // Validate uuid: - const uuidErrors = Uuidable.validateParams(dirtyParams); - if (uuidErrors.length > 0) { - errors.push(...uuidErrors); - } - - // Validate weights: - const weightsErrors = validateWeights(dirtyParams._weights); - if (weightsErrors.length > 0) { - errors.push(...weightsErrors); - } - - // Validate base place: - if (dirtyParams.basePlace !== undefined) { - const basePlaceErrors = BasePlace.validateParams(dirtyParams.basePlace); - if (basePlaceErrors.length > 0) { - errors.push(...basePlaceErrors); - } - } - - // Validate arrivalDate (if provided): - if ( - dirtyParams.arrivalDate !== undefined && - (!(arrivalDateObj instanceof Date) || (arrivalDateObj !== undefined && isNaN(arrivalDateObj.getDate()))) - ) { - errors.push(new Error('BaseVisitedPlace validateParams: arrivalDate should be a valid date string')); - } - - // Validate departureDate (if provided): - if ( - dirtyParams.departureDate !== undefined && - (!(departureDateObj instanceof Date) || - (departureDateObj !== undefined && isNaN(departureDateObj.getDate()))) - ) { - errors.push(new Error('BaseVisitedPlace validateParams: departureDate should be a valid date string')); - } - - // Validate arrivalTime (if provided): - if ( - dirtyParams.arrivalTime !== undefined && - (!Number.isInteger(dirtyParams.arrivalTime) || dirtyParams.arrivalTime < 0) - ) { - errors.push(new Error('BaseVisitedPlace validateParams: arrivalTime should be a positive integer')); - } - - // Validate departureTime (if provided): - if ( - dirtyParams.departureTime !== undefined && - (!Number.isInteger(dirtyParams.departureTime) || dirtyParams.departureTime < 0) - ) { - errors.push(new Error('BaseVisitedPlace validateParams: departureTime should be a positive integer')); - } - - // TODO: use updated activity categories to test from list of possible values: - // Validate activityCategory (if provided): - if (dirtyParams.activityCategory !== undefined && typeof dirtyParams.activityCategory !== 'string') { - errors.push(new Error('BaseVisitedPlace validateParams: activityCategory should be a string')); - } - // TODO: use updated activites to test from list of possible values: - // Validate activity (if provided): - if (dirtyParams.activity !== undefined && typeof dirtyParams.activity !== 'string') { - errors.push(new Error('BaseVisitedPlace validateParams: activity should be a string')); - } - - return errors; - } -} diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BaseAddress.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BaseAddress.test.ts deleted file mode 100644 index 34264ab4c..000000000 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BaseAddress.test.ts +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -import { v4 as uuidV4 } from 'uuid'; -import { BaseAddress } from '../BaseAddress'; - -describe('BaseAddress Class Tests', () => { - - const _uuid = uuidV4(); - const addressParams = { - _uuid, - civicNumber: 123, - civicNumberSuffix: 'A', - unitNumber: 456, - streetName: 'Main St', - streetNameHomogenized: 'main st', - streetNameId: 'ID001', - streetNameInternalId: 'INT001', - municipalityName: 'Sample City', - municipalityCode: 'SC001', - postalMunicipalityName: 'Postal Sample City', - region: 'Sample Region', - country: 'Sample Country', - postalCode: '12345', - addressId: 'A001', - internalId: 'INT_A001' - }; - - it('should instantiate correctly with the provided parameters', () => { - const addressInstance = new BaseAddress(addressParams); - expect(addressInstance._uuid).toEqual(_uuid); - expect(addressInstance.civicNumber).toEqual(addressParams.civicNumber); - expect(addressInstance.streetName).toEqual(addressParams.streetName); - expect(addressInstance.municipalityName).toEqual(addressParams.municipalityName); - }); - - it('should set optional properties correctly', () => { - const addressInstance = new BaseAddress(addressParams); - expect(addressInstance.civicNumberSuffix).toEqual(addressParams.civicNumberSuffix); - expect(addressInstance.unitNumber).toEqual(addressParams.unitNumber); - expect(addressInstance.streetNameHomogenized).toEqual(addressParams.streetNameHomogenized); - expect(addressInstance.streetNameId).toEqual(addressParams.streetNameId); - expect(addressInstance.streetNameInternalId).toEqual(addressParams.streetNameInternalId); - expect(addressInstance.municipalityCode).toEqual(addressParams.municipalityCode); - expect(addressInstance.postalMunicipalityName).toEqual(addressParams.postalMunicipalityName); - expect(addressInstance.postalCode).toEqual(addressParams.postalCode); - expect(addressInstance.addressId).toEqual(addressParams.addressId); - expect(addressInstance.internalId).toEqual(addressParams.internalId); - }); - - it('should have correct region and country', () => { - const addressInstance = new BaseAddress(addressParams); - expect(addressInstance.region).toEqual(addressParams.region); - expect(addressInstance.country).toEqual(addressParams.country); - }); - - it('should unserialize object', () => { - const instance = BaseAddress.unserialize(addressParams); - expect(instance).toBeInstanceOf(BaseAddress); - expect(instance.region).toEqual(addressParams.region); - expect(instance.country).toEqual(addressParams.country); - }); - -}); diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BaseHousehold.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BaseHousehold.test.ts deleted file mode 100644 index 055d1fc2b..000000000 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BaseHousehold.test.ts +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -import { v4 as uuidV4 } from 'uuid'; -import { BaseHousehold, BaseHouseholdAttributes, ExtendedHouseholdAttributes } from '../BaseHousehold'; -import * as HAttr from '../attributeTypes/HouseholdAttributes'; -import { Weight } from '../Weight'; -import { WeightMethod } from '../WeightMethod'; - -const weightMethodAttributes = { - _uuid: uuidV4(), - shortname: 'sample-shortname', - name: 'Sample Weight Method', - description: 'Sample weight method description', -}; - -const baseHouseholdAttributes: BaseHouseholdAttributes = { - _uuid: uuidV4(), - size: 3, - carNumber: 2, - category: 'bar' as HAttr.HouseholdCategory, - contactPhoneNumber: '123-456-7890', - contactEmail: 'test@example.com', - _weights: [{ weight: 5.5, method: new WeightMethod(weightMethodAttributes) }, { weight: 6.3, method: new WeightMethod(weightMethodAttributes) }], -}; - -const extendedHouseholdAttributes: ExtendedHouseholdAttributes = { - ...baseHouseholdAttributes, - additionalAttribute: 'additionalValue', -}; - -const baseHouseholdAttributes2: BaseHouseholdAttributes = { - _uuid: uuidV4(), - size: 2, - carNumber: 1, - twoWheelNumber: 1, - category: 'foo' as HAttr.HouseholdCategory, - homeCarParkings: ['bar', 'foo'] as HAttr.HomePrivateCarParkingType[], - contactPhoneNumber: '123-456-7890', - contactEmail: 'sample@example.com', - pluginHybridCarNumber: 1, - electricCarNumber: 0 -}; - -describe('BaseHousehold Class Tests', () => { - - it('should create a BaseHousehold instance', () => { - const household = new BaseHousehold(baseHouseholdAttributes); - expect(household).toBeInstanceOf(BaseHousehold); - }); - - it('should create a BaseHousehold instance with extended attributes', () => { - const household = new BaseHousehold(extendedHouseholdAttributes); - expect(household).toBeInstanceOf(BaseHousehold); - }); - - it('should implement the Validatable interface', () => { - const household = new BaseHousehold(baseHouseholdAttributes); - expect(household.validate).toBeInstanceOf(Function); - expect(household.isValid).toBeInstanceOf(Function); - }); - - it('should validate a valid household', () => { - const household = new BaseHousehold(baseHouseholdAttributes); - expect(household.validate()).toBe(true); - expect(household.isValid()).toBeTruthy(); - }); - - it('should return undefined validity for an unvalidated household', () => { - const household = new BaseHousehold(baseHouseholdAttributes); - expect(household.isValid()).toBeUndefined(); - }); - - it('should implement the Weightable interface', () => { - const household = new BaseHousehold(baseHouseholdAttributes); - expect(household._weights).toBeDefined(); - }); - - it('should set weight and method correctly', () => { - const household = new BaseHousehold(baseHouseholdAttributes); - const weight: Weight = household._weights?.[0] as Weight; - expect(weight.weight).toBe(5.5); - expect(weight.method).toBeInstanceOf(WeightMethod); - expect(weight.method?.shortname).toEqual('sample-shortname'); - expect(weight.method?.name).toEqual('Sample Weight Method'); - expect(weight.method?.description).toEqual('Sample weight method description'); - }); -}); - -describe('BaseHousehold Class Additional Tests', () => { - - it('should correctly set category property if provided', () => { - const householdInstance = new BaseHousehold(baseHouseholdAttributes2); - expect(householdInstance.category).toEqual(baseHouseholdAttributes2.category); - }); - - it('should correctly set car and vehicle related properties if provided', () => { - const householdInstance = new BaseHousehold(baseHouseholdAttributes2); - expect(householdInstance.carNumber).toEqual(baseHouseholdAttributes2.carNumber); - expect(householdInstance.twoWheelNumber).toEqual(baseHouseholdAttributes2.twoWheelNumber); - expect(householdInstance.pluginHybridCarNumber).toEqual(baseHouseholdAttributes2.pluginHybridCarNumber); - expect(householdInstance.electricCarNumber).toEqual(baseHouseholdAttributes2.electricCarNumber); - }); - - it('should correctly set contact details if provided', () => { - const householdInstance = new BaseHousehold(baseHouseholdAttributes2); - expect(householdInstance.contactPhoneNumber).toEqual(baseHouseholdAttributes2.contactPhoneNumber); - expect(householdInstance.contactEmail).toEqual(baseHouseholdAttributes2.contactEmail); - }); - - it('should correctly set home car parkings property if provided', () => { - const householdInstance = new BaseHousehold(baseHouseholdAttributes2); - expect(householdInstance.homeCarParkings).toEqual(baseHouseholdAttributes2.homeCarParkings); - }); - - it('should return an empty array for valid params', () => { - const validParams = { - _uuid: uuidV4(), - size: 2, - carNumber: 1, - twoWheelNumber: 0, - pluginHybridCarNumber: 0, - electricCarNumber: 0, - category: 'single-family', - homeCarParkings: ['private-garage'], - contactPhoneNumber: '1234567890', - contactEmail: 'valid@example.com', - }; - - const errors = BaseHousehold.validateParams(validParams); - - expect(errors).toEqual([]); - }); - - it('should return an array of errors for invalid params', () => { - const invalidParams = { - _uuid: 12345, // Invalid UUID - size: -1, // Should be a non-negative integer - carNumber: 'invalid', // Should be a non-negative integer - twoWheelNumber: 2.5, // Should be a non-negative integer - pluginHybridCarNumber: -3, // Should be a non-negative integer - electricCarNumber: 0.5, // Should be a non-negative integer - category: 123, // Should be a string - homeCarParkings: [123], // Should contain valid strings - contactPhoneNumber: 9876543210, // Should be a string - contactEmail: new Date(), // Should be a valid email address - _weights: [ - { weight: 5.5, method: 'foo' }, - { weight: -3.2, method: new WeightMethod(weightMethodAttributes) }, - { weight: 'bar', method: new WeightMethod(weightMethodAttributes) }] - }; - - const errors = BaseHousehold.validateParams(invalidParams); - - expect(errors).toEqual([ - new Error('Uuidable validateParams: _uuid should be a valid uuid'), - new Error('Weightable validateWeights: method at index 0 must be an instance of WeightMethod'), - new Error('Weightable validateWeights: weight at index 1 must be a positive number'), - new Error('Weightable validateWeights: weight at index 2 must be a positive number'), - new Error('BaseHousehold validateParams: size should be a positive integer'), - new Error('BaseHousehold validateParams: carNumber should be a positive integer'), - new Error('BaseHousehold validateParams: twoWheelNumber should be a positive integer'), - new Error('BaseHousehold validateParams: pluginHybridCarNumber should be a positive integer'), - new Error('BaseHousehold validateParams: electricCarNumber should be a positive integer'), - new Error('BaseHousehold validateParams: category should be a string'), - new Error('BaseHousehold validateParams: homeCarParkings index 0 should be a string'), - new Error('BaseHousehold validateParams: contactPhoneNumber should be a string'), - new Error('BaseHousehold validateParams: contactEmail should be a string'), - ]); - }); - - it('should return an empty array of errors for empty params', () => { - const emptyParams = {}; //Accept empty params - const errors = BaseHousehold.validateParams(emptyParams); - expect(errors).toEqual([]); - }); - - it('should unserialize object', () => { - const instance = BaseHousehold.unserialize(baseHouseholdAttributes); - expect(instance).toBeInstanceOf(BaseHousehold); - expect(instance.size).toEqual(baseHouseholdAttributes.size); - expect(instance.carNumber).toEqual(baseHouseholdAttributes.carNumber); - }); - -}); - diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BaseInterview.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BaseInterview.test.ts deleted file mode 100644 index bdfadc860..000000000 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BaseInterview.test.ts +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -import { v4 as uuidV4 } from 'uuid'; -import { BaseInterview, BaseInterviewAttributes } from '../BaseInterview'; -import { Survey, SurveyAttributes } from '../Survey'; -import { Sample, SampleAttributes } from '../Sample'; -import { SurveyableAttributes } from '../Surveyable'; - -const validUUID = uuidV4(); - -describe('BaseInterview', () => { - - const surveyAttributes: SurveyAttributes = { - name: 'Survey name', - shortname: 'survey_shortname', - startDate: '2023-10-01', - endDate: '2023-10-31' - }; - - const sampleAttributes: SampleAttributes = { - name: 'Sample name', - shortname: 'sample_shortname' - }; - - const surveyableAttributes: SurveyableAttributes = { - survey: new Survey(surveyAttributes), - sample: new Sample(sampleAttributes), - sampleBatchNumber: 123, - }; - - const interviewAttributes: BaseInterviewAttributes = { - _uuid: validUUID, - accessCode: '0000-0000', - _startedAt: new Date('2023-10-05 02:34:55'), - _updatedAt: new Date('2023-10-06 07:00:23'), - _completedAt: '2023-10-07 09:12:00', - assignedDate: '2023-10-03', - contactPhoneNumber: '+1 514-999-9999', - contactEmail: 'test@test.test', - _language: 'en', - _source: 'web', - _isCompleted: true, - _device: 'mobile' - }; - const baseInterviewAttributes: BaseInterviewAttributes & SurveyableAttributes = { - ...surveyableAttributes, - ...interviewAttributes - }; - - it('should create a new BaseInterview instance', () => { - const interview = new BaseInterview(baseInterviewAttributes); - expect(interview).toBeInstanceOf(BaseInterview); - expect(interview._uuid).toEqual(validUUID); - expect(interview.accessCode).toEqual('0000-0000'); - expect(interview._startedAt).toEqual(new Date('2023-10-05 02:34:55')); - expect(interview._updatedAt).toEqual(new Date('2023-10-06 07:00:23')); - expect(interview._completedAt).toEqual(new Date('2023-10-07 09:12:00')); - expect(interview.assignedDate).toEqual('2023-10-03'); - expect(interview.contactPhoneNumber).toEqual('+1 514-999-9999'); - expect(interview.contactEmail).toEqual('test@test.test'); - expect(interview._language).toEqual('en'); - expect(interview._source).toEqual('web'); - expect(interview._isCompleted).toEqual(true); - expect(interview._device).toEqual('mobile'); - - }); - - it('should create a new BaseInterview instance with minimal attributes', () => { - const minimalAttributes: BaseInterviewAttributes & SurveyableAttributes = { - _uuid: validUUID, - survey: surveyableAttributes.survey, - sample: surveyableAttributes.sample, - }; - - const interview = new BaseInterview(minimalAttributes); - expect(interview).toBeInstanceOf(BaseInterview); - expect(interview._uuid).toEqual(validUUID); - expect(interview.accessCode).toBeUndefined(); - expect(interview._startedAt).toBeUndefined(); - expect(interview._updatedAt).toBeUndefined(); - expect(interview._completedAt).toBeUndefined(); - expect(interview.assignedDate).toBeUndefined(); - expect(interview.contactPhoneNumber).toBeUndefined(); - expect(interview.contactEmail).toBeUndefined(); - expect(interview._language).toBeUndefined(); - expect(interview._source).toBeUndefined(); - expect(interview._isCompleted).toBeUndefined(); - expect(interview._device).toBeUndefined(); - - }); - - it('should validate a BaseInterview instance', () => { - const interview = new BaseInterview(baseInterviewAttributes); - expect(interview.isValid()).toBeUndefined(); - const validationResult = interview.validate(); - expect(validationResult).toBe(true); - expect(interview.isValid()).toBe(true); - }); - - it('should validate params', () => { - const validParams = { - _uuid: uuidV4(), - accessCode: '0000-0000', - _startedAt: new Date(), - _updatedAt: new Date(), - _completedAt: new Date(), - assignedDate: '2023-01-01', - constactEmail: 'test@test.test', - contactPhoneNumber: '514-999-9999 #999', - _language: 'fr', - _source: 'postal', - _isCompleted: false, - _device: 'unknown', - }; - - const validErrors = BaseInterview.validateParams(validParams); - expect(validErrors).toEqual([]); - - const invalidParams = { - _uuid: 'invalid-uuid', - accessCode: {}, - _startedAt: 'invalid-date', - _updatedAt: 'invalid-date', - assignedDate: {}, - _completedAt: {}, - contactEmail: new Date(), - contactPhoneNumber: Infinity, - _language: 'aaa', - _source: {}, - _isCompleted: 'true', - _device: 'foo', - }; - - const invalidErrors = BaseInterview.validateParams(invalidParams); - expect(invalidErrors.length).toBeGreaterThan(0); - expect(invalidErrors).toEqual([ - new Error('Uuidable validateParams: _uuid should be a valid uuid'), - new Error('BaseInterview validateParams: accessCode should be a string'), - new Error('BaseInterview validateParams: _language should be a string of two letters'), - new Error('BaseInterview validateParams: _source should be a string'), - new Error('BaseInterview validateParams: _isCompleted should be a boolean'), - new Error('BaseInterview validateParams: assignedDate should be a valid date string'), - new Error('BaseInterview validateParams: invalid _startedAt'), - new Error('BaseInterview validateParams: invalid _completedAt'), - new Error('BaseInterview validateParams: invalid _updatedAt'), - new Error('BaseInterview validateParams: contactPhoneNumber should be a string'), - new Error('BaseInterview validateParams: contactEmail should be a string'), - new Error('BaseInterview validateParams: _device is invalid'), - ]); - }); - - it('should unserialize object', () => { - const instance = BaseInterview.unserialize({ - ...interviewAttributes, - sample: sampleAttributes, - survey: surveyAttributes, - sampleBatchNumber: 123 - }); - expect(instance).toBeInstanceOf(BaseInterview); - expect(instance.accessCode).toEqual(baseInterviewAttributes.accessCode); - expect(instance.survey).toBeInstanceOf(Survey); - expect(instance.survey.name).toEqual(surveyAttributes.name); - expect(instance.sample).toBeInstanceOf(Sample); - expect(instance.sample.name).toEqual(sampleAttributes.name); - }); - -}); diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BaseJourney.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BaseJourney.test.ts deleted file mode 100644 index 88bac2668..000000000 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BaseJourney.test.ts +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -import { v4 as uuidV4 } from 'uuid'; -import { BaseJourney, BaseJourneyAttributes, ExtendedJourneyAttributes } from '../BaseJourney'; -import { BaseVisitedPlace, BaseVisitedPlaceAttributes } from '../BaseVisitedPlace'; -import { BaseTrip, BaseTripAttributes } from '../BaseTrip'; -import { BaseTripChain, BaseTripChainAttributes } from '../BaseTripChain'; -import { BasePlace } from '../BasePlace'; -import { Weight } from '../Weight'; -import { WeightMethod } from '../WeightMethod'; -import * as VPAttr from '../attributeTypes/VisitedPlaceAttributes'; -import * as TCAttr from '../attributeTypes/TripChainAttributes'; - -const validUUID = uuidV4(); - -describe('BaseJourney', () => { - - const weightMethodAttributes = { - _uuid: uuidV4(), - shortname: 'sample-shortname', - name: 'Sample Weight Method', - description: 'Sample weight method description', - }; - - const baseJourneyAttributes: ExtendedJourneyAttributes = { - _uuid: validUUID, - startDate: '2023-10-05', - endDate: '2023-10-06', - startTime: 36000, // 10 hours in seconds - endTime: 72000, // 20 hours in seconds - name: 'Journey name', - _weights: [{ weight: 2.333, method: new WeightMethod(weightMethodAttributes) }], - foo: 'bar' - }; - - it('should create a new BaseJourney instance', () => { - const journey = new BaseJourney(baseJourneyAttributes); - expect(journey).toBeInstanceOf(BaseJourney); - expect(journey._uuid).toEqual(validUUID); - expect(journey.name).toEqual('Journey name'); - expect(journey.startDate).toEqual('2023-10-05'); - expect(journey.endDate).toEqual('2023-10-06'); - expect(journey.startTime).toEqual(36000); - expect(journey.endTime).toEqual(72000); - expect(journey._weights).toBeDefined(); - }); - - it('should create a new BaseJourney instance with minimal attributes', () => { - const minimalAttributes: BaseJourneyAttributes = { - _uuid: validUUID, - startDate: '2023-10-07', - endDate: '2023-10-08', - startTime: 36100, // 10 hours in seconds - endTime: 72100, // 20 hours in seconds - }; - - const journey = new BaseJourney(minimalAttributes); - expect(journey).toBeInstanceOf(BaseJourney); - expect(journey._uuid).toEqual(validUUID); - }); - - it('should validate a BaseJourney instance', () => { - const journey = new BaseJourney(baseJourneyAttributes); - expect(journey.isValid()).toBeUndefined(); - const validationResult = journey.validate(); - expect(validationResult).toBe(true); - expect(journey.isValid()).toBe(true); - }); - - it('should set weight and method correctly', () => { - const journey = new BaseJourney(baseJourneyAttributes); - const weight: Weight = journey._weights?.[0] as Weight; - expect(weight.weight).toBe(2.333); - expect(weight.method).toBeInstanceOf(WeightMethod); - expect(weight.method?.shortname).toEqual('sample-shortname'); - expect(weight.method?.name).toEqual('Sample Weight Method'); - expect(weight.method?.description).toEqual('Sample weight method description'); - }); - - it('should validate params with valid values', () => { - const validParams = { - _uuid: uuidV4(), - startDate: '2023-01-06', - startTime: 3600, - endDate: '2023-10-06', - endTime: 7200, - name: 'Valid Journey', - _weights: [], - }; - - const errors = BaseJourney.validateParams(validParams); - expect(errors.length).toBe(0); - }); - - it('should return errors for invalid params', () => { - const invalidParams = { - _uuid: 'invalid-uuid', - startDate: 'invalid-date', - startTime: 'invalid-time', - endDate: 'invalid-date', - endTime: 'invalid-time', - name: 123, - }; - - const errors = BaseJourney.validateParams(invalidParams); - expect(errors.length).toBeGreaterThan(0); - expect(errors).toEqual([ - new Error('Uuidable validateParams: _uuid should be a valid uuid'), - new Error('BaseJourney validateParams: startDate is required and should be a valid date string'), - new Error('BaseJourney validateParams: startTime is required and should be a non-negative number'), - new Error('BaseJourney validateParams: endDate is required and should be a valid date string'), - new Error('BaseJourney validateParams: endTime is required and should be a non-negative number'), - new Error('BaseJourney validateParams: name should be a string'), - ]); - }); - - it('should unserialize object', () => { - const instance = BaseJourney.unserialize(baseJourneyAttributes); - expect(instance).toBeInstanceOf(BaseJourney); - expect(instance.name).toEqual(baseJourneyAttributes.name); - }); - - -}); diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BaseJunction.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BaseJunction.test.ts deleted file mode 100644 index 994b9d61a..000000000 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BaseJunction.test.ts +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright 2024, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -import { v4 as uuidV4 } from 'uuid'; -import { BaseJunction, BaseJunctionAttributes, ExtendedJunctionAttributes } from '../BaseJunction'; -import { BasePlace, BasePlaceAttributes } from '../BasePlace'; - -const validUUID = uuidV4(); - -describe('BaseJunction', () => { - - const geojson = { - type: 'Feature', - id: 444, - geometry: { - type: 'Point', - coordinates: [23.5, -11.0033423], - }, - properties: { - foo: 'boo2', - bar: 'far2' - }, - } as GeoJSON.Feature; - - const basePlaceAttributes: BasePlaceAttributes = { - geography: geojson, - _uuid: validUUID, - }; - - const baseJunctionAttributes: BaseJunctionAttributes = { - _uuid: validUUID, - arrivalDate: '2023-10-05', - departureDate: '2023-10-06', - arrivalTime: 36000, // 10:00 AM in seconds since midnight - departureTime: 45000, // 12:30 PM in seconds since midnight - }; - - it('should create a new BaseJunction instance', () => { - const junction = new BaseJunction({ ...baseJunctionAttributes, basePlace: new BasePlace(basePlaceAttributes) }); - expect(junction).toBeInstanceOf(BaseJunction); - expect(junction._uuid).toEqual(validUUID); - expect(junction.basePlace).toBeInstanceOf(BasePlace); - expect(junction.basePlace?.geography?.type).toEqual('Feature'); - expect(junction.basePlace?.geography?.geometry?.type).toEqual('Point'); - expect(junction.basePlace?.geography?.geometry?.coordinates).toEqual([23.5, -11.0033423]); - expect(junction.basePlace?.geography?.properties).toEqual({ foo: 'boo2', bar: 'far2' }); - expect(junction.basePlace?.geography?.id).toEqual(444); - expect(junction.arrivalDate).toEqual('2023-10-05'); - expect(junction.departureDate).toEqual('2023-10-06'); - expect(junction.arrivalTime).toEqual(36000); - expect(junction.departureTime).toEqual(45000); - }); - - it('should create a new BaseJunction instance with minimal attributes', () => { - const minimalBasePlaceAttributes: BasePlaceAttributes = { - geography: undefined, - _uuid: validUUID, - name: 'Minimal Test Place', - }; - - const minimalJunctionAttributes: BaseJunctionAttributes = { - _uuid: validUUID, - }; - - const junction = new BaseJunction({ ...minimalJunctionAttributes, basePlace: new BasePlace(minimalBasePlaceAttributes) }); - expect(junction).toBeInstanceOf(BaseJunction); - expect(junction._uuid).toEqual(validUUID); - expect(junction.basePlace).toBeInstanceOf(BasePlace); - expect(junction.basePlace?.geography).toBeUndefined(); - expect(junction.basePlace?.name).toEqual('Minimal Test Place'); - expect(junction.arrivalDate).toBeUndefined(); - expect(junction.departureDate).toBeUndefined(); - expect(junction.arrivalTime).toBeUndefined(); - expect(junction.departureTime).toBeUndefined(); - }); - - it('should validate a BaseJunction instance', () => { - const junction = new BaseJunction({ ...baseJunctionAttributes, basePlace: new BasePlace(basePlaceAttributes) }); - expect(junction.isValid()).toBeUndefined(); - const validationResult = junction.validate(); - expect(validationResult).toBe(true); - expect(junction.isValid()).toBe(true); - }); - - it('should accept extended attributes', () => { - const extendedJunctionAttributes: ExtendedJunctionAttributes = { - ...baseJunctionAttributes, - customAttribute: 'Custom Value', - }; - - const junction = new BaseJunction({ ...extendedJunctionAttributes, basePlace: new BasePlace(basePlaceAttributes) }); - expect(junction).toBeInstanceOf(BaseJunction); - }); - - it('should validate valid parameters', () => { - const validParams = { - _uuid: uuidV4(), - basePlace: new BasePlace({} as BasePlaceAttributes), - arrivalDate: '2023-01-15', - departureDate: '2023-01-16', - arrivalTime: 36000, // 10:00 AM in seconds since midnight - departureTime: 43200, // 12:00 PM in seconds since midnight - }; - const errors = BaseJunction.validateParams(validParams); - expect(errors).toEqual([]); - }); - - it('should validate valid parameters with minimal attributes', () => { - const validParams = {}; - const errors = BaseJunction.validateParams(validParams); - expect(errors).toEqual([]); - }); - - it('should validate valid parameters with additional attributes', () => { - const validParams = { - _uuid: uuidV4(), - basePlace: new BasePlace({} as BasePlaceAttributes), - arrivalDate: '2023-01-15', - departureDate: '2023-01-16', - arrivalTime: 36000, - departureTime: 43200, - additionalAttribute: 'additionalValue', - }; - const errors = BaseJunction.validateParams(validParams); - expect(errors).toEqual([]); - }); - - it('should return errors for invalid parameters', () => { - const invalidParams = { - _uuid: 'foo', // Invalid UUID - //basePlace: new BasePlace({} as BasePlaceAttributes), - arrivalDate: {}, // Invalid date string - departureDate: [], // Invalid date string - arrivalTime: -1, // Negative arrival time - departureTime: '12:00 PM', // Invalid departure time string - activityCategory: 44, // Invalid activity category - activity: -123283764.34534, // Invalid activity - }; - - const errors = BaseJunction.validateParams(invalidParams); - expect(errors).toHaveLength(5); - expect(errors[0].message).toEqual('Uuidable validateParams: _uuid should be a valid uuid'); - expect(errors[1].message).toEqual('BaseJunction validateParams: arrivalDate should be a valid date string'); - expect(errors[2].message).toEqual('BaseJunction validateParams: departureDate should be a valid date string'); - expect(errors[3].message).toEqual('BaseJunction validateParams: arrivalTime should be a positive integer'); - expect(errors[4].message).toEqual('BaseJunction validateParams: departureTime should be a positive integer'); - }); - - it('should use the create static factory method correctly', () => { - const invalidParams = { - _uuid: 'foo', // Invalid UUID - basePlace: new BasePlace({} as BasePlaceAttributes), - arrivalDate: '2023-01-15', // Invalid date string - departureDate: '2023-01-16', // Invalid date string - arrivalTime: -1, // Negative arrival time - departureTime: '12:00 PM', // Invalid departure time string - activityCategory: 44, // Invalid activity category - activity: -123283764.34534, // Invalid activity - }; - const validParams = { - _uuid: uuidV4(), // Invalid UUID - basePlace: new BasePlace({} as BasePlaceAttributes), - arrivalDate: '2023-01-15', - departureDate: '2023-01-16', - arrivalTime: 36000, - departureTime: 43200, - additionalAttribute: 'additionalValue', - }; - - const invalidResult = BaseJunction.create(invalidParams); - const validResult = BaseJunction.create(validParams); - - expect(invalidResult).toBeInstanceOf(Array); - expect(invalidResult[0]).toBeInstanceOf(Error); - expect(validResult).toBeInstanceOf(BaseJunction); - }); - - it('should unserialize object', () => { - const instance = BaseJunction.unserialize({ ...baseJunctionAttributes, basePlace: BasePlace.unserialize(basePlaceAttributes) }); - expect(instance).toBeInstanceOf(BaseJunction); - expect(instance.departureDate).toEqual(baseJunctionAttributes.departureDate); - }); -}); diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BaseOrganization.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BaseOrganization.test.ts deleted file mode 100644 index a98c9c14a..000000000 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BaseOrganization.test.ts +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -import { v4 as uuidV4 } from 'uuid'; -import { BaseOrganization, BaseOrganizationAttributes, ExtendedOrganizationAttributes } from '../BaseOrganization'; -import * as OAttr from '../attributeTypes/OrganizationAttributes'; -import { Weight } from '../Weight'; -import { WeightMethod } from '../WeightMethod'; - -const validUUID = uuidV4(); - -describe('BaseOrganization', () => { - - const weightMethodAttributes = { - _uuid: uuidV4(), - shortname: 'sample-shortname', - name: 'Sample Weight Method', - description: 'Sample weight method description', - }; - - const baseOrganizationAttributes: BaseOrganizationAttributes = { - _uuid: validUUID, - name: 'XYZ Corp', - shortname: 'xyz_corp', - category: 'company' as OAttr.OrganizationCategory, - size: 500, - contactEmail: 'test@test.test', - contactPhoneNumber: '123-456-7890', - vehicleNumber: 23, - pluginHybridVehicleNumber: 12, - electricVehicleNumber: 0, - _weights: [{ weight: 0.1, method: new WeightMethod(weightMethodAttributes) }], - }; - - - it('should create a new BaseOrganization instance', () => { - const organization = new BaseOrganization(baseOrganizationAttributes); - expect(organization).toBeInstanceOf(BaseOrganization); - expect(organization._uuid).toEqual(validUUID); - expect(organization.name).toEqual('XYZ Corp'); - expect(organization.shortname).toEqual('xyz_corp'); - expect(organization.category).toEqual('company'); - expect(organization.size).toEqual(500); - expect(organization.contactEmail).toEqual('test@test.test'); - expect(organization.contactPhoneNumber).toEqual('123-456-7890'); - expect(organization.vehicleNumber).toEqual(23); - expect(organization.pluginHybridVehicleNumber).toEqual(12); - expect(organization.electricVehicleNumber).toEqual(0); - expect(organization._weights).toBeDefined(); - }); - - it('should create a new BaseOrganization instance with minimal attributes', () => { - const minimalAttributes: BaseOrganizationAttributes = { - _uuid: validUUID - }; - - const organization = new BaseOrganization(minimalAttributes); - expect(organization).toBeInstanceOf(BaseOrganization); - expect(organization._uuid).toEqual(validUUID); - expect(organization.name).toBeUndefined(); - expect(organization.shortname).toBeUndefined(); - expect(organization.category).toBeUndefined(); - expect(organization.size).toBeUndefined(); - }); - - it('should validate a BaseOrganization instance', () => { - const organization = new BaseOrganization(baseOrganizationAttributes); - expect(organization.isValid()).toBeUndefined(); - const validationResult = organization.validate(); - expect(validationResult).toBe(true); - expect(organization.isValid()).toBe(true); - }); - - it('should accept extended attributes', () => { - const extendedAttributes: ExtendedOrganizationAttributes = { - ...baseOrganizationAttributes, - customAttribute: 'Custom Value', - }; - - const organization = new BaseOrganization(extendedAttributes); - expect(organization).toBeInstanceOf(BaseOrganization); - }); - - it('should set weight and method correctly', () => { - const organization = new BaseOrganization(baseOrganizationAttributes); - const weight: Weight = organization._weights?.[0] as Weight; - expect(weight.weight).toBe(0.1); - expect(weight.method).toBeInstanceOf(WeightMethod); - expect(weight.method?.shortname).toEqual('sample-shortname'); - expect(weight.method?.name).toEqual('Sample Weight Method'); - expect(weight.method?.description).toEqual('Sample weight method description'); - }); - - it('should validate params with valid values', () => { - const validParams = { - _uuid: uuidV4(), - name: 'Valid Org', - shortname: 'VO', - size: 100, - category: 'Category', - vehicleNumber: 5, - pluginHybridVehicleNumber: 2, - electricVehicleNumber: 3, - contactPhoneNumber: '123-456-7890', - contactEmail: 'valid@example.com', - _weights: [{ weight: 2.333, method: new WeightMethod(weightMethodAttributes) }], - }; - - const errors = BaseOrganization.validateParams(validParams); - expect(errors.length).toBe(0); - }); - - it('should return errors for invalid params', () => { - const invalidParams = { - _uuid: 123, // Invalid UUID - name: 123, // Invalid name - shortname: 456, // Invalid shortname - size: -1, // Negative size - category: {}, // Invalid category - vehicleNumber: 'invalid', // Non-integer vehicleNumber - pluginHybridVehicleNumber: -2, // Negative pluginHybridVehicleNumber - electricVehicleNumber: 'invalid', // Non-integer electricVehicleNumber - contactPhoneNumber: 1234567890, // Invalid contactPhoneNumber - contactEmail: 'invalid-email', // Invalid contactEmail - _weights: 'not-an-array', // Invalid _weights - }; - - const errors = BaseOrganization.validateParams(invalidParams); - expect(errors.length).toBeGreaterThan(0); - expect(errors).toEqual([ - new Error('Uuidable validateParams: _uuid should be a valid uuid'), - new Error('Weightable validateWeights: _weights should be an array'), - new Error('BaseOrganization validateParams: name should be a string'), - new Error('BaseOrganization validateParams: shortname should be a string'), - new Error('BaseOrganization validateParams: size should be a positive integer'), - new Error('BaseOrganization validateParams: category is not a valid organization category'), - new Error('BaseOrganization validateParams: vehicleNumber should be a positive integer'), - new Error('BaseOrganization validateParams: pluginHybridVehicleNumber should be a positive integer'), - new Error('BaseOrganization validateParams: electricVehicleNumber should be a positive integer'), - new Error('BaseOrganization validateParams: contactPhoneNumber should be a string'), - ]); - }); - - it('should validate params with missing optional values', () => { - const params = { - _uuid: uuidV4(), - name: 'Valid Org', - }; - - const errors = BaseOrganization.validateParams(params); - expect(errors.length).toBe(0); - }); - - it('should unserialize object', () => { - const instance = BaseOrganization.unserialize(baseOrganizationAttributes); - expect(instance).toBeInstanceOf(BaseOrganization); - expect(instance.name).toEqual(baseOrganizationAttributes.name); - }); -}); diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BasePerson.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BasePerson.test.ts deleted file mode 100644 index 555cb269e..000000000 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BasePerson.test.ts +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ -import { BasePerson, BasePersonAttributes, ExtendedPersonAttributes } from '../BasePerson'; -import { v4 as uuidV4 } from 'uuid'; -import * as PAttr from '../attributeTypes/PersonAttributes'; -import { Weight } from '../Weight'; -import { WeightMethod } from '../WeightMethod'; - -describe('BasePerson', () => { - - const validUuid = uuidV4(); // Generate a valid UUID - - const weightMethodAttributes = { - _uuid: uuidV4(), - shortname: 'sample-shortname', - name: 'Sample Weight Method', - description: 'Sample weight method description', - }; - - const personAttributes: ExtendedPersonAttributes = { - _uuid: validUuid, - age: 30, // Valid age - ageGroup: '30-34' as PAttr.AgeGroup, // Valid age group - gender: 'male' as PAttr.Gender, // Valid gender - drivingLicenseOwnership: 'yes' as PAttr.DrivingLicenseOwnership, // Valid driving license ownership - transitPassOwnership: 'no' as PAttr.TransitPassOwnership, // Valid transit pass ownership - carsharingMember: 'yes' as PAttr.CarsharingMember, // Valid carsharing membership - carsharingUser: 'yes' as PAttr.CarsharingUser, // Valid carsharing user status - bikesharingMember: 'no' as PAttr.BikesharingMember, // Valid bikesharing membership - bikesharingUser: 'no' as PAttr.BikesharingUser, // Valid bikesharing user status - ridesharingMember: 'dontKnow' as PAttr.RidesharingMember, // Valid ridesharing membership - ridesharingUser: 'nonApplicable' as PAttr.RidesharingUser, // Valid ridesharing user status - occupation: 'fullTimeWorker' as PAttr.Occupation, // Valid occupation - jobCategory: 'Software Developer' as PAttr.JobCategory, // Valid job category - jobName: 'Software Engineer' as PAttr.JobName, // Valid job name - isOnTheRoadWorker: 'no' as PAttr.IsOnTheRoadWorker, // Valid on-the-road worker status - isJobTelecommuteCompatible: 'yes' as PAttr.HasTelecommuteCompatibleJob, // Valid telecommute compatibility status, renamed from isJobTelecommuteCompatible to hasTelecommuteCompatibleJob in new Person class - educationalAttainment: 'PhD' as PAttr.EducationalAttainment, // Valid educational attainment - _weights: [{ weight: 1.5, method: new WeightMethod(weightMethodAttributes) }], - foo: 'bar', // extended attribute - }; - - it('should create a BasePerson instance with valid attributes', () => { - - - const person = new BasePerson(personAttributes); - - expect(person).toBeInstanceOf(BasePerson); - expect(person._uuid).toBe(validUuid); - expect(person.age).toBe(30); - expect(person.ageGroup).toBe('30-34'); - expect(person.gender).toBe('male'); - expect(person.drivingLicenseOwnership).toBe('yes'); - expect(person.transitPassOwnership).toBe('no'); - expect(person.carsharingMember).toBe('yes'); - expect(person.carsharingUser).toBe('yes'); - expect(person.bikesharingMember).toBe('no'); - expect(person.bikesharingUser).toBe('no'); - expect(person.ridesharingMember).toBe('dontKnow'); - expect(person.ridesharingUser).toBe('nonApplicable'); - expect(person.occupation).toBe('fullTimeWorker'); - expect(person.jobCategory).toBe('Software Developer'); - expect(person.jobName).toBe('Software Engineer'); - expect(person.isOnTheRoadWorker).toBe('no'); - expect(person.isJobTelecommuteCompatible).toBe('yes'); - expect(person.educationalAttainment).toBe('PhD'); - }); - - it('should allow empty arrays and home', () => { - - const personAttributes2: ExtendedPersonAttributes = { - _uuid: validUuid, - foo: 'bar', // extended attribute - }; - const person2 = new BasePerson(personAttributes2); - expect(person2).toBeInstanceOf(BasePerson); - }); - - it('should validate a BasePerson instance', () => { - const personAttributes = new BasePerson({} as BasePersonAttributes); - const person = new BasePerson(personAttributes); - expect(person.isValid()).toBeUndefined(); - const validationResult = person.validate(); - expect(validationResult).toBe(true); - expect(person.isValid()).toBe(true); - }); - - it('should set weight and method correctly', () => { - const person = new BasePerson(personAttributes); - const weight: Weight = person._weights?.[0] as Weight; - expect(weight.weight).toBe(1.5); - expect(weight.method).toBeInstanceOf(WeightMethod); - expect(weight.method?.shortname).toEqual('sample-shortname'); - expect(weight.method?.name).toEqual('Sample Weight Method'); - expect(weight.method?.description).toEqual('Sample weight method description'); - }); - - test('validateParams with valid parameters', () => { - const params = { - _uuid: uuidV4(), - age: 23, - ageGroup: '20-24', - gender: 'male', - drivingLicenseOwnership: 'yes', - transitPassOwnership: 'no', - carsharingMember: 'yes', - carsharingUser: 'yes', - bikesharingMember: 'no', - bikesharingUser: 'yes', - ridesharingMember: 'no', - ridesharingUser: 'yes', - occupation: 'retired', - jobCategory: 'technology', - jobName: 'Software Developer', - isOnTheRoadWorker: true, - isJobTelecommuteCompatible: false, - educationalAttainment: 'Ph.D', - nickname: 'John Doe', - contactPhoneNumber: '123-456-7890', - contactEmail: 'john.doe@example.com', - }; - - const errors = BasePerson.validateParams(params); - expect(errors.length).toBe(0); - }); - - test('validateParams with invalid parameters', () => { - const params = { - _uuid: 'invalid_uuid', - age: 'InvalidAge', - gender: 123, - drivingLicenseOwnership: 4343, - transitPassOwnership: {}, - carsharingMember: null, - carsharingUser: -932.34, - bikesharingMember: 44, - bikesharingUser: 324, - ridesharingMember: new Date(), - ridesharingUser: [], - occupation: [1,2,3], - jobCategory: ['foo', 'bar'], - jobName: 123, - isOnTheRoadWorker: Infinity, - isJobTelecommuteCompatible: 1234, - educationalAttainment: 5678, - contactPhoneNumber: 123, // Invalid type - contactEmail: 43.4, // Invalid email format - }; - - const errors = BasePerson.validateParams(params); - expect(errors.length).toBeGreaterThan(0); - expect(errors).toEqual([ - new Error('Uuidable validateParams: _uuid should be a valid uuid'), - new Error('BasePerson validateParams: age must be a positive integer'), - new Error('BasePerson validateParams: gender is not a valid value'), - new Error('BasePerson validateParams: drivingLicenseOwnership is not a valid value'), - new Error('BasePerson validateParams: transitPassOwnership is not a valid value'), - new Error('BasePerson validateParams: carsharingMember is not a valid value'), - new Error('BasePerson validateParams: carsharingUser is not a valid value'), - new Error('BasePerson validateParams: bikesharingMember is not a valid value'), - new Error('BasePerson validateParams: bikesharingUser is not a valid value'), - new Error('BasePerson validateParams: ridesharingMember is not a valid value'), - new Error('BasePerson validateParams: ridesharingUser is not a valid value'), - new Error('BasePerson validateParams: occupation is not a valid value'), - new Error('BasePerson validateParams: jobCategory is not a valid value'), - new Error('BasePerson validateParams: jobName should be a string'), - new Error('BasePerson validateParams: isOnTheRoadWorker should be a boolean'), - new Error('BasePerson validateParams: isJobTelecommuteCompatible should be a boolean'), - new Error('BasePerson validateParams: educationalAttainment is not a valid value'), - new Error('BasePerson validateParams: contactPhoneNumber should be a string'), - new Error('BasePerson validateParams: contactEmail should be a string'), - ]); - }); - - test('validateParams with invalid age', () => { - const params = { - age: -324 - }; - - const errors = BasePerson.validateParams(params); - expect(errors.length).toBeGreaterThan(0); - expect(errors).toEqual([ - new Error('BasePerson validateParams: age must be a positive integer') - ]); - - const params2 = { - age: 34.5 - }; - - const errors2 = BasePerson.validateParams(params2); - expect(errors2.length).toBeGreaterThan(0); - expect(errors).toEqual([ - new Error('BasePerson validateParams: age must be a positive integer') - ]); - - const params3 = { - age: -99.23434 - }; - - const errors3 = BasePerson.validateParams(params3); - expect(errors3.length).toBeGreaterThan(0); - expect(errors).toEqual([ - new Error('BasePerson validateParams: age must be a positive integer') - ]); - }); - - test('validateParams with empty parameters', () => { - const params = {}; - - const errors = BasePerson.validateParams(params); - expect(errors.length).toBe(0); - }); - - test('validateParams with valid optional parameters', () => { - const params = { - _uuid: uuidV4(), - age: 25, - gender: 'female', - drivingLicenseOwnership: 'no', - transitPassOwnership: 'no', - carsharingMember: 'yes', - carsharingUser: 'yes', - bikesharingMember: 'no', - bikesharingUser: 'yes', - ridesharingMember: 'no', - ridesharingUser: 'no', - occupation: 'other', - jobCategory: 'Technology', - isOnTheRoadWorker: true, - isJobTelecommuteCompatible: false, - educationalAttainment: 'none', - nickname: 'John Doe', - contactPhoneNumber: '123-456-7890', - contactEmail: 'john.doe@example.com', - }; - - const errors = BasePerson.validateParams(params); - expect(errors.length).toBe(0); - }); - - it('should unserialize object', () => { - const instance = BasePerson.unserialize(personAttributes); - expect(instance).toBeInstanceOf(BasePerson); - expect(instance.age).toEqual(personAttributes.age); - }); - -}); diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BasePlace.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BasePlace.test.ts deleted file mode 100644 index cc0d0b53d..000000000 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BasePlace.test.ts +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -import { v4 as uuidV4 } from 'uuid'; -import { BasePlace, BasePlaceAttributes } from '../BasePlace'; -import { GeocodingPrecisionCategory } from '../attributeTypes/PlaceAttributes'; -import { BaseAddress, BaseAddressAttributes } from '../BaseAddress'; - -const validUUID = uuidV4(); -const validUUID2 = uuidV4(); - -const baseAddressAttributes: BaseAddressAttributes = { - _uuid: validUUID, - civicNumber: 123, - civicNumberSuffix: 'A', - unitNumber: 456, - streetName: 'Main St', - streetNameHomogenized: 'main street', - streetNameId: 'street123', - streetNameInternalId: 'internalStreet123', - municipalityName: 'Sample City', - municipalityCode: 'sampleCode', - postalMunicipalityName: 'Sample Postal City', - region: 'Sample State', - country: 'Sample Country', - postalCode: '12345', - addressId: 'address123', - internalId: 'internal123', -}; - -const geojson = { - type: 'Feature', - id: 112223, - geometry: { - type: 'Point', - coordinates: [45.5, -89.0033423], - }, - properties: { - foo: 'boo', - bar: 'far' - }, -} as GeoJSON.Feature; - -const basePlaceAttributes: BasePlaceAttributes = { - _uuid: validUUID2, - geography: geojson, - name: 'Sample Place', - shortname: 'Sample', - osmId: 'n1234', - landRoleId: 'land123', - postalId: 'postal123', - buildingId: 'building123', - internalId: 'internal123', - geocodingPrecisionCategory: 'high' as GeocodingPrecisionCategory, - geocodingPrecisionMeters: 100, - geocodingQueryString: 'Sample query', - lastAction: 'preGeocoded', - deviceUsed: 'tablet', - zoom: 14, -}; - -const baseAddress = new BaseAddress(baseAddressAttributes); - -describe('BasePlace', () => { - - it('should create a new BasePlace instance', () => { - const place = new BasePlace({ ...basePlaceAttributes, address: baseAddress }); - expect(place).toBeInstanceOf(BasePlace); - expect(place._uuid).toEqual(validUUID2); - expect(place.name).toEqual('Sample Place'); - expect(place.shortname).toEqual('Sample'); - expect(place.address).toEqual(baseAddress); - expect(place.osmId).toEqual('n1234'); - expect(place.landRoleId).toEqual('land123'); - expect(place.postalId).toEqual('postal123'); - expect(place.buildingId).toEqual('building123'); - expect(place.internalId).toEqual('internal123'); - expect(place.geocodingPrecisionCategory).toEqual('high'); - expect(place.geocodingPrecisionMeters).toEqual(100); - expect(place.geocodingQueryString).toEqual('Sample query'); - expect(place.lastAction).toEqual('preGeocoded'); - expect(place.deviceUsed).toEqual('tablet'); - expect(place.zoom).toEqual(14); - expect(place.geography?.geometry.coordinates).toEqual([45.5, -89.0033423]); - expect(place.geography?.geometry.type).toEqual('Point'); - expect(place.geography?.type).toEqual('Feature'); - expect(place.geography?.id).toEqual(112223); - expect(place.geography?.properties?.bar).toEqual('far'); - expect(place.geography?.properties?.foo).toEqual('boo'); - }); - - it('should create a new BasePlace instance with only _uuid and name', () => { - const minimalAttributes: BasePlaceAttributes = { - geography: undefined, - _uuid: validUUID2, - name: 'Sample Place', - }; - - const place = new BasePlace(minimalAttributes); - expect(place).toBeInstanceOf(BasePlace); - expect(place._uuid).toEqual(validUUID2); - expect(place.name).toEqual('Sample Place'); - expect(place.shortname).toBeUndefined(); - expect(place.address).toBeUndefined(); - expect(place.osmId).toBeUndefined(); - expect(place.landRoleId).toBeUndefined(); - expect(place.postalId).toBeUndefined(); - expect(place.buildingId).toBeUndefined(); - expect(place.internalId).toBeUndefined(); - expect(place.geocodingPrecisionCategory).toBeUndefined(); - expect(place.geocodingPrecisionMeters).toBeUndefined(); - expect(place.geocodingQueryString).toBeUndefined(); - expect(place.lastAction).toBeUndefined(); - expect(place.deviceUsed).toBeUndefined(); - expect(place.zoom).toBeUndefined(); - }); - - it('should validate a BasePlace instance', () => { - const place = new BasePlace(basePlaceAttributes); - expect(place.isValid()).toBeUndefined(); - const validationResult = place.validate(); - expect(validationResult).toBe(true); - expect(place.isValid()).toBe(true); - }); -}); - -describe('validateParams', () => { - it('should validate params with valid GeoJSON', () => { - const validParams = { - _uuid: uuidV4(), - name: 'Sample Place', - shortname: 'SP', - geography: { - type: 'Feature', - geometry: { - type: 'Point', - coordinates: [1, 2] - }, - properties: { - name: 'Sample Place', - address: { - civicNumber: 123, - streetName: 'Main St', - municipalityName: 'City', - region: 'State', - country: 'Country', - postalCode: '12345' - } - } - }, - osmId: 'n1234', - landRoleId: 'lr123', - postalId: 'p123', - buildingId: 'b123', - internalId: 'i123', - geocodingPrecisionCategory: 'precise', - geocodingPrecisionMeters: 10, - geocodingQueryString: 'Main St, City', - lastAction: 'findPlace', - deviceUsed: 'mobile', - zoom: 20, - }; - - const errors = BasePlace.validateParams(validParams); - expect(errors).toEqual([]); - }); - - it('should validate params without geography', () => { - const validParams = { - _uuid: uuidV4(), - name: 'Sample Place', - shortname: 'SP', - osmId: 'n1234', - landRoleId: 'lr123', - postalId: 'p123', - buildingId: 'b123', - internalId: 'i123', - geocodingPrecisionCategory: 'precise', - geocodingPrecisionMeters: 10, - geocodingQueryString: 'Main St, City', - lastAction: 'markerDragged', - deviceUsed: 'desktop', - zoom: 18, - }; - - const errors = BasePlace.validateParams(validParams); - expect(errors).toEqual([]); - }); - - it('should return errors for invalid GeoJSON', () => { - const invalidParams = { - _uuid: uuidV4(), - name: 'Sample Place', - shortname: 'SP', - geography: { - type: 'Feature', - geometry: { - type: 'Polygon', // Invalid, should be Point - coordinates: [ - [[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]] - ] - }, - properties: { - name: 'Sample Place', - address: { - civicNumber: 123, - streetName: 'Main St', - municipalityName: 'City', - region: 'State', - country: 'Country', - postalCode: '12345' - } - } - }, - osmId: 'n1234', - landRoleId: 'lr123', - postalId: 'p123', - buildingId: 'b123', - internalId: 'i123', - geocodingPrecisionCategory: 'precise', - geocodingPrecisionMeters: 10, - geocodingQueryString: 'Main St, City', - lastAction: 'mapClicked', - deviceUsed: 'other', - zoom: 10, - }; - - const errors = BasePlace.validateParams(invalidParams); - expect(errors.length).toBeGreaterThan(0); - expect(errors[0].message).toContain('BasePlace validateParams: geography should be a GeoJSON Point feature'); - }); - - test('validateParams with invalid parameters', () => { - const params = { - geography: 'InvalidGeoJSON', // Invalid type - shortname: 124, // Invalid type - name: {}, // Invalid type - landRoleId: 56789, // Invalid type - osmId: Infinity, // Invalid type - buildingId: {}, // Invalid type - postalId: false, // Invalid type - internalId: new Date(), - geocodingPrecisionCategory: 123, // Invalid type - geocodingPrecisionMeters: 'foo', // Invalid type - geocodingQueryString: 123, // Invalid type, - lastAction: new Date(), // Invalid type, - deviceUsed: {}, // Invalid type, - zoom: -1.23, // Invalid type, - }; - - const errors = BasePlace.validateParams(params); - expect(errors.length).toBeGreaterThan(0); - expect(errors).toEqual([ - new Error('BasePlace validateParams: geography should be a GeoJSON Point feature'), - new Error('BasePlace validateParams: name should be a string'), - new Error('BasePlace validateParams: shortname should be a string'), - new Error('BasePlace validateParams: osmId should be a string'), - new Error('BasePlace validateParams: landRoleId should be a string'), - new Error('BasePlace validateParams: postalId should be a string'), - new Error('BasePlace validateParams: buildingId should be a string'), - new Error('BasePlace validateParams: internalId should be a string'), - new Error('BasePlace validateParams: geocodingPrecisionCategory should be a string'), - new Error('BasePlace validateParams: geocodingPrecisionMeters should be a number'), - new Error('BasePlace validateParams: geocodingQueryString should be a string'), - new Error('BasePlace validateParams: lastAction should be a string'), - new Error('BasePlace validateParams: deviceUsed should be a string'), - new Error('BasePlace validateParams: zoom should be a positive number'), - ]); - }); - - test('validateParams with empty parameters', () => { - const params = {}; - const errors = BasePlace.validateParams(params); - expect(errors.length).toBe(0); - }); - - it('should unserialize object', () => { - const instance = BasePlace.unserialize({ ...basePlaceAttributes, address: new BaseAddress(baseAddressAttributes) }); - expect(instance).toBeInstanceOf(BasePlace); - expect(instance.landRoleId).toEqual(basePlaceAttributes.landRoleId); - expect(instance.address).toBeInstanceOf(BaseAddress); - expect(instance.address?.civicNumber).toEqual(baseAddressAttributes.civicNumber); - }); - -}); diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BaseSegment.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BaseSegment.test.ts deleted file mode 100644 index 7e3bb405a..000000000 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BaseSegment.test.ts +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -import { v4 as uuidV4 } from 'uuid'; -import { BaseSegment, BaseSegmentAttributes, ExtendedSegmentAttributes } from '../BaseSegment'; -import { BaseVehicle, BaseVehicleAttributes } from '../BaseVehicle'; -import * as VAttr from '../attributeTypes/VehicleAttributes'; -import * as SAttr from '../attributeTypes/SegmentAttributes'; - -const validUUID = uuidV4(); - -describe('BaseSegment', () => { - - const baseSegmentAttributes: BaseSegmentAttributes = { - _uuid: validUUID, - modeCategory: 'car' as SAttr.ModeCategory, - mode: 'carDriver' as SAttr.Mode, - }; - - it('should create a new BaseSegment instance', () => { - const segment = new BaseSegment(baseSegmentAttributes); - expect(segment).toBeInstanceOf(BaseSegment); - expect(segment._uuid).toEqual(validUUID); - expect(segment.modeCategory).toEqual('car'); - expect(segment.mode).toEqual('carDriver'); - }); - - it('should create a new BaseSegment instance with minimal attributes', () => { - const minimalAttributes: BaseSegmentAttributes = { - _uuid: validUUID, - }; - - const segment = new BaseSegment(minimalAttributes); - expect(segment).toBeInstanceOf(BaseSegment); - expect(segment._uuid).toEqual(validUUID); - expect(segment.modeCategory).toBeUndefined(); - expect(segment.mode).toBeUndefined(); - }); - - it('should validate a BaseSegment instance', () => { - const segment = new BaseSegment(baseSegmentAttributes); - expect(segment.isValid()).toBeUndefined(); - const validationResult = segment.validate(); - expect(validationResult).toBe(true); - expect(segment.isValid()).toBe(true); - }); - - it('should accept extended attributes', () => { - const extendedAttributes: ExtendedSegmentAttributes = { - ...baseSegmentAttributes, - customAttribute: 'Custom Value', - }; - - const segment = new BaseSegment(extendedAttributes); - expect(segment).toBeInstanceOf(BaseSegment); - }); - - it('should return an empty array for valid parameters', () => { - const params = { - modeCategory: 'walk', - mode: 'walk', - baseVehicle: new BaseVehicle({}), - }; - - const result = BaseSegment.validateParams(params); - - expect(result).toEqual([]); - }); - - it('should accept empty params', () => { - const params = {}; - - const result = BaseSegment.validateParams(params); - - expect(result).toHaveLength(0); - }); - - it('should return an array of errors for invalid modeCategory', () => { - const params = { - modeCategory: 42, // Invalid type - mode: new Date(), // Invalid type - }; - - const result = BaseSegment.validateParams(params); - - expect(result).toHaveLength(2); - expect(result[0]).toBeInstanceOf(Error); - expect(result).toEqual([ - new Error('BaseSegment validateParams: modeCategory should be a string'), - new Error('BaseSegment validateParams: mode should be a string') - ]); - }); - - it('should unserialize object', () => { - const instance = BaseSegment.unserialize(baseSegmentAttributes); - expect(instance).toBeInstanceOf(BaseSegment); - expect(instance.mode).toEqual(baseSegmentAttributes.mode); - }); -}); diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BaseTrip.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BaseTrip.test.ts deleted file mode 100644 index b7d28b112..000000000 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BaseTrip.test.ts +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -import { v4 as uuidV4 } from 'uuid'; -import { BaseTrip, BaseTripAttributes, ExtendedTripAttributes } from '../BaseTrip'; -import { BaseVisitedPlace } from '../BaseVisitedPlace'; -import { BasePlace, BasePlaceAttributes } from '../BasePlace'; -import { BaseSegment, BaseSegmentAttributes } from '../BaseSegment'; -import { Weight } from '../Weight'; -import { WeightMethod } from '../WeightMethod'; - -const validUUID = uuidV4(); - -describe('BaseTrip', () => { - - const weightMethodAttributes = { - _uuid: uuidV4(), - shortname: 'sample-shortname2', - name: 'Sample Weight Method2', - description: 'Sample weight method description2', - }; - - const baseTripAttributes: BaseTripAttributes = { - _uuid: validUUID, - _weights: [{ weight: 34.444, method: new WeightMethod(weightMethodAttributes) }], - }; - - it('should create a new BaseTrip instance', () => { - const trip = new BaseTrip(baseTripAttributes); - expect(trip).toBeInstanceOf(BaseTrip); - expect(trip._uuid).toEqual(validUUID); - }); - - it('should create a new BaseTrip instance with minimal attributes', () => { - const minimalAttributes: BaseTripAttributes = { - _uuid: validUUID, - }; - - const trip = new BaseTrip(minimalAttributes); - expect(trip).toBeInstanceOf(BaseTrip); - expect(trip._uuid).toEqual(validUUID); - }); - - it('should validate a BaseTrip instance', () => { - const trip = new BaseTrip(baseTripAttributes); - expect(trip.isValid()).toBeUndefined(); - const validationResult = trip.validate(); - expect(validationResult).toBe(true); - expect(trip.isValid()).toBe(true); - }); - - it('should accept extended attributes', () => { - const extendedAttributes: ExtendedTripAttributes = { - ...baseTripAttributes, - customAttribute: 'Custom Value', - }; - - const trip = new BaseTrip(extendedAttributes); - expect(trip).toBeInstanceOf(BaseTrip); - }); - - it('should set weight and method correctly', () => { - const trip = new BaseTrip(baseTripAttributes); - const weight: Weight = trip._weights?.[0] as Weight; - expect(weight.weight).toBe(34.444); - expect(weight.method).toBeInstanceOf(WeightMethod); - expect(weight.method?.shortname).toEqual('sample-shortname2'); - expect(weight.method?.name).toEqual('Sample Weight Method2'); - expect(weight.method?.description).toEqual('Sample weight method description2'); - }); - - it('should return an empty array for valid parameters', () => { - const params = { - _uuid: uuidV4(), - - baseOrigin: new BaseVisitedPlace({ basePlace: new BasePlace({} as BasePlaceAttributes) }), - baseDestination: new BaseVisitedPlace({ basePlace: new BasePlace({} as BasePlaceAttributes) }), - baseSegments: [ - new BaseSegment({} as BaseSegmentAttributes), - new BaseSegment({} as BaseSegmentAttributes), - ], - }; - - const errors = BaseTrip.validateParams(params); - expect(errors).toEqual([]); - }); - - it('should return errors for invalid parameters', () => { - const params = { - _uuid: 'invalid-uuid', - }; - - const errors = BaseTrip.validateParams(params); - expect(errors).toHaveLength(1); // Two errors expected - expect(errors).toEqual([ - new Error('Uuidable validateParams: _uuid should be a valid uuid'), - ]); - }); - - it('should accept empty params', () => { - const params = {}; - const errors = BaseTrip.validateParams(params); - expect(errors).toHaveLength(0); - }); - - it('should unserialize object', () => { - const instance = BaseTrip.unserialize(baseTripAttributes); - expect(instance).toBeInstanceOf(BaseTrip); - expect(instance._uuid).toEqual(baseTripAttributes._uuid); - }); -}); diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BaseTripChain.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BaseTripChain.test.ts deleted file mode 100644 index 54ecf2985..000000000 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BaseTripChain.test.ts +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -import { v4 as uuidV4 } from 'uuid'; -import { BaseTrip, BaseTripAttributes } from '../BaseTrip'; -import { BaseTripChain, BaseTripChainAttributes, ExtendedTripChainAttributes } from '../BaseTripChain'; -import { Weight } from '../Weight'; -import { WeightMethod } from '../WeightMethod'; -import * as TCAttr from '../attributeTypes/TripChainAttributes'; -import * as VPAttr from '../attributeTypes/VisitedPlaceAttributes'; - -const validUUID = uuidV4(); - -describe('BaseTripChain', () => { - const baseTripAttributes: BaseTripAttributes = { - _uuid: uuidV4() - }; - - const weightMethodAttributes = { - _uuid: uuidV4(), - shortname: 'sample-shortname3', - name: 'Sample Weight Method3', - description: 'Sample weight method description3', - }; - - const baseTripChainAttributes: BaseTripChainAttributes = { - _uuid: validUUID, - _weights: [{ weight: 0.0, method: new WeightMethod(weightMethodAttributes) }], - isMultiloop: false, - isConstrained: true, - category: 'simple' as TCAttr.TripChainCategory, - mainActivityCategory: 'work' as VPAttr.ActivityCategory, - mainActivity: 'workOther' as VPAttr.Activity, - }; - - it('should create a new BaseTripChain instance', () => { - const tripChain = new BaseTripChain(baseTripChainAttributes); - expect(tripChain).toBeInstanceOf(BaseTripChain); - expect(tripChain._uuid).toEqual(validUUID); - expect(tripChain.isMultiloop).toEqual(false); - expect(tripChain.isConstrained).toEqual(true); - expect(tripChain.category).toEqual('simple'); - expect(tripChain.mainActivityCategory).toEqual('work'); - expect(tripChain.mainActivity).toEqual('workOther'); - }); - - it('should create a new BaseTripChain instance with minimal attributes', () => { - const minimalAttributes: BaseTripChainAttributes = { - _uuid: validUUID, - isMultiloop: true, - isConstrained: false, - category: 'complex' as TCAttr.TripChainCategory, - }; - - const tripChain = new BaseTripChain(minimalAttributes); - expect(tripChain).toBeInstanceOf(BaseTripChain); - expect(tripChain._uuid).toEqual(validUUID); - expect(tripChain.isMultiloop).toEqual(true); - expect(tripChain.isConstrained).toEqual(false); - expect(tripChain.category).toEqual('complex'); - expect(tripChain.mainActivityCategory).toBeUndefined(); - expect(tripChain.mainActivity).toBeUndefined(); - expect(tripChain._weights).toBeUndefined(); - }); - - it('should validate a BaseTripChain instance', () => { - const tripChain = new BaseTripChain(baseTripChainAttributes); - expect(tripChain.isValid()).toBeUndefined(); - const validationResult = tripChain.validate(); - expect(validationResult).toBe(true); - expect(tripChain.isValid()).toBe(true); - }); - - it('should accept extended attributes', () => { - const extendedAttributes: ExtendedTripChainAttributes = { - ...baseTripChainAttributes, - customAttribute: 'Custom Value', - }; - - const tripChain = new BaseTripChain(extendedAttributes); - expect(tripChain).toBeInstanceOf(BaseTripChain); - }); - - it('should set weight and method correctly', () => { - const tripChain = new BaseTripChain(baseTripChainAttributes); - const weight: Weight = tripChain._weights?.[0] as Weight; - expect(weight.weight).toBe(0); - expect(weight.method).toBeInstanceOf(WeightMethod); - expect(weight.method?.shortname).toEqual('sample-shortname3'); - expect(weight.method?.name).toEqual('Sample Weight Method3'); - expect(weight.method?.description).toEqual('Sample weight method description3'); - }); - - it('should return an empty array for valid parameters', () => { - const params = { - _uuid: uuidV4(), - isMultiloop: true, - isConstrained: false, - category: 'category', - mainActivityCategory: 'work', - mainActivity: 'workUsual', - _weights: [{ weight: 1, method: new WeightMethod(weightMethodAttributes) }], - }; - - const errors = BaseTripChain.validateParams(params); - expect(errors).toEqual([]); - }); - - it('should return errors for invalid parameters', () => { - const params = { - _uuid: 'invalid uuid', - isMultiloop: new Date(), - isConstrained: Infinity, - category: 2355, - mainActivityCategory: -324.5, - mainActivity: {}, - _weights: [{ weight: 1, method: new WeightMethod(weightMethodAttributes) }], - }; - - const errors = BaseTripChain.validateParams(params); - expect(errors).toEqual([ - new Error('Uuidable validateParams: _uuid should be a valid uuid'), - new Error('BaseTripChain validateParams: isMultiloop should be a boolean'), - new Error('BaseTripChain validateParams: isConstrained should be a boolean'), - new Error('BaseTripChain validateParams: category should be a string'), - new Error('BaseTripChain validateParams: mainActivityCategory should be a string'), - new Error('BaseTripChain validateParams: mainActivity should be a string'), - ]); - }); - - it('should accept empty params', () => { - const params = {}; - const errors = BaseTripChain.validateParams(params); - expect(errors).toHaveLength(0); - }); - - it('should unserialize object', () => { - const instance = BaseTripChain.unserialize(baseTripChainAttributes); - expect(instance).toBeInstanceOf(BaseTripChain); - expect(instance.category).toEqual(baseTripChainAttributes.category); - }); - -}); diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BaseVehicle.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BaseVehicle.test.ts deleted file mode 100644 index ae0fa3da1..000000000 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BaseVehicle.test.ts +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -import { v4 as uuidV4 } from 'uuid'; -import { BaseVehicle, BaseVehicleAttributes, ExtendedVehicleAttributes } from '../BaseVehicle'; -import * as VAttr from '../attributeTypes/VehicleAttributes'; -import { Weight } from '../Weight'; -import { WeightMethod } from '../WeightMethod'; - -const validUUID = uuidV4(); - -describe('BaseVehicle', () => { - - const weightMethodAttributes = { - _uuid: uuidV4(), - shortname: 'sample-shortname4', - name: 'Sample Weight Method4', - description: 'Sample weight method description4', - }; - - const baseVehicleAttributes: BaseVehicleAttributes = { - _uuid: validUUID, - make: 'Toyota' as VAttr.Make, - model: 'Camry' as VAttr.Model, - licensePlateNumber: 'ABC123', - capacitySeated: 5, - capacityStanding: 0, - _weights: [{ weight: 0.0001, method: new WeightMethod(weightMethodAttributes) }], - }; - - it('should create a new BaseVehicle instance', () => { - const vehicle = new BaseVehicle(baseVehicleAttributes); - expect(vehicle).toBeInstanceOf(BaseVehicle); - expect(vehicle._uuid).toEqual(validUUID); - expect(vehicle.make).toEqual('Toyota'); - expect(vehicle.model).toEqual('Camry'); - expect(vehicle.licensePlateNumber).toEqual('ABC123'); - expect(vehicle.capacitySeated).toEqual(5); - expect(vehicle.capacityStanding).toEqual(0); - }); - - it('should create a new BaseVehicle instance with minimal attributes', () => { - const minimalAttributes: BaseVehicleAttributes = { - _uuid: validUUID, - make: 'Honda' as VAttr.Make, - model: 'Civic' as VAttr.Model, - }; - - const vehicle = new BaseVehicle(minimalAttributes); - expect(vehicle).toBeInstanceOf(BaseVehicle); - expect(vehicle._uuid).toEqual(validUUID); - expect(vehicle.make).toEqual('Honda'); - expect(vehicle.model).toEqual('Civic'); - expect(vehicle.licensePlateNumber).toBeUndefined(); - expect(vehicle.capacitySeated).toBeUndefined(); - expect(vehicle.capacityStanding).toBeUndefined(); - }); - - it('should validate a BaseVehicle instance', () => { - const vehicle = new BaseVehicle(baseVehicleAttributes); - expect(vehicle.isValid()).toBeUndefined(); - const validationResult = vehicle.validate(); - expect(validationResult).toBe(true); - expect(vehicle.isValid()).toBe(true); - }); - - it('should accept extended attributes', () => { - const extendedAttributes: ExtendedVehicleAttributes = { - ...baseVehicleAttributes, - customAttribute: 'Custom Value', - }; - - const vehicle = new BaseVehicle(extendedAttributes); - expect(vehicle).toBeInstanceOf(BaseVehicle); - }); - - it('should set weight and method correctly', () => { - const vehicle = new BaseVehicle(baseVehicleAttributes); - const weight: Weight = vehicle._weights?.[0] as Weight; - expect(weight.weight).toBe(.0001); - expect(weight.method).toBeInstanceOf(WeightMethod); - expect(weight.method?.shortname).toEqual('sample-shortname4'); - expect(weight.method?.name).toEqual('Sample Weight Method4'); - expect(weight.method?.description).toEqual('Sample weight method description4'); - }); - - it('should return an empty array for valid params', () => { - const params = { - _uuid: uuidV4(), - make: 'Toyota', - model: 'Camry', - licensePlateNumber: 'ABC123', - capacitySeated: 5, - capacityStanding: 0, - }; - - const errors = BaseVehicle.validateParams(params); - expect(errors).toEqual([]); - }); - - it('should return an error for invalid make', () => { - const params = { - _uuid: -34, - make: 123, - model: 456, - licensePlateNumber: {}, - capacitySeated: new Date(), - capacityStanding: -34.65, - }; - - const errors = BaseVehicle.validateParams(params); - expect(errors).toEqual([ - new Error('Uuidable validateParams: _uuid should be a valid uuid'), - new Error('BaseVehicle validateParams: make should be a string'), - new Error('BaseVehicle validateParams: model should be a string'), - new Error('BaseVehicle validateParams: licensePlateNumber should be a string'), - new Error('BaseVehicle validateParams: capacitySeated should be a positive integer'), - new Error('BaseVehicle validateParams: capacityStanding should be a positive integer'), - ]); - }); - - it('should accept empty params', () => { - const params = {}; - const errors = BaseVehicle.validateParams(params); - expect(errors).toHaveLength(0); - }); - - it('should unserialize object', () => { - const instance = BaseVehicle.unserialize(baseVehicleAttributes); - expect(instance).toBeInstanceOf(BaseVehicle); - expect(instance.make).toEqual(baseVehicleAttributes.make); - }); - -}); diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BaseVisitedPlace.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BaseVisitedPlace.test.ts deleted file mode 100644 index 335a8c937..000000000 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BaseVisitedPlace.test.ts +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -import { v4 as uuidV4 } from 'uuid'; -import { BaseVisitedPlace, BaseVisitedPlaceAttributes, ExtendedVisitedPlaceAttributes } from '../BaseVisitedPlace'; -import { BasePlace, BasePlaceAttributes } from '../BasePlace'; -import * as VPAttr from '../attributeTypes/VisitedPlaceAttributes'; -import { Weight } from '../Weight'; -import { WeightMethod } from '../WeightMethod'; - -const validUUID = uuidV4(); - -describe('BaseVisitedPlace', () => { - - const geojson = { - type: 'Feature', - id: 444, - geometry: { - type: 'Point', - coordinates: [23.5, -11.0033423], - }, - properties: { - foo: 'boo2', - bar: 'far2' - }, - } as GeoJSON.Feature; - - const weightMethodAttributes = { - _uuid: uuidV4(), - shortname: 'sample-shortname5', - name: 'Sample Weight Method5', - description: 'Sample weight method description5', - }; - - const basePlaceAttributes: BasePlaceAttributes = { - geography: geojson, - _uuid: validUUID, - }; - - const baseVisitedPlaceAttributes: BaseVisitedPlaceAttributes = { - _uuid: validUUID, - arrivalDate: '2023-10-05', - departureDate: '2023-10-06', - arrivalTime: 36000, // 10:00 AM in seconds since midnight - departureTime: 45000, // 12:30 PM in seconds since midnight - activityCategory: 'school' as VPAttr.ActivityCategory, - activity: 'schoolOther' as VPAttr.Activity, - _weights: [{ weight: 0.9911, method: new WeightMethod(weightMethodAttributes) }], - }; - - it('should create a new BaseVisitedPlace instance', () => { - const visitedPlace = new BaseVisitedPlace({ ...baseVisitedPlaceAttributes, basePlace: new BasePlace(basePlaceAttributes) }); - expect(visitedPlace).toBeInstanceOf(BaseVisitedPlace); - expect(visitedPlace._uuid).toEqual(validUUID); - expect(visitedPlace.basePlace).toBeInstanceOf(BasePlace); - expect(visitedPlace.basePlace?.geography?.type).toEqual('Feature'); - expect(visitedPlace.basePlace?.geography?.geometry?.type).toEqual('Point'); - expect(visitedPlace.basePlace?.geography?.geometry?.coordinates).toEqual([23.5, -11.0033423]); - expect(visitedPlace.basePlace?.geography?.properties).toEqual({ foo: 'boo2', bar: 'far2' }); - expect(visitedPlace.basePlace?.geography?.id).toEqual(444); - expect(visitedPlace.arrivalDate).toEqual('2023-10-05'); - expect(visitedPlace.departureDate).toEqual('2023-10-06'); - expect(visitedPlace.arrivalTime).toEqual(36000); - expect(visitedPlace.departureTime).toEqual(45000); - expect(visitedPlace.activityCategory).toEqual('school'); - expect(visitedPlace.activity).toEqual('schoolOther'); - }); - - it('should create a new BaseVisitedPlace instance with minimal attributes', () => { - const minimalBasePlaceAttributes: BasePlaceAttributes = { - geography: undefined, - _uuid: validUUID, - name: 'Minimal Test Place', - }; - - const minimalVisitedPlaceAttributes: BaseVisitedPlaceAttributes = { - _uuid: validUUID, - activityCategory: 'home' as VPAttr.ActivityCategory, - activity: 'home' as VPAttr.Activity, - }; - - const visitedPlace = new BaseVisitedPlace({ ...minimalVisitedPlaceAttributes, basePlace: new BasePlace(minimalBasePlaceAttributes) }); - expect(visitedPlace).toBeInstanceOf(BaseVisitedPlace); - expect(visitedPlace._uuid).toEqual(validUUID); - expect(visitedPlace.basePlace).toBeInstanceOf(BasePlace); - expect(visitedPlace.basePlace?.geography).toBeUndefined(); - expect(visitedPlace.basePlace?.name).toEqual('Minimal Test Place'); - expect(visitedPlace.arrivalDate).toBeUndefined(); - expect(visitedPlace.departureDate).toBeUndefined(); - expect(visitedPlace.arrivalTime).toBeUndefined(); - expect(visitedPlace.departureTime).toBeUndefined(); - expect(visitedPlace.activityCategory).toEqual('home'); - expect(visitedPlace.activity).toEqual('home'); - }); - - it('should validate a BaseVisitedPlace instance', () => { - const visitedPlace = new BaseVisitedPlace({ ...baseVisitedPlaceAttributes, basePlace: new BasePlace(basePlaceAttributes) }); - expect(visitedPlace.isValid()).toBeUndefined(); - const validationResult = visitedPlace.validate(); - expect(validationResult).toBe(true); - expect(visitedPlace.isValid()).toBe(true); - }); - - it('should accept extended attributes', () => { - const extendedVisitedPlaceAttributes: ExtendedVisitedPlaceAttributes = { - ...baseVisitedPlaceAttributes, - customAttribute: 'Custom Value', - }; - - const visitedPlace = new BaseVisitedPlace({ ...extendedVisitedPlaceAttributes, basePlace: new BasePlace(basePlaceAttributes) }); - expect(visitedPlace).toBeInstanceOf(BaseVisitedPlace); - }); - - it('should set weight and method correctly', () => { - const visitedPlace = new BaseVisitedPlace({ ...baseVisitedPlaceAttributes, basePlace: new BasePlace(basePlaceAttributes) }); - const weight: Weight = visitedPlace._weights?.[0] as Weight; - expect(weight.weight).toBe(.9911); - expect(weight.method).toBeInstanceOf(WeightMethod); - expect(weight.method?.shortname).toEqual('sample-shortname5'); - expect(weight.method?.name).toEqual('Sample Weight Method5'); - expect(weight.method?.description).toEqual('Sample weight method description5'); - }); - - it('should validate valid parameters', () => { - const validParams = { - _uuid: uuidV4(), - basePlace: new BasePlace({} as BasePlaceAttributes), - arrivalDate: '2023-01-15', - departureDate: '2023-01-16', - arrivalTime: 36000, // 10:00 AM in seconds since midnight - departureTime: 43200, // 12:00 PM in seconds since midnight - activityCategory: 'work' as VPAttr.ActivityCategory, - activity: 'workUsual' as VPAttr.Activity, - }; - const errors = BaseVisitedPlace.validateParams(validParams); - expect(errors).toEqual([]); - }); - - it('should validate valid parameters with minimal attributes', () => { - const validParams = {}; - const errors = BaseVisitedPlace.validateParams(validParams); - expect(errors).toEqual([]); - }); - - it('should validate valid parameters with additional attributes', () => { - const validParams = { - _uuid: uuidV4(), - basePlace: new BasePlace({} as BasePlaceAttributes), - arrivalDate: '2023-01-15', - departureDate: '2023-01-16', - arrivalTime: 36000, - departureTime: 43200, - activityCategory: 'other' as VPAttr.ActivityCategory, - activity: 'other' as VPAttr.Activity, - additionalAttribute: 'additionalValue', - }; - const errors = BaseVisitedPlace.validateParams(validParams); - expect(errors).toEqual([]); - }); - - it('should return errors for invalid parameters', () => { - const invalidParams = { - _uuid: 'foo', // Invalid UUID - //basePlace: new BasePlace({} as BasePlaceAttributes), - arrivalDate: {}, // Invalid date string - departureDate: [], // Invalid date string - arrivalTime: -1, // Negative arrival time - departureTime: '12:00 PM', // Invalid departure time string - activityCategory: 44, // Invalid activity category - activity: -123283764.34534, // Invalid activity - }; - - const errors = BaseVisitedPlace.validateParams(invalidParams); - expect(errors).toHaveLength(7); - expect(errors[0].message).toEqual('Uuidable validateParams: _uuid should be a valid uuid'); - expect(errors[1].message).toEqual('BaseVisitedPlace validateParams: arrivalDate should be a valid date string'); - expect(errors[2].message).toEqual('BaseVisitedPlace validateParams: departureDate should be a valid date string'); - expect(errors[3].message).toEqual('BaseVisitedPlace validateParams: arrivalTime should be a positive integer'); - expect(errors[4].message).toEqual('BaseVisitedPlace validateParams: departureTime should be a positive integer'); - expect(errors[5].message).toEqual('BaseVisitedPlace validateParams: activityCategory should be a string'); - expect(errors[6].message).toEqual('BaseVisitedPlace validateParams: activity should be a string'); - }); - - it('should use the create static factory method correctly', () => { - const invalidParams = { - _uuid: 'foo', // Invalid UUID - basePlace: new BasePlace({} as BasePlaceAttributes), - arrivalDate: '2023-01-15', // Invalid date string - departureDate: '2023-01-16', // Invalid date string - arrivalTime: -1, // Negative arrival time - departureTime: '12:00 PM', // Invalid departure time string - activityCategory: 44, // Invalid activity category - activity: -123283764.34534, // Invalid activity - }; - const validParams = { - _uuid: uuidV4(), // Invalid UUID - basePlace: new BasePlace({} as BasePlaceAttributes), - arrivalDate: '2023-01-15', - departureDate: '2023-01-16', - arrivalTime: 36000, - departureTime: 43200, - activityCategory: 'other' as VPAttr.ActivityCategory, - activity: 'other' as VPAttr.Activity, - additionalAttribute: 'additionalValue', - }; - - const invalidResult = BaseVisitedPlace.create(invalidParams); - const validResult = BaseVisitedPlace.create(validParams); - - expect(invalidResult).toBeInstanceOf(Array); - expect(invalidResult[0]).toBeInstanceOf(Error); - expect(validResult).toBeInstanceOf(BaseVisitedPlace); - }); - - it('should unserialize object', () => { - const instance = BaseVisitedPlace.unserialize({ ...baseVisitedPlaceAttributes, basePlace: BasePlace.unserialize(basePlaceAttributes) }); - expect(instance).toBeInstanceOf(BaseVisitedPlace); - expect(instance.activity).toEqual(baseVisitedPlaceAttributes.activity); - }); -}); diff --git a/packages/evolution-common/src/services/baseObjects/baseObjectsAndCompositionDoc.md b/packages/evolution-common/src/services/baseObjects/baseObjectsAndCompositionDoc.md index cadde739a..ff3a410cd 100644 --- a/packages/evolution-common/src/services/baseObjects/baseObjectsAndCompositionDoc.md +++ b/packages/evolution-common/src/services/baseObjects/baseObjectsAndCompositionDoc.md @@ -1,24 +1,22 @@ -# This documentation is deprecated. See README.md in this folder for the latest documentation. +# Documentation on Survey Objects (Household, Person, etc.) -# Documentation on base objects (Household, Person, etc.) -These objects will be implemented first in admin/validation and will be created live during validation using the response json data from the interview. However, later on, we willl migrate these to the questionnaire/client itself so we can have better typing and validations and make sure the data is more robust during data collection. -The BaseHousehold, BasePerson, etc. are deprecated but are kept for compatibility with older surveys which are still using them like od_mtl_2023 and od_qc_2023. +These objects are used throughout the Evolution platform for data collection, validation, and analysis. They provide strong typing, validation, and composition capabilities for survey data structures. ## Composition-based classes -Classes can have composed objects. For example, a Person can have multiple WorkPlace objects and SchoolPlace objects as a composed objects. The Place class can have an Address as a composed object. Composed objects are created automatically if included in the json params used to create the main parent object. The create function will validate params for the parent object and for the composed objects. Composed attributes are ignored in the first part of the parent class constructor, but are then send to each composed class for creation/validation. +Classes can have composed objects. For example, a Person can have multiple WorkPlace objects and SchoolPlace objects as composed objects. A Person can also have multiple Journey objects, and each Journey can have VisitedPlace, Trip, and TripChain objects. The Place class can have an Address as a composed object. Composed objects are created automatically if included in the json params used to create the main parent object. The create function will validate params for the parent object and for the composed objects. Composed attributes are ignored in the first part of the parent class constructor, but are then sent to each composed class for creation/validation. ## Named attributes -Each class have named attributes. These are the attributes that are part of the base attributes, and composed objects have them too. They are stored in the attributes object and are saved into a separate column in the database. +Each class has named attributes. These are the attributes that are part of the core object structure, and composed objects have them too. They are stored in the attributes object and are saved into separate columns in the database. ## Custom attributes -Each class can have custom attributes. These are attributes that are not part of the base attributes, and are not part of the composed objects (but composed objects themsleves can have custom attributes). They are stored in the customAttributes object. and are saved into a json field in the database. +Each class can have custom attributes. These are attributes that are not part of the named attributes, and are not part of the composed objects (but composed objects themselves can have custom attributes). They are stored in the customAttributes object and are saved into a json field in the database. ## Accessor attributes -Each named attribute have accessors (get/set). Custom attributes do not have accessors. It is up to the specific project/survey using them to define accessors when needed. +Each named attribute has accessors (get/set). Custom attributes do not have accessors. It is up to the specific project/survey using them to define accessors when needed. ## Validations/Audits Most classes have a static create method which is a factory that takes dirty parameters from json data (interview data or other input data), and validate the types of each attribute, and checks for missing required attributes in both the parent class and the composed objects. Eventually, these checks will be run during the interview itself, on the client. This is the first validation step. Then after the object is created with its validated params, the validate method can be run to validate logic and more-specific attributes validations, or validations related to more than one attribute. This is the second and final validation step. This second step is only run in the backend by admins or validators, or in batch validation tasks (we also called these "after-interview" validations: audits). Custom attributes are not validated when creating the object, but should be inside the specific survey using them. ## About naming dates and times -The first method of naming dates and times in the previous base objecst was to use departureDate, departureTime, arrivalDate and arrivalTime. The main issue with this is that for instance, for a visitedPlace the arrival would be at the begignning and the departure would be at the end, while for a trip it would be the opposite and this created ambiguities. The solution is now to use startDate, startTime, endDate, endTime, and thus the order is always the same for all timed objects like VisitedPlace, Trip, Junction, Segment, Journey, etc. +The current approach for naming dates and times uses a consistent pattern: startDate, startTime, endDate, endTime. This eliminates ambiguities that existed with previous naming conventions (departureDate/arrivalDate). The order is always the same for all timed objects like VisitedPlace, Trip, Junction, Segment, Journey, etc., where start represents the beginning of the activity/period and end represents the conclusion.