diff --git a/actors.mjs b/actors.mjs index dfdf966..a6fe2f1 100644 --- a/actors.mjs +++ b/actors.mjs @@ -45,70 +45,6 @@ function getAverage(items) { return Math.round(items.reduce((acc, item) => acc + item, 0) / items.length) || null; } -function curateProfileEntry(profile) { - if (!profile.id) { - return null; - } - - const curatedProfileEntry = { - ...(profile.update !== false && { id: profile.update }), - actor_id: profile.id, - entity_id: profile.entity?.id || null, - date_of_birth: profile.dateOfBirth, - date_of_death: profile.dateOfDeath, - age: profile.age, - url: profile.url, - gender: profile.gender, - orientation: profile.orientation, - ethnicity: profile.ethnicity, - description: profile.description, - description_hash: profile.descriptionHash, - birth_city: profile.placeOfBirth?.city || null, - birth_state: profile.placeOfBirth?.state || null, - birth_country_alpha2: profile.placeOfBirth?.country || null, - residence_city: profile.placeOfResidence?.city || null, - residence_state: profile.placeOfResidence?.state || null, - residence_country_alpha2: profile.placeOfResidence?.country || null, - cup: profile.cup, - bust: profile.bust, - waist: profile.waist, - leg: profile.leg, - thigh: profile.thigh, - foot: profile.foot, - hip: profile.hip, - penis_length: profile.penisLength, - penis_girth: profile.penisGirth, - circumcised: profile.circumcised, - natural_boobs: profile.naturalBoobs, - boobs_volume: profile.boobsVolume, - boobs_implant: profile.boobsImplant, - boobs_placement: profile.boobsPlacement, - boobs_incision: profile.boobsIncision, - boobs_surgeon: profile.boobsSurgeon, - natural_butt: profile.naturalButt, - butt_volume: profile.buttVolume, - butt_implant: profile.buttImplant, - natural_lips: profile.naturalLips, - lips_volume: profile.lipsVolume, - natural_labia: profile.naturalLabia, - height: profile.height, - weight: profile.weight, - shoe_size: profile.shoeSize, - hair_color: profile.hairColor, - hair_type: profile.hairType, - eyes: profile.eyes, - has_tattoos: profile.hasTattoos, - has_piercings: profile.hasPiercings, - piercings: profile.piercings, - tattoos: profile.tattoos, - agency: profile.agency, - blood_type: profile.bloodType, - avatar_media_id: profile.avatarMediaId || null, - }; - - return curatedProfileEntry; -} - async function fetchProfiles(actorIdsOrNames, { knex }) { return knex('actors_profiles') .select(knex.raw('actors_profiles.*, actors.name, row_to_json(media) as avatar')) @@ -143,6 +79,7 @@ function mergeMainProfile(profile, mainProfile) { export async function interpolateProfiles(actorIdsOrNames, context, options = {}) { const profiles = await fetchProfiles(actorIdsOrNames, context); + const columns = await context.knex.table('actors').columnInfo().then((table) => Object.keys(table)); const profilesByActorId = profiles.reduce((acc, profile) => ({ ...acc, @@ -155,6 +92,16 @@ export async function interpolateProfiles(actorIdsOrNames, context, options = {} context.logger.info(`Interpolating ${profiles.length} profiles from ${Object.keys(profilesByActorId).length} actors`); const interpolatedProfiles = Object.entries(profilesByActorId).map(([actorId, actorProfiles]) => { + const mainProfile = actorProfiles.find((actorProfile) => actorProfile.entity_id === null); + + if (mainProfile && actorProfiles.length === 1) { + // no other profiles to interpolate + return { + ...Object.fromEntries(columns.map((key) => [key, mainProfile[key]])), + id: actorId, + }; + } + // group values from each profile const valuesByProperty = actorProfiles .filter((profile) => profile.entity_id !== null) // main profile is interpolated separately at the end @@ -180,47 +127,8 @@ export async function interpolateProfiles(actorIdsOrNames, context, options = {} }].filter((location) => Object.keys(location).length > 0), }), {}); - const mostFrequentValues = [ - 'gender', - 'orientation', - 'ethnicity', - 'cup', - 'bust', - 'waist', - 'hip', - 'leg', - 'thigh', - 'foot', - 'shoe_size', - 'penis_length', - 'penis_girth', - 'circumcised', - 'natural_boobs', - 'boobs_volume', - 'boobs_implant', - 'boobs_incision', - 'boobs_placement', - 'boobs_surgeon', - 'natural_butt', - 'butt_volume', - 'butt_implant', - 'natural_lips', - 'lips_volume', - 'natural_labia', - 'hair_color', - 'eyes', - 'has_tattoos', - 'has_piercings', - 'agency', - 'blood_type', - ].reduce((acc, property) => ({ - ...acc, - [property]: getMostFrequent(valuesByProperty[property], context), - }), {}); - const profile = { id: actorId, - ...mostFrequentValues, }; profile.height = getMostFrequent(valuesByProperty.height.filter((height) => height > 50 && height < 300), context); // remove unlikely values @@ -266,7 +174,12 @@ export async function interpolateProfiles(actorIdsOrNames, context, options = {} .sort((avatarA, avatarB) => avatarB.height - avatarA.height)[0]?.id || null; } - const mainProfile = actorProfiles.find((actorProfile) => actorProfile.entity_id === null); + columns.forEach((key) => { + // generic handling for remaining properties + if (Object.hasOwn(valuesByProperty, key) && !Object.hasOwn(profile, key)) { + profile[key] = getMostFrequent(valuesByProperty[key], context); + } + }); return mergeMainProfile(profile, mainProfile); }); @@ -274,9 +187,8 @@ export async function interpolateProfiles(actorIdsOrNames, context, options = {} const transaction = await context.knex.transaction(); // clear existing interpolated data - const emptyProfile = Object - .keys(context.omit(curateProfileEntry({ id: 1 }), ['id', 'actor_id', 'entity_id', 'url', 'description_hash'])) - .reduce((acc, key) => ({ ...acc, [key]: null }), {}); + const preservedKeys = ['id', 'name', 'slug', 'entity_id', 'entry_id']; + const emptyProfile = Object.fromEntries(columns.filter((key) => !preservedKeys.includes(key)).map((key) => [key, null])); await context.knex('actors') .modify((modifyBuilder) => { @@ -301,7 +213,10 @@ export async function interpolateProfiles(actorIdsOrNames, context, options = {} await Promise.all(queries) .then(transaction.commit) - .catch(transaction.rollback); + .catch(async (error) => { + context.logger.error(error); + return transaction.rollback(); + }); if (options.refreshView) { await context.knex.schema.refreshMaterializedView('actors_meta');