init
This commit is contained in:
@ -156,17 +156,17 @@ $$;
|
||||
drop trigger if exists set_profiles_updated_at on public.profiles;
|
||||
create trigger set_profiles_updated_at
|
||||
before update on public.profiles
|
||||
for each row execute function public.set_updated_at();
|
||||
for each row execute procedure public.set_updated_at();
|
||||
|
||||
drop trigger if exists set_dossiers_updated_at on public.dossiers;
|
||||
create trigger set_dossiers_updated_at
|
||||
before update on public.dossiers
|
||||
for each row execute function public.set_updated_at();
|
||||
for each row execute procedure public.set_updated_at();
|
||||
|
||||
drop trigger if exists set_investisseurs_updated_at on public.investisseurs;
|
||||
create trigger set_investisseurs_updated_at
|
||||
before update on public.investisseurs
|
||||
for each row execute function public.set_updated_at();
|
||||
for each row execute procedure public.set_updated_at();
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- RLS
|
||||
@ -218,7 +218,7 @@ begin
|
||||
end;
|
||||
$$;
|
||||
|
||||
drop trigger if exists on_auth_user_created on auth.users;
|
||||
create trigger on_auth_user_created
|
||||
drop trigger if exists mdb_on_auth_user_created on auth.users;
|
||||
create trigger mdb_on_auth_user_created
|
||||
after insert on auth.users
|
||||
for each row execute function public.handle_new_user();
|
||||
for each row execute procedure public.handle_new_user();
|
||||
|
||||
154
supabase/migrations/20260429200000_deals_sources_scout.sql
Normal file
154
supabase/migrations/20260429200000_deals_sources_scout.sql
Normal file
@ -0,0 +1,154 @@
|
||||
-- Flux opportunités aspirées + agent Scout (batch JSON côté Postgres)
|
||||
|
||||
create table if not exists public.deals_sources (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
user_id uuid not null references auth.users (id) on delete cascade,
|
||||
title text not null,
|
||||
description text,
|
||||
source_url text,
|
||||
source_name text,
|
||||
price_eur numeric(14, 2),
|
||||
surface_m2 numeric(12, 2) not null,
|
||||
price_per_m2_eur numeric(14, 2) not null,
|
||||
dvf_avg_m2_simulated numeric(14, 2) not null default 3500,
|
||||
distress_keywords text[] not null default '{}',
|
||||
opportunity_score numeric(6, 2) not null,
|
||||
grade text not null check (grade in ('A', 'B', 'C')),
|
||||
raw_payload jsonb,
|
||||
created_at timestamptz not null default now()
|
||||
);
|
||||
|
||||
create index if not exists deals_sources_user_score_idx
|
||||
on public.deals_sources (user_id, opportunity_score desc);
|
||||
create index if not exists deals_sources_user_created_idx
|
||||
on public.deals_sources (user_id, created_at desc);
|
||||
|
||||
alter table public.profiles add column if not exists expo_push_token text;
|
||||
|
||||
-- Activer Realtime sur cette table : Dashboard Supabase → Realtime → ajouter public.deals_sources
|
||||
|
||||
alter table public.deals_sources enable row level security;
|
||||
|
||||
create policy deals_sources_select_own on public.deals_sources
|
||||
for select to authenticated
|
||||
using (auth.uid() = user_id);
|
||||
|
||||
-- Pas d’INSERT direct : uniquement via la fonction SECURITY DEFINER ci-dessous.
|
||||
|
||||
create or replace function public.scout_process_batch(p_listings jsonb)
|
||||
returns jsonb
|
||||
language plpgsql
|
||||
security definer
|
||||
set search_path = public
|
||||
as $$
|
||||
declare
|
||||
uid uuid := auth.uid();
|
||||
el jsonb;
|
||||
txt text;
|
||||
price numeric;
|
||||
surf numeric;
|
||||
pm2 numeric;
|
||||
avg_m2 constant numeric := 3500;
|
||||
keywords text[] := array['succession', 'urgent', 'travaux important'];
|
||||
k text;
|
||||
matched text[];
|
||||
ok_kw boolean;
|
||||
ok_pm2 boolean;
|
||||
score numeric;
|
||||
grade text;
|
||||
n int := 0;
|
||||
na int := 0;
|
||||
begin
|
||||
if uid is null then
|
||||
raise exception 'scout_process_batch: non authentifié';
|
||||
end if;
|
||||
|
||||
for el in select * from jsonb_array_elements(coalesce(p_listings, '[]'::jsonb))
|
||||
loop
|
||||
txt := lower(
|
||||
coalesce(el ->> 'description', '') || ' ' || coalesce(el ->> 'title', '')
|
||||
);
|
||||
price := nullif(trim(el ->> 'price_eur'), '')::numeric;
|
||||
surf := nullif(trim(el ->> 'surface_m2'), '')::numeric;
|
||||
|
||||
if price is null or surf is null or surf <= 0 then
|
||||
continue;
|
||||
end if;
|
||||
|
||||
pm2 := price / surf;
|
||||
matched := array[]::text[];
|
||||
ok_kw := false;
|
||||
|
||||
foreach k in array keywords
|
||||
loop
|
||||
if strpos(txt, k) > 0 then
|
||||
ok_kw := true;
|
||||
matched := array_append(matched, k);
|
||||
end if;
|
||||
end loop;
|
||||
|
||||
ok_pm2 := pm2 < avg_m2;
|
||||
|
||||
if not (ok_kw and ok_pm2) then
|
||||
continue;
|
||||
end if;
|
||||
|
||||
score := 40::numeric
|
||||
+ greatest(0::numeric, (avg_m2 - pm2) / nullif(avg_m2, 0) * 50)
|
||||
+ coalesce(array_length(matched, 1), 0) * 10;
|
||||
|
||||
grade := case
|
||||
when score >= 80 then 'A'
|
||||
when score >= 55 then 'B'
|
||||
else 'C'
|
||||
end;
|
||||
|
||||
insert into public.deals_sources (
|
||||
user_id,
|
||||
title,
|
||||
description,
|
||||
source_url,
|
||||
source_name,
|
||||
price_eur,
|
||||
surface_m2,
|
||||
price_per_m2_eur,
|
||||
dvf_avg_m2_simulated,
|
||||
distress_keywords,
|
||||
opportunity_score,
|
||||
grade,
|
||||
raw_payload
|
||||
)
|
||||
values (
|
||||
uid,
|
||||
coalesce(nullif(trim(el ->> 'title'), ''), 'Sans titre'),
|
||||
el ->> 'description',
|
||||
nullif(trim(el ->> 'url'), ''),
|
||||
nullif(trim(el ->> 'source'), ''),
|
||||
price,
|
||||
surf,
|
||||
pm2,
|
||||
avg_m2,
|
||||
matched,
|
||||
score,
|
||||
grade,
|
||||
el
|
||||
);
|
||||
|
||||
n := n + 1;
|
||||
if grade = 'A' then
|
||||
na := na + 1;
|
||||
end if;
|
||||
end loop;
|
||||
|
||||
return jsonb_build_object(
|
||||
'inserted_count', n,
|
||||
'grade_a_count', na,
|
||||
'simulated_dvf_avg_m2', avg_m2
|
||||
);
|
||||
end;
|
||||
$$;
|
||||
|
||||
grant execute on function public.scout_process_batch(jsonb) to authenticated;
|
||||
|
||||
comment on function public.scout_process_batch(jsonb) is
|
||||
'Filtre un lot JSON d''annonces (mots-clés détresse + prix/m² < moyenne simulée) et insère dans deals_sources.';
|
||||
Reference in New Issue
Block a user