piccle 0.1.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/NOTES.md +69 -0
- data/README.md +175 -0
- data/Rakefile +8 -0
- data/agpl-3.0.md +660 -0
- data/assets/css/default.css +397 -0
- data/assets/css/normalize.css +427 -0
- data/assets/icons/android-chrome-192x192.png +0 -0
- data/assets/icons/android-chrome-512x512.png +0 -0
- data/assets/icons/apple-touch-icon.png +0 -0
- data/assets/icons/favicon-16x16.png +0 -0
- data/assets/icons/favicon-32x32.png +0 -0
- data/assets/icons/favicon.ico +0 -0
- data/bin/console +14 -0
- data/bin/piccle +355 -0
- data/bin/setup +8 -0
- data/db/migrations/001_create_photos.rb +15 -0
- data/db/migrations/002_update_photos.rb +14 -0
- data/db/migrations/003_create_keywords_and_join_table.rb +14 -0
- data/db/migrations/004_add_focal_length.rb +7 -0
- data/db/migrations/005_create_locations.rb +20 -0
- data/js-renderer/handlebars.min-v4.7.6.js +29 -0
- data/js-renderer/renderer.js +93 -0
- data/lib/piccle.rb +52 -0
- data/lib/piccle/config.rb +136 -0
- data/lib/piccle/database.rb +33 -0
- data/lib/piccle/dstk_service.rb +64 -0
- data/lib/piccle/extractor.rb +128 -0
- data/lib/piccle/js_renderer.rb +37 -0
- data/lib/piccle/models/keyword.rb +6 -0
- data/lib/piccle/models/location.rb +11 -0
- data/lib/piccle/models/photo.rb +211 -0
- data/lib/piccle/parser.rb +230 -0
- data/lib/piccle/quilt_generator.rb +30 -0
- data/lib/piccle/renderer.rb +175 -0
- data/lib/piccle/streams.rb +2 -0
- data/lib/piccle/streams/base_stream.rb +56 -0
- data/lib/piccle/streams/camera_stream.rb +35 -0
- data/lib/piccle/streams/date_stream.rb +95 -0
- data/lib/piccle/streams/event_stream.rb +73 -0
- data/lib/piccle/streams/keyword_stream.rb +24 -0
- data/lib/piccle/streams/location_stream.rb +57 -0
- data/lib/piccle/template_helpers.rb +79 -0
- data/lib/piccle/version.rb +3 -0
- data/lib/tasks/development.rake +38 -0
- data/piccle.gemspec +43 -0
- data/templates/_breadcrumbs.handlebars.slim +16 -0
- data/templates/_footer.handlebars.slim +2 -0
- data/templates/_header.handlebars.slim +36 -0
- data/templates/_navigation.handlebars.slim +16 -0
- data/templates/_substream.handlebars.slim +17 -0
- data/templates/feed.atom.slim +29 -0
- data/templates/index.html.handlebars.slim +36 -0
- data/templates/show.html.handlebars.slim +64 -0
- metadata +340 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rmagick'
|
2
|
+
|
3
|
+
# Generates an image quilt, ideal for OpenGraph previews.
|
4
|
+
module Piccle
|
5
|
+
class QuiltGenerator
|
6
|
+
# Generates a quilt of the given images - up to 9.
|
7
|
+
def self.generate_for(photo_paths)
|
8
|
+
photo_paths = photo_paths.first(9)
|
9
|
+
image_list = Magick::ImageList.new(*photo_paths)
|
10
|
+
image_list.montage do |conf|
|
11
|
+
conf.border_width = 0
|
12
|
+
conf.geometry = "200x200+0+0"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns a tuple of [width, height] output dimensions. When we stitch a quilt together each square is 200px,
|
17
|
+
# but variable numbers of images can lead to quilts of different sizes. OpenGraph tags should include this
|
18
|
+
# size information.
|
19
|
+
def self.dimensions_for(image_count)
|
20
|
+
case image_count
|
21
|
+
when 1 then [200, 200]
|
22
|
+
when 2 then [400, 200]
|
23
|
+
when 3 then [600, 200]
|
24
|
+
when 4 then [400, 400]
|
25
|
+
when 5..6 then [600, 400]
|
26
|
+
else [600, 600]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
module Piccle
|
2
|
+
# Renders a bunch of pages, based on the hash data loaded by the given parser.
|
3
|
+
class Renderer
|
4
|
+
def initialize(parser)
|
5
|
+
@parser = parser
|
6
|
+
@extractor = Piccle::Extractor.new(parser)
|
7
|
+
end
|
8
|
+
|
9
|
+
# Given an array that contains a path (not including a :photos key), get that photo
|
10
|
+
# data and render an index page using the :photos data found at that location.
|
11
|
+
#
|
12
|
+
# For instance, if selector was ["by-date", "2015"] you'd get an index page of photos
|
13
|
+
# for 2015 based on the data held by the parser.
|
14
|
+
def render_index(selector)
|
15
|
+
Piccle::TemplateHelpers.render("index", render_index_template_vars(selector))
|
16
|
+
end
|
17
|
+
|
18
|
+
# Renders the "main" index – the front page of our site.
|
19
|
+
def render_main_index
|
20
|
+
Piccle::TemplateHelpers.render("index", render_main_index_template_vars)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Renders an Atom feed of the given subsection.
|
24
|
+
def render_feed(selector = [])
|
25
|
+
photos = @parser.subsection_photos(selector).sort_by { |k, p| p[:created_at] }.reverse
|
26
|
+
escaped_selector = selector.map { |s| CGI::escape(s) }
|
27
|
+
|
28
|
+
template_vars = {
|
29
|
+
photos: photos,
|
30
|
+
joined_selector: "/#{escaped_selector.join("/")}/",
|
31
|
+
feed_update_time: photos.map { |k, v| v[:created_at] }.max,
|
32
|
+
selector: selector,
|
33
|
+
site_metadata: site_metadata
|
34
|
+
}
|
35
|
+
|
36
|
+
Piccle::TemplateHelpers.render_rss("feed", template_vars)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Render a page for a specific photo.
|
40
|
+
def render_photo(hash, selector=[])
|
41
|
+
Piccle::TemplateHelpers.render("show", render_photo_template_vars(hash, selector))
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
46
|
+
# Returns all the data we pass into the main index to render.
|
47
|
+
def render_main_index_template_vars
|
48
|
+
template_vars = {
|
49
|
+
photos: @parser.data[:photos],
|
50
|
+
event_starts: @parser.data[:event_starts],
|
51
|
+
event_ends: @parser.data[:event_ends],
|
52
|
+
nav_items: @extractor.navigation,
|
53
|
+
site_metadata: site_metadata
|
54
|
+
}
|
55
|
+
|
56
|
+
if Piccle.config.open_graph?
|
57
|
+
width, height = Piccle::QuiltGenerator.dimensions_for(@parser.data[:photos].length)
|
58
|
+
template_vars[:open_graph] = open_graph_for(title: site_title(),
|
59
|
+
description: "A gallery of photos by #{Piccle.config.author_name}",
|
60
|
+
image_url: "#{Piccle.config.home_url}quilt.jpg",
|
61
|
+
image_alt: "A quilt of the most recent images in this gallery.",
|
62
|
+
width: width,
|
63
|
+
height: height,
|
64
|
+
page_url: Piccle.config.home_url)
|
65
|
+
end
|
66
|
+
template_vars
|
67
|
+
end
|
68
|
+
|
69
|
+
# Returns all the data we pass into a template for rendering an index page, as a hash.
|
70
|
+
def render_index_template_vars(selector)
|
71
|
+
breadcrumbs = @extractor.breadcrumbs_for(selector)
|
72
|
+
selector_path = "#{selector.join('/')}/"
|
73
|
+
template_vars = {
|
74
|
+
photos: @parser.subsection_photos(selector),
|
75
|
+
event_starts: [],
|
76
|
+
event_ends: [],
|
77
|
+
nav_items: @extractor.navigation,
|
78
|
+
selector: selector,
|
79
|
+
selector_path: selector_path,
|
80
|
+
breadcrumbs: breadcrumbs,
|
81
|
+
site_url: Piccle.config.home_url,
|
82
|
+
include_prefix: Piccle::TemplateHelpers.include_prefix(selector),
|
83
|
+
site_metadata: site_metadata
|
84
|
+
}
|
85
|
+
|
86
|
+
if Piccle.config.open_graph?
|
87
|
+
width, height = Piccle::QuiltGenerator.dimensions_for(@parser.subsection_photo_hashes(selector).length)
|
88
|
+
template_vars[:open_graph] = open_graph_for(title: site_title(breadcrumbs),
|
89
|
+
description: "A gallery of photos by #{Piccle.config.author_name}",
|
90
|
+
image_url: "#{Piccle.config.home_url}#{selector_path}quilt.jpg",
|
91
|
+
image_alt: "A quilt of the most recent images in this gallery.",
|
92
|
+
width: width,
|
93
|
+
height: height,
|
94
|
+
page_url: "#{Piccle.config.home_url}#{selector_path}")
|
95
|
+
end
|
96
|
+
|
97
|
+
template_vars
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns all the template vars we use to render a photo page.
|
101
|
+
def render_photo_template_vars(hash, selector)
|
102
|
+
photo_data = @parser.data[:photos][hash]
|
103
|
+
substreams = [@parser.substream_for(hash)] + @parser.links_for(hash).map { |selector| @parser.interesting_substream_for(hash, selector) }.compact
|
104
|
+
|
105
|
+
template_vars = {
|
106
|
+
photo: photo_data,
|
107
|
+
selector: selector,
|
108
|
+
selector_path: selector.any? ? "#{selector.join('/')}/" : "",
|
109
|
+
breadcrumbs: @extractor.breadcrumbs_for(selector),
|
110
|
+
substreams: substreams.select { |stream| stream[:photos].length > 1 },
|
111
|
+
camera_link: @extractor.camera_link(hash),
|
112
|
+
keywords: @extractor.keywords(hash),
|
113
|
+
day_link: @extractor.day_link(hash),
|
114
|
+
month_link: @extractor.month_link(hash),
|
115
|
+
year_link: @extractor.year_link(hash),
|
116
|
+
city_link: @extractor.city_link(hash),
|
117
|
+
state_link: @extractor.state_link(hash),
|
118
|
+
country_link: @extractor.country_link(hash),
|
119
|
+
prev_link: @extractor.prev_link(hash, selector),
|
120
|
+
next_link: @extractor.next_link(hash, selector),
|
121
|
+
include_prefix: Piccle::TemplateHelpers.include_prefix(selector),
|
122
|
+
canonical: "photos/#{hash}.html", # TODO: Other paths live in piccle.rake. Why's this one here?
|
123
|
+
site_metadata: site_metadata
|
124
|
+
}
|
125
|
+
|
126
|
+
photo_title = photo_data[:title] || ""
|
127
|
+
photo_title = "Photo" if photo_title.empty?
|
128
|
+
template_vars[:breadcrumbs] << { friendly_name: photo_title } if selector.any?
|
129
|
+
|
130
|
+
if Piccle.config.open_graph?
|
131
|
+
template_vars[:open_graph] = open_graph_for(title: photo_data[:title] || "A photo by #{Piccle.config.author_name}",
|
132
|
+
description: photo_data[:description],
|
133
|
+
image_url: "#{Piccle.config.home_url}images/photos/#{hash}.#{photo_data[:file_name]}",
|
134
|
+
width: photo_data[:width],
|
135
|
+
height: photo_data[:height],
|
136
|
+
page_url: "#{Piccle.config.home_url}/#{hash}.html")
|
137
|
+
end
|
138
|
+
|
139
|
+
template_vars
|
140
|
+
end
|
141
|
+
|
142
|
+
# Gets information about our site, used on pretty much every page.
|
143
|
+
def site_metadata
|
144
|
+
unless @cached_site_metadata
|
145
|
+
min_year = Piccle::Photo.earliest_photo_year
|
146
|
+
max_year = Piccle::Photo.latest_photo_year
|
147
|
+
copyright_year = if min_year == max_year
|
148
|
+
max_year
|
149
|
+
else
|
150
|
+
"#{min_year} – #{max_year}"
|
151
|
+
end
|
152
|
+
|
153
|
+
@cached_site_metadata = { author_name: Piccle.config.author_name, copyright_year: copyright_year }
|
154
|
+
end
|
155
|
+
@cached_site_metadata
|
156
|
+
end
|
157
|
+
|
158
|
+
# Returns a hash of open graph data based on the parameters passed in.
|
159
|
+
def open_graph_for(title: nil, description: nil, image_url: nil, image_alt: nil, width: nil, height: nil, page_url: nil)
|
160
|
+
open_graph = { title: title, url: page_url, image: { width: width, height: height, url: image_url } }
|
161
|
+
open_graph[:image][:image_alt] = image_alt if image_alt
|
162
|
+
open_graph[:description] = description if description
|
163
|
+
open_graph
|
164
|
+
end
|
165
|
+
|
166
|
+
# Returns a human-readable title for this site.
|
167
|
+
def site_title(breadcrumbs = [])
|
168
|
+
title = "Photography by #{Piccle.config.author_name}"
|
169
|
+
if breadcrumbs.any?
|
170
|
+
title += " - " + breadcrumbs.map { |b| b[:friendly_name] }.join(" - ")
|
171
|
+
end
|
172
|
+
title
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Streams are a self-contained way of faceting data for a photo.
|
4
|
+
|
5
|
+
class Piccle::Streams::BaseStream
|
6
|
+
#
|
7
|
+
def namespace
|
8
|
+
"by-foo"
|
9
|
+
end
|
10
|
+
|
11
|
+
# Returns a hash that contains data to merge for the given photo.
|
12
|
+
def data_for(photo)
|
13
|
+
{}
|
14
|
+
end
|
15
|
+
|
16
|
+
# def metadata_for(photo)
|
17
|
+
# [{}]
|
18
|
+
# end
|
19
|
+
|
20
|
+
# Reorder the data within this stream.
|
21
|
+
#
|
22
|
+
# The parser calls #order on every stream once it's loaded all the photos. You can reorder your data as you see fit.
|
23
|
+
# For instance, most general streams will want to order their subphotos by date - but if you're a keyword stream,
|
24
|
+
# you might also put the most popular keywords first.
|
25
|
+
#
|
26
|
+
# The default implementation organises facets with the most popular items first, and reorders the photos by date.
|
27
|
+
#
|
28
|
+
# Each stream is expected to only meddle within its own namespace, but this is not enforced.
|
29
|
+
def order(data)
|
30
|
+
if data.key?(namespace)
|
31
|
+
data[namespace] = data[namespace].sort_by(&length_sort_proc(data)).reverse.to_h
|
32
|
+
data[namespace].each do |k, v|
|
33
|
+
data[namespace][k][:photos] = data[namespace][k][:photos].sort_by(&date_sort_proc(data)).reverse if k.is_a?(String)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
data
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
# A sort proc designed for hashes. Sorts all string keys in order of how many photos they contain.
|
43
|
+
def length_sort_proc(data)
|
44
|
+
Proc.new { |k, v| k.is_a?(String) ? data.dig(namespace, k, :photos)&.length : 0 }
|
45
|
+
end
|
46
|
+
|
47
|
+
# A date sort designed for arrays. Sorts all photo hashes in order of the date they were taken.
|
48
|
+
def date_sort_proc(data)
|
49
|
+
Proc.new { |hash| data.dig(:photos, hash, :taken_at) || Time.new(1970, 1, 1) }
|
50
|
+
end
|
51
|
+
|
52
|
+
# Converts a sentence into something suitable for use in a URL slug.
|
53
|
+
def slugify(name)
|
54
|
+
name.downcase.gsub(/[^a-z0-9]+/, '-').gsub(/^-+/, '').gsub(/-+$/, '')
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Browse photos by camera.
|
4
|
+
class Piccle::Streams::CameraStream < Piccle::Streams::BaseStream
|
5
|
+
def namespace
|
6
|
+
"by-camera"
|
7
|
+
end
|
8
|
+
|
9
|
+
def data_for(photo)
|
10
|
+
{
|
11
|
+
namespace => {
|
12
|
+
:friendly_name => "By Camera",
|
13
|
+
:interesting => false,
|
14
|
+
slugify(camera_name(photo)) => {
|
15
|
+
friendly_name: camera_name(photo),
|
16
|
+
photos: [photo.md5]
|
17
|
+
},
|
18
|
+
}
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def metadata_for(photo)
|
23
|
+
[{
|
24
|
+
friendly_name: camera_name(photo),
|
25
|
+
type: :camera,
|
26
|
+
selector: [namespace, slugify(camera_name(photo))]
|
27
|
+
}]
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def camera_name(photo)
|
33
|
+
photo.camera_name || "unknown"
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Enables browsing photos by date.
|
4
|
+
|
5
|
+
class Piccle::Streams::DateStream < Piccle::Streams::BaseStream
|
6
|
+
def namespace
|
7
|
+
"by-date"
|
8
|
+
end
|
9
|
+
|
10
|
+
# Standard method called by the parser object. This should return a hash that contains sub-categories (optionally)
|
11
|
+
# and a list of :photos for each tier.
|
12
|
+
def data_for(photo)
|
13
|
+
year, month, day = photo.taken_at&.year, photo.taken_at&.month, photo.taken_at&.day
|
14
|
+
if year && month && day
|
15
|
+
{ namespace => {
|
16
|
+
:friendly_name => "By Date",
|
17
|
+
:interesting => false,
|
18
|
+
year.to_s => {
|
19
|
+
:friendly_name => "#{year}",
|
20
|
+
:interesting => false,
|
21
|
+
month.to_s => {
|
22
|
+
:friendly_name => "#{Date::MONTHNAMES[month]}",
|
23
|
+
:interesting => false,
|
24
|
+
day.to_s => {
|
25
|
+
:friendly_name => "#{day}#{ordinal_for(day)}",
|
26
|
+
:interesting => false,
|
27
|
+
:photos => [photo.md5]
|
28
|
+
},
|
29
|
+
:photos => [photo.md5]
|
30
|
+
},
|
31
|
+
photos: [photo.md5]
|
32
|
+
}
|
33
|
+
}}
|
34
|
+
else
|
35
|
+
{}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def metadata_for(photo)
|
40
|
+
year, month, day = photo.taken_at&.year, photo.taken_at&.month, photo.taken_at&.day
|
41
|
+
if year && month && day
|
42
|
+
[{ friendly_name: "#{day}#{ordinal_for(day)}",
|
43
|
+
type: :date_day,
|
44
|
+
selector: [namespace, year, month, day]
|
45
|
+
}, {
|
46
|
+
friendly_name: "#{Date::MONTHNAMES[month]}",
|
47
|
+
type: :date_month,
|
48
|
+
selector: [namespace, year, month]
|
49
|
+
}, {
|
50
|
+
friendly_name: year.to_s,
|
51
|
+
type: :date_year,
|
52
|
+
selector: [namespace, year]
|
53
|
+
}]
|
54
|
+
else
|
55
|
+
[]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Standard method called by the parser object. Gives this stream an option to re-order its data. The stream is on
|
60
|
+
# its honour to only meddle within its own namespace.
|
61
|
+
def order(data)
|
62
|
+
sort_proc = Proc.new { |k, v| k.is_a?(String) ? k : "" }
|
63
|
+
|
64
|
+
data[namespace] = data[namespace].sort_by(&sort_proc).to_h # Sort years
|
65
|
+
|
66
|
+
data[namespace].each do |year_k, v|
|
67
|
+
# Sort photos in each year, and then the month keys
|
68
|
+
if year_k.is_a?(String)
|
69
|
+
if data[namespace][year_k].key?(:photos)
|
70
|
+
data[namespace][year_k][:photos] = data[namespace][year_k][:photos].sort_by(&date_sort_proc(data)).reverse
|
71
|
+
end
|
72
|
+
data[namespace][year_k] = data[namespace][year_k].sort_by(&sort_proc).to_h
|
73
|
+
|
74
|
+
data[namespace][year_k].each do |month_k, v|
|
75
|
+
# Sort photos in each month, and then the days. TODO.
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
data
|
80
|
+
end
|
81
|
+
|
82
|
+
protected
|
83
|
+
|
84
|
+
def ordinal_for(num)
|
85
|
+
if num % 10 == 1 && num != 11
|
86
|
+
"st"
|
87
|
+
elsif num % 10 == 2 && num != 12
|
88
|
+
"nd"
|
89
|
+
elsif num % 10 == 3 && num != 13
|
90
|
+
"rd"
|
91
|
+
else
|
92
|
+
"th"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
# A special-case stream that handles named "events". You can define details in events.yaml - with things like a name,
|
4
|
+
# dates, and whether it should be collapsed or not on the front page.
|
5
|
+
|
6
|
+
class Piccle::Streams::EventStream < Piccle::Streams::BaseStream
|
7
|
+
attr_accessor :events
|
8
|
+
|
9
|
+
def namespace
|
10
|
+
"by-event"
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@events = if File.exist?(Piccle.config.events_file)
|
15
|
+
YAML.load_file(Piccle.config.events_file).map do |event| # Convert keys to symbols; bring dates to life.
|
16
|
+
event = event.map { |k, v| [k.to_sym, v] }.to_h
|
17
|
+
if event[:from].is_a? Date
|
18
|
+
event[:from] = DateTime.new(event[:from].year, event[:from].month, event[:from].day, 0, 0, 0)
|
19
|
+
end
|
20
|
+
if event[:to].is_a? Date
|
21
|
+
event[:to] = DateTime.new(event[:to].year, event[:to].month, event[:to].day, 23, 59, 59)
|
22
|
+
end
|
23
|
+
event
|
24
|
+
end
|
25
|
+
else
|
26
|
+
[]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def data_for(photo)
|
31
|
+
if photo.taken_at
|
32
|
+
relevant_events = @events.select { |ev| photo.taken_at.to_datetime >= ev[:from] && photo.taken_at.to_datetime <= ev[:to] }
|
33
|
+
result = { namespace => { friendly_name: "By Event", interesting: true }}
|
34
|
+
relevant_events.each do |ev|
|
35
|
+
result[namespace][slugify(ev[:name])] = { friendly_name: ev[:name], interesting: true, photos: [photo.md5] }
|
36
|
+
end
|
37
|
+
|
38
|
+
result
|
39
|
+
else
|
40
|
+
{}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Sorts most recent events first; then organises photos by date. TODO
|
45
|
+
def order(data)
|
46
|
+
super(data)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Given an event name, get a selector hash for this event.
|
50
|
+
def selector_for(name)
|
51
|
+
[namespace, slugify(name)]
|
52
|
+
end
|
53
|
+
|
54
|
+
# "Sentinels" are data hashes that mark the start and end of an event. We use them to render tiles in the overall
|
55
|
+
# index page, if we have photos for the declared event. Each event has an event_start and an event_end marker.
|
56
|
+
#
|
57
|
+
# Returns an array of 2 items: [event_starts, event_ends]
|
58
|
+
def sentinels_for(data)
|
59
|
+
event_starts = {}
|
60
|
+
event_ends = {}
|
61
|
+
@events.each do |event|
|
62
|
+
slug = slugify(event[:name])
|
63
|
+
if data.dig(namespace, slug, :photos)&.any?
|
64
|
+
most_recent_hash = data[namespace][slug][:photos].first
|
65
|
+
oldest_hash = data[namespace][slug][:photos].last # Event starts are the furthest back in time!
|
66
|
+
event_starts[oldest_hash] = { name: event[:name], selector: selector_for(event[:name]) }
|
67
|
+
event_ends[most_recent_hash] = { name: event[:name], selector: selector_for(event[:name]) }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
[event_starts, event_ends]
|
72
|
+
end
|
73
|
+
end
|