Skip to content
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
14 changes: 0 additions & 14 deletions tools/migrations/26-02-16--add_translation_search.sql

This file was deleted.

18 changes: 18 additions & 0 deletions tools/migrations/26-02-17--simplify_translation_search.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
-- Drop old translation_search table (had meaning_id, now we just store search_word)
DROP TABLE IF EXISTS translation_search;

-- New simplified translation search history table
-- Stores the search word and learned language (active during search)
-- Filtered by current learned language when displayed
CREATE TABLE translation_search (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
search_word VARCHAR(255) NOT NULL,
learned_language_id INT NOT NULL,
search_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,

FOREIGN KEY (user_id) REFERENCES user(id),
FOREIGN KEY (learned_language_id) REFERENCES language(id),

INDEX idx_user_lang_time (user_id, learned_language_id, search_time DESC)
);
43 changes: 28 additions & 15 deletions zeeguu/api/endpoints/translation.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,6 @@ def get_multiple_translations(from_lang_code, to_lang_code):
translations = get_all_translations(word_str, context, from_lang_code, to_lang_code, is_separated_mwe, full_sentence_context)

# Save meanings for each translation
first_meaning = None
for t in translations:
translation_text = t.get("translation", "")
if translation_text:
Expand All @@ -201,18 +200,6 @@ def get_multiple_translations(from_lang_code, to_lang_code):
to_lang_code,
)
t["meaning_id"] = meaning.id
if first_meaning is None:
first_meaning = meaning

# Log search to history only if we found a translation
if first_meaning:
try:
user = User.find_by_id(flask.g.user_id)
TranslationSearch.log_search(db_session, user, first_meaning)
db_session.commit()
except Exception as e:
db_session.rollback()
zeeguu_log(f"[TRANSLATION] Failed to log search history: {e}")

return json_result(dict(translations=translations))

Expand All @@ -222,18 +209,44 @@ def get_multiple_translations(from_lang_code, to_lang_code):
@requires_session
def get_translation_history():
"""
Returns recent translation searches for the current user.
Returns recent translation searches for the current user's learned language.
Used by the Translation Tab's history view.

:return: json array with recent searches
"""
user = User.find_by_id(flask.g.user_id)
limit = request.args.get("limit", 50, type=int)

searches = TranslationSearch.get_history(user, limit=limit)
searches = TranslationSearch.get_history(user, user.learned_language, limit=limit)
return json_result([s.as_dict() for s in searches])


@api.route("/log_translation_search", methods=["POST"])
@cross_domain
@requires_session
def log_translation_search():
"""
Log a translation search to history.
Called by frontend when user searches for a word.

:param search_word: The word that was searched
:return: success status
"""
search_word = request.form.get("search_word", "").strip()
if not search_word:
return "search_word required", 400

try:
user = User.find_by_id(flask.g.user_id)
TranslationSearch.log_search(db_session, user, search_word, user.learned_language)
db_session.commit()
return "OK"
except Exception as e:
db_session.rollback()
zeeguu_log(f"[TRANSLATION] Failed to log search history: {e}")
return "OK" # Don't fail the request for logging errors


@api.route(
"/get_translations_stream/<from_lang_code>/<to_lang_code>", methods=["POST"]
)
Expand Down
41 changes: 23 additions & 18 deletions zeeguu/core/model/translation_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
from sqlalchemy import desc

from zeeguu.core.model.db import db
from zeeguu.core.model.meaning import Meaning
from zeeguu.core.model.language import Language
from zeeguu.core.model.user import User


class TranslationSearch(db.Model):
"""
Tracks successful translation searches made in the Translation Tab.
Only logs searches where a translation was found (meaning exists).
Tracks translation searches made in the Translation Tab.
Stores the search word and learned language (active during search).
"""

__tablename__ = "translation_search"
Expand All @@ -19,38 +19,46 @@ class TranslationSearch(db.Model):
user_id = db.Column(db.Integer, db.ForeignKey(User.id), nullable=False)
user = db.relationship(User)

meaning_id = db.Column(db.Integer, db.ForeignKey(Meaning.id), nullable=False)
meaning = db.relationship(Meaning)
search_word = db.Column(db.String(255), nullable=False)

learned_language_id = db.Column(
db.Integer, db.ForeignKey(Language.id), nullable=False
)
learned_language = db.relationship(Language)

search_time = db.Column(db.DateTime, nullable=False, default=datetime.now)

def __init__(self, user: User, meaning: Meaning):
def __init__(self, user: User, search_word: str, learned_language: Language):
self.user = user
self.meaning = meaning
self.search_word = search_word
self.learned_language = learned_language
self.search_time = datetime.now()

def __repr__(self):
return f"TranslationSearch({self.meaning.origin.content})"
return f"TranslationSearch({self.search_word}, {self.learned_language.code})"

@classmethod
def log_search(cls, session, user: User, meaning: Meaning):
def log_search(cls, session, user: User, search_word: str, learned_language: Language):
"""
Log a translation search to history.

Note: Does not commit - caller is responsible for committing.
"""
search = cls(user=user, meaning=meaning)
search = cls(user=user, search_word=search_word, learned_language=learned_language)
session.add(search)
return search

@classmethod
def get_history(cls, user: User, limit: int = 50):
def get_history(cls, user: User, learned_language: Language, limit: int = 50):
"""
Get recent translation searches for a user.
Get recent translation searches for a user in a specific language.
Returns most recent searches first.
"""
return (
cls.query.filter(cls.user_id == user.id)
cls.query.filter(
cls.user_id == user.id,
cls.learned_language_id == learned_language.id
)
.order_by(desc(cls.search_time))
.limit(limit)
.all()
Expand All @@ -60,10 +68,7 @@ def as_dict(self):
"""Return dictionary representation for API response."""
return {
"id": self.id,
"search_word": self.meaning.origin.content,
"translation": self.meaning.translation.content,
"from_language": self.meaning.origin.language.code,
"to_language": self.meaning.translation.language.code,
"meaning_id": self.meaning.id,
"search_word": self.search_word,
"language": self.learned_language.code,
"search_time": self.search_time.isoformat(),
}