mirror of
https://framagit.org/framasoft/mobilizon.git
synced 2024-12-21 23:44:30 +00:00
WIP
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
b3e7f23604
commit
a544e6aee7
7 changed files with 193 additions and 12 deletions
|
@ -248,6 +248,11 @@ config :mobilizon, :maps,
|
||||||
type: :openstreetmap
|
type: :openstreetmap
|
||||||
]
|
]
|
||||||
|
|
||||||
|
config :mobilizon, Mobilizon.Service.Search, extra_provider: Mobilizon.Service.Search.SearchIndex
|
||||||
|
|
||||||
|
config :mobilizon, Mobilizon.Service.Search.SearchIndex,
|
||||||
|
endpoint: "https://search.joinmobilizon.org"
|
||||||
|
|
||||||
config :mobilizon, :http_security,
|
config :mobilizon, :http_security,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
sts: false,
|
sts: false,
|
||||||
|
|
|
@ -74,7 +74,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
|
||||||
medias: medias,
|
medias: medias,
|
||||||
begins_on: object["startTime"],
|
begins_on: object["startTime"],
|
||||||
ends_on: object["endTime"],
|
ends_on: object["endTime"],
|
||||||
category: get_category(object["category"]),
|
category: Categories.get_category(object["category"]),
|
||||||
visibility: visibility,
|
visibility: visibility,
|
||||||
join_options: Map.get(object, "joinMode", "free"),
|
join_options: Map.get(object, "joinMode", "free"),
|
||||||
local: is_local?(object["id"]),
|
local: is_local?(object["id"]),
|
||||||
|
@ -331,15 +331,4 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
|
||||||
_participant_count
|
_participant_count
|
||||||
),
|
),
|
||||||
do: nil
|
do: nil
|
||||||
|
|
||||||
@spec get_category(String.t() | nil) :: String.t()
|
|
||||||
defp get_category(nil), do: "MEETING"
|
|
||||||
|
|
||||||
defp get_category(category) when is_binary(category) do
|
|
||||||
if category in Enum.map(Categories.list(), &String.upcase(to_string(&1.id))) do
|
|
||||||
category
|
|
||||||
else
|
|
||||||
get_category(nil)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -81,6 +81,8 @@ defmodule Mobilizon.GraphQL.Schema.SearchType do
|
||||||
description: "Radius around the location to search in"
|
description: "Radius around the location to search in"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
arg(:external, :boolean, default_value: false, description: "Also return external results")
|
||||||
|
|
||||||
arg(:page, :integer, default_value: 1, description: "Result page")
|
arg(:page, :integer, default_value: 1, description: "Result page")
|
||||||
arg(:limit, :integer, default_value: 10, description: "Results limit per page")
|
arg(:limit, :integer, default_value: 10, description: "Results limit per page")
|
||||||
|
|
||||||
|
@ -100,6 +102,7 @@ defmodule Mobilizon.GraphQL.Schema.SearchType do
|
||||||
description: "Radius around the location to search in"
|
description: "Radius around the location to search in"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
arg(:external, :boolean, default_value: false, description: "Also return external results")
|
||||||
arg(:page, :integer, default_value: 1, description: "Result page")
|
arg(:page, :integer, default_value: 1, description: "Result page")
|
||||||
arg(:limit, :integer, default_value: 10, description: "Results limit per page")
|
arg(:limit, :integer, default_value: 10, description: "Results limit per page")
|
||||||
arg(:begins_on, :datetime, description: "Filter events by their start date")
|
arg(:begins_on, :datetime, description: "Filter events by their start date")
|
||||||
|
|
|
@ -9,6 +9,20 @@ defmodule Mobilizon.Events.Categories do
|
||||||
build_in_categories() ++ extra_categories()
|
build_in_categories() ++ extra_categories()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Get a category for an input string
|
||||||
|
"""
|
||||||
|
@spec get_category(String.t() | nil) :: String.t()
|
||||||
|
def get_category(nil), do: "MEETING"
|
||||||
|
|
||||||
|
def get_category(category) when is_binary(category) do
|
||||||
|
if category in Enum.map(list(), &String.upcase(to_string(&1.id))) do
|
||||||
|
category
|
||||||
|
else
|
||||||
|
get_category(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp build_in_categories do
|
defp build_in_categories do
|
||||||
[
|
[
|
||||||
%{
|
%{
|
||||||
|
|
19
lib/service/search/external.ex
Normal file
19
lib/service/search/external.ex
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
defmodule Mobilizon.Service.Search.External do
|
||||||
|
@moduledoc """
|
||||||
|
Search providers manager
|
||||||
|
"""
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Queries the external search provider
|
||||||
|
"""
|
||||||
|
def search(options) do
|
||||||
|
provider().search(options)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec provider :: module()
|
||||||
|
defp provider do
|
||||||
|
:mobilizon
|
||||||
|
|> Application.get_env(Mobilizon.Service.Search, [])
|
||||||
|
|> Keyword.get(:extra_provider, Mobilizon.Service.Search.SearchIndex)
|
||||||
|
end
|
||||||
|
end
|
10
lib/service/search/provider.ex
Normal file
10
lib/service/search/provider.ex
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
defmodule Mobilizon.Service.Search.Provider do
|
||||||
|
@moduledoc """
|
||||||
|
Behaviour for a search provider
|
||||||
|
"""
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns a paginated list of events
|
||||||
|
"""
|
||||||
|
@callback search(options :: Keyword.t()) :: Mobilizon.Storage.Page.t(Mobilizon.Events.Event.t())
|
||||||
|
end
|
141
lib/service/search/search_index.ex
Normal file
141
lib/service/search/search_index.ex
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
defmodule Mobilizon.Service.Search.SearchIndex do
|
||||||
|
@moduledoc """
|
||||||
|
Search provider for https://search.joinmobilizon.org
|
||||||
|
"""
|
||||||
|
|
||||||
|
alias Mobilizon.Addresses.Address
|
||||||
|
alias Mobilizon.Actors.Actor
|
||||||
|
alias Mobilizon.Events.{Categories, Event, EventOptions}
|
||||||
|
alias Mobilizon.Service.HTTP.GeospatialClient
|
||||||
|
alias Mobilizon.Service.Search.Provider
|
||||||
|
alias Mobilizon.Storage.Page
|
||||||
|
import Plug.Conn.Query, only: [encode: 1]
|
||||||
|
|
||||||
|
@behaviour Provider
|
||||||
|
|
||||||
|
@default_endpoint "https://search.joinmobilizon.org"
|
||||||
|
|
||||||
|
@events_api_endpoint "/api/v1/search/events"
|
||||||
|
@groups_api_endpoint "/api/v1/search/groups"
|
||||||
|
|
||||||
|
@default_options [
|
||||||
|
start: 0,
|
||||||
|
count: 10,
|
||||||
|
distance: "10_km"
|
||||||
|
]
|
||||||
|
|
||||||
|
# "?search=test&startDateMin=2022-04-21T16:08:32.675Z&boostLanguages[]=fr&boostLanguages[]=en&distance=10_km&sort=-match&start=0&count=5"
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns a paginated list of events
|
||||||
|
"""
|
||||||
|
@impl Provider
|
||||||
|
def search(options) do
|
||||||
|
case fetch(options) do
|
||||||
|
{:ok, %Tesla.Env{body: body, status: 200}} ->
|
||||||
|
{:ok, transform_results(body, Keyword.get(options, :type, :events))}
|
||||||
|
|
||||||
|
err ->
|
||||||
|
require Logger
|
||||||
|
Logger.error(inspect(err))
|
||||||
|
{:error, :http_error}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fetch(options) do
|
||||||
|
@default_options
|
||||||
|
|> Keyword.merge(options)
|
||||||
|
|> build_url()
|
||||||
|
|> GeospatialClient.get()
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec transform_results(%{String.t() => list(map()) | non_neg_integer()}, atom()) ::
|
||||||
|
Page.t(Event.t() | Actor.t())
|
||||||
|
defp transform_results(%{"data" => data, "total" => total}, type) do
|
||||||
|
%Page{
|
||||||
|
total: total,
|
||||||
|
elements:
|
||||||
|
data
|
||||||
|
|> Enum.sort(&(&1["score"] >= &2["score"]))
|
||||||
|
|> Enum.map(fn element -> transform_result(element, type) end)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec transform_result(map(), :events | :groups) :: Event.t() | Actor.t()
|
||||||
|
defp transform_result(event, :events) do
|
||||||
|
%Event{
|
||||||
|
category: Categories.get_category(event["category"]),
|
||||||
|
organizer_actor: transform_actor(event["creator"]),
|
||||||
|
ends_on: event["endTime"],
|
||||||
|
attributed_to: transform_actor(event["group"], :Group),
|
||||||
|
id: event["id"],
|
||||||
|
join_options: event["joinMode"],
|
||||||
|
options: %EventOptions{
|
||||||
|
is_online: event["isOnline"],
|
||||||
|
maximum_attendee_capacity: event["maximumAttendeeCapacity"]
|
||||||
|
},
|
||||||
|
physical_address: transform_address(event["location"]),
|
||||||
|
title: event["name"],
|
||||||
|
publish_at: event["published"],
|
||||||
|
begins_on: event["startTime"],
|
||||||
|
status: String.downcase(event["status"]),
|
||||||
|
tags: event["tags"],
|
||||||
|
uuid: event["uuid"],
|
||||||
|
url: event["url"]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp transform_result(group, :groups) do
|
||||||
|
group = transform_actor(group)
|
||||||
|
|
||||||
|
%Actor{
|
||||||
|
group
|
||||||
|
| type: :Group,
|
||||||
|
physical_address: transform_address(group["location"])
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp transform_actor(actor, type \\ :Person) do
|
||||||
|
%Actor{
|
||||||
|
type: type,
|
||||||
|
id: actor["id"],
|
||||||
|
url: actor["url"],
|
||||||
|
summary: actor["description"],
|
||||||
|
preferred_username: actor["name"],
|
||||||
|
name: actor["displayName"],
|
||||||
|
domain: actor["host"]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp transform_address(address) when is_map(address) do
|
||||||
|
%Address{
|
||||||
|
description: address["name"],
|
||||||
|
geom: %Geo.Point{
|
||||||
|
coordinates: {address["location"]["lon"], address["location"]["lat"]},
|
||||||
|
srid: 4326
|
||||||
|
},
|
||||||
|
country: address["address"]["addressCountry"],
|
||||||
|
locality: address["address"]["addressLocality"],
|
||||||
|
region: address["address"]["addressRegion"],
|
||||||
|
postal_code: address["address"]["postalCode"],
|
||||||
|
street: address["address"]["streetAddress"]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp transform_address(_), do: nil
|
||||||
|
|
||||||
|
@spec endpoint :: String.t()
|
||||||
|
defp endpoint do
|
||||||
|
:mobilizon
|
||||||
|
|> Application.get_env(__MODULE__, [])
|
||||||
|
|> Keyword.get(:endpoint, @default_endpoint)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp build_url(options) do
|
||||||
|
{type, options} = Keyword.pop(options, :type, :events)
|
||||||
|
"#{endpoint()}#{api_endpoint(type)}?#{encode(options)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
defp api_endpoint(:events), do: @events_api_endpoint
|
||||||
|
defp api_endpoint(:groups), do: @groups_api_endpoint
|
||||||
|
end
|
Loading…
Reference in a new issue