defmodule Archeometer.Reports.Fragment do
@moduledoc """
Represents a fragment, which is part of a section in a page.
"""
use Archeometer.Repo
alias Archeometer.Reports.Config
def make_custom_env do
import Archeometer.Query, warn: false
alias Archeometer.Schema.{Function, Module, XRef, App, Behaviour}, warn: false
alias Archeometer.Repo, warn: false
alias Archeometer.Analysis.DSM, warn: false
alias Archeometer.Analysis.DSM.ConsoleRender, warn: false
Code.put_compiler_option(:tracers, [])
__ENV__
end
defstruct [:query_type, :result_type, :desc, :code, :result, :uuid, :alt_code, :alt_code_lang]
defmodule Definition do
@moduledoc """
Represents the definition of a Fragment.
"""
defstruct [
:query_type,
:result_type,
:desc,
:code,
:result_name,
:table_headers,
:alt_code,
:alt_code_lang,
:receives
]
end
def process(%__MODULE__.Definition{} = fdef, bindings, db_name \\ default_db_name()) do
_process(fdef, bindings, db_name)
end
def alt_process(
%__MODULE__.Definition{query_type: :cql, result_type: :table} = fdef,
bindings,
db_name \\ default_db_name()
) do
{:ok, conn} = DB.open(db_name)
query = render(fdef.alt_code, bindings)
query_res = execute_query(conn, query, [])
DB.close(conn)
res = struct(__MODULE__, Map.from_struct(fdef))
%{
res
| code: query,
result: %{headers: fdef.table_headers, values: query_res},
uuid: UUID.uuid4()
}
end
defp _process(
%__MODULE__.Definition{query_type: :cql, result_type: :table} = fdef,
bindings,
_db_name
) do
description = render(fdef.desc, bindings)
query = render(fdef.code, bindings)
alt_query = render(fdef.alt_code, bindings)
{query_res, _} = Code.eval_string(query, [], make_custom_env())
res = struct(__MODULE__, Map.from_struct(fdef)) |> validate_fragment_result(query_res.rows)
%{
res
| code: query,
result: %{headers: fdef.table_headers, values: query_res.rows},
uuid: UUID.uuid4(),
alt_code: alt_query,
desc: description
}
end
defp _process(
%__MODULE__.Definition{query_type: :elixir, result_type: :table} = fdef,
bindings,
db_name
) do
description = render(fdef.desc, bindings)
code_str = render(fdef.code, bindings ++ [db_name: db_name])
{code_res, _} = Code.eval_string(code_str, bindings ++ [db_name: db_name], make_custom_env())
res = struct(__MODULE__, Map.from_struct(fdef)) |> validate_fragment_result(code_res)
%{
res
| code: code_str,
result: %{headers: fdef.table_headers, values: code_res},
uuid: UUID.uuid4(),
desc: description
}
end
defp _process(
%__MODULE__.Definition{query_type: :mix_task} = fdef,
bindings,
db_name
) do
["mix" | [task | args]] = String.split(fdef.code)
{switches, _params, []} = OptionParser.parse(args, switches: [])
format =
if Enum.member?([:treemap_svg, :svg], fdef.result_type),
do: "svg",
else: Keyword.get(switches, :format)
base_fname = task <> "." <> format
fname =
Path.expand(
UUID.uuid4() <> "-" <> base_fname,
Config.static_report_img_path()
)
code_str = render(fdef.code, bindings ++ [fname: base_fname])
execute_code_str = render(fdef.code, bindings ++ [fname: fname])
["mix" | [^task | task_args]] = String.split(execute_code_str)
task_args = task_args ++ ["--db", db_name]
:ok = Mix.Task.rerun(task, task_args)
res = struct(__MODULE__, Map.from_struct(fdef))
%{res | code: code_str, result: fname, uuid: UUID.uuid4()}
end
defp _process(
%__MODULE__.Definition{query_type: :cli_command} = fdef,
bindings,
db_name
) do
[cmd | args] = fdef.code |> String.split()
{switches, _params, []} =
OptionParser.parse(
args,
switches: [],
aliases: [f: :format]
)
format =
if Enum.member?([:treemap_svg, :svg], fdef.result_type),
do: "svg",
else: Keyword.get(switches, :format, "png")
base_fname = cmd <> "." <> format
fname =
Path.expand(
UUID.uuid4() <> base_fname,
Config.static_report_img_path()
)
code_str = render(fdef.code, bindings ++ [fname: base_fname, db: db_name])
command = render(fdef.code, bindings ++ [fname: fname, db: db_name])
Mix.Shell.cmd(command, [], &IO.puts/1)
res = struct(__MODULE__, Map.from_struct(fdef))
%{res | code: code_str, result: fname, uuid: UUID.uuid4()}
end
defp _process(
%__MODULE__.Definition{query_type: :empty_input} = fdef,
_bindings,
_db_name
) do
res = struct(__MODULE__, Map.from_struct(fdef))
%{
res
| result_type: :empty_input,
uuid: UUID.uuid4()
}
end
defp render(code_str, bindings) do
EEx.eval_string(code_str, assigns: bindings)
end
defp validate_fragment_result(res, []) do
%{
res
| result_type: :empty_result
}
end
defp validate_fragment_result(res, _query_res), do: res
end