fix(server): include the previous year in memories for January 1, 2, 3 (#23832)

* Test memory creation in advance

Use year 2035 to make sure it's in the future of current time of a test run

* Use target year instead of current year when fetching assets during memory creation

This fixes an edge case of creating memories in advance when target year is
different from current year.
Example: job runs on 2025-12-31 (current year is 2025) and creates memories
to be shown on 2026-01-01 (target year is 2026). If using _current_ year in
calculation then range of years is capped at (2025 - 1 = 2024) thus excluding
2025-01-01 from created memories. With _target_ year it is (2026 - 1 = 2025),
so 2025-01-01 will be included in memories.

* Update sql queries
This commit is contained in:
Sergey Katsubo
2025-11-13 00:38:03 +03:00
committed by GitHub
parent 8969b8bdb2
commit 2c54b506b3
3 changed files with 50 additions and 9 deletions

View File

@@ -64,7 +64,7 @@ with
from
asset
),
date_part('year', current_date)::int - 1
$3
) as "year"
)
select
@@ -81,21 +81,21 @@ with
where
"asset_job_status"."previewAt" is not null
and (asset."localDateTime" at time zone 'UTC')::date = today.date
and "asset"."ownerId" = any ($3::uuid[])
and "asset"."visibility" = $4
and "asset"."ownerId" = any ($4::uuid[])
and "asset"."visibility" = $5
and exists (
select
from
"asset_file"
where
"assetId" = "asset"."id"
and "asset_file"."type" = $5
and "asset_file"."type" = $6
)
and "asset"."deletedAt" is null
order by
(asset."localDateTime" at time zone 'UTC')::date desc
limit
$6
$7
) as "a" on true
inner join "asset_exif" on "a"."id" = "asset_exif"."assetId"
)

View File

@@ -73,9 +73,10 @@ export interface TimeBucketItem {
count: number;
}
export interface MonthDay {
export interface YearMonthDay {
day: number;
month: number;
year: number;
}
interface AssetExploreFieldOptions {
@@ -259,8 +260,8 @@ export class AssetRepository {
return this.db.insertInto('asset').values(assets).returningAll().execute();
}
@GenerateSql({ params: [DummyValue.UUID, { day: 1, month: 1 }] })
getByDayOfYear(ownerIds: string[], { day, month }: MonthDay) {
@GenerateSql({ params: [DummyValue.UUID, { year: 2000, day: 1, month: 1 }] })
getByDayOfYear(ownerIds: string[], { year, day, month }: YearMonthDay) {
return this.db
.with('res', (qb) =>
qb
@@ -270,7 +271,7 @@ export class AssetRepository {
eb
.fn('generate_series', [
sql`(select date_part('year', min(("localDateTime" at time zone 'UTC')::date))::int from asset)`,
sql`date_part('year', current_date)::int - 1`,
sql`${year - 1}`,
])
.as('year'),
)

View File

@@ -153,6 +153,46 @@ describe(MemoryService.name, () => {
);
});
it('should create a memory from an asset - in advance', async () => {
const { sut, ctx } = setup();
const assetRepo = ctx.get(AssetRepository);
const memoryRepo = ctx.get(MemoryRepository);
const now = DateTime.fromObject({ year: 2035, month: 2, day: 26 }, { zone: 'utc' }) as DateTime<true>;
const { user } = await ctx.newUser();
const { asset } = await ctx.newAsset({ ownerId: user.id, localDateTime: now.minus({ years: 1 }).toISO() });
await Promise.all([
ctx.newExif({ assetId: asset.id, make: 'Canon' }),
ctx.newJobStatus({ assetId: asset.id }),
assetRepo.upsertFiles([
{ assetId: asset.id, type: AssetFileType.Preview, path: '/path/to/preview.jpg' },
{ assetId: asset.id, type: AssetFileType.Thumbnail, path: '/path/to/thumbnail.jpg' },
]),
]);
vi.setSystemTime(now.toJSDate());
await sut.onMemoriesCreate();
const memories = await memoryRepo.search(user.id, {});
expect(memories.length).toBe(1);
expect(memories[0]).toEqual(
expect.objectContaining({
id: expect.any(String),
createdAt: expect.any(Date),
memoryAt: expect.any(Date),
updatedAt: expect.any(Date),
deletedAt: null,
ownerId: user.id,
assets: expect.arrayContaining([expect.objectContaining({ id: asset.id })]),
isSaved: false,
showAt: now.startOf('day').toJSDate(),
hideAt: now.endOf('day').toJSDate(),
seenAt: null,
type: 'on_this_day',
data: { year: 2034 },
}),
);
});
it('should not generate a memory twice for the same day', async () => {
const { sut, ctx } = setup();
const assetRepo = ctx.get(AssetRepository);