diff --git a/client/src/app/core/core-services/operator.service.ts b/client/src/app/core/core-services/operator.service.ts index 44e3ddc13..e1e9b8ba6 100644 --- a/client/src/app/core/core-services/operator.service.ts +++ b/client/src/app/core/core-services/operator.service.ts @@ -358,6 +358,10 @@ export class OperatorService implements OnAfterAppsLoaded { return groupIds.some(id => this.user.groups_id.includes(id)); } + public isSuperAdmin(): boolean { + return this.isInGroupIdsNonAdminCheck(2); + } + /** * Update the operators permissions and publish the operator afterwards. * Saves the current WhoAmI to storage with the updated permissions diff --git a/client/src/app/core/core-services/time-travel.service.ts b/client/src/app/core/core-services/time-travel.service.ts index 95498c0d5..06d16acdd 100644 --- a/client/src/app/core/core-services/time-travel.service.ts +++ b/client/src/app/core/core-services/time-travel.service.ts @@ -9,6 +9,7 @@ import { BaseModel } from 'app/shared/models/base/base-model'; import { OpenSlidesStatusService } from './openslides-status.service'; import { OpenSlidesService } from './openslides.service'; import { HttpService } from './http.service'; +import { DataStoreUpdateManagerService } from './data-store-update-manager.service'; /** * Interface for full history data objects. @@ -51,7 +52,8 @@ export class TimeTravelService { private modelMapperService: CollectionStringMapperService, private DS: DataStoreService, private OSStatus: OpenSlidesStatusService, - private OpenSlides: OpenSlidesService + private OpenSlides: OpenSlidesService, + private DSUpdateManager: DataStoreUpdateManagerService ) {} /** @@ -60,6 +62,8 @@ export class TimeTravelService { * @param history the desired point in the history of OpenSlides */ public async loadHistoryPoint(history: History): Promise { + const updateSlot = await this.DSUpdateManager.getNewUpdateSlot(this.DS); + await this.stopTime(history); const fullDataHistory: HistoryData[] = await this.getHistoryData(history); for (const historyObject of fullDataHistory) { @@ -74,6 +78,8 @@ export class TimeTravelService { await this.DS.remove(collectionString, [+id]); } } + + this.DSUpdateManager.commit(updateSlot); } /** @@ -94,25 +100,16 @@ export class TimeTravelService { * @returns the full history on the given date */ private async getHistoryData(history: History): Promise { - const queryParams = { timestamp: Math.ceil(+history.unixtime) }; - return this.httpService.get(`${environment.urlPrefix}/core/history/`, null, queryParams); + const queryParams = { timestamp: Math.ceil(history.timestamp) }; + return this.httpService.get(`${environment.urlPrefix}/core/history/data/`, null, queryParams); } /** * Clears the DataStore and stops the WebSocket connection */ private async stopTime(history: History): Promise { - this.webSocketService.close(); - await this.cleanDataStore(); + await this.webSocketService.close(); + await this.DS.set(); // Same as clear, but not persistent. this.OSStatus.enterHistoryMode(history); } - - /** - * Clean the DataStore to inject old Data. - * Remove everything "but" the history. - */ - private async cleanDataStore(): Promise { - const historyArchive = this.DS.getAll(History); - await this.DS.set(historyArchive); - } } diff --git a/client/src/app/core/core-services/websocket.service.ts b/client/src/app/core/core-services/websocket.service.ts index 79adce478..4b46ccfeb 100644 --- a/client/src/app/core/core-services/websocket.service.ts +++ b/client/src/app/core/core-services/websocket.service.ts @@ -276,7 +276,7 @@ export class WebsocketService { if (data instanceof ArrayBuffer) { const compressedSize = data.byteLength; const decompressedBuffer: Uint8Array = decompress(new Uint8Array(data)); - console.log( + console.debug( `Recieved ${compressedSize / 1024} KB (${decompressedBuffer.byteLength / 1024} KB uncompressed), ratio ${decompressedBuffer.byteLength / compressedSize}` ); @@ -285,7 +285,7 @@ export class WebsocketService { } const message: IncommingWebsocketMessage = JSON.parse(data); - console.log('Received', message); + console.debug('Received', message); const type = message.type; const inResponse = message.in_response; const callbacks = this.responseCallbacks[inResponse]; diff --git a/client/src/app/core/repositories/history/history-repository.service.spec.ts b/client/src/app/core/repositories/history/history-repository.service.spec.ts deleted file mode 100644 index fc8a86317..000000000 --- a/client/src/app/core/repositories/history/history-repository.service.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { HistoryRepositoryService } from './history-repository.service'; -import { E2EImportsModule } from 'e2e-imports.module'; - -describe('HistoryRepositoryService', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [E2EImportsModule], - providers: [HistoryRepositoryService] - }); - }); - - it('should be created', () => { - const service = TestBed.get(HistoryRepositoryService); - expect(service).toBeTruthy(); - }); -}); diff --git a/client/src/app/core/repositories/history/history-repository.service.ts b/client/src/app/core/repositories/history/history-repository.service.ts deleted file mode 100644 index c15835052..000000000 --- a/client/src/app/core/repositories/history/history-repository.service.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { Injectable } from '@angular/core'; - -import { TranslateService } from '@ngx-translate/core'; - -import { CollectionStringMapperService } from 'app/core/core-services/collection-string-mapper.service'; -import { DataStoreService } from 'app/core/core-services/data-store.service'; -import { BaseRepository } from 'app/core/repositories/base-repository'; -import { History } from 'app/shared/models/core/history'; -import { Identifiable } from 'app/shared/models/base/identifiable'; -import { HttpService } from 'app/core/core-services/http.service'; -import { ViewHistory, ProxyHistory, HistoryTitleInformation } from 'app/site/history/models/view-history'; -import { TimeTravelService } from 'app/core/core-services/time-travel.service'; -import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; -import { ViewUser } from 'app/site/users/models/view-user'; -import { DataSendService } from 'app/core/core-services/data-send.service'; - -/** - * Repository for the history. - * - * Gets new history objects/entries and provides them for the view. - */ -@Injectable({ - providedIn: 'root' -}) -export class HistoryRepositoryService extends BaseRepository { - /** - * Constructs the history repository - * - * @param DS The DataStore - * @param mapperService mapps the models to the collection string - * @param httpService OpenSlides own HTTP service - * @param timeTravel To change the time - */ - public constructor( - DS: DataStoreService, - dataSend: DataSendService, - mapperService: CollectionStringMapperService, - viewModelStoreService: ViewModelStoreService, - translate: TranslateService, - private httpService: HttpService, - private timeTravel: TimeTravelService - ) { - super(DS, dataSend, mapperService, viewModelStoreService, translate, History); - } - - public getVerboseName = (plural: boolean = false) => { - return this.translate.instant(plural ? 'Histories' : 'History'); - }; - - public getTitle = (titleInformation: HistoryTitleInformation) => { - return titleInformation.element_id; - }; - - /** - * Creates a new ViewHistory objects out of a historyObject - * - * @param history the source history object - * @return a new ViewHistory object - */ - public createViewModel(history: History): ViewHistory { - return new ViewHistory(this.createProxyHistory(history)); - } - - /** - * Creates a ProxyHistory from a History by wrapping it and give access to the user. - * - * @param history The History object - * @returns the ProxyHistory - */ - private createProxyHistory(history: History): ProxyHistory { - return new Proxy(history, { - get: (instance, property) => { - if (property === 'user') { - return this.viewModelStoreService.get(ViewUser, instance.user_id); - } else { - return instance[property]; - } - } - }); - } - - /** - * Overwrites the default procedure - * - * @ignore - */ - public async create(): Promise { - throw new Error('You cannot create a history object'); - } - - /** - * Overwrites the default procedure - * - * @ignore - */ - public async update(): Promise { - throw new Error('You cannot update a history object'); - } - - /** - * Overwrites the default procedure - * - * @ignore - */ - public async patch(): Promise { - throw new Error('You cannot patch a history object'); - } - - /** - * Overwrites the default procedure - * - * Sends a post-request to delete history objects - */ - public async delete(): Promise { - const restPath = '/rest/core/history/clear_history/'; - await this.httpService.post(restPath); - } - - /** - * Get the ListTitle of a history Element from the dataStore - * using the collection string and the ID. - * - * @param collectionString the models collection string - * @param id the models id - * @returns the ListTitle or null if the model was deleted already - */ - public getOldModelInfo(collectionString: string, id: number): string { - const model = this.viewModelStoreService.get(collectionString, id); - if (model) { - return model.getListTitle(); - } - return null; - } - - /** - * Get the full data on the given date and use the - * TimeTravelService to browse the history on the - * given date - * - * @param viewHistory determines to point to travel back to - */ - public async browseHistory(viewHistory: ViewHistory): Promise { - return this.timeTravel.loadHistoryPoint(viewHistory.history); - } -} diff --git a/client/src/app/core/ui-services/prompt.service.ts b/client/src/app/core/ui-services/prompt.service.ts index ead337231..146fc992d 100644 --- a/client/src/app/core/ui-services/prompt.service.ts +++ b/client/src/app/core/ui-services/prompt.service.ts @@ -16,7 +16,7 @@ export class PromptService { * @param title The title to display in the dialog * @param content The content in the dialog */ - public async open(title: string, content: string): Promise { + public async open(title: string, content: string = ''): Promise { const dialogRef = this.dialog.open(PromptDialogComponent, { width: '250px', data: { title: title, content: content } diff --git a/client/src/app/shared/models/core/history.ts b/client/src/app/shared/models/core/history.ts index 87f4730d0..b17e9e363 100644 --- a/client/src/app/shared/models/core/history.ts +++ b/client/src/app/shared/models/core/history.ts @@ -1,16 +1,15 @@ -import { BaseModel } from '../base/base-model'; +import { Deserializable } from '../base/deserializable'; /** * Representation of a history object. * * @ignore */ -export class History extends BaseModel { - public static COLLECTIONSTRING = 'core/history'; - public id: number; +export class History implements Deserializable { public element_id: string; - public now: string; + public timestamp: number; public information: string; + public restricted: boolean; public user_id: number; /** @@ -19,18 +18,21 @@ export class History extends BaseModel { * @returns a Data object */ public get date(): Date { - return new Date(this.now); + return new Date(this.timestamp * 1000); } - /** - * Converts the timestamp to unix time - */ - public get unixtime(): number { - return Date.parse(this.now) / 1000; + public get collectionString(): string { + return this.element_id.split(':')[0]; } - public constructor(input?: any) { - super(History.COLLECTIONSTRING, input); + public get modelId(): number { + return +this.element_id.split(':')[1]; + } + + public constructor(input: History) { + if (input) { + this.deserialize(input); + } } /** @@ -42,4 +44,8 @@ export class History extends BaseModel { public getLocaleString(locale: string): string { return this.date.toLocaleString(locale); } + + public deserialize(input: any): void { + Object.assign(this, input); + } } diff --git a/client/src/app/site/history/components/history-list/history-list.component.html b/client/src/app/site/history/components/history-list/history-list.component.html index 8e7f04bbd..27d82705b 100644 --- a/client/src/app/site/history/components/history-list/history-list.component.html +++ b/client/src/app/site/history/components/history-list/history-list.component.html @@ -5,20 +5,41 @@
- - - search - +
+ + + + + + +
+
+ + + search + +
- + Timestamp @@ -43,23 +64,23 @@ Comment - {{ parseInformation(history.information) }} + {{ parseInformation(history) }} Changed by - {{ history.user_full_name }} + {{ getUserName(history) }} - + - diff --git a/client/src/app/site/history/components/history-list/history-list.component.scss b/client/src/app/site/history/components/history-list/history-list.component.scss index 3e36f60e7..c8585210c 100644 --- a/client/src/app/site/history/components/history-list/history-list.component.scss +++ b/client/src/app/site/history/components/history-list/history-list.component.scss @@ -1,4 +1,4 @@ -.os-listview-table { +.mat-table { /** Time */ .mat-column-time { flex: 1 0 50px; @@ -24,3 +24,12 @@ font-style: italic; color: slategray; // TODO: Colors per theme } + +.custom-table-header { + justify-content: space-between; + text-align: left; + + & > div { + padding: 0 20px; + } +} diff --git a/client/src/app/site/history/components/history-list/history-list.component.ts b/client/src/app/site/history/components/history-list/history-list.component.ts index 0a215abca..c23e22ecd 100644 --- a/client/src/app/site/history/components/history-list/history-list.component.ts +++ b/client/src/app/site/history/components/history-list/history-list.component.ts @@ -1,19 +1,26 @@ import { Component, OnInit } from '@angular/core'; -import { MatSnackBar } from '@angular/material'; +import { MatSnackBar, MatTableDataSource } from '@angular/material'; import { Router } from '@angular/router'; import { Title } from '@angular/platform-browser'; import { TranslateService } from '@ngx-translate/core'; -import { Subject } from 'rxjs'; +import { Subject, BehaviorSubject } from 'rxjs'; -import { History } from 'app/shared/models/core/history'; -import { HistoryRepositoryService } from 'app/core/repositories/history/history-repository.service'; +import { environment } from 'environments/environment'; import { isDetailNavigable } from 'app/shared/models/base/detail-navigable'; -import { ListViewBaseComponent } from 'app/site/base/list-view-base'; import { OperatorService } from 'app/core/core-services/operator.service'; -import { ViewHistory } from '../../models/view-history'; import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; import { langToLocale } from 'app/shared/utils/lang-to-locale'; +import { TimeTravelService } from 'app/core/core-services/time-travel.service'; +import { HttpService } from 'app/core/core-services/http.service'; +import { BaseViewComponent } from 'app/site/base/base-view'; +import { History } from 'app/shared/models/core/history'; +import { ViewUser } from 'app/site/users/models/view-user'; +import { FormGroup, FormBuilder } from '@angular/forms'; +import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service'; +import { BaseViewModel } from 'app/site/base/base-view-model'; +import { Motion } from 'app/shared/models/motions/motion'; +import { PromptService } from 'app/core/ui-services/prompt.service'; /** * A list view for the history. @@ -25,13 +32,41 @@ import { langToLocale } from 'app/shared/utils/lang-to-locale'; templateUrl: './history-list.component.html', styleUrls: ['./history-list.component.scss'] }) -export class HistoryListComponent extends ListViewBaseComponent - implements OnInit { +export class HistoryListComponent extends BaseViewComponent implements OnInit { /** * Subject determine when the custom timestamp subject changes */ public customTimestampChanged: Subject = new Subject(); + public dataSource: MatTableDataSource = new MatTableDataSource(); + + public get isSuperAdmin(): boolean { + return this.operator.isSuperAdmin(); + } + + public pageSizes = [50, 100, 150, 200, 250]; + + /** + * The form for the selection of the model + * When more models are supproted, add a "collection"-dropdown + */ + public modelSelectForm: FormGroup; + + /** + * The observer for the selected collection, which is currently hardcoded + * to motions. + */ + public collectionObserver: BehaviorSubject; + + /** + * The current selected collection. THis may move to `modelSelectForm`, if this can be choosen. + */ + private currentCollection = Motion.COLLECTIONSTRING; + + public get currentModelId(): number | null { + return this.modelSelectForm.controls.model.value; + } + /** * Constructor for the history list component * @@ -47,12 +82,25 @@ export class HistoryListComponent extends ListViewBaseComponent { + this.queryElementId(this.currentCollection, id); + }); } /** @@ -60,23 +108,34 @@ export class HistoryListComponent extends ListViewBaseComponent { - this.sortAndPublish(history); - }); - } + this.dataSource.filterPredicate = (history: History, filter: string) => { + filter = filter ? filter.toLowerCase() : ''; - /** - * Sorts the given ViewHistory array and sets it in the table data source - * - * @param unsortedHistoryList - */ - private sortAndPublish(unsortedHistoryList: ViewHistory[]): void { - const sortedList = unsortedHistoryList.map(history => history).filter(item => item.information.length > 0); - sortedList.sort((a, b) => b.history.unixtime - a.history.unixtime); - this.dataSource.data = sortedList; + if (!history) { + return false; + } + + const userfullname = this.getUserName(history); + if (userfullname.toLowerCase().indexOf(filter) >= 0) { + return true; + } + + if ( + this.getElementInfo(history) && + this.getElementInfo(history) + .toLowerCase() + .indexOf(filter) >= 0 + ) { + return true; + } + + return ( + this.parseInformation(history) + .toLowerCase() + .indexOf(filter) >= 0 + ); + }; } /** @@ -92,17 +151,12 @@ export class HistoryListComponent extends ListViewBaseComponent { - if (this.operator.isInGroupIds(2)) { - await this.repo.browseHistory(history); - const element = this.viewModelStore.get(history.getCollectionString(), history.getModelId()); - if (element && isDetailNavigable(element)) { - this.router.navigate([element.getDetailStateURL()]); - } else { - const message = this.translate.instant('Cannot navigate to the selected history element.'); - this.raiseError(message); + public async onClickRow(history: History): Promise { + if (!this.isSuperAdmin) { + return; + } + + await this.timeTravelService.loadHistoryPoint(history); + const element = this.viewModelStore.get(history.collectionString, history.modelId); + if (element && isDetailNavigable(element)) { + this.router.navigate([element.getDetailStateURL()]); + } else { + const message = this.translate.instant('Cannot navigate to the selected history element.'); + this.raiseError(message); + } + } + + public getTimestamp(history: History): string { + return history.getLocaleString(langToLocale(this.translate.currentLang)); + } + + /** + * clears the whole history. + */ + public async clearHistory(): Promise { + const title = this.translate.instant('Are you sure you want delete the whole history?'); + if (await this.promptService.open(title)) { + try { + await this.http.delete(`${environment.urlPrefix}/core/history/information/`); + this.refresh(); + } catch (e) { + this.raiseError(e); } } } - public getTimestamp(viewHistory: ViewHistory): string { - return viewHistory.history.getLocaleString(langToLocale(this.translate.currentLang)); - } - - /** - * Handler for the delete all button - */ - public onDeleteAllButton(): void { - if (this.operator.isInGroupIds(2)) { - this.repo.delete(); + public refresh(): void { + if (this.currentCollection && this.currentModelId) { + this.queryElementId(this.currentCollection, this.currentModelId); } } /** * Returns a translated history information string which contains optional (translated) arguments. * - * @param information history information string + * @param history the history */ - public parseInformation(information: string): string { - if (information.length) { - const base_string = this.translate.instant(information[0]); - let argument_string; - if (information.length > 1) { - argument_string = this.translate.instant(information[1]); - } - return base_string.replace(/{arg1}/g, argument_string); + public parseInformation(history: History): string { + if (!history.information || !history.information.length) { + return ''; } + + const baseString = this.translate.instant(history.information[0]); + let argumentString; + if (history.information.length > 1) { + argumentString = this.translate.instant(history.information[1]); + } + return baseString.replace(/{arg1}/g, argumentString); + } + + public getUserName(history: History): string { + const user = this.viewModelStore.get(ViewUser, history.user_id); + return user ? user.full_name : ''; } /** @@ -163,31 +238,13 @@ export class HistoryListComponent extends ListViewBaseComponent { - if (!data || !data.information) { - return false; - } - filter = filter ? filter.toLowerCase() : ''; - if ( - this.getElementInfo(data) && - this.getElementInfo(data) - .toLowerCase() - .indexOf(filter) >= 0 - ) { - return true; - } - if (data.user && data.user.full_name.toLowerCase().indexOf(filter) >= 0) { - return true; - } - return ( - this.parseInformation(data.information) - .toLowerCase() - .indexOf(filter) >= 0 - ); - }; + private async queryElementId(collectionString: string, id: number): Promise { + const historyData = await this.http.get(`${environment.urlPrefix}/core/history/information/`, null, { + type: 'element', + value: `${collectionString}:${id}` + }); + this.dataSource.data = historyData.map(data => new History(data)); } } diff --git a/client/src/app/site/history/history.config.ts b/client/src/app/site/history/history.config.ts index 74d533a31..27450eea1 100644 --- a/client/src/app/site/history/history.config.ts +++ b/client/src/app/site/history/history.config.ts @@ -1,7 +1,4 @@ import { AppConfig } from '../../core/app-config'; -import { History } from 'app/shared/models/core/history'; -import { HistoryRepositoryService } from 'app/core/repositories/history/history-repository.service'; -import { ViewHistory } from './models/view-history'; /** * Config object for history. @@ -9,14 +6,6 @@ import { ViewHistory } from './models/view-history'; */ export const HistoryAppConfig: AppConfig = { name: 'history', - models: [ - { - collectionString: 'core/history', - model: History, - viewModel: ViewHistory, - repository: HistoryRepositoryService - } - ], mainMenuEntries: [ { route: '/history', diff --git a/client/src/app/site/history/models/view-history.ts b/client/src/app/site/history/models/view-history.ts deleted file mode 100644 index e099ac4d3..000000000 --- a/client/src/app/site/history/models/view-history.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { BaseViewModel } from 'app/site/base/base-view-model'; -import { History } from 'app/shared/models/core/history'; -import { ViewUser } from 'app/site/users/models/view-user'; - -export type ProxyHistory = History & { user?: ViewUser }; - -export interface HistoryTitleInformation { - element_id: string; -} - -/** - * View model for history objects - */ -export class ViewHistory extends BaseViewModel implements HistoryTitleInformation { - public static COLLECTIONSTRING = History.COLLECTIONSTRING; - - /** - * Read the history property - */ - public get history(): ProxyHistory { - return this._model; - } - - /** - * Gets the users ViewUser. - */ - public get user(): ViewUser | null { - return this.history.user; - } - - /** - * Get the id of the history object - * Required by BaseViewModel - * - * @returns the id as number - */ - public get id(): number { - return this.history.id; - } - - /** - * @returns the users full name - */ - public get user_full_name(): string { - return this.history.user ? this.history.user.full_name : ''; - } - - /** - * Get the elementIDs of the history object - * - * @returns the element ID as String - */ - public get element_id(): string { - return this.history.element_id; - } - - /** - * Get the information about the history - * - * @returns a string with the information to the history object - */ - public get information(): string { - return this.history.information; - } - - /** - * Get the time of the history as number - * - * @returns the unix timestamp as number - */ - public get now(): string { - return this.history.now; - } - - /** - * Construction of a ViewHistory - * - * @param history the real history BaseModel - * @param user the real user BaseModel - */ - public constructor(history: ProxyHistory) { - super(History.COLLECTIONSTRING, history); - } - - /** - * Converts elementID into collection string - * @returns the CollectionString to the model - */ - public getCollectionString(): string { - return this.element_id.split(':')[0]; - } - - /** - * Extract the models ID from the elementID - * @returns a model id - */ - public getModelId(): number { - return +this.element_id.split(':')[1]; - } - - public updateDependencies(update: BaseViewModel): void {} -} diff --git a/client/src/styles.scss b/client/src/styles.scss index 476af6e0a..50e26554c 100644 --- a/client/src/styles.scss +++ b/client/src/styles.scss @@ -530,6 +530,9 @@ button.mat-menu-item.selected { .spacer-left-10 { margin-left: 10px; } +.spacer-left-20 { + margin-left: 20px; +} .spacer-left-50 { margin-left: 50px !important; } diff --git a/client/tslint.json b/client/tslint.json index 383db0ced..7b35ad80b 100644 --- a/client/tslint.json +++ b/client/tslint.json @@ -20,7 +20,7 @@ ], "no-arg": true, "no-bitwise": true, - "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], + "no-console": [true, "table", "clear", "count", "countReset", "info", "time", "timeEnd", "timeline", "timelineEnd", "trace"], "no-construct": true, "no-debugger": true, "no-duplicate-super": true, diff --git a/openslides/core/views.py b/openslides/core/views.py index c875ddfb4..b310ca6f2 100644 --- a/openslides/core/views.py +++ b/openslides/core/views.py @@ -494,7 +494,6 @@ class HistoryInformationView(utils_views.APIView): Examples: /?type=element&value=motions%2Fmotion%3A42 if your search for motion 42 - /?type=text&value=my%20question Use DELETE to clear the history. """ @@ -509,15 +508,12 @@ class HistoryInformationView(utils_views.APIView): self.permission_denied(self.request) type = self.request.query_params.get("type") value = self.request.query_params.get("value") - if type not in ("element", "text"): + if type not in ("element"): raise ValidationError( {"detail": "Invalid input. Type should be 'element' or 'text'."} ) - if type == "element": - data = self.get_data_element_search(value) - else: - # type == "text" - data = self.get_data_text_search(value) + # We currently just support searching by element id. + data = self.get_data_element_search(value) return data def get_data_element_search(self, value): @@ -525,7 +521,7 @@ class HistoryInformationView(utils_views.APIView): Retrieves history information for element search. """ data = [] - for instance in History.objects.filter(element_id=value): + for instance in History.objects.filter(element_id=value).order_by("-now"): data.append( { "element_id": instance.element_id, @@ -537,13 +533,6 @@ class HistoryInformationView(utils_views.APIView): ) return data - def get_data_text_search(self, value): - """ - Retrieves history information for text search. - """ - # TODO: Add results here. - return [] - def delete(self, request, *args, **kwargs): """ Deletes and rebuilds the history. diff --git a/openslides/utils/stats.py b/openslides/utils/stats.py index 1f0e7eab8..8ecb19591 100644 --- a/openslides/utils/stats.py +++ b/openslides/utils/stats.py @@ -111,7 +111,7 @@ class WebsocketThroughputLogger: async def check_and_flush(self) -> None: # If we waited longer then 60 seconds, flush the data. current_time = time.time() - if current_time > (self.time + 20): + if current_time > (self.time + 60): send_ratio = receive_ratio = 1.0 if self.send_compressed > 0: