Skip to content

Commit 92b51db

Browse files
authored
GTFS-diff : Améliorations présentations des résultats - structure des fichiers (#4487)
1 parent 71be3e7 commit 92b51db

File tree

17 files changed

+1288
-141
lines changed

17 files changed

+1288
-141
lines changed

apps/transport/client/stylesheets/components/_gtfs_diff.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,10 @@
163163
overflow: scroll;
164164
}
165165
}
166+
167+
.symbol {
168+
margin-inline: 6px;
169+
}
166170
}
167171

168172
@media (max-width: 749px) {

apps/transport/lib/transport_web/live/gtfs_diff_explain.ex

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,73 @@ defmodule TransportWeb.GTFSDiffExplain do
1414
|> Map.update("identifier", %{}, &try_jason_decode(&1))
1515

1616
[]
17-
|> explanation_add_file(diff)
18-
|> explanation_delete_file(diff)
1917
|> explanation_update_stop_name(diff)
2018
|> explanation_stop_wheelchair_access(diff)
2119
|> explanation_update_stop_longitude(diff)
2220
|> explanation_update_stop_latitude(diff)
2321
end)
2422
end
2523

24+
@doc """
25+
Analyses structural changes of diff, focusing on column changes and if the file has been created or deleted.
26+
27+
It ignores the row changes.
28+
"""
29+
def structural_changes(diffs) do
30+
Enum.reduce(diffs, %{}, fn action, acc ->
31+
if Map.get(action, "target") == "row" do
32+
acc
33+
else
34+
new_diff = structural_change(action)
35+
36+
Map.update(acc, Map.get(action, "file"), [new_diff], &merge_diffs(new_diff, &1))
37+
end
38+
end)
39+
end
40+
41+
defp structural_change(%{"target" => "file", "action" => action}) do
42+
case action do
43+
"add" -> :added_file
44+
"delete" -> :deleted_file
45+
end
46+
end
47+
48+
defp structural_change(%{"target" => "column", "action" => action, "identifier" => identifier}) do
49+
%{"column" => column} = Jason.decode!(identifier)
50+
51+
case action do
52+
"add" -> {:added_columns, [column]}
53+
"delete" -> {:deleted_columns, [column]}
54+
end
55+
end
56+
57+
defp merge_diffs({column_action, _} = new_diff, diffs) do
58+
existing_action =
59+
Enum.find(diffs, fn elem ->
60+
case elem do
61+
{^column_action, _} -> true
62+
_ -> false
63+
end
64+
end)
65+
66+
if existing_action do
67+
Enum.map(diffs, &merge_existing_actions(new_diff, &1))
68+
else
69+
[new_diff | diffs]
70+
end
71+
end
72+
73+
defp merge_diffs(new_diff, diffs) do
74+
[new_diff | diffs]
75+
end
76+
77+
defp merge_existing_actions({column_action, columns1}, elem) do
78+
case elem do
79+
{^column_action, columns2} -> {column_action, Enum.sort(columns1 ++ columns2)}
80+
_ -> elem
81+
end
82+
end
83+
2684
@doc """
2785
Creates a summary of a given GTFS Diff
2886

apps/transport/lib/transport_web/live/gtfs_diff_select_live.ex

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,10 +279,12 @@ defmodule TransportWeb.Live.GTFSDiffSelectLive do
279279
defp update_results_with_diff(socket, diff) do
280280
diff_summary = diff |> GTFSDiffExplain.diff_summary()
281281
diff_explanations = diff |> GTFSDiffExplain.diff_explanations() |> drop_empty()
282+
structural_changes = diff |> GTFSDiffExplain.structural_changes()
282283

283284
update_many(socket, :results, [
284285
set(:diff_summary, diff_summary),
285-
set(:diff_explanations, diff_explanations)
286+
set(:diff_explanations, diff_explanations),
287+
set(:structural_changes, structural_changes)
286288
])
287289
end
288290

apps/transport/lib/transport_web/live/gtfs_diff_select_live/analysis.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ defmodule TransportWeb.Live.GTFSDiffSelectLive.Analysis do
22
@moduledoc """
33
Analysis step of the GTFS diff tool.
44
"""
5-
use Phoenix.LiveView
5+
use Phoenix.Component
66
use TransportWeb.InputHelpers
77
import TransportWeb.Gettext
88

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
defmodule TransportWeb.Live.GTFSDiffSelectLive.Differences do
2+
@moduledoc """
3+
Differences of the selected file.
4+
"""
5+
use Phoenix.Component
6+
import TransportWeb.Gettext
7+
import TransportWeb.Live.GTFSDiffSelectLive.GTFSSpecification
8+
9+
def differences(
10+
%{diff_summary: _, selected_file: _, diff_explanations: _, profile: _, structural_changes: _} = assigns
11+
) do
12+
~H"""
13+
<div class="main">
14+
<.file_description selected_file={@selected_file} />
15+
<.structural_differences
16+
:if={Map.get(@structural_changes, @selected_file)}
17+
selected_file={@selected_file}
18+
structural_changes={Map.get(@structural_changes, @selected_file)}
19+
/>
20+
<.diff_summaries_for_file diff_summary={@diff_summary} selected_file={@selected_file} profile={@profile} />
21+
<%= if assigns[:diff_explanations] do %>
22+
<% active_explanations =
23+
@diff_explanations
24+
|> Enum.filter(fn {file, _} -> file == @selected_file end)
25+
|> Enum.map(fn {_, explanation} -> explanation end) %>
26+
<.detailed_explanations :if={not Enum.empty?(active_explanations)} active_explanations={active_explanations} />
27+
<% end %>
28+
</div>
29+
"""
30+
end
31+
32+
defp structural_differences(%{structural_changes: structural_changes, selected_file: _} = assigns) do
33+
assigns =
34+
assigns
35+
|> assign(file_differences: Enum.filter(structural_changes, &file_difference?/1))
36+
|> assign(column_differences: Enum.filter(structural_changes, &column_difference?/1))
37+
38+
~H"""
39+
<.file_differences
40+
selected_file={@selected_file}
41+
file_differences={@file_differences}
42+
column_differences={@column_differences}
43+
file_criteria={:added_file}
44+
column_criteria={:added_columns}
45+
/>
46+
<.file_differences
47+
selected_file={@selected_file}
48+
file_differences={@file_differences}
49+
column_differences={@column_differences}
50+
file_criteria={:deleted_file}
51+
column_criteria={:deleted_columns}
52+
/>
53+
<.columns_differences
54+
selected_file={@selected_file}
55+
file_differences={@file_differences}
56+
column_differences={@column_differences}
57+
criteria={:added_columns}
58+
/>
59+
<.columns_differences
60+
selected_file={@selected_file}
61+
file_differences={@file_differences}
62+
column_differences={@column_differences}
63+
criteria={:deleted_columns}
64+
/>
65+
"""
66+
end
67+
68+
defp file_difference?(:added_file), do: true
69+
defp file_difference?(:deleted_file), do: true
70+
defp file_difference?(_), do: false
71+
72+
defp file_differences(
73+
%{file_differences: _, column_differences: _, selected_file: _, file_criteria: _, column_criteria: _} = assigns
74+
) do
75+
~H"""
76+
<p :if={@file_differences == [@file_criteria]}>
77+
<i class={pick_symbol(@file_criteria)}></i>
78+
<%= pick_file_message(@file_criteria, count_column_differences(@column_differences, @column_criteria)) %>
79+
</p>
80+
<.columns_list
81+
:if={@file_differences == [@file_criteria]}
82+
selected_file={@selected_file}
83+
column_differences={@column_differences}
84+
criteria={@column_criteria}
85+
/>
86+
"""
87+
end
88+
89+
defp pick_file_message(:added_file, count),
90+
do:
91+
dngettext(
92+
"validations",
93+
"file added with %{count} new column:",
94+
"file added with %{count} new columns:",
95+
count
96+
)
97+
98+
defp pick_file_message(:deleted_file, count),
99+
do:
100+
dngettext(
101+
"validations",
102+
"file deleted along with %{count} column:",
103+
"file deleted along with %{count} columns:",
104+
count
105+
)
106+
107+
defp column_difference?({:added_columns, _}), do: true
108+
defp column_difference?({:deleted_columns, _}), do: true
109+
defp column_difference?(_), do: false
110+
111+
defp columns_differences(%{file_differences: _, column_differences: _, criteria: _, selected_file: _} = assigns) do
112+
~H"""
113+
<p :if={@file_differences == [] && Keyword.get(@column_differences, @criteria)}>
114+
<i class={pick_symbol(@criteria)}></i>
115+
<%= pick_column_message(@criteria, count_column_differences(@column_differences, @criteria)) %>
116+
</p>
117+
<.columns_list
118+
:if={@file_differences == [] && Keyword.get(@column_differences, @criteria)}
119+
selected_file={@selected_file}
120+
column_differences={@column_differences}
121+
criteria={@criteria}
122+
/>
123+
"""
124+
end
125+
126+
defp columns_list(%{column_differences: _, criteria: _, selected_file: _} = assigns) do
127+
~H"""
128+
<ul>
129+
<li :for={column <- Keyword.get(@column_differences, @criteria)}>
130+
<code><%= column %></code>
131+
<span :if={not standard_column?(@selected_file, column)}>
132+
<i class="symbol fa fa-warning orange"></i> <%= dgettext("validations", "non standard column") %>
133+
</span>
134+
</li>
135+
</ul>
136+
"""
137+
end
138+
139+
defp count_column_differences(column_differences, criteria) do
140+
column_differences |> Keyword.get(criteria) |> Enum.count()
141+
end
142+
143+
defp pick_column_message(:added_columns, count),
144+
do: dngettext("validations", "Added %{count} column:", "Added %{count} columns:", count)
145+
146+
defp pick_column_message(:deleted_columns, count),
147+
do: dngettext("validations", "Deleted %{count} column:", "Deleted %{count} columns:", count)
148+
149+
defp pick_symbol(:added_columns), do: "symbol fa fa-square-plus green"
150+
defp pick_symbol(:added_file), do: "symbol fa fa-square-plus green"
151+
defp pick_symbol(:deleted_columns), do: "symbol fa fa-square-minus red"
152+
defp pick_symbol(:deleted_file), do: "symbol fa fa-square-minus red"
153+
154+
defp diff_summaries_for_file(%{selected_file: _, diff_summary: _, profile: _} = assigns) do
155+
~H"""
156+
<p><%= dgettext("validations", "Row changes:") %></p>
157+
<ul>
158+
<.diff_summary_for_file
159+
:for={{nature, translation, css_class} <- diff_natures()}
160+
summary={@diff_summary[nature]}
161+
translation={translation}
162+
selected_file={@selected_file}
163+
class={css_class}
164+
/>
165+
</ul>
166+
<.partial_difference_warning :if={@selected_file not in Transport.GTFSDiff.files_to_analyze(@profile)} />
167+
"""
168+
end
169+
170+
defp diff_summary_for_file(%{summary: _, selected_file: _, translation: _, class: _} = assigns) do
171+
~H"""
172+
<%= for {{file, _nature, target}, n} <- @summary || [] do %>
173+
<li :if={file == @selected_file && target == "row"}>
174+
<span class={@class}><%= @translation %></span>&nbsp;<%= translate_target(target, n) %>
175+
</li>
176+
<% end %>
177+
"""
178+
end
179+
180+
defp partial_difference_warning(%{} = assigns) do
181+
~H"""
182+
<div class="notification warning">
183+
<%= dgettext(
184+
"validations",
185+
"Row changes have not been analyzed for this file. We suggest you dive into both GTFS files for more details."
186+
) %>
187+
</div>
188+
"""
189+
end
190+
191+
defp diff_natures do
192+
[
193+
{"add", dgettext("validations", "added"), "green"},
194+
{"update", dgettext("validations", "updated"), "orange"},
195+
{"delete", dgettext("validations", "deleted"), "red"}
196+
]
197+
end
198+
199+
defp detailed_explanations(%{active_explanations: _} = assigns) do
200+
~H"""
201+
<p>
202+
<%= dgettext("validations", "Notable changes:") %>
203+
<ul>
204+
<li :for={explanation <- @active_explanations}><%= explanation %></li>
205+
</ul>
206+
</p>
207+
"""
208+
end
209+
210+
@doc """
211+
iex> Gettext.put_locale("en")
212+
iex> translate_target("file", 1)
213+
"1 file"
214+
iex> translate_target("file", 3)
215+
"3 files"
216+
iex> translate_target("row", 1)
217+
"1 row"
218+
iex> translate_target("row", 3)
219+
"3 rows"
220+
iex> Gettext.put_locale("fr")
221+
iex> translate_target("file", 1)
222+
"1 fichier"
223+
iex> translate_target("file", 3)
224+
"3 fichiers"
225+
iex> translate_target("row", 1)
226+
"1 ligne"
227+
iex> translate_target("row", 3)
228+
"3 lignes"
229+
"""
230+
def translate_target(target, n) do
231+
case target do
232+
"file" -> dngettext("validations", "%{count} file", "%{count} files", n)
233+
"row" -> dngettext("validations", "%{count} row", "%{count} rows", n)
234+
"column" -> dngettext("validations", "%{count} column", "%{count} columns", n)
235+
_ -> "#{n} #{target}#{if n > 1, do: "s"}"
236+
end
237+
end
238+
end

0 commit comments

Comments
 (0)