hanreki 0.0.1 → 0.1.0
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/.gitignore +1 -0
- data/.travis.yml +7 -1
- data/LICENSE +21 -0
- data/README.md +12 -13
- data/circle.yml +8 -0
- data/hanreki.gemspec +4 -0
- data/lib/hanreki/cli.rb +60 -0
- data/lib/hanreki/event.rb +125 -0
- data/lib/hanreki/i_calendar.rb +55 -0
- data/lib/hanreki/schedule.rb +131 -0
- data/lib/hanreki/time_util.rb +39 -0
- data/lib/hanreki/version.rb +1 -1
- data/lib/hanreki.rb +3 -0
- metadata +25 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ce2183461786fb22b00f52b7717e8c378b3ee42
|
4
|
+
data.tar.gz: e8989a1d2a10e6b4c54bdcfdd22751c558fb84af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 56ab148a883d11a01bb45a789555aea9e22334638954d709af39b49ed459a626fdc5fd4c58a7ad0d1b31b0d969a35c255dc0b001e07cf1be30fd8d5e09cfacde
|
7
|
+
data.tar.gz: 957ee0ac1079a57dc19f1b25bcda5d04988726d54ce003ba5439c98c48e6da44bc9c7b45d378949fb55d8cbef8373d3586269040028e10d8d1d57a9d1e2c51fa
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
sudo: false
|
2
2
|
language: ruby
|
3
3
|
rvm:
|
4
|
+
- 2.1
|
5
|
+
- 2.2
|
4
6
|
- 2.3.1
|
5
|
-
before_install:
|
7
|
+
before_install:
|
8
|
+
- gem install bundler
|
9
|
+
notifications:
|
10
|
+
slack:
|
11
|
+
secure: SqYPNEedzTJqs9XHn0SW1RvPVG2GPcMJWMrE4hg35WnzZk7rSk9DbHqjjZo1+qAS8MF/du68KKPRJe1KewerK+Zub2wwsFAR2Wvp0Elrq2fgKKNMY67ulG+ET6f2Mz9Uij83CO9fBxbCXxqja+nTbHZhx7YM5B9KSAqwNs6DYahxtueLiqK4y4YVULt/KUvo9mA2gjIbeTZE0TZ/se0pXVtunh/UFW+CE34rkTT3zoUtk4CcCO/wMXu56vwqyHluwwmhVVjvMB96WYrJ3O2R9ef3Cm9rfzAeyJ0LDBddOwr+z4ZsI2Yz+b270bM6F1Mb6sDO48WGi12u8JRRGECy/GA8R93Jo6mnC+cEPx50XElPE5njbI56LRBuzAjimEyxpwfA9UWBgRDGmHyEc3B16ri/DKknEE+YxEvsEfgKSt012sXBS0gYO6jIz318GMxdj6+EYYVJXwJMj8MZlCUtvfgqq4qQwk/vBDK9BAcfDjIbayWLEnenXJWnRwW7gVn4hQG4qx+TUUZDcgiZ+adFcMLZjvd500GSOeV8H+4U5pR0MWIzuC1MTAIYB4E7Tma2QQqU2dG3ztHqo4ubApTb/qBpcYrFOjCBPT2ItGC85mSlGMs4vgMnVUGyFaLR5u4PYe6Hd/uNfE6i8ayJ2E1STFVUuwD0EdOXqEZT6ulnvD4=
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 CAMPHOR-
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
CHANGED
@@ -1,12 +1,18 @@
|
|
1
1
|
# Hanreki
|
2
2
|
|
3
|
-
|
3
|
+
[](https://travis-ci.org/camphor-/hanreki)
|
4
4
|
|
5
|
-
|
5
|
+
Simple schedule manager for [CAMPHOR-](https://camph.net/)
|
6
6
|
|
7
7
|
## Installation
|
8
|
+
### Gem
|
9
|
+
Install hanreki with gem command:
|
8
10
|
|
9
|
-
|
11
|
+
$ gem install hanreki
|
12
|
+
$ hanreki
|
13
|
+
|
14
|
+
### Bundler
|
15
|
+
You can also install hanreki with bundler. Add this line to your Gemfile:
|
10
16
|
|
11
17
|
```ruby
|
12
18
|
gem 'hanreki'
|
@@ -15,22 +21,15 @@ gem 'hanreki'
|
|
15
21
|
And then execute:
|
16
22
|
|
17
23
|
$ bundle
|
18
|
-
|
19
|
-
Or install it yourself as:
|
20
|
-
|
21
|
-
$ gem install hanreki
|
24
|
+
$ bundle exec hanreki
|
22
25
|
|
23
26
|
## Usage
|
24
|
-
|
25
|
-
TODO: Write usage instructions here
|
27
|
+
TODO
|
26
28
|
|
27
29
|
## Development
|
28
|
-
|
29
30
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
30
31
|
|
31
32
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
33
|
|
33
34
|
## Contributing
|
34
|
-
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/hanreki.
|
36
|
-
|
35
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/camphor-/hanreki.
|
data/circle.yml
ADDED
data/hanreki.gemspec
CHANGED
@@ -12,13 +12,17 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.summary = 'Simple schedule manager for CAMPHOR-'
|
13
13
|
spec.description = 'Simple schedule manager for CAMPHOR-'
|
14
14
|
spec.homepage = 'https://github.com/camphor-/hanreki'
|
15
|
+
spec.license = 'MIT'
|
15
16
|
|
16
17
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
18
|
spec.bindir = 'exe'
|
18
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
20
|
spec.require_paths = ['lib']
|
20
21
|
|
22
|
+
spec.required_ruby_version = '>= 2.1.0'
|
23
|
+
|
21
24
|
spec.add_dependency 'thor', '~> 0.19'
|
25
|
+
spec.add_dependency 'icalendar', '~> 2.3'
|
22
26
|
|
23
27
|
spec.add_development_dependency 'bundler', '~> 1.12'
|
24
28
|
spec.add_development_dependency 'rake', '~> 10.0'
|
data/lib/hanreki/cli.rb
CHANGED
@@ -1,6 +1,66 @@
|
|
1
|
+
require 'date'
|
1
2
|
require 'thor'
|
3
|
+
require 'hanreki/schedule'
|
4
|
+
require 'hanreki/time_util'
|
2
5
|
|
3
6
|
module Hanreki
|
4
7
|
class CLI < Thor
|
8
|
+
using ExtendTime
|
9
|
+
|
10
|
+
desc 'blank --date [YYYYMM]', 'Generate a blank master file'
|
11
|
+
method_option :date, type: :numeric
|
12
|
+
def blank
|
13
|
+
year, month = parse_month_in_options(options, Date.today.next_month)
|
14
|
+
schedule = Schedule.initialize_for_month(year, month)
|
15
|
+
raise 'file already exists' if schedule.master_file_exists?
|
16
|
+
schedule.out_master
|
17
|
+
end
|
18
|
+
|
19
|
+
desc 'edit --date [YYYYMM]', 'Open a master file in the editor'
|
20
|
+
method_option :date, type: :numeric
|
21
|
+
def edit
|
22
|
+
year, month = parse_month_in_options(options, Date.today)
|
23
|
+
master_file = sprintf('master/%04d%02d.csv', year, month)
|
24
|
+
raise 'file does not exist' unless File.exists? master_file
|
25
|
+
|
26
|
+
editor = ENV['CAMPHOR_SCHEDULE_EDITOR'] || ENV['EDITOR'] || 'vi'
|
27
|
+
system "#{editor} #{master_file}"
|
28
|
+
validate
|
29
|
+
end
|
30
|
+
|
31
|
+
desc 'sync', 'Sync iCal, JSON, and JSONP'
|
32
|
+
def sync
|
33
|
+
schedule = Schedule.new
|
34
|
+
schedule.load_master_files
|
35
|
+
schedule.out_ical
|
36
|
+
schedule.out_json
|
37
|
+
schedule.out_jsonp
|
38
|
+
end
|
39
|
+
|
40
|
+
desc 'validate', 'Validate master files'
|
41
|
+
def validate
|
42
|
+
schedule = Schedule.new
|
43
|
+
begin
|
44
|
+
schedule.load_master_files
|
45
|
+
rescue => e
|
46
|
+
raise "validation error: #{e}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# Parse a string (YYYYMM) in options[:date] and returns [YYYY, MM]
|
53
|
+
def parse_month_in_options(options, default = nil)
|
54
|
+
date = options[:date]
|
55
|
+
if options[:date]
|
56
|
+
begin
|
57
|
+
Time.parse_month(options[:date].to_s)
|
58
|
+
rescue ArgumentError => e
|
59
|
+
raise "invalid date option: #{e.message}"
|
60
|
+
end
|
61
|
+
elsif default
|
62
|
+
[default.year, default.month]
|
63
|
+
end
|
64
|
+
end
|
5
65
|
end
|
6
66
|
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'icalendar'
|
2
|
+
require 'hanreki/time_util'
|
3
|
+
|
4
|
+
# Event of CAMPHOR- Schedule
|
5
|
+
class Event
|
6
|
+
using ExtendTime
|
7
|
+
WDAYS = %w(日 月 火 水 木 金 土)
|
8
|
+
attr_accessor :start, :end
|
9
|
+
attr_accessor :public_summary, :private_summary
|
10
|
+
attr_accessor :url
|
11
|
+
|
12
|
+
def initialize(attributes)
|
13
|
+
# Create assign methods for event attributes
|
14
|
+
attributes.each do |attribute, v|
|
15
|
+
send("#{attribute}=", v) if respond_to?("#{attribute}=")
|
16
|
+
end if attributes
|
17
|
+
yield self if block_given?
|
18
|
+
end
|
19
|
+
|
20
|
+
# マスターファイルの1行を読み込む処理
|
21
|
+
# CSV を読む混む際にしかチェックできないバリデーションはここで行う.
|
22
|
+
# その他のバリデーションは #validate で行う.
|
23
|
+
# month: String (e.g. 201510)
|
24
|
+
# line: CSV::Row
|
25
|
+
# (eg. [01, 水, 15:00, 20:00, "HOUSE 開館", "HOUSE 当番 @ivstivs", https://camph.net])
|
26
|
+
def self.from_master(month, line)
|
27
|
+
event = Event.new({})
|
28
|
+
first_day = Date.parse("#{month}01")
|
29
|
+
day, _, hour_start, hour_end, public_summary, private_summary, url = line.fields.map { |c| c.to_s.strip }
|
30
|
+
# Zero-fill (eg. 1 -> 01, 02 -> 02)
|
31
|
+
day = day.rjust(2, "0")
|
32
|
+
raise ArgumentError, 'invalid day' unless day.length == 2
|
33
|
+
raise ArgumentError, 'invalid day' unless (first_day...first_day.next_month).include?(Date.parse("#{month}#{day}"))
|
34
|
+
event.start = Time.parse("#{month}#{day} #{hour_start} +09:00")
|
35
|
+
event.end = Time.parse("#{month}#{day} #{hour_end} +09:00")
|
36
|
+
event.public_summary = public_summary unless public_summary.empty?
|
37
|
+
event.private_summary = private_summary unless private_summary.empty?
|
38
|
+
event.url = url unless url.empty?
|
39
|
+
event.validate
|
40
|
+
event
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_h(type, time_type = :time)
|
44
|
+
fail ArgumentError, 'Invalid type' unless [:public, :private].include?(type)
|
45
|
+
fail ArgumentError, 'Invalid time_type' unless [:time, :string].include?(time_type)
|
46
|
+
hash = {
|
47
|
+
start: @start,
|
48
|
+
end: @end,
|
49
|
+
url: @url
|
50
|
+
}
|
51
|
+
case type
|
52
|
+
when :public then hash[:title] = @public_summary
|
53
|
+
when :private then hash[:title] = @private_summary
|
54
|
+
end
|
55
|
+
if time_type == :string
|
56
|
+
hash[:start] = hash[:start].iso8601
|
57
|
+
hash[:end] = hash[:end].iso8601
|
58
|
+
end
|
59
|
+
hash
|
60
|
+
end
|
61
|
+
|
62
|
+
# Format for master CSV files
|
63
|
+
def to_master
|
64
|
+
wday = WDAYS[@start.wday]
|
65
|
+
date_str = @start.strftime('%d')
|
66
|
+
start_string = @start.strftime('%R')
|
67
|
+
end_string = @end.strftime('%R')
|
68
|
+
[date_str, wday, start_string, end_string, @public_summary, @private_summary, @url]
|
69
|
+
end
|
70
|
+
|
71
|
+
def private?
|
72
|
+
!@private_summary.nil?
|
73
|
+
end
|
74
|
+
|
75
|
+
def public?
|
76
|
+
!@public_summary.nil?
|
77
|
+
end
|
78
|
+
|
79
|
+
# イベントのバリデーションを行う
|
80
|
+
# バリデーションに成功した場合は true を返し, 失敗した場合は例外を送出する
|
81
|
+
def validate
|
82
|
+
raise ArgumentError, 'invalid start & end' if @start > @end
|
83
|
+
|
84
|
+
if @public_summary.nil? and @private_summary.nil?
|
85
|
+
raise ArgumentError, 'both summaries are not set'
|
86
|
+
end
|
87
|
+
|
88
|
+
if @private_summary == 'Closed'
|
89
|
+
if not @public_summary.nil?
|
90
|
+
raise ArgumentError, 'invalid public summary for a closed event'
|
91
|
+
end
|
92
|
+
|
93
|
+
if not @start.is_midnight_in_jst
|
94
|
+
raise ArgumentError, 'start must be 0:00 for a closed event'
|
95
|
+
end
|
96
|
+
|
97
|
+
if not @end.is_midnight_in_jst
|
98
|
+
raise ArgumentError, 'end must be 0:00 for a closed event'
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
unless @url.nil?
|
103
|
+
unless @url.start_with?('http://') or @url.start_with?('https://')
|
104
|
+
raise ArgumentError, 'invalid url scheme'
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
true
|
109
|
+
end
|
110
|
+
|
111
|
+
# Header for master CSV files
|
112
|
+
def self.master_header
|
113
|
+
%w(day day_of_week hour_start hour_end public_summary private_summary url)
|
114
|
+
end
|
115
|
+
|
116
|
+
def ==(other)
|
117
|
+
self.class == other.class && self.state == other.state
|
118
|
+
end
|
119
|
+
|
120
|
+
protected
|
121
|
+
|
122
|
+
def state
|
123
|
+
[@start, @end, @public_summary, @private_summary, @url]
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'icalendar'
|
2
|
+
require 'hanreki/time_util'
|
3
|
+
|
4
|
+
# iCal
|
5
|
+
class ICalendar
|
6
|
+
using ExtendTime
|
7
|
+
def initialize(file = nil)
|
8
|
+
@calendar = Icalendar::Calendar.new
|
9
|
+
if file
|
10
|
+
@calendar = Icalendar.parse(file).first
|
11
|
+
else
|
12
|
+
make_blank
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
@calendar.publish
|
18
|
+
@calendar.to_ical
|
19
|
+
end
|
20
|
+
|
21
|
+
def set_event(event, type)
|
22
|
+
fail ArgumentError, 'Invalid date' unless event.start && event.end
|
23
|
+
fail ArgumentError, 'Invalid type' unless [:public, :private].include?(type)
|
24
|
+
@calendar.event do |e|
|
25
|
+
e.dtstart = event.start
|
26
|
+
e.dtend = event.end
|
27
|
+
e.url = event.url
|
28
|
+
case type
|
29
|
+
when :public then e.summary = event.public_summary
|
30
|
+
when :private then e.summary = event.private_summary
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Generate new calendar
|
38
|
+
def make_blank
|
39
|
+
@calendar.append_custom_property('X-WR-CALNAME', 'CAMPHOR- Events')
|
40
|
+
set_timezone
|
41
|
+
end
|
42
|
+
|
43
|
+
def set_timezone
|
44
|
+
# TODO: Rewrite with tzinfo
|
45
|
+
@calendar.timezone do |t|
|
46
|
+
t.tzid = 'Asia/Tokyo'
|
47
|
+
t.standard do |s|
|
48
|
+
s.tzoffsetfrom = '+0900'
|
49
|
+
s.tzoffsetto = '+0900'
|
50
|
+
s.tzname = 'JST'
|
51
|
+
s.dtstart = '19700101T000000'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'csv'
|
2
|
+
require 'date'
|
3
|
+
require 'json'
|
4
|
+
require 'hanreki/event'
|
5
|
+
require 'hanreki/i_calendar'
|
6
|
+
require 'hanreki/time_util'
|
7
|
+
|
8
|
+
# Schedule for CAMPHOR- HOUSE assignments and other events
|
9
|
+
class Schedule
|
10
|
+
using ExtendTime
|
11
|
+
ICAL_PUBLIC_PATH = 'ical/camphor_public_events.ics'
|
12
|
+
ICAL_PRIVATE_PATH = 'ical/camphor_private_events.ics'
|
13
|
+
JSON_PUBLIC_PATH = 'json/camphor_public_events.json'
|
14
|
+
JSON_PRIVATE_PATH = 'json/camphor_private_events.json'
|
15
|
+
JSONP_PUBLIC_PATH = 'jsonp/camphor_public_events.js'
|
16
|
+
JSONP_PRIVATE_PATH = 'jsonp/camphor_private_events.js'
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
# マスターファイルを生成する際のファイル名の決定に使用
|
20
|
+
@first_day_of_month = nil
|
21
|
+
@events = []
|
22
|
+
end
|
23
|
+
|
24
|
+
# 指定された月の初期割り当てを @events に設定する
|
25
|
+
def self.initialize_for_month(year, month)
|
26
|
+
schedule = Schedule.new
|
27
|
+
schedule.send(:initialize_assignments, year, month)
|
28
|
+
schedule
|
29
|
+
end
|
30
|
+
|
31
|
+
# マスターファイルを読み込んで @events に設定する
|
32
|
+
def load_master_files
|
33
|
+
@events = Dir.glob('master/*.csv').flat_map do |f|
|
34
|
+
month = f.match(/master\/(\d+).csv/)[1]
|
35
|
+
CSV.open(f, headers: true) do |csv|
|
36
|
+
csv.map { |line| Event.from_master(month, line) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Output private and public iCal calendar files
|
42
|
+
def out_ical
|
43
|
+
File.open(ICAL_PUBLIC_PATH, 'w') do |f|
|
44
|
+
ical = ICalendar.new
|
45
|
+
public_events.each { |event| ical.set_event(event, :public) }
|
46
|
+
f.write(ical)
|
47
|
+
end
|
48
|
+
File.open(ICAL_PRIVATE_PATH, 'w') do |f|
|
49
|
+
ical = ICalendar.new
|
50
|
+
private_events.each { |event| ical.set_event(event, :private) }
|
51
|
+
f.write(ical)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Output private and public JSON calendar files
|
56
|
+
def out_json
|
57
|
+
File.open(JSON_PUBLIC_PATH, 'w') do |f|
|
58
|
+
JSON.dump(public_events.map { |event| event.to_h(:public, :string) } , f)
|
59
|
+
end
|
60
|
+
File.open(JSON_PRIVATE_PATH, 'w') do |f|
|
61
|
+
JSON.dump(private_events.map { |event| event.to_h(:private, :string) } , f)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Output private and public JSONP calendar files
|
66
|
+
def out_jsonp(callback = 'callback')
|
67
|
+
File.open(JSONP_PUBLIC_PATH, 'w') do |f|
|
68
|
+
events = public_events.map { |event| event.to_h(:public, :string) }
|
69
|
+
f.write("#{callback}(#{events.to_json});")
|
70
|
+
end
|
71
|
+
File.open(JSONP_PRIVATE_PATH, 'w') do |f|
|
72
|
+
events = private_events.map { |event| event.to_h(:private, :string) }
|
73
|
+
f.write("#{callback}(#{events.to_json});")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Output master file
|
78
|
+
def out_master
|
79
|
+
CSV.open(
|
80
|
+
file_path, 'w',
|
81
|
+
headers: Event.master_header, write_headers: true
|
82
|
+
) do |master_file|
|
83
|
+
@events.each { |event| master_file << event.to_master }
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def master_file_exists?
|
88
|
+
File.exists? file_path
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def file_path
|
94
|
+
"master/#{@first_day_of_month.strftime('%Y%m')}.csv".freeze
|
95
|
+
end
|
96
|
+
|
97
|
+
def initialize_assignments(year, month)
|
98
|
+
@first_day_of_month =
|
99
|
+
begin
|
100
|
+
Date.new(year, month, 1)
|
101
|
+
rescue TypeError
|
102
|
+
raise ArgumentError, 'Invalid year and month'
|
103
|
+
end
|
104
|
+
|
105
|
+
days = @first_day_of_month...@first_day_of_month.next_month
|
106
|
+
@events = days.map { |day| default_assignment(day) }
|
107
|
+
end
|
108
|
+
|
109
|
+
def default_assignment(date)
|
110
|
+
# Set timezone explicitly (DO NOT use date.to_time, it uses local timezone)
|
111
|
+
time = Time.new(date.year, date.month, date.day, 0, 0, 0, '+09:00')
|
112
|
+
start, _end, public_summary, private_summary =
|
113
|
+
case time.wday
|
114
|
+
when 0 then [time.next_day(0), time.next_day(1), '', 'Closed']
|
115
|
+
when 1..5 then [time.next_hour(15), time.next_hour(20), 'Open', '@']
|
116
|
+
when 6 then [time.next_hour(13), time.next_hour(20), 'Open', '@']
|
117
|
+
end
|
118
|
+
Event.new(
|
119
|
+
start: start, end: _end,
|
120
|
+
public_summary: public_summary,
|
121
|
+
private_summary: private_summary)
|
122
|
+
end
|
123
|
+
|
124
|
+
def public_events
|
125
|
+
@events.select(&:public?)
|
126
|
+
end
|
127
|
+
|
128
|
+
def private_events
|
129
|
+
@events.select(&:private?)
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
# Add some useful methods to Time, provided as refinement
|
4
|
+
module ExtendTime
|
5
|
+
refine Time do
|
6
|
+
SECONDS_OF_HOUR = 60 * 60
|
7
|
+
SECONDS_OF_DAY = SECONDS_OF_HOUR * 24
|
8
|
+
|
9
|
+
# n 日後の Time を返す
|
10
|
+
def next_day(n = 1)
|
11
|
+
self + (n * SECONDS_OF_DAY)
|
12
|
+
end
|
13
|
+
|
14
|
+
# n 時間後の Time を返す
|
15
|
+
def next_hour(n = 1)
|
16
|
+
self + (n * SECONDS_OF_HOUR)
|
17
|
+
end
|
18
|
+
|
19
|
+
# JST で 0:00 a.m. がどうかを返す
|
20
|
+
def is_midnight_in_jst
|
21
|
+
seconds_in_local = ((self.hour * 60) + self.min) * 60 + self.sec
|
22
|
+
seconds_in_jst = seconds_in_local - self.utc_offset + 9 * 60 * 60
|
23
|
+
seconds_in_jst % (24 * 60 * 60) == 0
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
refine Time.singleton_class do
|
28
|
+
# YYYYMM 形式の String をパースして [YYYY, MM] を返す
|
29
|
+
# パースに失敗した場合は ArgumentError を送出する
|
30
|
+
def parse_month(date)
|
31
|
+
match = /^(\d{4})(\d{2})$/.match(date)
|
32
|
+
raise ArgumentError, 'invalid date format' if match.nil?
|
33
|
+
year, month = match.captures.map(&:to_i)
|
34
|
+
raise ArgumentError, 'invalid year' unless (1970..2030).include? year
|
35
|
+
raise ArgumentError, 'invalid month' unless (1..12).include? month
|
36
|
+
[year, month]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/hanreki/version.rb
CHANGED
data/lib/hanreki.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hanreki
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- CAMPHOR-
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-06-
|
11
|
+
date: 2016-06-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0.19'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: icalendar
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.3'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.3'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -78,17 +92,24 @@ files:
|
|
78
92
|
- ".rspec"
|
79
93
|
- ".travis.yml"
|
80
94
|
- Gemfile
|
95
|
+
- LICENSE
|
81
96
|
- README.md
|
82
97
|
- Rakefile
|
83
98
|
- bin/console
|
84
99
|
- bin/setup
|
100
|
+
- circle.yml
|
85
101
|
- exe/hanreki
|
86
102
|
- hanreki.gemspec
|
87
103
|
- lib/hanreki.rb
|
88
104
|
- lib/hanreki/cli.rb
|
105
|
+
- lib/hanreki/event.rb
|
106
|
+
- lib/hanreki/i_calendar.rb
|
107
|
+
- lib/hanreki/schedule.rb
|
108
|
+
- lib/hanreki/time_util.rb
|
89
109
|
- lib/hanreki/version.rb
|
90
110
|
homepage: https://github.com/camphor-/hanreki
|
91
|
-
licenses:
|
111
|
+
licenses:
|
112
|
+
- MIT
|
92
113
|
metadata: {}
|
93
114
|
post_install_message:
|
94
115
|
rdoc_options: []
|
@@ -98,7 +119,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
98
119
|
requirements:
|
99
120
|
- - ">="
|
100
121
|
- !ruby/object:Gem::Version
|
101
|
-
version:
|
122
|
+
version: 2.1.0
|
102
123
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
124
|
requirements:
|
104
125
|
- - ">="
|