Fixed two little issues with relations and reverse mapping

- Reverse setup for normal autoupdates (no initial loading)
- reverse "set null" to be reflected to the mapping

Also fixed a bug with redis
This commit is contained in:
FinnStutzenstein 2019-08-13 16:45:39 +02:00
parent a1b7b1c69d
commit 1d718dcb74
26 changed files with 198 additions and 107 deletions

View File

@ -97,7 +97,7 @@ export class AutoupdateService {
elements = elements.concat(this.mapObjectsToBaseModels(collection, autoupdate.changed[collection])); elements = elements.concat(this.mapObjectsToBaseModels(collection, autoupdate.changed[collection]));
}); });
const updateSlot = await this.DSUpdateManager.getNewUpdateSlot(this.DS); const updateSlot = await this.DSUpdateManager.getNewUpdateSlot(this.DS, true);
await this.DS.set(elements, autoupdate.to_change_id); await this.DS.set(elements, autoupdate.to_change_id);
this.DSUpdateManager.commit(updateSlot); this.DSUpdateManager.commit(updateSlot);
} }
@ -172,7 +172,7 @@ export class AutoupdateService {
const oldChangeId = this.DS.maxChangeId; const oldChangeId = this.DS.maxChangeId;
const response = await this.websocketService.sendAndGetResponse<{}, AutoupdateFormat>('getElements', {}); const response = await this.websocketService.sendAndGetResponse<{}, AutoupdateFormat>('getElements', {});
const updateSlot = await this.DSUpdateManager.getNewUpdateSlot(this.DS); const updateSlot = await this.DSUpdateManager.getNewUpdateSlot(this.DS, true);
let allModels: BaseModel[] = []; let allModels: BaseModel[] = [];
for (const collection of Object.keys(response.changed)) { for (const collection of Object.keys(response.changed)) {
if (this.modelMapper.isCollectionRegistered(collection)) { if (this.modelMapper.isCollectionRegistered(collection)) {

View File

@ -49,7 +49,7 @@ export class UpdateSlot {
/** /**
* @param DS Carries the DataStore: TODO (see below `DataStoreUpdateManagerService.getNewUpdateSlot`) * @param DS Carries the DataStore: TODO (see below `DataStoreUpdateManagerService.getNewUpdateSlot`)
*/ */
public constructor(public readonly DS: DataStoreService) { public constructor(public readonly DS: DataStoreService, public readonly initialLoading: boolean) {
this._id = UpdateSlot.ID_COUTNER++; this._id = UpdateSlot.ID_COUTNER++;
} }
@ -185,13 +185,13 @@ export class DataStoreUpdateManagerService {
* @param DS The DataStore. This is a hack, becuase we cannot use the DataStore * @param DS The DataStore. This is a hack, becuase we cannot use the DataStore
* here, because these are cyclic dependencies... --> TODO * here, because these are cyclic dependencies... --> TODO
*/ */
public async getNewUpdateSlot(DS: DataStoreService): Promise<UpdateSlot> { public async getNewUpdateSlot(DS: DataStoreService, initialLoading: boolean = false): Promise<UpdateSlot> {
if (this.currentUpdateSlot) { if (this.currentUpdateSlot) {
const request = new Deferred(); const request = new Deferred();
this.updateSlotRequests.push(request); this.updateSlotRequests.push(request);
await request; await request;
} }
this.currentUpdateSlot = new UpdateSlot(DS); this.currentUpdateSlot = new UpdateSlot(DS, initialLoading);
return this.currentUpdateSlot; return this.currentUpdateSlot;
} }
@ -221,7 +221,7 @@ export class DataStoreUpdateManagerService {
const deletedModelIds = slot.getDeletedModelIdsForCollection(repo.collectionString); const deletedModelIds = slot.getDeletedModelIdsForCollection(repo.collectionString);
repo.deleteModels(deletedModelIds); repo.deleteModels(deletedModelIds);
const changedModelIds = slot.getChangedModelIdsForCollection(repo.collectionString); const changedModelIds = slot.getChangedModelIdsForCollection(repo.collectionString);
repo.changedModels(changedModelIds); repo.changedModels(changedModelIds, slot.initialLoading);
if (deletedModelIds.length || changedModelIds.length) { if (deletedModelIds.length || changedModelIds.length) {
affectedRepos[repo.collectionString] = repo; affectedRepos[repo.collectionString] = repo;
@ -350,7 +350,7 @@ export class DataStoreService {
// This promise will be resolved with cached datastore. // This promise will be resolved with cached datastore.
const store = await this.storageService.get<JsonStorage>(DataStoreService.cachePrefix + 'DS'); const store = await this.storageService.get<JsonStorage>(DataStoreService.cachePrefix + 'DS');
if (store) { if (store) {
const updateSlot = await this.DSUpdateManager.getNewUpdateSlot(this); const updateSlot = await this.DSUpdateManager.getNewUpdateSlot(this, true);
// There is a store. Deserialize it // There is a store. Deserialize it
this.jsonStore = store; this.jsonStore = store;

View File

@ -48,12 +48,13 @@ export class RelationManagerService {
public createViewModel<M extends BaseModel, V extends BaseViewModel>( public createViewModel<M extends BaseModel, V extends BaseViewModel>(
model: M, model: M,
modelCtor: ViewModelConstructor<V>, modelCtor: ViewModelConstructor<V>,
relations: RelationDefinition[] relations: RelationDefinition[],
initialLoading: boolean
): V { ): V {
const viewModel = new modelCtor(model) as V; const viewModel = new modelCtor(model) as V;
relations.forEach(relation => { relations.forEach(relation => {
this.setRelationsInViewModel(model, viewModel, relation); this.setRelationsInViewModel(model, viewModel, relation, initialLoading);
}); });
return viewModel; return viewModel;
@ -66,30 +67,65 @@ export class RelationManagerService {
protected setRelationsInViewModel<M extends BaseModel, V extends BaseViewModel>( protected setRelationsInViewModel<M extends BaseModel, V extends BaseViewModel>(
model: M, model: M,
viewModel: V, viewModel: V,
relation: RelationDefinition relation: RelationDefinition,
initialLoading: boolean
): void { ): void {
// no reverse setting needed. This is done in the second phase of the ds-upgrade-manager if (isNormalRelationDefinition(relation)) {
if (isReverseRelationDefinition(relation)) { if (
return; (relation.type === 'M2M' || relation.type === 'O2M') &&
} model[relation.ownIdKey] &&
model[relation.ownIdKey].constructor === Array
if ( ) {
(relation.type === 'M2M' || relation.type === 'O2M') && const foreignViewModels = this.viewModelStoreService.getMany(
model[relation.ownIdKey] && relation.foreignViewModel,
model[relation.ownIdKey].constructor === Array model[relation.ownIdKey]
) { );
const foreignViewModels = this.viewModelStoreService.getMany( viewModel['_' + relation.ownKey] = foreignViewModels;
relation.foreignModel, this.sortByRelation(relation, viewModel);
model[relation.ownIdKey] } else if (relation.type === 'M2O') {
); const foreignViewModel = this.viewModelStoreService.get(
viewModel['_' + relation.ownKey] = foreignViewModels; relation.foreignViewModel,
this.sortByRelation(relation, viewModel); model[relation.ownIdKey]
} else if (relation.type === 'M2O') { );
const foreignViewModel = this.viewModelStoreService.get(relation.foreignModel, model[relation.ownIdKey]); viewModel['_' + relation.ownKey] = foreignViewModel;
viewModel['_' + relation.ownKey] = foreignViewModel; }
} else if (isReverseRelationDefinition(relation) && !initialLoading) {
if (relation.type === 'M2M') {
const foreignViewModels = this.viewModelStoreService.filter(
relation.foreignViewModel,
foreignViewModel =>
foreignViewModel[relation.foreignIdKey] &&
foreignViewModel[relation.foreignIdKey].constructor === Array &&
foreignViewModel[relation.foreignIdKey].includes(model.id)
);
viewModel['_' + relation.ownKey] = foreignViewModels;
this.sortByRelation(relation, viewModel);
} else if (relation.type === 'O2M') {
const foreignViewModels = this.viewModelStoreService.filter(
relation.foreignViewModel,
foreignViewModel =>
foreignViewModel[relation.foreignIdKey] && foreignViewModel[relation.foreignIdKey] === model.id
);
viewModel['_' + relation.ownKey] = foreignViewModels;
this.sortByRelation(relation, viewModel);
} else if (relation.type === 'M2O') {
const foreignViewModel = this.viewModelStoreService.find(
relation.foreignViewModel,
_foreignViewModel =>
_foreignViewModel[relation.foreignIdKey] &&
_foreignViewModel[relation.foreignIdKey] === model.id
);
viewModel['_' + relation.ownKey] = foreignViewModel;
}
} else if (isNestedRelationDefinition(relation)) { } else if (isNestedRelationDefinition(relation)) {
const foreignViewModels: BaseViewModel[] = model[relation.ownKey].map(foreignModel => const foreignModels = model[relation.ownKey].map(m => new relation.foreignModel(m));
this.createViewModel(foreignModel, relation.foreignModel, relation.relationDefinition || []) const foreignViewModels: BaseViewModel[] = foreignModels.map((foreignModel: BaseModel) =>
this.createViewModel(
foreignModel,
relation.foreignViewModel,
relation.relationDefinition || [],
initialLoading
)
); );
viewModel['_' + relation.ownKey] = foreignViewModels; viewModel['_' + relation.ownKey] = foreignViewModels;
this.sortByRelation(relation, viewModel); this.sortByRelation(relation, viewModel);
@ -181,15 +217,29 @@ export class RelationManagerService {
// The foreign model has one id. Check, if the ownViewModel is the matching view model. // The foreign model has one id. Check, if the ownViewModel is the matching view model.
// If so, add the foreignViewModel to the array from the ownViewModel (with many foreignViewModels) // If so, add the foreignViewModel to the array from the ownViewModel (with many foreignViewModels)
// If not, check, if the model _was_ in our foreignViewModel array and remove it.
if (relation.type === 'O2M') { if (relation.type === 'O2M') {
if (foreignViewModel[relation.foreignIdKey] === ownViewModel.id) { if (foreignViewModel[relation.foreignIdKey] === ownViewModel.id) {
this.setForeingViewModelInOwnViewModelArray(foreignViewModel, ownViewModel, relation.ownKey); this.setForeingViewModelInOwnViewModelArray(foreignViewModel, ownViewModel, relation.ownKey);
return true; return true;
} else {
const ownViewModelArray = <any>ownViewModel['_' + relation.ownKey];
if (ownViewModelArray) {
// We have the array of foreign view models for our own view model. Remove the foreignViewModel (if it was there).
const index = ownViewModelArray.findIndex(
_foreignViewModel => _foreignViewModel.id === foreignViewModel.id
);
if (index > -1) {
ownViewModelArray.splice(index, 1);
return true;
}
}
} }
} }
// The foreign model should hold an array of ids. If the ownViewModel is in it, the foreignViewModel must // The foreign model should hold an array of ids. If the ownViewModel is in it, the foreignViewModel must
// be included into the array from the ownViewModel (with many foreignViewModels) // be included into the array from the ownViewModel (with many foreignViewModels).
// If not, check, if the model _was_ in our foreignViewModel array and remove it.
else if (relation.type === 'M2M') { else if (relation.type === 'M2M') {
if ( if (
foreignViewModel[relation.foreignIdKey] && foreignViewModel[relation.foreignIdKey] &&
@ -198,11 +248,24 @@ export class RelationManagerService {
) { ) {
this.setForeingViewModelInOwnViewModelArray(foreignViewModel, ownViewModel, relation.ownKey); this.setForeingViewModelInOwnViewModelArray(foreignViewModel, ownViewModel, relation.ownKey);
return true; return true;
} else {
const ownViewModelArray = <any>ownViewModel['_' + relation.ownKey];
if (ownViewModelArray) {
// We have the array of foreign view models for our own view model. Remove the foreignViewModel (if it was there).
const index = ownViewModelArray.findIndex(
_foreignViewModel => _foreignViewModel.id === foreignViewModel.id
);
if (index > -1) {
ownViewModelArray.splice(index, 1);
return true;
}
}
} }
} }
// The foreign model should hold an array of ids. If the ownViewModel is in it, the foreignViewModel is the // The foreign model should hold an array of ids. If the ownViewModel is in it, the foreignViewModel is the
// one and only matching model for the ownViewModel // one and only matching model for the ownViewModel. If the ownViewModel is not in it, check if the
// foreignViewModel _was_ the matching model. If so, set the reference to null.
else if (relation.type === 'M2O') { else if (relation.type === 'M2O') {
if ( if (
foreignViewModel[relation.foreignIdKey] && foreignViewModel[relation.foreignIdKey] &&
@ -211,6 +274,11 @@ export class RelationManagerService {
) { ) {
ownViewModel['_' + relation.ownKey] = foreignViewModel; ownViewModel['_' + relation.ownKey] = foreignViewModel;
return true; return true;
} else if (
ownViewModel['_' + relation.ownKey] &&
ownViewModel['_' + relation.ownKey].id === foreignViewModel.id
) {
ownViewModel['_' + relation.ownKey] = null;
} }
} }
} else if (isNestedRelationDefinition(relation)) { } else if (isNestedRelationDefinition(relation)) {

View File

@ -53,7 +53,7 @@ export class TimeTravelService {
* @param history the desired point in the history of OpenSlides * @param history the desired point in the history of OpenSlides
*/ */
public async loadHistoryPoint(history: History): Promise<void> { public async loadHistoryPoint(history: History): Promise<void> {
const updateSlot = await this.DSUpdateManager.getNewUpdateSlot(this.DS); const updateSlot = await this.DSUpdateManager.getNewUpdateSlot(this.DS, true);
await this.stopTime(history); await this.stopTime(history);
const historyData: HistoryData = await this.getHistoryData(history); const historyData: HistoryData = await this.getHistoryData(history);

View File

@ -1,4 +1,4 @@
import { BaseModel } from 'app/shared/models/base/base-model'; import { BaseModel, ModelConstructor } from 'app/shared/models/base/base-model';
import { BaseViewModel, ViewModelConstructor } from 'app/site/base/base-view-model'; import { BaseViewModel, ViewModelConstructor } from 'app/site/base/base-view-model';
// All "standard" relations. // All "standard" relations.
@ -21,7 +21,7 @@ interface BaseRelationDefinition<VForeign extends BaseViewModel> {
/** /**
* The model on the other side of the relation. * The model on the other side of the relation.
*/ */
foreignModel: ViewModelConstructor<VForeign>; foreignViewModel: ViewModelConstructor<VForeign>;
} }
export interface BaseOrderedRelation<VForeign extends BaseViewModel> extends BaseRelationDefinition<VForeign> { export interface BaseOrderedRelation<VForeign extends BaseViewModel> extends BaseRelationDefinition<VForeign> {
@ -97,7 +97,7 @@ interface BaseReverseRelationDefinition<VForeign extends BaseViewModel> {
/** /**
* The model on the other side of the relation. * The model on the other side of the relation.
*/ */
foreignModel: ViewModelConstructor<VForeign>; foreignViewModel: ViewModelConstructor<VForeign>;
} }
interface ReverseM2MRelationDefinition<VForeign extends BaseViewModel> interface ReverseM2MRelationDefinition<VForeign extends BaseViewModel>
@ -130,7 +130,7 @@ export function isReverseRelationDefinition(obj: RelationDefinition): obj is Rev
* Nested relations in the REST-API. For the most values see * Nested relations in the REST-API. For the most values see
* `NormalRelationDefinition`. * `NormalRelationDefinition`.
*/ */
interface NestedRelationDefinition<VForeign extends BaseViewModel> extends BaseOrderedRelation<VForeign> { export interface NestedRelationDefinition<VForeign extends BaseViewModel> extends BaseOrderedRelation<VForeign> {
type: 'nested'; type: 'nested';
ownKey: string; ownKey: string;
@ -138,6 +138,11 @@ interface NestedRelationDefinition<VForeign extends BaseViewModel> extends BaseO
* The nested relations. * The nested relations.
*/ */
relationDefinition?: RelationDefinition[]; relationDefinition?: RelationDefinition[];
/**
* The matching model for the foreignViewModel.
*/
foreignModel: ModelConstructor<BaseModel>;
} }
export function isNestedRelationDefinition(obj: RelationDefinition): obj is NestedRelationDefinition<BaseViewModel> { export function isNestedRelationDefinition(obj: RelationDefinition): obj is NestedRelationDefinition<BaseViewModel> {
@ -172,7 +177,7 @@ export function isGenericRelationDefinition(obj: RelationDefinition): obj is Gen
*/ */
interface CustomRelationDefinition<VForeign extends BaseViewModel> { interface CustomRelationDefinition<VForeign extends BaseViewModel> {
type: 'custom'; type: 'custom';
foreignModel: ViewModelConstructor<VForeign>; foreignViewModel: ViewModelConstructor<VForeign>;
/** /**
* Called, when the view model is created from the model. * Called, when the view model is created from the model.

View File

@ -8,6 +8,7 @@ import { RelationManagerService } from 'app/core/core-services/relation-manager.
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { RelationDefinition } from 'app/core/definitions/relations'; import { RelationDefinition } from 'app/core/definitions/relations';
import { ListOfSpeakers } from 'app/shared/models/agenda/list-of-speakers'; import { ListOfSpeakers } from 'app/shared/models/agenda/list-of-speakers';
import { Speaker } from 'app/shared/models/agenda/speaker';
import { Identifiable } from 'app/shared/models/base/identifiable'; import { Identifiable } from 'app/shared/models/base/identifiable';
import { ListOfSpeakersTitleInformation, ViewListOfSpeakers } from 'app/site/agenda/models/view-list-of-speakers'; import { ListOfSpeakersTitleInformation, ViewListOfSpeakers } from 'app/site/agenda/models/view-list-of-speakers';
import { ViewSpeaker } from 'app/site/agenda/models/view-speaker'; import { ViewSpeaker } from 'app/site/agenda/models/view-speaker';
@ -39,14 +40,15 @@ const ListOfSpeakersRelations: RelationDefinition[] = [
{ {
type: 'nested', type: 'nested',
ownKey: 'speakers', ownKey: 'speakers',
foreignModel: ViewSpeaker, foreignViewModel: ViewSpeaker,
foreignModel: Speaker,
order: 'weight', order: 'weight',
relationDefinition: [ relationDefinition: [
{ {
type: 'M2O', type: 'M2O',
ownIdKey: 'user_id', ownIdKey: 'user_id',
ownKey: 'user', ownKey: 'user',
foreignModel: ViewUser foreignViewModel: ViewUser
} }
] ]
} }

View File

@ -9,6 +9,8 @@ import { ViewModelStoreService } from 'app/core/core-services/view-model-store.s
import { RelationDefinition } from 'app/core/definitions/relations'; import { RelationDefinition } from 'app/core/definitions/relations';
import { Assignment } from 'app/shared/models/assignments/assignment'; import { Assignment } from 'app/shared/models/assignments/assignment';
import { AssignmentPoll } from 'app/shared/models/assignments/assignment-poll'; import { AssignmentPoll } from 'app/shared/models/assignments/assignment-poll';
import { AssignmentPollOption } from 'app/shared/models/assignments/assignment-poll-option';
import { AssignmentRelatedUser } from 'app/shared/models/assignments/assignment-related-user';
import { AssignmentTitleInformation, ViewAssignment } from 'app/site/assignments/models/view-assignment'; import { AssignmentTitleInformation, ViewAssignment } from 'app/site/assignments/models/view-assignment';
import { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-poll'; import { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-poll';
import { ViewAssignmentPollOption } from 'app/site/assignments/models/view-assignment-poll-option'; import { ViewAssignmentPollOption } from 'app/site/assignments/models/view-assignment-poll-option';
@ -25,44 +27,47 @@ const AssignmentRelations: RelationDefinition[] = [
type: 'M2M', type: 'M2M',
ownIdKey: 'tags_id', ownIdKey: 'tags_id',
ownKey: 'tags', ownKey: 'tags',
foreignModel: ViewTag foreignViewModel: ViewTag
}, },
{ {
type: 'M2M', type: 'M2M',
ownIdKey: 'attachments_id', ownIdKey: 'attachments_id',
ownKey: 'attachments', ownKey: 'attachments',
foreignModel: ViewMediafile foreignViewModel: ViewMediafile
}, },
{ {
type: 'nested', type: 'nested',
ownKey: 'assignment_related_users', ownKey: 'assignment_related_users',
foreignModel: ViewAssignmentRelatedUser, foreignViewModel: ViewAssignmentRelatedUser,
foreignModel: AssignmentRelatedUser,
order: 'weight', order: 'weight',
relationDefinition: [ relationDefinition: [
{ {
type: 'M2O', type: 'M2O',
ownIdKey: 'user_id', ownIdKey: 'user_id',
ownKey: 'user', ownKey: 'user',
foreignModel: ViewUser foreignViewModel: ViewUser
} }
] ]
}, },
{ {
type: 'nested', type: 'nested',
ownKey: 'polls', ownKey: 'polls',
foreignModel: ViewAssignmentPoll, foreignViewModel: ViewAssignmentPoll,
foreignModel: AssignmentPoll,
relationDefinition: [ relationDefinition: [
{ {
type: 'nested', type: 'nested',
ownKey: 'options', ownKey: 'options',
foreignModel: ViewAssignmentPollOption, foreignViewModel: ViewAssignmentPollOption,
foreignModel: AssignmentPollOption,
order: 'weight', order: 'weight',
relationDefinition: [ relationDefinition: [
{ {
type: 'M2O', type: 'M2O',
ownIdKey: 'user_id', ownIdKey: 'user_id',
ownKey: 'user', ownKey: 'user',
foreignModel: ViewUser foreignViewModel: ViewUser
} }
] ]
} }

View File

@ -40,9 +40,9 @@ export abstract class BaseHasContentObjectRepository<
/** /**
* @override * @override
*/ */
public changedModels(ids: number[]): void { public changedModels(ids: number[], initialLoading: boolean): void {
ids.forEach(id => { ids.forEach(id => {
const v = this.createViewModelWithTitles(this.DS.get(this.collectionString, id)); const v = this.createViewModelWithTitles(this.DS.get(this.collectionString, id), initialLoading);
this.viewModelStore[id] = v; this.viewModelStore[id] = v;
const contentObject = v.contentObjectData; const contentObject = v.contentObjectData;

View File

@ -42,13 +42,13 @@ export abstract class BaseIsAgendaItemAndListOfSpeakersContentObjectRepository<
type: 'M2O', type: 'M2O',
ownIdKey: 'agenda_item_id', ownIdKey: 'agenda_item_id',
ownKey: 'item', ownKey: 'item',
foreignModel: ViewItem foreignViewModel: ViewItem
}); });
this.relationDefinitions.push({ this.relationDefinitions.push({
type: 'M2O', type: 'M2O',
ownIdKey: 'list_of_speakers_id', ownIdKey: 'list_of_speakers_id',
ownKey: 'list_of_speakers', ownKey: 'list_of_speakers',
foreignModel: ViewListOfSpeakers foreignViewModel: ViewListOfSpeakers
}); });
super.groupRelationsByCollections(); super.groupRelationsByCollections();
} }
@ -72,8 +72,8 @@ export abstract class BaseIsAgendaItemAndListOfSpeakersContentObjectRepository<
return this.getAgendaSlideTitle(titleInformation); return this.getAgendaSlideTitle(titleInformation);
}; };
protected createViewModelWithTitles(model: M): V { protected createViewModelWithTitles(model: M, initialLoading: boolean): V {
const viewModel = super.createViewModelWithTitles(model); const viewModel = super.createViewModelWithTitles(model, initialLoading);
viewModel.getAgendaListTitle = () => this.getAgendaListTitle(viewModel); viewModel.getAgendaListTitle = () => this.getAgendaListTitle(viewModel);
viewModel.getAgendaSlideTitle = () => this.getAgendaSlideTitle(viewModel); viewModel.getAgendaSlideTitle = () => this.getAgendaSlideTitle(viewModel);
viewModel.getListOfSpeakersTitle = () => this.getListOfSpeakersTitle(viewModel); viewModel.getListOfSpeakersTitle = () => this.getListOfSpeakersTitle(viewModel);

View File

@ -68,7 +68,7 @@ export abstract class BaseIsAgendaItemContentObjectRepository<
type: 'M2O', type: 'M2O',
ownIdKey: 'agenda_item_id', ownIdKey: 'agenda_item_id',
ownKey: 'item', ownKey: 'item',
foreignModel: ViewItem foreignViewModel: ViewItem
}); });
super.groupRelationsByCollections(); super.groupRelationsByCollections();
} }
@ -93,8 +93,8 @@ export abstract class BaseIsAgendaItemContentObjectRepository<
/** /**
* Adds the agenda titles to the viewmodel. * Adds the agenda titles to the viewmodel.
*/ */
protected createViewModelWithTitles(model: M): V { protected createViewModelWithTitles(model: M, initialLoading: boolean): V {
const viewModel = super.createViewModelWithTitles(model); const viewModel = super.createViewModelWithTitles(model, initialLoading);
viewModel.getAgendaListTitle = () => this.getAgendaListTitle(viewModel); viewModel.getAgendaListTitle = () => this.getAgendaListTitle(viewModel);
viewModel.getAgendaSlideTitle = () => this.getAgendaSlideTitle(viewModel); viewModel.getAgendaSlideTitle = () => this.getAgendaSlideTitle(viewModel);
return viewModel; return viewModel;

View File

@ -66,7 +66,7 @@ export abstract class BaseIsListOfSpeakersContentObjectRepository<
type: 'M2O', type: 'M2O',
ownIdKey: 'list_of_speakers_id', ownIdKey: 'list_of_speakers_id',
ownKey: 'list_of_speakers', ownKey: 'list_of_speakers',
foreignModel: ViewListOfSpeakers foreignViewModel: ViewListOfSpeakers
}); });
super.groupRelationsByCollections(); super.groupRelationsByCollections();
} }
@ -82,8 +82,8 @@ export abstract class BaseIsListOfSpeakersContentObjectRepository<
/** /**
* Adds the list of speakers titles to the view model * Adds the list of speakers titles to the view model
*/ */
protected createViewModelWithTitles(model: M): V { protected createViewModelWithTitles(model: M, initialLoading: boolean): V {
const viewModel = super.createViewModelWithTitles(model); const viewModel = super.createViewModelWithTitles(model, initialLoading);
viewModel.getListOfSpeakersTitle = () => this.getListOfSpeakersTitle(viewModel); viewModel.getListOfSpeakersTitle = () => this.getListOfSpeakersTitle(viewModel);
viewModel.getListOfSpeakersSlideTitle = () => this.getListOfSpeakersSlideTitle(viewModel); viewModel.getListOfSpeakersSlideTitle = () => this.getListOfSpeakersSlideTitle(viewModel);
return viewModel; return viewModel;

View File

@ -153,7 +153,7 @@ export abstract class BaseRepository<V extends BaseViewModel & T, M extends Base
relation.type === 'O2M' || relation.type === 'O2M' ||
relation.type === 'custom' relation.type === 'custom'
) { ) {
const collection = relation.foreignModel.COLLECTIONSTRING; const collection = relation.foreignViewModel.COLLECTIONSTRING;
if (!this.relationsByCollection[collection]) { if (!this.relationsByCollection[collection]) {
this.relationsByCollection[collection] = []; this.relationsByCollection[collection] = [];
} }
@ -214,9 +214,12 @@ export abstract class BaseRepository<V extends BaseViewModel & T, M extends Base
* *
* @param ids All model ids. * @param ids All model ids.
*/ */
public changedModels(ids: number[]): void { public changedModels(ids: number[], initialLoading: boolean): void {
ids.forEach(id => { ids.forEach(id => {
this.viewModelStore[id] = this.createViewModelWithTitles(this.DS.get(this.collectionString, id)); this.viewModelStore[id] = this.createViewModelWithTitles(
this.DS.get(this.collectionString, id),
initialLoading
);
this.updateViewModelObservable(id); this.updateViewModelObservable(id);
}); });
} }
@ -225,8 +228,13 @@ export abstract class BaseRepository<V extends BaseViewModel & T, M extends Base
* After creating a view model, all functions for models form the repo * After creating a view model, all functions for models form the repo
* are assigned to the new view model. * are assigned to the new view model.
*/ */
protected createViewModelWithTitles(model: M): V { protected createViewModelWithTitles(model: M, initialLoading: boolean): V {
const viewModel = this.relationManager.createViewModel(model, this.baseViewModelCtor, this.relationDefinitions); const viewModel = this.relationManager.createViewModel(
model,
this.baseViewModelCtor,
this.relationDefinitions,
initialLoading
);
viewModel.getTitle = () => this.getTitle(viewModel); viewModel.getTitle = () => this.getTitle(viewModel);
viewModel.getListTitle = () => this.getListTitle(viewModel); viewModel.getListTitle = () => this.getListTitle(viewModel);
viewModel.getVerboseName = this.getVerboseName; viewModel.getVerboseName = this.getVerboseName;

View File

@ -154,8 +154,8 @@ export class ConfigRepositoryService extends BaseRepository<ViewConfig, Config,
throw new Error('Config variables cannot be created'); throw new Error('Config variables cannot be created');
} }
public changedModels(ids: number[]): void { public changedModels(ids: number[], initialLoading: boolean): void {
super.changedModels(ids); super.changedModels(ids, initialLoading);
ids.forEach(id => { ids.forEach(id => {
this.updateConfigStructure(false, this.viewModelStore[id]); this.updateConfigStructure(false, this.viewModelStore[id]);

View File

@ -23,19 +23,19 @@ const MediafileRelations: RelationDefinition[] = [
type: 'M2O', type: 'M2O',
ownIdKey: 'parent_id', ownIdKey: 'parent_id',
ownKey: 'parent', ownKey: 'parent',
foreignModel: ViewMediafile foreignViewModel: ViewMediafile
}, },
{ {
type: 'M2M', type: 'M2M',
ownIdKey: 'access_groups_id', ownIdKey: 'access_groups_id',
ownKey: 'access_groups', ownKey: 'access_groups',
foreignModel: ViewGroup foreignViewModel: ViewGroup
}, },
{ {
type: 'M2M', type: 'M2M',
ownIdKey: 'inherited_access_groups_id', ownIdKey: 'inherited_access_groups_id',
ownKey: 'inherited_access_groups', ownKey: 'inherited_access_groups',
foreignModel: ViewGroup foreignViewModel: ViewGroup
} }
]; ];

View File

@ -20,20 +20,20 @@ const CategoryRelations: RelationDefinition[] = [
type: 'M2O', type: 'M2O',
ownIdKey: 'parent_id', ownIdKey: 'parent_id',
ownKey: 'parent', ownKey: 'parent',
foreignModel: ViewCategory foreignViewModel: ViewCategory
}, },
{ {
type: 'O2M', type: 'O2M',
foreignIdKey: 'category_id', foreignIdKey: 'category_id',
ownKey: 'motions', ownKey: 'motions',
foreignModel: ViewMotion, foreignViewModel: ViewMotion,
order: 'category_weight' order: 'category_weight'
}, },
{ {
type: 'O2M', type: 'O2M',
foreignIdKey: 'parent_id', foreignIdKey: 'parent_id',
ownKey: 'children', ownKey: 'children',
foreignModel: ViewCategory, foreignViewModel: ViewCategory,
order: 'weight' order: 'weight'
} }
]; ];

View File

@ -20,7 +20,7 @@ const MotionBlockRelations: RelationDefinition[] = [
type: 'O2M', type: 'O2M',
foreignIdKey: 'motion_block_id', foreignIdKey: 'motion_block_id',
ownKey: 'motions', ownKey: 'motions',
foreignModel: ViewMotion foreignViewModel: ViewMotion
} }
]; ];

View File

@ -23,13 +23,13 @@ const MotionCommentSectionRelations: RelationDefinition[] = [
type: 'M2M', type: 'M2M',
ownIdKey: 'read_groups_id', ownIdKey: 'read_groups_id',
ownKey: 'read_groups', ownKey: 'read_groups',
foreignModel: ViewGroup foreignViewModel: ViewGroup
}, },
{ {
type: 'M2M', type: 'M2M',
ownIdKey: 'write_groups_id', ownIdKey: 'write_groups_id',
ownKey: 'write_groups', ownKey: 'write_groups',
foreignModel: ViewGroup foreignViewModel: ViewGroup
} }
]; ];

View File

@ -16,6 +16,7 @@ import { DiffLinesInParagraph, DiffService } from 'app/core/ui-services/diff.ser
import { TreeIdNode } from 'app/core/ui-services/tree.service'; import { TreeIdNode } from 'app/core/ui-services/tree.service';
import { Motion } from 'app/shared/models/motions/motion'; import { Motion } from 'app/shared/models/motions/motion';
import { MotionPoll } from 'app/shared/models/motions/motion-poll'; import { MotionPoll } from 'app/shared/models/motions/motion-poll';
import { Submitter } from 'app/shared/models/motions/submitter';
import { ViewUnifiedChange } from 'app/shared/models/motions/view-unified-change'; import { ViewUnifiedChange } from 'app/shared/models/motions/view-unified-change';
import { PersonalNoteContent } from 'app/shared/models/users/personal-note'; import { PersonalNoteContent } from 'app/shared/models/users/personal-note';
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile'; import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
@ -73,43 +74,44 @@ const MotionRelations: RelationDefinition[] = [
type: 'M2O', type: 'M2O',
ownIdKey: 'state_id', ownIdKey: 'state_id',
ownKey: 'state', ownKey: 'state',
foreignModel: ViewState foreignViewModel: ViewState
}, },
{ {
type: 'M2O', type: 'M2O',
ownIdKey: 'recommendation_id', ownIdKey: 'recommendation_id',
ownKey: 'recommendation', ownKey: 'recommendation',
foreignModel: ViewState foreignViewModel: ViewState
}, },
{ {
type: 'M2O', type: 'M2O',
ownIdKey: 'workflow_id', ownIdKey: 'workflow_id',
ownKey: 'workflow', ownKey: 'workflow',
foreignModel: ViewWorkflow foreignViewModel: ViewWorkflow
}, },
{ {
type: 'M2O', type: 'M2O',
ownIdKey: 'category_id', ownIdKey: 'category_id',
ownKey: 'category', ownKey: 'category',
foreignModel: ViewCategory foreignViewModel: ViewCategory
}, },
{ {
type: 'M2O', type: 'M2O',
ownIdKey: 'motion_block_id', ownIdKey: 'motion_block_id',
ownKey: 'motion_block', ownKey: 'motion_block',
foreignModel: ViewMotionBlock foreignViewModel: ViewMotionBlock
}, },
{ {
type: 'nested', type: 'nested',
ownKey: 'submitters', ownKey: 'submitters',
foreignModel: ViewSubmitter, foreignViewModel: ViewSubmitter,
foreignModel: Submitter,
order: 'weight', order: 'weight',
relationDefinition: [ relationDefinition: [
{ {
type: 'M2O', type: 'M2O',
ownIdKey: 'user_id', ownIdKey: 'user_id',
ownKey: 'user', ownKey: 'user',
foreignModel: ViewUser foreignViewModel: ViewUser
} }
] ]
}, },
@ -117,31 +119,31 @@ const MotionRelations: RelationDefinition[] = [
type: 'M2M', type: 'M2M',
ownIdKey: 'supporters_id', ownIdKey: 'supporters_id',
ownKey: 'supporters', ownKey: 'supporters',
foreignModel: ViewUser foreignViewModel: ViewUser
}, },
{ {
type: 'M2M', type: 'M2M',
ownIdKey: 'attachments_id', ownIdKey: 'attachments_id',
ownKey: 'attachments', ownKey: 'attachments',
foreignModel: ViewMediafile foreignViewModel: ViewMediafile
}, },
{ {
type: 'M2M', type: 'M2M',
ownIdKey: 'tags_id', ownIdKey: 'tags_id',
ownKey: 'tags', ownKey: 'tags',
foreignModel: ViewTag foreignViewModel: ViewTag
}, },
{ {
type: 'M2O', type: 'M2O',
ownIdKey: 'parent_id', ownIdKey: 'parent_id',
ownKey: 'parent', ownKey: 'parent',
foreignModel: ViewMotion foreignViewModel: ViewMotion
}, },
{ {
type: 'M2M', type: 'M2M',
ownIdKey: 'change_recommendations_id', ownIdKey: 'change_recommendations_id',
ownKey: 'changeRecommendations', ownKey: 'changeRecommendations',
foreignModel: ViewMotionChangeRecommendation foreignViewModel: ViewMotionChangeRecommendation
} }
// Personal notes are dynamically added in the repo. // Personal notes are dynamically added in the repo.
]; ];
@ -212,7 +214,7 @@ export class MotionRepositoryService extends BaseIsAgendaItemAndListOfSpeakersCo
protected groupRelationsByCollections(): void { protected groupRelationsByCollections(): void {
this.relationDefinitions.push({ this.relationDefinitions.push({
type: 'custom', type: 'custom',
foreignModel: ViewPersonalNote, foreignViewModel: ViewPersonalNote,
setRelations: (motion: Motion, viewMotion: ViewMotion) => { setRelations: (motion: Motion, viewMotion: ViewMotion) => {
viewMotion.personalNote = this.getPersonalNoteForMotion(motion); viewMotion.personalNote = this.getPersonalNoteForMotion(motion);
}, },
@ -269,8 +271,8 @@ export class MotionRepositoryService extends BaseIsAgendaItemAndListOfSpeakersCo
return this.translate.instant(plural ? 'Motions' : 'Motion'); return this.translate.instant(plural ? 'Motions' : 'Motion');
}; };
protected createViewModelWithTitles(model: Motion): ViewMotion { protected createViewModelWithTitles(model: Motion, initialLoading: boolean): ViewMotion {
const viewModel = super.createViewModelWithTitles(model); const viewModel = super.createViewModelWithTitles(model, initialLoading);
viewModel.getIdentifierOrTitle = () => this.getIdentifierOrTitle(viewModel); viewModel.getIdentifierOrTitle = () => this.getIdentifierOrTitle(viewModel);
viewModel.getProjectorTitle = () => this.getAgendaSlideTitle(viewModel); viewModel.getProjectorTitle = () => this.getAgendaSlideTitle(viewModel);
return viewModel; return viewModel;

View File

@ -18,13 +18,13 @@ const StateRelations: RelationDefinition[] = [
type: 'M2O', type: 'M2O',
ownIdKey: 'workflow_id', ownIdKey: 'workflow_id',
ownKey: 'workflow', ownKey: 'workflow',
foreignModel: ViewWorkflow foreignViewModel: ViewWorkflow
}, },
{ {
type: 'M2M', type: 'M2M',
ownIdKey: 'next_states_id', ownIdKey: 'next_states_id',
ownKey: 'next_states', ownKey: 'next_states',
foreignModel: ViewState foreignViewModel: ViewState
} }
]; ];

View File

@ -19,13 +19,13 @@ const WorkflowRelations: RelationDefinition[] = [
type: 'M2M', type: 'M2M',
ownIdKey: 'states_id', ownIdKey: 'states_id',
ownKey: 'states', ownKey: 'states',
foreignModel: ViewState foreignViewModel: ViewState
}, },
{ {
type: 'M2O', type: 'M2O',
ownIdKey: 'first_state_id', ownIdKey: 'first_state_id',
ownKey: 'first_state', ownKey: 'first_state',
foreignModel: ViewState foreignViewModel: ViewState
} }
]; ];

View File

@ -28,7 +28,7 @@ const ProjectorRelations: RelationDefinition[] = [
type: 'M2O', type: 'M2O',
ownIdKey: 'reference_projector_id', ownIdKey: 'reference_projector_id',
ownKey: 'referenceProjector', ownKey: 'referenceProjector',
foreignModel: ViewProjector foreignViewModel: ViewProjector
} }
]; ];

View File

@ -18,7 +18,7 @@ const TopicRelations: RelationDefinition[] = [
type: 'M2M', type: 'M2M',
ownIdKey: 'attachments_id', ownIdKey: 'attachments_id',
ownKey: 'attachments', ownKey: 'attachments',
foreignModel: ViewMediafile foreignViewModel: ViewMediafile
} }
]; ];

View File

@ -30,7 +30,7 @@ const UserRelations: RelationDefinition[] = [
type: 'M2M', type: 'M2M',
ownIdKey: 'groups_id', ownIdKey: 'groups_id',
ownKey: 'groups', ownKey: 'groups',
foreignModel: ViewGroup foreignViewModel: ViewGroup
} }
]; ];
@ -147,8 +147,8 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User, UserTi
/** /**
* Adds teh short and full name to the view user. * Adds teh short and full name to the view user.
*/ */
protected createViewModelWithTitles(model: User): ViewUser { protected createViewModelWithTitles(model: User, initialLoading: boolean): ViewUser {
const viewModel = super.createViewModelWithTitles(model); const viewModel = super.createViewModelWithTitles(model, initialLoading);
viewModel.getFullName = () => this.getFullName(viewModel); viewModel.getFullName = () => this.getFullName(viewModel);
viewModel.getShortName = () => this.getShortName(viewModel); viewModel.getShortName = () => this.getShortName(viewModel);
return viewModel; return viewModel;

View File

@ -15,8 +15,12 @@ export interface AssignmentOptionVote {
export class AssignmentPollOption extends BaseModel<AssignmentPollOption> { export class AssignmentPollOption extends BaseModel<AssignmentPollOption> {
public static COLLECTIONSTRING = 'assignments/assignment-poll-option'; public static COLLECTIONSTRING = 'assignments/assignment-poll-option';
public id: number; // The AssignmentUser id of the candidate public id: number; // The AssignmentPollOption id
public candidate_id: number; // the User id of the candidate public candidate_id: number; // the user id of the candidate
public get user_id(): number {
// to be consistent with user...
return this.candidate_id;
}
public is_elected: boolean; public is_elected: boolean;
public votes: AssignmentOptionVote[]; public votes: AssignmentOptionVote[];
public poll_id: number; public poll_id: number;

View File

@ -29,11 +29,8 @@ export class ViewAssignmentPollOption extends BaseViewModel<AssignmentPollOption
return this.option.id; return this.option.id;
} }
/**
* Note: "User" instead of "candidate" to be consistent.
*/
public get user_id(): number { public get user_id(): number {
return this.option.candidate_id; return this.option.user_id;
} }
public get is_elected(): boolean { public get is_elected(): boolean {

View File

@ -186,7 +186,7 @@ class RedisCacheProvider:
if (nd > 0) then if (nd > 0) then
max = 3 + nc + nd max = 3 + nc + nd
redis.call('hdel', KEYS[1], unpack(ARGV, 4 + nc, max)) redis.call('hdel', KEYS[1], unpack(ARGV, 4 + nc, max))
for i = 4 + nc, max, 2 do for i = 4 + nc, max, 1 do
redis.call('zadd', KEYS[2], change_id, ARGV[i]) redis.call('zadd', KEYS[2], change_id, ARGV[i])
end end
end end