Skip to content

Conversation

Girik1105
Copy link
Contributor

@Girik1105 Girik1105 commented Jan 31, 2025

Guidelines for Pull Requests

If you haven't yet read our code review guidelines, please do so, You can find them here.

Please confirm the following by adding an x for each item (turn [ ] into [x]).

  • I have removed all code style changes that are not necessary (e.g. changing blanks across the whole file that don’t need to be changed, adding empty lines in parts other than your own code)
  • I am not making any changes to files that don’t have any effect (e.g. imports added that don’t need to be added)
  • I do not have any sysout statements in my code or commented out code that isn’t needed anymore
  • I am not reformatting any files in the wrong format or without cause.
  • I am not changing file encoding or line endings to something else than UTF-8, LF
  • My pull request does not show an insane amount of files being changed although my ticket only requires a few files being changed
  • I have added Javadoc/documentation where appropriate
  • I have added test cases where appropriate
  • I have explained any part of my code/implementation decisions that is not be self-explanatory

Please provide a brief description of your ticket

Unable to edit/delete appellations once created in Text Annotation View

There is no way to edit the created notation. One a notation is created, it is not addressed to edit / how to edit the created notation.

#46

VOGRE-54

Are there any other pull requests that this one depends on?

No

Anything else the reviewer needs to know?

... describe here ...

@diging-jenkins
Copy link
Collaborator

Can one of the admins verify this patch?

@Girik1105 Girik1105 mentioned this pull request Jan 31, 2025
9 tasks
concept = Concept.objects.get(uri=request.data['interpretation'])
instance.interpretation = concept
except Concept.DoesNotExist:
pass
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

an appellation without a concept should not be savable.

if in_relations:
return Response(
{"detail": "Cannot delete an Appellation used in a Relation."},
status=status.HTTP_403_FORBIDDEN
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forbidden is used for permission issues, here we maybe want to use 409 Conflict ("The 409 (Conflict) status code indicates that the request could not be completed due to a conflict with the current state of the target resource. This code is used in situations where the user might be able to resolve the conflict and resubmit the request. ")

)

self.perform_destroy(instance)
return Response(status=status.HTTP_204_NO_CONTENT)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this indicates that the appellation has been deleted, it should return OK 200.

logger.error(f"Error deleting appellation: {str(e)}", exc_info=True)
return Response(
{"detail": f"Error deleting appellation: {str(e)}"},
status=status.HTTP_400_BAD_REQUEST
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this is for any exception that may happen, it should be 500.

@jdamerow jdamerow closed this Feb 6, 2025
@Girik1105 Girik1105 reopened this Feb 7, 2025
if in_relations:
return Response(
{"detail": "Cannot delete an Appellation used in a Relation."},
status=status.HTTP_409_CONFLICT
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

conflicts are typically send with there is a version of the sent resource that is different than the current one in the system (or different from what the sender based their changes on), so more like a GitHub conflict. Here I would send a bad request (since this should not be allowed in the first place to delete appellations used in a relation).

@jdamerow jdamerow requested a review from jophals March 7, 2025 16:55
@jdamerow
Copy link
Member

jdamerow commented Mar 7, 2025

Added Julian as reviewer especially for the Vue stuff.

store.commit('resetCreateAppelltionsToText');

// Notify parent components about deselection
this.$root.$emit('appellationDeselected', this.appellation);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's fairly conflicting to use both Vuex and the $root instance to manage state. Generally considered a better idea to use Vuex but I would stick to one.

@jdamerow
Copy link
Member

and now also conflicts

@jdamerow jdamerow closed this Mar 11, 2025
@Girik1105 Girik1105 reopened this Mar 12, 2025
@Girik1105 Girik1105 reopened this Apr 8, 2025
return True # Let has_object_permission handle it

if request.method in ['PUT', 'PATCH']:
return True # Let has_object_permission handle it
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not add all of the above to line 48? with a comment that includes the comments here?

@jdamerow jdamerow closed this Apr 17, 2025
@Girik1105 Girik1105 reopened this Apr 17, 2025
@jdamerow jdamerow closed this Apr 22, 2025
@Girik1105 Girik1105 reopened this Apr 22, 2025
// Force a refresh of positions for updated appellation
this.$nextTick(() => {
this.updatePosition();
EventBus.$emit('updateposition');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where is this emitted event caught? Is that here?


If so, wouldn't there be two updatePosition methods be called that are very similar?

Also, why are there two methods that are almost but not quite identical?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still. There seem to be two functions that are very similar.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The functions do perform similar tasks, they take a representation text selection and use the utility function calculateOverlayPositions to convert those positions into screen coordinates suitable for showing the highlight or overlay over the text.

But they have a few differences:

updatePosition in AppellationDisplay calculates overlays for pre-existing, saved appellations (using this.appellation.position).

TextDisplay.js calculates overlays for the current, user selection (using this.selected) typically for creating new annotations or providing real-time selection feedback. It also includes logic to clear the selection display.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still confused. This code:

this.updatePosition();
EventBus.$emit('updateposition');

first calls below updatePosition function, right? Then it emits an event updateposition. Won't this event be caught in textdisplay.js line 45, which will then call the updatePosition function in textdisplay.js?

this.deleteError = "Error deleting annotation, Please try again later.";
}

// Clear error message after 5 seconds
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error messages should not be automatically cleared. What if the user turns around or goes to get coffee and misses the error message?

v-on:selectappellation="selectAppellation"
v-on:removeAppellation="removeAppellation($event)"
v-on:addAppellation="addAppellation($event)"
v-on:addAppellation="addAppellation($event)"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation?

@jdamerow jdamerow closed this May 1, 2025
@Girik1105 Girik1105 reopened this May 13, 2025
// Force a refresh of positions for updated appellation
this.$nextTick(() => {
this.updatePosition();
EventBus.$emit('updateposition');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still. There seem to be two functions that are very similar.

getFormattedDate: function (isodate) {
return moment(isodate).format('dddd LL [at] LT');
}
if (typeof moment !== 'undefined') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when would this happen? wouldn't this be something that can be avoided by ensuring moment is included?

const deletedAppId = this.appellation.id;

if (typeof Appellation === 'undefined' || !Appellation.delete) {
this.deleteError = "Cannot delete: Appellation resource unavailable.";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this mean? Does this mean the appellation doesn't exist on the server? This is the error message that's being shown to the user, right? So it should be something the user can understand.

try {
const editingApp = JSON.parse(editingAppData);
// Check if the parsed data has an id and it matches the deleted item's id
if (editingApp && typeof editingApp.id !== 'undefined' && editingApp.id === deletedAppId) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a reason to use typeof editingApp.id !== 'undefined' over editingApp.id !== undefined?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the typeof function returns the string "undefined" for variables that are declared but not initialized, or for object properties that don't exist so using "undefined" just adds an extra layer of checking.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there anything in this file that was actually changed? if no, it should not be changed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Im not sure why this is happening, I didn't change anything in this file. I even tried to copy paste the file from develop, but it still shows this.

@jdamerow jdamerow closed this May 16, 2025
@Girik1105 Girik1105 reopened this May 22, 2025
this.position = calc.position;
this.mid_lines = calc.mid_lines;
this.end_position = calc.end_position;
this.line_height = calc.line_height;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this exact same code is also called in textdisplay.js:

this.position     = calc.position;
this.mid_lines    = calc.mid_lines;
this.end_position = calc.end_position;
this.line_height  = calc.line_height;

// Force a refresh of positions for updated appellation
this.$nextTick(() => {
this.updatePosition();
EventBus.$emit('updateposition');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still confused. This code:

this.updatePosition();
EventBus.$emit('updateposition');

first calls below updatePosition function, right? Then it emits an event updateposition. Won't this event be caught in textdisplay.js line 45, which will then call the updatePosition function in textdisplay.js?

@jdamerow jdamerow closed this Jun 6, 2025
@Girik1105
Copy link
Contributor Author

So out here each component only updates its own position data. They don't call each other's methods.

What both the updatePosition functions do:

  1. TextSelectionDisplay.updatePosition() (textdisplay.js lines 67-78)
    Creates temporary grey/blue highlight overlays when user selects text to create new appellations. When a user is selecting the text for updating an existing appellation.

  2. AppellationDisplayItem.updatePosition() (appellationdisplay.js lines 119-125):
    Creates permanent colored highlight overlays for existing saved appellations. This pulls the position from the database, this is the yellow highlight of the saved appellations

Workflow for when a user creates a new appellation:

INITIAL STATE:

  • User sees text and any existing colored highlights (permanent appellations)
  • No current text selection (no blue highlight)
  1. User drags mouse to select text (e.g., offsets 100–200)
  2. TextDisplay.handleMouseup() captures the selection:
    • Sets: this.selected = { startOffset: 100, endOffset: 200, representation: "Ferdinand Schmutzer" }
  3. TextSelectionDisplay.updatePosition() triggers automatically (because it watches 'selected'):
    • Converts selection offsets (100–200) to screen coordinates
    • Updates its own data to show a blue.grey highlight over the selected text
  4. User confirms creation
    • Appellation is saved to the database with position 100–200
  5. New AppellationDisplayItem is created for the new appellation:
    • Calls AppellationDisplayItem.updatePosition()
    • Shows a permanent colored highlight at 100–200
  6. EventBus.$emit('updateposition') is fired
    • Notifies all components (TextSelectionDisplay, all AppellationDisplayItems) to recalculate their positions
    • Ensures all highlights are perfectly synchronized
  7. TextSelectionDisplay.selected is cleared (no selection)
    • Blue highlight disappears

FINAL STATE:

  • The new colored highlight (permanent) is visible at 100–200
  • No blue highlight (no active selection)
  • All highlights are in sync with the current layout

The Workflow for editing an appellation and how each function works:

INITIAL STATE:

  • Appellation 1 highlighted at positions 150-200 example: "Ferdinand Schmutzer"
  • No current text selection
  1. User clicks the edit icon on Appellation 1
  2. EditAppellation() function:
    • localStorage.setItem('editingAppellation', JSON.stringify(appellation))
    • EventBus.$emit('startEdit') → Sets isEditing = true
  3. User selects NEW text at positions 300-350 ("Albert Einstein")
  4. TextDisplay.handleMouseup() in edit mode:
    • Creates selection: { startOffset: 300, endOffset: 350, representation: "Albert Einstein" }
  5. TextSelectionDisplay.updatePosition() triggers (watch: selected):
    • calculateOverlayPositions({ startOffset: 300, endOffset: 350 })
    • applyOverlayPositions(this, calc)
    • Shows BLUE highlight at positions 300-350 (temporary)
  6. Server update happens:
    • Appellation.update() sends new position_value: "300,350"
    • Backend responds with updated appellation data
  7. AppellationDisplayItem receives 'appellationUpdated' event:
    • Object.assign(this.appellation, updatedAppellation)
    • this.appellation.position.startOffset = 300
    • this.appellation.position.endOffset = 350
  8. AppellationDisplayItem.updatePosition() fires:
    • calculateOverlayPositions(this.appellation.position) // 300-350
    • applyOverlayPositions(this, calc)
    • Moves COLORED highlight from 150-200 → 300-350
    • EventBus.$emit('updateposition') // Notify others
  9. TextSelectionDisplay receives 'updateposition' event:
    • sleep(200).then(self.updatePosition)
    • TextSelectionDisplay.updatePosition() fires:
      • Recalculates current selection highlight (if any)
      • Ensures blue highlight stays accurate
  10. Edit cleanup:
    • localStorage.removeItem('editingAppellation')
    • EventBus.$emit('cancelEdit') → Sets isEditing = false
    • Blue highlight disappears (selection cleared)

FINAL STATE:

  • Appellation 1 is now highlighted from positions 150-200 ("Ferdinand Schmutzer") to positions 300-350 ("Albert Einstein")
  • No current text selection

Why TextSelectionDisplay Needs to Update:

  • When an appellation changes position, it can affect the text layout in ways that make the current text selection highlight appear in the wrong place.

Different updatePosition() Methods

  • They're different functions on different component instances:
  • AppellationDisplayItem.updatePosition() - Updates permanent appellation highlights
  • TextSelectionDisplay.updatePosition() - Updates temporary text selection highlights

Its not a circular loop:

  • TextSelectionDisplay.updatePosition() (line 62-73) does NOT emit any events
  • It only updates its own position data and stops
  • The chain ends there

@Girik1105 Girik1105 reopened this Jun 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants