1
0
Fork 0
backend/main.rb

315 lines
8.1 KiB
Ruby
Raw Normal View History

2023-02-04 18:43:57 -05:00
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'connection_pool'
require 'pg'
require 'sinatra'
2023-02-04 21:44:16 -05:00
require 'sinatra/json'
2023-02-04 18:43:57 -05:00
$DB_POOL = ConnectionPool.new size: 5, timeout: 5 do
PG.connect(
host: 'pg.causa-arcana.com',
dbname: 'leqsikoni',
user: 'leqsikoni',
password: 'ggeucene3ou7mqh2upehhm52tfp5bkcj',
2023-02-04 20:18:22 -05:00
).tap do |conn|
conn.type_map_for_results = PG::BasicTypeMapForResults.new conn
end
2023-02-04 18:43:57 -05:00
end
2023-02-04 21:53:47 -05:00
before do
headers 'Access-Control-Allow-Origin' => '*'
end
2023-02-11 00:54:34 -05:00
get '/words/:id' do
2023-02-18 05:40:01 -05:00
Context.call db_pool: $DB_POOL, user_lang_id: 2 do |context|
2023-02-11 00:54:34 -05:00
word_id = Integer params[:id]
2023-02-04 20:18:22 -05:00
2023-02-19 00:06:01 -05:00
word_info = context.word_info(word_id)
2023-02-18 04:44:02 -05:00
json({
2023-02-19 00:06:01 -05:00
primary_form: word_info[:primary_form],
2023-02-18 04:44:02 -05:00
part_of_speech: context.part_of_speech_name(word_id),
2023-02-19 00:06:01 -05:00
commentary: word_info[:commentary],
2023-02-18 09:09:20 -05:00
translations: context.translations(word_id),
2023-03-05 12:11:30 -05:00
inflections_old: word_info[:inflections],
inflections: context.inflections(word_id),
2023-02-18 04:44:02 -05:00
examples: context.examples(word_id),
})
end
end
class Context
private_class_method :new
def self.call(db_pool:, **kwargs, &block)
new(**kwargs).send :call, db_pool: db_pool, &block
end
def initialize(user_lang_id:)
self.user_lang_id = user_lang_id
end
private
attr_reader :user_lang_id
def call(db_pool:)
db_pool.with do |db_conn|
@db_conn = db_conn
result = yield self
@db_conn = nil
result
end
end
def user_lang_id=(user_lang_id)
return @user_lang_id = nil if user_lang_id.nil?
user_lang_id = Integer user_lang_id
raise unless user_lang_id.positive?
@user_lang_id = user_lang_id
end
public
2023-02-18 06:43:07 -05:00
##
2023-02-19 00:06:01 -05:00
# @return [Hash{Symbol => String}, nil]
2023-02-18 06:43:07 -05:00
#
2023-02-19 00:06:01 -05:00
def word_info(word_id)
2023-02-18 06:43:07 -05:00
word_id = Integer word_id
2023-02-19 00:06:01 -05:00
row = @db_conn.exec_params(
2023-02-19 00:29:32 -05:00
'SELECT primary_form, commentary, inflections FROM words WHERE id = $1',
2023-02-18 06:43:07 -05:00
[word_id],
2023-02-19 00:06:01 -05:00
).to_a.first
2023-02-18 06:43:07 -05:00
2023-02-19 00:06:01 -05:00
primary_form = String(row['primary_form']).strip.freeze
primary_form = nil if primary_form.empty?
commentary = String(row['commentary']).strip.freeze
commentary = nil if commentary.empty?
2023-02-19 00:29:32 -05:00
inflections = String(row['inflections']).strip.freeze
inflections = nil if inflections.empty?
2023-02-19 00:06:01 -05:00
{
primary_form: primary_form,
commentary: commentary,
2023-02-19 00:29:32 -05:00
inflections: inflections,
2023-02-19 00:06:01 -05:00
}.freeze
2023-02-18 06:43:07 -05:00
end
2023-02-18 04:44:02 -05:00
##
# @return [String, nil]
#
def part_of_speech_name(word_id)
word_id = Integer word_id
column = @db_conn.exec_params(
2023-02-18 02:59:47 -05:00
(
<<~SQL
2023-02-18 05:40:01 -05:00
SELECT part_names.value
2023-02-18 02:59:47 -05:00
FROM words
INNER JOIN parts
ON words.part_id = parts.id
2023-02-18 05:40:01 -05:00
INNER JOIN part_names
ON part_names.part_id = parts.id
WHERE
words.id = $1
AND (
part_names.name_lang_id = $2
OR
$2 IS NULL
)
2023-02-18 02:59:47 -05:00
LIMIT 1
SQL
),
2023-02-18 05:40:01 -05:00
[word_id, user_lang_id],
2023-02-18 03:07:11 -05:00
).values.first&.first
2023-02-18 02:59:47 -05:00
2023-02-18 04:44:02 -05:00
str = String(column).strip.freeze
str unless str.empty?
end
2023-02-18 09:09:20 -05:00
def translations(word_id)
word_id = Integer word_id
values = @db_conn.exec_params(
(
<<~SQL
SELECT
translation_texts.value,
translation_texts.commentary
FROM words
INNER JOIN translations
ON translations.word_id = words.id
INNER JOIN translation_texts
ON translation_texts.translation_id = translations.id
WHERE
word_id = $1
AND
translation_texts.lang_id = $2
2023-02-18 09:14:42 -05:00
ORDER BY translations.index ASC
2023-02-18 09:09:20 -05:00
SQL
),
[word_id, user_lang_id],
).to_a
2023-02-18 09:14:42 -05:00
values.map.with_index do |row, index|
2023-02-18 09:32:15 -05:00
commentary = String(row['commentary']).strip.freeze
commentary = nil if commentary.empty?
2023-02-18 09:09:20 -05:00
{
2023-02-18 09:14:42 -05:00
index: index + 1,
2023-02-18 09:09:20 -05:00
translation: String(row['value']).strip.freeze,
2023-02-18 09:32:15 -05:00
commentary: commentary,
2023-02-18 09:09:20 -05:00
}.freeze
end.freeze
end
2023-03-05 12:11:30 -05:00
def inflections(word_id)
word_id = Integer word_id
result = @db_conn.exec_params(
(
<<~SQL
SELECT inflections.descr, word_forms.value
FROM words
INNER JOIN parts
ON parts.id = words.part_id
INNER JOIN inflections
ON inflections.part_id = parts.id
LEFT JOIN word_forms
ON word_forms.inflection_id = inflections.id
WHERE words.id = $1 AND word_forms.word_id = $1
2023-03-05 12:45:42 -05:00
ORDER BY inflections.descr
2023-03-05 12:11:30 -05:00
SQL
),
[word_id],
).to_a
result.map do |row|
descr = String(row['descr']).strip.freeze
value = String(row['value']).strip.freeze
{
descr: descr,
value: value,
}
end.freeze
end
2023-02-18 04:44:02 -05:00
def examples(word_id)
word_id = Integer word_id
2023-02-20 07:35:31 -05:00
left_lang_id = 5 # Georgian
right_lang_id = 2 # Russian
lang_order = left_lang_id < right_lang_id ? 'ASC' : 'DESC'
result = @db_conn.exec_params(
2023-02-04 20:18:22 -05:00
(
<<~SQL
SELECT
2023-02-20 07:35:31 -05:00
foo.example_id,
array_agg(foo.id ORDER BY foo.language_id #{lang_order})::text[]
AS ids,
array_agg(foo.value ORDER BY foo.language_id #{lang_order})::text[]
AS values
2023-02-04 20:18:22 -05:00
FROM example_texts foo
INNER JOIN (
SELECT example_id, language_id, MIN(index) as min_index
FROM example_texts
WHERE
2023-02-11 00:54:34 -05:00
example_id = ANY(
SELECT example_texts.example_id
FROM words
INNER JOIN word_forms
ON word_forms.word_id = words.id
INNER JOIN word_form_example_texts
ON word_form_example_texts.word_form_id = word_forms.id
INNER JOIN example_texts
ON example_texts.id = word_form_example_texts.example_text_id
WHERE words.id = $1
GROUP BY example_texts.example_id
)
2023-02-04 20:18:22 -05:00
AND
2023-02-20 07:35:31 -05:00
(language_id = $2 OR language_id = $3)
2023-02-04 20:18:22 -05:00
GROUP BY example_id, language_id
) bar
ON
foo.example_id = bar.example_id AND
foo.language_id = bar.language_id AND
foo.index = bar.min_index
GROUP BY foo.example_id
SQL
),
2023-02-20 07:35:31 -05:00
[word_id, left_lang_id, right_lang_id],
2023-02-20 04:37:38 -05:00
).map do |row|
{
2023-02-20 07:35:31 -05:00
example_id: Integer(row['example_id']),
left_id: Integer(row['ids'].first),
right_id: Integer(row['ids'].last),
left: String(row['values'].first).strip.freeze,
right: String(row['values'].last).strip.freeze,
highlights: [],
2023-02-20 04:37:38 -05:00
}.freeze
end.freeze
2023-02-20 07:35:31 -05:00
highlights =
@db_conn.exec_params(
(
<<~SQL
SELECT
example_texts.id AS example_text_id,
array_agg(
word_form_example_texts.pos
ORDER BY word_form_example_texts.word_form_id
) AS positions,
array_agg(
word_forms.len
ORDER BY word_forms.id
) AS lengths
FROM example_texts
INNER JOIN word_form_example_texts
ON word_form_example_texts.example_text_id = example_texts.id
INNER JOIN word_forms
ON word_form_example_texts.word_form_id = word_forms.id
WHERE
language_id = $1::bigint
AND
example_texts.example_id = ANY($2::bigint[])
GROUP BY example_texts.id
SQL
),
[
left_lang_id,
PG::TextEncoder::Array.new.encode(result.map { |h| h[:example_id] }),
],
)
.map do |row|
example_text_id = Integer(row['example_text_id'])
positions = row['positions'].map { |pos| Integer pos }.freeze
lengths = row['lengths'].map { |len| Integer len }.freeze
{
example_text_id: example_text_id,
data: positions.zip(lengths).map do |pos, len|
{ pos: pos, len: len }.freeze
end.sort_by { |h| h[:pos] }.freeze,
}
end.to_a
highlights.each do |highlight|
result
.find { |item| item[:left_id] == highlight[:example_text_id] }
&.tap { |item| item[:highlights].replace highlight[:data] }
end
result.each { |h| h[:highlights].freeze }
2023-02-04 18:43:57 -05:00
end
end