Skip to content

Commit

Permalink
Realizacao do merge da branch 22-proximoVideoRecomendado
Browse files Browse the repository at this point in the history
US22 - Eu, como usuário, quero a opção de pular para o próximo vídeo recomendado, para ter uma experiência contínua de visualização.
  • Loading branch information
GabrielRoger07 authored Aug 30, 2024
2 parents 2448994 + 2e04299 commit 90b6bd9
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 98 deletions.
10 changes: 8 additions & 2 deletions src/app/pages/video-viewer/video-viewer.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ <h4 class="font-custom font-bold text-gray-800">
</h4>
<div class="button-container" *ngIf="userId!==''">
<button class="favorite-button" (click)="toggleFavorite()" [class.favorited]="isFavorite">
<img alt="coracao" src="../../../assets/coracao.svg" class="favorite-icon"/>
<img alt="coracao" src="../../../assets/coracao.svg" class="favorite-icon"/>
<span *ngIf="isFavorite"> Favoritado</span>
<span *ngIf="!isFavorite"> Favoritar </span>
</button>
</button>
<button class="watch-later-button" (click)="toggleWatchLater()" [ngClass]="{'watch-later-active': isWatchLater}">
{{ isWatchLater ? 'Remover da lista': 'Assistir Mais Tarde'}}
</button>
Expand All @@ -29,6 +29,12 @@ <h4 class="font-custom font-bold text-gray-800">
<img src="../../../assets/arrow.png" alt="Seta" class="arrow">
</button>
</div>
<div class="button-container" *ngIf="userId==''">
<button class="next-video-button" [title]="titleNextVideo" (click)="nextVideo()">
Próximo Vídeo
<img src="../../../assets/arrow.png" alt="Seta" class="arrow">
</button>
</div>
</div>
<div
class="max-w-full max-h-full flex text-[12px] font-custom text-gray-800"
Expand Down
40 changes: 34 additions & 6 deletions src/app/pages/video-viewer/video-viewer.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ class VideoServiceMock {
return of(new HttpResponse({ body: mockVideo }));
}

getRecommendationFromRecord(userId: string) {
const mockRecommendation = {
recommend_videos: {
1: 'Mock Video Recommendation 1',
2: 'Mock Video Recommendation 2',
},
};
return of(mockRecommendation);
}

//Procurar próximo vídeo
findAll() {
const mockResponse = {
Expand Down Expand Up @@ -399,13 +409,31 @@ describe('VideoViewerComponent', () => {
expect(component.recordVideos).toEqual(expectedResponse);
});

/*it('should check tracking status and set trackingEnabled correctly', fakeAsync(() => {
const mySpy = spyOn(videoService, 'checkTrackingStatus').and.returnValue(of({ track_enabled: true }));
it('should check tracking status and set trackingEnabled correctly', fakeAsync(() => {
const expectedResponse = { track_enabled: true };
const checkTrackingStatusSpy = spyOn(videoService, 'checkTrackingStatus').and.returnValue(of(expectedResponse));

component.userId = '1';
tick(); // Simulate passage of time for the async call
component.checkTrackingStatus().then(() => {
expect(mySpy).toHaveBeenCalledWith('1');
expect(checkTrackingStatusSpy).toHaveBeenCalledWith('1');
expect(component.trackingEnabled).toBe(true);
console.log('Tracking status:', component.trackingEnabled);
});

tick(); // Simula a passagem do tempo para a chamada assíncrona
}));

it('should handle checkTrackingStatus errors gracefully', fakeAsync(() => {
const checkTrackingStatusSpy = spyOn(videoService, 'checkTrackingStatus').and.returnValue(throwError('Error'));
const consoleErrorSpy = spyOn(console, 'error').and.callThrough(); // Usando callThrough para garantir que a função original ainda seja chamada

component.userId = '1';
component.checkTrackingStatus().catch(() => {
expect(checkTrackingStatusSpy).toHaveBeenCalledWith('1');
expect(component.trackingEnabled).toBeTrue();
});
}));*/
});

tick(); // Simula a passagem do tempo para a chamada assíncrona
}));

});
91 changes: 68 additions & 23 deletions src/app/pages/video-viewer/video-viewer.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ export class VideoViewerComponent implements OnInit {
idNextVideo: number;
titleNextVideo: any;
showTitleNextVideo: boolean = false;
recommendedVideos: any = [];
recommendedVideo: any;
trackingEnabled: boolean = true;

expandDescription() {
this.showDescription = !this.showDescription;
Expand All @@ -61,13 +64,20 @@ export class VideoViewerComponent implements OnInit {
if (this.authService.isAuthenticated()){
this.setUserIdFromToken(localStorage.getItem('token') as string);
this.getUserDetails();
this.checkTrackingStatus();
this.addRecord();
this.findVideoById();
iframe.src = this.eduplayVideoUrl + this.idVideo;
this.checkRecord();
this.checkRecommendation().then(() => {
this.findAll();
});
}else{
this.findVideoById();
iframe.src = this.eduplayVideoUrl + this.idVideo;
this.checkRecord();
this.findAll();
}

this.findVideoById();
iframe.src = this.eduplayVideoUrl + this.idVideo;
this.checkRecord();
this.findAll();
}

checkRecord(): Promise<void> {
Expand All @@ -85,34 +95,72 @@ export class VideoViewerComponent implements OnInit {
});
}

checkRecommendation(): Promise<void> {
return new Promise((resolve, reject) => {
this.videoService.getRecommendationFromRecord(this.userId.toString()).subscribe({
next: (response) => {
this.recommendedVideos = response;
resolve();
},
error: (err) => {
console.error('Error checking record', err);
reject(err);
}
});
});
}

async checkTrackingStatus(): Promise<void> {
const status = await this.videoService.checkTrackingStatus(this.userId).toPromise();
this.trackingEnabled = status.track_enabled;
console.log('Tracking status:', this.trackingEnabled);
}

//Função responsável por trazer todos os vídeos já assistidos pelo usuário
filterVideosByRecord(): void {
const keys = Object.keys(this.recordVideos.videos).map(id => parseInt(id, 10))
this.filteredVideos = this.unbTvVideos.filter(video => video.id !== undefined && keys.includes(video.id));
}

filterRecommendVideo(): void {
this.recommendedVideo = Object.values(this.recommendedVideos.recommend_videos)[0];
}

findAll(): void {
this.videoService.findAll().subscribe({
next: (data) => {
this.videosEduplay = data.body?.videoList ?? [];
this.filterVideosByChannel(this.videosEduplay);
this.videoService.videosCatalog(this.unbTvVideos, this.catalog)

//Loop para encontrar a categoria do vídeo atual
this.unbTvVideos.forEach((video) => {
if(video.id == this.idVideo){
this.categoryVideo = video.catalog
return;
if(this.authService.isAuthenticated() && this.trackingEnabled){
//Loop para encontrar a categoria do vídeo atual
this.unbTvVideos.forEach((video) => {
if(video.id == this.idVideo){
this.categoryVideo = video.catalog
return;
}
})
//Chamada de função para encontrar o programa do vídeo atual
this.program = this.videoService.findProgramName(this.catalog, this.categoryVideo, this.idVideo);

this.videosByCategory = this.videoService.filterVideosByCategory(this.unbTvVideos, this.categoryVideo);
this.filterVideosByRecord();
this.filterRecommendVideo();
this.idNextVideo = this.videoService.recommendVideo(this.videosByCategory, this.catalog, this.categoryVideo, this.filteredVideos, this.program, this.recommendedVideo);
}else{
for(const i in this.unbTvVideos){
if(this.unbTvVideos[i].id == this.idVideo){
if(Number(i) == (this.unbTvVideos.length - 1)){
this.idNextVideo = Number(this.unbTvVideos[0].id);
}else{
this.idNextVideo = Number(this.unbTvVideos[Number(i) + 1].id)
}
break;
}
}
})
//Chamada de função para encontrar o programa do vídeo atual
this.program = this.videoService.findProgramName(this.catalog, this.categoryVideo, this.idVideo);

this.videosByCategory = this.videoService.filterVideosByCategory(this.unbTvVideos, this.categoryVideo);
console.log("vídeos da categoria do atual: ", this.videosByCategory)
this.filterVideosByRecord();
console.log("videos assistidos: ", this.filteredVideos)
this.idNextVideo = this.videoService.recommendVideo(this.videosByCategory, this.catalog, this.categoryVideo, this.filteredVideos, this.program);
}
//Se o id for diferente de -1, o usuário ainda não viu todos os vídeos da categoria atual
if(this.idNextVideo != -1){
//Loop para encontrar o título do próximo vídeo
Expand All @@ -125,8 +173,6 @@ export class VideoViewerComponent implements OnInit {
}else{
this.titleNextVideo = "Não há vídeo para ser recomendado"
}
console.log("id do próximo vídeo: ", this.idNextVideo)
console.log("título do próximo vídeo: ", this.titleNextVideo)
},
error: (error) => {
console.log(error);
Expand All @@ -153,7 +199,7 @@ export class VideoViewerComponent implements OnInit {
window.location.reload();
});
}
}
}

setUserIdFromToken(token: string) {
const decodedToken: any = jwt_decode(token);
Expand Down Expand Up @@ -233,7 +279,6 @@ export class VideoViewerComponent implements OnInit {
}
});
}


// Favoritar
toggleFavorite() {
Expand Down Expand Up @@ -301,4 +346,4 @@ export class VideoViewerComponent implements OnInit {
console.warn('A API de compartilhamento não é suportada neste navegador.');
}
}
}
}
45 changes: 36 additions & 9 deletions src/app/services/video.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1266,7 +1266,8 @@ describe('toggleTracking', () => {
catalog,
'Jornalismo',
watchedVideos,
'falaJovem'
'falaJovem',
123456
);

expect(nextVideoId).toBe(2); // Informe UnB should be recommended next
Expand All @@ -1280,13 +1281,14 @@ describe('toggleTracking', () => {
catalog,
'Arte e Cultura',
watchedVideos,
'esbocos'
'esbocos',
123456
);

expect(nextVideoId).toBe(3); // Esboços: Artista should be recommended
});

it('should return -1 if all videos in the category are watched', () => {
it('should return the recommended video id if all videos in the category are watched', () => {
watchedVideos = [
{ id: 1, title: 'Fala, jovem', catalog: 'Jornalismo' } as IVideo,
{ id: 2, title: 'Informe UnB', catalog: 'Jornalismo' } as IVideo,
Expand All @@ -1299,23 +1301,48 @@ describe('toggleTracking', () => {
catalog,
'Jornalismo',
watchedVideos,
'falaJovem'
'falaJovem',
123456
);

expect(nextVideoId).toBe(-1); // No video left to recommend
expect(nextVideoId).toBe(123456); // No video left to recommend
});

it('should return -1 if the program or category does not exist', () => {
it('should return the recommended video id if the program or category does not exist', () => {
const nextVideoId = videoService.recommendVideo(
videos,
catalog,
'Nonexistent Category',
watchedVideos,
'nonexistentProgram'
'unbtv',
123456
);

expect(nextVideoId).toBe(-1); // Invalid category and program
expect(nextVideoId).toBe(123456); // Invalid category and program
});
});

describe('getRecommendationFromRecord', () => {
it('should get the recommendation from record for a user', () => {
const mockUserId = 'user123';
const mockResponse = {
recommend_videos: {
1: 'Mock Video Recommendation 1',
2: 'Mock Video Recommendation 2',
},
};

});
service.getRecommendationFromRecord(mockUserId).subscribe((response) => {
expect(response).toEqual(mockResponse);
});

const req = httpMock.expectOne(
`${environment.videoAPIURL}/recommendation/get_recommendation_record/?user_id=${mockUserId}`
);
expect(req.request.method).toBe('GET');

req.flush(mockResponse);
});
});

});
Loading

0 comments on commit 90b6bd9

Please sign in to comment.