sciolyff 0.5.3 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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.