sciolyff 0.5.3 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9d439db52050ce2e14cdefb6e5d2520d1105dbea5dbe49a4cf5a5e83323a30bd
4
- data.tar.gz: 1f6611b0af9696973430b849c5241a61ef9d23fa254db9cba12b4b08d3c8647d
3
+ metadata.gz: 64f19b6f05fc3a19ae643aa8c783561f094ddd93959021b77eda3ad437ec9f6d
4
+ data.tar.gz: e0359c5b02d570f75d068ae5d4b0dd2965d0bece8acfa7451b6cc29acfe19cb7
5
5
  SHA512:
6
- metadata.gz: 2901fb2fdb8e8e7aa26e9cc97cd1c2c0989d1cc18233f1cd9b038c724dae54530b64be62d812abc03a753f92bed46763df2d487a7b377471d9559bfc809049d9
7
- data.tar.gz: 61b9fca3f88e142116e211cf576b6751d98e99ba7a464433a96af2ec401b9f6a71acf4879ea247f6612965a65e5672b81264790e812cfb8006252e128190879c
6
+ metadata.gz: dfacd78064019cebf1be0d4cf7351965eb6dd134d2457a1f00d8ed26babb535c94d292d90b9cb6f21f04350faa8b9d96745bdb61d05b8c00806e8a4fbde57df6
7
+ data.tar.gz: 51e0a6fd2deaa74b1f0692e7a39a4d2712bef21f3a4f92b322d205219ac58d2dd0102ba438c2a6b085850d2d23885ee1d95386f67c81c5c66d355b4c7bef2db0
data/bin/sciolyff CHANGED
@@ -6,7 +6,7 @@ require 'yaml'
6
6
  require 'sciolyff'
7
7
 
8
8
  opts = Optimist.options do
9
- version 'sciolyff 0.5.3'
9
+ version 'sciolyff 0.6.0'
10
10
  banner <<~STRING
11
11
  Checks if a given file is in the Scioly File Format
12
12
 
data/lib/sciolyff.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'yaml'
4
+ require 'date'
4
5
  require 'sciolyff/top_level'
5
6
  require 'sciolyff/sections'
6
7
  require 'sciolyff/tournament'
@@ -9,6 +10,7 @@ require 'sciolyff/teams'
9
10
  require 'sciolyff/placings'
10
11
  require 'sciolyff/scores'
11
12
  require 'sciolyff/penalties'
13
+ require 'sciolyff/interpreter'
12
14
 
13
15
  # API methods for the Scioly File Format
14
16
  #
@@ -46,6 +48,8 @@ module SciolyFF
46
48
 
47
49
  STRING
48
50
 
51
+ # Deprecated: Please use `SciolyFF::Interpreter` instead
52
+ #
49
53
  # Wrapper class around a SciolyFF Ruby object representation with utility
50
54
  # methods to help in displaying results
51
55
  class Helper
@@ -53,6 +57,9 @@ module SciolyFF
53
57
  attr_reader :placings_by_event, :placings_by_team, :penalties_by_team
54
58
 
55
59
  def initialize(rep)
60
+ warn 'Class `SciolyFF::Helper` is deprecated. '\
61
+ 'Please use `SciolyFF::Interpreter` instead.'
62
+
56
63
  @rep = rep
57
64
  @exhibition_teams_count = rep[:Teams].count { |t| t[:exhibition] }
58
65
  @exempt_placings_count = rep[:Placings].count { |p| p[:exempt] }
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SciolyFF
4
+ # Interprets the YAML representation of a SciolyFF file through objects that
5
+ # respond to idiomatic Ruby method calls
6
+ class Interpreter
7
+ require 'sciolyff/interpreter/tournament'
8
+ require 'sciolyff/interpreter/event'
9
+ require 'sciolyff/interpreter/team'
10
+ require 'sciolyff/interpreter/placing'
11
+ require 'sciolyff/interpreter/penalty'
12
+
13
+ attr_reader :tournament, :events, :teams, :placings, :penalties
14
+
15
+ def initialize(rep)
16
+ create_models(rep)
17
+ link_models(self)
18
+
19
+ sort_events_naturally
20
+ sort_teams_by_rank
21
+
22
+ freeze_models
23
+ end
24
+
25
+ private
26
+
27
+ def create_models(rep)
28
+ @tournament = Tournament.new(rep)
29
+ @events = map_array_to_models rep[:Events], Event, rep
30
+ @teams = map_array_to_models rep[:Teams], Team, rep
31
+ @placings = map_array_to_models rep[:Placings], Placing, rep
32
+ @penalties = map_array_to_models rep[:Penalties], Penalty, rep
33
+ end
34
+
35
+ def map_array_to_models(arr, object_class, rep)
36
+ return [] if arr.nil?
37
+
38
+ arr.map.with_index { |_, index| object_class.new(rep, index) }
39
+ end
40
+
41
+ def link_models(interpreter)
42
+ # models have to linked in reverse order because reasons
43
+ @penalties.each { |m| m.link_to_other_models(interpreter) }
44
+ @placings .each { |m| m.link_to_other_models(interpreter) }
45
+ @teams .each { |m| m.link_to_other_models(interpreter) }
46
+ @events .each { |m| m.link_to_other_models(interpreter) }
47
+ @tournament.link_to_other_models(interpreter)
48
+ end
49
+
50
+ def freeze_models
51
+ @events.freeze
52
+ @teams.freeze
53
+ @placings.freeze
54
+ @penalties.freeze
55
+ end
56
+
57
+ def sort_events_naturally
58
+ @events.sort! do |a, b|
59
+ next 1 if a.trial? && !b.trial?
60
+ next -1 if !a.trial? && b.trial?
61
+
62
+ a.name <=> b.name
63
+ end
64
+ end
65
+
66
+ def sort_teams_by_rank
67
+ @teams.sort! do |team_a, team_b|
68
+ next 1 if team_a.exhibition? && !team_b.exhibition?
69
+ next -1 if !team_a.exhibition? && team_b.exhibition?
70
+
71
+ cmp = team_a.points <=> team_b.points
72
+ cmp.zero? ? break_tie(team_a, team_b) : cmp
73
+ end
74
+ end
75
+
76
+ def break_tie(team_a, team_b)
77
+ team_a.medal_counts
78
+ .zip(team_b.medal_counts)
79
+ .map { |counts| counts.last - counts.first }
80
+ .find(proc { break_second_tie(team_a, team_b) }, &:nonzero?)
81
+ end
82
+
83
+ def break_second_tie(team_a, team_b)
84
+ cmp = team_a.trial_event_points <=> team_b.trial_event_points
85
+ cmp.zero? ? break_third_tie(team_a, team_b) : cmp
86
+ end
87
+
88
+ def break_third_tie(team_a, team_b)
89
+ team_a.trial_event_medal_counts
90
+ .zip(team_b.trial_event_medal_counts)
91
+ .map { |counts| counts.last - counts.first }
92
+ .find(proc { team_a.number <=> team_b.number }, &:nonzero?)
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sciolyff/interpreter/model'
4
+
5
+ module SciolyFF
6
+ # Models an instance of a Science Olympiad event at a specific tournament
7
+ class Interpreter::Event < Interpreter::Model
8
+ def link_to_other_models(interpreter)
9
+ super
10
+ @placings = interpreter.placings.select { |p| p.event == self }
11
+ @placings_by_team =
12
+ @placings.group_by(&:team).transform_values!(&:first)
13
+
14
+ @placings.freeze
15
+ end
16
+
17
+ attr_reader :placings
18
+
19
+ def name
20
+ @rep[:name]
21
+ end
22
+
23
+ def trial?
24
+ @rep[:trial] == true
25
+ end
26
+
27
+ def trialed?
28
+ @rep[:trialed] == true
29
+ end
30
+
31
+ def high_score_wins?
32
+ @rep[:scoring] == 'high'
33
+ end
34
+
35
+ def low_score_wins?
36
+ !high_score_wins?
37
+ end
38
+
39
+ def placing_for(team)
40
+ @placings_by_team[team]
41
+ end
42
+
43
+ def competing_teams
44
+ return placings.map(&:team) if trial?
45
+
46
+ placings.map(&:team).reject(&:exhibition?)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SciolyFF
4
+ # Parent class for other nested classes within Interpreter
5
+ class Interpreter::Model
6
+ def initialize(rep, index)
7
+ @rep = rep[pluralize_for_key(self.class)][index]
8
+ @cache = {}
9
+ end
10
+
11
+ def link_to_other_models(interpreter)
12
+ @tournament = interpreter.tournament
13
+ end
14
+
15
+ attr_reader :tournament
16
+
17
+ # prevents infinite loop due caused by intentional circular references
18
+ def inspect
19
+ to_s.delete_suffix('>') + " @rep=#{@rep}>"
20
+ end
21
+
22
+ private
23
+
24
+ def pluralize_for_key(klass)
25
+ name = klass.name.split('::').last
26
+ name = name.delete_suffix('y') + 'ie' if name.end_with?('y')
27
+ (name + 's').to_sym
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sciolyff/interpreter/model'
4
+
5
+ module SciolyFF
6
+ # Models a team penalty for a Science Olympiad team at a tournament
7
+ class Interpreter::Penalty < Interpreter::Model
8
+ def link_to_other_models(interpreter)
9
+ super
10
+ @team = interpreter.teams.find { |t| t.number == @rep[:team] }
11
+ end
12
+
13
+ attr_reader :team
14
+
15
+ def points
16
+ @rep[:points]
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sciolyff/interpreter/model'
4
+
5
+ module SciolyFF
6
+ # Models the result of a team participating (or not) in an event
7
+ class Interpreter::Placing < Interpreter::Model
8
+ def link_to_other_models(interpreter)
9
+ super
10
+ @event = interpreter.events.find { |e| e.name == @rep[:event] }
11
+ @team = interpreter.teams .find { |t| t.number == @rep[:team] }
12
+ end
13
+
14
+ attr_reader :event, :team
15
+
16
+ def participated?
17
+ @rep[:participated] == true || @rep[:participated].nil?
18
+ end
19
+
20
+ def disqualified?
21
+ @rep[:disqualified] == true
22
+ end
23
+
24
+ def exempt?
25
+ @rep[:exempt] == true
26
+ end
27
+
28
+ def unknown?
29
+ @rep[:unknown] == true
30
+ end
31
+
32
+ def tie?
33
+ @rep[:tie] == true
34
+ end
35
+
36
+ def place
37
+ @rep[:place]
38
+ end
39
+
40
+ def did_not_participate?
41
+ !participated? && !disqualified?
42
+ end
43
+
44
+ def participation_only?
45
+ participated? && !place && !unknown?
46
+ end
47
+
48
+ def points
49
+ return @cache[:points] if @cache[:points]
50
+
51
+ n = event.competing_teams.count
52
+
53
+ @cache[:points] =
54
+ if disqualified? then n + 2
55
+ elsif did_not_participate? then n + 1
56
+ elsif participation_only? then n
57
+ elsif unknown? then n - 1
58
+ else calculate_points
59
+ end
60
+ end
61
+
62
+ def considered_for_team_points?
63
+ !(event.trial? || event.trialed? || exempt?)
64
+ end
65
+
66
+ private
67
+
68
+ def calculate_points
69
+ return place if event.trial?
70
+
71
+ place - event.placings.count do |p|
72
+ (p.exempt? || p.team.exhibition?) &&
73
+ p.place &&
74
+ p.place < place
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sciolyff/interpreter/model'
4
+
5
+ module SciolyFF
6
+ # Models an instance of a Science Olympiad team at a specific tournament
7
+ class Interpreter::Team < Interpreter::Model
8
+ def link_to_other_models(interpreter)
9
+ super
10
+ @placings = interpreter.placings .select { |p| p.team == self }
11
+ @penalties = interpreter.penalties.select { |p| p.team == self }
12
+ @placings_by_event =
13
+ @placings.group_by(&:event).transform_values!(&:first)
14
+
15
+ @placings.freeze
16
+ @penalties.freeze
17
+ end
18
+
19
+ attr_reader :placings, :penalties
20
+
21
+ def school
22
+ @rep[:school]
23
+ end
24
+
25
+ def school_abbreviation
26
+ @rep[:'school abbreviation']
27
+ end
28
+
29
+ def suffix
30
+ @rep[:suffix]
31
+ end
32
+
33
+ def subdivision
34
+ @rep[:subdivision]
35
+ end
36
+
37
+ def exhibition?
38
+ @rep[:exhibition] == true
39
+ end
40
+
41
+ def number
42
+ @rep[:number]
43
+ end
44
+
45
+ def city
46
+ @rep[:city]
47
+ end
48
+
49
+ def state
50
+ @rep[:state]
51
+ end
52
+
53
+ def placing_for(event)
54
+ @placings_by_event[event]
55
+ end
56
+
57
+ def rank
58
+ @tournament.teams.find_index(self) + 1
59
+ end
60
+
61
+ def points
62
+ return @cache[:points] if @cache[:points]
63
+
64
+ counted_placings = placings.select(&:considered_for_team_points?)
65
+
66
+ if @tournament.worst_placings_dropped?
67
+ counted_placings
68
+ .sort!(&:points)
69
+ .reverse!
70
+ .drop!(@tournament.worst_placings_dropped)
71
+ end
72
+
73
+ @cache[:points] =
74
+ counted_placings.sum(&:points) + penalties.sum(&:points)
75
+ end
76
+
77
+ def trial_event_points
78
+ placings.select { |p| p.event.trial? }.sum(&:points)
79
+ end
80
+
81
+ def medal_counts
82
+ (1..@tournament.max_points_per_event).map do |medal_points|
83
+ placings.select(&:considered_for_team_points?)
84
+ .count { |p| p.points == medal_points }
85
+ end
86
+ end
87
+
88
+ def trial_event_medal_counts
89
+ (1..@tournament.max_points_per_event(trial: true)).map do |medal_points|
90
+ placings.select { |p| p.event.trial? }
91
+ .count { |p| p.points == medal_points }
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sciolyff/interpreter/model'
4
+
5
+ module SciolyFF
6
+ # Models a Science Olympiad tournament
7
+ class Interpreter::Tournament < Interpreter::Model
8
+ def initialize(rep)
9
+ @rep = rep[:Tournament]
10
+ end
11
+
12
+ def link_to_other_models(interpreter)
13
+ @events = interpreter.events
14
+ @teams = interpreter.teams
15
+ @placings = interpreter.placings
16
+ @penalties = interpreter.penalties
17
+ end
18
+
19
+ attr_reader :events, :teams, :placings, :penalties
20
+
21
+ undef tournament
22
+
23
+ def name
24
+ @rep[:name]
25
+ end
26
+
27
+ def short_name
28
+ @rep[:'short name']
29
+ end
30
+
31
+ def location
32
+ @rep[:location]
33
+ end
34
+
35
+ def level
36
+ @rep[:level]
37
+ end
38
+
39
+ def state
40
+ @rep[:state]
41
+ end
42
+
43
+ def division
44
+ @rep[:division]
45
+ end
46
+
47
+ def year
48
+ @rep[:year]
49
+ end
50
+
51
+ def date
52
+ @rep[:date]
53
+ end
54
+
55
+ def worst_placings_dropped?
56
+ @rep[:'worst placings dropped'].instance_of? Integer
57
+ end
58
+
59
+ def worst_placings_dropped
60
+ return 0 unless @rep[:'worst placings dropped']
61
+
62
+ @rep[:'worst placings dropped']
63
+ end
64
+
65
+ def max_points_per_event(trial: false)
66
+ return @teams.size + 2 if trial
67
+
68
+ @teams.count { |t| !t.exhibition? } + 2
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require 'sciolyff/interpreter'
5
+ require 'sciolyff'
6
+ require 'pp'
7
+
8
+ FILENAME = '/home/qianm/Repositories/unosmium/unosmium.org/data/2019-04-27_OH_states_b.yaml'
9
+
10
+ rep = YAML.safe_load(File.read(FILENAME), symbolize_names: true); nil
11
+ interpreter = SciolyFF::Interpreter.new(rep)
12
+ # helper = SciolyFF::Helper.new(rep)
13
+ # helper.sort_teams_by_rank
14
+
15
+ pp interpreter.teams.map { |t| "#{t.number} #{t.school} #{t.points}" }
16
+
17
+ p interpreter.teams.first.medal_counts
18
+
19
+ p interpreter.teams.find { |t| t.number == 1 }.rank
20
+
21
+ # puts interpreter.teams.find { |t| t.exhibition? }.placings.map(&:points)
@@ -101,8 +101,9 @@ module SciolyFF
101
101
  event = SciolyFF.rep[:Events].find do |e|
102
102
  e[:name] == placing[:event]
103
103
  end
104
- assert event[:trial] || event[:trialed] || event.nil?,
105
- 'Cannot have unknown place for non-trial/trialed event'
104
+ assert event[:trial] || event[:trialed] || placing[:exempt],
105
+ 'Cannot have unknown place for non-trial/trialed event '\
106
+ 'or non-exempt place'
106
107
  end
107
108
  end
108
109
 
@@ -171,7 +172,7 @@ module SciolyFF
171
172
  .select { |p| p[:team] == team[:number] }
172
173
  .map { |p| p[:event] }
173
174
 
174
- assert_equal events, events_with_placings
175
+ assert_equal events.sort, events_with_placings.sort
175
176
  end
176
177
  end
177
178
  end
@@ -66,17 +66,13 @@ module SciolyFF
66
66
 
67
67
  def test_each_score_has_valid_exempt
68
68
  @scores.select { |s| s.instance_of? Hash }.each do |score|
69
- if score.key? :exempt
70
- assert_includes [true, false], score[:exempt]
71
- end
69
+ assert_includes [true, false], score[:exempt] if score.key? :exempt
72
70
  end
73
71
  end
74
72
 
75
73
  def test_each_score_has_valid_unknown
76
74
  @scores.select { |s| s.instance_of? Hash }.each do |score|
77
- if score.key? :unknown
78
- assert_includes [true, false], score[:unknown]
79
- end
75
+ assert_includes [true, false], score[:unknown] if score.key? :unknown
80
76
  end
81
77
  end
82
78
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sciolyff
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.3
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Em Zhan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-17 00:00:00.000000000 Z
11
+ date: 2019-08-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -62,6 +62,14 @@ files:
62
62
  - bin/sciolyff
63
63
  - lib/sciolyff.rb
64
64
  - lib/sciolyff/events.rb
65
+ - lib/sciolyff/interpreter.rb
66
+ - lib/sciolyff/interpreter/event.rb
67
+ - lib/sciolyff/interpreter/model.rb
68
+ - lib/sciolyff/interpreter/penalty.rb
69
+ - lib/sciolyff/interpreter/placing.rb
70
+ - lib/sciolyff/interpreter/team.rb
71
+ - lib/sciolyff/interpreter/tournament.rb
72
+ - lib/sciolyff/interpreter/ztestscript.rb
65
73
  - lib/sciolyff/penalties.rb
66
74
  - lib/sciolyff/placings.rb
67
75
  - lib/sciolyff/scores.rb
@@ -88,7 +96,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
88
96
  - !ruby/object:Gem::Version
89
97
  version: '0'
90
98
  requirements: []
91
- rubygems_version: 3.0.4
99
+ rubygems_version: 3.0.6
92
100
  signing_key:
93
101
  specification_version: 4
94
102
  summary: A file format for Science Olympiad tournament results.