Skip to content

Commit 617fc01

Browse files
apply various feedback
1 parent 403b061 commit 617fc01

File tree

8 files changed

+61
-67
lines changed

8 files changed

+61
-67
lines changed

frontend/javascripts/admin/rest_api.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2050,9 +2050,7 @@ export async function getAgglomeratesForSegmentsFromDatastore<T extends number |
20502050
);
20512051
});
20522052
// Ensure that the values are bigint if the keys are bigint
2053-
const adaptToType = Utils.isBigInt(segmentIds[0])
2054-
? (el: NumberLike) => BigInt(el)
2055-
: (el: NumberLike) => el;
2053+
const adaptToType = Utils.getAdaptToTypeFunctionFromList(segmentIds);
20562054
const keyValues = _.zip(segmentIds, parseProtoListOfLong(listArrayBuffer).map(adaptToType));
20572055
// @ts-ignore
20582056
return new Map(keyValues);
@@ -2094,9 +2092,7 @@ export async function getAgglomeratesForSegmentsFromTracingstore<T extends numbe
20942092
});
20952093

20962094
// Ensure that the values are bigint if the keys are bigint
2097-
const adaptToType = Utils.isBigInt(segmentIds[0])
2098-
? (el: NumberLike) => BigInt(el)
2099-
: (el: NumberLike) => el;
2095+
const adaptToType = Utils.getAdaptToTypeFunctionFromList(segmentIds);
21002096

21012097
const keyValues = _.zip(segmentIds, parseProtoListOfLong(listArrayBuffer).map(adaptToType));
21022098
// @ts-ignore

frontend/javascripts/libs/utils.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import type { BoundingBoxMinMaxType } from "types/bounding_box";
88
import type { ArbitraryObject, Comparator } from "types/globals";
99
import type { ColorObject, Point3, TypedArray, Vector3, Vector4, Vector6 } from "viewer/constants";
1010
import type { TreeGroup } from "viewer/model/types/tree_types";
11-
import type { BoundingBoxObject, NumberLike, SegmentGroup } from "viewer/store";
11+
import type { BoundingBoxObject, Mapping, NumberLike, SegmentGroup } from "viewer/store";
1212

1313
type UrlParams = Record<string, string>;
1414

@@ -1257,6 +1257,16 @@ export function isNumberMap(x: Map<NumberLike, NumberLike>): x is Map<number, nu
12571257
return Boolean(typeof value[0] === "number");
12581258
}
12591259

1260+
export function getAdaptToTypeFunction(mapping: Mapping | null | undefined) {
1261+
return mapping && isNumberMap(mapping) ? (el: number) => el : (el: number) => BigInt(el);
1262+
}
1263+
1264+
export function getAdaptToTypeFunctionFromList<T extends number | bigint>(list: Array<T>) {
1265+
return list[0] == null || Boolean(typeof list[0] === "number")
1266+
? (el: NumberLike) => el
1267+
: (el: NumberLike) => BigInt(el);
1268+
}
1269+
12601270
export function isBigInt(x: NumberLike): x is bigint {
12611271
return typeof x === "bigint";
12621272
}

frontend/javascripts/test/sagas/proofreading/proofreading_skeleton_interaction.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,3 +767,4 @@ describe("Proofreading (With Agglomerate Skeleton interactions)", () => {
767767
// TODOM: write tests for cutFromAllNeighbours -> new test file
768768
// TODOM: write tests for partitionedMinCut -> new test file
769769
// TODOM: Write a test that pulling update fro the backend does not yield diff tracing changes that would be pushed to the backend in a live collab scenario!
770+
// TODOM: Write test without injected version and test via spy or so that no rebasing was tried and thus only the update actions were sent to the server!

frontend/javascripts/viewer/model/reducers/save_reducer.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,10 +210,6 @@ function SaveReducer(state: WebknossosState, action: Action): WebknossosState {
210210
});
211211
}
212212

213-
case "SET_MAPPING": {
214-
return state;
215-
}
216-
217213
case "PREPARE_REBASING": {
218214
const rebaseInfo = state.save.rebaseRelevantServerAnnotationState;
219215
console.error("Setting isRebasing = true");

frontend/javascripts/viewer/model/sagas/saving/save_saga.ts

Lines changed: 41 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { getAgglomeratesForSegmentsFromTracingstore, getUpdateActionLog } from "admin/rest_api";
22
import ErrorHandling from "libs/error_handling";
33
import Toast from "libs/toast";
4-
import { isNumberMap, sleep } from "libs/utils";
4+
import { getAdaptToTypeFunction, sleep } from "libs/utils";
55
import _ from "lodash";
66
import { buffers, type Channel } from "redux-saga";
77
import { actionChannel, call, delay, flush, fork, put, race, takeEvery } from "typed-redux-saga";
@@ -404,8 +404,7 @@ export function* tryToIncorporateActions(
404404
case "updateUserBoundingBoxVisibilityInVolumeTracing":
405405
case "updateSegmentGroupsExpandedState": {
406406
if (areUnsavedChangesOfUser) {
407-
// TODOM: Missing actions still need support in applyVolumeUpdateActionsFromServerAction
408-
// TODOM: --------------------- User specific actions must be reapplied if local actions!!!!!! ------------------------------------------
407+
// Maybe write tests for these? Maybe part of M4
409408
yield* put(applyVolumeUpdateActionsFromServerAction([action]));
410409
}
411410
break;
@@ -603,53 +602,61 @@ export function* tryToIncorporateActions(
603602
return { success: true };
604603
}
605604

605+
type IdsToReloadPerMapping = Record<string, number[]>;
606+
607+
// Gathers mapped agglomerate ids for unknown but relevant segments to apply the passed save queue entries correctly.
608+
// This is needed in case proofreading was done via mesh interactions whose mapping info is present in the meshes
609+
// but not in the activeMappingByLayer.mapping. Due to incorporating backend updates the agglomerate ids of the
610+
// meshes might be outdated, thus we reload this info and store it in the local mapping to perform the correct merge.
611+
// Returns a list of segment ids to reload for each needed volume / editable tracing id.
606612
function* getAllUnknownSegmentIdsInPendingUpdates(
607613
saveQueue: SaveQueueEntry[],
608-
): Saga<Record<string, number[]>> {
614+
): Saga<IdsToReloadPerMapping> {
609615
const activeMappingByLayer = yield* select(
610616
(store) => store.temporaryConfiguration.activeMappingByLayer,
611617
);
612-
const idsToRequest = {} as Record<string, number[]>;
618+
const idsToRequestByLayerId = {} as IdsToReloadPerMapping;
613619
for (const saveQueueEntry of saveQueue) {
614620
for (const action of saveQueueEntry.actions) {
615621
switch (action.name) {
616622
case "mergeAgglomerate":
617623
case "splitAgglomerate": {
618624
const { segmentId1, segmentId2, actionTracingId } = action.value;
619-
const upToDateMapping = activeMappingByLayer[actionTracingId]?.mapping;
620-
if (!upToDateMapping || segmentId1 == null || segmentId2 == null) {
625+
const mappingSyncedWithBackend = activeMappingByLayer[actionTracingId]?.mapping;
626+
if (!mappingSyncedWithBackend || segmentId1 == null || segmentId2 == null) {
621627
continue;
622628
}
623-
const adaptToType =
624-
upToDateMapping && isNumberMap(upToDateMapping)
625-
? (el: number) => el
626-
: (el: number) => BigInt(el);
627-
const upToDateAgglomerateId1 = (upToDateMapping as NumberLikeMap).get(
629+
630+
const adaptToType = getAdaptToTypeFunction(mappingSyncedWithBackend);
631+
const updatedAgglomerateId1 = (mappingSyncedWithBackend as NumberLikeMap).get(
628632
adaptToType(segmentId1),
629633
);
630-
const upToDateAgglomerateId2 = (upToDateMapping as NumberLikeMap).get(
634+
const updatedAgglomerateId2 = (mappingSyncedWithBackend as NumberLikeMap).get(
631635
adaptToType(segmentId2),
632636
);
633-
if (!upToDateAgglomerateId1) {
634-
if (!(actionTracingId in idsToRequest)) {
635-
idsToRequest[actionTracingId] = [];
637+
if (!updatedAgglomerateId1) {
638+
if (!(actionTracingId in idsToRequestByLayerId)) {
639+
idsToRequestByLayerId[actionTracingId] = [];
636640
}
637-
idsToRequest[actionTracingId].push(segmentId1);
641+
idsToRequestByLayerId[actionTracingId].push(segmentId1);
638642
}
639-
if (!upToDateAgglomerateId2) {
640-
if (!(actionTracingId in idsToRequest)) {
641-
idsToRequest[actionTracingId] = [];
643+
if (!updatedAgglomerateId2) {
644+
if (!(actionTracingId in idsToRequestByLayerId)) {
645+
idsToRequestByLayerId[actionTracingId] = [];
642646
}
643-
idsToRequest[actionTracingId].push(segmentId2);
647+
idsToRequestByLayerId[actionTracingId].push(segmentId2);
644648
}
645649
}
646650
}
647651
}
648652
}
649-
return idsToRequest;
653+
return idsToRequestByLayerId;
650654
}
651655

652-
function* addMissingSegmentsToLoadedMappings(idsToRequest: Record<string, number[]>): Saga<void> {
656+
// For each passed mapping reload the segment ids' mapping information and store it in the local mapping.
657+
// Needed after getAllUnknownSegmentIdsInPendingUpdates to load updated mapping info for segment ids of
658+
// mesh interaction proofreading actions to ensure reapplying these actions is done with up-to-date mapping info.
659+
function* addMissingSegmentsToLoadedMappings(idsToRequest: IdsToReloadPerMapping): Saga<void> {
653660
const annotationId = yield* select((state) => state.annotation.annotationId);
654661
const version = yield* select((state) => state.annotation.version);
655662
const tracingStoreUrl = yield* select((state) => state.annotation.tracingStore.url);
@@ -661,7 +668,7 @@ function* addMissingSegmentsToLoadedMappings(idsToRequest: Record<string, number
661668
continue;
662669
}
663670
const activeMapping = activeMappingByLayer[volumeTracingId];
664-
// Ask the server to map the (split) segment ids. This creates a partial mapping
671+
// Ask the server to map the segment ids needing reloading. This creates a partial mapping
665672
// that only contains these ids.
666673
const mappingWithMissingIds = yield* call(
667674
getAgglomeratesForSegmentsFromTracingstore,
@@ -684,6 +691,11 @@ function* addMissingSegmentsToLoadedMappings(idsToRequest: Record<string, number
684691
}
685692
}
686693

694+
// Gathers info mapped info for segment ids from proofreading actions where the mapping is unknown.
695+
// This happens in case of mesh proofreading actions. To re-apply the user's changes in the rebasing
696+
// up-to-date mapping info is needed for all segments in all proofreading actions. Thus, the missing info
697+
// is first loaded and then the save queue update actions are remapped to update their agglomerate id infos
698+
// to apply them correctly during rebasing. Last the save queue is replaced with the updated save queue entries.
687699
function* updateSaveQueueEntriesToStateAfterRebase(): Saga<
688700
| {
689701
success: false;
@@ -710,8 +722,8 @@ function* updateSaveQueueEntriesToStateAfterRebase(): Saga<
710722
case "mergeAgglomerate":
711723
case "splitAgglomerate": {
712724
const { segmentId1, segmentId2, actionTracingId } = action.value;
713-
const upToDateMapping = activeMappingByLayer[actionTracingId]?.mapping;
714-
if (!upToDateMapping) {
725+
const mappingSyncedWithBackend = activeMappingByLayer[actionTracingId]?.mapping;
726+
if (!mappingSyncedWithBackend) {
715727
console.error(
716728
"Found proofreading action without matching mapping in save queue. This should never happen.",
717729
action,
@@ -728,14 +740,11 @@ function* updateSaveQueueEntriesToStateAfterRebase(): Saga<
728740
return null;
729741
}
730742

731-
const adaptToType =
732-
upToDateMapping && isNumberMap(upToDateMapping)
733-
? (el: number) => el
734-
: (el: number) => BigInt(el);
735-
let upToDateAgglomerateId1 = (upToDateMapping as NumberLikeMap).get(
743+
const adaptToType = getAdaptToTypeFunction(mappingSyncedWithBackend);
744+
let upToDateAgglomerateId1 = (mappingSyncedWithBackend as NumberLikeMap).get(
736745
adaptToType(segmentId1),
737746
);
738-
let upToDateAgglomerateId2 = (upToDateMapping as NumberLikeMap).get(
747+
let upToDateAgglomerateId2 = (mappingSyncedWithBackend as NumberLikeMap).get(
739748
adaptToType(segmentId2),
740749
);
741750
if (!upToDateAgglomerateId1 || !upToDateAgglomerateId2) {

frontend/javascripts/viewer/model/sagas/volume/proofread_saga.ts

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
} from "admin/rest_api";
99
import { V3 } from "libs/mjs";
1010
import Toast from "libs/toast";
11-
import { SoftError, isNumberMap } from "libs/utils";
11+
import { SoftError, getAdaptToTypeFunction, isNumberMap } from "libs/utils";
1212
import window from "libs/window";
1313
import _ from "lodash";
1414
import messages from "messages";
@@ -148,10 +148,6 @@ export default function* proofreadRootSaga(): Saga<void> {
148148
yield* takeEvery("TOGGLE_SEGMENT_IN_PARTITION", showToastIfSegmentOfOtherAgglomerateWasSelected);
149149
}
150150

151-
function getAdaptToTypeFunction(mapping: Mapping | null | undefined) {
152-
return mapping && isNumberMap(mapping) ? (el: number) => el : (el: number) => BigInt(el);
153-
}
154-
155151
function* clearMinCutPartitionsOnMultiCutDeselect(
156152
action: UpdateUserSettingAction | EscapeAction,
157153
): Saga<void> {
@@ -530,21 +526,6 @@ function* handleSkeletonProofreadingAction(action: Action): Saga<void> {
530526
targetAgglomerateId,
531527
);
532528

533-
// TODOM: just as below: check whether this is really needed
534-
/*
535-
if (action.type === "MERGE_TREES") {
536-
console.log("Calling updateMappingWithMerge again after saving was done.");
537-
// During saving, newer versions might have been pulled from the server.
538-
yield* call(
539-
updateMappingWithMerge,
540-
volumeTracingId,
541-
activeMapping,
542-
sourceAgglomerateId,
543-
targetAgglomerateId,
544-
);
545-
}
546-
*/
547-
548529
if (action.type === "MIN_CUT_AGGLOMERATE_WITH_NODE_IDS" || action.type === "DELETE_EDGE") {
549530
if (sourceAgglomerateId !== targetAgglomerateId) {
550531
Toast.error(

frontend/javascripts/viewer/model/sagas/volume/update_actions.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -933,7 +933,8 @@ export function splitAgglomerate(
933933
actionTracingId: string;
934934
segmentId1: number | undefined;
935935
segmentId2: number | undefined;
936-
// Needed in live collab setting to notice changes of loaded agglomerates done by other users.
936+
// agglomerateId is needed in live collab setting to notice changes of loaded agglomerates done by other users.
937+
// Kept up-to-date in save queue by updateSaveQueueEntriesToStateAfterRebase saga.
937938
agglomerateId?: number | undefined;
938939
// For backwards compatibility reasons,
939940
// older segments are defined using their positions (and mag)
@@ -970,7 +971,8 @@ export function mergeAgglomerate(
970971
actionTracingId: string;
971972
segmentId1: number | undefined;
972973
segmentId2: number | undefined;
973-
// Needed in live collab setting to notice changes of loaded agglomerates done by other users.
974+
// agglomerateId1 and agglomerateId2 are needed in live collab setting to notice changes of loaded agglomerates done by other users.
975+
// Kept up-to-date in save queue by updateSaveQueueEntriesToStateAfterRebase saga.
974976
agglomerateId1?: number;
975977
agglomerateId2?: number;
976978
// For backwards compatibility reasons,

frontend/javascripts/viewer/store.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -625,14 +625,13 @@ export const combinedReducer = reduceReducers(
625625
VolumeTracingReducer,
626626
ProofreadingReducer,
627627
TaskReducer,
628+
SaveReducer,
628629
FlycamReducer,
629630
FlycamInfoCacheReducer,
630631
ViewModeReducer,
631632
AnnotationReducer,
632633
UserReducer,
633634
UiReducer,
634-
// SaveReducer needs to be behind Settings and Volumetracing reducer to react to changes SET MAPPING changes.
635-
SaveReducer,
636635
ConnectomeReducer,
637636
OrganizationReducer,
638637
) as Reducer;

0 commit comments

Comments
 (0)