piccle 0.1.0.rc1 → 0.1.1.pre
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 +4 -4
- data/{agpl-3.0.md → LICENSE.md} +0 -0
- data/NOTES.md +8 -17
- data/README.md +66 -18
- data/assets/css/default.css +87 -19
- data/bin/piccle +31 -18
- data/db/migrations/006_create_people_and_join_table.rb +14 -0
- data/db/migrations/007_add_indexes.rb +14 -0
- data/lib/piccle.rb +3 -2
- data/lib/piccle/extractor.rb +14 -5
- data/lib/piccle/js_renderer.rb +4 -2
- data/lib/piccle/models/person.rb +6 -0
- data/lib/piccle/models/photo.rb +28 -4
- data/lib/piccle/parser.rb +7 -1
- data/lib/piccle/renderer.rb +61 -10
- data/lib/piccle/streams/base_stream.rb +7 -3
- data/lib/piccle/streams/date_stream.rb +1 -1
- data/lib/piccle/streams/event_stream.rb +34 -11
- data/lib/piccle/streams/keyword_stream.rb +2 -1
- data/lib/piccle/streams/person_stream.rb +16 -0
- data/lib/piccle/version.rb +1 -1
- data/templates/_header.handlebars.slim +8 -4
- data/templates/index.html.handlebars.slim +55 -13
- data/templates/show.html.handlebars.slim +59 -57
- metadata +7 -4
- data/.travis.yml +0 -5
@@ -0,0 +1,14 @@
|
|
1
|
+
Sequel.migration do
|
2
|
+
change do
|
3
|
+
create_table(:people) do
|
4
|
+
primary_key :id
|
5
|
+
String :name, null: false, unique: true
|
6
|
+
end
|
7
|
+
|
8
|
+
create_table(:people_photos) do
|
9
|
+
Integer :person_id, null: false
|
10
|
+
Integer :photo_id, null: false
|
11
|
+
primary_key [:person_id, :photo_id], name: :people_photos_pk
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
Sequel.migration do
|
2
|
+
change do
|
3
|
+
alter_table(:photos) do
|
4
|
+
add_index :md5, unique: true
|
5
|
+
add_index :file_name
|
6
|
+
add_index :path
|
7
|
+
add_index [:path, :file_name], unique: true
|
8
|
+
end
|
9
|
+
|
10
|
+
# Keywords and people are case-insensitive, enforce that in the DB
|
11
|
+
alter_table(:keywords) { add_index Sequel.function(:lower, :name), unique: true }
|
12
|
+
alter_table(:people) { add_index Sequel.function(:lower, :name), unique: true }
|
13
|
+
end
|
14
|
+
end
|
data/lib/piccle.rb
CHANGED
@@ -13,6 +13,7 @@ require "piccle/streams/date_stream"
|
|
13
13
|
require "piccle/streams/event_stream"
|
14
14
|
require "piccle/streams/keyword_stream"
|
15
15
|
require "piccle/streams/location_stream"
|
16
|
+
require "piccle/streams/person_stream"
|
16
17
|
require "piccle/template_helpers"
|
17
18
|
require "piccle/version"
|
18
19
|
|
@@ -38,9 +39,9 @@ module Piccle
|
|
38
39
|
def Piccle.const_missing(name)
|
39
40
|
Sequel::Model.db = Piccle.config.db
|
40
41
|
|
41
|
-
if %i[
|
42
|
+
if %i[Keyword Location Person Photo].include?(name)
|
42
43
|
Dir[Piccle.config.gem_root_join("lib", "piccle", "models", "*.rb")].each { |f| require f.delete_prefix("lib/").delete_suffix(".rb") }
|
43
|
-
models = [Piccle::
|
44
|
+
models = [Piccle::Keyword, Piccle::Location, Piccle::Person, Piccle::Photo]
|
44
45
|
models.each(&:finalize_associations)
|
45
46
|
models.each(&:freeze)
|
46
47
|
end
|
data/lib/piccle/extractor.rb
CHANGED
@@ -51,7 +51,7 @@ module Piccle
|
|
51
51
|
|
52
52
|
index = hashes.index(photo_hash)
|
53
53
|
if index && index > 0
|
54
|
-
"#{
|
54
|
+
"#{hashes[index-1]}.html"
|
55
55
|
else
|
56
56
|
nil
|
57
57
|
end
|
@@ -64,22 +64,31 @@ module Piccle
|
|
64
64
|
|
65
65
|
index = hashes.index(photo_hash)
|
66
66
|
if index && index < (hashes.length - 1)
|
67
|
-
"#{
|
67
|
+
"#{hashes[index + 1]}.html"
|
68
68
|
else
|
69
69
|
nil
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
|
+
# Gets the photos to show on the main index page. This is everything, except for photos within a collapsed event.
|
74
|
+
# We keep the first photo, but this is typically rendererd as a quilt tile rather than the photo itself.
|
75
|
+
def main_index_photos
|
76
|
+
all_photos = @parser.data[:photos]
|
77
|
+
collapsed_events = @parser.data[:events].select { |ev| ev[:collapsed] }
|
78
|
+
removable_hashes = collapsed_events.flat_map { |ev| @parser.subsection_photo_hashes(ev[:selector]).drop(1) }
|
79
|
+
all_photos.reject { |k, _v| removable_hashes.include?(k) }
|
80
|
+
end
|
81
|
+
|
73
82
|
# Gets a (currently top-level only) navigation structure. All entries have at least one photo.
|
74
83
|
def navigation
|
75
84
|
navigation_entries = @parser.faceted_data.map do |k, v|
|
76
85
|
{ friendly_name: v[:friendly_name],
|
77
|
-
|
86
|
+
min_for_nav: v[:min_for_nav],
|
87
|
+
entries: entries_for(v, k),
|
78
88
|
}
|
79
89
|
end
|
80
|
-
# A hack, to only show keywords that have more than one image.
|
81
90
|
navigation_entries.each do |nav_entry|
|
82
|
-
nav_entry[:entries].keep_if { |entry| entry[:photo_count]
|
91
|
+
nav_entry[:entries].keep_if { |entry| entry[:photo_count] >= nav_entry[:min_for_nav] } if nav_entry[:min_for_nav]
|
83
92
|
end
|
84
93
|
end
|
85
94
|
|
data/lib/piccle/js_renderer.rb
CHANGED
@@ -9,11 +9,13 @@ module Piccle
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def render_main_index
|
12
|
-
|
12
|
+
template_vars = paginate(render_main_index_template_vars)
|
13
|
+
template_vars.map { |page_template_vars| call_nodejs("index", page_template_vars) }
|
13
14
|
end
|
14
15
|
|
15
16
|
def render_index(selector)
|
16
|
-
|
17
|
+
template_vars = paginate(render_index_template_vars(selector))
|
18
|
+
template_vars.map { |page_template_vars| call_nodejs("index", page_template_vars) }
|
17
19
|
end
|
18
20
|
|
19
21
|
def render_photo(hash, selector = [])
|
data/lib/piccle/models/photo.rb
CHANGED
@@ -8,6 +8,7 @@ require 'json'
|
|
8
8
|
# Represents an image in the system. Reading info from an image? Inferring something based on the data? Put it here.
|
9
9
|
class Piccle::Photo < Sequel::Model
|
10
10
|
many_to_many :keywords
|
11
|
+
many_to_many :people
|
11
12
|
attr_accessor :changed_hash # Has this file been modified?
|
12
13
|
attr_accessor :freshly_created # Have we just generated this file?
|
13
14
|
|
@@ -29,7 +30,10 @@ class Piccle::Photo < Sequel::Model
|
|
29
30
|
photo.freshly_created = freshly_created
|
30
31
|
|
31
32
|
# Pull out keywords for this file, if it's new or changed.
|
32
|
-
|
33
|
+
if freshly_created || photo.changed_hash?
|
34
|
+
photo.sync_keywords
|
35
|
+
photo.sync_people
|
36
|
+
end
|
33
37
|
|
34
38
|
photo
|
35
39
|
end
|
@@ -183,16 +187,36 @@ class Piccle::Photo < Sequel::Model
|
|
183
187
|
end
|
184
188
|
|
185
189
|
# Read the keywords from the photo file, and ensure they're included in the DB.
|
186
|
-
|
187
|
-
def generate_keywords
|
190
|
+
def sync_keywords
|
188
191
|
exif_info = EXIFR::JPEG.new(original_photo_path)
|
189
192
|
xmp = XMP.parse(exif_info)
|
190
193
|
|
191
194
|
if xmp && xmp.namespaces && xmp.namespaces.include?("dc") && xmp.dc.attributes.include?("subject")
|
192
|
-
|
195
|
+
# Add new keywords
|
196
|
+
downcased_keywords = xmp.dc.subject.map(&:downcase)
|
197
|
+
downcased_keywords.each do |keyword|
|
193
198
|
keyword = Piccle::Keyword.find_or_create(name: keyword)
|
194
199
|
add_keyword(keyword) unless keywords.include?(keyword)
|
195
200
|
end
|
201
|
+
|
202
|
+
# Remove old keywords
|
203
|
+
keywords.each { |keyword| remove_keyword(keyword) unless downcased_keywords.include?(keyword.name.downcase) }
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def sync_people
|
208
|
+
exif_info = EXIFR::JPEG.new(original_photo_path)
|
209
|
+
xmp = XMP.parse(exif_info)
|
210
|
+
|
211
|
+
if xmp && xmp.namespaces && xmp.namespaces.include?("Iptc4xmpExt") && xmp.Iptc4xmpExt.attributes.include?("PersonInImage")
|
212
|
+
people_names = xmp.Iptc4xmpExt.PersonInImage
|
213
|
+
people_names.each do |name|
|
214
|
+
person = Piccle::Person.find(Sequel.ilike(:name, name)) || Piccle::Person.create(name: name)
|
215
|
+
add_person(person) unless people.include?(person)
|
216
|
+
end
|
217
|
+
|
218
|
+
downcased_names = people_names.map(&:downcase)
|
219
|
+
people.each { |person| remove_person(person) unless downcased_names.include?(person.name.downcase) }
|
196
220
|
end
|
197
221
|
end
|
198
222
|
|
data/lib/piccle/parser.rb
CHANGED
@@ -120,7 +120,8 @@ module Piccle
|
|
120
120
|
|
121
121
|
# Get photo hashes in a given subsection, given a diggable path.
|
122
122
|
def subsection_photo_hashes(subsection_path)
|
123
|
-
@data.dig(*subsection_path)
|
123
|
+
subsection = @data.dig(*subsection_path)
|
124
|
+
subsection ? subsection.fetch(:photos, []) : []
|
124
125
|
end
|
125
126
|
|
126
127
|
# Gets the actual photo objects for a given subsection.
|
@@ -132,6 +133,11 @@ module Piccle
|
|
132
133
|
end
|
133
134
|
end
|
134
135
|
|
136
|
+
# Determines whether the given subsection is marked as "collapsed". This is currently specific to event streams.
|
137
|
+
def subsection_collapsed?(subsection_path)
|
138
|
+
@data.dig(*subsection_path).fetch(:collapsed, false)
|
139
|
+
end
|
140
|
+
|
135
141
|
# Given an MD5 hash, returns an array of arrays. Each array is a set of strings that, combined with the MD5, gives a link to the photo.
|
136
142
|
# So for instance, with a date stream parser, if a photo was taken on 2016-04-19 you'll get back:
|
137
143
|
# [["by-date", "2016"], ["by-date", "2016", "4"], ["by-date", "2016", "4", "19"]]
|
data/lib/piccle/renderer.rb
CHANGED
@@ -12,12 +12,14 @@ module Piccle
|
|
12
12
|
# For instance, if selector was ["by-date", "2015"] you'd get an index page of photos
|
13
13
|
# for 2015 based on the data held by the parser.
|
14
14
|
def render_index(selector)
|
15
|
-
|
15
|
+
template_vars = paginate(render_index_template_vars(selector))
|
16
|
+
template_vars.map { |page_template_vars| Piccle::TemplateHelpers.render("index", page_template_vars) }
|
16
17
|
end
|
17
18
|
|
18
19
|
# Renders the "main" index – the front page of our site.
|
19
20
|
def render_main_index
|
20
|
-
|
21
|
+
template_vars = paginate(render_main_index_template_vars)
|
22
|
+
template_vars.map { |page_template_vars| Piccle::TemplateHelpers.render("index", page_template_vars) }
|
21
23
|
end
|
22
24
|
|
23
25
|
# Renders an Atom feed of the given subsection.
|
@@ -41,22 +43,30 @@ module Piccle
|
|
41
43
|
Piccle::TemplateHelpers.render("show", render_photo_template_vars(hash, selector))
|
42
44
|
end
|
43
45
|
|
46
|
+
# What should we call this index page? The first page is "index" (ie. index.html), later ones have a number
|
47
|
+
# (eg. "index-02"). NB! Our indexes are 0 based, but our pages are 1 based. (0 → index, 1 → index-02, 2 → index-03).
|
48
|
+
def index_page_name_for(index)
|
49
|
+
index.zero? ? "index" : "index-#{sprintf("%02d", index + 1)}"
|
50
|
+
end
|
51
|
+
|
44
52
|
protected
|
45
53
|
|
46
54
|
# Returns all the data we pass into the main index to render.
|
47
55
|
def render_main_index_template_vars
|
56
|
+
page_description = "A gallery of photos by #{Piccle.config.author_name}"
|
48
57
|
template_vars = {
|
49
|
-
photos: @
|
58
|
+
photos: @extractor.main_index_photos,
|
50
59
|
event_starts: @parser.data[:event_starts],
|
51
60
|
event_ends: @parser.data[:event_ends],
|
52
61
|
nav_items: @extractor.navigation,
|
53
|
-
site_metadata: site_metadata
|
62
|
+
site_metadata: site_metadata,
|
63
|
+
page_description: page_description
|
54
64
|
}
|
55
65
|
|
56
66
|
if Piccle.config.open_graph?
|
57
67
|
width, height = Piccle::QuiltGenerator.dimensions_for(@parser.data[:photos].length)
|
58
68
|
template_vars[:open_graph] = open_graph_for(title: site_title(),
|
59
|
-
description:
|
69
|
+
description: page_description,
|
60
70
|
image_url: "#{Piccle.config.home_url}quilt.jpg",
|
61
71
|
image_alt: "A quilt of the most recent images in this gallery.",
|
62
72
|
width: width,
|
@@ -70,6 +80,7 @@ module Piccle
|
|
70
80
|
def render_index_template_vars(selector)
|
71
81
|
breadcrumbs = @extractor.breadcrumbs_for(selector)
|
72
82
|
selector_path = "#{selector.join('/')}/"
|
83
|
+
page_description = "A gallery of photos by #{Piccle.config.author_name}"
|
73
84
|
template_vars = {
|
74
85
|
photos: @parser.subsection_photos(selector),
|
75
86
|
event_starts: [],
|
@@ -80,13 +91,14 @@ module Piccle
|
|
80
91
|
breadcrumbs: breadcrumbs,
|
81
92
|
site_url: Piccle.config.home_url,
|
82
93
|
include_prefix: Piccle::TemplateHelpers.include_prefix(selector),
|
83
|
-
site_metadata: site_metadata
|
94
|
+
site_metadata: site_metadata,
|
95
|
+
page_description: page_description
|
84
96
|
}
|
85
97
|
|
86
98
|
if Piccle.config.open_graph?
|
87
99
|
width, height = Piccle::QuiltGenerator.dimensions_for(@parser.subsection_photo_hashes(selector).length)
|
88
100
|
template_vars[:open_graph] = open_graph_for(title: site_title(breadcrumbs),
|
89
|
-
description:
|
101
|
+
description: page_description,
|
90
102
|
image_url: "#{Piccle.config.home_url}#{selector_path}quilt.jpg",
|
91
103
|
image_alt: "A quilt of the most recent images in this gallery.",
|
92
104
|
width: width,
|
@@ -97,10 +109,48 @@ module Piccle
|
|
97
109
|
template_vars
|
98
110
|
end
|
99
111
|
|
112
|
+
# Given a set of template variables, paginate them. Specifically:
|
113
|
+
# - Take the :photos key, and break it into chunks
|
114
|
+
# - Add a :pagination key with details of all the pages
|
115
|
+
# - Returns an array of template vars.
|
116
|
+
def paginate(template_vars)
|
117
|
+
slices = template_vars[:photos].each_slice(100)
|
118
|
+
if slices.count > 1
|
119
|
+
pages = (0...slices.count).map { |i| { label: i + 1, page_name: index_page_name_for(i) } }
|
120
|
+
|
121
|
+
slices.map.with_index do |slice, i|
|
122
|
+
pages_with_current = pages.map.with_index { |page, index| page.merge(is_current: (i == index)) }
|
123
|
+
is_first = i.zero?
|
124
|
+
is_last = i == slices.count - 1
|
125
|
+
|
126
|
+
new_vars = { photos: slice.to_h }
|
127
|
+
new_vars[:prev_link] = "#{index_page_name_for(i-1)}.html" unless is_first
|
128
|
+
new_vars[:next_link] = "#{index_page_name_for(i+1)}.html" unless is_last
|
129
|
+
|
130
|
+
pagination = { pagination: { page_name: index_page_name_for(i),
|
131
|
+
pages: pages_with_current,
|
132
|
+
is_first_page: is_first,
|
133
|
+
is_last_page: is_last,
|
134
|
+
previous_page_name: index_page_name_for(i-1),
|
135
|
+
next_page_name: index_page_name_for(i+1),
|
136
|
+
total_pages: slices.count }}
|
137
|
+
|
138
|
+
template_vars.merge(new_vars, pagination)
|
139
|
+
end
|
140
|
+
else # Just the one page, no point in adding any pagination.
|
141
|
+
[template_vars]
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
100
145
|
# Returns all the template vars we use to render a photo page.
|
101
146
|
def render_photo_template_vars(hash, selector)
|
102
147
|
photo_data = @parser.data[:photos][hash]
|
103
148
|
substreams = [@parser.substream_for(hash)] + @parser.links_for(hash).map { |selector| @parser.interesting_substream_for(hash, selector) }.compact
|
149
|
+
page_description = if photo_data[:description] && photo_data[:description].strip.length > 0
|
150
|
+
photo_data[:description]
|
151
|
+
else
|
152
|
+
"A photo by #{Piccle.config.author_name}"
|
153
|
+
end
|
104
154
|
|
105
155
|
template_vars = {
|
106
156
|
photo: photo_data,
|
@@ -119,8 +169,9 @@ module Piccle
|
|
119
169
|
prev_link: @extractor.prev_link(hash, selector),
|
120
170
|
next_link: @extractor.next_link(hash, selector),
|
121
171
|
include_prefix: Piccle::TemplateHelpers.include_prefix(selector),
|
122
|
-
canonical: "
|
123
|
-
site_metadata: site_metadata
|
172
|
+
canonical: "#{hash}.html",
|
173
|
+
site_metadata: site_metadata,
|
174
|
+
page_description: page_description
|
124
175
|
}
|
125
176
|
|
126
177
|
photo_title = photo_data[:title] || ""
|
@@ -129,7 +180,7 @@ module Piccle
|
|
129
180
|
|
130
181
|
if Piccle.config.open_graph?
|
131
182
|
template_vars[:open_graph] = open_graph_for(title: photo_data[:title] || "A photo by #{Piccle.config.author_name}",
|
132
|
-
description:
|
183
|
+
description: page_description,
|
133
184
|
image_url: "#{Piccle.config.home_url}images/photos/#{hash}.#{photo_data[:file_name]}",
|
134
185
|
width: photo_data[:width],
|
135
186
|
height: photo_data[:height],
|
@@ -28,7 +28,7 @@ class Piccle::Streams::BaseStream
|
|
28
28
|
# Each stream is expected to only meddle within its own namespace, but this is not enforced.
|
29
29
|
def order(data)
|
30
30
|
if data.key?(namespace)
|
31
|
-
data[namespace] = data[namespace].sort_by(&length_sort_proc(data)).
|
31
|
+
data[namespace] = data[namespace].sort_by(&length_sort_proc(data)).to_h
|
32
32
|
data[namespace].each do |k, v|
|
33
33
|
data[namespace][k][:photos] = data[namespace][k][:photos].sort_by(&date_sort_proc(data)).reverse if k.is_a?(String)
|
34
34
|
end
|
@@ -39,9 +39,13 @@ class Piccle::Streams::BaseStream
|
|
39
39
|
|
40
40
|
protected
|
41
41
|
|
42
|
-
# A sort proc designed for hashes. Sorts all string keys in order of how many photos they contain.
|
42
|
+
# A sort proc designed for hashes. Sorts all string keys in order of how many photos they contain, then alphabetically.
|
43
43
|
def length_sort_proc(data)
|
44
|
-
Proc.new
|
44
|
+
Proc.new do |k, v|
|
45
|
+
length = k.is_a?(String) ? data.dig(namespace, k, :photos)&.length || 0 : 0
|
46
|
+
name = k.is_a?(String) ? k.downcase : ""
|
47
|
+
[length * -1, name]
|
48
|
+
end
|
45
49
|
end
|
46
50
|
|
47
51
|
# A date sort designed for arrays. Sorts all photo hashes in order of the date they were taken.
|
@@ -61,7 +61,7 @@ class Piccle::Streams::DateStream < Piccle::Streams::BaseStream
|
|
61
61
|
def order(data)
|
62
62
|
sort_proc = Proc.new { |k, v| k.is_a?(String) ? k : "" }
|
63
63
|
|
64
|
-
data[namespace] = data[namespace].sort_by(&sort_proc).to_h # Sort years
|
64
|
+
data[namespace] = data[namespace].sort_by(&sort_proc).reverse.to_h # Sort years
|
65
65
|
|
66
66
|
data[namespace].each do |year_k, v|
|
67
67
|
# Sort photos in each year, and then the month keys
|
@@ -14,13 +14,8 @@ class Piccle::Streams::EventStream < Piccle::Streams::BaseStream
|
|
14
14
|
@events = if File.exist?(Piccle.config.events_file)
|
15
15
|
YAML.load_file(Piccle.config.events_file).map do |event| # Convert keys to symbols; bring dates to life.
|
16
16
|
event = event.map { |k, v| [k.to_sym, v] }.to_h
|
17
|
-
|
18
|
-
|
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
|
17
|
+
event[:selector] = selector_for(event[:name])
|
18
|
+
transform_dates(event)
|
24
19
|
end
|
25
20
|
else
|
26
21
|
[]
|
@@ -32,7 +27,8 @@ class Piccle::Streams::EventStream < Piccle::Streams::BaseStream
|
|
32
27
|
relevant_events = @events.select { |ev| photo.taken_at.to_datetime >= ev[:from] && photo.taken_at.to_datetime <= ev[:to] }
|
33
28
|
result = { namespace => { friendly_name: "By Event", interesting: true }}
|
34
29
|
relevant_events.each do |ev|
|
35
|
-
result[namespace][slugify(ev[:name])] = { friendly_name: ev[:name], interesting: true, photos: [photo.md5]
|
30
|
+
result[namespace][slugify(ev[:name])] = { friendly_name: ev[:name], interesting: true, photos: [photo.md5],
|
31
|
+
collapsed: ev[:collapsed], sort_date: ev[:from] }
|
36
32
|
end
|
37
33
|
|
38
34
|
result
|
@@ -43,7 +39,14 @@ class Piccle::Streams::EventStream < Piccle::Streams::BaseStream
|
|
43
39
|
|
44
40
|
# Sorts most recent events first; then organises photos by date. TODO
|
45
41
|
def order(data)
|
46
|
-
|
42
|
+
if data.key?(namespace)
|
43
|
+
data[namespace] = data[namespace].sort_by { |k, v| k.is_a?(String) ? v[:sort_date] : DateTime.new(1826, 1, 1) }.reverse.to_h
|
44
|
+
data[namespace].each do |k, v|
|
45
|
+
data[namespace][k][:photos] = data[namespace][k][:photos].sort_by(&date_sort_proc(data)).reverse if k.is_a?(String)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
data
|
47
50
|
end
|
48
51
|
|
49
52
|
# Given an event name, get a selector hash for this event.
|
@@ -63,11 +66,31 @@ class Piccle::Streams::EventStream < Piccle::Streams::BaseStream
|
|
63
66
|
if data.dig(namespace, slug, :photos)&.any?
|
64
67
|
most_recent_hash = data[namespace][slug][:photos].first
|
65
68
|
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]) }
|
69
|
+
event_starts[oldest_hash] = { name: event[:name], selector: selector_for(event[:name]), collapsed: event[:collapsed] }
|
70
|
+
event_ends[most_recent_hash] = { name: event[:name], selector: selector_for(event[:name]), collapsed: event[:collapsed] }
|
68
71
|
end
|
69
72
|
end
|
70
73
|
|
71
74
|
[event_starts, event_ends]
|
72
75
|
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
|
79
|
+
# Given an event, munge the dates appropriately.
|
80
|
+
# - If we have an "at" date specified, make the from/to fields match the start and end of the day.
|
81
|
+
# - If we have "from"/"to" specified as dates, line them up with the start/end of the day.
|
82
|
+
def transform_dates(event)
|
83
|
+
if event[:at]
|
84
|
+
event[:from] = DateTime.new(event[:at].year, event[:at].month, event[:at].day, 0, 0, 0)
|
85
|
+
event[:to] = DateTime.new(event[:at].year, event[:at].month, event[:at].day, 23, 59, 59)
|
86
|
+
elsif event[:from] && event[:to]
|
87
|
+
if event[:from].is_a? Date
|
88
|
+
event[:from] = DateTime.new(event[:from].year, event[:from].month, event[:from].day, 0, 0, 0)
|
89
|
+
end
|
90
|
+
if event[:to].is_a? Date
|
91
|
+
event[:to] = DateTime.new(event[:to].year, event[:to].month, event[:to].day, 23, 59, 59)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
event
|
95
|
+
end
|
73
96
|
end
|