sciolyff 0.8.0 → 0.9.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 +4 -4
- data/bin/sciolyff +7 -10
- data/lib/sciolyff/interpreter/event.rb +20 -8
- data/lib/sciolyff/interpreter/html/helpers.rb +130 -0
- data/lib/sciolyff/interpreter/html/template.html.erb +558 -0
- data/lib/sciolyff/interpreter/html.rb +47 -0
- data/lib/sciolyff/interpreter/placing.rb +31 -6
- data/lib/sciolyff/interpreter/raw.rb +50 -0
- data/lib/sciolyff/interpreter/subdivisions.rb +41 -0
- data/lib/sciolyff/interpreter/team.rb +16 -3
- data/lib/sciolyff/interpreter/tiebreaks.rb +34 -0
- data/lib/sciolyff/interpreter/tournament.rb +28 -5
- data/lib/sciolyff/interpreter.rb +34 -44
- data/lib/sciolyff/validator/checker.rb +20 -0
- data/lib/sciolyff/validator/events.rb +96 -0
- data/lib/sciolyff/validator/logger.rb +47 -0
- data/lib/sciolyff/validator/penalties.rb +19 -0
- data/lib/sciolyff/validator/placings.rb +120 -0
- data/lib/sciolyff/validator/raws.rb +19 -0
- data/lib/sciolyff/validator/sections.rb +32 -0
- data/lib/sciolyff/validator/teams.rb +81 -0
- data/lib/sciolyff/validator/top_level.rb +22 -0
- data/lib/sciolyff/validator/tournament.rb +72 -0
- data/lib/sciolyff/validator.rb +88 -0
- data/lib/sciolyff.rb +2 -45
- metadata +20 -25
- data/lib/sciolyff/events.rb +0 -66
- data/lib/sciolyff/penalties.rb +0 -52
- data/lib/sciolyff/placings.rb +0 -198
- data/lib/sciolyff/scores.rb +0 -148
- data/lib/sciolyff/sections.rb +0 -40
- data/lib/sciolyff/teams.rb +0 -96
- data/lib/sciolyff/top_level.rb +0 -13
- data/lib/sciolyff/tournament.rb +0 -94
@@ -0,0 +1,120 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sciolyff/validator/checker'
|
4
|
+
require 'sciolyff/validator/sections'
|
5
|
+
|
6
|
+
module SciolyFF
|
7
|
+
# Checks for one placing in the Placings section of a SciolyFF file
|
8
|
+
class Validator::Placings < Validator::Checker
|
9
|
+
include Validator::Sections
|
10
|
+
|
11
|
+
REQUIRED = {
|
12
|
+
event: String,
|
13
|
+
team: Integer
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
OPTIONAL = {
|
17
|
+
place: Integer,
|
18
|
+
participated: [true, false],
|
19
|
+
disqualified: [true, false],
|
20
|
+
exempt: [true, false],
|
21
|
+
tie: [true, false],
|
22
|
+
unknown: [true, false],
|
23
|
+
raw: Hash
|
24
|
+
}.freeze
|
25
|
+
|
26
|
+
def initialize(rep)
|
27
|
+
@events_by_name = rep[:Events].group_by { |e| e[:name] }
|
28
|
+
.transform_values(&:first)
|
29
|
+
@teams_by_number = rep[:Teams].group_by { |t| t[:number] }
|
30
|
+
.transform_values(&:first)
|
31
|
+
@event_names = @events_by_name.keys
|
32
|
+
@team_numbers = @teams_by_number.keys
|
33
|
+
@placings = rep[:Placings]
|
34
|
+
@maximum_place = rep[:Tournament][:'maximum place']
|
35
|
+
end
|
36
|
+
|
37
|
+
def matching_event?(placing, logger)
|
38
|
+
return true if @event_names.include? placing[:event]
|
39
|
+
|
40
|
+
logger.error "'event: #{placing[:event]}' in Placings "\
|
41
|
+
'does not match any event name in Events'
|
42
|
+
end
|
43
|
+
|
44
|
+
def matching_team?(placing, logger)
|
45
|
+
return true if @team_numbers.include? placing[:team]
|
46
|
+
|
47
|
+
logger.error "'team: #{placing[:team]}' in Placings "\
|
48
|
+
'does not match any team number in Teams'
|
49
|
+
end
|
50
|
+
|
51
|
+
def unique_event_and_team?(placing, logger)
|
52
|
+
return true if @placings.count do |other|
|
53
|
+
placing[:event] == other[:event] && placing[:team] == other[:team]
|
54
|
+
end == 1
|
55
|
+
|
56
|
+
logger.error "duplicate #{placing_log(placing)}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def having_a_place_makes_sense?(placing, logger)
|
60
|
+
return true unless placing[:place] &&
|
61
|
+
(placing[:participated] == false ||
|
62
|
+
placing[:disqualified] ||
|
63
|
+
placing[:unknown] ||
|
64
|
+
placing[:raw])
|
65
|
+
|
66
|
+
logger.error 'having a place does not make sense for '\
|
67
|
+
"#{placing_log(placing)}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def having_a_raw_makes_sense?(placing, logger)
|
71
|
+
return true unless placing[:raw] &&
|
72
|
+
(placing[:participated] == false ||
|
73
|
+
placing[:disqualified] ||
|
74
|
+
placing[:unknown] ||
|
75
|
+
placing[:place])
|
76
|
+
|
77
|
+
logger.error 'having raw section does not make sense for '\
|
78
|
+
"#{placing_log(placing)}"
|
79
|
+
end
|
80
|
+
|
81
|
+
def possible_participated_disqualified_combination?(placing, logger)
|
82
|
+
return true unless placing[:participated] == false &&
|
83
|
+
placing[:disqualified]
|
84
|
+
|
85
|
+
logger.error 'impossible participation-disqualified combination for '\
|
86
|
+
"#{placing_log(placing)}"
|
87
|
+
end
|
88
|
+
|
89
|
+
def possible_unknown_disqualified_combination?(placing, logger)
|
90
|
+
return true unless placing[:unknown] && placing[:disqualified]
|
91
|
+
|
92
|
+
logger.error 'impossible unknown-disqualified combination for '\
|
93
|
+
"#{placing_log(placing)}"
|
94
|
+
end
|
95
|
+
|
96
|
+
def unknown_allowed?(placing, logger)
|
97
|
+
event = @events_by_name[placing[:event]]
|
98
|
+
team = @teams_by_number[placing[:team]]
|
99
|
+
return true unless invalid_unknown?(placing, event, team)
|
100
|
+
|
101
|
+
logger.error "unknown place not allowed for #{placing_log(placing)} "\
|
102
|
+
'(either placing must be exempt or event must be trial/trialed)'
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def placing_log(placing)
|
108
|
+
"placing with 'team: #{placing[:team]}' and 'event: #{placing[:event]}'"
|
109
|
+
end
|
110
|
+
|
111
|
+
def invalid_unknown?(placing, event, team)
|
112
|
+
placing[:unknown] &&
|
113
|
+
@maximum_place.nil? &&
|
114
|
+
!placing[:exempt] &&
|
115
|
+
!event[:trial] &&
|
116
|
+
!event[:trialed] &&
|
117
|
+
!team[:exhibition]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sciolyff/validator/checker'
|
4
|
+
require 'sciolyff/validator/sections'
|
5
|
+
|
6
|
+
module SciolyFF
|
7
|
+
# Checks for one raw of a placing in the Placings section of a SciolyFF file
|
8
|
+
class Validator::Raws < Validator::Checker
|
9
|
+
include Validator::Sections
|
10
|
+
|
11
|
+
REQUIRED = {
|
12
|
+
score: Float
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
OPTIONAL = {
|
16
|
+
'tiebreaker rank': Integer
|
17
|
+
}.freeze
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SciolyFF
|
4
|
+
# Generic tests for (sub-)sections and types. Including classes must have two
|
5
|
+
# hashes REQUIRED and OPTIONAL (see other files in this dir for examples)
|
6
|
+
module Validator::Sections
|
7
|
+
def all_required_sections?(rep, logger)
|
8
|
+
missing = self.class::REQUIRED.keys - rep.keys
|
9
|
+
return true if missing.empty?
|
10
|
+
|
11
|
+
logger.error "missing required sections: #{missing.join ', '}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def no_extra_sections?(rep, logger)
|
15
|
+
extra = rep.keys - (self.class::REQUIRED.keys + self.class::OPTIONAL.keys)
|
16
|
+
return true if extra.empty?
|
17
|
+
|
18
|
+
logger.error "extra section(s) found: #{extra.join ', '}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def sections_are_correct_type?(rep, logger)
|
22
|
+
correct_types = self.class::REQUIRED.merge self.class::OPTIONAL
|
23
|
+
rep.all? do |key, value|
|
24
|
+
correct = correct_types[key]
|
25
|
+
next true if (correct.instance_of?(Array) && correct.include?(value)) ||
|
26
|
+
(value.instance_of? correct)
|
27
|
+
|
28
|
+
logger.error "#{key}: #{value} is not #{correct}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sciolyff/validator/checker'
|
4
|
+
require 'sciolyff/validator/sections'
|
5
|
+
|
6
|
+
module SciolyFF
|
7
|
+
# Checks for one team in the Teams section of a SciolyFF file
|
8
|
+
class Validator::Teams < Validator::Checker
|
9
|
+
include Validator::Sections
|
10
|
+
|
11
|
+
REQUIRED = {
|
12
|
+
number: Integer,
|
13
|
+
school: String,
|
14
|
+
state: String
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
OPTIONAL = {
|
18
|
+
'school abbreviation': String,
|
19
|
+
subdivision: String,
|
20
|
+
suffix: String,
|
21
|
+
city: String,
|
22
|
+
disqualified: [true, false],
|
23
|
+
exhibition: [true, false]
|
24
|
+
}.freeze
|
25
|
+
|
26
|
+
def initialize(rep)
|
27
|
+
initialize_teams_info(rep[:Teams])
|
28
|
+
@placings = rep[:Placings].group_by { |p| p[:team] }
|
29
|
+
@exempt = rep[:Tournament][:'exempt placings'] || 0
|
30
|
+
end
|
31
|
+
|
32
|
+
def unique_number?(team, logger)
|
33
|
+
return true if @numbers.count(team[:number]) == 1
|
34
|
+
|
35
|
+
logger.error "duplicate team number: #{team[:number]}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def unique_suffix_per_school?(team, logger)
|
39
|
+
full_school = [team[:school], team[:city], team[:state]]
|
40
|
+
return true if @schools[full_school].count do |other|
|
41
|
+
!other[:suffix].nil? && other[:suffix] == team[:suffix]
|
42
|
+
end <= 1
|
43
|
+
|
44
|
+
logger.error "team number #{team[:number]} has the same suffix "\
|
45
|
+
'as another team from the same school'
|
46
|
+
end
|
47
|
+
|
48
|
+
def unambiguous_cities_per_school?(team, logger)
|
49
|
+
return true unless @schools.keys.find do |other|
|
50
|
+
team[:city].nil? && !other[1].nil? &&
|
51
|
+
team[:school] == other[0] &&
|
52
|
+
team[:state] == other[2]
|
53
|
+
end
|
54
|
+
|
55
|
+
logger.error "city for team number #{team[:number]} is ambiguous, "\
|
56
|
+
'value is required for unambiguity'
|
57
|
+
end
|
58
|
+
|
59
|
+
def correct_number_of_exempt_placings?(team, logger)
|
60
|
+
count = @placings[team[:number]].count { |p| p[:exempt] }
|
61
|
+
return true if count == @exempt || team[:exhibition]
|
62
|
+
|
63
|
+
logger.error "'team: #{team[:number]}' has incorrect number of "\
|
64
|
+
"exempt placings (#{count} insteand of #{@exempt})"
|
65
|
+
end
|
66
|
+
|
67
|
+
def in_a_subdivision_if_possible?(team, logger)
|
68
|
+
return true unless @subdivisions && !team[:subdivision]
|
69
|
+
|
70
|
+
logger.warn "missing subdivision for 'team: #{team[:number]}'"
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def initialize_teams_info(teams)
|
76
|
+
@numbers = teams.map { |t| t[:number] }
|
77
|
+
@schools = teams.group_by { |t| [t[:school], t[:city], t[:state]] }
|
78
|
+
@subdivisions = teams.find { |t| t[:subdivision] }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sciolyff/validator/checker'
|
4
|
+
require 'sciolyff/validator/sections'
|
5
|
+
|
6
|
+
module SciolyFF
|
7
|
+
# Top-level sections of a SciolyFF file
|
8
|
+
class Validator::TopLevel < Validator::Checker
|
9
|
+
include Validator::Sections
|
10
|
+
|
11
|
+
REQUIRED = {
|
12
|
+
Tournament: Hash,
|
13
|
+
Events: Array,
|
14
|
+
Teams: Array,
|
15
|
+
Placings: Array
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
OPTIONAL = {
|
19
|
+
Penalties: Array
|
20
|
+
}.freeze
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sciolyff/validator/checker'
|
4
|
+
require 'sciolyff/validator/sections'
|
5
|
+
|
6
|
+
module SciolyFF
|
7
|
+
# Checks for Tournament section of a SciolyFF file
|
8
|
+
class Validator::Tournament < Validator::Checker
|
9
|
+
include Validator::Sections
|
10
|
+
|
11
|
+
REQUIRED = {
|
12
|
+
location: String,
|
13
|
+
level: %w[Invitational Regionals States Nationals],
|
14
|
+
division: %w[A B C],
|
15
|
+
year: Integer,
|
16
|
+
date: Date
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
OPTIONAL = {
|
20
|
+
name: String,
|
21
|
+
state: String,
|
22
|
+
'short name': String,
|
23
|
+
'worst placings dropped': Integer,
|
24
|
+
'exempt placings': Integer,
|
25
|
+
'maximum place': Integer,
|
26
|
+
'per-event n': [true, false],
|
27
|
+
'n offset': Integer
|
28
|
+
}.freeze
|
29
|
+
|
30
|
+
def initialize(rep)
|
31
|
+
@maximum_place = rep[:Teams].count { |t| !t[:exhibition] }
|
32
|
+
end
|
33
|
+
|
34
|
+
def name_for_not_states_or_nationals?(tournament, logger)
|
35
|
+
level = tournament[:level]
|
36
|
+
return true if %w[States Nationals].include?(level) || tournament[:name]
|
37
|
+
|
38
|
+
logger.error 'name for Tournament required '\
|
39
|
+
"('level: #{level}' is not States or Nationals)"
|
40
|
+
end
|
41
|
+
|
42
|
+
def state_for_not_nationals?(tournament, logger)
|
43
|
+
return true if tournament[:level] == 'Nationals' || tournament[:state]
|
44
|
+
|
45
|
+
logger.error 'state for Tournament required '\
|
46
|
+
"('level: #{tournament[:level]}' is not Nationals)"
|
47
|
+
end
|
48
|
+
|
49
|
+
def short_name_is_relevant?(tournament, logger)
|
50
|
+
return true unless tournament[:'short name'] && !tournament[:name]
|
51
|
+
|
52
|
+
logger.error "'short name: #{tournament[:'short name']}' for Tournament "\
|
53
|
+
"requires a normal 'name:' as well"
|
54
|
+
end
|
55
|
+
|
56
|
+
def short_name_is_short?(tournament, logger)
|
57
|
+
return true if tournament[:'short name'].nil? ||
|
58
|
+
tournament[:'short name'].length < tournament[:name].length
|
59
|
+
|
60
|
+
logger.error "'short name: #{tournament[:'short name']}' for Tournament "\
|
61
|
+
"is longer than normal 'name: #{tournament[:name]}'"
|
62
|
+
end
|
63
|
+
|
64
|
+
def maximum_place_within_range?(tournament, logger)
|
65
|
+
return true if tournament[:'maximum place'].nil? ||
|
66
|
+
tournament[:'maximum place'].between?(1, @maximum_place)
|
67
|
+
|
68
|
+
logger.error "custom 'maximum place: #{tournament[:'maximum place']}' "\
|
69
|
+
"is not within range [1, #{@maximum_place}]"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SciolyFF
|
4
|
+
# Checks if SciolyFF YAML files and/or representations (i.e. hashes that can
|
5
|
+
# be directly converted to YAML) comply with spec (i.e. safe for interpreting)
|
6
|
+
class Validator
|
7
|
+
require 'sciolyff/validator/logger'
|
8
|
+
require 'sciolyff/validator/checker'
|
9
|
+
require 'sciolyff/validator/sections'
|
10
|
+
|
11
|
+
require 'sciolyff/validator/top_level'
|
12
|
+
require 'sciolyff/validator/tournament'
|
13
|
+
require 'sciolyff/validator/events'
|
14
|
+
require 'sciolyff/validator/teams'
|
15
|
+
require 'sciolyff/validator/placings'
|
16
|
+
require 'sciolyff/validator/penalties'
|
17
|
+
require 'sciolyff/validator/raws'
|
18
|
+
|
19
|
+
def initialize(loglevel = Logger::WARN)
|
20
|
+
@logger = Logger.new loglevel
|
21
|
+
@checkers = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def valid?(rep_or_file)
|
25
|
+
@logger.flush
|
26
|
+
|
27
|
+
if rep_or_file.instance_of? String
|
28
|
+
valid_file?(rep_or_file, @logger)
|
29
|
+
else
|
30
|
+
valid_rep?(rep_or_file, @logger)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def last_log
|
35
|
+
@logger.log
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def valid_rep?(rep, logger)
|
41
|
+
unless rep.instance_of? Hash
|
42
|
+
logger.error 'improper file structure'
|
43
|
+
return false
|
44
|
+
end
|
45
|
+
|
46
|
+
result = check_all(rep, logger)
|
47
|
+
|
48
|
+
@checkers.clear # aka this method is not thread-safe
|
49
|
+
result
|
50
|
+
end
|
51
|
+
|
52
|
+
def valid_file?(path, logger)
|
53
|
+
rep = YAML.safe_load(
|
54
|
+
File.read(path),
|
55
|
+
permitted_classes: [Date],
|
56
|
+
symbolize_names: true
|
57
|
+
)
|
58
|
+
rescue StandardError => e
|
59
|
+
logger.error "could not read file as YAML:\n#{e.message}"
|
60
|
+
else
|
61
|
+
valid_rep?(rep, logger)
|
62
|
+
end
|
63
|
+
|
64
|
+
def check_all(rep, logger)
|
65
|
+
check(TopLevel, rep, rep, logger) &&
|
66
|
+
check(Tournament, rep, rep[:Tournament], logger) &&
|
67
|
+
[Events, Teams, Placings, Penalties].all? do |klass|
|
68
|
+
check_list(klass, rep, logger)
|
69
|
+
end &&
|
70
|
+
rep[:Placings].map { |p| p[:raw] }.compact.all? do |r|
|
71
|
+
check(Raws, rep, r, logger)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def check_list(klass, rep, logger)
|
76
|
+
key = klass.to_s.split('::').last.to_sym
|
77
|
+
return true unless rep.key? key # ignore optional sections like Penalties
|
78
|
+
|
79
|
+
rep[key].map { |e| check(klass, rep, e, logger) }.all?
|
80
|
+
end
|
81
|
+
|
82
|
+
def check(klass, top_level_rep, rep, logger)
|
83
|
+
@checkers[klass] ||= klass.new top_level_rep
|
84
|
+
checks = klass.instance_methods - Checker.instance_methods
|
85
|
+
checks.map { |im| @checkers[klass].send im, rep, logger }.all?
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
data/lib/sciolyff.rb
CHANGED
@@ -2,49 +2,6 @@
|
|
2
2
|
|
3
3
|
require 'yaml'
|
4
4
|
require 'date'
|
5
|
-
require 'sciolyff/top_level'
|
6
|
-
require 'sciolyff/sections'
|
7
|
-
require 'sciolyff/tournament'
|
8
|
-
require 'sciolyff/events'
|
9
|
-
require 'sciolyff/teams'
|
10
|
-
require 'sciolyff/placings'
|
11
|
-
require 'sciolyff/scores'
|
12
|
-
require 'sciolyff/penalties'
|
13
|
-
require 'sciolyff/interpreter'
|
14
|
-
|
15
|
-
# API methods for the Scioly File Format
|
16
|
-
#
|
17
|
-
module SciolyFF
|
18
|
-
class << self
|
19
|
-
attr_accessor :rep
|
20
|
-
end
|
21
|
-
|
22
|
-
# Assumes rep is the output of YAML.load
|
23
|
-
def self.validate(rep, opts: {})
|
24
|
-
SciolyFF.rep = rep
|
25
|
-
|
26
|
-
mt_args = []
|
27
|
-
mt_args << '--verbose' if opts[:verbose]
|
28
|
-
|
29
|
-
Minitest.run mt_args
|
30
|
-
end
|
31
5
|
|
32
|
-
|
33
|
-
|
34
|
-
rep = YAML.safe_load(file, permitted_classes: [Date], symbolize_names: true)
|
35
|
-
rescue StandardError => e
|
36
|
-
puts 'Error: could not read file as YAML.'
|
37
|
-
warn e.message
|
38
|
-
else
|
39
|
-
puts FILE_VALIDATION_MESSAGE
|
40
|
-
validate(rep, opts: opts)
|
41
|
-
end
|
42
|
-
|
43
|
-
FILE_VALIDATION_MESSAGE = <<~STRING
|
44
|
-
Validating file with Minitest...
|
45
|
-
|
46
|
-
Overkill? Probably.
|
47
|
-
Doesn't give line numbers from original file? Yeah.
|
48
|
-
|
49
|
-
STRING
|
50
|
-
end
|
6
|
+
require 'sciolyff/interpreter'
|
7
|
+
require 'sciolyff/validator'
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sciolyff
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.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:
|
11
|
+
date: 2020-04-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: minitest
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '5.11'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '5.11'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: optimist
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -61,22 +47,31 @@ extra_rdoc_files: []
|
|
61
47
|
files:
|
62
48
|
- bin/sciolyff
|
63
49
|
- lib/sciolyff.rb
|
64
|
-
- lib/sciolyff/events.rb
|
65
50
|
- lib/sciolyff/interpreter.rb
|
66
51
|
- lib/sciolyff/interpreter/event.rb
|
52
|
+
- lib/sciolyff/interpreter/html.rb
|
53
|
+
- lib/sciolyff/interpreter/html/helpers.rb
|
54
|
+
- lib/sciolyff/interpreter/html/template.html.erb
|
67
55
|
- lib/sciolyff/interpreter/model.rb
|
68
56
|
- lib/sciolyff/interpreter/penalty.rb
|
69
57
|
- lib/sciolyff/interpreter/placing.rb
|
58
|
+
- lib/sciolyff/interpreter/raw.rb
|
59
|
+
- lib/sciolyff/interpreter/subdivisions.rb
|
70
60
|
- lib/sciolyff/interpreter/team.rb
|
61
|
+
- lib/sciolyff/interpreter/tiebreaks.rb
|
71
62
|
- lib/sciolyff/interpreter/tournament.rb
|
72
63
|
- lib/sciolyff/interpreter/ztestscript.rb
|
73
|
-
- lib/sciolyff/
|
74
|
-
- lib/sciolyff/
|
75
|
-
- lib/sciolyff/
|
76
|
-
- lib/sciolyff/
|
77
|
-
- lib/sciolyff/
|
78
|
-
- lib/sciolyff/
|
79
|
-
- lib/sciolyff/
|
64
|
+
- lib/sciolyff/validator.rb
|
65
|
+
- lib/sciolyff/validator/checker.rb
|
66
|
+
- lib/sciolyff/validator/events.rb
|
67
|
+
- lib/sciolyff/validator/logger.rb
|
68
|
+
- lib/sciolyff/validator/penalties.rb
|
69
|
+
- lib/sciolyff/validator/placings.rb
|
70
|
+
- lib/sciolyff/validator/raws.rb
|
71
|
+
- lib/sciolyff/validator/sections.rb
|
72
|
+
- lib/sciolyff/validator/teams.rb
|
73
|
+
- lib/sciolyff/validator/top_level.rb
|
74
|
+
- lib/sciolyff/validator/tournament.rb
|
80
75
|
homepage: https://github.com/zqianem/sciolyff
|
81
76
|
licenses:
|
82
77
|
- MIT
|
@@ -96,7 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
96
91
|
- !ruby/object:Gem::Version
|
97
92
|
version: '0'
|
98
93
|
requirements: []
|
99
|
-
rubygems_version: 3.
|
94
|
+
rubygems_version: 3.1.2
|
100
95
|
signing_key:
|
101
96
|
specification_version: 4
|
102
97
|
summary: A file format for Science Olympiad tournament results.
|
data/lib/sciolyff/events.rb
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'minitest/test'
|
4
|
-
require 'set'
|
5
|
-
|
6
|
-
module SciolyFF
|
7
|
-
# Tests that also serve as the specification for the sciolyff file format
|
8
|
-
#
|
9
|
-
class Events < Minitest::Test
|
10
|
-
def setup
|
11
|
-
skip unless SciolyFF.rep.instance_of? Hash
|
12
|
-
@events = SciolyFF.rep[:Events]
|
13
|
-
skip unless @events.instance_of? Array
|
14
|
-
end
|
15
|
-
|
16
|
-
def test_has_valid_events
|
17
|
-
@events.each do |event|
|
18
|
-
assert_instance_of Hash, event
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def test_each_event_does_not_have_extra_info
|
23
|
-
@events.select { |e| e.instance_of? Hash }.each do |event|
|
24
|
-
info = Set.new %i[name trial trialed scoring tiers]
|
25
|
-
assert Set.new(event.keys).subset? info
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def test_each_event_has_valid_name
|
30
|
-
@events.select { |e| e.instance_of? Hash }.each do |event|
|
31
|
-
assert_instance_of String, event[:name]
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def test_each_event_has_valid_trial
|
36
|
-
@events.select { |e| e.instance_of? Hash }.each do |event|
|
37
|
-
assert_includes [true, false], event[:trial] if event.key? :trial
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def test_each_event_has_valid_trialed
|
42
|
-
@events.select { |e| e.instance_of? Hash }.each do |event|
|
43
|
-
assert_includes [true, false], event[:trialed] if event.key? :trialed
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def test_each_event_has_valid_scoring
|
48
|
-
@events.select { |e| e.instance_of? Hash }.each do |event|
|
49
|
-
assert_includes %w[high low], event[:scoring] if event.key? :scoring
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def test_each_event_has_valid_tiers
|
54
|
-
@events.select { |e| e.instance_of? Hash }.each do |event|
|
55
|
-
assert_instance_of Integer, event[:tiers] if event.key? :tiers
|
56
|
-
assert_includes (1..), event[:tiers] if event.key? :tiers
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def test_each_event_has_unique_name
|
61
|
-
names = @events.select { |e| e.instance_of? Hash }
|
62
|
-
.map { |e| e[:name] }
|
63
|
-
assert_nil names.uniq!
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
data/lib/sciolyff/penalties.rb
DELETED
@@ -1,52 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'minitest/test'
|
4
|
-
require 'set'
|
5
|
-
|
6
|
-
module SciolyFF
|
7
|
-
# Tests that also serve as the specification for the sciolyff file format
|
8
|
-
#
|
9
|
-
class Penalties < Minitest::Test
|
10
|
-
def setup
|
11
|
-
skip unless SciolyFF.rep.instance_of? Hash
|
12
|
-
@penalties = SciolyFF.rep[:Penalties]
|
13
|
-
skip unless @penalties.instance_of? Array
|
14
|
-
end
|
15
|
-
|
16
|
-
def test_has_valid_penalties
|
17
|
-
@penalties.each do |penalty|
|
18
|
-
assert_instance_of Hash, penalty
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def test_each_penalty_does_not_have_extra_info
|
23
|
-
@penalties.select { |p| p.instance_of? Hash }.each do |penalty|
|
24
|
-
info = Set.new %i[team points]
|
25
|
-
assert Set.new(penalty.keys).subset? info
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def test_each_penalty_has_valid_team
|
30
|
-
@penalties.select { |p| p.instance_of? Hash }.each do |penalty|
|
31
|
-
assert_instance_of Integer, penalty[:team]
|
32
|
-
skip unless SciolyFF.rep[:Teams].instance_of? Array
|
33
|
-
|
34
|
-
team_numbers = SciolyFF.rep[:Teams].map { |t| t[:number] }
|
35
|
-
assert_includes team_numbers, penalty[:team]
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def test_each_penalty_has_valid_points
|
40
|
-
@penalties.select { |p| p.instance_of? Hash }.each do |penalty|
|
41
|
-
assert_instance_of Integer, penalty[:points]
|
42
|
-
assert penalty[:points] >= 0
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def test_penalties_are_unique_for_team
|
47
|
-
teams = @penalties.select { |p| p.instance_of? Hash }
|
48
|
-
.map { |p| p[:team] }
|
49
|
-
assert_nil teams.uniq!
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|