Refine photo sync status checks
This commit is contained in:
parent
fa94b707de
commit
eea8f94eea
@ -2,7 +2,6 @@
|
||||
|
||||
import { Photo } from '@/photo';
|
||||
import AdminPhotosTable from '@/admin/AdminPhotosTable';
|
||||
import LoaderButton from '@/components/primitives/LoaderButton';
|
||||
import IconGrSync from '@/components/icons/IconGrSync';
|
||||
import Note from '@/components/Note';
|
||||
import AdminChildPage from '@/components/AdminChildPage';
|
||||
@ -12,6 +11,7 @@ import { syncPhotosAction } from '@/photo/actions';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import ResponsiveText from '@/components/primitives/ResponsiveText';
|
||||
import { LiaBroomSolid } from 'react-icons/lia';
|
||||
import ProgressButton from '@/components/primitives/ProgressButton';
|
||||
|
||||
const UPDATE_BATCH_SIZE_MAX = 4;
|
||||
|
||||
@ -37,7 +37,7 @@ export default function AdminPhotosSyncClient({
|
||||
breadcrumb={<ResponsiveText shortText="Needs Sync">
|
||||
Needs Sync ({photos.length})
|
||||
</ResponsiveText>}
|
||||
accessory={<LoaderButton
|
||||
accessory={<ProgressButton
|
||||
primary
|
||||
icon={<IconGrSync className="translate-y-[1px]" />}
|
||||
hideTextOnMobile={false}
|
||||
@ -68,7 +68,7 @@ export default function AdminPhotosSyncClient({
|
||||
{arePhotoIdsSyncing
|
||||
? 'Syncing'
|
||||
: 'Sync All'}
|
||||
</LoaderButton>}
|
||||
</ProgressButton>}
|
||||
>
|
||||
<div className="space-y-6">
|
||||
<Note
|
||||
|
||||
@ -93,11 +93,7 @@ export default function AdminPhotosTable({
|
||||
'text-dim',
|
||||
)}>
|
||||
{<>
|
||||
<PhotoDate {...{
|
||||
photo,
|
||||
dateType,
|
||||
timezone,
|
||||
}} />
|
||||
<PhotoDate {...{ photo, dateType, timezone }} />
|
||||
{photoHasSyncStatusText(photo) &&
|
||||
<Tooltip
|
||||
content={photoSyncStatusText(photo)}
|
||||
|
||||
@ -10,7 +10,7 @@ export default function Tooltip({
|
||||
}) {
|
||||
return (
|
||||
<TooltipPrimitive {...rest}>
|
||||
{children ?? <IoInformationCircleOutline size={19} />}
|
||||
{children ?? <IoInformationCircleOutline size={18} />}
|
||||
</TooltipPrimitive>
|
||||
);
|
||||
}
|
||||
|
||||
@ -414,7 +414,7 @@ export const syncPhotoAction = async (photoId: string) =>
|
||||
semanticDescription: aiSemanticDescription,
|
||||
} = await generateAiImageQueries(
|
||||
imageResizedBase64,
|
||||
AI_TEXT_AUTO_GENERATED_FIELDS,
|
||||
photo.syncStatus.missingAiTextFields,
|
||||
);
|
||||
|
||||
const formDataFromPhoto = convertPhotoToFormData(photo);
|
||||
|
||||
@ -599,7 +599,7 @@ const needsAiTextWhereClauses = (
|
||||
switch (field) {
|
||||
case 'title': return `(title <> '') IS NOT TRUE`;
|
||||
case 'caption': return `(caption <> '') IS NOT TRUE`;
|
||||
case 'tags': return `array_length(tags, 1) = 0`;
|
||||
case 'tags': return `(tags IS NULL OR array_length(tags, 1) = 0)`;
|
||||
case 'semantic': return `(semantic_description <> '') IS NOT TRUE`;
|
||||
}
|
||||
})
|
||||
|
||||
@ -97,7 +97,7 @@ export interface PhotoDb extends
|
||||
updatedAt: Date
|
||||
createdAt: Date
|
||||
takenAt: Date
|
||||
tags?: string[]
|
||||
tags: string[] | null
|
||||
}
|
||||
|
||||
// Parsed db response
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { MAKE_FUJIFILM } from '@/platforms/fujifilm';
|
||||
import { Photo, PhotoDb } from '.';
|
||||
import { AI_TEXT_AUTO_GENERATED_FIELDS } from '@/app/config';
|
||||
import { AiAutoGeneratedField } from './ai';
|
||||
|
||||
export interface PhotoSyncStatus {
|
||||
isOutdated: boolean;
|
||||
isMissingAiText: boolean;
|
||||
missingAiTextFields: AiAutoGeneratedField[];
|
||||
}
|
||||
|
||||
export const SYNC_QUERY_LIMIT = 1000;
|
||||
@ -19,32 +20,44 @@ const isPhotoOutdated = (photo: PhotoDb) =>
|
||||
photo.make === MAKE_FUJIFILM
|
||||
);
|
||||
|
||||
const doesPhotoNeedAiText = ({
|
||||
const getMissingAiTextFields = ({
|
||||
title,
|
||||
caption,
|
||||
tags = [],
|
||||
tags,
|
||||
semanticDescription,
|
||||
}: PhotoDb) =>
|
||||
(AI_TEXT_AUTO_GENERATED_FIELDS.includes('title') && !title) ||
|
||||
(AI_TEXT_AUTO_GENERATED_FIELDS.includes('caption') && !caption) ||
|
||||
(AI_TEXT_AUTO_GENERATED_FIELDS.includes('tags') && tags.length === 0) ||
|
||||
(AI_TEXT_AUTO_GENERATED_FIELDS.includes('semantic') && !semanticDescription);
|
||||
}: PhotoDb | Photo): AiAutoGeneratedField[] =>
|
||||
AI_TEXT_AUTO_GENERATED_FIELDS.reduce((fields, field) => {
|
||||
switch (field) {
|
||||
case 'title':
|
||||
return !title ? [...fields, 'title'] : fields;
|
||||
case 'caption':
|
||||
return !caption ? [...fields, 'caption'] : fields;
|
||||
case 'tags':
|
||||
return (tags ?? []).length === 0 ? [...fields, 'tags'] : fields;
|
||||
case 'semantic':
|
||||
return !semanticDescription ? [...fields, 'semantic'] : fields;
|
||||
}
|
||||
}, [] as AiAutoGeneratedField[]);
|
||||
|
||||
export const generatePhotoSyncStatus = (photo: PhotoDb): PhotoSyncStatus => ({
|
||||
isOutdated: isPhotoOutdated(photo),
|
||||
isMissingAiText: doesPhotoNeedAiText(photo),
|
||||
missingAiTextFields: getMissingAiTextFields(photo),
|
||||
});
|
||||
|
||||
export const photoHasSyncStatusText = (photo: Photo) =>
|
||||
photo.syncStatus.isOutdated || photo.syncStatus.isMissingAiText;
|
||||
photo.syncStatus.isOutdated ||
|
||||
photo.syncStatus.missingAiTextFields.length > 0;
|
||||
|
||||
export const photoSyncStatusText = (photo: Photo) => {
|
||||
const { isOutdated, isMissingAiText } = photo.syncStatus;
|
||||
const { isOutdated, missingAiTextFields } = photo.syncStatus;
|
||||
const text: string[] = [];
|
||||
if (isOutdated) {
|
||||
text.push('Outdated');
|
||||
} else if (isMissingAiText) {
|
||||
text.push('Missing AI Text');
|
||||
text.push('Outdated Data');
|
||||
} else if (missingAiTextFields.length > 0) {
|
||||
const missingFieldsText = missingAiTextFields
|
||||
.map(field => field.toLocaleUpperCase())
|
||||
.join(', ');
|
||||
text.push(`Missing AI Text (${missingFieldsText})`);
|
||||
}
|
||||
return text.join(' and ');
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user