hanreki 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/camphor-/hanreki.svg?branch=master)](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
|
- - ">="
|