Implemented Comments to prettify and code cleanup
parent
0a1a65a275
commit
07173efc50
|
|
@ -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?",
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 })) {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue