style: fix pre-existing formatting drift across codebase
This commit is contained in:
@@ -27,7 +27,9 @@ defmodule BDS.AI.ChatConversation do
|
||||
|
||||
def changeset(conversation, attrs) do
|
||||
conversation
|
||||
|> cast(attrs, [:id, :title, :model, :copilot_session_id, :surface_state, :created_at, :updated_at],
|
||||
|> cast(
|
||||
attrs,
|
||||
[:id, :title, :model, :copilot_session_id, :surface_state, :created_at, :updated_at],
|
||||
empty_values: [nil]
|
||||
)
|
||||
|> validate_required([:id, :title, :created_at, :updated_at])
|
||||
|
||||
@@ -263,8 +263,13 @@ defmodule BDS.Desktop.Overlay do
|
||||
def set_ai_suggestions_error(overlay, _error_message), do: overlay
|
||||
|
||||
defp normalize_ai_fields(fields) do
|
||||
Enum.map(fields, fn %{key: key, label: label, current_value: current,
|
||||
suggested_value: suggested, locked: locked} = field ->
|
||||
Enum.map(fields, fn %{
|
||||
key: key,
|
||||
label: label,
|
||||
current_value: current,
|
||||
suggested_value: suggested,
|
||||
locked: locked
|
||||
} = field ->
|
||||
%{
|
||||
key: to_string(key),
|
||||
label: label,
|
||||
|
||||
@@ -103,7 +103,9 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
|
||||
socket
|
||||
) do
|
||||
next_data = Map.put(socket.assigns.surface_data, surface_id, fields)
|
||||
{:noreply, assign(socket, :surface_data, next_data) |> schedule_surface_state_persist() |> build_data()}
|
||||
|
||||
{:noreply,
|
||||
assign(socket, :surface_data, next_data) |> schedule_surface_state_persist() |> build_data()}
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
@@ -227,8 +229,11 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
|
||||
build_data(socket)
|
||||
|
||||
socket.assigns.offline_mode ->
|
||||
Notify.output(dgettext("ui", "Chat"),
|
||||
dgettext("ui", "Automatic AI actions stay gated by airplane mode."), "info")
|
||||
Notify.output(
|
||||
dgettext("ui", "Chat"),
|
||||
dgettext("ui", "Automatic AI actions stay gated by airplane mode."),
|
||||
"info"
|
||||
)
|
||||
|
||||
build_data(socket)
|
||||
|
||||
|
||||
@@ -35,14 +35,28 @@ defmodule BDS.Desktop.ShellLive.GalleryImport do
|
||||
known_refs = MapSet.new(tasks, & &1.ref)
|
||||
|
||||
drain_tasks(
|
||||
remaining, tasks, known_refs, project_id, post_id, language, translate_targets, parent
|
||||
remaining,
|
||||
tasks,
|
||||
known_refs,
|
||||
project_id,
|
||||
post_id,
|
||||
language,
|
||||
translate_targets,
|
||||
parent
|
||||
)
|
||||
|
||||
send(parent, {:add_images_complete, length(paths)})
|
||||
end
|
||||
|
||||
defp drain_tasks(
|
||||
[], tasks, _known_refs, _project_id, _post_id, _language, _translate_targets, _parent
|
||||
[],
|
||||
tasks,
|
||||
_known_refs,
|
||||
_project_id,
|
||||
_post_id,
|
||||
_language,
|
||||
_translate_targets,
|
||||
_parent
|
||||
) do
|
||||
Enum.each(tasks, fn task -> Task.await(task, :infinity) end)
|
||||
end
|
||||
@@ -65,7 +79,12 @@ defmodule BDS.Desktop.ShellLive.GalleryImport do
|
||||
new_task =
|
||||
Task.async(fn ->
|
||||
process_single_image(
|
||||
next_path, project_id, post_id, language, translate_targets, parent
|
||||
next_path,
|
||||
project_id,
|
||||
post_id,
|
||||
language,
|
||||
translate_targets,
|
||||
parent
|
||||
)
|
||||
end)
|
||||
|
||||
@@ -81,8 +100,14 @@ defmodule BDS.Desktop.ShellLive.GalleryImport do
|
||||
)
|
||||
else
|
||||
drain_tasks(
|
||||
[next_path | rest], tasks, known_refs,
|
||||
project_id, post_id, language, translate_targets, parent
|
||||
[next_path | rest],
|
||||
tasks,
|
||||
known_refs,
|
||||
project_id,
|
||||
post_id,
|
||||
language,
|
||||
translate_targets,
|
||||
parent
|
||||
)
|
||||
end
|
||||
|
||||
@@ -93,7 +118,12 @@ defmodule BDS.Desktop.ShellLive.GalleryImport do
|
||||
new_task =
|
||||
Task.async(fn ->
|
||||
process_single_image(
|
||||
next_path, project_id, post_id, language, translate_targets, parent
|
||||
next_path,
|
||||
project_id,
|
||||
post_id,
|
||||
language,
|
||||
translate_targets,
|
||||
parent
|
||||
)
|
||||
end)
|
||||
|
||||
@@ -109,8 +139,14 @@ defmodule BDS.Desktop.ShellLive.GalleryImport do
|
||||
)
|
||||
else
|
||||
drain_tasks(
|
||||
[next_path | rest], tasks, known_refs,
|
||||
project_id, post_id, language, translate_targets, parent
|
||||
[next_path | rest],
|
||||
tasks,
|
||||
known_refs,
|
||||
project_id,
|
||||
post_id,
|
||||
language,
|
||||
translate_targets,
|
||||
parent
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -124,16 +160,22 @@ defmodule BDS.Desktop.ShellLive.GalleryImport do
|
||||
end
|
||||
|
||||
defp process_single_image(
|
||||
path, project_id, post_id, language, translate_targets, parent
|
||||
path,
|
||||
project_id,
|
||||
post_id,
|
||||
language,
|
||||
translate_targets,
|
||||
parent
|
||||
) do
|
||||
with {:ok, media} <- Media.import_media(%{project_id: project_id, source_path: path}),
|
||||
true <- String.starts_with?(media.mime_type || "", "image/"),
|
||||
{:ok, result} <- AI.analyze_image(media.id, language: language),
|
||||
{:ok, _updated} <- Media.update_media(media.id, %{
|
||||
title: result.title,
|
||||
alt: result.alt,
|
||||
caption: result.caption
|
||||
}),
|
||||
{:ok, _updated} <-
|
||||
Media.update_media(media.id, %{
|
||||
title: result.title,
|
||||
alt: result.alt,
|
||||
caption: result.caption
|
||||
}),
|
||||
{:ok, _link} <- Media.link_media_to_post(media.id, post_id) do
|
||||
translate_media_translations(media.id, translate_targets)
|
||||
title = result.title || media.original_name
|
||||
|
||||
@@ -642,7 +642,10 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
defp maybe_update_tab_meta(socket, name) do
|
||||
title = name || dgettext("ui", "Untitled Import")
|
||||
|
||||
Notify.tab_meta(:import, socket.assigns.definition_id, title,
|
||||
Notify.tab_meta(
|
||||
:import,
|
||||
socket.assigns.definition_id,
|
||||
title,
|
||||
dgettext(
|
||||
"ui",
|
||||
"Select a WordPress export file (WXR) and an uploads folder to analyze what would be imported."
|
||||
|
||||
@@ -126,8 +126,12 @@ defmodule BDS.Desktop.ShellLive.MediaEditor do
|
||||
|
||||
Notify.dirty(:media, media.id, false)
|
||||
|
||||
Notify.tab_meta(:media, media.id, display_title(updated_media),
|
||||
updated_media.original_name || updated_media.mime_type || "")
|
||||
Notify.tab_meta(
|
||||
:media,
|
||||
media.id,
|
||||
display_title(updated_media),
|
||||
updated_media.original_name || updated_media.mime_type || ""
|
||||
)
|
||||
|
||||
{:noreply, socket}
|
||||
|
||||
@@ -484,8 +488,12 @@ defmodule BDS.Desktop.ShellLive.MediaEditor do
|
||||
|
||||
Notify.dirty(:media, media.id, false)
|
||||
|
||||
Notify.tab_meta(:media, media.id, display_title(updated_media),
|
||||
updated_media.original_name || updated_media.mime_type || "")
|
||||
Notify.tab_meta(
|
||||
:media,
|
||||
media.id,
|
||||
display_title(updated_media),
|
||||
updated_media.original_name || updated_media.mime_type || ""
|
||||
)
|
||||
|
||||
notify_output(socket, dgettext("ui", "Media"), dgettext("ui", "Media saved"))
|
||||
socket
|
||||
|
||||
@@ -471,8 +471,12 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|
||||
|> assign(:dirty?, false)
|
||||
|> build_data()
|
||||
|
||||
Notify.tab_meta(:post, post.id, record_title(record, refreshed_post),
|
||||
Atom.to_string(record_status(record)))
|
||||
Notify.tab_meta(
|
||||
:post,
|
||||
post.id,
|
||||
record_title(record, refreshed_post),
|
||||
Atom.to_string(record_status(record))
|
||||
)
|
||||
|
||||
Notify.dirty(:post, post.id, false)
|
||||
Notify.cancel_auto_save(:post, post.id)
|
||||
@@ -511,8 +515,12 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|
||||
|> assign(:dirty?, false)
|
||||
|> build_data()
|
||||
|
||||
Notify.tab_meta(:post, post.id, record_title(record, refreshed_post),
|
||||
Atom.to_string(record_status(record)))
|
||||
Notify.tab_meta(
|
||||
:post,
|
||||
post.id,
|
||||
record_title(record, refreshed_post),
|
||||
Atom.to_string(record_status(record))
|
||||
)
|
||||
|
||||
Notify.dirty(:post, post.id, false)
|
||||
notify_output(socket, dgettext("ui", "Post"), dgettext("ui", "Post published"))
|
||||
@@ -546,9 +554,12 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|
||||
|> assign(:dirty?, false)
|
||||
|> build_data()
|
||||
|
||||
Notify.tab_meta(:post, post.id,
|
||||
Notify.tab_meta(
|
||||
:post,
|
||||
post.id,
|
||||
restored_post.title || restored_post.slug || restored_post.id,
|
||||
Atom.to_string(restored_post.status || :draft))
|
||||
Atom.to_string(restored_post.status || :draft)
|
||||
)
|
||||
|
||||
Notify.dirty(:post, post.id, false)
|
||||
socket
|
||||
|
||||
@@ -271,10 +271,10 @@ defmodule BDS.Desktop.ShellLive.TagsEditor do
|
||||
end
|
||||
end
|
||||
|
||||
attr :color, :string, default: nil
|
||||
attr :presets, :list, required: true
|
||||
attr :pick_event, :string, required: true
|
||||
attr :target, :any, required: true
|
||||
attr(:color, :string, default: nil)
|
||||
attr(:presets, :list, required: true)
|
||||
attr(:pick_event, :string, required: true)
|
||||
attr(:target, :any, required: true)
|
||||
|
||||
defp colour_picker(assigns) do
|
||||
~H"""
|
||||
|
||||
@@ -584,7 +584,9 @@ defmodule BDS.Embeddings do
|
||||
end
|
||||
|
||||
defp duplicate_pairs_with_rebuild(project_id, entries, on_progress) do
|
||||
case Index.duplicate_pairs(project_id, entries, @duplicate_threshold, on_progress: on_progress) do
|
||||
case Index.duplicate_pairs(project_id, entries, @duplicate_threshold,
|
||||
on_progress: on_progress
|
||||
) do
|
||||
{:ok, pairs} ->
|
||||
{:ok, pairs}
|
||||
|
||||
|
||||
@@ -155,7 +155,9 @@ defmodule BDS.Embeddings.Backends.Neural do
|
||||
# Place model params/tensors on the Apple GPU (Metal) when accelerating with
|
||||
# EMLX so the compiled inference pass actually runs on-device. EXLA manages
|
||||
# its own device placement, so nothing to do there.
|
||||
defp maybe_set_default_backend(:emlx), do: Nx.global_default_backend({EMLX.Backend, device: :gpu})
|
||||
defp maybe_set_default_backend(:emlx),
|
||||
do: Nx.global_default_backend({EMLX.Backend, device: :gpu})
|
||||
|
||||
defp maybe_set_default_backend(:exla), do: :ok
|
||||
|
||||
@doc false
|
||||
|
||||
@@ -58,7 +58,11 @@ defmodule BDS.Embeddings.Index do
|
||||
"""
|
||||
def neighbors(project_id, query_label, query_vector, limit)
|
||||
when is_binary(project_id) and is_integer(query_label) and is_binary(query_vector) do
|
||||
GenServer.call(__MODULE__, {:neighbors, project_id, query_label, query_vector, limit}, :infinity)
|
||||
GenServer.call(
|
||||
__MODULE__,
|
||||
{:neighbors, project_id, query_label, query_vector, limit},
|
||||
:infinity
|
||||
)
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -167,7 +171,10 @@ defmodule BDS.Embeddings.Index do
|
||||
|
||||
defp build_entry(dimensions, entries) do
|
||||
count = length(entries)
|
||||
{:ok, index} = HNSWLib.Index.new(@space, dimensions, count, m: @m, ef_construction: @ef_construction)
|
||||
|
||||
{:ok, index} =
|
||||
HNSWLib.Index.new(@space, dimensions, count, m: @m, ef_construction: @ef_construction)
|
||||
|
||||
:ok = HNSWLib.Index.set_ef(index, @ef_search)
|
||||
|
||||
tensor =
|
||||
|
||||
@@ -82,9 +82,7 @@ defmodule BDS.Generation.Pagefind do
|
||||
# Returns nil when the page is not marked, so unmarked pages are excluded
|
||||
# from the index entirely (matching Pagefind semantics).
|
||||
defp body_text(content) do
|
||||
case Regex.run(~r/<([a-zA-Z0-9]+)[^>]*\bdata-pagefind-body\b[^>]*>/, content,
|
||||
return: :index
|
||||
) do
|
||||
case Regex.run(~r/<([a-zA-Z0-9]+)[^>]*\bdata-pagefind-body\b[^>]*>/, content, return: :index) do
|
||||
[{open_start, open_len}, {tag_start, tag_len}] ->
|
||||
tag = binary_part(content, tag_start, tag_len)
|
||||
region = scoped_region(content, tag, open_start + open_len)
|
||||
@@ -117,7 +115,10 @@ defmodule BDS.Generation.Pagefind do
|
||||
end
|
||||
|
||||
defp event_kind(rest, pos, tag) do
|
||||
if String.starts_with?(binary_part(rest, pos, min(2 + byte_size(tag), byte_size(rest) - pos)), "</") do
|
||||
if String.starts_with?(
|
||||
binary_part(rest, pos, min(2 + byte_size(tag), byte_size(rest) - pos)),
|
||||
"</"
|
||||
) do
|
||||
:close
|
||||
else
|
||||
:open
|
||||
|
||||
@@ -240,7 +240,11 @@ defmodule BDS.Media do
|
||||
|> Repo.insert_or_update!()
|
||||
end) do
|
||||
{:ok, updated_translation} ->
|
||||
log_sidecar_error(write_translation_sidecar(project, media, updated_translation), media.id)
|
||||
log_sidecar_error(
|
||||
write_translation_sidecar(project, media, updated_translation),
|
||||
media.id
|
||||
)
|
||||
|
||||
:ok = Search.sync_media(media.id)
|
||||
{:ok, updated_translation}
|
||||
|
||||
|
||||
@@ -178,7 +178,9 @@ defmodule BDS.Preview do
|
||||
case kind do
|
||||
:media ->
|
||||
serve_file(safe_join(server.data_dir, Path.join(["media", relative_path])),
|
||||
server: server, query_params: query_params)
|
||||
server: server,
|
||||
query_params: query_params
|
||||
)
|
||||
|
||||
:generated ->
|
||||
case BDS.Preview.Router.render_route(server.project_id, request_path) do
|
||||
@@ -187,7 +189,9 @@ defmodule BDS.Preview do
|
||||
|
||||
:not_matched ->
|
||||
serve_file(safe_join(Path.join(server.data_dir, "html"), relative_path),
|
||||
server: server, query_params: query_params)
|
||||
server: server,
|
||||
query_params: query_params
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -240,7 +244,10 @@ defmodule BDS.Preview do
|
||||
|
||||
defp draft_preview_payload(post, query_params) do
|
||||
requested_language = query_params |> Map.get("lang") |> normalize_requested_language()
|
||||
effective_slug = post.template_slug || TemplateSelection.resolve_post_template_slug(post.project_id, post.tags, post.categories)
|
||||
|
||||
effective_slug =
|
||||
post.template_slug ||
|
||||
TemplateSelection.resolve_post_template_slug(post.project_id, post.tags, post.categories)
|
||||
|
||||
case draft_preview_translation(post.id, requested_language, post.language) do
|
||||
%Translation{} = translation ->
|
||||
|
||||
@@ -132,6 +132,7 @@ defmodule BDS.Preview.Router do
|
||||
defp render(project_id, {:home, page_number}, language, main_language, metadata) do
|
||||
posts = load_published_list_posts(project_id, metadata)
|
||||
posts = maybe_resolve_language(posts, language, main_language, project_id)
|
||||
|
||||
render_list(project_id, posts, page_number, metadata, language, main_language, %{kind: "core"})
|
||||
end
|
||||
|
||||
@@ -193,7 +194,13 @@ defmodule BDS.Preview.Router do
|
||||
})
|
||||
end
|
||||
|
||||
defp render(project_id, {:day, year, month, day, page_number}, language, main_language, metadata) do
|
||||
defp render(
|
||||
project_id,
|
||||
{:day, year, month, day, page_number},
|
||||
language,
|
||||
main_language,
|
||||
metadata
|
||||
) do
|
||||
posts = load_published_posts_by_day(project_id, year, month, day)
|
||||
posts = maybe_resolve_language(posts, language, main_language, project_id)
|
||||
|
||||
@@ -208,7 +215,8 @@ defmodule BDS.Preview.Router do
|
||||
## Post rendering
|
||||
|
||||
defp render_post(project_id, post, language, main_language) do
|
||||
{effective_record, body} = resolve_post_for_language(project_id, post, language, main_language)
|
||||
{effective_record, body} =
|
||||
resolve_post_for_language(project_id, post, language, main_language)
|
||||
|
||||
assigns = %{
|
||||
id: effective_record.id,
|
||||
@@ -220,7 +228,9 @@ defmodule BDS.Preview.Router do
|
||||
_post_record: effective_record
|
||||
}
|
||||
|
||||
effective_slug = post.template_slug || TemplateSelection.resolve_post_template_slug(project_id, post.tags, post.categories)
|
||||
effective_slug =
|
||||
post.template_slug ||
|
||||
TemplateSelection.resolve_post_template_slug(project_id, post.tags, post.categories)
|
||||
|
||||
case Rendering.render_post_page(project_id, effective_slug, assigns) do
|
||||
{:ok, rendered} -> {:ok, rendered}
|
||||
@@ -370,7 +380,8 @@ defmodule BDS.Preview.Router do
|
||||
|
||||
defp archive_page_title(%{kind: "date", year: y, month: m, day: d})
|
||||
when is_integer(y) and is_integer(m) and is_integer(d),
|
||||
do: "#{y}-#{String.pad_leading(Integer.to_string(m), 2, "0")}-#{String.pad_leading(Integer.to_string(d), 2, "0")}"
|
||||
do:
|
||||
"#{y}-#{String.pad_leading(Integer.to_string(m), 2, "0")}-#{String.pad_leading(Integer.to_string(d), 2, "0")}"
|
||||
|
||||
defp archive_page_title(%{kind: "date", year: y, month: m})
|
||||
when is_integer(y) and is_integer(m),
|
||||
@@ -504,7 +515,8 @@ defmodule BDS.Preview.Router do
|
||||
if String.downcase(to_string(language)) == String.downcase(to_string(main_language)) do
|
||||
posts
|
||||
else
|
||||
translations = load_translations_for_language(project_id, Enum.map(posts, & &1.id), language)
|
||||
translations =
|
||||
load_translations_for_language(project_id, Enum.map(posts, & &1.id), language)
|
||||
|
||||
Enum.map(posts, fn post ->
|
||||
case Map.get(translations, post.id) do
|
||||
|
||||
@@ -104,8 +104,9 @@ defmodule BDS.Projects do
|
||||
end
|
||||
|
||||
@spec project_data_dir(Project.t()) :: String.t()
|
||||
def project_data_dir(%Project{data_path: data_path}) when is_binary(data_path) and data_path != "",
|
||||
do: data_path
|
||||
def project_data_dir(%Project{data_path: data_path})
|
||||
when is_binary(data_path) and data_path != "",
|
||||
do: data_path
|
||||
|
||||
# A project without an explicit data_path resolves to its folder under the
|
||||
# per-user default content location — never priv/data inside the repo
|
||||
|
||||
@@ -43,12 +43,26 @@ defmodule BDS.Rendering.Filters do
|
||||
_language_prefix,
|
||||
context
|
||||
) do
|
||||
render_markdown(value, canonical_post_paths, canonical_media_paths, language, context, post_id)
|
||||
render_markdown(
|
||||
value,
|
||||
canonical_post_paths,
|
||||
canonical_media_paths,
|
||||
language,
|
||||
context,
|
||||
post_id
|
||||
)
|
||||
end
|
||||
|
||||
@spec render_markdown(term(), map() | nil, map() | nil, String.t(), Liquex.Context.t(), term()) ::
|
||||
String.t()
|
||||
def render_markdown(value, canonical_post_paths, canonical_media_paths, language, context, post_id \\ nil) do
|
||||
def render_markdown(
|
||||
value,
|
||||
canonical_post_paths,
|
||||
canonical_media_paths,
|
||||
language,
|
||||
context,
|
||||
post_id \\ nil
|
||||
) do
|
||||
value
|
||||
|> to_string()
|
||||
|> replace_built_in_macros(language, context, post_id)
|
||||
@@ -161,7 +175,11 @@ defmodule BDS.Rendering.Filters do
|
||||
else
|
||||
{:error, reason, line} ->
|
||||
require Logger
|
||||
Logger.warning("Macro template parse failed (#{template_path}): #{reason} at line #{line}")
|
||||
|
||||
Logger.warning(
|
||||
"Macro template parse failed (#{template_path}): #{reason} at line #{line}"
|
||||
)
|
||||
|
||||
""
|
||||
|
||||
{:error, message} ->
|
||||
@@ -365,8 +383,7 @@ defmodule BDS.Rendering.Filters do
|
||||
"root_classes" => "macro-photo-archive",
|
||||
"data_attrs" => [],
|
||||
"months" => months,
|
||||
"empty_label" =>
|
||||
BDS.Gettext.lgettext(language, "render", "No photos found")
|
||||
"empty_label" => BDS.Gettext.lgettext(language, "render", "No photos found")
|
||||
},
|
||||
context
|
||||
)
|
||||
@@ -391,8 +408,7 @@ defmodule BDS.Rendering.Filters do
|
||||
"width" => Map.get(params, "width", width),
|
||||
"height" => Map.get(params, "height", height),
|
||||
"aria_label" => "Tag cloud",
|
||||
"empty_label" =>
|
||||
BDS.Gettext.lgettext(language, "render", "No tags")
|
||||
"empty_label" => BDS.Gettext.lgettext(language, "render", "No tags")
|
||||
},
|
||||
context
|
||||
)
|
||||
@@ -456,9 +472,18 @@ defmodule BDS.Rendering.Filters do
|
||||
|
||||
defp group_by_media_month(media_records) do
|
||||
month_names = %{
|
||||
1 => "January", 2 => "February", 3 => "March", 4 => "April",
|
||||
5 => "May", 6 => "June", 7 => "July", 8 => "August",
|
||||
9 => "September", 10 => "October", 11 => "November", 12 => "December"
|
||||
1 => "January",
|
||||
2 => "February",
|
||||
3 => "March",
|
||||
4 => "April",
|
||||
5 => "May",
|
||||
6 => "June",
|
||||
7 => "July",
|
||||
8 => "August",
|
||||
9 => "September",
|
||||
10 => "October",
|
||||
11 => "November",
|
||||
12 => "December"
|
||||
}
|
||||
|
||||
media_records
|
||||
@@ -555,8 +580,10 @@ defmodule BDS.Rendering.Filters do
|
||||
_ -> default
|
||||
end
|
||||
end
|
||||
|
||||
defp normalize_columns(value, _default, min, max) when is_integer(value),
|
||||
do: value |> max(min) |> min(max)
|
||||
|
||||
defp normalize_columns(_value, default, _min, _max), do: default
|
||||
|
||||
defp parse_integer(value) when is_binary(value) do
|
||||
@@ -565,6 +592,7 @@ defmodule BDS.Rendering.Filters do
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
defp parse_integer(value) when is_integer(value), do: value
|
||||
defp parse_integer(_value), do: nil
|
||||
|
||||
@@ -583,6 +611,7 @@ defmodule BDS.Rendering.Filters do
|
||||
case DateTime.from_iso8601("#{year}-#{pad(month + 1)}-01T00:00:00Z") do
|
||||
{:ok, dt, _} ->
|
||||
dt |> DateTime.add(-1, :second) |> DateTime.to_date() |> Map.get(:day)
|
||||
|
||||
_ ->
|
||||
31
|
||||
end
|
||||
|
||||
@@ -349,5 +349,4 @@ defmodule BDS.Rendering.ListArchive do
|
||||
do: RenderMetadata.calendar_initial_month(post)
|
||||
|
||||
defp calendar_initial_month_from_posts([]), do: nil
|
||||
|
||||
end
|
||||
|
||||
@@ -63,8 +63,12 @@ defmodule BDS.Scripting do
|
||||
args,
|
||||
Keyword.put(opts, :timeout, timeout)
|
||||
) do
|
||||
{:ok, nil} -> {:ok, ""}
|
||||
{:ok, value} -> {:ok, to_string(value)}
|
||||
{:ok, nil} ->
|
||||
{:ok, ""}
|
||||
|
||||
{:ok, value} ->
|
||||
{:ok, to_string(value)}
|
||||
|
||||
{:error, reason} ->
|
||||
Logger.warning("execute_macro failed for project #{project_id}: #{inspect(reason)}")
|
||||
{:error, reason}
|
||||
|
||||
@@ -111,8 +111,7 @@ defmodule BDS.Scripting.Capabilities do
|
||||
get_by_status: one_arg(fn status -> posts_by_status(project_id, status) end),
|
||||
get_by_year_month: zero_or_one_arg(fn _args -> post_counts_by_year_month(project_id) end),
|
||||
get_dashboard_stats: zero_or_one_arg(fn _args -> post_dashboard_stats(project_id) end),
|
||||
get_linked_by:
|
||||
one_arg(fn post_id -> linked_posts_for(project_id, post_id, :incoming) end),
|
||||
get_linked_by: one_arg(fn post_id -> linked_posts_for(project_id, post_id, :incoming) end),
|
||||
get_links_to: one_arg(fn post_id -> linked_posts_for(project_id, post_id, :outgoing) end),
|
||||
get_preview_url:
|
||||
two_arg(fn post_id, options -> preview_url(project_id, post_id, options) end),
|
||||
@@ -157,8 +156,7 @@ defmodule BDS.Scripting.Capabilities do
|
||||
end),
|
||||
filter: one_arg(fn filters -> filter_media(project_id, filters) end),
|
||||
import: one_arg(fn attrs -> import_media(project_id, attrs) end),
|
||||
get_by_year_month:
|
||||
zero_or_one_arg(fn _args -> media_counts_by_year_month(project_id) end),
|
||||
get_by_year_month: zero_or_one_arg(fn _args -> media_counts_by_year_month(project_id) end),
|
||||
get_file_path: one_arg(fn media_id -> media_file_path(project_id, media_id) end),
|
||||
update: two_arg(fn media_id, attrs -> update_media(project_id, media_id, attrs) end),
|
||||
delete: one_arg(fn media_id -> delete_media(project_id, media_id) end),
|
||||
@@ -172,8 +170,7 @@ defmodule BDS.Scripting.Capabilities do
|
||||
two_arg(fn media_id, language ->
|
||||
load_media_translation(project_id, media_id, language)
|
||||
end),
|
||||
get_translations:
|
||||
one_arg(fn media_id -> list_media_translations(project_id, media_id) end),
|
||||
get_translations: one_arg(fn media_id -> list_media_translations(project_id, media_id) end),
|
||||
get_url: one_arg(fn media_id -> media_url(project_id, media_id) end),
|
||||
rebuild_from_files: zero_or_one_arg(fn _args -> rebuild_media_from_files(project_id) end),
|
||||
regenerate_missing_thumbnails:
|
||||
@@ -201,8 +198,7 @@ defmodule BDS.Scripting.Capabilities do
|
||||
get: one_arg(fn script_id -> load_script(project_id, script_id) end),
|
||||
get_all: zero_or_one_arg(fn _args -> list_scripts(project_id) end),
|
||||
publish: one_arg(fn script_id -> publish_script(project_id, script_id) end),
|
||||
rebuild_from_files:
|
||||
zero_or_one_arg(fn _args -> rebuild_scripts_from_files(project_id) end)
|
||||
rebuild_from_files: zero_or_one_arg(fn _args -> rebuild_scripts_from_files(project_id) end)
|
||||
}
|
||||
end
|
||||
|
||||
@@ -295,8 +291,7 @@ defmodule BDS.Scripting.Capabilities do
|
||||
find_similar: two_arg(fn post_id, limit -> find_similar(post_id, limit) end),
|
||||
compute_similarities:
|
||||
two_arg(fn post_id, target_ids -> compute_similarities(post_id, target_ids) end),
|
||||
suggest_tags:
|
||||
two_arg(fn post_id, exclude_tags -> suggest_tags(post_id, exclude_tags) end),
|
||||
suggest_tags: two_arg(fn post_id, exclude_tags -> suggest_tags(post_id, exclude_tags) end),
|
||||
find_duplicates: zero_or_one_arg(fn _args -> find_duplicates(project_id) end),
|
||||
dismiss_pair: two_arg(fn post_id_a, post_id_b -> dismiss_pair(post_id_a, post_id_b) end),
|
||||
index_unindexed_posts: zero_or_one_arg(fn _args -> index_unindexed_posts(project_id) end)
|
||||
|
||||
@@ -284,14 +284,24 @@ defmodule BDS.Search do
|
||||
|
||||
defp maybe_where_year(query, year) do
|
||||
year_str = to_string(year)
|
||||
where(query, [p], fragment("strftime('%Y', datetime(? / 1000, 'unixepoch')) = ?", p.created_at, ^year_str))
|
||||
|
||||
where(
|
||||
query,
|
||||
[p],
|
||||
fragment("strftime('%Y', datetime(? / 1000, 'unixepoch')) = ?", p.created_at, ^year_str)
|
||||
)
|
||||
end
|
||||
|
||||
defp maybe_where_month(query, nil), do: query
|
||||
|
||||
defp maybe_where_month(query, month) do
|
||||
month_str = String.pad_leading(to_string(month), 2, "0")
|
||||
where(query, [p], fragment("strftime('%m', datetime(? / 1000, 'unixepoch')) = ?", p.created_at, ^month_str))
|
||||
|
||||
where(
|
||||
query,
|
||||
[p],
|
||||
fragment("strftime('%m', datetime(? / 1000, 'unixepoch')) = ?", p.created_at, ^month_str)
|
||||
)
|
||||
end
|
||||
|
||||
defp maybe_where_from(query, nil), do: query
|
||||
@@ -305,7 +315,10 @@ defmodule BDS.Search do
|
||||
defp maybe_where_tags(query, tags) do
|
||||
tags_clause =
|
||||
Enum.reduce(tags, false, fn tag, acc ->
|
||||
dynamic([p], ^acc or fragment("EXISTS (SELECT 1 FROM json_each(?) WHERE value = ?)", p.tags, ^tag))
|
||||
dynamic(
|
||||
[p],
|
||||
^acc or fragment("EXISTS (SELECT 1 FROM json_each(?) WHERE value = ?)", p.tags, ^tag)
|
||||
)
|
||||
end)
|
||||
|
||||
where(query, [p], ^tags_clause)
|
||||
@@ -316,7 +329,10 @@ defmodule BDS.Search do
|
||||
defp maybe_where_tags_media(query, tags) do
|
||||
tags_clause =
|
||||
Enum.reduce(tags, false, fn tag, acc ->
|
||||
dynamic([m], ^acc or fragment("EXISTS (SELECT 1 FROM json_each(?) WHERE value = ?)", m.tags, ^tag))
|
||||
dynamic(
|
||||
[m],
|
||||
^acc or fragment("EXISTS (SELECT 1 FROM json_each(?) WHERE value = ?)", m.tags, ^tag)
|
||||
)
|
||||
end)
|
||||
|
||||
where(query, [m], ^tags_clause)
|
||||
@@ -327,7 +343,15 @@ defmodule BDS.Search do
|
||||
defp maybe_where_categories(query, categories) do
|
||||
categories_clause =
|
||||
Enum.reduce(categories, false, fn category, acc ->
|
||||
dynamic([p], ^acc or fragment("EXISTS (SELECT 1 FROM json_each(?) WHERE value = ?)", p.categories, ^category))
|
||||
dynamic(
|
||||
[p],
|
||||
^acc or
|
||||
fragment(
|
||||
"EXISTS (SELECT 1 FROM json_each(?) WHERE value = ?)",
|
||||
p.categories,
|
||||
^category
|
||||
)
|
||||
)
|
||||
end)
|
||||
|
||||
where(query, [p], ^categories_clause)
|
||||
@@ -548,8 +572,6 @@ defmodule BDS.Search do
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
|
||||
defp post_index_fields(post, translations) do
|
||||
post_language = normalize_language(post.language)
|
||||
|
||||
@@ -656,8 +678,8 @@ defmodule BDS.Search do
|
||||
else
|
||||
Repo.all(
|
||||
from translation in MediaTranslation,
|
||||
where: translation.translation_for in ^media_ids,
|
||||
select: {translation.translation_for, translation}
|
||||
where: translation.translation_for in ^media_ids,
|
||||
select: {translation.translation_for, translation}
|
||||
)
|
||||
|> Enum.group_by(fn {media_id, _} -> media_id end, fn {_, translation} -> translation end)
|
||||
end
|
||||
|
||||
@@ -239,6 +239,7 @@ defmodule BDS.Tags do
|
||||
{:error, changeset} -> Repo.rollback(changeset)
|
||||
end
|
||||
end)
|
||||
|
||||
Enum.map(affected_posts, & &1.id)
|
||||
end)
|
||||
|> case do
|
||||
|
||||
@@ -79,8 +79,14 @@ defmodule BDS.UI.Dashboard do
|
||||
from post in Post,
|
||||
where: post.project_id == ^project_id,
|
||||
group_by: [
|
||||
fragment("CAST(strftime('%Y', datetime(? / 1000, 'unixepoch')) AS INTEGER)", post.created_at),
|
||||
fragment("CAST(strftime('%m', datetime(? / 1000, 'unixepoch')) AS INTEGER)", post.created_at)
|
||||
fragment(
|
||||
"CAST(strftime('%Y', datetime(? / 1000, 'unixepoch')) AS INTEGER)",
|
||||
post.created_at
|
||||
),
|
||||
fragment(
|
||||
"CAST(strftime('%m', datetime(? / 1000, 'unixepoch')) AS INTEGER)",
|
||||
post.created_at
|
||||
)
|
||||
],
|
||||
select: %{
|
||||
year:
|
||||
|
||||
Reference in New Issue
Block a user