Skip to content

Commit

Permalink
Espace réutilisateur : formulaire pour ajouter des données améliorées (
Browse files Browse the repository at this point in the history
  • Loading branch information
AntoineAugusti authored Feb 5, 2025
1 parent 9919d1b commit e016e65
Show file tree
Hide file tree
Showing 14 changed files with 392 additions and 17 deletions.
4 changes: 4 additions & 0 deletions apps/transport/client/stylesheets/reuser_space.scss
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,7 @@ form.search-followed-datasets {
.align-right {
text-align: right;
}

form.full-width {
max-width: 100%;
}
30 changes: 30 additions & 0 deletions apps/transport/lib/db/reuser_improved_data.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
defmodule DB.ReuserImprovedData do
@moduledoc """
Represents improved static data shared by reusers.
"""
use Ecto.Schema
use TypedEctoSchema
import Ecto.Changeset

typed_schema "reuser_improved_data" do
belongs_to(:dataset, DB.Dataset)
belongs_to(:resource, DB.Resource)
belongs_to(:contact, DB.Contact)
belongs_to(:organization, DB.Organization, type: :string)
field(:download_url, :string)
timestamps(type: :utc_datetime_usec)
end

def changeset(%__MODULE__{} = struct, attrs \\ %{}) do
fields = [:dataset_id, :resource_id, :contact_id, :organization_id, :download_url]

struct
|> cast(attrs, fields)
|> validate_required(fields)
|> assoc_constraint(:dataset)
|> assoc_constraint(:resource)
|> assoc_constraint(:contact)
|> assoc_constraint(:organization)
|> unique_constraint([:resource_id, :organization_id])
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ defmodule TransportWeb.ReuserSpaceController do
use TransportWeb, :controller
import Ecto.Query

plug(:find_dataset_or_redirect when action in [:datasets_edit, :unfavorite])
plug(:find_dataset_or_redirect when action in [:datasets_edit, :unfavorite, :add_improved_data])

def espace_reutilisateur(%Plug.Conn{assigns: %{current_user: %{"id" => datagouv_user_id}}} = conn, _) do
contact = DB.Repo.get_by!(DB.Contact, datagouv_user_id: datagouv_user_id)
Expand All @@ -14,7 +14,54 @@ defmodule TransportWeb.ReuserSpaceController do
|> render("index.html")
end

def datasets_edit(%Plug.Conn{} = conn, _), do: render(conn, "datasets_edit.html")
def datasets_edit(
%Plug.Conn{assigns: %{dataset: %DB.Dataset{} = dataset, contact: %DB.Contact{} = contact}} = conn,
_
) do
contact = DB.Repo.preload(contact, :organizations)
eligible_organizations = data_sharing_eligible_orgs(contact)

conn
|> assign(:contact, contact)
|> assign(:dataset, DB.Repo.preload(dataset, :resources))
|> assign(:eligible_to_data_sharing_pilot, data_sharing_pilot?(dataset, contact))
|> assign(:eligible_organizations, eligible_organizations)
|> assign(:existing_improved_data, existing_improved_data(dataset, eligible_organizations))
|> render("datasets_edit.html")
end

defp existing_improved_data(%DB.Dataset{id: dataset_id}, [%DB.Organization{id: organization_id}]) do
DB.ReuserImprovedData
|> where([r], r.dataset_id == ^dataset_id and r.organization_id == ^organization_id)
|> DB.Repo.one()
end

defp existing_improved_data(%DB.Dataset{}, _orgs), do: nil

def add_improved_data(
%Plug.Conn{
assigns: %{dataset: %DB.Dataset{} = dataset, contact: %DB.Contact{} = contact},
params: %{
"resource_id" => resource_id,
"organization_id" => organization_id,
"download_url" => download_url
}
} = conn,
_
) do
DB.ReuserImprovedData.changeset(%DB.ReuserImprovedData{}, %{
dataset_id: dataset.id,
resource_id: resource_id,
contact_id: contact.id,
organization_id: organization_id,
download_url: download_url
})
|> DB.Repo.insert!()

conn
|> put_flash(:info, dgettext("reuser-space", "Your improved data has been saved."))
|> redirect(to: reuser_space_path(conn, :datasets_edit, dataset.id))
end

def unfavorite(%Plug.Conn{assigns: %{dataset: %DB.Dataset{} = dataset, contact: %DB.Contact{} = contact}} = conn, _) do
DB.DatasetFollower.unfollow!(contact, dataset)
Expand Down Expand Up @@ -59,4 +106,29 @@ defmodule TransportWeb.ReuserSpaceController do
|> halt()
end
end

@doc """
Is the following dataset eligible for the data sharing pilot for this contact, member
of various organizations?
"""
@spec data_sharing_pilot?(DB.Dataset.t(), DB.Contact.t()) :: boolean()
def data_sharing_pilot?(%DB.Dataset{} = dataset, %DB.Contact{} = contact) do
eligible_dataset_type = dataset.type == "public-transit"
has_dataset_tag = DB.Dataset.has_custom_tag?(dataset, config_value(:dataset_custom_tag))
member_eligible_org = data_sharing_eligible_orgs(contact) |> Enum.count() == 1

Enum.all?([eligible_dataset_type, has_dataset_tag, member_eligible_org])
end

def data_sharing_eligible_orgs(%DB.Contact{organizations: organizations}) do
data_sharing_eligible_orgs(organizations)
end

def data_sharing_eligible_orgs(organizations) when is_list(organizations) do
Enum.filter(organizations, &(&1.id in config_value(:eligible_datagouv_organization_ids)))
end

defp config_value(key) do
Application.fetch_env!(:transport, :"data_sharing_pilot_#{key}")
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ defmodule TransportWeb.CustomTagsLive do
%{
name: "experimental",
doc: "Ajoute sur la page du JDD une bannière indiquant que le jeu est expérimental"
},
%{
name: Application.fetch_env!(:transport, :data_sharing_pilot_dataset_custom_tag),
doc: "Indique que ce jeu de données est éligible à l'expérimentation du repartage de données améliorées"
}
]
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ defmodule TransportWeb.Plugs.CustomSecureBrowserHeaders do

def call(conn, _opts) do
nonce = generate_nonce()
csp_headers = csp_headers(Application.fetch_env!(:transport, :app_env), nonce)
csp_headers = csp_headers(Mix.env(), Application.fetch_env!(:transport, :app_env), nonce)
headers = Map.merge(csp_headers, %{"x-frame-options" => "DENY"})

conn
Expand All @@ -23,16 +23,16 @@ defmodule TransportWeb.Plugs.CustomSecureBrowserHeaders do
Returns content-security-policy headers for an app environment.
iex> nonce = "foo"
iex> match?(%{"content-security-policy" => _csp_content}, csp_headers(:production, nonce))
iex> match?(%{"content-security-policy" => _csp_content}, csp_headers(:prod, :production, nonce))
true
iex> match?(%{"content-security-policy" => _csp_content}, csp_headers(:staging, nonce))
iex> match?(%{"content-security-policy" => _csp_content}, csp_headers(:prod, :staging, nonce))
true
iex> csp_headers(:staging, nonce) != csp_headers(:production, nonce)
iex> csp_headers(:prod, :staging, nonce) != csp_headers(:prod, :production, nonce)
true
iex> String.contains?("report-uri", csp_headers(:dev, nonce) |> Map.fetch!("content-security-policy"))
iex> String.contains?("report-uri", csp_headers(:dev, :dev, nonce) |> Map.fetch!("content-security-policy"))
false
"""
def csp_headers(app_env, nonce) do
def csp_headers(mix_env, app_env, nonce) do
# https://github.com/vega/vega-embed/issues/1214#issuecomment-1670812445
vega_hash_values =
"'sha256-9uoGUaZm3j6W7+Fh2wfvjI8P7zXcclRw5tVUu3qKZa0=' 'sha256-MmUum7+PiN7Rz79EUMm0OmUFWjCx6NZ97rdjoIbTnAg='"
Expand All @@ -51,7 +51,7 @@ defmodule TransportWeb.Plugs.CustomSecureBrowserHeaders do
"report-uri" => ""
}
|> Enum.map(fn {directive, value} ->
extra = " #{additional_content(directive, app_env)}" |> String.trim()
extra = " #{additional_content(directive, mix_env, app_env) |> String.trim()}"
{directive, value <> extra}
end)
|> Enum.reject(fn {_, v} -> v == "" end)
Expand All @@ -60,15 +60,19 @@ defmodule TransportWeb.Plugs.CustomSecureBrowserHeaders do
%{"content-security-policy" => policy}
end

defp additional_content("img-src", :staging) do
"https://demo-static.data.gouv.fr https://demo.data.gouv.fr"
defp additional_content("img-src", mix_env, app_env) do
if mix_env == :dev or app_env == :staging do
"https://demo-static.data.gouv.fr https://demo.data.gouv.fr"
else
""
end
end

defp additional_content("report-uri", app_env) when app_env in [:production, :staging] do
defp additional_content("report-uri", _mix_env, app_env) when app_env in [:production, :staging] do
Application.fetch_env!(:sentry, :csp_url)
end

defp additional_content(_directive, _app_env) do
defp additional_content(_directive, _mix_env, _app_env) do
""
end
end
1 change: 1 addition & 0 deletions apps/transport/lib/transport_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ defmodule TransportWeb.Router do
pipe_through([:reuser_space])
get("/", ReuserSpaceController, :espace_reutilisateur)
get("/datasets/:dataset_id", ReuserSpaceController, :datasets_edit)
post("/datasets/:dataset_id/add_improved_data", ReuserSpaceController, :add_improved_data)
post("/datasets/:dataset_id/unfavorite", ReuserSpaceController, :unfavorite)

live_session :reuser_space, session: %{"role" => :reuser}, root_layout: {TransportWeb.LayoutView, :app} do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,61 @@
<h3><%= dgettext("reuser-space", "Manage notifications") %></h3>
<%= live_render(@conn, TransportWeb.Live.DatasetNotificationsLive, session: %{"dataset_id" => @dataset.id}) %>
</div>
<div class="panel">
<div id="data-sharing" class="panel">
<h3><%= dgettext("reuser-space", "Improved data sharing") %></h3>
<p class="notification">
<%= dgettext("reuser-space", "This feature is coming soon!") %>
</p>
<%= if @eligible_to_data_sharing_pilot do %>
<% [organization] = @eligible_organizations %>
<%= if is_nil(@existing_improved_data) do %>
<div class="align-right">
<img src={organization.logo_thumbnail} title={organization.name} />
</div>
<%= form_for @conn, reuser_space_path(@conn, :add_improved_data, @dataset.id), [class: "full-width"], fn f -> %>
<h4>
<%= dgettext("reuser-space", "Step 1: choose the initial resource from the producer") %>
</h4>
<div class="ressources-list">
<%= for resource <- @dataset.resources |> Enum.filter(&DB.Resource.gtfs?/1) do %>
<div class="panel resource">
<h4>
<%= radio_button(f, :resource_id, resource.id, id: "resource-#{resource.id}", required: true) %>
<%= label f, resource.id, class: "label-inline", for: "resource-#{resource.id}" do %>
<%= resource.title %>
<% end %>
</h4>

<div class="resource-panel-bottom">
<div class="resource-actions">
<div class="resource-format" title={dgettext("page-dataset-details", "resource format")}>
<span class="label"><%= resource.format %></span>
</div>
</div>
</div>
</div>
<% end %>
</div>
<p class="small">
<%= dgettext("reuser-space", "Only GTFS files are eligible for now.") %>
</p>
<div class="form__group">
<h4>
<%= dgettext("reuser-space", "Step 2: add the URL to download your improved data") %>
</h4>
<%= label(f, :download_url, dgettext("reuser-space", "Your improved data URL")) %>
<%= text_input(f, :download_url, type: "url", required: true) %>
</div>
<%= hidden_input(f, :organization_id, value: organization.id) %>
<%= submit(dgettext("reuser-space", "Share improved data"), class: "button") %>
<% end %>
<% else %>
<p class="notification">
<%= dgettext("reuser-space", "You already shared improved data for this dataset, thanks!") %>
</p>
<% end %>
<% else %>
<p class="notification">
<%= dgettext("reuser-space", "This feature is coming soon!") %>
</p>
<% end %>
</div>
<div class="panel">
<h3><%= dgettext("reuser-space", "Discussions") %></h3>
Expand Down
28 changes: 28 additions & 0 deletions apps/transport/priv/gettext/en/LC_MESSAGES/reuser-space.po
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,31 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "Warning, you are going to remove \"%{dataset_title}\" from your favorites. You will lose any settings or actions you previously performed on this dataset."
msgstr ""

#, elixir-autogen, elixir-format
msgid "Only GTFS files are eligible for now."
msgstr ""

#, elixir-autogen, elixir-format
msgid "Share improved data"
msgstr ""

#, elixir-autogen, elixir-format
msgid "Your improved data URL"
msgstr ""

#, elixir-autogen, elixir-format
msgid "Step 1: choose the initial resource from the producer"
msgstr ""

#, elixir-autogen, elixir-format
msgid "Step 2: add the URL to download your improved data"
msgstr ""

#, elixir-autogen, elixir-format
msgid "You already shared improved data for this dataset, thanks!"
msgstr ""

#, elixir-autogen, elixir-format
msgid "Your improved data has been saved."
msgstr ""
28 changes: 28 additions & 0 deletions apps/transport/priv/gettext/fr/LC_MESSAGES/reuser-space.po
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,31 @@ msgstr "Cette fonctionnalité arrive bientôt !"
#, elixir-autogen, elixir-format
msgid "Warning, you are going to remove \"%{dataset_title}\" from your favorites. You will lose any settings or actions you previously performed on this dataset."
msgstr "Attention, vous allez supprimer le jeu de données \"%{dataset_title}\" de vos favoris. Vous allez perdre tous les paramétrages ou actions que vous avez réalisés précédemment sur ce jeu de données."

#, elixir-autogen, elixir-format
msgid "Only GTFS files are eligible for now."
msgstr "Seuls les fichiers GTFS sont éligibles pour le moment."

#, elixir-autogen, elixir-format
msgid "Share improved data"
msgstr "Repartager vos données améliorées"

#, elixir-autogen, elixir-format
msgid "Your improved data URL"
msgstr "Lien vers vos données améliorées"

#, elixir-autogen, elixir-format
msgid "Step 1: choose the initial resource from the producer"
msgstr "Étape 1 : sélectionnez la ressource initiale du producteur"

#, elixir-autogen, elixir-format
msgid "Step 2: add the URL to download your improved data"
msgstr "Étape 2 : renseignez l'URL de téléchargement de votre fichier amélioré"

#, elixir-autogen, elixir-format
msgid "You already shared improved data for this dataset, thanks!"
msgstr "Vous avez déjà partagé des données améliorées pour ce jeu de données, merci !"

#, elixir-autogen, elixir-format
msgid "Your improved data has been saved."
msgstr "Vos données améliorées ont bien été sauvegardées."
28 changes: 28 additions & 0 deletions apps/transport/priv/gettext/reuser-space.pot
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,31 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "Warning, you are going to remove \"%{dataset_title}\" from your favorites. You will lose any settings or actions you previously performed on this dataset."
msgstr ""

#, elixir-autogen, elixir-format
msgid "Only GTFS files are eligible for now."
msgstr ""

#, elixir-autogen, elixir-format
msgid "Share improved data"
msgstr ""

#, elixir-autogen, elixir-format
msgid "Your improved data URL"
msgstr ""

#, elixir-autogen, elixir-format
msgid "Step 1: choose the initial resource from the producer"
msgstr ""

#, elixir-autogen, elixir-format
msgid "Step 2: add the URL to download your improved data"
msgstr ""

#, elixir-autogen, elixir-format
msgid "You already shared improved data for this dataset, thanks!"
msgstr ""

#, elixir-autogen, elixir-format
msgid "Your improved data has been saved."
msgstr ""
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
defmodule DB.Repo.Migrations.ReuserImprovedData do
use Ecto.Migration

def change do
create table(:reuser_improved_data) do
add(:dataset_id, references(:dataset, on_delete: :delete_all), null: false)
add(:resource_id, references(:resource, on_delete: :delete_all), null: false)
add(:contact_id, references(:contact, on_delete: :delete_all), null: false)
add(:organization_id, references(:organization, type: :string, on_delete: :delete_all), null: false)
add(:download_url, :string, null: false)
timestamps(type: :utc_datetime_usec)
end

create(index(:reuser_improved_data, [:dataset_id]))
create(index(:reuser_improved_data, [:organization_id]))
create(unique_index(:reuser_improved_data, [:resource_id, :organization_id]))
end
end
Loading

0 comments on commit e016e65

Please sign in to comment.