Skip to content

Commit 4e0a576

Browse files
Merge pull request #2149 from AletheiaFact/feature/identified-personalities-on-verification-request-visual
Feature: Displays personalities mentioned in verification requests with their avatars and descriptions
2 parents 6c9f760 + da5a9f9 commit 4e0a576

File tree

12 files changed

+773
-26
lines changed

12 files changed

+773
-26
lines changed

public/locales/en/verificationRequest.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,5 +84,12 @@
8484
"viewFullPage": "View full page",
8585
"automated_monitoring": "Automated Monitoring",
8686
"filterBySourceChannel": "Filter by Source Channel",
87-
"errorUpdatingStatus": "Error updating status:"
87+
"errorUpdatingStatus": "Error updating status:",
88+
"identifiedPersonalities": "People Mentioned",
89+
"loadingPersonalities": "Loading personalities...",
90+
"personalitiesList": "List of identified personalities",
91+
"noPersonalitiesFound": "No personalities identified in this request",
92+
"errorLoadingPersonalities": "Error loading personalities. Please try again.",
93+
"retryLoadingPersonalities": "Retry",
94+
"viewPersonalityPage": "View personality page for {{name}}"
8895
}

public/locales/pt/verificationRequest.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,5 +84,12 @@
8484
"viewFullPage": "Ver página completa",
8585
"automated_monitoring": "Monitoramento Automatizado",
8686
"filterBySourceChannel": "Filtrar por Canal de Origem",
87-
"errorUpdatingStatus": "Erro ao atualizar status:"
87+
"errorUpdatingStatus": "Erro ao atualizar status:",
88+
"identifiedPersonalities": "Pessoas Mencionadas",
89+
"loadingPersonalities": "Carregando personalidades...",
90+
"personalitiesList": "Lista de personalidades identificadas",
91+
"noPersonalitiesFound": "Nenhuma personalidade identificada nesta denúncia",
92+
"errorLoadingPersonalities": "Erro ao carregar personalidades. Por favor, tente novamente.",
93+
"retryLoadingPersonalities": "Tentar novamente",
94+
"viewPersonalityPage": "Ver página da personalidade {{name}}"
8895
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* DTO for personality data enriched with Wikidata information
3+
*/
4+
export interface PersonalityWithWikidataDto {
5+
/** Personality database ID */
6+
personalityId: string;
7+
8+
/** Personality name */
9+
name: string;
10+
11+
/** Personality URL slug */
12+
slug: string;
13+
14+
/** Personality description (from database or Wikidata) */
15+
description: string | null;
16+
17+
/** Wikidata avatar/image URL, null if not available */
18+
avatar: string | null;
19+
20+
/** Wikidata entity ID (e.g., Q22686), null if not linked to Wikidata */
21+
wikidata: string | null;
22+
}
23+
24+
/**
25+
* Type guard to check if a personality has a valid wikidata ID
26+
*/
27+
export function hasWikidataId(
28+
personality: PersonalityWithWikidataDto
29+
): personality is PersonalityWithWikidataDto & { wikidata: string } {
30+
return personality.wikidata !== null && personality.wikidata !== undefined;
31+
}

server/verification-request/verification-request.controller.ts

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import {
3333
CheckAbilities,
3434
} from "../auth/ability/ability.decorator";
3535
import { Roles } from "../auth/ability/ability.factory";
36+
import { WikidataService } from "../wikidata/wikidata.service";
37+
import { PersonalityWithWikidataDto } from "./dto/personality-with-wikidata.dto";
3638

3739
@Controller(":namespace?")
3840
export class VerificationRequestController {
@@ -44,7 +46,8 @@ export class VerificationRequestController {
4446
private viewService: ViewService,
4547
private reviewTaskService: ReviewTaskService,
4648
private captchaService: CaptchaService,
47-
private verificationRequestStateMachineService: VerificationRequestStateMachineService
49+
private readonly verificationRequestStateMachineService: VerificationRequestStateMachineService,
50+
private readonly wikidataService: WikidataService
4851
) {}
4952

5053
@ApiTags("verification-request")
@@ -117,6 +120,113 @@ export class VerificationRequestController {
117120
return this.verificationRequestService.getById(verificationRequestId);
118121
}
119122

123+
/**
124+
* Get personalities associated with a verification request, enriched with Wikidata information
125+
* @param verificationRequestId - The verification request ID
126+
* @param language - Language code for Wikidata labels (default: 'en')
127+
* @returns Array of personalities with Wikidata avatar and metadata
128+
*/
129+
@ApiTags("verification-request")
130+
@Get("api/verification-request/:id/personalities")
131+
@Header("Cache-Control", "max-age=60, must-revalidate")
132+
@Public()
133+
public async getPersonalitiesWithWikidata(
134+
@Param("id") verificationRequestId: string,
135+
@Query("language") language: string = "en"
136+
): Promise<PersonalityWithWikidataDto[]> {
137+
const verificationRequest =
138+
await this.verificationRequestService.getByIdWithPopulatedFields(
139+
verificationRequestId,
140+
["identifiedData"]
141+
);
142+
143+
if (
144+
!verificationRequest?.identifiedData ||
145+
verificationRequest.identifiedData.length === 0
146+
) {
147+
this.logger.debug(
148+
`No identifiedData found for VR ${verificationRequestId}`
149+
);
150+
return [];
151+
}
152+
153+
this.logger.debug(
154+
`Found ${verificationRequest.identifiedData.length} personalities for VR ${verificationRequestId}`
155+
);
156+
157+
const personalities: PersonalityWithWikidataDto[] = await Promise.all(
158+
verificationRequest.identifiedData.map(
159+
async (
160+
personality: any
161+
): Promise<PersonalityWithWikidataDto> => {
162+
if (!personality?.name) {
163+
this.logger.warn(
164+
`Personality missing name field for VR ${verificationRequestId}`
165+
);
166+
return {
167+
personalityId: personality._id?.toString() || "",
168+
name: "Unknown",
169+
slug: "",
170+
description: null,
171+
avatar: null,
172+
wikidata: null,
173+
};
174+
}
175+
176+
const baseData = {
177+
personalityId: personality._id.toString(),
178+
name: personality.name,
179+
slug: personality.slug || "",
180+
description: personality.description || null,
181+
};
182+
183+
if (!personality.wikidata) {
184+
this.logger.debug(
185+
`Personality ${personality.name} has no wikidata ID`
186+
);
187+
return {
188+
...baseData,
189+
avatar: null,
190+
wikidata: null,
191+
};
192+
}
193+
194+
try {
195+
const wikidataProps =
196+
await this.wikidataService.fetchProperties({
197+
wikidataId: personality.wikidata,
198+
language,
199+
});
200+
201+
return {
202+
...baseData,
203+
name: wikidataProps.name || personality.name,
204+
description:
205+
wikidataProps.description ||
206+
personality.description ||
207+
null,
208+
avatar: wikidataProps.avatar || null,
209+
wikidata: personality.wikidata,
210+
};
211+
} catch (error) {
212+
this.logger.error(
213+
`Error fetching wikidata for personality ${personality.name} (${personality.wikidata}):`,
214+
error.message
215+
);
216+
217+
return {
218+
...baseData,
219+
avatar: null,
220+
wikidata: personality.wikidata,
221+
};
222+
}
223+
}
224+
)
225+
);
226+
227+
return personalities;
228+
}
229+
120230
@ApiTags("verification-request")
121231
@Post("api/verification-request")
122232
async create(

server/verification-request/verification-request.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { AbilityModule } from "../auth/ability/ability.module";
2222
import { TopicModule } from "../topic/topic.module";
2323
import { PersonalityModule } from "../personality/personality.module";
2424
import { VerificationRequestStateMachineService } from "./state-machine/verification-request.state-machine.service";
25+
import { WikidataModule } from "../wikidata/wikidata.module";
2526

2627
const VerificationRequestModel = MongooseModule.forFeature([
2728
{
@@ -43,6 +44,7 @@ const VerificationRequestModel = MongooseModule.forFeature([
4344
AiTaskModule,
4445
CallbackDispatcherModule,
4546
AbilityModule,
47+
WikidataModule,
4648
TopicModule,
4749
PersonalityModule.register(),
4850
],

server/verification-request/verification-request.service.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,8 @@ export class VerificationRequestService {
605605
.populate("group")
606606
.populate("source")
607607
.populate("impactArea")
608-
.populate("topics");
608+
.populate("topics")
609+
.populate("identifiedData");
609610
}
610611

611612
return this.VerificationRequestModel.findOne({ data_hash });
@@ -924,9 +925,9 @@ export class VerificationRequestService {
924925
async updateVerificationRequestWithTopics(topics, data_hash) {
925926
const verificationRequest = await this.findByDataHash(data_hash, false);
926927
const foundTopics = await this.topicService.findByWikidataIds(
927-
topics.map(topic => topic.value || topic.wikidataId)
928+
topics.map((topic) => topic.value || topic.wikidataId)
928929
);
929-
const topicIds = foundTopics.map(topic => topic._id);
930+
const topicIds = foundTopics.map((topic) => topic._id);
930931

931932
const latestVerificationRequest = verificationRequest.toObject();
932933

@@ -972,7 +973,7 @@ export class VerificationRequestService {
972973
status,
973974
impactArea,
974975
startDate,
975-
endDate
976+
endDate,
976977
} = filters;
977978
const query: any = {};
978979

@@ -988,8 +989,7 @@ export class VerificationRequestService {
988989
Types.ObjectId(impactArea._id)
989990
);
990991

991-
if (topicIds.length)
992-
orConditions.push({ topics: { $in: topicIds } });
992+
if (topicIds.length) orConditions.push({ topics: { $in: topicIds } });
993993

994994
if (impactAreaIds.length)
995995
orConditions.push({ impactArea: { $in: impactAreaIds } });
@@ -1000,9 +1000,9 @@ export class VerificationRequestService {
10001000
}));
10011001
orConditions.push(...contentConditions);
10021002
}
1003-
1003+
10041004
const dateQuery = buildDateQuery(startDate, endDate);
1005-
1005+
10061006
if (dateQuery) query.date = dateQuery;
10071007

10081008
if (orConditions.length) {

0 commit comments

Comments
 (0)