-
Notifications
You must be signed in to change notification settings - Fork 31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add list of speakers #143
Add list of speakers #143
Changes from all commits
bac82f5
42525a5
2068f94
6bc05eb
92b581b
986ae37
54b7ffb
e22c486
78adcc8
ef2e65f
a2e0635
81d1e88
d05d78b
69a320b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# This file opens the meeting markdown files | ||
# and attempts to extract the speakers & their | ||
|
||
require './lib/generate_speaker_list/generator' | ||
require './lib/generate_speaker_list/parse_file' | ||
require './lib/generate_speaker_list/parse_agenda' | ||
require './lib/generate_speaker_list/parse_agenda_item' | ||
|
||
GenerateSpeakerList::Generator.new.call |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
require 'json' | ||
|
||
module GenerateSpeakerList | ||
class Generator | ||
SPEAKERS_DATA_FILE = 'data/speakers.json' | ||
|
||
def initialize | ||
@files = Dir['source/meetings/*/*/index.html.md'].sort.reverse | ||
@talks = [] | ||
@authors = {} | ||
end | ||
|
||
def call | ||
load_all_talks_from_files! | ||
|
||
@talks = @talks.flatten.select { |talk| talk[:author] != nil }.group_by { |talk| talk[:author] } | ||
|
||
@talks.each do |author_name, talks| | ||
@authors[author_name] = { | ||
name: author_name, | ||
links: talks.collect { |talk| talk[:author_link] }.uniq.compact, | ||
talks: talks.collect do |talk| | ||
{ | ||
title: talk[:title], | ||
coverage: talk[:coverage], | ||
summary: talk[:summary], | ||
year: talk[:year], | ||
month: talk[:month] | ||
} | ||
end | ||
} | ||
end | ||
|
||
|
||
pp @authors | ||
|
||
save_authors_to_json! | ||
end | ||
|
||
private | ||
|
||
def load_all_talks_from_files! | ||
@files.each do |file| | ||
@talks << ParseFile.new(file).call | ||
end | ||
end | ||
|
||
def save_authors_to_json! | ||
File.write(SPEAKERS_DATA_FILE, JSON.pretty_generate(@authors)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Be nice if this file was sorted by name so it's easier to look through and easier to edit as we add things for future meetings. Thinking particularly of the case where we have to add a new talk to an existing speaker. The current order is weird so would get out of hand quickly as we added to it. |
||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
module GenerateSpeakerList | ||
class ParseAgenda | ||
def initialize(agenda, year, month) | ||
@agenda = agenda | ||
@year = year | ||
@month = month | ||
@agenda_item_index = -1 | ||
@agenda_items = {} | ||
end | ||
|
||
def call | ||
@agenda.each do |line| | ||
split_agenda_into_items_by_line(line) | ||
end | ||
|
||
@agenda_items | ||
.collect { |index, agenda_item| ParseAgendaItem.new(agenda_item, @year, @month) } | ||
.select(&:valid?) | ||
.collect(&:to_h) | ||
end | ||
|
||
private | ||
|
||
def split_agenda_into_items_by_line(line) | ||
if line.start_with?('### ') | ||
@agenda_item_index += 1 | ||
@agenda_items[@agenda_item_index] ||= [] | ||
end | ||
|
||
if @agenda_item_index >= 0 | ||
@agenda_items[@agenda_item_index] << line | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
module GenerateSpeakerList | ||
class ParseAgendaItem | ||
MARKDOWN_LINK_REGEX = /(.*)\[([^\]]+)\]\(([^)]+)\)(.*+)/ | ||
|
||
def initialize(agenda_item, year, month) | ||
@agenda_item = agenda_item | ||
@year = year | ||
@month = month | ||
end | ||
|
||
def to_h | ||
{ | ||
title: title, | ||
author: author, | ||
author_link: author_link, | ||
coverage: coverage, | ||
summary: summary, | ||
year: @year, | ||
month: @month | ||
} | ||
end | ||
|
||
def valid? | ||
title != "" && author != "" && summary != "" && coverage != nil | ||
end | ||
|
||
def title | ||
@agenda_item[0].strip.sub('### ', '') | ||
end | ||
|
||
def author | ||
return unless @agenda_item[2].match?(MARKDOWN_LINK_REGEX) | ||
|
||
@agenda_item[2].strip.gsub(MARKDOWN_LINK_REGEX, '\2') | ||
end | ||
|
||
def author_link | ||
return unless @agenda_item[2].match?(MARKDOWN_LINK_REGEX) | ||
|
||
@agenda_item[2].strip.gsub(MARKDOWN_LINK_REGEX, '\3') | ||
end | ||
|
||
def coverage | ||
coverage_line = @agenda_item.select do |line| | ||
line.start_with?('{::coverage') | ||
end | ||
|
||
coverage_line.first&.strip | ||
end | ||
|
||
def summary | ||
@agenda_item | ||
.select { |line| line.start_with?('> ') } | ||
.collect { |line| line.gsub(/^\> /, '') } | ||
.join.strip | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
module GenerateSpeakerList | ||
class ParseFile | ||
def initialize(file) | ||
@file = file | ||
@agenda_block = [] | ||
@recording = false | ||
end | ||
|
||
def call | ||
File.open(@file).each do |line| | ||
parse_file_line(line) | ||
end | ||
|
||
ParseAgenda.new(@agenda_block, year, month).call | ||
end | ||
|
||
private | ||
|
||
def year | ||
@file.split('/')[2] | ||
end | ||
|
||
def month | ||
@file.split('/')[3] | ||
end | ||
|
||
def parse_file_line(line) | ||
if @recording && line.start_with?('## ') | ||
@recording = false | ||
elsif @recording && line.start_with?('## Misc.') | ||
@recording = false | ||
elsif @recording && line.start_with?('## Pub') | ||
@recording = false | ||
elsif !@recording && line.start_with?('## Agenda') | ||
@recording = true | ||
end | ||
|
||
if @recording | ||
@agenda_block << line | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
require 'ostruct' | ||
|
||
module SpeakerHelpers | ||
def all_speakers | ||
data.speakers | ||
.collect { |name, speaker_data| Speaker.new(name, speaker_data) } | ||
end | ||
|
||
private | ||
|
||
class Speaker | ||
attr_reader :name | ||
attr_reader :links | ||
attr_reader :talks | ||
|
||
def initialize(name, speaker_data) | ||
@name = name | ||
@talks = speaker_data[:talks] | ||
@links = speaker_data[:links] | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,40 @@ | ||||||
--- | ||||||
published_at: 2020-11-18 00:00:00 Z | ||||||
title: Speakers | ||||||
created_at: 2020-11-18 00:00:00 Z | ||||||
updated_at: 2020-11-18 00:00:00 Z | ||||||
status: Published | ||||||
created_by: | ||||||
email: [email protected] | ||||||
name: Mike Rogers | ||||||
--- | ||||||
|
||||||
<div class="speaker__list"> | ||||||
|
||||||
<% all_speakers.each do |speaker| %> | ||||||
<div class="speaker"> | ||||||
<h2 class="speaker__name"><%= speaker.name %></h2> | ||||||
<div class="speaker__socials"> | ||||||
|
||||||
<% speaker.links.each do |speaker_link| %> | ||||||
<% link_to speaker_link do %> | ||||||
<% if speaker_link.include?('twitter.com') %> | ||||||
<%= image_tag 'twitter.svg', width: 24, height: 24 %> | ||||||
<% elsif speaker_link.include?('github.com') %> | ||||||
<%= image_tag 'github.svg', width: 24, height: 24 %> | ||||||
<% else %> | ||||||
🔗 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we get an SVG for a standard link svg too? I don't know where the twitter and GitHub ones came from, but the link emoji looks different and has a different baseline, so hopefully we can get a link svg from the same place. |
||||||
<% end %> | ||||||
<% end %> | ||||||
<% end %> | ||||||
</div> | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know we've taken inspiration from some other website on this, but I'm pretty sure we'll never get most of these details, so lets pare it right back. I quite like the svg icons for the twitter / GitHub links, but I also know that without a huge amount of effort we're probably not going to get twitter and GitHub links for most of our speakers, and for some we don't even have those, some folk just link to LinkedIn and personal websites were the preferred link back in the past. Perhaps we can just introspect on the one link we do have and render an appropriate icon? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I agree :) I was hoping to find a way to pull in a bit more information, I think I'll save that for another PR :) |
||||||
|
||||||
<ul> | ||||||
<% speaker.talks.each do |talk| %> | ||||||
<li><%= link_to talk[:title], "/meetings/#{talk[:year]}/#{talk[:month]}/" %> - <%= talk[:month] %> <%= talk[:year] %></li> | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor but let's titlecase the month:
Suggested change
|
||||||
<% end %> | ||||||
</ul> | ||||||
</div> | ||||||
<% end %> | ||||||
|
||||||
</div> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
.speaker__list { | ||
display: grid; | ||
grid-template-columns: repeat(12, 1fr); | ||
grid-gap: $space-3; | ||
} | ||
|
||
.speaker { | ||
grid-column: span 13; | ||
} | ||
|
||
.speaker__name { | ||
display: inline-block; | ||
margin-top: 0; | ||
margin-bottom: 0; | ||
} | ||
|
||
.speaker__socials { | ||
display: inline-block; | ||
|
||
a { | ||
padding-left: $space-3; | ||
|
||
&:hover { | ||
background: none; | ||
opacity: 0.5; | ||
} | ||
} | ||
|
||
img { | ||
display: inline-block; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's nothing in the file for talks from before 2010. I know the structure of the write-ups has changed a bit, but not by a huge amount I don't think. So it's weird there's nothing in there from those early meetings.
In the json file I've spotted a few weird speaker names where we've got a greedy regexp match on names with multiple links in them. So I wonder if we should revisit the parsing approach in general? Would we get better results if we rendered the page from markdown -> HTML and then parse with nokogiri? Or maybe we render into Kramdowns internal structure and walk parse that?