mirror of
https://github.com/immich-app/immich.git
synced 2025-07-07 18:24:10 -04:00
feat: show archived assets for a person
This commit is contained in:
parent
e8015dc7d7
commit
882d9bee04
14
mobile/openapi/lib/api/people_api.dart
generated
14
mobile/openapi/lib/api/people_api.dart
generated
@ -184,7 +184,9 @@ class PeopleApi {
|
|||||||
/// Parameters:
|
/// Parameters:
|
||||||
///
|
///
|
||||||
/// * [String] id (required):
|
/// * [String] id (required):
|
||||||
Future<Response> getPersonStatisticsWithHttpInfo(String id,) async {
|
///
|
||||||
|
/// * [bool] withArchived:
|
||||||
|
Future<Response> getPersonStatisticsWithHttpInfo(String id, { bool? withArchived, }) async {
|
||||||
// ignore: prefer_const_declarations
|
// ignore: prefer_const_declarations
|
||||||
final path = r'/people/{id}/statistics'
|
final path = r'/people/{id}/statistics'
|
||||||
.replaceAll('{id}', id);
|
.replaceAll('{id}', id);
|
||||||
@ -196,6 +198,10 @@ class PeopleApi {
|
|||||||
final headerParams = <String, String>{};
|
final headerParams = <String, String>{};
|
||||||
final formParams = <String, String>{};
|
final formParams = <String, String>{};
|
||||||
|
|
||||||
|
if (withArchived != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'withArchived', withArchived));
|
||||||
|
}
|
||||||
|
|
||||||
const contentTypes = <String>[];
|
const contentTypes = <String>[];
|
||||||
|
|
||||||
|
|
||||||
@ -213,8 +219,10 @@ class PeopleApi {
|
|||||||
/// Parameters:
|
/// Parameters:
|
||||||
///
|
///
|
||||||
/// * [String] id (required):
|
/// * [String] id (required):
|
||||||
Future<PersonStatisticsResponseDto?> getPersonStatistics(String id,) async {
|
///
|
||||||
final response = await getPersonStatisticsWithHttpInfo(id,);
|
/// * [bool] withArchived:
|
||||||
|
Future<PersonStatisticsResponseDto?> getPersonStatistics(String id, { bool? withArchived, }) async {
|
||||||
|
final response = await getPersonStatisticsWithHttpInfo(id, withArchived: withArchived, );
|
||||||
if (response.statusCode >= HttpStatus.badRequest) {
|
if (response.statusCode >= HttpStatus.badRequest) {
|
||||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||||
}
|
}
|
||||||
|
24
mobile/openapi/lib/model/people_update_item.dart
generated
24
mobile/openapi/lib/model/people_update_item.dart
generated
@ -18,6 +18,7 @@ class PeopleUpdateItem {
|
|||||||
required this.id,
|
required this.id,
|
||||||
this.isHidden,
|
this.isHidden,
|
||||||
this.name,
|
this.name,
|
||||||
|
this.withArchived,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Person date of birth. Note: the mobile app cannot currently set the birth date to null.
|
/// Person date of birth. Note: the mobile app cannot currently set the birth date to null.
|
||||||
@ -53,13 +54,23 @@ class PeopleUpdateItem {
|
|||||||
///
|
///
|
||||||
String? name;
|
String? name;
|
||||||
|
|
||||||
|
/// This property was added in v1.118.0
|
||||||
|
///
|
||||||
|
/// Please note: This property should have been non-nullable! Since the specification file
|
||||||
|
/// does not include a default value (using the "default:" property), however, the generated
|
||||||
|
/// source code must fall back to having a nullable type.
|
||||||
|
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||||
|
///
|
||||||
|
bool? withArchived;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) => identical(this, other) || other is PeopleUpdateItem &&
|
bool operator ==(Object other) => identical(this, other) || other is PeopleUpdateItem &&
|
||||||
other.birthDate == birthDate &&
|
other.birthDate == birthDate &&
|
||||||
other.featureFaceAssetId == featureFaceAssetId &&
|
other.featureFaceAssetId == featureFaceAssetId &&
|
||||||
other.id == id &&
|
other.id == id &&
|
||||||
other.isHidden == isHidden &&
|
other.isHidden == isHidden &&
|
||||||
other.name == name;
|
other.name == name &&
|
||||||
|
other.withArchived == withArchived;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
@ -68,10 +79,11 @@ class PeopleUpdateItem {
|
|||||||
(featureFaceAssetId == null ? 0 : featureFaceAssetId!.hashCode) +
|
(featureFaceAssetId == null ? 0 : featureFaceAssetId!.hashCode) +
|
||||||
(id.hashCode) +
|
(id.hashCode) +
|
||||||
(isHidden == null ? 0 : isHidden!.hashCode) +
|
(isHidden == null ? 0 : isHidden!.hashCode) +
|
||||||
(name == null ? 0 : name!.hashCode);
|
(name == null ? 0 : name!.hashCode) +
|
||||||
|
(withArchived == null ? 0 : withArchived!.hashCode);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'PeopleUpdateItem[birthDate=$birthDate, featureFaceAssetId=$featureFaceAssetId, id=$id, isHidden=$isHidden, name=$name]';
|
String toString() => 'PeopleUpdateItem[birthDate=$birthDate, featureFaceAssetId=$featureFaceAssetId, id=$id, isHidden=$isHidden, name=$name, withArchived=$withArchived]';
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final json = <String, dynamic>{};
|
final json = <String, dynamic>{};
|
||||||
@ -96,6 +108,11 @@ class PeopleUpdateItem {
|
|||||||
} else {
|
} else {
|
||||||
// json[r'name'] = null;
|
// json[r'name'] = null;
|
||||||
}
|
}
|
||||||
|
if (this.withArchived != null) {
|
||||||
|
json[r'withArchived'] = this.withArchived;
|
||||||
|
} else {
|
||||||
|
// json[r'withArchived'] = null;
|
||||||
|
}
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,6 +130,7 @@ class PeopleUpdateItem {
|
|||||||
id: mapValueOfType<String>(json, r'id')!,
|
id: mapValueOfType<String>(json, r'id')!,
|
||||||
isHidden: mapValueOfType<bool>(json, r'isHidden'),
|
isHidden: mapValueOfType<bool>(json, r'isHidden'),
|
||||||
name: mapValueOfType<String>(json, r'name'),
|
name: mapValueOfType<String>(json, r'name'),
|
||||||
|
withArchived: mapValueOfType<bool>(json, r'withArchived'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
24
mobile/openapi/lib/model/person_response_dto.dart
generated
24
mobile/openapi/lib/model/person_response_dto.dart
generated
@ -19,6 +19,7 @@ class PersonResponseDto {
|
|||||||
required this.name,
|
required this.name,
|
||||||
required this.thumbnailPath,
|
required this.thumbnailPath,
|
||||||
this.updatedAt,
|
this.updatedAt,
|
||||||
|
this.withArchived,
|
||||||
});
|
});
|
||||||
|
|
||||||
DateTime? birthDate;
|
DateTime? birthDate;
|
||||||
@ -40,6 +41,15 @@ class PersonResponseDto {
|
|||||||
///
|
///
|
||||||
DateTime? updatedAt;
|
DateTime? updatedAt;
|
||||||
|
|
||||||
|
/// This property was added in v1.118.0
|
||||||
|
///
|
||||||
|
/// Please note: This property should have been non-nullable! Since the specification file
|
||||||
|
/// does not include a default value (using the "default:" property), however, the generated
|
||||||
|
/// source code must fall back to having a nullable type.
|
||||||
|
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||||
|
///
|
||||||
|
bool? withArchived;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) => identical(this, other) || other is PersonResponseDto &&
|
bool operator ==(Object other) => identical(this, other) || other is PersonResponseDto &&
|
||||||
other.birthDate == birthDate &&
|
other.birthDate == birthDate &&
|
||||||
@ -47,7 +57,8 @@ class PersonResponseDto {
|
|||||||
other.isHidden == isHidden &&
|
other.isHidden == isHidden &&
|
||||||
other.name == name &&
|
other.name == name &&
|
||||||
other.thumbnailPath == thumbnailPath &&
|
other.thumbnailPath == thumbnailPath &&
|
||||||
other.updatedAt == updatedAt;
|
other.updatedAt == updatedAt &&
|
||||||
|
other.withArchived == withArchived;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
@ -57,10 +68,11 @@ class PersonResponseDto {
|
|||||||
(isHidden.hashCode) +
|
(isHidden.hashCode) +
|
||||||
(name.hashCode) +
|
(name.hashCode) +
|
||||||
(thumbnailPath.hashCode) +
|
(thumbnailPath.hashCode) +
|
||||||
(updatedAt == null ? 0 : updatedAt!.hashCode);
|
(updatedAt == null ? 0 : updatedAt!.hashCode) +
|
||||||
|
(withArchived == null ? 0 : withArchived!.hashCode);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'PersonResponseDto[birthDate=$birthDate, id=$id, isHidden=$isHidden, name=$name, thumbnailPath=$thumbnailPath, updatedAt=$updatedAt]';
|
String toString() => 'PersonResponseDto[birthDate=$birthDate, id=$id, isHidden=$isHidden, name=$name, thumbnailPath=$thumbnailPath, updatedAt=$updatedAt, withArchived=$withArchived]';
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final json = <String, dynamic>{};
|
final json = <String, dynamic>{};
|
||||||
@ -78,6 +90,11 @@ class PersonResponseDto {
|
|||||||
} else {
|
} else {
|
||||||
// json[r'updatedAt'] = null;
|
// json[r'updatedAt'] = null;
|
||||||
}
|
}
|
||||||
|
if (this.withArchived != null) {
|
||||||
|
json[r'withArchived'] = this.withArchived;
|
||||||
|
} else {
|
||||||
|
// json[r'withArchived'] = null;
|
||||||
|
}
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,6 +113,7 @@ class PersonResponseDto {
|
|||||||
name: mapValueOfType<String>(json, r'name')!,
|
name: mapValueOfType<String>(json, r'name')!,
|
||||||
thumbnailPath: mapValueOfType<String>(json, r'thumbnailPath')!,
|
thumbnailPath: mapValueOfType<String>(json, r'thumbnailPath')!,
|
||||||
updatedAt: mapDateTime(json, r'updatedAt', r''),
|
updatedAt: mapDateTime(json, r'updatedAt', r''),
|
||||||
|
withArchived: mapValueOfType<bool>(json, r'withArchived'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
24
mobile/openapi/lib/model/person_update_dto.dart
generated
24
mobile/openapi/lib/model/person_update_dto.dart
generated
@ -17,6 +17,7 @@ class PersonUpdateDto {
|
|||||||
this.featureFaceAssetId,
|
this.featureFaceAssetId,
|
||||||
this.isHidden,
|
this.isHidden,
|
||||||
this.name,
|
this.name,
|
||||||
|
this.withArchived,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Person date of birth. Note: the mobile app cannot currently set the birth date to null.
|
/// Person date of birth. Note: the mobile app cannot currently set the birth date to null.
|
||||||
@ -49,12 +50,22 @@ class PersonUpdateDto {
|
|||||||
///
|
///
|
||||||
String? name;
|
String? name;
|
||||||
|
|
||||||
|
/// This property was added in v1.118.0
|
||||||
|
///
|
||||||
|
/// Please note: This property should have been non-nullable! Since the specification file
|
||||||
|
/// does not include a default value (using the "default:" property), however, the generated
|
||||||
|
/// source code must fall back to having a nullable type.
|
||||||
|
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||||
|
///
|
||||||
|
bool? withArchived;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) => identical(this, other) || other is PersonUpdateDto &&
|
bool operator ==(Object other) => identical(this, other) || other is PersonUpdateDto &&
|
||||||
other.birthDate == birthDate &&
|
other.birthDate == birthDate &&
|
||||||
other.featureFaceAssetId == featureFaceAssetId &&
|
other.featureFaceAssetId == featureFaceAssetId &&
|
||||||
other.isHidden == isHidden &&
|
other.isHidden == isHidden &&
|
||||||
other.name == name;
|
other.name == name &&
|
||||||
|
other.withArchived == withArchived;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
@ -62,10 +73,11 @@ class PersonUpdateDto {
|
|||||||
(birthDate == null ? 0 : birthDate!.hashCode) +
|
(birthDate == null ? 0 : birthDate!.hashCode) +
|
||||||
(featureFaceAssetId == null ? 0 : featureFaceAssetId!.hashCode) +
|
(featureFaceAssetId == null ? 0 : featureFaceAssetId!.hashCode) +
|
||||||
(isHidden == null ? 0 : isHidden!.hashCode) +
|
(isHidden == null ? 0 : isHidden!.hashCode) +
|
||||||
(name == null ? 0 : name!.hashCode);
|
(name == null ? 0 : name!.hashCode) +
|
||||||
|
(withArchived == null ? 0 : withArchived!.hashCode);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'PersonUpdateDto[birthDate=$birthDate, featureFaceAssetId=$featureFaceAssetId, isHidden=$isHidden, name=$name]';
|
String toString() => 'PersonUpdateDto[birthDate=$birthDate, featureFaceAssetId=$featureFaceAssetId, isHidden=$isHidden, name=$name, withArchived=$withArchived]';
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final json = <String, dynamic>{};
|
final json = <String, dynamic>{};
|
||||||
@ -89,6 +101,11 @@ class PersonUpdateDto {
|
|||||||
} else {
|
} else {
|
||||||
// json[r'name'] = null;
|
// json[r'name'] = null;
|
||||||
}
|
}
|
||||||
|
if (this.withArchived != null) {
|
||||||
|
json[r'withArchived'] = this.withArchived;
|
||||||
|
} else {
|
||||||
|
// json[r'withArchived'] = null;
|
||||||
|
}
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,6 +122,7 @@ class PersonUpdateDto {
|
|||||||
featureFaceAssetId: mapValueOfType<String>(json, r'featureFaceAssetId'),
|
featureFaceAssetId: mapValueOfType<String>(json, r'featureFaceAssetId'),
|
||||||
isHidden: mapValueOfType<bool>(json, r'isHidden'),
|
isHidden: mapValueOfType<bool>(json, r'isHidden'),
|
||||||
name: mapValueOfType<String>(json, r'name'),
|
name: mapValueOfType<String>(json, r'name'),
|
||||||
|
withArchived: mapValueOfType<bool>(json, r'withArchived'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -20,6 +20,7 @@ class PersonWithFacesResponseDto {
|
|||||||
required this.name,
|
required this.name,
|
||||||
required this.thumbnailPath,
|
required this.thumbnailPath,
|
||||||
this.updatedAt,
|
this.updatedAt,
|
||||||
|
this.withArchived,
|
||||||
});
|
});
|
||||||
|
|
||||||
DateTime? birthDate;
|
DateTime? birthDate;
|
||||||
@ -43,6 +44,15 @@ class PersonWithFacesResponseDto {
|
|||||||
///
|
///
|
||||||
DateTime? updatedAt;
|
DateTime? updatedAt;
|
||||||
|
|
||||||
|
/// This property was added in v1.118.0
|
||||||
|
///
|
||||||
|
/// Please note: This property should have been non-nullable! Since the specification file
|
||||||
|
/// does not include a default value (using the "default:" property), however, the generated
|
||||||
|
/// source code must fall back to having a nullable type.
|
||||||
|
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||||
|
///
|
||||||
|
bool? withArchived;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) => identical(this, other) || other is PersonWithFacesResponseDto &&
|
bool operator ==(Object other) => identical(this, other) || other is PersonWithFacesResponseDto &&
|
||||||
other.birthDate == birthDate &&
|
other.birthDate == birthDate &&
|
||||||
@ -51,7 +61,8 @@ class PersonWithFacesResponseDto {
|
|||||||
other.isHidden == isHidden &&
|
other.isHidden == isHidden &&
|
||||||
other.name == name &&
|
other.name == name &&
|
||||||
other.thumbnailPath == thumbnailPath &&
|
other.thumbnailPath == thumbnailPath &&
|
||||||
other.updatedAt == updatedAt;
|
other.updatedAt == updatedAt &&
|
||||||
|
other.withArchived == withArchived;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
@ -62,10 +73,11 @@ class PersonWithFacesResponseDto {
|
|||||||
(isHidden.hashCode) +
|
(isHidden.hashCode) +
|
||||||
(name.hashCode) +
|
(name.hashCode) +
|
||||||
(thumbnailPath.hashCode) +
|
(thumbnailPath.hashCode) +
|
||||||
(updatedAt == null ? 0 : updatedAt!.hashCode);
|
(updatedAt == null ? 0 : updatedAt!.hashCode) +
|
||||||
|
(withArchived == null ? 0 : withArchived!.hashCode);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'PersonWithFacesResponseDto[birthDate=$birthDate, faces=$faces, id=$id, isHidden=$isHidden, name=$name, thumbnailPath=$thumbnailPath, updatedAt=$updatedAt]';
|
String toString() => 'PersonWithFacesResponseDto[birthDate=$birthDate, faces=$faces, id=$id, isHidden=$isHidden, name=$name, thumbnailPath=$thumbnailPath, updatedAt=$updatedAt, withArchived=$withArchived]';
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final json = <String, dynamic>{};
|
final json = <String, dynamic>{};
|
||||||
@ -84,6 +96,11 @@ class PersonWithFacesResponseDto {
|
|||||||
} else {
|
} else {
|
||||||
// json[r'updatedAt'] = null;
|
// json[r'updatedAt'] = null;
|
||||||
}
|
}
|
||||||
|
if (this.withArchived != null) {
|
||||||
|
json[r'withArchived'] = this.withArchived;
|
||||||
|
} else {
|
||||||
|
// json[r'withArchived'] = null;
|
||||||
|
}
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,6 +120,7 @@ class PersonWithFacesResponseDto {
|
|||||||
name: mapValueOfType<String>(json, r'name')!,
|
name: mapValueOfType<String>(json, r'name')!,
|
||||||
thumbnailPath: mapValueOfType<String>(json, r'thumbnailPath')!,
|
thumbnailPath: mapValueOfType<String>(json, r'thumbnailPath')!,
|
||||||
updatedAt: mapDateTime(json, r'updatedAt', r''),
|
updatedAt: mapDateTime(json, r'updatedAt', r''),
|
||||||
|
withArchived: mapValueOfType<bool>(json, r'withArchived'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -4152,6 +4152,14 @@
|
|||||||
"format": "uuid",
|
"format": "uuid",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "withArchived",
|
||||||
|
"required": false,
|
||||||
|
"in": "query",
|
||||||
|
"schema": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@ -10097,6 +10105,10 @@
|
|||||||
"name": {
|
"name": {
|
||||||
"description": "Person name.",
|
"description": "Person name.",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"withArchived": {
|
||||||
|
"description": "This property was added in v1.118.0",
|
||||||
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
@ -10229,6 +10241,10 @@
|
|||||||
"description": "This property was added in v1.107.0",
|
"description": "This property was added in v1.107.0",
|
||||||
"format": "date-time",
|
"format": "date-time",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"withArchived": {
|
||||||
|
"description": "This property was added in v1.118.0",
|
||||||
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
@ -10270,6 +10286,10 @@
|
|||||||
"name": {
|
"name": {
|
||||||
"description": "Person name.",
|
"description": "Person name.",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"withArchived": {
|
||||||
|
"description": "This property was added in v1.118.0",
|
||||||
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"type": "object"
|
"type": "object"
|
||||||
@ -10303,6 +10323,10 @@
|
|||||||
"description": "This property was added in v1.107.0",
|
"description": "This property was added in v1.107.0",
|
||||||
"format": "date-time",
|
"format": "date-time",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"withArchived": {
|
||||||
|
"description": "This property was added in v1.118.0",
|
||||||
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
|
@ -220,6 +220,8 @@ export type PersonWithFacesResponseDto = {
|
|||||||
thumbnailPath: string;
|
thumbnailPath: string;
|
||||||
/** This property was added in v1.107.0 */
|
/** This property was added in v1.107.0 */
|
||||||
updatedAt?: string;
|
updatedAt?: string;
|
||||||
|
/** This property was added in v1.118.0 */
|
||||||
|
withArchived?: boolean;
|
||||||
};
|
};
|
||||||
export type SmartInfoResponseDto = {
|
export type SmartInfoResponseDto = {
|
||||||
objects?: string[] | null;
|
objects?: string[] | null;
|
||||||
@ -502,6 +504,8 @@ export type PersonResponseDto = {
|
|||||||
thumbnailPath: string;
|
thumbnailPath: string;
|
||||||
/** This property was added in v1.107.0 */
|
/** This property was added in v1.107.0 */
|
||||||
updatedAt?: string;
|
updatedAt?: string;
|
||||||
|
/** This property was added in v1.118.0 */
|
||||||
|
withArchived?: boolean;
|
||||||
};
|
};
|
||||||
export type AssetFaceResponseDto = {
|
export type AssetFaceResponseDto = {
|
||||||
boundingBoxX1: number;
|
boundingBoxX1: number;
|
||||||
@ -703,6 +707,8 @@ export type PeopleUpdateItem = {
|
|||||||
isHidden?: boolean;
|
isHidden?: boolean;
|
||||||
/** Person name. */
|
/** Person name. */
|
||||||
name?: string;
|
name?: string;
|
||||||
|
/** This property was added in v1.118.0 */
|
||||||
|
withArchived?: boolean;
|
||||||
};
|
};
|
||||||
export type PeopleUpdateDto = {
|
export type PeopleUpdateDto = {
|
||||||
people: PeopleUpdateItem[];
|
people: PeopleUpdateItem[];
|
||||||
@ -717,6 +723,8 @@ export type PersonUpdateDto = {
|
|||||||
isHidden?: boolean;
|
isHidden?: boolean;
|
||||||
/** Person name. */
|
/** Person name. */
|
||||||
name?: string;
|
name?: string;
|
||||||
|
/** This property was added in v1.118.0 */
|
||||||
|
withArchived?: boolean;
|
||||||
};
|
};
|
||||||
export type MergePersonDto = {
|
export type MergePersonDto = {
|
||||||
ids: string[];
|
ids: string[];
|
||||||
@ -2410,13 +2418,16 @@ export function reassignFaces({ id, assetFaceUpdateDto }: {
|
|||||||
body: assetFaceUpdateDto
|
body: assetFaceUpdateDto
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
export function getPersonStatistics({ id }: {
|
export function getPersonStatistics({ id, withArchived }: {
|
||||||
id: string;
|
id: string;
|
||||||
|
withArchived?: boolean;
|
||||||
}, opts?: Oazapfts.RequestOpts) {
|
}, opts?: Oazapfts.RequestOpts) {
|
||||||
return oazapfts.ok(oazapfts.fetchJson<{
|
return oazapfts.ok(oazapfts.fetchJson<{
|
||||||
status: 200;
|
status: 200;
|
||||||
data: PersonStatisticsResponseDto;
|
data: PersonStatisticsResponseDto;
|
||||||
}>(`/people/${encodeURIComponent(id)}/statistics`, {
|
}>(`/people/${encodeURIComponent(id)}/statistics${QS.query(QS.explode({
|
||||||
|
withArchived
|
||||||
|
}))}`, {
|
||||||
...opts
|
...opts
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import {
|
|||||||
PersonResponseDto,
|
PersonResponseDto,
|
||||||
PersonSearchDto,
|
PersonSearchDto,
|
||||||
PersonStatisticsResponseDto,
|
PersonStatisticsResponseDto,
|
||||||
|
PersonStatsDto,
|
||||||
PersonUpdateDto,
|
PersonUpdateDto,
|
||||||
} from 'src/dtos/person.dto';
|
} from 'src/dtos/person.dto';
|
||||||
import { Permission } from 'src/enum';
|
import { Permission } from 'src/enum';
|
||||||
@ -65,8 +66,12 @@ export class PersonController {
|
|||||||
|
|
||||||
@Get(':id/statistics')
|
@Get(':id/statistics')
|
||||||
@Authenticated({ permission: Permission.PERSON_STATISTICS })
|
@Authenticated({ permission: Permission.PERSON_STATISTICS })
|
||||||
getPersonStatistics(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<PersonStatisticsResponseDto> {
|
getPersonStatistics(
|
||||||
return this.service.getStatistics(auth, id);
|
@Auth() auth: AuthDto,
|
||||||
|
@Param() { id }: UUIDParamDto,
|
||||||
|
@Query() dto: PersonStatsDto,
|
||||||
|
): Promise<PersonStatisticsResponseDto> {
|
||||||
|
return this.service.getStatistics(auth, id, dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id/thumbnail')
|
@Get(':id/thumbnail')
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
import { IsArray, IsInt, IsNotEmpty, IsString, Max, Min, ValidateNested } from 'class-validator';
|
import { IsArray, IsBoolean, IsInt, IsNotEmpty, IsString, Max, Min, ValidateNested } from 'class-validator';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { PropertyLifecycle } from 'src/decorators';
|
import { PropertyLifecycle } from 'src/decorators';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
@ -41,6 +41,11 @@ export class PersonUpdateDto extends PersonCreateDto {
|
|||||||
@Optional()
|
@Optional()
|
||||||
@IsString()
|
@IsString()
|
||||||
featureFaceAssetId?: string;
|
featureFaceAssetId?: string;
|
||||||
|
|
||||||
|
@Optional()
|
||||||
|
@IsBoolean()
|
||||||
|
@PropertyLifecycle({ addedAt: 'v1.118.0' })
|
||||||
|
withArchived?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PeopleUpdateDto {
|
export class PeopleUpdateDto {
|
||||||
@ -93,6 +98,8 @@ export class PersonResponseDto {
|
|||||||
isHidden!: boolean;
|
isHidden!: boolean;
|
||||||
@PropertyLifecycle({ addedAt: 'v1.107.0' })
|
@PropertyLifecycle({ addedAt: 'v1.107.0' })
|
||||||
updatedAt?: Date;
|
updatedAt?: Date;
|
||||||
|
@PropertyLifecycle({ addedAt: 'v1.118.0' })
|
||||||
|
withArchived?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PersonWithFacesResponseDto extends PersonResponseDto {
|
export class PersonWithFacesResponseDto extends PersonResponseDto {
|
||||||
@ -147,6 +154,11 @@ export class PersonStatisticsResponseDto {
|
|||||||
assets!: number;
|
assets!: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class PersonStatsDto {
|
||||||
|
@ValidateBoolean({ optional: true })
|
||||||
|
withArchived?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export class PeopleResponseDto {
|
export class PeopleResponseDto {
|
||||||
@ApiProperty({ type: 'integer' })
|
@ApiProperty({ type: 'integer' })
|
||||||
total!: number;
|
total!: number;
|
||||||
@ -167,6 +179,7 @@ export function mapPerson(person: PersonEntity): PersonResponseDto {
|
|||||||
thumbnailPath: person.thumbnailPath,
|
thumbnailPath: person.thumbnailPath,
|
||||||
isHidden: person.isHidden,
|
isHidden: person.isHidden,
|
||||||
updatedAt: person.updatedAt,
|
updatedAt: person.updatedAt,
|
||||||
|
withArchived: person.withArchived,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,4 +49,7 @@ export class PersonEntity {
|
|||||||
|
|
||||||
@Column({ default: false })
|
@Column({ default: false })
|
||||||
isHidden!: boolean;
|
isHidden!: boolean;
|
||||||
|
|
||||||
|
@Column({ default: false })
|
||||||
|
withArchived!: boolean;
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,10 @@ export interface DeleteFacesOptions {
|
|||||||
sourceType: SourceType;
|
sourceType: SourceType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PersonStatsOptions {
|
||||||
|
withArchived?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export type UnassignFacesOptions = DeleteFacesOptions;
|
export type UnassignFacesOptions = DeleteFacesOptions;
|
||||||
|
|
||||||
export interface IPersonRepository {
|
export interface IPersonRepository {
|
||||||
@ -74,7 +78,7 @@ export interface IPersonRepository {
|
|||||||
getFaces(assetId: string): Promise<AssetFaceEntity[]>;
|
getFaces(assetId: string): Promise<AssetFaceEntity[]>;
|
||||||
getFacesByIds(ids: AssetFaceId[]): Promise<AssetFaceEntity[]>;
|
getFacesByIds(ids: AssetFaceId[]): Promise<AssetFaceEntity[]>;
|
||||||
getRandomFace(personId: string): Promise<AssetFaceEntity | null>;
|
getRandomFace(personId: string): Promise<AssetFaceEntity | null>;
|
||||||
getStatistics(personId: string): Promise<PersonStatistics>;
|
getStatistics(personId: string, options: PersonStatsOptions): Promise<PersonStatistics>;
|
||||||
reassignFace(assetFaceId: string, newPersonId: string): Promise<number>;
|
reassignFace(assetFaceId: string, newPersonId: string): Promise<number>;
|
||||||
getNumberOfPeople(userId: string): Promise<PeopleStatistics>;
|
getNumberOfPeople(userId: string): Promise<PeopleStatistics>;
|
||||||
reassignFaces(data: UpdateFacesData): Promise<number>;
|
reassignFaces(data: UpdateFacesData): Promise<number>;
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class AddWithArchivedToPerson1728944141526 implements MigrationInterface {
|
||||||
|
name = 'AddWithArchivedToPerson1728944141526'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "person" ADD "withArchived" boolean NOT NULL DEFAULT false`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "withArchived"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -212,6 +212,7 @@ SELECT
|
|||||||
"8258e303a73a72cf6abb13d73fb592dde0d68280"."thumbnailPath" AS "8258e303a73a72cf6abb13d73fb592dde0d68280_thumbnailPath",
|
"8258e303a73a72cf6abb13d73fb592dde0d68280"."thumbnailPath" AS "8258e303a73a72cf6abb13d73fb592dde0d68280_thumbnailPath",
|
||||||
"8258e303a73a72cf6abb13d73fb592dde0d68280"."faceAssetId" AS "8258e303a73a72cf6abb13d73fb592dde0d68280_faceAssetId",
|
"8258e303a73a72cf6abb13d73fb592dde0d68280"."faceAssetId" AS "8258e303a73a72cf6abb13d73fb592dde0d68280_faceAssetId",
|
||||||
"8258e303a73a72cf6abb13d73fb592dde0d68280"."isHidden" AS "8258e303a73a72cf6abb13d73fb592dde0d68280_isHidden",
|
"8258e303a73a72cf6abb13d73fb592dde0d68280"."isHidden" AS "8258e303a73a72cf6abb13d73fb592dde0d68280_isHidden",
|
||||||
|
"8258e303a73a72cf6abb13d73fb592dde0d68280"."withArchived" AS "8258e303a73a72cf6abb13d73fb592dde0d68280_withArchived",
|
||||||
"AssetEntity__AssetEntity_stack"."id" AS "AssetEntity__AssetEntity_stack_id",
|
"AssetEntity__AssetEntity_stack"."id" AS "AssetEntity__AssetEntity_stack_id",
|
||||||
"AssetEntity__AssetEntity_stack"."ownerId" AS "AssetEntity__AssetEntity_stack_ownerId",
|
"AssetEntity__AssetEntity_stack"."ownerId" AS "AssetEntity__AssetEntity_stack_ownerId",
|
||||||
"AssetEntity__AssetEntity_stack"."primaryAssetId" AS "AssetEntity__AssetEntity_stack_primaryAssetId",
|
"AssetEntity__AssetEntity_stack"."primaryAssetId" AS "AssetEntity__AssetEntity_stack_primaryAssetId",
|
||||||
|
@ -17,7 +17,8 @@ SELECT
|
|||||||
"person"."birthDate" AS "person_birthDate",
|
"person"."birthDate" AS "person_birthDate",
|
||||||
"person"."thumbnailPath" AS "person_thumbnailPath",
|
"person"."thumbnailPath" AS "person_thumbnailPath",
|
||||||
"person"."faceAssetId" AS "person_faceAssetId",
|
"person"."faceAssetId" AS "person_faceAssetId",
|
||||||
"person"."isHidden" AS "person_isHidden"
|
"person"."isHidden" AS "person_isHidden",
|
||||||
|
"person"."withArchived" AS "person_withArchived"
|
||||||
FROM
|
FROM
|
||||||
"person" "person"
|
"person" "person"
|
||||||
LEFT JOIN "asset_faces" "face" ON "face"."personId" = "person"."id"
|
LEFT JOIN "asset_faces" "face" ON "face"."personId" = "person"."id"
|
||||||
@ -54,7 +55,8 @@ SELECT
|
|||||||
"person"."birthDate" AS "person_birthDate",
|
"person"."birthDate" AS "person_birthDate",
|
||||||
"person"."thumbnailPath" AS "person_thumbnailPath",
|
"person"."thumbnailPath" AS "person_thumbnailPath",
|
||||||
"person"."faceAssetId" AS "person_faceAssetId",
|
"person"."faceAssetId" AS "person_faceAssetId",
|
||||||
"person"."isHidden" AS "person_isHidden"
|
"person"."isHidden" AS "person_isHidden",
|
||||||
|
"person"."withArchived" AS "person_withArchived"
|
||||||
FROM
|
FROM
|
||||||
"person" "person"
|
"person" "person"
|
||||||
LEFT JOIN "asset_faces" "face" ON "face"."personId" = "person"."id"
|
LEFT JOIN "asset_faces" "face" ON "face"."personId" = "person"."id"
|
||||||
@ -83,7 +85,8 @@ SELECT
|
|||||||
"AssetFaceEntity__AssetFaceEntity_person"."birthDate" AS "AssetFaceEntity__AssetFaceEntity_person_birthDate",
|
"AssetFaceEntity__AssetFaceEntity_person"."birthDate" AS "AssetFaceEntity__AssetFaceEntity_person_birthDate",
|
||||||
"AssetFaceEntity__AssetFaceEntity_person"."thumbnailPath" AS "AssetFaceEntity__AssetFaceEntity_person_thumbnailPath",
|
"AssetFaceEntity__AssetFaceEntity_person"."thumbnailPath" AS "AssetFaceEntity__AssetFaceEntity_person_thumbnailPath",
|
||||||
"AssetFaceEntity__AssetFaceEntity_person"."faceAssetId" AS "AssetFaceEntity__AssetFaceEntity_person_faceAssetId",
|
"AssetFaceEntity__AssetFaceEntity_person"."faceAssetId" AS "AssetFaceEntity__AssetFaceEntity_person_faceAssetId",
|
||||||
"AssetFaceEntity__AssetFaceEntity_person"."isHidden" AS "AssetFaceEntity__AssetFaceEntity_person_isHidden"
|
"AssetFaceEntity__AssetFaceEntity_person"."isHidden" AS "AssetFaceEntity__AssetFaceEntity_person_isHidden",
|
||||||
|
"AssetFaceEntity__AssetFaceEntity_person"."withArchived" AS "AssetFaceEntity__AssetFaceEntity_person_withArchived"
|
||||||
FROM
|
FROM
|
||||||
"asset_faces" "AssetFaceEntity"
|
"asset_faces" "AssetFaceEntity"
|
||||||
LEFT JOIN "person" "AssetFaceEntity__AssetFaceEntity_person" ON "AssetFaceEntity__AssetFaceEntity_person"."id" = "AssetFaceEntity"."personId"
|
LEFT JOIN "person" "AssetFaceEntity__AssetFaceEntity_person" ON "AssetFaceEntity__AssetFaceEntity_person"."id" = "AssetFaceEntity"."personId"
|
||||||
@ -116,7 +119,8 @@ FROM
|
|||||||
"AssetFaceEntity__AssetFaceEntity_person"."birthDate" AS "AssetFaceEntity__AssetFaceEntity_person_birthDate",
|
"AssetFaceEntity__AssetFaceEntity_person"."birthDate" AS "AssetFaceEntity__AssetFaceEntity_person_birthDate",
|
||||||
"AssetFaceEntity__AssetFaceEntity_person"."thumbnailPath" AS "AssetFaceEntity__AssetFaceEntity_person_thumbnailPath",
|
"AssetFaceEntity__AssetFaceEntity_person"."thumbnailPath" AS "AssetFaceEntity__AssetFaceEntity_person_thumbnailPath",
|
||||||
"AssetFaceEntity__AssetFaceEntity_person"."faceAssetId" AS "AssetFaceEntity__AssetFaceEntity_person_faceAssetId",
|
"AssetFaceEntity__AssetFaceEntity_person"."faceAssetId" AS "AssetFaceEntity__AssetFaceEntity_person_faceAssetId",
|
||||||
"AssetFaceEntity__AssetFaceEntity_person"."isHidden" AS "AssetFaceEntity__AssetFaceEntity_person_isHidden"
|
"AssetFaceEntity__AssetFaceEntity_person"."isHidden" AS "AssetFaceEntity__AssetFaceEntity_person_isHidden",
|
||||||
|
"AssetFaceEntity__AssetFaceEntity_person"."withArchived" AS "AssetFaceEntity__AssetFaceEntity_person_withArchived"
|
||||||
FROM
|
FROM
|
||||||
"asset_faces" "AssetFaceEntity"
|
"asset_faces" "AssetFaceEntity"
|
||||||
LEFT JOIN "person" "AssetFaceEntity__AssetFaceEntity_person" ON "AssetFaceEntity__AssetFaceEntity_person"."id" = "AssetFaceEntity"."personId"
|
LEFT JOIN "person" "AssetFaceEntity__AssetFaceEntity_person" ON "AssetFaceEntity__AssetFaceEntity_person"."id" = "AssetFaceEntity"."personId"
|
||||||
@ -153,6 +157,7 @@ FROM
|
|||||||
"AssetFaceEntity__AssetFaceEntity_person"."thumbnailPath" AS "AssetFaceEntity__AssetFaceEntity_person_thumbnailPath",
|
"AssetFaceEntity__AssetFaceEntity_person"."thumbnailPath" AS "AssetFaceEntity__AssetFaceEntity_person_thumbnailPath",
|
||||||
"AssetFaceEntity__AssetFaceEntity_person"."faceAssetId" AS "AssetFaceEntity__AssetFaceEntity_person_faceAssetId",
|
"AssetFaceEntity__AssetFaceEntity_person"."faceAssetId" AS "AssetFaceEntity__AssetFaceEntity_person_faceAssetId",
|
||||||
"AssetFaceEntity__AssetFaceEntity_person"."isHidden" AS "AssetFaceEntity__AssetFaceEntity_person_isHidden",
|
"AssetFaceEntity__AssetFaceEntity_person"."isHidden" AS "AssetFaceEntity__AssetFaceEntity_person_isHidden",
|
||||||
|
"AssetFaceEntity__AssetFaceEntity_person"."withArchived" AS "AssetFaceEntity__AssetFaceEntity_person_withArchived",
|
||||||
"AssetFaceEntity__AssetFaceEntity_asset"."id" AS "AssetFaceEntity__AssetFaceEntity_asset_id",
|
"AssetFaceEntity__AssetFaceEntity_asset"."id" AS "AssetFaceEntity__AssetFaceEntity_asset_id",
|
||||||
"AssetFaceEntity__AssetFaceEntity_asset"."deviceAssetId" AS "AssetFaceEntity__AssetFaceEntity_asset_deviceAssetId",
|
"AssetFaceEntity__AssetFaceEntity_asset"."deviceAssetId" AS "AssetFaceEntity__AssetFaceEntity_asset_deviceAssetId",
|
||||||
"AssetFaceEntity__AssetFaceEntity_asset"."ownerId" AS "AssetFaceEntity__AssetFaceEntity_asset_ownerId",
|
"AssetFaceEntity__AssetFaceEntity_asset"."ownerId" AS "AssetFaceEntity__AssetFaceEntity_asset_ownerId",
|
||||||
@ -213,7 +218,8 @@ SELECT
|
|||||||
"person"."birthDate" AS "person_birthDate",
|
"person"."birthDate" AS "person_birthDate",
|
||||||
"person"."thumbnailPath" AS "person_thumbnailPath",
|
"person"."thumbnailPath" AS "person_thumbnailPath",
|
||||||
"person"."faceAssetId" AS "person_faceAssetId",
|
"person"."faceAssetId" AS "person_faceAssetId",
|
||||||
"person"."isHidden" AS "person_isHidden"
|
"person"."isHidden" AS "person_isHidden",
|
||||||
|
"person"."withArchived" AS "person_withArchived"
|
||||||
FROM
|
FROM
|
||||||
"person" "person"
|
"person" "person"
|
||||||
WHERE
|
WHERE
|
||||||
@ -242,11 +248,18 @@ FROM
|
|||||||
"asset_faces" "face"
|
"asset_faces" "face"
|
||||||
LEFT JOIN "assets" "asset" ON "asset"."id" = "face"."assetId"
|
LEFT JOIN "assets" "asset" ON "asset"."id" = "face"."assetId"
|
||||||
AND ("asset"."deletedAt" IS NULL)
|
AND ("asset"."deletedAt" IS NULL)
|
||||||
|
LEFT JOIN "person" "person" ON "person"."id" = "face"."personId"
|
||||||
WHERE
|
WHERE
|
||||||
"face"."personId" = $1
|
"face"."personId" = $1
|
||||||
AND "asset"."isArchived" = false
|
|
||||||
AND "asset"."deletedAt" IS NULL
|
AND "asset"."deletedAt" IS NULL
|
||||||
AND "asset"."livePhotoVideoId" IS NULL
|
AND "asset"."livePhotoVideoId" IS NULL
|
||||||
|
AND (
|
||||||
|
(
|
||||||
|
"person"."withArchived" = false
|
||||||
|
AND "asset"."isArchived" = false
|
||||||
|
)
|
||||||
|
OR "person"."withArchived" = true
|
||||||
|
)
|
||||||
|
|
||||||
-- PersonRepository.getNumberOfPeople
|
-- PersonRepository.getNumberOfPeople
|
||||||
SELECT
|
SELECT
|
||||||
|
@ -17,6 +17,7 @@ import {
|
|||||||
PersonNameSearchOptions,
|
PersonNameSearchOptions,
|
||||||
PersonSearchOptions,
|
PersonSearchOptions,
|
||||||
PersonStatistics,
|
PersonStatistics,
|
||||||
|
PersonStatsOptions,
|
||||||
UnassignFacesOptions,
|
UnassignFacesOptions,
|
||||||
UpdateFacesData,
|
UpdateFacesData,
|
||||||
} from 'src/interfaces/person.interface';
|
} from 'src/interfaces/person.interface';
|
||||||
@ -214,17 +215,33 @@ export class PersonRepository implements IPersonRepository {
|
|||||||
return queryBuilder.getMany();
|
return queryBuilder.getMany();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
@GenerateSql({ params: [DummyValue.UUID, { withArchived: undefined }] })
|
||||||
async getStatistics(personId: string): Promise<PersonStatistics> {
|
async getStatistics(personId: string, options: PersonStatsOptions): Promise<PersonStatistics> {
|
||||||
const items = await this.assetFaceRepository
|
/*
|
||||||
|
* withArchived: true -> Return the count of all assets for a given person
|
||||||
|
* withArchived: false -> Return the count of all unarchived assets for a given person
|
||||||
|
* withArchived: undefiend ->
|
||||||
|
* - If person.withArchived = true -> Return the count of all assets for a given person
|
||||||
|
* - If person.withArchived = false -> Return the count of all unarchived assets for a given person
|
||||||
|
*/
|
||||||
|
|
||||||
|
let queryBuilder = this.assetFaceRepository
|
||||||
.createQueryBuilder('face')
|
.createQueryBuilder('face')
|
||||||
.leftJoin('face.asset', 'asset')
|
.leftJoin('face.asset', 'asset')
|
||||||
.where('face.personId = :personId', { personId })
|
.where('face.personId = :personId', { personId })
|
||||||
.andWhere('asset.isArchived = false')
|
|
||||||
.andWhere('asset.deletedAt IS NULL')
|
.andWhere('asset.deletedAt IS NULL')
|
||||||
.andWhere('asset.livePhotoVideoId IS NULL')
|
.andWhere('asset.livePhotoVideoId IS NULL')
|
||||||
.select('COUNT(DISTINCT(asset.id))', 'count')
|
.select('COUNT(DISTINCT(asset.id))', 'count');
|
||||||
.getRawOne();
|
|
||||||
|
if (options.withArchived === false) {
|
||||||
|
queryBuilder = queryBuilder.andWhere('asset.isArchived = false');
|
||||||
|
} else if (options.withArchived === undefined) {
|
||||||
|
queryBuilder = queryBuilder
|
||||||
|
.leftJoin('face.person', 'person')
|
||||||
|
.andWhere('((person.withArchived = false AND asset.isArchived = false) OR person.withArchived = true)');
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = await queryBuilder.getRawOne();
|
||||||
return {
|
return {
|
||||||
assets: items.count ?? 0,
|
assets: items.count ?? 0,
|
||||||
};
|
};
|
||||||
|
@ -31,6 +31,7 @@ const responseDto: PersonResponseDto = {
|
|||||||
thumbnailPath: '/path/to/thumbnail.jpg',
|
thumbnailPath: '/path/to/thumbnail.jpg',
|
||||||
isHidden: false,
|
isHidden: false,
|
||||||
updatedAt: expect.any(Date),
|
updatedAt: expect.any(Date),
|
||||||
|
withArchived: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const statistics = { assets: 3 };
|
const statistics = { assets: 3 };
|
||||||
@ -118,6 +119,7 @@ describe(PersonService.name, () => {
|
|||||||
thumbnailPath: '/path/to/thumbnail.jpg',
|
thumbnailPath: '/path/to/thumbnail.jpg',
|
||||||
isHidden: true,
|
isHidden: true,
|
||||||
updatedAt: expect.any(Date),
|
updatedAt: expect.any(Date),
|
||||||
|
withArchived: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@ -218,6 +220,16 @@ describe(PersonService.name, () => {
|
|||||||
expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1']));
|
expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1']));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should update a person's withArchived", async () => {
|
||||||
|
personMock.update.mockResolvedValue(personStub.withName);
|
||||||
|
accessMock.person.checkOwnerAccess.mockResolvedValue(new Set(['person-1']));
|
||||||
|
|
||||||
|
await expect(sut.update(authStub.admin, 'person-1', { withArchived: true })).resolves.toEqual(responseDto);
|
||||||
|
|
||||||
|
expect(personMock.update).toHaveBeenCalledWith({ id: 'person-1', withArchived: true });
|
||||||
|
expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1']));
|
||||||
|
});
|
||||||
|
|
||||||
it("should update a person's date of birth", async () => {
|
it("should update a person's date of birth", async () => {
|
||||||
personMock.update.mockResolvedValue(personStub.withBirthDate);
|
personMock.update.mockResolvedValue(personStub.withBirthDate);
|
||||||
accessMock.person.checkOwnerAccess.mockResolvedValue(new Set(['person-1']));
|
accessMock.person.checkOwnerAccess.mockResolvedValue(new Set(['person-1']));
|
||||||
@ -229,6 +241,7 @@ describe(PersonService.name, () => {
|
|||||||
thumbnailPath: '/path/to/thumbnail.jpg',
|
thumbnailPath: '/path/to/thumbnail.jpg',
|
||||||
isHidden: false,
|
isHidden: false,
|
||||||
updatedAt: expect.any(Date),
|
updatedAt: expect.any(Date),
|
||||||
|
withArchived: false,
|
||||||
});
|
});
|
||||||
expect(personMock.update).toHaveBeenCalledWith({ id: 'person-1', birthDate: '1976-06-30' });
|
expect(personMock.update).toHaveBeenCalledWith({ id: 'person-1', birthDate: '1976-06-30' });
|
||||||
expect(jobMock.queue).not.toHaveBeenCalled();
|
expect(jobMock.queue).not.toHaveBeenCalled();
|
||||||
@ -381,6 +394,7 @@ describe(PersonService.name, () => {
|
|||||||
name: personStub.noName.name,
|
name: personStub.noName.name,
|
||||||
thumbnailPath: personStub.noName.thumbnailPath,
|
thumbnailPath: personStub.noName.thumbnailPath,
|
||||||
updatedAt: expect.any(Date),
|
updatedAt: expect.any(Date),
|
||||||
|
withArchived: personStub.noName.withArchived,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(jobMock.queue).not.toHaveBeenCalledWith();
|
expect(jobMock.queue).not.toHaveBeenCalledWith();
|
||||||
@ -1171,13 +1185,13 @@ describe(PersonService.name, () => {
|
|||||||
personMock.getById.mockResolvedValue(personStub.primaryPerson);
|
personMock.getById.mockResolvedValue(personStub.primaryPerson);
|
||||||
personMock.getStatistics.mockResolvedValue(statistics);
|
personMock.getStatistics.mockResolvedValue(statistics);
|
||||||
accessMock.person.checkOwnerAccess.mockResolvedValue(new Set(['person-1']));
|
accessMock.person.checkOwnerAccess.mockResolvedValue(new Set(['person-1']));
|
||||||
await expect(sut.getStatistics(authStub.admin, 'person-1')).resolves.toEqual({ assets: 3 });
|
await expect(sut.getStatistics(authStub.admin, 'person-1', {})).resolves.toEqual({ assets: 3 });
|
||||||
expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1']));
|
expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1']));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should require person.read permission', async () => {
|
it('should require person.read permission', async () => {
|
||||||
personMock.getById.mockResolvedValue(personStub.primaryPerson);
|
personMock.getById.mockResolvedValue(personStub.primaryPerson);
|
||||||
await expect(sut.getStatistics(authStub.admin, 'person-1')).rejects.toBeInstanceOf(BadRequestException);
|
await expect(sut.getStatistics(authStub.admin, 'person-1', {})).rejects.toBeInstanceOf(BadRequestException);
|
||||||
expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1']));
|
expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1']));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -14,6 +14,7 @@ import {
|
|||||||
PersonResponseDto,
|
PersonResponseDto,
|
||||||
PersonSearchDto,
|
PersonSearchDto,
|
||||||
PersonStatisticsResponseDto,
|
PersonStatisticsResponseDto,
|
||||||
|
PersonStatsDto,
|
||||||
PersonUpdateDto,
|
PersonUpdateDto,
|
||||||
mapFaces,
|
mapFaces,
|
||||||
mapPerson,
|
mapPerson,
|
||||||
@ -153,9 +154,9 @@ export class PersonService extends BaseService {
|
|||||||
return this.findOrFail(id).then(mapPerson);
|
return this.findOrFail(id).then(mapPerson);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getStatistics(auth: AuthDto, id: string): Promise<PersonStatisticsResponseDto> {
|
async getStatistics(auth: AuthDto, id: string, dto: PersonStatsDto): Promise<PersonStatisticsResponseDto> {
|
||||||
await this.requireAccess({ auth, permission: Permission.PERSON_READ, ids: [id] });
|
await this.requireAccess({ auth, permission: Permission.PERSON_READ, ids: [id] });
|
||||||
return this.personRepository.getStatistics(id);
|
return this.personRepository.getStatistics(id, dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getThumbnail(auth: AuthDto, id: string): Promise<ImmichFileResponse> {
|
async getThumbnail(auth: AuthDto, id: string): Promise<ImmichFileResponse> {
|
||||||
@ -184,7 +185,7 @@ export class PersonService extends BaseService {
|
|||||||
async update(auth: AuthDto, id: string, dto: PersonUpdateDto): Promise<PersonResponseDto> {
|
async update(auth: AuthDto, id: string, dto: PersonUpdateDto): Promise<PersonResponseDto> {
|
||||||
await this.requireAccess({ auth, permission: Permission.PERSON_UPDATE, ids: [id] });
|
await this.requireAccess({ auth, permission: Permission.PERSON_UPDATE, ids: [id] });
|
||||||
|
|
||||||
const { name, birthDate, isHidden, featureFaceAssetId: assetId } = dto;
|
const { name, birthDate, isHidden, featureFaceAssetId: assetId, withArchived } = dto;
|
||||||
// TODO: set by faceId directly
|
// TODO: set by faceId directly
|
||||||
let faceId: string | undefined = undefined;
|
let faceId: string | undefined = undefined;
|
||||||
if (assetId) {
|
if (assetId) {
|
||||||
@ -197,7 +198,14 @@ export class PersonService extends BaseService {
|
|||||||
faceId = face.id;
|
faceId = face.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
const person = await this.personRepository.update({ id, faceAssetId: faceId, name, birthDate, isHidden });
|
const person = await this.personRepository.update({
|
||||||
|
id,
|
||||||
|
faceAssetId: faceId,
|
||||||
|
name,
|
||||||
|
birthDate,
|
||||||
|
isHidden,
|
||||||
|
withArchived,
|
||||||
|
});
|
||||||
|
|
||||||
if (assetId) {
|
if (assetId) {
|
||||||
await this.jobRepository.queue({ name: JobName.GENERATE_PERSON_THUMBNAIL, data: { id } });
|
await this.jobRepository.queue({ name: JobName.GENERATE_PERSON_THUMBNAIL, data: { id } });
|
||||||
|
9
server/test/fixtures/person.stub.ts
vendored
9
server/test/fixtures/person.stub.ts
vendored
@ -15,6 +15,7 @@ export const personStub = {
|
|||||||
faceAssetId: null,
|
faceAssetId: null,
|
||||||
faceAsset: null,
|
faceAsset: null,
|
||||||
isHidden: false,
|
isHidden: false,
|
||||||
|
withArchived: false,
|
||||||
}),
|
}),
|
||||||
hidden: Object.freeze<PersonEntity>({
|
hidden: Object.freeze<PersonEntity>({
|
||||||
id: 'person-1',
|
id: 'person-1',
|
||||||
@ -29,6 +30,7 @@ export const personStub = {
|
|||||||
faceAssetId: null,
|
faceAssetId: null,
|
||||||
faceAsset: null,
|
faceAsset: null,
|
||||||
isHidden: true,
|
isHidden: true,
|
||||||
|
withArchived: false,
|
||||||
}),
|
}),
|
||||||
withName: Object.freeze<PersonEntity>({
|
withName: Object.freeze<PersonEntity>({
|
||||||
id: 'person-1',
|
id: 'person-1',
|
||||||
@ -43,6 +45,7 @@ export const personStub = {
|
|||||||
faceAssetId: 'assetFaceId',
|
faceAssetId: 'assetFaceId',
|
||||||
faceAsset: null,
|
faceAsset: null,
|
||||||
isHidden: false,
|
isHidden: false,
|
||||||
|
withArchived: false,
|
||||||
}),
|
}),
|
||||||
withBirthDate: Object.freeze<PersonEntity>({
|
withBirthDate: Object.freeze<PersonEntity>({
|
||||||
id: 'person-1',
|
id: 'person-1',
|
||||||
@ -57,6 +60,7 @@ export const personStub = {
|
|||||||
faceAssetId: null,
|
faceAssetId: null,
|
||||||
faceAsset: null,
|
faceAsset: null,
|
||||||
isHidden: false,
|
isHidden: false,
|
||||||
|
withArchived: false,
|
||||||
}),
|
}),
|
||||||
noThumbnail: Object.freeze<PersonEntity>({
|
noThumbnail: Object.freeze<PersonEntity>({
|
||||||
id: 'person-1',
|
id: 'person-1',
|
||||||
@ -71,6 +75,7 @@ export const personStub = {
|
|||||||
faceAssetId: null,
|
faceAssetId: null,
|
||||||
faceAsset: null,
|
faceAsset: null,
|
||||||
isHidden: false,
|
isHidden: false,
|
||||||
|
withArchived: false,
|
||||||
}),
|
}),
|
||||||
newThumbnail: Object.freeze<PersonEntity>({
|
newThumbnail: Object.freeze<PersonEntity>({
|
||||||
id: 'person-1',
|
id: 'person-1',
|
||||||
@ -85,6 +90,7 @@ export const personStub = {
|
|||||||
faceAssetId: 'asset-id',
|
faceAssetId: 'asset-id',
|
||||||
faceAsset: null,
|
faceAsset: null,
|
||||||
isHidden: false,
|
isHidden: false,
|
||||||
|
withArchived: false,
|
||||||
}),
|
}),
|
||||||
primaryPerson: Object.freeze<PersonEntity>({
|
primaryPerson: Object.freeze<PersonEntity>({
|
||||||
id: 'person-1',
|
id: 'person-1',
|
||||||
@ -99,6 +105,7 @@ export const personStub = {
|
|||||||
faceAssetId: null,
|
faceAssetId: null,
|
||||||
faceAsset: null,
|
faceAsset: null,
|
||||||
isHidden: false,
|
isHidden: false,
|
||||||
|
withArchived: false,
|
||||||
}),
|
}),
|
||||||
mergePerson: Object.freeze<PersonEntity>({
|
mergePerson: Object.freeze<PersonEntity>({
|
||||||
id: 'person-2',
|
id: 'person-2',
|
||||||
@ -113,6 +120,7 @@ export const personStub = {
|
|||||||
faceAssetId: null,
|
faceAssetId: null,
|
||||||
faceAsset: null,
|
faceAsset: null,
|
||||||
isHidden: false,
|
isHidden: false,
|
||||||
|
withArchived: false,
|
||||||
}),
|
}),
|
||||||
randomPerson: Object.freeze<PersonEntity>({
|
randomPerson: Object.freeze<PersonEntity>({
|
||||||
id: 'person-3',
|
id: 'person-3',
|
||||||
@ -127,5 +135,6 @@ export const personStub = {
|
|||||||
faceAssetId: null,
|
faceAssetId: null,
|
||||||
faceAsset: null,
|
faceAsset: null,
|
||||||
isHidden: false,
|
isHidden: false,
|
||||||
|
withArchived: false,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
@ -1140,6 +1140,7 @@
|
|||||||
"show_albums": "Show albums",
|
"show_albums": "Show albums",
|
||||||
"show_all_people": "Show all people",
|
"show_all_people": "Show all people",
|
||||||
"show_and_hide_people": "Show & hide people",
|
"show_and_hide_people": "Show & hide people",
|
||||||
|
"show_archived_or_unarchived_assets": "{with, select, true {With} other {Without}} archived assets",
|
||||||
"show_file_location": "Show file location",
|
"show_file_location": "Show file location",
|
||||||
"show_gallery": "Show gallery",
|
"show_gallery": "Show gallery",
|
||||||
"show_hidden_people": "Show hidden people",
|
"show_hidden_people": "Show hidden people",
|
||||||
|
@ -45,6 +45,8 @@
|
|||||||
import {
|
import {
|
||||||
mdiAccountBoxOutline,
|
mdiAccountBoxOutline,
|
||||||
mdiAccountMultipleCheckOutline,
|
mdiAccountMultipleCheckOutline,
|
||||||
|
mdiArchiveArrowDown,
|
||||||
|
mdiArchiveArrowDownOutline,
|
||||||
mdiArrowLeft,
|
mdiArrowLeft,
|
||||||
mdiCalendarEditOutline,
|
mdiCalendarEditOutline,
|
||||||
mdiDotsVertical,
|
mdiDotsVertical,
|
||||||
@ -72,8 +74,10 @@
|
|||||||
UNASSIGN_ASSETS = 'unassign-faces',
|
UNASSIGN_ASSETS = 'unassign-faces',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$: isArchived = person.withArchived ? undefined : person.withArchived;
|
||||||
|
|
||||||
let assetStore = new AssetStore({
|
let assetStore = new AssetStore({
|
||||||
isArchived: false,
|
isArchived,
|
||||||
personId: data.person.id,
|
personId: data.person.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -81,7 +85,7 @@
|
|||||||
$: thumbnailData = getPeopleThumbnailUrl(person);
|
$: thumbnailData = getPeopleThumbnailUrl(person);
|
||||||
$: if (person) {
|
$: if (person) {
|
||||||
handlePromiseError(updateAssetCount());
|
handlePromiseError(updateAssetCount());
|
||||||
handlePromiseError(assetStore.updateOptions({ personId: person.id }));
|
handlePromiseError(assetStore.updateOptions({ personId: person.id, isArchived }));
|
||||||
}
|
}
|
||||||
|
|
||||||
const assetInteractionStore = createAssetInteractionStore();
|
const assetInteractionStore = createAssetInteractionStore();
|
||||||
@ -338,6 +342,27 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleToggleWithArhived = async () => {
|
||||||
|
const withArchived = !person.withArchived;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await updatePerson({
|
||||||
|
id: person.id,
|
||||||
|
personUpdateDto: { withArchived },
|
||||||
|
});
|
||||||
|
|
||||||
|
data.person.withArchived = withArchived;
|
||||||
|
refreshAssetGrid = !refreshAssetGrid;
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error, $t('errors.unable_to_archive_unarchive', { values: { archived: withArchived } }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteAssets = async (assetIds: string[]) => {
|
||||||
|
$assetStore.removeAssets(assetIds);
|
||||||
|
await updateAssetCount();
|
||||||
|
};
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
assetStore.destroy();
|
assetStore.destroy();
|
||||||
});
|
});
|
||||||
@ -395,7 +420,7 @@
|
|||||||
<ChangeDate menuItem />
|
<ChangeDate menuItem />
|
||||||
<ChangeLocation menuItem />
|
<ChangeLocation menuItem />
|
||||||
<ArchiveAction menuItem unarchive={isAllArchive} onArchive={(assetIds) => $assetStore.removeAssets(assetIds)} />
|
<ArchiveAction menuItem unarchive={isAllArchive} onArchive={(assetIds) => $assetStore.removeAssets(assetIds)} />
|
||||||
<DeleteAssets menuItem onAssetDelete={(assetIds) => $assetStore.removeAssets(assetIds)} />
|
<DeleteAssets menuItem onAssetDelete={handleDeleteAssets} />
|
||||||
</ButtonContextMenu>
|
</ButtonContextMenu>
|
||||||
</AssetSelectControlBar>
|
</AssetSelectControlBar>
|
||||||
{:else}
|
{:else}
|
||||||
@ -423,6 +448,11 @@
|
|||||||
icon={mdiAccountMultipleCheckOutline}
|
icon={mdiAccountMultipleCheckOutline}
|
||||||
onClick={() => (viewMode = ViewMode.MERGE_PEOPLE)}
|
onClick={() => (viewMode = ViewMode.MERGE_PEOPLE)}
|
||||||
/>
|
/>
|
||||||
|
<MenuOption
|
||||||
|
text={$t('show_archived_or_unarchived_assets', { values: { with: !person.withArchived } })}
|
||||||
|
icon={person.withArchived ? mdiArchiveArrowDown : mdiArchiveArrowDownOutline}
|
||||||
|
onClick={handleToggleWithArhived}
|
||||||
|
/>
|
||||||
</ButtonContextMenu>
|
</ButtonContextMenu>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</ControlAppBar>
|
</ControlAppBar>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user