Speaker repo

Add an own repository service to manage speakers
This commit is contained in:
Sean Engelhardt 2019-02-28 14:26:31 +01:00
parent 77e192719d
commit 03c590c66d
6 changed files with 209 additions and 117 deletions

View File

@ -11,14 +11,11 @@ import { HttpService } from 'app/core/core-services/http.service';
import { Identifiable } from 'app/shared/models/base/identifiable'; import { Identifiable } from 'app/shared/models/base/identifiable';
import { Item } from 'app/shared/models/agenda/item'; import { Item } from 'app/shared/models/agenda/item';
import { OSTreeSortEvent } from 'app/shared/components/sorting-tree/sorting-tree.component'; import { OSTreeSortEvent } from 'app/shared/components/sorting-tree/sorting-tree.component';
import { Speaker } from 'app/shared/models/agenda/speaker';
import { ViewItem } from 'app/site/agenda/models/view-item'; import { ViewItem } from 'app/site/agenda/models/view-item';
import { ViewSpeaker } from 'app/site/agenda/models/view-speaker';
import { TreeService } from 'app/core/ui-services/tree.service'; import { TreeService } from 'app/core/ui-services/tree.service';
import { BaseAgendaViewModel } from 'app/site/base/base-agenda-view-model'; import { BaseAgendaViewModel } from 'app/site/base/base-agenda-view-model';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { BaseViewModel } from 'app/site/base/base-view-model'; import { BaseViewModel } from 'app/site/base/base-view-model';
import { ViewUser } from 'app/site/users/models/view-user';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object-repository'; import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object-repository';
@ -130,105 +127,6 @@ export class ItemRepositoryService extends BaseRepository<ViewItem, Item> {
} }
} }
/**
* Generate viewSpeaker objects from a given agenda Item
*
* @param item agenda Item holding speakers
* @returns the list of view speakers corresponding to the given item
*/
public createViewSpeakers(item: Item): ViewSpeaker[] {
let viewSpeakers = [];
const speakers = item.speakers;
if (speakers && speakers.length > 0) {
speakers.forEach((speaker: Speaker) => {
const user = this.viewModelStoreService.get(ViewUser, speaker.user_id);
viewSpeakers.push(new ViewSpeaker(speaker, user));
});
}
// sort speakers by their weight
viewSpeakers = viewSpeakers.sort((a, b) => a.weight - b.weight);
return viewSpeakers;
}
/**
* Add a new speaker to an agenda item.
* Sends the users ID to the server
* Might need another repo
*
* @param speakerId {@link User} id of the new speaker
* @param item the target agenda item
*/
public async addSpeaker(speakerId: number, item: ViewItem): Promise<void> {
const restUrl = `rest/agenda/item/${item.id}/manage_speaker/`;
await this.httpService.post<Identifiable>(restUrl, { user: speakerId });
}
/**
* Sets the given speaker ID to Speak
*
* @param speakerId the speakers id
* @param item the target agenda item
*/
public async startSpeaker(speakerId: number, item: ViewItem): Promise<void> {
const restUrl = `rest/agenda/item/${item.id}/speak/`;
await this.httpService.put(restUrl, { speaker: speakerId });
}
/**
* Stops the current speaker
*
* @param item the target agenda item
*/
public async stopCurrentSpeaker(item: ViewItem): Promise<void> {
const restUrl = `rest/agenda/item/${item.id}/speak/`;
await this.httpService.delete(restUrl);
}
/**
* Marks the current speaker
*
* @param speakerId {@link User} id of the new speaker
* @param mark determine if the user was marked or not
* @param item the target agenda item
*/
public async markSpeaker(speakerId: number, mark: boolean, item: ViewItem): Promise<void> {
const restUrl = `rest/agenda/item/${item.id}/manage_speaker/`;
await this.httpService.patch(restUrl, { user: speakerId, marked: mark });
}
/**
* Deletes the given speaker for the agenda item
*
* @param item the target agenda item
* @param speakerId (otional) the speakers id. If no id is given, the current operator
* is removed.
*/
public async deleteSpeaker(item: ViewItem, speakerId?: number): Promise<void> {
const restUrl = `rest/agenda/item/${item.id}/manage_speaker/`;
await this.httpService.delete(restUrl, speakerId ? { speaker: speakerId } : null);
}
/**
* Deletes all speakers of the given agenda item.
*
* @param item the target agenda item
*/
public async deleteAllSpeakers(item: ViewItem): Promise<void> {
const restUrl = `rest/agenda/item/${item.id}/manage_speaker/`;
await this.httpService.delete(restUrl, { speaker: item.speakers.map(speaker => speaker.id) });
}
/**
* Posts an (manually) sorted speaker list to the server
*
* @param speakerIds array of speaker id numbers
* @param Item the target agenda item
*/
public async sortSpeakers(speakerIds: number[], item: Item): Promise<void> {
const restUrl = `rest/agenda/item/${item.id}/sort_speakers/`;
await this.httpService.post(restUrl, { speakers: speakerIds });
}
/** /**
* Updates an agenda item * Updates an agenda item
* *

View File

@ -0,0 +1,17 @@
import { TestBed } from '@angular/core/testing';
import { SpeakerRepositoryService } from './speaker-repository.service';
import { E2EImportsModule } from 'e2e-imports.module';
describe('SpeakerRepositoryService', () => {
beforeEach(() =>
TestBed.configureTestingModule({
imports: [E2EImportsModule]
})
);
it('should be created', () => {
const service: SpeakerRepositoryService = TestBed.get(SpeakerRepositoryService);
expect(service).toBeTruthy();
});
});

View File

@ -0,0 +1,163 @@
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { HttpService } from 'app/core/core-services/http.service';
import { Identifiable } from 'app/shared/models/base/identifiable';
import { Item } from 'app/shared/models/agenda/item';
import { Speaker } from 'app/shared/models/agenda/speaker';
import { ViewSpeaker } from 'app/site/agenda/models/view-speaker';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { ViewItem } from 'app/site/agenda/models/view-item';
import { ViewUser } from 'app/site/users/models/view-user';
/**
* Define what actions might occur on speaker lists
*/
type SpeakerAction = 'manage_speaker' | 'sort_speakers' | 'speak';
/**
* Repository for speakers.
*
* Since speakers are no base models, normal repository methods do not apply here.
*/
@Injectable({
providedIn: 'root'
})
export class SpeakerRepositoryService {
/**
* Constructor
*
* @param viewModelStoreService To get the view users
* @param httpService make custom requests
* @param translate translate
*/
public constructor(
private viewModelStoreService: ViewModelStoreService,
private httpService: HttpService,
private translate: TranslateService
) {}
/**
* Add a new speaker to an agenda item.
* Sends the users ID to the server
* Might need another repo
*
* @param speakerId {@link User} id of the new speaker
* @param item the target agenda item
*/
public async create(speakerId: number, item: ViewItem): Promise<void> {
const restUrl = this.getRestUrl(item.id, 'manage_speaker');
await this.httpService.post<Identifiable>(restUrl, { user: speakerId });
}
/**
* Removes the given speaker for the agenda item
*
* @param item the target agenda item
* @param speakerId (otional) the speakers id. If no id is given, the current operator
* is removed.
*/
public async delete(item: ViewItem, speakerId?: number): Promise<void> {
const restUrl = this.getRestUrl(item.id, 'manage_speaker');
await this.httpService.delete(restUrl, speakerId ? { speaker: speakerId } : null);
}
/**
* Creates and returns a new ViewSpeaker out of a speaker model
*
* @param speaker speaker to transform
* @return a new ViewSpeaker
*/
private createViewModel(speaker: Speaker): ViewSpeaker {
const user = this.viewModelStoreService.get(ViewUser, speaker.user_id);
const viewSpeaker = new ViewSpeaker(speaker, user);
viewSpeaker.getVerboseName = (plural: boolean = false) => {
return this.translate.instant(plural ? 'Speakers' : 'Speaker');
};
return viewSpeaker;
}
/**
* Generate viewSpeaker objects from a given agenda Item
*
* @param item agenda Item holding speakers
* @returns the list of view speakers corresponding to the given item
*/
public createSpeakerList(item: Item): ViewSpeaker[] {
let viewSpeakers = [];
const speakers = item.speakers;
if (speakers && speakers.length > 0) {
viewSpeakers = speakers.map(speaker => {
return this.createViewModel(speaker);
});
}
// sort speakers by their weight
viewSpeakers = viewSpeakers.sort((a, b) => a.weight - b.weight);
return viewSpeakers;
}
/**
* Deletes all speakers of the given agenda item.
*
* @param item the target agenda item
*/
public async deleteAllSpeakers(item: ViewItem): Promise<void> {
const restUrl = this.getRestUrl(item.id, 'manage_speaker');
await this.httpService.delete(restUrl, { speaker: item.speakers.map(speaker => speaker.id) });
}
/**
* Posts an (manually) sorted speaker list to the server
*
* @param speakerIds array of speaker id numbers
* @param Item the target agenda item
*/
public async sortSpeakers(speakerIds: number[], item: Item): Promise<void> {
const restUrl = this.getRestUrl(item.id, 'sort_speakers');
await this.httpService.post(restUrl, { speakers: speakerIds });
}
/**
* Marks the current speaker
*
* @param speakerId {@link User} id of the new speaker
* @param mark determine if the user was marked or not
* @param item the target agenda item
*/
public async markSpeaker(speakerId: number, mark: boolean, item: ViewItem): Promise<void> {
const restUrl = this.getRestUrl(item.id, 'manage_speaker');
await this.httpService.patch(restUrl, { user: speakerId, marked: mark });
}
/**
* Stops the current speaker
*
* @param item the target agenda item
*/
public async stopCurrentSpeaker(item: ViewItem): Promise<void> {
const restUrl = this.getRestUrl(item.id, 'speak');
await this.httpService.delete(restUrl);
}
/**
* Sets the given speaker ID to Speak
*
* @param speakerId the speakers id
* @param item the target agenda item
*/
public async startSpeaker(speakerId: number, item: ViewItem): Promise<void> {
const restUrl = this.getRestUrl(item.id, 'speak');
await this.httpService.put(restUrl, { speaker: speakerId });
}
/**
* Helper function get the url to the speaker rest address
*
* @param itemId id of the agenda item
* @param suffix the desired speaker action
*/
private getRestUrl(itemId: number, suffix: SpeakerAction): string {
return `rest/agenda/item/${itemId}/${suffix}/`;
}
}

View File

@ -17,6 +17,8 @@ export enum SpeakerState {
* @ignore * @ignore
*/ */
export class Speaker extends Deserializer { export class Speaker extends Deserializer {
public static COLLECTIONSTRING = 'agenda/item/speakers';
public id: number; public id: number;
public user_id: number; public user_id: number;

View File

@ -40,8 +40,8 @@
<span>{{ number + 1 }}. {{ speaker }}</span> <span>{{ number + 1 }}. {{ speaker }}</span>
</div> </div>
<div class="finished-suffix"> <div class="finished-suffix">
&nbsp;&nbsp; {{ durationString(speaker) }} &nbsp;&nbsp; {{ durationString(speaker) }} ({{ 'Start time' | translate }}:
({{ 'Start time' | translate }}: {{ startTimeToString(speaker) }}) {{ startTimeToString(speaker) }})
</div> </div>
<button <button
mat-stroked-button mat-stroked-button
@ -131,7 +131,11 @@
</mat-card> </mat-card>
<mat-menu #speakerMenu="matMenu"> <mat-menu #speakerMenu="matMenu">
<os-projector-button *ngIf="viewItem" [object]="viewItem.listOfSpeakersSlide" [menuItem]="true"></os-projector-button> <os-projector-button
*ngIf="viewItem"
[object]="viewItem.listOfSpeakersSlide"
[menuItem]="true"
></os-projector-button>
<button mat-menu-item *ngIf="closedList" (click)="openSpeakerList()"> <button mat-menu-item *ngIf="closedList" (click)="openSpeakerList()">
<mat-icon>mic</mat-icon> <mat-icon>mic</mat-icon>

View File

@ -7,12 +7,12 @@ import { Title } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { BehaviorSubject, Subscription } from 'rxjs'; import { BehaviorSubject, Subscription } from 'rxjs';
import { ItemRepositoryService } from 'app/core/repositories/agenda/item-repository.service';
import { BaseViewComponent } from 'app/site/base/base-view'; import { BaseViewComponent } from 'app/site/base/base-view';
import { OperatorService } from 'app/core/core-services/operator.service'; import { OperatorService } from 'app/core/core-services/operator.service';
import { ProjectorRepositoryService } from 'app/core/repositories/projector/projector-repository.service'; import { ProjectorRepositoryService } from 'app/core/repositories/projector/projector-repository.service';
import { PromptService } from 'app/core/ui-services/prompt.service'; import { PromptService } from 'app/core/ui-services/prompt.service';
import { SpeakerState } from 'app/shared/models/agenda/speaker'; import { SpeakerState } from 'app/shared/models/agenda/speaker';
import { SpeakerRepositoryService } from 'app/core/repositories/agenda/speaker-repository.service';
import { ViewItem } from '../../models/view-item'; import { ViewItem } from '../../models/view-item';
import { ViewSpeaker } from '../../models/view-speaker'; import { ViewSpeaker } from '../../models/view-speaker';
import { ViewProjector } from 'app/site/projector/models/view-projector'; import { ViewProjector } from 'app/site/projector/models/view-projector';
@ -20,6 +20,7 @@ import { ViewUser } from 'app/site/users/models/view-user';
import { UserRepositoryService } from 'app/core/repositories/users/user-repository.service'; import { UserRepositoryService } from 'app/core/repositories/users/user-repository.service';
import { DurationService } from 'app/core/ui-services/duration.service'; import { DurationService } from 'app/core/ui-services/duration.service';
import { CurrentAgendaItemService } from 'app/site/projector/services/current-agenda-item.service'; import { CurrentAgendaItemService } from 'app/site/projector/services/current-agenda-item.service';
import { ItemRepositoryService } from 'app/core/repositories/agenda/item-repository.service';
/** /**
* The list of speakers for agenda items. * The list of speakers for agenda items.
@ -101,7 +102,8 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
* @param projectorRepo * @param projectorRepo
* @param route Angulars ActivatedRoute * @param route Angulars ActivatedRoute
* @param DS the DataStore * @param DS the DataStore
* @param itemRepo Repository fpr agenda items * @param repo Repository for speakers
* @param itemRepo Repository for agendaItems
* @param op the current operator * @param op the current operator
* @param promptService * @param promptService
* @param currentAgendaItemService * @param currentAgendaItemService
@ -113,6 +115,7 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
snackBar: MatSnackBar, snackBar: MatSnackBar,
projectorRepo: ProjectorRepositoryService, projectorRepo: ProjectorRepositoryService,
private route: ActivatedRoute, private route: ActivatedRoute,
private repo: SpeakerRepositoryService,
private itemRepo: ItemRepositoryService, private itemRepo: ItemRepositoryService,
private op: OperatorService, private op: OperatorService,
private promptService: PromptService, private promptService: PromptService,
@ -144,7 +147,12 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
public ngOnInit(): void { public ngOnInit(): void {
// load and observe users // load and observe users
this.users = new BehaviorSubject(this.userRepository.getViewModelList()); this.users = new BehaviorSubject(this.userRepository.getViewModelList());
this.userRepository.getSortedViewModelListObservable().subscribe(users => this.users.next(users)); this.userRepository.getSortedViewModelListObservable().subscribe(users => {
this.users.next(users);
if (this.viewItem) {
this.setSpeakerList(this.viewItem.id);
}
});
// detect changes in the form // detect changes in the form
this.addSpeakerForm.valueChanges.subscribe(formResult => { this.addSpeakerForm.valueChanges.subscribe(formResult => {
@ -174,7 +182,7 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
} }
/** /**
* Shows the current lost of speakers of a given projector. * Shows the current list of speakers (CLOS) of a given projector.
* Triggers after mat-select-change * Triggers after mat-select-change
* *
* @param event Mat select change event, holds the projector in value * @param event Mat select change event, holds the projector in value
@ -212,7 +220,7 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
this.itemRepo.getViewModelObservable(id).subscribe(newAgendaItem => { this.itemRepo.getViewModelObservable(id).subscribe(newAgendaItem => {
if (newAgendaItem) { if (newAgendaItem) {
this.viewItem = newAgendaItem; this.viewItem = newAgendaItem;
const allSpeakers = this.itemRepo.createViewSpeakers(newAgendaItem.item); const allSpeakers = this.repo.createSpeakerList(newAgendaItem.item);
this.speakers = allSpeakers.filter(speaker => speaker.state === SpeakerState.WAITING); this.speakers = allSpeakers.filter(speaker => speaker.state === SpeakerState.WAITING);
this.finishedSpeakers = allSpeakers.filter(speaker => speaker.state === SpeakerState.FINISHED); this.finishedSpeakers = allSpeakers.filter(speaker => speaker.state === SpeakerState.FINISHED);
this.activeSpeaker = allSpeakers.find(speaker => speaker.state === SpeakerState.CURRENT); this.activeSpeaker = allSpeakers.find(speaker => speaker.state === SpeakerState.CURRENT);
@ -226,7 +234,7 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
* @param userId the user id to add to the list. No parameter adds the operators user as speaker. * @param userId the user id to add to the list. No parameter adds the operators user as speaker.
*/ */
public addNewSpeaker(userId?: number): void { public addNewSpeaker(userId?: number): void {
this.itemRepo.addSpeaker(userId, this.viewItem).then(() => this.addSpeakerForm.reset(), this.raiseError); this.repo.create(userId, this.viewItem).then(() => this.addSpeakerForm.reset(), this.raiseError);
} }
/** /**
@ -237,7 +245,7 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
public onSortingChange(listInNewOrder: ViewSpeaker[]): void { public onSortingChange(listInNewOrder: ViewSpeaker[]): void {
// extract the ids from the ViewSpeaker array // extract the ids from the ViewSpeaker array
const userIds = listInNewOrder.map(speaker => speaker.id); const userIds = listInNewOrder.map(speaker => speaker.id);
this.itemRepo.sortSpeakers(userIds, this.viewItem.item).then(null, this.raiseError); this.repo.sortSpeakers(userIds, this.viewItem.item).then(null, this.raiseError);
} }
/** /**
@ -246,14 +254,14 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
* @param item the speaker marked in the list * @param item the speaker marked in the list
*/ */
public onStartButton(item: ViewSpeaker): void { public onStartButton(item: ViewSpeaker): void {
this.itemRepo.startSpeaker(item.id, this.viewItem).then(null, this.raiseError); this.repo.startSpeaker(item.id, this.viewItem).then(null, this.raiseError);
} }
/** /**
* Click on the mic-cross button * Click on the mic-cross button
*/ */
public onStopButton(): void { public onStopButton(): void {
this.itemRepo.stopCurrentSpeaker(this.viewItem).then(null, this.raiseError); this.repo.stopCurrentSpeaker(this.viewItem).then(null, this.raiseError);
} }
/** /**
@ -262,7 +270,7 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
* @param item * @param item
*/ */
public onMarkButton(item: ViewSpeaker): void { public onMarkButton(item: ViewSpeaker): void {
this.itemRepo.markSpeaker(item.user.id, !item.marked, this.viewItem).then(null, this.raiseError); this.repo.markSpeaker(item.user.id, !item.marked, this.viewItem).then(null, this.raiseError);
} }
/** /**
@ -271,7 +279,7 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
* @param speaker * @param speaker
*/ */
public onDeleteButton(speaker?: ViewSpeaker): void { public onDeleteButton(speaker?: ViewSpeaker): void {
this.itemRepo.deleteSpeaker(this.viewItem, speaker ? speaker.id : null).then(null, this.raiseError); this.repo.delete(this.viewItem, speaker ? speaker.id : null).then(null, this.raiseError);
} }
/** /**
@ -318,7 +326,7 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
public async clearSpeakerList(): Promise<void> { public async clearSpeakerList(): Promise<void> {
const content = this.translate.instant('This will clear all speakers from the list.'); const content = this.translate.instant('This will clear all speakers from the list.');
if (await this.promptService.open('Are you sure?', content)) { if (await this.promptService.open('Are you sure?', content)) {
this.itemRepo.deleteAllSpeakers(this.viewItem); this.repo.deleteAllSpeakers(this.viewItem);
} }
} }