refactor e/gp pairs to pose enum

This commit is contained in:
Matt Dunn-Rankin 2020-05-23 12:47:06 -07:00
parent 772917fde6
commit 75a0fe2e8c
13 changed files with 210 additions and 182 deletions

View file

@ -128,8 +128,7 @@ function SubmitPetForm() {
name: petName,
species: species.id,
color: color.id,
emotion: "HAPPY", // TODO: Ask PetService
genderPresentation: "FEMININE", // TODO: Ask PetService
pose: "HAPPY_FEM", // TODO: Ask PetService
});
for (const item of items) {
params.append("objects[]", item.id);

View file

@ -32,7 +32,7 @@ function PosePicker({
}) {
const theme = useTheme();
const checkedInputRef = React.useRef();
const { loading, error, poses } = usePoses(outfitState);
const { loading, error, poseInfos } = usePoses(outfitState);
if (loading) {
return null;
@ -45,19 +45,15 @@ function PosePicker({
}
// If there's only one pose anyway, don't bother showing a picker!
const numAvailablePoses = Object.values(poses).filter((p) => p.isAvailable)
.length;
const numAvailablePoses = Object.values(poseInfos).filter(
(p) => p.isAvailable
).length;
if (numAvailablePoses <= 1) {
return null;
}
const onChange = (e) => {
const [emotion, genderPresentation] = e.target.value.split("-");
dispatchToOutfit({
type: "setPose",
emotion,
genderPresentation,
});
dispatchToOutfit({ type: "setPose", pose: e.target.value });
};
return (
@ -99,13 +95,13 @@ function PosePicker({
isOpen && "is-open"
)}
>
{outfitState.emotion === "HAPPY" && (
{getEmotion(outfitState.pose) === "HAPPY" && (
<EmojiImage src={twemojiSmile} alt="Choose a pose" />
)}
{outfitState.emotion === "SAD" && (
{getEmotion(outfitState.pose) === "SAD" && (
<EmojiImage src={twemojiCry} alt="Choose a pose" />
)}
{outfitState.emotion === "SICK" && (
{getEmotion(outfitState.pose) === "SICK" && (
<EmojiImage src={twemojiSick} alt="Choose a pose" />
)}
</Button>
@ -133,24 +129,30 @@ function PosePicker({
<EmojiImage src={twemojiMasc} alt="Masculine" />
</Cell>
<Cell as="td">
<PoseButton
pose={poses.happyMasc}
<PoseOption
poseInfo={poseInfos.happyMasc}
onChange={onChange}
inputRef={poses.happyMasc.isSelected && checkedInputRef}
inputRef={
poseInfos.happyMasc.isSelected && checkedInputRef
}
/>
</Cell>
<Cell as="td">
<PoseButton
pose={poses.sadMasc}
<PoseOption
poseInfo={poseInfos.sadMasc}
onChange={onChange}
inputRef={poses.sadMasc.isSelected && checkedInputRef}
inputRef={
poseInfos.sadMasc.isSelected && checkedInputRef
}
/>
</Cell>
<Cell as="td">
<PoseButton
pose={poses.sickMasc}
<PoseOption
poseInfo={poseInfos.sickMasc}
onChange={onChange}
inputRef={poses.sickMasc.isSelected && checkedInputRef}
inputRef={
poseInfos.sickMasc.isSelected && checkedInputRef
}
/>
</Cell>
</tr>
@ -159,24 +161,30 @@ function PosePicker({
<EmojiImage src={twemojiFem} alt="Feminine" />
</Cell>
<Cell as="td">
<PoseButton
pose={poses.happyFem}
<PoseOption
poseInfo={poseInfos.happyFem}
onChange={onChange}
inputRef={poses.happyFem.isSelected && checkedInputRef}
inputRef={
poseInfos.happyFem.isSelected && checkedInputRef
}
/>
</Cell>
<Cell as="td">
<PoseButton
pose={poses.sadFem}
<PoseOption
poseInfo={poseInfos.sadFem}
onChange={onChange}
inputRef={poses.sadFem.isSelected && checkedInputRef}
inputRef={
poseInfos.sadFem.isSelected && checkedInputRef
}
/>
</Cell>
<Cell as="td">
<PoseButton
pose={poses.sickFem}
<PoseOption
poseInfo={poseInfos.sickFem}
onChange={onChange}
inputRef={poses.sickFem.isSelected && checkedInputRef}
inputRef={
poseInfos.sickFem.isSelected && checkedInputRef
}
/>
</Cell>
</tr>
@ -213,14 +221,14 @@ const GENDER_PRESENTATION_STRINGS = {
FEMININE: "Feminine",
};
function PoseButton({ pose, onChange, inputRef }) {
function PoseOption({ poseInfo, onChange, inputRef }) {
const theme = useTheme();
const genderPresentationStr =
GENDER_PRESENTATION_STRINGS[pose.genderPresentation];
const emotionStr = EMOTION_STRINGS[pose.emotion];
GENDER_PRESENTATION_STRINGS[poseInfo.genderPresentation];
const emotionStr = EMOTION_STRINGS[poseInfo.emotion];
let label = `${emotionStr} and ${genderPresentationStr}`;
if (!pose.isAvailable) {
if (!poseInfo.isAvailable) {
label += ` (not modeled yet)`;
}
@ -239,9 +247,9 @@ function PoseButton({ pose, onChange, inputRef }) {
type="radio"
aria-label={label}
name="pose"
value={`${pose.emotion}-${pose.genderPresentation}`}
checked={pose.isSelected}
disabled={!pose.isAvailable}
value={poseInfo.pose}
checked={poseInfo.isSelected}
disabled={!poseInfo.isAvailable}
onChange={onChange}
ref={inputRef || null}
/>
@ -253,11 +261,11 @@ function PoseButton({ pose, onChange, inputRef }) {
width="50px"
height="50px"
title={
pose.isAvailable
poseInfo.isAvailable
? // A lil debug output, so that we can quickly identify glitched
// PetStates and manually mark them as glitched!
window.location.hostname.includes("localhost") &&
`#${pose.petStateId}`
`#${poseInfo.petStateId}`
: "Not modeled yet"
}
position="relative"
@ -298,18 +306,18 @@ function PoseButton({ pose, onChange, inputRef }) {
border-width: 3px;
}
`,
!pose.isAvailable && "not-available"
!poseInfo.isAvailable && "not-available"
)}
/>
{pose.isAvailable ? (
{poseInfo.isAvailable ? (
<Box
width="50px"
height="50px"
transform={
transformsByBodyId[pose.bodyId] || transformsByBodyId.default
transformsByBodyId[poseInfo.bodyId] || transformsByBodyId.default
}
>
<OutfitLayers visibleLayers={getVisibleLayers(pose, [])} />
<OutfitLayers visibleLayers={getVisibleLayers(poseInfo, [])} />
</Box>
) : (
<Flex align="center" justify="center">
@ -342,8 +350,7 @@ function usePoses(outfitState) {
id
petStateId
bodyId
genderPresentation
emotion
pose
approximateThumbnailUrl
...PetAppearanceForOutfitPreview
}
@ -354,28 +361,39 @@ function usePoses(outfitState) {
);
const petAppearances = data?.petAppearances || [];
const buildPose = (e, gp) => {
const appearance = petAppearances.find(
(pa) => pa.emotion === e && pa.genderPresentation === gp
);
const buildPoseInfo = (pose) => {
const appearance = petAppearances.find((pa) => pa.pose === pose);
return {
...appearance,
isAvailable: Boolean(appearance),
isSelected:
outfitState.emotion === e && outfitState.genderPresentation === gp,
isSelected: outfitState.pose === pose,
};
};
const poses = {
happyMasc: buildPose("HAPPY", "MASCULINE"),
sadMasc: buildPose("SAD", "MASCULINE"),
sickMasc: buildPose("SICK", "MASCULINE"),
happyFem: buildPose("HAPPY", "FEMININE"),
sadFem: buildPose("SAD", "FEMININE"),
sickFem: buildPose("SICK", "FEMININE"),
const poseInfos = {
happyMasc: buildPoseInfo("HAPPY_MASC"),
sadMasc: buildPoseInfo("SAD_MASC"),
sickMasc: buildPoseInfo("SICK_MASC"),
happyFem: buildPoseInfo("HAPPY_FEM"),
sadFem: buildPoseInfo("SAD_FEM"),
sickFem: buildPoseInfo("SICK_FEM"),
};
return { loading, error, poses };
return { loading, error, poseInfos };
}
function getEmotion(pose) {
if (["HAPPY_MASC", "HAPPY_FEM"].includes(pose)) {
return "HAPPY";
} else if (["SAD_MASC", "SAD_FEM"].includes(pose)) {
return "SAD";
} else if (["SICK_MASC", "SICK_FEM"].includes(pose)) {
return "SICK";
} else if (["UNCONVERTED", "UNKNOWN"].includes(pose)) {
return null;
} else {
throw new Error(`unrecognized pose ${JSON.stringify(pose)}`);
}
}
const transformsByBodyId = {

View file

@ -16,8 +16,8 @@ const cacheRedirects = {
// way, when you switch pet poses, Apollo knows it already has the
// appearance data and doesn't need to ask the server again!
petAppearance: (_, args, { getCacheKey }) => {
const { speciesId, colorId, emotion, genderPresentation } = args;
const id = `${speciesId}-${colorId}-${emotion}-${genderPresentation}`;
const { speciesId, colorId, pose } = args;
const id = `${speciesId}-${colorId}-${pose}`;
return getCacheKey({ __typename: "PetAppearance", id });
},
},

View file

@ -6,13 +6,7 @@ import { useQuery } from "@apollo/react-hooks";
* visibleLayers for rendering.
*/
export default function useOutfitAppearance(outfitState) {
const {
wornItemIds,
speciesId,
colorId,
emotion,
genderPresentation,
} = outfitState;
const { wornItemIds, speciesId, colorId, pose } = outfitState;
const { loading, error, data } = useQuery(
gql`
@ -20,15 +14,9 @@ export default function useOutfitAppearance(outfitState) {
$wornItemIds: [ID!]!
$speciesId: ID!
$colorId: ID!
$emotion: Emotion!
$genderPresentation: GenderPresentation!
) {
petAppearance(
speciesId: $speciesId
colorId: $colorId
emotion: $emotion
genderPresentation: $genderPresentation
$pose: Pose!
) {
petAppearance(speciesId: $speciesId, colorId: $colorId, pose: $pose) {
...PetAppearanceForOutfitPreview
}
@ -47,8 +35,7 @@ export default function useOutfitAppearance(outfitState) {
wornItemIds,
speciesId,
colorId,
emotion,
genderPresentation,
pose,
},
}
);

View file

@ -15,7 +15,7 @@ function useOutfitState() {
initialState
);
const { name, speciesId, colorId, emotion, genderPresentation } = state;
const { name, speciesId, colorId, pose } = state;
// It's more convenient to manage these as a Set in state, but most callers
// will find it more convenient to access them as arrays! e.g. for `.map()`
@ -84,8 +84,7 @@ function useOutfitState() {
allItemIds,
speciesId,
colorId,
emotion,
genderPresentation,
pose,
url,
};
@ -159,28 +158,21 @@ const outfitStateReducer = (apolloClient) => (baseState, action) => {
closetedItemIds.delete(itemId);
});
case "setPose":
return produce(baseState, (state) => {
const { emotion, genderPresentation } = action;
state.emotion = emotion;
state.genderPresentation = genderPresentation;
});
return { ...baseState, pose: action.pose };
case "reset":
return produce(baseState, (state) => {
const {
name,
speciesId,
colorId,
emotion,
genderPresentation,
pose,
wornItemIds,
closetedItemIds,
} = action;
state.name = name;
state.speciesId = speciesId ? String(speciesId) : baseState.speciesId;
state.colorId = colorId ? String(colorId) : baseState.colorId;
state.emotion = emotion || baseState.emotion;
state.genderPresentation =
genderPresentation || baseState.genderPresentation;
state.pose = pose || baseState.pose;
state.wornItemIds = wornItemIds
? new Set(wornItemIds.map(String))
: baseState.wornItemIds;
@ -199,8 +191,7 @@ function parseOutfitUrl() {
name: urlParams.get("name"),
speciesId: urlParams.get("species"),
colorId: urlParams.get("color"),
emotion: urlParams.get("emotion") || "HAPPY",
genderPresentation: urlParams.get("genderPresentation") || "FEMININE",
pose: urlParams.get("pose") || "HAPPY_FEM",
wornItemIds: new Set(urlParams.getAll("objects[]")),
closetedItemIds: new Set(urlParams.getAll("closet[]")),
};
@ -335,8 +326,7 @@ function buildOutfitUrl(state) {
name,
speciesId,
colorId,
emotion,
genderPresentation,
pose,
wornItemIds,
closetedItemIds,
} = state;
@ -345,8 +335,7 @@ function buildOutfitUrl(state) {
name: name || "",
species: speciesId,
color: colorId,
emotion,
genderPresentation,
pose,
});
for (const itemId of wornItemIds) {
params.append("objects[]", itemId);

View file

@ -1,25 +1,25 @@
import connectToDb from "./db";
import { getPose } from "./util";
import { getPoseFromPetState, normalizeRow } from "./util";
export default async function getValidPetPoses() {
const db = await connectToDb();
const numSpeciesPromise = getNumSpecies(db);
const numColorsPromise = getNumColors(db);
const poseTuplesPromise = getPoseTuples(db);
const distinctPetStatesPromise = getDistinctPetStates(db);
const [numSpecies, numColors, poseTuples] = await Promise.all([
const [numSpecies, numColors, distinctPetStates] = await Promise.all([
numSpeciesPromise,
numColorsPromise,
poseTuplesPromise,
distinctPetStatesPromise,
]);
const poseStrs = new Set();
for (const poseTuple of poseTuples) {
const { species_id, color_id, mood_id, female, unconverted } = poseTuple;
const pose = getPose(mood_id, female, unconverted);
const poseStr = `${species_id}-${color_id}-${pose}`;
for (const petState of distinctPetStates) {
const { speciesId, colorId } = petState;
const pose = getPoseFromPetState(petState);
const poseStr = `${speciesId}-${colorId}-${pose}`;
poseStrs.add(poseStr);
}
@ -77,11 +77,11 @@ async function getNumColors(db) {
return rows[0]["count(*)"];
}
async function getPoseTuples(db) {
async function getDistinctPetStates(db) {
const [rows, _] = await db.query(`
SELECT DISTINCT species_id, color_id, mood_id, female, unconverted
FROM pet_states
INNER JOIN pet_types ON pet_types.id = pet_states.pet_type_id
WHERE glitched IS false AND color_id >= 1`);
return rows;
return rows.map(normalizeRow);
}

View file

@ -3,6 +3,15 @@ import getValidPetPoses from "./getValidPetPoses";
describe("getValidPetPoses", () => {
it("gets them and writes them to a buffer", async () => {
const buffer = await getValidPetPoses();
expect(buffer.toString()).toMatchSnapshot();
expect(asBinaryString(buffer)).toMatchSnapshot();
});
});
function asBinaryString(buffer) {
let str = "";
for (let i = 0; i < buffer.length; i++) {
const byte = buffer.readUInt8(i);
str += byte.toString(2).padStart(8, "0") + "\n";
}
return str;
}

View file

@ -3,7 +3,12 @@ const { gql } = require("apollo-server");
const connectToDb = require("./db");
const buildLoaders = require("./loaders");
const neopets = require("./neopets");
const { capitalize, getEmotion, getGenderPresentation } = require("./util");
const {
capitalize,
getPoseFromPetState,
getEmotion,
getGenderPresentation,
} = require("./util");
const typeDefs = gql`
enum LayerImageSize {
@ -12,6 +17,20 @@ const typeDefs = gql`
SIZE_150
}
"""
The poses a PetAppearance can take!
"""
enum Pose {
HAPPY_MASC
SAD_MASC
SICK_MASC
HAPPY_FEM
SAD_FEM
SICK_FEM
UNCONVERTED
UNKNOWN # for when we have the data, but we don't know what it is
}
"""
A pet's gender presentation: masculine or feminine.
@ -50,8 +69,9 @@ const typeDefs = gql`
id: ID!
petStateId: ID!
bodyId: ID!
genderPresentation: GenderPresentation
emotion: Emotion
pose: Pose!
genderPresentation: GenderPresentation # deprecated
emotion: Emotion # deprecated
approximateThumbnailUrl: String!
layers: [AppearanceLayer!]!
}
@ -120,12 +140,7 @@ const typeDefs = gql`
offset: Int
limit: Int
): ItemSearchResult!
petAppearance(
speciesId: ID!
colorId: ID!
emotion: Emotion!
genderPresentation: GenderPresentation!
): PetAppearance
petAppearance(speciesId: ID!, colorId: ID!, pose: Pose!): PetAppearance
petAppearances(speciesId: ID!, colorId: ID!): [PetAppearance!]!
petOnNeopetsDotCom(petName: String!): Outfit
@ -177,15 +192,15 @@ const resolvers = {
PetAppearance: {
id: ({ petType, petState }) => {
const { speciesId, colorId } = petType;
const emotion = getEmotion(petState.moodId);
const genderPresentation = getGenderPresentation(petState.female);
return `${speciesId}-${colorId}-${emotion}-${genderPresentation}`;
const pose = getPoseFromPetState(petState);
return `${speciesId}-${colorId}-${pose}`;
},
petStateId: ({ petState }) => petState.id,
bodyId: ({ petType }) => petType.bodyId,
pose: ({ petState }) => getPoseFromPetState(petState),
genderPresentation: ({ petState }) =>
getGenderPresentation(petState.female),
emotion: ({ petState }) => getEmotion(petState.moodId),
getGenderPresentation(getPoseFromPetState(petState)),
emotion: ({ petState }) => getEmotion(getPoseFromPetState(petState)),
approximateThumbnailUrl: ({ petType, petState }) => {
return `http://pets.neopets.com/cp/${petType.basicImageHash}/${petState.moodId}/1.png`;
},
@ -309,7 +324,7 @@ const resolvers = {
},
petAppearance: async (
_,
{ speciesId, colorId, emotion, genderPresentation },
{ speciesId, colorId, pose },
{ petTypeLoader, petStateLoader }
) => {
const petType = await petTypeLoader.load({
@ -319,11 +334,7 @@ const resolvers = {
const petStates = await petStateLoader.load(petType.id);
// TODO: This could be optimized into the query condition 🤔
const petState = petStates.find(
(ps) =>
getEmotion(ps.moodId) === emotion &&
getGenderPresentation(ps.female) === genderPresentation
);
const petState = petStates.find((ps) => getPoseFromPetState(ps) === pose);
if (!petState) {
return null;
}

View file

@ -1,4 +1,5 @@
const DataLoader = require("dataloader");
const { normalizeRow } = require("./util");
const loadAllColors = (db) => async () => {
const [rows, _] = await db.execute(`SELECT * FROM colors WHERE prank = 0`);
@ -277,18 +278,6 @@ const buildZoneTranslationLoader = (db) =>
);
});
function normalizeRow(row) {
const normalizedRow = {};
for (let [key, value] of Object.entries(row)) {
key = key.replace(/_([a-z])/gi, (m) => m[1].toUpperCase());
if ((key === "id" || key.endsWith("Id")) && typeof value === "number") {
value = String(value);
}
normalizedRow[key] = value;
}
return normalizedRow;
}
function buildLoaders(db) {
return {
loadAllColors: loadAllColors(db),

View file

@ -6,12 +6,7 @@ describe("PetAppearance", () => {
const res = await query({
query: gql`
query {
petAppearance(
speciesId: "54"
colorId: "75"
emotion: HAPPY
genderPresentation: FEMININE
) {
petAppearance(speciesId: "54", colorId: "75", pose: HAPPY_FEM) {
layers {
id
imageUrl(size: SIZE_600)
@ -77,6 +72,7 @@ describe("PetAppearance", () => {
id
bodyId
petStateId
pose
genderPresentation
emotion
approximateThumbnailUrl

View file

@ -64,8 +64,8 @@ Object {
"approximateThumbnailUrl": "http://pets.neopets.com/cp/vghhzlgf/1/1.png",
"bodyId": "180",
"emotion": "HAPPY",
"genderPresentation": "FEMININE",
"id": "54-75-HAPPY-FEMININE",
"genderPresentation": "MASCULINE",
"id": "54-75-HAPPY_FEM",
"layers": Array [
Object {
"id": "5995",
@ -111,13 +111,14 @@ Object {
},
],
"petStateId": "17723",
"pose": "HAPPY_FEM",
},
Object {
"approximateThumbnailUrl": "http://pets.neopets.com/cp/vghhzlgf/1/1.png",
"bodyId": "180",
"emotion": "HAPPY",
"genderPresentation": "MASCULINE",
"id": "54-75-HAPPY-MASCULINE",
"id": "54-75-HAPPY_MASC",
"layers": Array [
Object {
"id": "5995",
@ -163,13 +164,14 @@ Object {
},
],
"petStateId": "17742",
"pose": "HAPPY_MASC",
},
Object {
"approximateThumbnailUrl": "http://pets.neopets.com/cp/vghhzlgf/4/1.png",
"bodyId": "180",
"emotion": "SICK",
"genderPresentation": "FEMININE",
"id": "54-75-SICK-FEMININE",
"genderPresentation": "MASCULINE",
"id": "54-75-SICK_FEM",
"layers": Array [
Object {
"id": "5995",
@ -215,13 +217,14 @@ Object {
},
],
"petStateId": "10014",
"pose": "SICK_FEM",
},
Object {
"approximateThumbnailUrl": "http://pets.neopets.com/cp/vghhzlgf/4/1.png",
"bodyId": "180",
"emotion": "SICK",
"genderPresentation": "MASCULINE",
"id": "54-75-SICK-MASCULINE",
"id": "54-75-SICK_MASC",
"layers": Array [
Object {
"id": "5995",
@ -267,13 +270,14 @@ Object {
},
],
"petStateId": "11089",
"pose": "SICK_MASC",
},
Object {
"approximateThumbnailUrl": "http://pets.neopets.com/cp/vghhzlgf/2/1.png",
"bodyId": "180",
"emotion": "SAD",
"genderPresentation": "FEMININE",
"id": "54-75-SAD-FEMININE",
"genderPresentation": "MASCULINE",
"id": "54-75-SAD_FEM",
"layers": Array [
Object {
"id": "5995",
@ -319,13 +323,14 @@ Object {
},
],
"petStateId": "5991",
"pose": "SAD_FEM",
},
Object {
"approximateThumbnailUrl": "http://pets.neopets.com/cp/vghhzlgf/2/1.png",
"bodyId": "180",
"emotion": "SAD",
"genderPresentation": "MASCULINE",
"id": "54-75-SAD-MASCULINE",
"id": "54-75-SAD_MASC",
"layers": Array [
Object {
"id": "5995",
@ -371,13 +376,14 @@ Object {
},
],
"petStateId": "436",
"pose": "SAD_MASC",
},
Object {
"approximateThumbnailUrl": "http://pets.neopets.com/cp/vghhzlgf/null/1.png",
"bodyId": "180",
"emotion": null,
"genderPresentation": null,
"id": "54-75-null-null",
"id": "54-75-UNKNOWN",
"layers": Array [
Object {
"id": "5995",
@ -430,13 +436,14 @@ Object {
},
],
"petStateId": "2",
"pose": "UNKNOWN",
},
Object {
"approximateThumbnailUrl": "http://pets.neopets.com/cp/vghhzlgf/null/1.png",
"bodyId": "180",
"emotion": null,
"genderPresentation": null,
"id": "54-75-null-null",
"id": "54-75-UNKNOWN",
"layers": Array [
Object {
"id": "5995",
@ -489,6 +496,7 @@ Object {
},
],
"petStateId": "4751",
"pose": "UNKNOWN",
},
],
}

View file

@ -2,55 +2,77 @@ function capitalize(str) {
return str[0].toUpperCase() + str.slice(1);
}
function getEmotion(moodId) {
if (String(moodId) === "1") {
function getEmotion(pose) {
if (["HAPPY_MASC", "HAPPY_FEM"].includes(pose)) {
return "HAPPY";
} else if (String(moodId) === "2") {
} else if (["SAD_MASC", "SAD_FEM"].includes(pose)) {
return "SAD";
} else if (String(moodId) === "4") {
} else if (["SICK_MASC", "SICK_FEM"].includes(pose)) {
return "SICK";
} else if (moodId === null) {
} else if (["UNCONVERTED", "UNKNOWN"].includes(pose)) {
return null;
} else {
throw new Error(`unrecognized moodId ${JSON.stringify(moodId)}`);
throw new Error(`unrecognized pose ${JSON.stringify(pose)}`);
}
}
function getGenderPresentation(modelPetWasFemale) {
if (String(modelPetWasFemale) === "1") {
return "FEMININE";
} else if (String(modelPetWasFemale) === "0") {
function getGenderPresentation(pose) {
if (["HAPPY_MASC", "SAD_MASC", "SICK_MASC"].includes(pose)) {
return "MASCULINE";
} else {
} else if (["HAPPY_FEM", "SAD_FEM", "SICK_FEM"].includes(pose)) {
return "MASCULINE";
} else if (["UNCONVERTED", "UNKNOWN"].includes(pose)) {
return null;
} else {
throw new Error(`unrecognized pose ${JSON.stringify(pose)}`);
}
}
function getPose(moodId, modelPetWasFemale, isUnconverted) {
if (isUnconverted) {
function getPoseFromPetState(petState) {
const { moodId, female, unconverted } = petState;
if (unconverted) {
return "UNCONVERTED";
} else if (moodId == null || modelPetWasFemale == null) {
} else if (moodId == null || female == null) {
return "UNKNOWN";
} else if (String(moodId) === "1" && String(modelPetWasFemale) === "0") {
} else if (String(moodId) === "1" && String(female) === "0") {
return "HAPPY_MASC";
} else if (String(moodId) === "1" && String(modelPetWasFemale) === "1") {
} else if (String(moodId) === "1" && String(female) === "1") {
return "HAPPY_FEM";
} else if (String(moodId) === "2" && String(modelPetWasFemale) === "0") {
} else if (String(moodId) === "2" && String(female) === "0") {
return "SAD_MASC";
} else if (String(moodId) === "2" && String(modelPetWasFemale) === "1") {
} else if (String(moodId) === "2" && String(female) === "1") {
return "SAD_FEM";
} else if (String(moodId) === "4" && String(modelPetWasFemale) === "0") {
} else if (String(moodId) === "4" && String(female) === "0") {
return "SICK_MASC";
} else if (String(moodId) === "4" && String(modelPetWasFemale) === "1") {
} else if (String(moodId) === "4" && String(female) === "1") {
return "SICK_FEM";
} else {
throw new Error(
`could not identify pose: ` +
`moodId=${moodId}, ` +
`modelPetWasFemale=${modelPetWasFemale}, ` +
`isUnconverted=${isUnconverted}`
`female=${female}, ` +
`unconverted=${unconverted}`
);
}
}
module.exports = { capitalize, getEmotion, getGenderPresentation, getPose };
function normalizeRow(row) {
const normalizedRow = {};
for (let [key, value] of Object.entries(row)) {
key = key.replace(/_([a-z])/gi, (m) => m[1].toUpperCase());
if ((key === "id" || key.endsWith("Id")) && typeof value === "number") {
value = String(value);
}
normalizedRow[key] = value;
}
return normalizedRow;
}
module.exports = {
capitalize,
getEmotion,
getGenderPresentation,
getPoseFromPetState,
normalizeRow,
};