- Word import (multi-format: word, word-def, word:def, word|def) - Flashcard filter UI (swipe + keyboard arrows + Space to flip) - SM-2 spaced repetition review queue - Stimulus flashcard controller with 3D flip animation
56 lines
1.4 KiB
Ruby
56 lines
1.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class WordsController < ApplicationController
|
|
def index
|
|
@stats = {
|
|
total: Word.count,
|
|
unseen: Review.where(status: "new").count,
|
|
known: Review.where(status: "known").count,
|
|
learning: Review.where(status: "learning").count,
|
|
skipped: Review.where(status: "skipped").count,
|
|
due: Review.due.count
|
|
}
|
|
@words = Word.includes(:review).by_text
|
|
end
|
|
|
|
def create
|
|
raw = params[:word_list].to_s.strip
|
|
lines = raw.split("\n").map(&:strip).reject { |l| l.blank? || l.start_with?("#") }
|
|
|
|
imported = 0
|
|
skipped = 0
|
|
|
|
lines.each do |line|
|
|
text, definition = parse_line(line)
|
|
next if text.blank?
|
|
|
|
w = Word.new(text: text.strip, definition: definition&.strip.presence)
|
|
if w.save
|
|
imported += 1
|
|
else
|
|
skipped += 1
|
|
end
|
|
end
|
|
|
|
flash[:notice] = "Imported #{imported} word#{"s" unless imported == 1}."
|
|
flash[:alert] = "Skipped #{skipped} (duplicates or invalid)." if skipped > 0
|
|
redirect_to words_path
|
|
end
|
|
|
|
def destroy
|
|
Word.find(params[:id]).destroy
|
|
redirect_to words_path, notice: "Removed."
|
|
end
|
|
|
|
private
|
|
|
|
def parse_line(line)
|
|
# "word - definition", "word: definition", "word | definition", "word\tdefinition"
|
|
if (m = line.match(/\A(.+?)\s*(?:\t|-{1,2}|:{1,2}|\|)\s*(.+)\z/))
|
|
[ m[1], m[2] ]
|
|
else
|
|
[ line, nil ]
|
|
end
|
|
end
|
|
end
|