Merge pull request #4221 from FinnStutzenstein/client-projector

projector cleanup
This commit is contained in:
Sean 2019-01-31 19:03:13 +01:00 committed by GitHub
commit c00b1a8325
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 194 additions and 91 deletions

View File

@ -39,41 +39,11 @@ export class ProjectorService extends OpenSlidesComponent {
}
/**
* Checks, if a given object is projected.
* Retusn the identifiable projector element from the given types of slides/elements/descriptors
*
* @param obj The object in question
* @returns true, if the object is projected on one projector.
* @param obj Something related to IdentifiableProjectorElement
* @returns the identifiable projector element from obj.
*/
public isProjected(obj: Projectable | ProjectorElementBuildDeskriptor): boolean {
if (isProjectable(obj)) {
return this.DS.getAll<Projector>('core/projector').some(projector => {
return projector.isElementShown(obj.getSlide().getBasicProjectorElement());
});
} else {
return this.DS.getAll<Projector>('core/projector').some(projector => {
return projector.isElementShown(obj.getBasicProjectorElement());
});
}
}
/**
* Get all projectors where the object is prejected on.
*
* @param obj The object in question
* @return All projectors, where this Object is projected on
*/
public getProjectorsWhichAreProjecting(obj: Projectable | ProjectorElementBuildDeskriptor): Projector[] {
if (isProjectable(obj)) {
return this.DS.getAll<Projector>('core/projector').filter(projector => {
return projector.isElementShown(obj.getSlide().getBasicProjectorElement());
});
} else {
return this.DS.getAll<Projector>('core/projector').filter(projector => {
return projector.isElementShown(obj.getBasicProjectorElement());
});
}
}
private getProjectorElement(
obj: Projectable | ProjectorElementBuildDeskriptor | IdentifiableProjectorElement
): IdentifiableProjectorElement {
@ -86,6 +56,34 @@ export class ProjectorService extends OpenSlidesComponent {
}
}
/**
* Checks, if a given object is projected.
*
* @param obj The object in question
* @returns true, if the object is projected on one projector.
*/
public isProjected(obj: Projectable | ProjectorElementBuildDeskriptor | IdentifiableProjectorElement): boolean {
const element = this.getProjectorElement(obj);
return this.DS.getAll<Projector>('core/projector').some(projector => {
return projector.isElementShown(element);
});
}
/**
* Get all projectors where the object is prejected on.
*
* @param obj The object in question
* @return All projectors, where this Object is projected on
*/
public getProjectorsWhichAreProjecting(
obj: Projectable | ProjectorElementBuildDeskriptor | IdentifiableProjectorElement
): Projector[] {
const element = this.getProjectorElement(obj);
return this.DS.getAll<Projector>('core/projector').filter(projector => {
return projector.isElementShown(element);
});
}
/**
* Checks, if the object is projected on the given projector.
*
@ -101,12 +99,8 @@ export class ProjectorService extends OpenSlidesComponent {
}
/**
* Projects the given ProjectorElement on the given projectors.
*
* TODO: this does not care about the element being stable. Some more logic must be added later.
*
* On the given projectors: Delete all non-stable elements and add the given element.
* On all other projectors: If the element (compared with name and id) is there, it will be deleted.
* Projects the given ProjectorElement on the given projectors. Removes the element
* from all non-given projectors
*
* @param projectors All projectors where to add the element.
* @param element The element in question.
@ -121,6 +115,13 @@ export class ProjectorService extends OpenSlidesComponent {
});
}
/**
* Projcets the given object on the projector. If the object is non-stable, all other non-stable
* elements will be removed and added to the history.
*
* @param projector The projector to add the object to.
* @param obj The object to project
*/
public async projectOn(
projector: Projector,
obj: Projectable | ProjectorElementBuildDeskriptor | IdentifiableProjectorElement
@ -147,26 +148,40 @@ export class ProjectorService extends OpenSlidesComponent {
}
}
/**
* Removes the given object from the projector. Non stable elements will be added to the history.
*
* @param projector The projector
* @param obj the object to unproject
*/
public async removeFrom(
projector: Projector,
obj: Projectable | ProjectorElementBuildDeskriptor | IdentifiableProjectorElement
): Promise<void> {
const element = this.getProjectorElement(obj);
if (element.stable) {
// Just remove this stable element
projector.removeElements(element);
await this.projectRequest(projector, projector.elements);
} else {
// For non-stable elements remove all current non-stable elements and add them to the history
const removedElements = projector.removeElements(element);
if (removedElements.length > 0) {
console.log(projector.elements, removedElements);
const removedElements = projector.removeElements(element);
if (removedElements.length > 0) {
if (element.stable) {
await this.projectRequest(projector, projector.elements);
} else {
// For non-stable elements: Add removed elements to the history.
await this.projectRequest(projector, projector.elements, null, removedElements);
}
}
}
/**
* Executes the request to change projector elements.
*
* Note: Just one of `appendToHistory` and `deleteLastHistoryElement` can be given.
*
* @param projector The affected projector
* @param elements (optional) Elements to set.
* @param preview (optional) preview to set
* @param appendToHistory (optional) Elements to be appended to the history
* @param deleteLastHistroyElement (optional) If given, the last history element will be removed.
*/
private async projectRequest(
projector: Projector,
elements?: ProjectorElements,
@ -206,21 +221,41 @@ export class ProjectorService extends OpenSlidesComponent {
});
}
/**
* Returns a model associated with the identifiable projector element. Throws an error,
* if the element is not mappable.
*
* @param element The projector element
* @returns the model from the projector element
*/
public getModelFromProjectorElement<T extends BaseModel>(element: IdentifiableProjectorElement): T {
if (!this.slideManager.canSlideBeMappedToModel(element.name)) {
throw new Error('THis projectorelement cannot be mapped to a model');
throw new Error('This projector element cannot be mapped to a model');
}
const identifiers = element.getIdentifiers();
if (!identifiers.includes('name') || !identifiers.includes('name')) {
if (!identifiers.includes('name') || !identifiers.includes('id')) {
throw new Error('To map this element to a model, a name and id is needed.');
}
return this.DS.get<T>(element.name, element.id);
}
/**
* Projects the next slide in the queue. Moves all currently projected
* non-stable slides to the history.
*
* @param projector The projector
*/
public async projectNextSlide(projector: Projector): Promise<void> {
await this.projectPreviewSlide(projector, 0);
}
/**
* Projects one slide (given by the index of the preview) on the given projector. Moves
* all current projected non-stable elements to the history.
*
* @param projector The projector
* @param previewIndex The index in the `elements_preview` array.
*/
public async projectPreviewSlide(projector: Projector, previewIndex: number): Promise<void> {
if (projector.elements_preview.length === 0 || previewIndex >= projector.elements_preview.length) {
return;
@ -231,6 +266,11 @@ export class ProjectorService extends OpenSlidesComponent {
await this.projectRequest(projector, projector.elements, projector.elements_preview, removedElements);
}
/**
* Projects the last slide of the history. This slide will be removed from the history.
*
* @param projector The projector
*/
public async projectPreviousSlide(projector: Projector): Promise<void> {
if (projector.elements_history.length === 0) {
return;
@ -253,10 +293,21 @@ export class ProjectorService extends OpenSlidesComponent {
await this.projectRequest(projector, projector.elements, projector.elements_preview, null, true);
}
/**
* Saves the preview of the projector
*
* @param projector The projector to save the preview.
*/
public async savePreview(projector: Projector): Promise<void> {
await this.projectRequest(projector, null, projector.elements_preview);
}
/**
* Appends the given element to the preview.
*
* @param projector The projector
* @param element The element to add to the preview.
*/
public async addElementToPreview(projector: Projector, element: ProjectorElement): Promise<void> {
projector.elements_preview.push(element);
await this.projectRequest(projector, null, projector.elements_preview);

View File

@ -5,6 +5,13 @@ import { HttpService } from './http.service';
import { environment } from 'environments/environment.prod';
import { BehaviorSubject, Observable } from 'rxjs';
/**
* This service provides the timeoffset to the server and a user of this service
* can query the servertime.
*
* This service needs to be started with `startScheduler` which will update
* the servertime frequently.
*/
@Injectable({
providedIn: 'root'
})
@ -13,7 +20,7 @@ export class ServertimeService extends OpenSlidesComponent {
private static NORMAL_TIMEOUT = 60 * 5;
/**
* In milliseconds
* The server offset in milliseconds
*/
private serverOffsetSubject = new BehaviorSubject<number>(0);
@ -21,27 +28,40 @@ export class ServertimeService extends OpenSlidesComponent {
super();
}
/**
* Starts the scheduler to sync with the server.
*/
public startScheduler(): void {
this.scheduleNextRefresh(0);
}
/**
* Get an observable for the server offset.
*/
public getServerOffsetObservable(): Observable<number> {
return this.serverOffsetSubject.asObservable();
}
/**
* Schedules the next sync with the server.
*
* @param seconds The timeout in seconds to the refresh.
*/
private scheduleNextRefresh(seconds: number): void {
setTimeout(async () => {
let timeout = ServertimeService.NORMAL_TIMEOUT;
try {
await this.refreshServertime();
} catch (e) {
console.log(e);
timeout = ServertimeService.FAILURE_TIMEOUT;
}
this.scheduleNextRefresh(timeout);
}, 1000 * seconds);
}
/**
* Queries the servertime and calculates the offset.
*/
private async refreshServertime(): Promise<void> {
// servertime is the time in seconds.
const servertime = await this.http.get<number>(environment.urlPrefix + '/core/servertime/');
@ -52,6 +72,9 @@ export class ServertimeService extends OpenSlidesComponent {
this.serverOffsetSubject.next(Math.floor(Date.now() - servertime * 1000));
}
/**
* Calculate the time of the server.
*/
public getServertime(): number {
return Date.now() - this.serverOffsetSubject.getValue();
}

View File

@ -1,10 +1,19 @@
import { Component, OnInit, Input } from '@angular/core';
import { Projectable, ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
import {
Projectable,
ProjectorElementBuildDeskriptor,
isProjectable,
isProjectorElementBuildDeskriptor
} from 'app/site/base/projectable';
import { ProjectionDialogService } from 'app/core/services/projection-dialog.service';
import { ProjectorService } from '../../../core/services/projector.service';
/**
* The projector button to project something on the projector.
*
* Use the input [object] to specify the object to project. It can either be
* a Projectable or a ProjectorElementBuildDeskriptor
*/
@Component({
selector: 'os-projector-button',
@ -12,11 +21,29 @@ import { ProjectorService } from '../../../core/services/projector.service';
styleUrls: ['./projector-button.component.scss']
})
export class ProjectorButtonComponent implements OnInit {
/**
* The object to project.
*/
private _object: Projectable | ProjectorElementBuildDeskriptor;
public get object(): Projectable | ProjectorElementBuildDeskriptor {
return this._object;
}
@Input()
public object: Projectable | ProjectorElementBuildDeskriptor;
public set object(obj: Projectable | ProjectorElementBuildDeskriptor) {
if (isProjectable(obj) || isProjectorElementBuildDeskriptor(obj)) {
this._object = obj;
} else {
console.error(
'Your model for the projectorbutton is not projectable and not' + 'a projectorElementBuildDescriptor!',
obj
);
}
}
/**
* The consotructor
* The constructor
*/
public constructor(
private projectionDialogService: ProjectionDialogService,
@ -28,6 +55,11 @@ export class ProjectorButtonComponent implements OnInit {
*/
public ngOnInit(): void {}
/**
* Click on the projector button
*
* @param event the click event
*/
public onClick(event: Event): void {
event.stopPropagation();
this.projectionDialogService.openProjectDialogFor(this.object);
@ -39,10 +71,9 @@ export class ProjectorButtonComponent implements OnInit {
* @returns true, if the object is projected on one projector.
*/
public isProjected(): boolean {
if (this.object) {
return this.projectorService.isProjected(this.object);
} else {
if (!this.object) {
return false;
}
return this.projectorService.isProjected(this.object);
}
}

View File

@ -84,8 +84,12 @@ export class Projector extends BaseModel<Projector> {
* @returns all removed unstable elements
*/
public removeAllNonStableElements(): ProjectorElements {
const unstableElements = this.elements.filter(element => !element.stable);
this.elements = this.elements.filter(element => element.stable);
let unstableElements: ProjectorElements;
let stableElements: ProjectorElements;
[unstableElements, stableElements] = this.partitionArray(this.elements, element => !element.stable);
this.elements = stableElements;
return unstableElements;
}
@ -99,8 +103,11 @@ export class Projector extends BaseModel<Projector> {
}
/**
* Must match everything. If a projectorelement does not have all keys
* to identify, it will be removed, if all existing keys match
* Removes and returns all projector elements, witch can be identified with the
* given element.
*
* @param element The element to remove
* @returns all removed projector elements
*/
public removeElements(element: IdentifiableProjectorElement): ProjectorElements {
let removedElements: ProjectorElements;
@ -114,6 +121,14 @@ export class Projector extends BaseModel<Projector> {
return removedElements;
}
/**
* Splits up the array into two arrays. All elements with a true return value from the callback
* will be in the fist array, all others in the second one.
*
* @param array The array to split
* @param callback To evaluate every entry
* @returns the splitted array
*/
private partitionArray<T>(array: T[], callback: (element: T) => boolean): [T[], T[]] {
return array.reduce(
(result, element) => {

View File

@ -69,7 +69,7 @@ export class CountdownListComponent extends BaseViewComponent implements OnInit
}
/**
* Add a new Section.
* Add a new countdown.
*/
public onPlusButton(): void {
if (!this.countdownToCreate) {

View File

@ -7,7 +7,7 @@ import { BaseViewComponent } from '../../../base/base-view';
import { MatSnackBar } from '@angular/material';
/**
* List view for the statute paragraphs.
* List view for the projector messages.
*/
@Component({
selector: 'os-projectormessage-list',
@ -22,7 +22,7 @@ export class ProjectorMessageListComponent extends BaseViewComponent implements
/**
* Init function.
*
* Sets the title and gets/observes countdowns from DataStore
* Sets the title and gets/observes messages from DataStore
*/
public ngOnInit(): void {
super.setTitle('Messages');

View File

@ -15,7 +15,7 @@ export class CurrentListOfSpeakersSlideService {
return {
name: overlay ? 'agenda/current-list-of-speakers-overlay' : 'agenda/current-list-of-speakers',
stable: overlay,
getIdentifiers: () => ['name', 'stable']
getIdentifiers: () => ['name']
};
}

View File

@ -46,7 +46,7 @@ export class ProjectorDataService {
Object.keys(update).forEach(_id => {
const id = parseInt(_id, 10);
if ((<{ error: string }>update[id]).error !== undefined) {
console.log('TODO: Why does the server sends errors on autpupdates?');
console.log('TODO: Why does the server sends errors on autoupdates?');
} else {
if (this.currentProjectorData[id]) {
this.currentProjectorData[id].next(update[id] as ProjectorData);

View File

@ -1,9 +1,7 @@
import { Component, OnInit, Input } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { BaseSlideComponent } from 'app/slides/base-slide-component';
import { HttpService } from 'app/core/services/http.service';
import { AgendaCurrentListOfSpeakersSlideData } from '../base/agenda-current-list-of-speakers-slide-data';
import { SlideData } from 'app/site/projector/services/projector-data.service';
@Component({
selector: 'os-agenda-current-list-of-speakers-slide',
@ -12,23 +10,8 @@ import { SlideData } from 'app/site/projector/services/projector-data.service';
})
export class AgendaCurrentListOfSpeakersSlideComponent extends BaseSlideComponent<AgendaCurrentListOfSpeakersSlideData>
implements OnInit {
private _data: SlideData<AgendaCurrentListOfSpeakersSlideData>;
public isOverlay: boolean;
public get data(): SlideData<AgendaCurrentListOfSpeakersSlideData> {
return this._data;
}
@Input()
public set data(value: SlideData<AgendaCurrentListOfSpeakersSlideData>) {
this.isOverlay = !value || value.element.stable;
this._data = value;
}
public constructor(private http: HttpService) {
public constructor() {
super();
console.log(this.http);
}
public ngOnInit(): void {

View File

@ -32,7 +32,7 @@ export class CoreClockSlideComponent extends BaseSlideComponent<{}> implements O
const hours = '0' + time.getHours();
const minutes = '0' + time.getMinutes();
// Will display time in 10:30:23 format
// Will display time in hh:mm format
this.time = hours.slice(-2) + ':' + minutes.slice(-2);
}

View File

@ -24,7 +24,8 @@
<div *ngIf="!data.data.is_child" [innerHTML]="data.data.text"></div>
<!-- Amendment text -->
<div *ngIf="data.data.is_child" [innerHTML]="data.data.amendment_paragraphs[0]"></div>
<div *ngIf="data.data.is_child && data.data.amendment_paragraphs"
[innerHTML]="data.data.amendment_paragraphs[0]"></div>
<!-- Reason -->
<div *ngIf="data.data.reason">

View File

@ -23,15 +23,14 @@ def user_slide(all_data: AllData, element: Dict[str, Any]) -> Dict[str, Any]:
user_id = element.get("id")
if user_id is None:
return {"error": "id is required for user slide"}
raise ProjectorElementException("id is required for user slide")
try:
user = all_data["users/user"][user_id]
except KeyError:
raise ProjectorElementException(f"user with id {user_id} does not exist")
return_value = {"user": get_user_name(all_data, user["id"])}
return return_value
return {"user": get_user_name(all_data, user["id"])}
def get_user_name(all_data: AllData, user_id: int) -> str: