Merge branch 'main' into feat/447_concistency
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is passing

This commit is contained in:
carla 2026-02-25 16:52:33 +01:00
commit c7c082b867
27 changed files with 926 additions and 131 deletions

View file

@ -1969,11 +1969,6 @@ msgstr "Fehlgeschlagen: %{count} Zeile(n)"
msgid "German Template"
msgstr "Deutsche Vorlage"
#: lib/mv_web/live/import_live.ex
#, elixir-autogen, elixir-format
msgid "Import Members (CSV)"
msgstr "Mitglieder importieren (CSV)"
#: lib/mv_web/live/import_live/components.ex
#, elixir-autogen, elixir-format
msgid "Import Results"
@ -2298,11 +2293,6 @@ msgstr "Du hast keine Berechtigung, auf diese Seite zuzugreifen."
msgid "Manage Member Data"
msgstr "Mitgliederdaten verwalten"
#: lib/mv_web/live/import_live/components.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Use the data field name as the CSV column header in your file. Data fields must exist in Mila before importing, so they must be listed in the list of member data (like e-mail or first name). Unknown data field columns will be ignored with a warning."
msgstr "Verwende die Namen der Datenfelder als Spaltennamen in der CSV Datei. Datenfelder müssen in Mila bereits angelegt sein, damit sie importiert werden können. sie müssen in der Liste der Mitgliederdaten als Datenfeld enthalten sein (z.B. E-Mail). Spalten mit unbekannten Spaltenüberschriften werden mit einer Warnung ignoriert."
#: lib/mv_web/components/export_dropdown.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Export members to CSV"
@ -2491,7 +2481,7 @@ msgstr "Erstellt am:"
#: lib/mv_web/components/export_dropdown.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Export"
msgstr "Nach CSV exportieren"
msgstr "Export"
#: lib/mv_web/controllers/member_pdf_export_controller.ex
#, elixir-autogen, elixir-format
@ -2534,6 +2524,13 @@ msgstr "Import"
msgid "Value type cannot be changed after creation"
msgstr "Der Wertetyp kann nach dem Erstellen nicht mehr geändert werden."
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/index.html.heex
#: lib/mv_web/translations/member_fields.ex
#, elixir-autogen, elixir-format
msgid "Country"
msgstr "Land"
#: lib/mv_web/live/group_live/show.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Could not load member list. Please try again."
@ -2893,6 +2890,31 @@ msgstr "Für die Vereinfacht-Integration erforderlich und kann nicht deaktiviert
msgid "Fee Type"
msgstr "Beitragsart"
#: lib/mv_web/live/import_live.ex
#, elixir-autogen, elixir-format
msgid "Import members from CSV files."
msgstr "Miglieder aus CSV Dateien importieren."
#: lib/mv_web/live/import_live/components.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Use the data field name as the CSV column header in your file. Data fields must exist in Mila before importing, because unknown data field columns will be ignored. Groups and membership fees are not supported for import."
msgstr "Verwende die Namen der Datenfelder als Spaltennamen in der CSV Datei. Datenfelder müssen in Mila bereits angelegt sein, da unbekannte Spaltennamen ignoriert werden. Gruppen und der Beitragsstatus kann nicht importiert werden."
#: lib/mv_web/live/import_live.ex
#, elixir-autogen, elixir-format
msgid "Choose CSV file"
msgstr "CSV Datei auswählen"
#: lib/mv_web/live/import_live.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Import Members"
msgstr "Mitglieder importieren (CSV)"
#~ #: lib/mv_web/live/import_live.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Datei auswählen"
#~ msgstr ""
#: lib/mv_web/live/global_settings_live.ex
#, elixir-autogen, elixir-format
msgid "Admin group name"

View file

@ -1970,11 +1970,6 @@ msgstr ""
msgid "German Template"
msgstr ""
#: lib/mv_web/live/import_live.ex
#, elixir-autogen, elixir-format
msgid "Import Members (CSV)"
msgstr ""
#: lib/mv_web/live/import_live/components.ex
#, elixir-autogen, elixir-format
msgid "Import Results"
@ -2299,11 +2294,6 @@ msgstr ""
msgid "Manage Member Data"
msgstr ""
#: lib/mv_web/live/import_live/components.ex
#, elixir-autogen, elixir-format
msgid "Use the data field name as the CSV column header in your file. Data fields must exist in Mila before importing, so they must be listed in the list of member data (like e-mail or first name). Unknown data field columns will be ignored with a warning."
msgstr ""
#: lib/mv_web/components/export_dropdown.ex
#, elixir-autogen, elixir-format
msgid "Export members to CSV"
@ -2535,6 +2525,13 @@ msgstr ""
msgid "Value type cannot be changed after creation"
msgstr ""
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/index.html.heex
#: lib/mv_web/translations/member_fields.ex
#, elixir-autogen, elixir-format
msgid "Country"
msgstr ""
#: lib/mv_web/live/group_live/show.ex
#, elixir-autogen, elixir-format
msgid "Could not load member list. Please try again."
@ -2893,6 +2890,26 @@ msgstr ""
msgid "Fee Type"
msgstr ""
#: lib/mv_web/live/import_live.ex
#, elixir-autogen, elixir-format
msgid "Import members from CSV files."
msgstr ""
#: lib/mv_web/live/import_live/components.ex
#, elixir-autogen, elixir-format
msgid "Use the data field name as the CSV column header in your file. Data fields must exist in Mila before importing, because unknown data field columns will be ignored. Groups and membership fees are not supported for import."
msgstr ""
#: lib/mv_web/live/import_live.ex
#, elixir-autogen, elixir-format
msgid "Choose CSV file"
msgstr ""
#: lib/mv_web/live/import_live.ex
#, elixir-autogen, elixir-format
msgid "Import Members"
msgstr ""
#: lib/mv_web/live/global_settings_live.ex
#, elixir-autogen, elixir-format
msgid "Admin group name"

View file

@ -1970,11 +1970,6 @@ msgstr ""
msgid "German Template"
msgstr ""
#: lib/mv_web/live/import_live.ex
#, elixir-autogen, elixir-format
msgid "Import Members (CSV)"
msgstr ""
#: lib/mv_web/live/import_live/components.ex
#, elixir-autogen, elixir-format
msgid "Import Results"
@ -2299,11 +2294,6 @@ msgstr ""
msgid "Manage Member Data"
msgstr ""
#: lib/mv_web/live/import_live/components.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Use the data field name as the CSV column header in your file. Data fields must exist in Mila before importing, so they must be listed in the list of member data (like e-mail or first name). Unknown data field columns will be ignored with a warning."
msgstr ""
#: lib/mv_web/components/export_dropdown.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Export members to CSV"
@ -2535,6 +2525,13 @@ msgstr ""
msgid "Value type cannot be changed after creation"
msgstr ""
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/index.html.heex
#: lib/mv_web/translations/member_fields.ex
#, elixir-autogen, elixir-format
msgid "Country"
msgstr ""
#: lib/mv_web/live/group_live/show.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Could not load member list. Please try again."
@ -2893,6 +2890,26 @@ msgstr "Required for Vereinfacht integration and cannot be disabled."
msgid "Fee Type"
msgstr "Fee Type"
#: lib/mv_web/live/import_live.ex
#, elixir-autogen, elixir-format
msgid "Import members from CSV files."
msgstr ""
#: lib/mv_web/live/import_live/components.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Use the data field name as the CSV column header in your file. Data fields must exist in Mila before importing, because unknown data field columns will be ignored. Groups and membership fees are not supported for import."
msgstr ""
#: lib/mv_web/live/import_live.ex
#, elixir-autogen, elixir-format
msgid "Choose CSV file"
msgstr ""
#: lib/mv_web/live/import_live.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Import Members"
msgstr ""
#: lib/mv_web/live/global_settings_live.ex
#, elixir-autogen, elixir-format
msgid "Admin group name"

View file

@ -9,7 +9,13 @@
#set page(
paper: "a4",
flipped: true,
margin: (top: 1.2cm, bottom: 1.2cm, left: 1.0cm, right: 1.0cm)
margin: (top: 1.2cm, bottom: 1.2cm, left: 1.0cm, right: 1.0cm),
numbering: "1",
footer: context [
#set text(size: 8pt)
#set align(center)
#counter(page).display("1 / 1", both: true)
]
)
#set text(size: 9pt, hyphenate: true)
@ -58,7 +64,6 @@
#let start = fixed_count + chunk_index * max_dynamic_cols
#let page_cols = fixed_cols + dyn_cols_chunk
#let headers = page_cols.map(c => c.at("label", default: ""))
// widths: first two columns fixed (32mm, 42mm), rest distributed as 1fr
#let widths = (
@ -67,9 +72,9 @@
..((1fr,) * dyn_count)
)
#let header_cells = headers.map(h => text(weight: "bold", size: 9pt)[#h])
#let header_cells = page_cols.map(c => text(weight: "bold", size: 9pt)[#c.at("label", default: "")])
// Body cells (row-major), nur die Spalten dieses Chunks
// Body cells (row-major), only columns of this chunk
#let body_cells = (
rows
.map(row => row.slice(0, fixed_count) + row.slice(start, start + dyn_count))
@ -77,8 +82,27 @@
.flatten()
)
// Thinner grid for body; thicker vertical line between column 2 and 3; header and outer borders thick
#let thin_stroke = 0.3pt + black
#let thick_sep = 1.5pt + black
#let thick_stroke = 1pt + black
#let last_x = fixed_count + dyn_count - 1
#let last_y = rows.len()
#let stroke_fn = (x, y) => {
let top = if y == 0 or y == 1 { thick_stroke } else { thin_stroke }
let bottom = if y == 0 or y == last_y { thick_stroke } else { thin_stroke }
let left = if x == 0 { thick_stroke } else if x == 2 { thick_sep } else { thin_stroke }
let right = if x == last_x { thick_stroke } else { thin_stroke }
(top: top, bottom: bottom, left: left, right: right)
}
// Light gray background for first two columns (first_name, last_name)
#let fill_fn = (x, y) => if x < 2 { rgb("f2f2f2") } else { none }
#table(
columns: widths,
stroke: stroke_fn,
fill: fill_fn,
table.header(..header_cells),
..body_cells,
)

View file

@ -0,0 +1,577 @@
defmodule Mv.Repo.Migrations.AddCountryToMembers do
@moduledoc """
Adds country as an optional member field and includes it in full-text search.
- Adds :country column to members table (text, nullable)
- Updates members_search_vector_trigger() to include country (weight C)
- Updates update_member_search_vector_from_custom_field_value() to include country
- Updates update_member_search_vector_from_member_groups() to include country
- Backfills existing members' search_vector with country
"""
use Ecto.Migration
def up do
alter table(:members) do
add :country, :text
end
# 1. Main trigger on members: add country to search_vector
execute("""
CREATE OR REPLACE FUNCTION members_search_vector_trigger() RETURNS trigger AS $$
DECLARE
custom_values_text text;
groups_text text;
BEGIN
SELECT string_agg(
CASE
WHEN value ? '_union_value' THEN value->>'_union_value'
WHEN value ? 'value' THEN value->>'value'
ELSE ''
END,
' '
)
INTO custom_values_text
FROM custom_field_values
WHERE member_id = NEW.id AND value IS NOT NULL;
SELECT string_agg(g.name, ' ')
INTO groups_text
FROM member_groups mg
JOIN groups g ON g.id = mg.group_id
WHERE mg.member_id = NEW.id;
NEW.search_vector :=
setweight(to_tsvector('simple', coalesce(NEW.first_name, '')), 'A') ||
setweight(to_tsvector('simple', coalesce(NEW.last_name, '')), 'A') ||
setweight(to_tsvector('simple', coalesce(NEW.email, '')), 'B') ||
setweight(to_tsvector('simple', coalesce(NEW.join_date::text, '')), 'D') ||
setweight(to_tsvector('simple', coalesce(NEW.exit_date::text, '')), 'D') ||
setweight(to_tsvector('simple', coalesce(NEW.notes, '')), 'B') ||
setweight(to_tsvector('simple', coalesce(NEW.city, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(NEW.street, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(NEW.house_number::text, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(NEW.postal_code::text, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(NEW.country, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(custom_values_text, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(groups_text, '')), 'B');
RETURN NEW;
END
$$ LANGUAGE plpgsql;
""")
# 2. Custom field trigger: include country in recomputed search_vector
execute("""
CREATE OR REPLACE FUNCTION update_member_search_vector_from_custom_field_value() RETURNS trigger AS $$
DECLARE
member_id_val uuid;
member_first_name text;
member_last_name text;
member_email text;
member_join_date date;
member_exit_date date;
member_notes text;
member_city text;
member_street text;
member_house_number text;
member_postal_code text;
member_country text;
custom_values_text text;
groups_text text;
old_value_text text;
new_value_text text;
BEGIN
member_id_val := COALESCE(NEW.member_id, OLD.member_id);
IF TG_OP = 'UPDATE' THEN
old_value_text := COALESCE(
NULLIF(OLD.value->>'_union_value', ''),
NULLIF(OLD.value->>'value', ''),
''
);
new_value_text := COALESCE(
NULLIF(NEW.value->>'_union_value', ''),
NULLIF(NEW.value->>'value', ''),
''
);
IF (old_value_text IS NOT DISTINCT FROM new_value_text) AND
(OLD.member_id IS NOT DISTINCT FROM NEW.member_id) AND
(OLD.custom_field_id IS NOT DISTINCT FROM NEW.custom_field_id) THEN
RETURN COALESCE(NEW, OLD);
END IF;
END IF;
SELECT
first_name,
last_name,
email,
join_date,
exit_date,
notes,
city,
street,
house_number,
postal_code,
country
INTO
member_first_name,
member_last_name,
member_email,
member_join_date,
member_exit_date,
member_notes,
member_city,
member_street,
member_house_number,
member_postal_code,
member_country
FROM members
WHERE id = member_id_val;
SELECT string_agg(
CASE
WHEN value ? '_union_value' THEN value->>'_union_value'
WHEN value ? 'value' THEN value->>'value'
ELSE ''
END,
' '
)
INTO custom_values_text
FROM custom_field_values
WHERE member_id = member_id_val AND value IS NOT NULL;
SELECT string_agg(g.name, ' ')
INTO groups_text
FROM member_groups mg
JOIN groups g ON g.id = mg.group_id
WHERE mg.member_id = member_id_val;
UPDATE members
SET search_vector =
setweight(to_tsvector('simple', coalesce(member_first_name, '')), 'A') ||
setweight(to_tsvector('simple', coalesce(member_last_name, '')), 'A') ||
setweight(to_tsvector('simple', coalesce(member_email, '')), 'B') ||
setweight(to_tsvector('simple', coalesce(member_join_date::text, '')), 'D') ||
setweight(to_tsvector('simple', coalesce(member_exit_date::text, '')), 'D') ||
setweight(to_tsvector('simple', coalesce(member_notes, '')), 'B') ||
setweight(to_tsvector('simple', coalesce(member_city, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(member_street, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(member_house_number::text, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(member_postal_code::text, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(member_country, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(custom_values_text, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(groups_text, '')), 'B')
WHERE id = member_id_val;
RETURN COALESCE(NEW, OLD);
END
$$ LANGUAGE plpgsql;
""")
# 3. Member groups trigger: include country when refreshing search_vector
execute("""
CREATE OR REPLACE FUNCTION update_member_search_vector_from_member_groups() RETURNS trigger AS $$
DECLARE
member_id_val uuid;
member_first_name text;
member_last_name text;
member_email text;
member_join_date date;
member_exit_date date;
member_notes text;
member_city text;
member_street text;
member_house_number text;
member_postal_code text;
member_country text;
custom_values_text text;
groups_text text;
BEGIN
FOR member_id_val IN
SELECT COALESCE(NEW.member_id, OLD.member_id)
UNION ALL
SELECT OLD.member_id
WHERE TG_OP = 'UPDATE' AND OLD.member_id IS DISTINCT FROM NEW.member_id
LOOP
SELECT
first_name,
last_name,
email,
join_date,
exit_date,
notes,
city,
street,
house_number,
postal_code,
country
INTO
member_first_name,
member_last_name,
member_email,
member_join_date,
member_exit_date,
member_notes,
member_city,
member_street,
member_house_number,
member_postal_code,
member_country
FROM members
WHERE id = member_id_val;
SELECT string_agg(
CASE
WHEN value ? '_union_value' THEN value->>'_union_value'
WHEN value ? 'value' THEN value->>'value'
ELSE ''
END,
' '
)
INTO custom_values_text
FROM custom_field_values
WHERE member_id = member_id_val AND value IS NOT NULL;
SELECT string_agg(g.name, ' ')
INTO groups_text
FROM member_groups mg
JOIN groups g ON g.id = mg.group_id
WHERE mg.member_id = member_id_val;
UPDATE members
SET search_vector =
setweight(to_tsvector('simple', coalesce(member_first_name, '')), 'A') ||
setweight(to_tsvector('simple', coalesce(member_last_name, '')), 'A') ||
setweight(to_tsvector('simple', coalesce(member_email, '')), 'B') ||
setweight(to_tsvector('simple', coalesce(member_join_date::text, '')), 'D') ||
setweight(to_tsvector('simple', coalesce(member_exit_date::text, '')), 'D') ||
setweight(to_tsvector('simple', coalesce(member_notes, '')), 'B') ||
setweight(to_tsvector('simple', coalesce(member_city, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(member_street, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(member_house_number::text, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(member_postal_code::text, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(member_country, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(custom_values_text, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(groups_text, '')), 'B')
WHERE id = member_id_val;
END LOOP;
RETURN COALESCE(NEW, OLD);
END
$$ LANGUAGE plpgsql;
""")
# 4. Backfill: update all members' search_vector to include country
execute("""
UPDATE members m
SET search_vector =
setweight(to_tsvector('simple', coalesce(m.first_name, '')), 'A') ||
setweight(to_tsvector('simple', coalesce(m.last_name, '')), 'A') ||
setweight(to_tsvector('simple', coalesce(m.email, '')), 'B') ||
setweight(to_tsvector('simple', coalesce(m.join_date::text, '')), 'D') ||
setweight(to_tsvector('simple', coalesce(m.exit_date::text, '')), 'D') ||
setweight(to_tsvector('simple', coalesce(m.notes, '')), 'B') ||
setweight(to_tsvector('simple', coalesce(m.city, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(m.street, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(m.house_number::text, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(m.postal_code::text, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(m.country, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(
(SELECT string_agg(
CASE
WHEN value ? '_union_value' THEN value->>'_union_value'
WHEN value ? 'value' THEN value->>'value'
ELSE ''
END,
' '
)
FROM custom_field_values
WHERE member_id = m.id AND value IS NOT NULL),
''
)), 'C') ||
setweight(to_tsvector('simple', coalesce(
(SELECT string_agg(g.name, ' ')
FROM member_groups mg
JOIN groups g ON g.id = mg.group_id
WHERE mg.member_id = m.id),
''
)), 'B')
""")
end
def down do
# Restore trigger functions without country (revert to previous version from AddGroupNamesToMemberSearchVector)
execute("""
CREATE OR REPLACE FUNCTION members_search_vector_trigger() RETURNS trigger AS $$
DECLARE
custom_values_text text;
groups_text text;
BEGIN
SELECT string_agg(
CASE
WHEN value ? '_union_value' THEN value->>'_union_value'
WHEN value ? 'value' THEN value->>'value'
ELSE ''
END,
' '
)
INTO custom_values_text
FROM custom_field_values
WHERE member_id = NEW.id AND value IS NOT NULL;
SELECT string_agg(g.name, ' ')
INTO groups_text
FROM member_groups mg
JOIN groups g ON g.id = mg.group_id
WHERE mg.member_id = NEW.id;
NEW.search_vector :=
setweight(to_tsvector('simple', coalesce(NEW.first_name, '')), 'A') ||
setweight(to_tsvector('simple', coalesce(NEW.last_name, '')), 'A') ||
setweight(to_tsvector('simple', coalesce(NEW.email, '')), 'B') ||
setweight(to_tsvector('simple', coalesce(NEW.join_date::text, '')), 'D') ||
setweight(to_tsvector('simple', coalesce(NEW.exit_date::text, '')), 'D') ||
setweight(to_tsvector('simple', coalesce(NEW.notes, '')), 'B') ||
setweight(to_tsvector('simple', coalesce(NEW.city, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(NEW.street, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(NEW.house_number::text, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(NEW.postal_code::text, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(custom_values_text, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(groups_text, '')), 'B');
RETURN NEW;
END
$$ LANGUAGE plpgsql;
""")
execute("""
CREATE OR REPLACE FUNCTION update_member_search_vector_from_custom_field_value() RETURNS trigger AS $$
DECLARE
member_id_val uuid;
member_first_name text;
member_last_name text;
member_email text;
member_join_date date;
member_exit_date date;
member_notes text;
member_city text;
member_street text;
member_house_number text;
member_postal_code text;
custom_values_text text;
groups_text text;
old_value_text text;
new_value_text text;
BEGIN
member_id_val := COALESCE(NEW.member_id, OLD.member_id);
IF TG_OP = 'UPDATE' THEN
old_value_text := COALESCE(
NULLIF(OLD.value->>'_union_value', ''),
NULLIF(OLD.value->>'value', ''),
''
);
new_value_text := COALESCE(
NULLIF(NEW.value->>'_union_value', ''),
NULLIF(NEW.value->>'value', ''),
''
);
IF (old_value_text IS NOT DISTINCT FROM new_value_text) AND
(OLD.member_id IS NOT DISTINCT FROM NEW.member_id) AND
(OLD.custom_field_id IS NOT DISTINCT FROM NEW.custom_field_id) THEN
RETURN COALESCE(NEW, OLD);
END IF;
END IF;
SELECT
first_name,
last_name,
email,
join_date,
exit_date,
notes,
city,
street,
house_number,
postal_code
INTO
member_first_name,
member_last_name,
member_email,
member_join_date,
member_exit_date,
member_notes,
member_city,
member_street,
member_house_number,
member_postal_code
FROM members
WHERE id = member_id_val;
SELECT string_agg(
CASE
WHEN value ? '_union_value' THEN value->>'_union_value'
WHEN value ? 'value' THEN value->>'value'
ELSE ''
END,
' '
)
INTO custom_values_text
FROM custom_field_values
WHERE member_id = member_id_val AND value IS NOT NULL;
SELECT string_agg(g.name, ' ')
INTO groups_text
FROM member_groups mg
JOIN groups g ON g.id = mg.group_id
WHERE mg.member_id = member_id_val;
UPDATE members
SET search_vector =
setweight(to_tsvector('simple', coalesce(member_first_name, '')), 'A') ||
setweight(to_tsvector('simple', coalesce(member_last_name, '')), 'A') ||
setweight(to_tsvector('simple', coalesce(member_email, '')), 'B') ||
setweight(to_tsvector('simple', coalesce(member_join_date::text, '')), 'D') ||
setweight(to_tsvector('simple', coalesce(member_exit_date::text, '')), 'D') ||
setweight(to_tsvector('simple', coalesce(member_notes, '')), 'B') ||
setweight(to_tsvector('simple', coalesce(member_city, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(member_street, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(member_house_number::text, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(member_postal_code::text, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(custom_values_text, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(groups_text, '')), 'B')
WHERE id = member_id_val;
RETURN COALESCE(NEW, OLD);
END
$$ LANGUAGE plpgsql;
""")
execute("""
CREATE OR REPLACE FUNCTION update_member_search_vector_from_member_groups() RETURNS trigger AS $$
DECLARE
member_id_val uuid;
member_first_name text;
member_last_name text;
member_email text;
member_join_date date;
member_exit_date date;
member_notes text;
member_city text;
member_street text;
member_house_number text;
member_postal_code text;
custom_values_text text;
groups_text text;
BEGIN
FOR member_id_val IN
SELECT COALESCE(NEW.member_id, OLD.member_id)
UNION ALL
SELECT OLD.member_id
WHERE TG_OP = 'UPDATE' AND OLD.member_id IS DISTINCT FROM NEW.member_id
LOOP
SELECT
first_name,
last_name,
email,
join_date,
exit_date,
notes,
city,
street,
house_number,
postal_code
INTO
member_first_name,
member_last_name,
member_email,
member_join_date,
member_exit_date,
member_notes,
member_city,
member_street,
member_house_number,
member_postal_code
FROM members
WHERE id = member_id_val;
SELECT string_agg(
CASE
WHEN value ? '_union_value' THEN value->>'_union_value'
WHEN value ? 'value' THEN value->>'value'
ELSE ''
END,
' '
)
INTO custom_values_text
FROM custom_field_values
WHERE member_id = member_id_val AND value IS NOT NULL;
SELECT string_agg(g.name, ' ')
INTO groups_text
FROM member_groups mg
JOIN groups g ON g.id = mg.group_id
WHERE mg.member_id = member_id_val;
UPDATE members
SET search_vector =
setweight(to_tsvector('simple', coalesce(member_first_name, '')), 'A') ||
setweight(to_tsvector('simple', coalesce(member_last_name, '')), 'A') ||
setweight(to_tsvector('simple', coalesce(member_email, '')), 'B') ||
setweight(to_tsvector('simple', coalesce(member_join_date::text, '')), 'D') ||
setweight(to_tsvector('simple', coalesce(member_exit_date::text, '')), 'D') ||
setweight(to_tsvector('simple', coalesce(member_notes, '')), 'B') ||
setweight(to_tsvector('simple', coalesce(member_city, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(member_street, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(member_house_number::text, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(member_postal_code::text, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(custom_values_text, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(groups_text, '')), 'B')
WHERE id = member_id_val;
END LOOP;
RETURN COALESCE(NEW, OLD);
END
$$ LANGUAGE plpgsql;
""")
# Backfill without country
execute("""
UPDATE members m
SET search_vector =
setweight(to_tsvector('simple', coalesce(m.first_name, '')), 'A') ||
setweight(to_tsvector('simple', coalesce(m.last_name, '')), 'A') ||
setweight(to_tsvector('simple', coalesce(m.email, '')), 'B') ||
setweight(to_tsvector('simple', coalesce(m.join_date::text, '')), 'D') ||
setweight(to_tsvector('simple', coalesce(m.exit_date::text, '')), 'D') ||
setweight(to_tsvector('simple', coalesce(m.notes, '')), 'B') ||
setweight(to_tsvector('simple', coalesce(m.city, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(m.street, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(m.house_number::text, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(m.postal_code::text, '')), 'C') ||
setweight(to_tsvector('simple', coalesce(
(SELECT string_agg(
CASE
WHEN value ? '_union_value' THEN value->>'_union_value'
WHEN value ? 'value' THEN value->>'value'
ELSE ''
END,
' '
)
FROM custom_field_values
WHERE member_id = m.id AND value IS NOT NULL),
''
)), 'C') ||
setweight(to_tsvector('simple', coalesce(
(SELECT string_agg(g.name, ' ')
FROM member_groups mg
JOIN groups g ON g.id = mg.group_id
WHERE mg.member_id = m.id),
''
)), 'B')
""")
alter table(:members) do
remove :country
end
end
end

View file

@ -1,2 +1,2 @@
Vorname;Nachname;E-Mail;Straße;PLZ;Stadt
Max;Mustermann;max.mustermann@example.com;Hauptstraße;10115;Berlin
Vorname;Nachname;E-Mail;Land;Stadt;Straße;Hausnummer;PLZ;Beitrittsdatum;Austrittsdatum;Notizen;Beitragsbeginn
Max;Mustermann;max.mustermann@example.com;Deutschland;Berlin;Hauptstraße;12;10115;2020-01-15;;;

1 Vorname Nachname E-Mail Land Stadt Straße Hausnummer PLZ Beitrittsdatum Austrittsdatum Notizen Beitragsbeginn
2 Max Mustermann max.mustermann@example.com Deutschland Berlin Hauptstraße 12 10115 2020-01-15

View file

@ -1,2 +1,2 @@
first_name;last_name;email;street;postal_code;city
John;Doe;john.doe@example.com;Main Street;12345;Berlin
first_name;last_name;email;country;city;street;house_number;postal_code;join_date;exit_date;notes;membership_fee_start_date
John;Doe;john.doe@example.com;Germany;Berlin;Main Street;1a;12345;2020-01-15;;;

1 first_name last_name email country city street house_number postal_code join_date exit_date notes membership_fee_start_date
2 John Doe john.doe@example.com Germany Berlin Main Street 1a 12345 2020-01-15