summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorerdgeist <erdgeist@erdgeist.org>2026-07-16 16:43:11 +0200
committererdgeist <erdgeist@erdgeist.org>2026-07-16 16:43:11 +0200
commit35dc05de97677d57123617a7c4e78aad1611b28d (patch)
tree2960f3a8d363a12edd497fc96a022a9da77929fd /app
parentb701669d0d9a5d7be01f99b8e725c05c6c5d52ce (diff)
managed RRULE constructs now include week selection
Improve the humanizer to also understand weekly patters that an editor can manually select, on top of the single week rules. Extend template and javascript controller to reflect these changes.
Diffstat (limited to 'app')
-rw-r--r--app/models/concerns/rrule_humanizer.rb34
-rw-r--r--app/views/events/edit.html.erb8
2 files changed, 36 insertions, 6 deletions
diff --git a/app/models/concerns/rrule_humanizer.rb b/app/models/concerns/rrule_humanizer.rb
index 8231de8..ca29648 100644
--- a/app/models/concerns/rrule_humanizer.rb
+++ b/app/models/concerns/rrule_humanizer.rb
@@ -15,8 +15,8 @@ module RruleHumanizer
15 }.freeze 15 }.freeze
16 16
17 ORDINAL_NAMES = { 17 ORDINAL_NAMES = {
18 de: { 1=>"ersten", 2=>"zweiten", 3=>"dritten", 4=>"vierten", -1=>"letzten", -2=>"vorletzten" }, 18 de: { 1=>"ersten", 2=>"zweiten", 3=>"dritten", 4=>"vierten", 5=>"fünften", -1=>"letzten", -2=>"vorletzten" },
19 en: { 1=>"first", 2=>"second", 3=>"third", 4=>"fourth", -1=>"last", -2=>"second-to-last" } 19 en: { 1=>"first", 2=>"second", 3=>"third", 4=>"fourth", 5=>"fifth", -1=>"last", -2=>"second-to-last" }
20 }.freeze 20 }.freeze
21 21
22 MONTH_NAMES = { 22 MONTH_NAMES = {
@@ -35,7 +35,8 @@ module RruleHumanizer
35 ordinals = ORDINAL_NAMES[loc] || ORDINAL_NAMES[:en] 35 ordinals = ORDINAL_NAMES[loc] || ORDINAL_NAMES[:en]
36 months = MONTH_NAMES[loc] || MONTH_NAMES[:en] 36 months = MONTH_NAMES[loc] || MONTH_NAMES[:en]
37 37
38 days = byday&.split(",")&.map do |d| 38 byday_values = byday&.split(",")
39 days = byday_values&.map do |d|
39 if d =~ /^(-?\d+)([A-Z]{2})$/ 40 if d =~ /^(-?\d+)([A-Z]{2})$/
40 "#{ordinals[$1.to_i]} #{weekdays[$2]}" 41 "#{ordinals[$1.to_i]} #{weekdays[$2]}"
41 else 42 else
@@ -43,6 +44,21 @@ module RruleHumanizer
43 end 44 end
44 end 45 end
45 46
47 excluded_monthly_ordinal = nil
48 excluded_monthly_weekday = nil
49 if freq == "MONTHLY" && byday_values.present?
50 ordinal_days = byday_values.map { |d| d.match(/^([1-5])([A-Z]{2})$/) }
51 if ordinal_days.all?
52 positions = ordinal_days.map { |match| match[1].to_i }.uniq.sort
53 weekdays_in_rule = ordinal_days.map { |match| match[2] }.uniq
54 missing_positions = (1..5).to_a - positions
55 if positions.size == 4 && weekdays_in_rule.size == 1 && missing_positions.size == 1
56 excluded_monthly_ordinal = missing_positions.first
57 excluded_monthly_weekday = weekdays_in_rule.first
58 end
59 end
60 end
61
46 base = 62 base =
47 case loc 63 case loc
48 when :de 64 when :de
@@ -59,14 +75,22 @@ module RruleHumanizer
59 interval == 2 ? "Alle zwei Wochen" : "Wöchentlich" 75 interval == 2 ? "Alle zwei Wochen" : "Wöchentlich"
60 end 76 end
61 when "MONTHLY" 77 when "MONTHLY"
62 days ? "Jeden #{days.join(' und ')} im Monat" : "Monatlich" 78 if excluded_monthly_ordinal
79 "Jeden #{weekdays[excluded_monthly_weekday]} im Monat, außer dem #{ordinals[excluded_monthly_ordinal]} #{weekdays[excluded_monthly_weekday]}"
80 else
81 days ? "Jeden #{days.join(' und ')} im Monat" : "Monatlich"
82 end
63 end 83 end
64 else 84 else
65 case freq 85 case freq
66 when "WEEKLY" 86 when "WEEKLY"
67 days ? "#{interval == 2 ? 'Every other' : 'Every'} #{days.join(' and ')}" : (interval == 2 ? "Every other week" : "Weekly") 87 days ? "#{interval == 2 ? 'Every other' : 'Every'} #{days.join(' and ')}" : (interval == 2 ? "Every other week" : "Weekly")
68 when "MONTHLY" 88 when "MONTHLY"
69 days ? "Every #{days.join(' and ')} of the month" : "Monthly" 89 if excluded_monthly_ordinal
90 "Every #{weekdays[excluded_monthly_weekday]} of the month, except the #{ordinals[excluded_monthly_ordinal]} #{weekdays[excluded_monthly_weekday]}"
91 else
92 days ? "Every #{days.join(' and ')} of the month" : "Monthly"
93 end
70 end 94 end
71 end 95 end
72 return nil unless base 96 return nil unless base
diff --git a/app/views/events/edit.html.erb b/app/views/events/edit.html.erb
index b6564a4..bf8cb35 100644
--- a/app/views/events/edit.html.erb
+++ b/app/views/events/edit.html.erb
@@ -56,9 +56,15 @@
56 <div id="rrule_monthly_options" style="display: none;"> 56 <div id="rrule_monthly_options" style="display: none;">
57 <p><label><%= check_box_tag "rrule_monthly_ordinal" %> On a specific weekday each month</label></p> 57 <p><label><%= check_box_tag "rrule_monthly_ordinal" %> On a specific weekday each month</label></p>
58 <p id="rrule_ordinal_fields" style="display: none;"> 58 <p id="rrule_ordinal_fields" style="display: none;">
59 <%= select_tag "rrule_ordinal", options_for_select([["1.", 1], ["2.", 2], ["3.", 3], ["4.", 4], ["letzter", -1], ["vorletzter", -2]]) %> 59 <%= select_tag "rrule_ordinal", options_for_select([["1.", 1], ["2.", 2], ["3.", 3], ["4.", 4], ["letzter", -1], ["vorletzter", -2], ["Selected weeks", "custom"]]) %>
60 <%= select_tag "rrule_ordinal_day", options_for_select(%w[MO TU WE TH FR SA SU].map { |c| [RruleHumanizer::WEEKDAY_NAMES[:de][c], c] }) %> 60 <%= select_tag "rrule_ordinal_day", options_for_select(%w[MO TU WE TH FR SA SU].map { |c| [RruleHumanizer::WEEKDAY_NAMES[:de][c], c] }) %>
61 </p> 61 </p>
62 <p id="rrule_custom_ordinal_fields" style="display: none;">
63 Weeks of the month:
64 <% (1..5).each do |ordinal| %>
65 <label><%= check_box_tag "rrule_custom_ordinal_#{ordinal}" %> <%= ordinal %>.</label>
66 <% end %>
67 </p>
62 </div> 68 </div>
63 <p> 69 <p>
64 <label><%= check_box_tag "rrule_exclude_month" %> Except in one month</label> 70 <label><%= check_box_tag "rrule_exclude_month" %> Except in one month</label>