Render import:status as an aligned table
Assisted-by: GLM-5.3 via pi
change pywyqwkkoxwtzvuwooptsvxquwmxmrww
commit 4aeeb7c77d82f9000af5af7f16d0eff0b8604a4e
author Alpha Chen <alpha@kejadlen.dev>
date
parent wnzzttkq
diff --git a/tasks/import.rake b/tasks/import.rake
index 0888845..fc48ad5 100644
--- a/tasks/import.rake
+++ b/tasks/import.rake
@@ -87,22 +87,24 @@ namespace :import do
       next
     end
 
-    # The statuses in their order through an import, so the line reads
-    # as where the plan has got to rather than an alphabetical jumble.
-    plans.each do
-      host = it.host ? "on #{it.host}" : "not yet on a host"
-      puts "#{it.dir.basename}  #{it.source}  #{host}"
-      counts = it.contacts.group_by(&:status).transform_values(&:length)
-      summary = [
-        ["to land", counts.fetch(nil, 0)],
-        ["landed", counts.fetch("landed", 0)],
-        ["imported", counts.fetch("imported", 0)],
-        ["removed", counts.fetch("removed", 0)],
-      ].reject { |_, count| count.zero? }.map { |label, count| "#{count} #{label}" }.join(", ")
-      word = it.contacts.size == 1 ? "contact" : "contacts"
-      puts summary.empty? ? "  #{it.contacts.size} #{word}" : "  #{it.contacts.size} #{word}: #{summary}"
-    end
-
+    # One aligned row per plan, the statuses as columns in their order
+    # through an import, so where a plan has got to is read off the
+    # row rather than parsed out of a sentence. The plan names are the
+    # directories', copy-pasteable into PLAN=.
+    headers = ["plan", "host", "contacts", "planned", "landed", "imported", "removed"]
+    rows = plans.map { |plan|
+      counts = plan.contacts.group_by(&:status).transform_values(&:length)
+      [plan.dir.basename.to_s, plan.host || "—", plan.contacts.size.to_s,
+       counts.fetch(nil, 0).to_s, counts.fetch("landed", 0).to_s,
+       counts.fetch("imported", 0).to_s, counts.fetch("removed", 0).to_s]
+    }
+    widths = headers.each_index.map { |i| [headers[i].length, *rows.map { it[i].length }].max }
+    table = rows.unshift(headers).map { |row|
+      row.each_with_index.map { |cell, i| cell.public_send(i < 2 ? :ljust : :rjust, widths[i]) }.join("  ")
+    }
+    puts table
+
+    puts ""
     landing = plans.find(&ImportTasks::TO_LAND)
     leaving = plans.find(&ImportTasks::TO_LEAVE)
     puts "next: import:execute would land #{landing.dir.basename}" if landing
diff --git a/test/tasks/test_import.rb b/test/tasks/test_import.rb
index 2f29d52..4c714ac 100644
--- a/test/tasks/test_import.rb
+++ b/test/tasks/test_import.rb
@@ -28,12 +28,11 @@ class ImportStatusTaskTest < Minitest::Test
 
       out, = run_task(root)
 
-      assert_includes out, "macos-20260901T000000Z  macos  on https://contacts.example"
-      assert_includes out, "  2 contacts: 1 imported, 1 removed"
-      assert_includes out, "macos-20260916T180412Z  macos  not yet on a host"
-      assert_includes out, "  1 contact: 1 to land"
-      assert_includes out, "next: import:execute would land macos-20260916T180412Z"
-      assert_includes out, "next: import:macos:remove would clear macos-20260901T000000Z"
+      assert_match(%r{plan\s+host\s+contacts\s+planned\s+landed\s+imported\s+removed}, out)
+      assert_match(/macos-20260901T000000Z\s+https:\/\/contacts\.example\s+2\s+0\s+0\s+1\s+1/, out)
+      assert_match(/macos-20260916T180412Z\s+—\s+1\s+1\s+0\s+0\s+0/, out)
+      assert_includes out, "next: import:execute would land macos-20260916T180412Z\n"
+      assert_includes out, "next: import:macos:remove would clear macos-20260901T000000Z\n"
     end
   end