Implemented Comments to prettify and code cleanup

pull/28593/head
Dell 2026-05-26 13:01:58 -04:00
parent 0a1a65a275
commit 07173efc50
7 changed files with 179 additions and 30 deletions

View File

@ -1386,6 +1386,7 @@
"leave": "Leave",
"leave_album": "Leave album",
"lens_model": "Lens model",
"less": "Less",
"let_others_respond": "Let others respond",
"level": "Level",
"library": "Library",
@ -2392,6 +2393,12 @@
"updated_at": "Updated",
"updated_password": "Updated password",
"upload": "Upload",
"upload_activity": "Upload activity",
"upload_activity_day_count": "{date}: {count, plural, one {# upload} other {# uploads}}",
"upload_activity_day_friday": "Fri",
"upload_activity_day_monday": "Mon",
"upload_activity_day_wednesday": "Wed",
"upload_activity_total_count": "{count, plural, one {# upload} other {# uploads}}",
"upload_concurrency": "Upload concurrency",
"upload_details": "Upload Details",
"upload_dialog_info": "Do you want to backup the selected Asset(s) to the server?",

View File

@ -14477,6 +14477,77 @@
"x-immich-state": "Stable"
}
},
"/users/me/stats/uploads": {
"get": {
"description": "Retrieve daily upload counts for the current user.",
"operationId": "getMyUploadStatistics",
"parameters": [
{
"name": "from",
"required": false,
"in": "query",
"description": "Start date in UTC",
"schema": {
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$",
"example": "2024-01-01",
"type": "string"
}
},
{
"name": "to",
"required": false,
"in": "query",
"description": "End date in UTC",
"schema": {
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$",
"example": "2024-01-01",
"type": "string"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UserUploadStatsResponseDto"
}
}
},
"description": ""
}
},
"security": [
{
"bearer": []
},
{
"cookie": []
},
{
"api_key": []
}
],
"summary": "Get current user upload statistics",
"tags": [
"Users"
],
"x-immich-history": [
{
"version": "v3",
"state": "Added"
},
{
"version": "v3",
"state": "Stable"
}
],
"x-immich-permission": "user.read",
"x-immich-state": "Stable"
}
},
"/users/profile-image": {
"delete": {
"description": "Delete the profile image of the current user.",
@ -25949,6 +26020,71 @@
},
"type": "object"
},
"UserUploadStatsResponseDto": {
"properties": {
"from": {
"description": "Start date in UTC",
"example": "2024-01-01",
"type": "string"
},
"series": {
"items": {
"properties": {
"count": {
"description": "Number of uploads",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"type": "integer"
},
"date": {
"description": "Date in UTC",
"example": "2024-01-01",
"type": "string"
}
},
"required": [
"date",
"count"
],
"type": "object"
},
"type": "array"
},
"summary": {
"properties": {
"totalCount": {
"description": "Total number of uploads",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"type": "integer"
}
},
"required": [
"totalCount"
],
"type": "object"
},
"to": {
"description": "End date in UTC",
"example": "2024-12-31",
"type": "string"
},
"userId": {
"description": "User ID",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$",
"type": "string"
}
},
"required": [
"from",
"series",
"summary",
"to",
"userId"
],
"type": "object"
},
"ValidateAccessTokenResponseDto": {
"properties": {
"authStatus": {

View File

@ -72,12 +72,9 @@ export class UserController {
@Endpoint({
summary: 'Get current user upload statistics',
description: 'Retrieve daily upload counts for the current user.',
history: new HistoryBuilder().added('v2'),
history: new HistoryBuilder().added('v3').stable('v3'),
})
getMyUploadStatistics(
@Auth() auth: AuthDto,
@Query() dto: UserUploadStatsDto,
): Promise<UserUploadStatsResponseDto> {
getMyUploadStatistics(@Auth() auth: AuthDto, @Query() dto: UserUploadStatsDto): Promise<UserUploadStatsResponseDto> {
return this.service.getUploadStatistics(auth, dto);
}

View File

@ -339,6 +339,22 @@ where
limit
$3
-- AssetRepository.getUploadStatistics
select
date_trunc('day', "createdAt" AT TIME ZONE 'UTC') AT TIME ZONE 'UTC' as "date",
count(*) as "count"
from
"asset"
where
"ownerId" = $1::uuid
and "createdAt" >= $2
and "createdAt" < $3
and "deletedAt" is null
group by
date_trunc('day', "createdAt" AT TIME ZONE 'UTC') AT TIME ZONE 'UTC'
order by
"date" asc
-- AssetRepository.getTimeBuckets
with
"asset" as (

View File

@ -97,16 +97,6 @@ export interface TimeBucketItem {
count: number;
}
export interface UploadStatisticsOptions {
from: Date;
to: Date;
}
export interface UploadStatisticsItem {
date: string;
count: number;
}
export interface YearMonthDay {
day: number;
month: number;
@ -717,12 +707,12 @@ export class AssetRepository {
}
@GenerateSql({ params: [DummyValue.UUID, { from: DummyValue.DATE, to: DummyValue.DATE }] })
getUploadStatistics(ownerId: string, options: UploadStatisticsOptions): Promise<UploadStatisticsItem[]> {
getUploadStatistics(ownerId: string, options: { from: Date; to: Date }) {
const uploadDate = sql<Date>`date_trunc('day', "createdAt" AT TIME ZONE 'UTC') AT TIME ZONE 'UTC'`;
return this.db
.selectFrom('asset')
.select(sql<string>`(${uploadDate} AT TIME ZONE 'UTC')::date::text`.as('date'))
.select(uploadDate.as('date'))
.select((eb) => eb.fn.countAll<number>().as('count'))
.where('ownerId', '=', asUuid(ownerId))
.where('createdAt', '>=', options.from)

View File

@ -54,15 +54,16 @@ export class UserService extends BaseService {
}
async getUploadStatistics(auth: AuthDto, dto: UserUploadStatsDto): Promise<UserUploadStatsResponseDto> {
const formatUploadDate = (date: Date) => date.toISOString().slice(0, 10);
const toDate = DateTime.fromJSDate(dto.to ?? new Date(), { zone: 'utc' }).startOf('day');
const fromDate = DateTime.fromJSDate(dto.from ?? toDate.minus({ weeks: 52 }).plus({ days: 1 }).toJSDate(), {
zone: 'utc',
}).startOf('day');
const fromDate = (
dto.from ? DateTime.fromJSDate(dto.from, { zone: 'utc' }) : toDate.minus({ weeks: 52 }).plus({ days: 1 })
).startOf('day');
const uploadCounts = await this.assetRepository.getUploadStatistics(auth.user.id, {
from: fromDate.toJSDate(),
to: toDate.plus({ days: 1 }).toJSDate(),
});
const countsByDate = new Map(uploadCounts.map((item) => [item.date, item.count]));
const countsByDate = new Map(uploadCounts.map((item) => [formatUploadDate(item.date), item.count]));
const series: UserUploadStatsResponseDto['series'] = [];
for (let date = fromDate; date <= toDate; date = date.plus({ days: 1 })) {

View File

@ -160,7 +160,7 @@
</TableBody>
</Table>
<Heading size="tiny" class="mt-8">Upload activity</Heading>
<Heading size="tiny" class="mt-8">{$t('upload_activity')}</Heading>
<div class="mt-4 w-full">
<div class="w-full">
<div class="mb-1 ml-7 flex justify-between text-xs text-gray-500 dark:text-gray-400">
@ -172,11 +172,11 @@
<div class="flex gap-1">
<div class="grid w-6 shrink-0 grid-rows-7 gap-px py-0.5 text-xs text-gray-500 sm:gap-1 dark:text-gray-400">
<div></div>
<div>Mon</div>
<div>{$t('upload_activity_day_monday')}</div>
<div></div>
<div>Wed</div>
<div>{$t('upload_activity_day_wednesday')}</div>
<div></div>
<div>Fri</div>
<div>{$t('upload_activity_day_friday')}</div>
<div></div>
</div>
@ -186,8 +186,8 @@
{#each week as day}
<div
class={`aspect-square w-full min-w-0 rounded-sm ${getUploadActivityLevel(day.count)}`}
title={`${day.date}: ${day.count.toLocaleString($locale)} uploads`}
aria-label={`${day.date}: ${day.count.toLocaleString($locale)} uploads`}
title={$t('upload_activity_day_count', { values: { date: day.date, count: day.count } })}
aria-label={$t('upload_activity_day_count', { values: { date: day.date, count: day.count } })}
></div>
{/each}
</div>
@ -196,14 +196,16 @@
</div>
<div class="mt-2 flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400">
<span>Less</span>
<span>{$t('less')}</span>
<span class="size-3 rounded-sm bg-gray-200 dark:bg-gray-700"></span>
<span class="size-3 rounded-sm bg-immich-primary/30"></span>
<span class="size-3 rounded-sm bg-immich-primary/50"></span>
<span class="size-3 rounded-sm bg-immich-primary/70"></span>
<span class="size-3 rounded-sm bg-immich-primary"></span>
<span>More</span>
<span class="ml-4">{uploadStats.summary.totalCount.toLocaleString($locale)} uploads</span>
<span>{$t('more')}</span>
<span class="ml-4"
>{$t('upload_activity_total_count', { values: { count: uploadStats.summary.totalCount } })}</span
>
</div>
</div>
</div>