mo2tex 1.0.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 +7 -0
- data/.rspec +3 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/LICENSE +674 -0
- data/README.md +29 -0
- data/Rakefile +8 -0
- data/config/courses.yml +165 -0
- data/config/templates/latex/body.tex.erb +5 -0
- data/config/templates/latex/header.tex +20 -0
- data/config/templates/latex/trailer.tex +1 -0
- data/config/templates/pic/body.pic.erb +14 -0
- data/config/templates/pic/header.pic +13 -0
- data/config/templates/pic/objects.pic +46 -0
- data/config/templates/pic/trailer.pic.erb +3 -0
- data/exe/m2c +16 -0
- data/exe/m2l +16 -0
- data/exe/m2p +16 -0
- data/lib/mo2tex/calendar.rb +54 -0
- data/lib/mo2tex/course.rb +198 -0
- data/lib/mo2tex/course_helper.rb +123 -0
- data/lib/mo2tex/datetime_helper.rb +29 -0
- data/lib/mo2tex/day_schedule.rb +135 -0
- data/lib/mo2tex/event.rb +77 -0
- data/lib/mo2tex/exceptions.rb +21 -0
- data/lib/mo2tex/exclude_date.rb +68 -0
- data/lib/mo2tex/exclusion_manager.rb +59 -0
- data/lib/mo2tex/extensions/date_time.rb +66 -0
- data/lib/mo2tex/latex.rb +66 -0
- data/lib/mo2tex/main.rb +60 -0
- data/lib/mo2tex/pic.rb +315 -0
- data/lib/mo2tex/school.rb +68 -0
- data/lib/mo2tex/version.rb +5 -0
- data/lib/mo2tex.rb +31 -0
- data/sig/mo2tex.rbs +4 -0
- data/tmp/.gitkeep +1 -0
- metadata +136 -0
data/lib/mo2tex/pic.rb
ADDED
@@ -0,0 +1,315 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'erb'
|
4
|
+
require 'singleton'
|
5
|
+
require 'git/describe'
|
6
|
+
|
7
|
+
module Mo2tex
|
8
|
+
|
9
|
+
module Pic
|
10
|
+
|
11
|
+
DEFAULT_PTDIR = File.expand_path(File.join(['..']*3, 'config', 'templates', 'pic'), __FILE__)
|
12
|
+
|
13
|
+
class Counter
|
14
|
+
|
15
|
+
attr_reader :pg, :cntr, :slot
|
16
|
+
|
17
|
+
include Singleton
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
self.reset
|
21
|
+
end
|
22
|
+
|
23
|
+
def reset
|
24
|
+
@pg = @cntr = @slot = 0
|
25
|
+
end
|
26
|
+
|
27
|
+
def next_pg
|
28
|
+
return nxt(:pg)
|
29
|
+
end
|
30
|
+
|
31
|
+
def next_cntr
|
32
|
+
return nxt(:cntr)
|
33
|
+
end
|
34
|
+
|
35
|
+
def next_slot
|
36
|
+
return nxt(:slot)
|
37
|
+
end
|
38
|
+
|
39
|
+
def prev_pg
|
40
|
+
return self.pg == 0 ? self.pg : self.pg-1
|
41
|
+
end
|
42
|
+
|
43
|
+
def prev_cntr(back = 1)
|
44
|
+
return self.cntr == 0 ? self.cntr : self.cntr-back
|
45
|
+
end
|
46
|
+
|
47
|
+
def prev_slot(back = 1)
|
48
|
+
return self.slot == 0 ? self.slot : self.slot-back
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def nxt(what)
|
54
|
+
receiver = ('@' + what.to_s).to_sym
|
55
|
+
result = self.send(what)
|
56
|
+
self.instance_variable_set(receiver, self.send(what) + 1)
|
57
|
+
return result
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
COUNTER = Counter.instance
|
63
|
+
|
64
|
+
class Location
|
65
|
+
|
66
|
+
attr_reader :pfx, :sfx
|
67
|
+
|
68
|
+
def initialize(p, s)
|
69
|
+
@pfx = p
|
70
|
+
@sfx = s
|
71
|
+
end
|
72
|
+
|
73
|
+
def tag(n)
|
74
|
+
return sprintf("%s%d.%s", self.pfx, n, self.sfx)
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
class Base
|
80
|
+
|
81
|
+
attr_reader :number, :loc
|
82
|
+
|
83
|
+
def initialize(n, lt)
|
84
|
+
@number = n
|
85
|
+
@loc = lt
|
86
|
+
end
|
87
|
+
|
88
|
+
def location_tag
|
89
|
+
return self.loc
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
class Slot < Base
|
95
|
+
|
96
|
+
attr_reader :slot
|
97
|
+
|
98
|
+
def initialize(n, sl, lt)
|
99
|
+
super(n, lt)
|
100
|
+
@slot = sl
|
101
|
+
end
|
102
|
+
|
103
|
+
def color
|
104
|
+
return self.slot.color
|
105
|
+
end
|
106
|
+
|
107
|
+
DAY_DURATION = 8 # in hours
|
108
|
+
|
109
|
+
def pic_dur
|
110
|
+
result = (self.slot.dtend - self.slot.dtstart).to_f*24
|
111
|
+
return result
|
112
|
+
end
|
113
|
+
|
114
|
+
def pic_time
|
115
|
+
return sprintf("%s-%s", self.slot.dtstart.hm_to_s, self.slot.dtend.hm_to_s)
|
116
|
+
end
|
117
|
+
|
118
|
+
def pic_title
|
119
|
+
return self.slot.title.sub(/^.*([TB][RN])/, '\1')
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
class Container < Base
|
125
|
+
|
126
|
+
attr_reader :day_schedule, :slots
|
127
|
+
|
128
|
+
def initialize(n, ds, lt)
|
129
|
+
super(n, lt)
|
130
|
+
@day_schedule = ds
|
131
|
+
@slots = create_slots
|
132
|
+
end
|
133
|
+
|
134
|
+
def wdate
|
135
|
+
result = sprintf("%s %s", self.day_schedule.day.to_it_roff_wday, self.day_schedule.day.day_only.strftime("%d/%m"))
|
136
|
+
return result
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
#
|
142
|
+
# four tags
|
143
|
+
#
|
144
|
+
LOC_TAGS = [ Location.new('Subcontainer', 'nw'), Location.new('Event', 'sw') ]
|
145
|
+
|
146
|
+
def create_slots
|
147
|
+
result = []
|
148
|
+
cidx = 0
|
149
|
+
nidx = 0
|
150
|
+
loc_tag = LOC_TAGS[cidx].tag(COUNTER.prev_cntr)
|
151
|
+
cidx = 1
|
152
|
+
self.day_schedule.each_slot do
|
153
|
+
|slot|
|
154
|
+
# cidx = (nidx + 1) % LOC_TAGS.size
|
155
|
+
result << Slot.new(COUNTER.next_slot, slot, loc_tag)
|
156
|
+
loc_tag = LOC_TAGS[cidx].tag(COUNTER.prev_slot)
|
157
|
+
nidx += 1
|
158
|
+
end
|
159
|
+
return result
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
|
164
|
+
class Page < Base
|
165
|
+
|
166
|
+
attr_reader :containers
|
167
|
+
|
168
|
+
def initialize(n)
|
169
|
+
super(n, '')
|
170
|
+
@containers = []
|
171
|
+
end
|
172
|
+
|
173
|
+
def <<(cont)
|
174
|
+
raise NotAPicContainer, "#{cont.class}" unless cont.is_a?(Container)
|
175
|
+
self.containers << cont
|
176
|
+
return cont
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
class Writer
|
182
|
+
|
183
|
+
attr_reader :school, :header, :trailer, :body, :total_hours_done, :max_daily_hours
|
184
|
+
attr_accessor :page_no, :day_no, :event_no, :days, :pages
|
185
|
+
|
186
|
+
def initialize(s)
|
187
|
+
@total_hours_done = 0
|
188
|
+
@school = s
|
189
|
+
@header = read_header
|
190
|
+
@trailer = read_trailer
|
191
|
+
@body = read_body
|
192
|
+
@max_daily_hours = calculate_max_daily_hours
|
193
|
+
@page_no = @day_no = @event_no = 0
|
194
|
+
self.days = []
|
195
|
+
generate_days
|
196
|
+
self.pages = create_pages
|
197
|
+
end
|
198
|
+
|
199
|
+
def generate
|
200
|
+
result = ''
|
201
|
+
self.pages.each do
|
202
|
+
|page|
|
203
|
+
result += self.header
|
204
|
+
result += self.body.result(binding)
|
205
|
+
result += self.trailer.result(binding)
|
206
|
+
end
|
207
|
+
return result
|
208
|
+
end
|
209
|
+
|
210
|
+
DAYS_PER_PAGE = 12
|
211
|
+
DAYS_PER_ROW = 4
|
212
|
+
|
213
|
+
#
|
214
|
+
# twelve tags in a repeating pattern
|
215
|
+
#
|
216
|
+
LOC_TAGS = [ Location.new('PageFrame', 'nw'), ([[ Location.new('Container', 'ne') ] * 3, Location.new('Container', 'sw') ] * 3).flatten ].flatten
|
217
|
+
|
218
|
+
private
|
219
|
+
|
220
|
+
include DateTimeHelper
|
221
|
+
|
222
|
+
def create_pages
|
223
|
+
result = []
|
224
|
+
ltag = nil
|
225
|
+
self.days.each_with_index do
|
226
|
+
|day, idx|
|
227
|
+
dppidx = idx % DAYS_PER_PAGE
|
228
|
+
if dppidx == 0
|
229
|
+
result << Page.new(COUNTER.next_pg)
|
230
|
+
ltag = LOC_TAGS[dppidx].tag(COUNTER.prev_pg)
|
231
|
+
else
|
232
|
+
dpr = dppidx % DAYS_PER_ROW
|
233
|
+
back = dpr == 0 ? 4 : 1
|
234
|
+
ltag = LOC_TAGS[dppidx].tag(COUNTER.prev_cntr(back))
|
235
|
+
end
|
236
|
+
cur_page = result.last
|
237
|
+
cur_page << Container.new(COUNTER.next_cntr, day, ltag)
|
238
|
+
end
|
239
|
+
return result
|
240
|
+
end
|
241
|
+
|
242
|
+
def generate_days
|
243
|
+
COUNTER.reset
|
244
|
+
self.school.each_course do
|
245
|
+
|title, course|
|
246
|
+
course.events.each do
|
247
|
+
|ev|
|
248
|
+
load_event(ev)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
self.days.sort! { |a, b| a.dtstart <=> b.dtstart }
|
252
|
+
end
|
253
|
+
|
254
|
+
def load_event(ev)
|
255
|
+
result = nil
|
256
|
+
n = 0
|
257
|
+
self.days.each do
|
258
|
+
|day|
|
259
|
+
if day.this_slot?(ev)
|
260
|
+
result = day
|
261
|
+
break
|
262
|
+
end
|
263
|
+
n += 1
|
264
|
+
end
|
265
|
+
unless result
|
266
|
+
dtday = ev.dtstart.day_only
|
267
|
+
itwday = dtday.to_it_wday
|
268
|
+
template = self.school.cal.day_schedule_templates
|
269
|
+
result = DaySchedule.new(dtday, n, itwday, template)
|
270
|
+
self.days << result
|
271
|
+
end
|
272
|
+
result << ev
|
273
|
+
end
|
274
|
+
|
275
|
+
def read_header
|
276
|
+
return read('header.pic')
|
277
|
+
end
|
278
|
+
|
279
|
+
def read_trailer
|
280
|
+
template = read('trailer.pic.erb')
|
281
|
+
return ERB.new(template, trim_mode: '-')
|
282
|
+
end
|
283
|
+
|
284
|
+
def read_body
|
285
|
+
template = read('body.pic.erb')
|
286
|
+
return ERB.new(template, trim_mode: '-')
|
287
|
+
end
|
288
|
+
|
289
|
+
def read(what)
|
290
|
+
result = nil
|
291
|
+
tdir ||= DEFAULT_PTDIR
|
292
|
+
File.open(File.join(tdir, what)) { |fh| result = fh.readlines.join }
|
293
|
+
return result
|
294
|
+
end
|
295
|
+
|
296
|
+
def calculate_max_daily_hours
|
297
|
+
earliest_start = latest_end = nil
|
298
|
+
self.school.cal.day_schedule_templates.each do
|
299
|
+
|key, prop|
|
300
|
+
this_start = datetime_create(DateTime.first_midnight, prop['orario'][0])
|
301
|
+
this_end = datetime_create(DateTime.first_midnight, prop['orario'][1])
|
302
|
+
earliest_start = this_start unless earliest_start
|
303
|
+
earliest_start = this_start if this_start < earliest_start
|
304
|
+
latest_end = this_end unless latest_end
|
305
|
+
latest_end = this_end if this_end > latest_end
|
306
|
+
end
|
307
|
+
dur = DateTime.dayfrac_to_dur(latest_end - earliest_start)
|
308
|
+
return dur
|
309
|
+
end
|
310
|
+
|
311
|
+
end
|
312
|
+
|
313
|
+
end
|
314
|
+
|
315
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Mo2tex
|
6
|
+
|
7
|
+
class School
|
8
|
+
|
9
|
+
attr_reader :courses, :cal, :exm
|
10
|
+
|
11
|
+
def initialize(config)
|
12
|
+
parse(config)
|
13
|
+
end
|
14
|
+
|
15
|
+
def each_course(&block)
|
16
|
+
self.courses.each do
|
17
|
+
|title, course|
|
18
|
+
yield(title, course)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# +generate(head, tail, &block)+
|
24
|
+
#
|
25
|
+
# - head: is a proc that is called before each table
|
26
|
+
# - tail: is a proc that is called after each table
|
27
|
+
#
|
28
|
+
def generate(head, tail, &block)
|
29
|
+
result = ''
|
30
|
+
hours_done = 0
|
31
|
+
self.each_course do
|
32
|
+
|title, c|
|
33
|
+
result += head.call(title)
|
34
|
+
result += c.generate(&block)
|
35
|
+
result += tail.call(c.hours_done)
|
36
|
+
hours_done += c.hours_done
|
37
|
+
end
|
38
|
+
return [hours_done, result]
|
39
|
+
end
|
40
|
+
|
41
|
+
def warnings(&block)
|
42
|
+
return unless block_given?
|
43
|
+
self.courses.each do
|
44
|
+
|c|
|
45
|
+
c.warnings.each { |k, v| yield(k, v) }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
include CourseHelper
|
52
|
+
|
53
|
+
def parse(config)
|
54
|
+
@courses = {}
|
55
|
+
temp_h = nil
|
56
|
+
File.open(config, 'r') { |fh| temp_h = YAML.load(fh) }
|
57
|
+
@cal = Calendar.new(temp_h['calendar'])
|
58
|
+
temp_h['courses'].each do
|
59
|
+
|title, desc|
|
60
|
+
t = normalize_title(title)
|
61
|
+
@courses[t] = Course.create(t, desc, self.cal.exclude)
|
62
|
+
end
|
63
|
+
return nil
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
data/lib/mo2tex.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mo2tex
|
4
|
+
|
5
|
+
PATH = 'mo2tex'
|
6
|
+
EXT_PATH = File.join(PATH, 'extensions')
|
7
|
+
|
8
|
+
class Error < StandardError; end
|
9
|
+
|
10
|
+
%w(
|
11
|
+
date_time
|
12
|
+
).each { |f| require_relative File.join(EXT_PATH, f) }
|
13
|
+
|
14
|
+
%w(
|
15
|
+
version
|
16
|
+
exceptions
|
17
|
+
datetime_helper
|
18
|
+
course_helper
|
19
|
+
day_schedule
|
20
|
+
event
|
21
|
+
exclude_date
|
22
|
+
exclusion_manager
|
23
|
+
calendar
|
24
|
+
course
|
25
|
+
school
|
26
|
+
latex
|
27
|
+
pic
|
28
|
+
main
|
29
|
+
).each { |f| require_relative File.join(PATH, f) }
|
30
|
+
|
31
|
+
end
|
data/sig/mo2tex.rbs
ADDED
data/tmp/.gitkeep
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
|
metadata
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mo2tex
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nicola Bernardini
|
8
|
+
bindir: exe
|
9
|
+
cert_chain: []
|
10
|
+
date: 2024-12-05 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: rdoc
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '0'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: base64
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: icalendar
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '2.10'
|
47
|
+
type: :runtime
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '2.10'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: git-describe
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
type: :runtime
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
description: mo2tex transforms a Monte Ore calendar into a Tex table
|
69
|
+
email:
|
70
|
+
- nicola.bernardini.rome@gmail.com
|
71
|
+
executables:
|
72
|
+
- m2c
|
73
|
+
- m2l
|
74
|
+
- m2p
|
75
|
+
extensions: []
|
76
|
+
extra_rdoc_files: []
|
77
|
+
files:
|
78
|
+
- ".rspec"
|
79
|
+
- ".ruby-gemset"
|
80
|
+
- ".ruby-version"
|
81
|
+
- LICENSE
|
82
|
+
- README.md
|
83
|
+
- Rakefile
|
84
|
+
- config/courses.yml
|
85
|
+
- config/templates/latex/body.tex.erb
|
86
|
+
- config/templates/latex/header.tex
|
87
|
+
- config/templates/latex/trailer.tex
|
88
|
+
- config/templates/pic/body.pic.erb
|
89
|
+
- config/templates/pic/header.pic
|
90
|
+
- config/templates/pic/objects.pic
|
91
|
+
- config/templates/pic/trailer.pic.erb
|
92
|
+
- exe/m2c
|
93
|
+
- exe/m2l
|
94
|
+
- exe/m2p
|
95
|
+
- lib/mo2tex.rb
|
96
|
+
- lib/mo2tex/calendar.rb
|
97
|
+
- lib/mo2tex/course.rb
|
98
|
+
- lib/mo2tex/course_helper.rb
|
99
|
+
- lib/mo2tex/datetime_helper.rb
|
100
|
+
- lib/mo2tex/day_schedule.rb
|
101
|
+
- lib/mo2tex/event.rb
|
102
|
+
- lib/mo2tex/exceptions.rb
|
103
|
+
- lib/mo2tex/exclude_date.rb
|
104
|
+
- lib/mo2tex/exclusion_manager.rb
|
105
|
+
- lib/mo2tex/extensions/date_time.rb
|
106
|
+
- lib/mo2tex/latex.rb
|
107
|
+
- lib/mo2tex/main.rb
|
108
|
+
- lib/mo2tex/pic.rb
|
109
|
+
- lib/mo2tex/school.rb
|
110
|
+
- lib/mo2tex/version.rb
|
111
|
+
- sig/mo2tex.rbs
|
112
|
+
- tmp/.gitkeep
|
113
|
+
homepage: https://rubygems.org/gems/mo2tex
|
114
|
+
licenses: []
|
115
|
+
metadata:
|
116
|
+
allowed_push_host: https://rubygems.org
|
117
|
+
homepage_uri: https://rubygems.org/gems/mo2tex
|
118
|
+
source_code_uri: https://gitlab.com/SMELAT/mo2tex
|
119
|
+
rdoc_options: []
|
120
|
+
require_paths:
|
121
|
+
- lib
|
122
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: 3.0.0
|
127
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
requirements: []
|
133
|
+
rubygems_version: 3.6.0.dev
|
134
|
+
specification_version: 4
|
135
|
+
summary: mo2tex transforms a Monte Ore calendar into a Tex table
|
136
|
+
test_files: []
|