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.
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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mo2tex
4
+ VERSION = "1.0.0"
5
+ 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
@@ -0,0 +1,4 @@
1
+ module Mo2tex
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
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: []