This is an automated email from the ASF dual-hosted git repository.

sebb pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/whimsy.git


The following commit(s) were added to refs/heads/master by this push:
     new 1a9e3b00 Rework parsing to behave like steve-tools/seed-issues.py
1a9e3b00 is described below

commit 1a9e3b00a1a1cae42db018e4bf7ef3956c2afd24
Author: Sebb <[email protected]>
AuthorDate: Wed Feb 21 19:30:10 2024 +0000

    Rework parsing to behave like steve-tools/seed-issues.py
---
 lib/whimsy/asf/member-files.rb | 87 +++++++++++++++++++++++++-----------------
 1 file changed, 52 insertions(+), 35 deletions(-)

diff --git a/lib/whimsy/asf/member-files.rb b/lib/whimsy/asf/member-files.rb
index 1f113e50..0c1a3b8e 100644
--- a/lib/whimsy/asf/member-files.rb
+++ b/lib/whimsy/asf/member-files.rb
@@ -12,9 +12,31 @@ module ASF
 
     NOMINATED_MEMBERS = 'nominated-members.txt'
     NOMINATED_BOARD = 'board_nominations.txt'
-    # N.B. Board does not include email
-    VALID_KEYS = ['Nominated by','Nomination Statement', 'Nominee email', 
'Seconded by']
-  
+    NAME2OUTPUTKEY = { # names (from Regex) and corresponding output keys
+      'email' => 'Nominee email',
+      'nomby' => 'Nominated by',
+      'seconds' => 'Seconded by',
+      'statement' => 'Nomination Statement',
+    }
+
+    # Same as MEMBER_REGEX, but no <uid> and no <email>
+    BOARD_REGEX = %r{
+        \A(?<header>(?<name>[^:]+?):?)\r?\n
+        \s*Nominated\ by:\s*(?<nomby>.*)\r?\n
+        \s*Seconded\ by:\s*(?<seconds>.*?)\r?\n+
+        \s*Nomination\ [sS]tatement:\s*?\r?\n+(?<statement>.*)\z
+        }mx
+
+    # This Regex is very similar to the one in the script used to create 
ballots:
+    # 
https://svn.apache.org/repos/private/foundation/Meetings/steve-tools/seed-issues.py
+    MEMBER_REGEX = %r{
+      \A(?<header>(?:(?<uid>[-_.a-z0-9]+)\s+)?(?<name>[^:]+?):?)\r?\n
+      \s*Nominee\ email:\s*(?<email>.*)\r?\n
+      \s*Nominated\ by:\s*(?<nomby>.*)\r?\n
+      \s*Seconded\ by:\s*(?<seconds>.*?)\r?\n+
+      \s*Nomination\ [sS]tatement:\s*?\r?\n+(?<statement>.*)\z
+      }mx
+
     # get the latest meeting directory or nomination file
     def self.latest_meeting(name=nil)
       if name.nil? # we want the parent directory
@@ -35,11 +57,19 @@ module ASF
     # Seconded by => array of seconders
     # Nomination Statement => array of text lines
     def self.parse_file(name)
+      case name
+      when NOMINATED_BOARD
+        regex = BOARD_REGEX
+      when NOMINATED_MEMBERS
+        regex = MEMBER_REGEX
+      else
+        raise ArgumentError.new "Unexpected name: #{name}"
+      end
       # N.B. The format has changed over the years. This is the syntax as of 
2021.
       # -----------------------------------------
       # <empty line>
       #  header line
-      #    Nominee email:
+      #    Nominee email: (not present in board file)
       #    Nominated by:
       #    Seconded by:
 
@@ -54,45 +84,32 @@ module ASF
       # This is necessary to avoid issues with matching Regexes.
       File.open(nomfile, mode: 'rb:UTF-8')
         .map(&:scrub)
-        .slice_before(/^\s*---+--\s*/)
+        .slice_before(/^\s*-{35,60}\s*/)
         .drop(2) # instructions and sample block
         .each do |block|
         block.shift(1) # divider
-        block.shift(1) if block[0]&.strip == '' # Allow for missing blank line 
(last block is empty)
         nominee = {}
         header = nil
-        block
-            .slice_before(/^ +(\w+ \w+):\s*/) # split on the header names
-            .each_with_index do |para, idx|
-          if idx == 0 # id and name (or just name for board)
-            header = para.first.strip
-            raise ArgumentError.new "Unexpected start to entry after 
#{lastheader}: #{para}" unless header.size > 3
-            lastheader = header
+        data = block.join.strip
+        next if data == ''
+        md = regex.match(data)
+        raise  ArgumentError.new "Cannot parse #{data}" unless md
+        md.named_captures.each do |k, v|
+          case k
+          when 'header'
+            header = v
+          when 'header'
+            header = v
+          when 'uid', 'name'
+            # not currently used
           else
-            key, value = para.shift.strip.split(':', 2)
-            unless VALID_KEYS.include? key
-              raise ArgumentError.new "Invalid key name '#{key}' at 
'#{header}' in #{nomfile}"
-            end
-            if para.size == 0 # no more data to follow
-              nominee[key] = value
-            else
-              tmp = [value, para.map(&:chomp)].flatten.compact
-              tmp.pop if tmp[-1].empty? # drop trailing empty line only
-              nominee[key] = tmp
-            end
-          end
-        end
-
-        unless header.nil? || header.empty?
-          keys = nominee.keys
-          case name
-          when NOMINATED_BOARD
-            raise ArgumentError.new "Expected 3 keys, found #{keys} at 
'#{header}' in #{name}" unless keys.size == 3
-          when NOMINATED_MEMBERS
-            raise ArgumentError.new "Expected 4 keys, found #{keys} at 
'#{header}' in #{name}" unless keys.size == 4
+            outkey = NAME2OUTPUTKEY[k]
+            raise ArgumentError.new "Unexpected regex capture name: #{k}" if 
outkey.nil?
+            v = v.split("\n") if k == 'statement' or k == 'seconds'
+            nominee[outkey] = v
           end
-          yield header, nominee 
         end
+        yield header, nominee
       end
     end
 

Reply via email to