Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debug token delete #311

Merged
merged 4 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def _set_sqlite_case_insensitive_pragma(dbapi_con, connection_record):
""" This ensures that SQLite is not case-insensitive when using LIKEs"""
if isinstance(dbapi_con, SQLite3Connection):
dbapi_con.execute("PRAGMA case_sensitive_like=ON;")
dbapi_con.execute("PRAGMA foreign_keys=ON;")

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

Expand Down
12 changes: 8 additions & 4 deletions app/main/filters.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from flask import request, url_for
from json import dumps
import math

import locale
from typing import Optional
from json import dumps

from flask import request, url_for

from . import main
from ..models import WordToken

Expand All @@ -27,7 +29,9 @@ def json(obj):


@main.app_template_filter("get_token_uri")
def get_token_uri(token: WordToken):
def get_token_uri(token: Optional[WordToken]):
if token is None:
return "#"
per_page = request.args.get("per_page", 100, int)
page = int(math.ceil(token.order_id / 100))
return url_for(
Expand Down
21 changes: 14 additions & 7 deletions app/models/corpus.py
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,7 @@ class WordToken(db.Model):
left_context = db.Column(db.String(512))
right_context = db.Column(db.String(512))

_changes = db.relationship("ChangeRecord", cascade="all,delete")
_changes = db.relationship("ChangeRecord")

CONTEXT_LEFT = 3
CONTEXT_RIGHT = 3
Expand Down Expand Up @@ -742,7 +742,8 @@ def edit_form(self, form, corpus, user):
old=self.form,
action_type=TokenHistory.TYPES.Edition,
user_id=user.id,
word_token_id=self.id
word_token_id=self.id,
order_id = self.order_id
))
self.form = form
db.session.add(self)
Expand Down Expand Up @@ -782,7 +783,8 @@ def add_form(self, form, corpus, user):
new=form,
action_type=TokenHistory.TYPES.Addition,
user_id=user.id,
word_token_id=new_token.id
word_token_id=new_token.id,
order_id = new_token.order_id
))

# Update the contexts
Expand All @@ -809,16 +811,19 @@ def del_form(self, corpus, user):
WordToken.order_id > self.order_id
)).update({WordToken.order_id: WordToken.order_id - 1})

# Update
# Record the change
db.session.add(TokenHistory(
corpus=corpus.id,
new="",
old=self.form,
action_type=TokenHistory.TYPES.Deletion,
user_id=user.id,
word_token_id=self.id
#word_token_id=self.id,
order_id = self.order_id
))


# Update the contexts
self.update_context_around(corpus, delete=self.id)

Expand Down Expand Up @@ -1348,11 +1353,12 @@ class TYPES(enum.Enum):

id = db.Column(db.Integer, primary_key=True, autoincrement=True)
corpus = db.Column(db.Integer, db.ForeignKey('corpus.id', ondelete="CASCADE"))
word_token_id = db.Column(db.Integer, db.ForeignKey('word_token.id'))
word_token_id = db.Column(db.Integer, db.ForeignKey('word_token.id', ondelete='SET NULL'), nullable=True)
user_id = db.Column(db.Integer, db.ForeignKey(User.id))
action_type = db.Column(db.Enum(TYPES), nullable=False)
new = db.Column(db.String(100), nullable=True)
old = db.Column(db.String(100), nullable=True)
order_id = db.Column(db.Integer, nullable=True)
created_on = db.Column(db.DateTime, server_default=db.func.now())

user = db.relationship(User, lazy='select')
Expand Down Expand Up @@ -1464,8 +1470,9 @@ def get_like(corpus_id, form, group_by, category="lemma"):
class ChangeRecord(db.Model):
""" A change record keep track of lemma, POS or morph that have been changed for a particular form"""
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
corpus = db.Column(db.Integer, db.ForeignKey('corpus.id'))
word_token_id = db.Column(db.Integer, db.ForeignKey('word_token.id'))
corpus = db.Column(db.Integer, db.ForeignKey('corpus.id', ondelete="CASCADE"))
word_token_id = db.Column(db.Integer, db.ForeignKey('word_token.id', ondelete="SET NULL"), nullable=True,
default=None)
user_id = db.Column(db.Integer, db.ForeignKey(User.id))
form = db.Column(db.String(128))
lemma = db.Column(db.String(128))
Expand Down
19 changes: 13 additions & 6 deletions tests/test_selenium/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,18 @@

def get_chrome():
# This ensures compatibility with nektos/act
if os.path.isfile('/usr/bin/chromium-browser'):
return '/usr/bin/chromium-browser'
elif os.path.isfile('/usr/bin/chromium'):
return '/usr/bin/chromium'
elif os.path.isfile('/usr/bin/chrome'):
if os.path.isfile('/usr/bin/chrome'):
return '/usr/bin/chrome'
elif os.path.isfile('/usr/bin/google-chrome'):
return '/usr/bin/google-chrome'
elif os.path.isfile('/usr/bin/chromium-browser'):
return '/usr/bin/chromium-browser'
elif os.path.isfile('/usr/bin/chromium'):
return '/usr/bin/chromium'
else:
return None



class _element_has_count(object):
""" An expectation for checking that an element has a particular css class.

Expand Down Expand Up @@ -414,6 +413,14 @@ def url_for_with_port(self, *args, **kwargs):
url = url.replace('://localhost/', '://localhost:%d/' % (self.app.config["LIVESERVER_PORT"]))
return url

def token_dropdown_link(self, tok_id, link):
self.driver.get(self.url_for_with_port("main.tokens_correct", corpus_id="1"))
self.driver_find_element_by_id("dd_t" + str(tok_id)).click()
self.driver.implicitly_wait(2)
dd = self.driver_find_element_by_css_selector("*[aria-labelledby='dd_t{}']".format(tok_id))
self.element_find_element_by_partial_link_text(dd, link).click()
self.driver.implicitly_wait(2)


class TokenCorrectBase(TestBase):
""" Base class with helpers to test token edition page """
Expand Down
35 changes: 35 additions & 0 deletions tests/test_selenium/test_token_correct.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from tests.test_selenium.base import TokenCorrectBase, TokenCorrect2CorporaBase
import selenium
from sqlalchemy import text


class TestTokenCorrectWauchierCorpus(TokenCorrectBase):
Expand Down Expand Up @@ -223,3 +224,37 @@ def test_edit_token_lemma_with_typeahead_click(self):
"s", id_row=str(self.first_token_id(2)+1), corpus_id="2",
autocomplete_selector=".autocomplete-suggestion[data-val='saint']"
)

def test_correct_delete(self):
""" [TokenCorrectDelete] Check that we are able to edit then delete the form of a token """
self.addCorpus(with_token=True, with_allowed_lemma=True, tokens_up_to=24)
# We edit the lemma of the first token
token, status_text, row = self.edith_nth_row_value("saint", id_row="1")
# We checked it was saved
self.assert_saved(row)
self.assertEqual(
len(self.db.session.execute(text("SELECT * FROM change_record")).all()), 1,
"There should be one record in the history"
)
# We delete the token
self.token_dropdown_link(1, "Delete")
# Confirm the token's form on delete form
inp = self.driver_find_element_by_css_selector("input[name='form']")
inp.clear()
inp.send_keys("De")
# Delete
self.driver_find_element_by_css_selector("button[type='submit']").click()
# Check that it was removed
row = self.driver_find_elements_by_css_selector("tr.token-anchor[data-token-order='1']")[0]
self.assertEqual(
self.element_find_elements_by_tag_name(row, "td")[1].text, "seint",
"The token has been removed"
)
self.driver_find_element_by_link_text("History").click()
self.assertEqual(
len(self.db.session.execute(text("SELECT * FROM change_record")).all()), 1,
"There should be one record in the history"
)
history_row = self.driver_find_elements_by_css_selector("tbody tr.history")
self.assertEqual(len(history_row), 1, "There should be one record in the history")

25 changes: 8 additions & 17 deletions tests/test_selenium/test_tokens_edit.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,6 @@ class TestTokenEdit(TestBase):
""" Check token form edition, token update, token delete
"""

def dropdown_link(self, tok_id, link):
self.driver.get(self.url_for_with_port("main.tokens_correct", corpus_id="1"))
self.driver_find_element_by_id("dd_t"+str(tok_id)).click()
self.driver.implicitly_wait(2)
dd = self.driver_find_element_by_css_selector("*[aria-labelledby='dd_t{}']".format(tok_id))
self.element_find_element_by_partial_link_text(dd, link).click()
self.driver.implicitly_wait(2)

def change_form_value(self, value):
inp = self.driver_find_element_by_css_selector("input[name='form']")
inp.clear()
Expand Down Expand Up @@ -46,7 +38,7 @@ def test_edition(self):
""" [TokenEdit] Check that we are able to edit the form of a token """
self.addCorpus("wauchier")
# First edition
self.dropdown_link(5, "Edit")
self.token_dropdown_link(5, "Edit")
self.change_form_value("oulala")
self.driver_find_element_by_css_selector("button[type='submit']").click()
self.driver.implicitly_wait(5)
Expand All @@ -69,7 +61,7 @@ def test_edition(self):
"History should be saved"
)
# Second edition
self.dropdown_link(8, "Edit")
self.token_dropdown_link(8, "Edit")
self.change_form_value("Oulipo")
self.driver_find_element_by_css_selector("button[type='submit']").click()
self.driver.implicitly_wait(5)
Expand All @@ -96,7 +88,7 @@ def test_addition(self):
""" [TokenEdit] Check that we are able to add tokens"""
self.addCorpus("wauchier")
# First edition
self.dropdown_link(5, "Add")
self.token_dropdown_link(5, "Add")
self.change_form_value("oulala")
self.driver_find_element_by_css_selector("button[type='submit']").click()
self.driver.implicitly_wait(5)
Expand All @@ -120,7 +112,7 @@ def test_addition(self):
"History should be saved"
)
# Second edition
self.dropdown_link(8, "Add")
self.token_dropdown_link(8, "Add")
self.change_form_value("Oulipo")
self.driver_find_element_by_css_selector("button[type='submit']").click()
self.driver.implicitly_wait(5)
Expand All @@ -143,16 +135,16 @@ def test_addition(self):
"History should be saved"
)

def test_delete(self):
""" [TokenEdit] Check that we are able to edit the form of a token """
def test_edit_delete(self):
""" [TokenEdit] Check that we are able to edit then delete the form of a token """
self.addCorpus("wauchier")

# Get the original value
self.driver.get(self.url_for_with_port("main.tokens_correct", corpus_id="1"))
original_set = self.select_context_around(5)

# First we add a token
self.dropdown_link(5, "Add")
self.token_dropdown_link(5, "Add")
self.change_form_value("oulala")
self.driver_find_element_by_css_selector("button[type='submit']").click()
self.driver.implicitly_wait(5)
Expand All @@ -177,11 +169,10 @@ def test_delete(self):
)

# Then we remove it
self.dropdown_link(6, "Delete")
self.token_dropdown_link(6, "Delete")
self.change_form_value("oulala")
self.driver_find_element_by_css_selector("button[type='submit']").click()
self.driver.implicitly_wait(5)

self.assertEqual(
self.select_context_around(5),
original_set,
Expand Down