defmodule Api.Studies do import Ecto.Query alias Api.Repo require Logger def studies_sql_query(filters) do customfilter = case Map.get(filters, "customfilter") do nil -> dynamic([_], true) "" -> dynamic([_], true) expr -> case ExpressionParser.parse(expr) do {:ok, ast} -> ExpressionToEcto.to_dynamic(ast) {:error, _reason} -> raise "Error al parsear customfilter" end end page = filters["page"] || 1 size = filters["size"] || 24 filter = filters["filter"] || [] sort = filters["sort"] || [%{"dir" => "desc", "field" => "idstudy"}] Logger.info("sort -> #{inspect(sort)}") # Construcción de condiciones de filtro dinámicas filter_conditions = Enum.reduce(filter, dynamic(true), fn f, acc -> field = f["field"] value = f["value"] condition = case field do "modality" -> dynamic([s], ilike(s.modality, ^"%#{value}%")) "idstudy" -> dynamic([s], like(fragment("CAST(? AS TEXT)", s.idstudy), ^"#{value}%")) "studydate" -> dynamic([s], fragment("CAST(? AS DATE)::text LIKE ?", s.studydate, ^"%#{value}%")) "studydate_start" -> if value == "" do dynamic(true) else dynamic([s], fragment("CAST(? AS DATE)::text", s.studydate) >= ^value) end "studydate_end" -> if value == "" do dynamic(true) else dynamic([s], fragment("CAST(? AS DATE)::text", s.studydate) <= ^value) end "accessionnumber" -> dynamic([s], ilike(s.accessionnumber, ^"%#{value}%")) "proceduredescription" -> dynamic([s], ilike(s.studydescription, ^"%#{value}%")) "insurer" -> dynamic([s], ilike(s.insurer, ^"%#{value}%")) "sitename" -> dynamic([s], ilike(s.institutionname, ^"%#{value}%")) "nrodocumento" -> dynamic([s, p], ilike(p.patientid, ^"%#{value}%")) "patientname" -> dynamic([s, p], ilike(p.patientname, ^"%#{value}%")) "region" -> dynamic([s], s.region == ^value) "studytime" -> dynamic([s], fragment("CAST(? AS TIME)::text LIKE ?", s.studytime, ^"#{value}%")) _ -> dynamic(true) end # Combina las condiciones dinámicas dynamic([s, p], ^condition and ^acc) end) sort_conditions = Enum.map(sort, fn v -> direction = String.to_atom(v["dir"]) field = String.to_atom(v["field"]) {direction, field} end) Logger.info("sort_conditions --> #{inspect(sort_conditions)}") combined_filter = dynamic([q], ^filter_conditions and ^customfilter) query = from s in "study", join: p in "patient", on: p.idpatient == s.idpatient, left_join: sr in "studyreport", on: sr.idstudy == s.idstudy, left_join: st in "statuses", on: st.idstatus == sr.idstudyreport, where: ^combined_filter, select: %{ recordstotal: fragment("count(*) over()"), idstudy: fragment("substring(encrypt(?::text::bytea, '1nf0rm3', 'aes')::text from 3)", s.idstudy), #idstudy: s.idstudy, accessionnumber: s.accessionnumber, studydate: s.studydate, studytime: s.studytime, patientname: fragment("select replace(?, '^', ' ')", p.patientname), proceduredescription: s.studydescription, modality: s.modality, sitename: s.institutionname, insurer: s.insurer, nrodocumento: fragment("substring(encrypt(?::text::bytea, '1nf0rm3', 'aes')::text from 3)", p.patientid), #nrodocumento: p.patientid, hasaudio: fragment("CASE WHEN ? IS NOT NULL THEN true ELSE false END", s.audiofile) }, order_by: ^sort_conditions, limit: ^size, offset: ^((page - 1) * size) result = Repo.one( from q in subquery(query), group_by: q.recordstotal, select: %{ data: fragment("json_agg(?)::text", q), recordstotal: q.recordstotal } ) case result do nil -> %{data: [], last_page: 0} result -> # Calcula last_page last_page = div(result.recordstotal + size - 1, size) %{data: Jason.decode!(result.data), last_page: last_page} end end end