From 41ca03fda52596a212b70ecf8db708587b6e7545 Mon Sep 17 00:00:00 2001 From: Chris Kenst Date: Tue, 21 Oct 2025 15:15:13 -0700 Subject: [PATCH 1/3] Script to identify old events --- .gitignore | 5 +- tools/identify_updates.rb | 154 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 tools/identify_updates.rb diff --git a/.gitignore b/.gitignore index 5f8bee99..c0e8d9aa 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ _site .sass-cache .DS_Store -# IntelliJ IDE -/.idea/ + +# Script output +tmp/pending_updates.yml diff --git a/tools/identify_updates.rb b/tools/identify_updates.rb new file mode 100644 index 00000000..7229c344 --- /dev/null +++ b/tools/identify_updates.rb @@ -0,0 +1,154 @@ +#!/usr/bin/env ruby +require 'yaml' +require 'date' +require 'fileutils' + +DATA_DIR = File.expand_path('../_data', __dir__) +CURRENT_FILE = File.join(DATA_DIR, 'current.yml') + +OUTPUT_FILE = File.expand_path('../tmp/pending_updates.yml', __dir__) + +if ARGV.empty? + warn 'Usage: identify_updates.rb YYYY-MM-DD' + exit 1 +end + +begin + today = Date.parse(ARGV[0]) +rescue ArgumentError + warn "Invalid date: #{ARGV[0]}" + exit 1 +end + +unless File.exist?(CURRENT_FILE) + warn "Could not find #{CURRENT_FILE}" + exit 1 +end + +current_events = YAML.load_file(CURRENT_FILE) + +MONTH_REGEX = /(January|February|March|April|May|June|July|August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Sept|Oct|Nov|Dec)/i + +def normalize_month(name) + case name.downcase + when 'jan' then 'January' + when 'feb' then 'February' + when 'mar' then 'March' + when 'apr' then 'April' + when 'may' then 'May' + when 'jun' then 'June' + when 'jul' then 'July' + when 'aug' then 'August' + when 'sep', 'sept' then 'September' + when 'oct' then 'October' + when 'nov' then 'November' + when 'dec' then 'December' + else + name.capitalize + end +end + +def parse_end_date(range_str) + return nil if range_str.nil? + + s = range_str.to_s.strip + s = s.gsub(/[–—]/, '-') + + # Handle ranges like "April 26 - May 1, 2026" + if (m = s.match(/(\w+)\s+(\d{1,2})\s*-\s*(\w+)\s*(\d{1,2}),?\s*(\d{4})$/)) + month2 = normalize_month(m[3]) + day2 = m[4] + year = m[5] + return Date.parse("#{month2} #{day2} #{year}") rescue nil + end + + # Handle ranges like "September 21-26, 2025" + if (m = s.match(/(\w+)\s+\d{1,2}\s*-\s*(\d{1,2}),?\s*(\d{4})$/)) + month = normalize_month(m[1]) + day2 = m[2] + year = m[3] + return Date.parse("#{month} #{day2} #{year}") rescue nil + end + + # Handle single day "September 27, 2025" + if (m = s.match(/(#{MONTH_REGEX})\s+(\d{1,2}),?\s*(\d{4})$/i)) + month = normalize_month(m[1]) + day = m[2] + year = m[3] + return Date.parse("#{month} #{day} #{year}") rescue nil + end + + # Handle formats like "March 4-5 March, 2026" + if (m = s.match(/(#{MONTH_REGEX})\s+\d{1,2}\s*-\s*(\d{1,2})\s+(#{MONTH_REGEX}),?\s*(\d{4})$/i)) + month = normalize_month(m[3]) + day = m[2] + year = m[4] + return Date.parse("#{month} #{day} #{year}") rescue nil + end + + # Fallback: let Date.parse try entire string + Date.parse(s) rescue nil +end + +def extract_status_dates(status_text) + return [] if status_text.nil? + stripped = status_text.to_s.gsub(/<[^>]+>/, ' ') + results = [] + + stripped.scan(/(#{MONTH_REGEX}\s*\d{1,2}(?:\s*-\s*(?:#{MONTH_REGEX})?\s*\d{1,2})?,?\s*\d{4})/i) do |match| + segment = match.first + end_date = parse_end_date(segment) + results << { segment: segment.strip, date: end_date } if end_date + end + + results +end + +updates = [] + +current_events.each do |event| + reasons = [] + + end_date = parse_end_date(event['dates']) + if end_date && end_date < today + reasons << "Event ended on #{end_date}" + elsif end_date.nil? + # Keep track if we couldn't parse but date appears to include a year + reasons << 'Could not parse event end date' if event['dates'].to_s =~ /\d{4}/ + end + + status_dates = extract_status_dates(event['status']) + status_dates.each do |item| + if item[:date] && item[:date] < today + reasons << "Status mentions past date #{item[:date]} (segment: '#{item[:segment]}')" + end + end + + unless reasons.empty? + updates << event.dup + end +end + +FileUtils.mkdir_p(File.dirname(OUTPUT_FILE)) + +formatted_entries = updates.map do |event| + yaml = YAML.dump([event]) + lines = yaml.lines + lines.shift if lines.first&.start_with?('---') + lines.reject! { |line| line.strip == '...' } + lines.map!(&:rstrip) + lines.join("\n") +end + +File.open(OUTPUT_FILE, 'w') do |f| + if formatted_entries.empty? + f.write("# No events needing attention for #{today}\n") + else + f.write(formatted_entries.join("\n\n")) + f.write("\n") + end +end + +puts "Identified #{updates.size} events needing attention." +puts "Details written to #{OUTPUT_FILE}." +puts 'Preview only; no source files were modified.' \ No newline at end of file From fb6b135c494d2d43d7f25d72895d295117aeb840 Mon Sep 17 00:00:00 2001 From: Chris Kenst Date: Wed, 22 Oct 2025 13:00:58 -0700 Subject: [PATCH 2/3] Updated list --- _data/current.yml | 59 +++++++++++------------------------------------ _data/past.yml | 27 ++++++++++++++++++++++ 2 files changed, 41 insertions(+), 45 deletions(-) diff --git a/_data/current.yml b/_data/current.yml index 4e711df8..fb2b81b9 100644 --- a/_data/current.yml +++ b/_data/current.yml @@ -1,35 +1,3 @@ -- name: "TestFlix 2025 - World’s Leading Virtual Software Testing Conference" - location: Online - dates: "October 10-11, 2025" - url: https://www.thetesttribe.com/testflix/?utm_source=testingconferences - status: Registration is Open and Free - -- name: Pacific Northwest Software Quality Conference (PNSQC) 2025 - location: Portland, Oregon, USA and Online - dates: "October 13-15, 2025" - url: https://www.pnsqc.org/?utm_source=testingconferences - twitter: PNSQC - status: Registration is Open - -- name: HUSTEF Software Testing Conference 2025 - location: Budapest, Hungary - dates: "October 14-16, 2025" - url: https://hustef.hu/?utm_source=testingconferences - twitter: HunTestingBoard - status: Registration is Open - -- name: "Quality Beacon - Lighting the Path for Next-Gen Testing" - location: Copenhagen, Denmark - dates: "October 20-21, 2025" - url: https://www.dstb.dk/konferencer/2025/?utm_source=testingconferences - status: Registration is Open until October 6, 2025 - -- name: CypressConf 2025 - location: Online - dates: "October 21-22, 2025" - url: https://cypress.registration.goldcast.io/events/5e06455f-45f2-49c3-98dd-e0ae952e79a0?__hstc=204449871.f2117c4cea0098a12902ced8536e8ec7.1751241536846.1751241536846.1751241536846.1&__hssc=204449871.1.1751241536846&__hsfp=287177776?utm_source=testingconferences - status: Registration is Open and Free - - name: "TestCon Europe 2025" location: Vilnius, Lithuania dates: "October 21-24, 2025" @@ -61,7 +29,7 @@ dates: "October 27-30, 2025" url: https://starcanada.techwell.com/?utm_source=testingconferences twitter: TechWell - status: Early Bird Registration is Open until September 26, 2025 + status: Registration is Open - name: WOPR Latam 04 location: Montevideo, UY @@ -73,12 +41,13 @@ dates: "November 10-11, 2025" url: https://automation.eurostarsoftwaretesting.com/?utm_source=testingconferences twitter: esconfs - status: Early Bird Registration is Open until September 30, 2025 + status: Registration is Open - name: Quality Sense Conf 2025 location: Montevideo, Uruguay dates: "November 11, 2025" url: https://qualitysenseconf.com/?utm_source=testingconferences + status: Registration is Open and Free - name: Software-QS-Tag 2025 location: Möhrendorf, Germany @@ -98,7 +67,7 @@ dates: "November 14, 2025" url: https://www.tokyotestfest.com/?utm_source=testingconferences twitter: tokyotestfest - status: Early Bird Registration is Open + status: Registration is Open - name: Testing Assembly location: Helsinki, Finland @@ -122,27 +91,27 @@ - name: "Workshop: Find more bugs with Test Gap Analysis" location: Online dates: "December 3, 2025" - url: https://tmscl.me/4ntgbEk - status: Registration is open + url: https://teamscale.com/events/tga-2025-12?utm_source=testingconferences + status: Registration is Open - name: Robocon 2026 In Person location: Helsinki, Finland - dates: "February 12-13, 2026" + dates: "February 10-13, 2026" url: https://www.robocon.io/?utm_source=testingconferences - status: CFP is Open until October 19, 2025 + status: Blind Robot Registration is Open - name: Robocon 2026 Online location: Online - dates: "March 4-5 March, 2026" + dates: "March 3-6, 2026" url: https://www.robocon.io/?utm_source=testingconferences - status: CFP is Open until October 19, 2025 + status: Blind Robot Registration is Open - name: Testing Peers Conference 2026 location: Nottingham, UK dates: "March 12, 2026" url: https://testingpeerscon.com/?utm_source=testingconferences twitter: testingpeers - status: CFP is Open until September 30, 2025 | Early Bird Registration is Open + status: Early Bird Registration is Open - name: ParisTestConf 2026 location: Paris, FR @@ -174,7 +143,7 @@ dates: "May 6-8, 2026" url: https://seleniumconf.com/?utm_source=testingconferences twitter: seleniumconf - status: CFP is Open until October 19, 2025 + status: Blind Early Bird Registration is Open - name: WeTest.Athens 2026 location: Athens, Greece @@ -187,11 +156,11 @@ dates: "May 26-28, 2026" url: https://expoqa.eu/?utm_source=testingconferences twitter: expoqa - status: CFP is Open until October 12, 2025 + status: Early Bird Registration is Open - name: EuroSTAR 2026 Software Testing Conference location: Oslo, Norway dates: "June 15-18, 2026" url: https://conference.eurostarsoftwaretesting.com/?utm_source=testingconferences twitter: esconfs - status: CFP is Open until October 3, 2025 + status: Super Early Bird Pricing is Open diff --git a/_data/past.yml b/_data/past.yml index 4383dab4..874074ea 100644 --- a/_data/past.yml +++ b/_data/past.yml @@ -1,3 +1,30 @@ +- name: CypressConf 2025 + location: Online + dates: "October 21-22, 2025" + url: https://cypress.registration.goldcast.io/events/5e06455f-45f2-49c3-98dd-e0ae952e79a0?__hstc=204449871.f2117c4cea0098a12902ced8536e8ec7.1751241536846.1751241536846.1751241536846.1&__hssc=204449871.1.1751241536846&__hsfp=287177776?utm_source=testingconferences + +- name: "Quality Beacon - Lighting the Path for Next-Gen Testing" + location: Copenhagen, Denmark + dates: "October 20-21, 2025" + url: https://www.dstb.dk/konferencer/2025/?utm_source=testingconferences + +- name: HUSTEF Software Testing Conference 2025 + location: Budapest, Hungary + dates: "October 14-16, 2025" + url: https://hustef.hu/?utm_source=testingconferences + twitter: HunTestingBoard + +- name: Pacific Northwest Software Quality Conference (PNSQC) 2025 + location: Portland, Oregon, USA and Online + dates: "October 13-15, 2025" + url: https://www.pnsqc.org/?utm_source=testingconferences + twitter: PNSQC + +- name: "TestFlix 2025 - World’s Leading Virtual Software Testing Conference" + location: Online + dates: "October 10-11, 2025" + url: https://www.thetesttribe.com/testflix/?utm_source=testingconferences + - name: TestSociety location: Lisboa, Portugal dates: "October 10, 2025" From 5a995ecf64b6f8b21ef7b130d3677d45194edd1b Mon Sep 17 00:00:00 2001 From: Chris Kenst Date: Wed, 22 Oct 2025 13:02:56 -0700 Subject: [PATCH 3/3] New tool for identifying updates necessary --- tools/identify_updates.rb | 55 +++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/tools/identify_updates.rb b/tools/identify_updates.rb index 7229c344..982b4654 100644 --- a/tools/identify_updates.rb +++ b/tools/identify_updates.rb @@ -27,7 +27,8 @@ current_events = YAML.load_file(CURRENT_FILE) -MONTH_REGEX = /(January|February|March|April|May|June|July|August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Sept|Oct|Nov|Dec)/i +MONTH_PATTERN = '(?:January|February|March|April|May|June|July|August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Sept|Oct|Nov|Dec)' +MONTH_REGEX = /#{MONTH_PATTERN}/i def normalize_month(name) case name.downcase @@ -48,6 +49,15 @@ def normalize_month(name) end end +def build_date_from_components(month_name, day, year) + month_index = Date::MONTHNAMES.index(month_name) + return nil unless month_index + + Date.new(year.to_i, month_index, day.to_i) +rescue ArgumentError + nil +end + def parse_end_date(range_str) return nil if range_str.nil? @@ -55,35 +65,35 @@ def parse_end_date(range_str) s = s.gsub(/[–—]/, '-') # Handle ranges like "April 26 - May 1, 2026" - if (m = s.match(/(\w+)\s+(\d{1,2})\s*-\s*(\w+)\s*(\d{1,2}),?\s*(\d{4})$/)) + if (m = s.match(/(#{MONTH_PATTERN})\s+(\d{1,2})\s*-\s*(#{MONTH_PATTERN})\s*(\d{1,2}),?\s*(\d{4})$/i)) month2 = normalize_month(m[3]) day2 = m[4] year = m[5] - return Date.parse("#{month2} #{day2} #{year}") rescue nil + return build_date_from_components(month2, day2, year) end # Handle ranges like "September 21-26, 2025" - if (m = s.match(/(\w+)\s+\d{1,2}\s*-\s*(\d{1,2}),?\s*(\d{4})$/)) + if (m = s.match(/(#{MONTH_PATTERN})\s+\d{1,2}\s*-\s*(\d{1,2}),?\s*(\d{4})$/i)) month = normalize_month(m[1]) day2 = m[2] year = m[3] - return Date.parse("#{month} #{day2} #{year}") rescue nil + return build_date_from_components(month, day2, year) end # Handle single day "September 27, 2025" - if (m = s.match(/(#{MONTH_REGEX})\s+(\d{1,2}),?\s*(\d{4})$/i)) + if (m = s.match(/(#{MONTH_PATTERN})\s+(\d{1,2}),?\s*(\d{4})$/i)) month = normalize_month(m[1]) day = m[2] year = m[3] - return Date.parse("#{month} #{day} #{year}") rescue nil + return build_date_from_components(month, day, year) end # Handle formats like "March 4-5 March, 2026" - if (m = s.match(/(#{MONTH_REGEX})\s+\d{1,2}\s*-\s*(\d{1,2})\s+(#{MONTH_REGEX}),?\s*(\d{4})$/i)) + if (m = s.match(/(#{MONTH_PATTERN})\s+\d{1,2}\s*-\s*(\d{1,2})\s+(#{MONTH_PATTERN}),?\s*(\d{4})$/i)) month = normalize_month(m[3]) day = m[2] year = m[4] - return Date.parse("#{month} #{day} #{year}") rescue nil + return build_date_from_components(month, day, year) end # Fallback: let Date.parse try entire string @@ -95,7 +105,7 @@ def extract_status_dates(status_text) stripped = status_text.to_s.gsub(/<[^>]+>/, ' ') results = [] - stripped.scan(/(#{MONTH_REGEX}\s*\d{1,2}(?:\s*-\s*(?:#{MONTH_REGEX})?\s*\d{1,2})?,?\s*\d{4})/i) do |match| + stripped.scan(/(#{MONTH_PATTERN}\s*\d{1,2}(?:\s*-\s*(?:#{MONTH_PATTERN})?\s*\d{1,2})?,?\s*\d{4})/i) do |match| segment = match.first end_date = parse_end_date(segment) results << { segment: segment.strip, date: end_date } if end_date @@ -107,26 +117,20 @@ def extract_status_dates(status_text) updates = [] current_events.each do |event| - reasons = [] + event_needs_move = false + status_needs_update = false end_date = parse_end_date(event['dates']) - if end_date && end_date < today - reasons << "Event ended on #{end_date}" - elsif end_date.nil? - # Keep track if we couldn't parse but date appears to include a year - reasons << 'Could not parse event end date' if event['dates'].to_s =~ /\d{4}/ + if end_date + event_needs_move = true if end_date < today + elsif event['dates'].to_s =~ /\d{4}/ + warn "Warning: could not parse end date for '#{event['name']}': #{event['dates']}" end status_dates = extract_status_dates(event['status']) - status_dates.each do |item| - if item[:date] && item[:date] < today - reasons << "Status mentions past date #{item[:date]} (segment: '#{item[:segment]}')" - end - end + status_needs_update = status_dates.any? { |item| item[:date] && item[:date] < today } - unless reasons.empty? - updates << event.dup - end + updates << event.dup if event_needs_move || status_needs_update end FileUtils.mkdir_p(File.dirname(OUTPUT_FILE)) @@ -150,5 +154,4 @@ def extract_status_dates(status_text) end puts "Identified #{updates.size} events needing attention." -puts "Details written to #{OUTPUT_FILE}." -puts 'Preview only; no source files were modified.' \ No newline at end of file +puts "Details written to #{OUTPUT_FILE}." \ No newline at end of file