sutazekarate 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: eae254383108ccb05e4697c0c45278a5f89516b69dfcd7d030460bbcd64d32ec
4
+ data.tar.gz: 9dd2c18dd3460b48f4114104f283511ce628e0d3cb88f0690c2408de841d0095
5
+ SHA512:
6
+ metadata.gz: b440cb40afe838d89f231324e526243173ffeba1533c4743d64ab1105134dce1d002094f0747886c3f4e89d10f0f0bc50abf30bdb8cc4ee163d527e74f26c629
7
+ data.tar.gz: 537289598cbeebf9d35d00509d98bfe30f7eb1625b11e57ec2550df636af0278fdafc67577b857a41ac76b0c60d670667e35987296fd24a36d857de7f33a9094
data/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # Sutazekarate
2
+
3
+ Web scraper for www.sutazekarate.sk
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'sutazekarate'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install sutazekarate
20
+
21
+ ## Development
22
+
23
+ After checking out the repo, run `bin/console` for an interactive prompt that will allow you to experiment.
24
+
25
+ To install this gem onto your local machine, run `bundle exec rake install`.
@@ -0,0 +1,119 @@
1
+ module Sutazekarate
2
+ class Category
3
+ include ActiveModel::Model
4
+ include ActiveModel::Attributes
5
+ include ActiveModel::Serializers::JSON
6
+
7
+ include Logging
8
+ include Concurrent::Async
9
+
10
+ attribute :id
11
+ attribute :position
12
+ attribute :name
13
+ attribute :discipline
14
+ attribute :location
15
+ attribute :location_color
16
+ attribute :time_range
17
+
18
+ def competitors
19
+ @competitors ||= begin
20
+ logger.debug("Fetching competitors for category #{id}")
21
+ response = HTTP.get("https://www.sutazekarate.sk/ajax/av_kategoriazoz.php?kategoria=#{id}&order=asc&limit=1000&offset=0")
22
+ rows = JSON.parse(response.body.to_s)
23
+ rows.map do |row|
24
+ Competitor.build(row)
25
+ end
26
+ end
27
+ end
28
+
29
+ def ladder
30
+ @ladder ||= begin
31
+ logger.debug("Fetching ladder for category #{id}")
32
+ response = HTTP.get("https://www.sutazekarate.sk/sutaze_kategoriarozl.php?k=#{id}")
33
+ html = Nokogiri::HTML5(response.body.to_s)
34
+
35
+ export_element = html.search('a').find do |elem|
36
+ elem.attr('href').start_with?('pdf_rozlosovanieexport.php')
37
+ end
38
+ export_url = "https://www.sutazekarate.sk/#{export_element.attr('href')}"
39
+
40
+ stages = []
41
+
42
+ stage_index = 0
43
+ loop do
44
+ stage_element = html.search(".stlpecn.posun#{stage_index}").first
45
+ unless stage_element
46
+ break
47
+ end
48
+
49
+ pairs = stage_element.search('.obalao').map.with_index do |pair_element, pair_index|
50
+ competitors = pair_element.search('.okienkopavukR').map do |competitor_element|
51
+ id_element = competitor_element.search('#sutaziaci').first
52
+ unless id_element
53
+ next nil
54
+ end
55
+
56
+ id = competitor_element.search('#sutaziaci').first.attr('value')
57
+ name = competitor_element.search('.meno').text.strip
58
+ club = competitor_element.search('.klub').text.strip
59
+
60
+ Competitor.new(
61
+ id: id,
62
+ name: name,
63
+ club: Club.new(name: club),
64
+ )
65
+ end
66
+
67
+ Pair.new(
68
+ index: pair_index,
69
+ competitor1: competitors[0],
70
+ competitor2: competitors[1],
71
+ )
72
+ end
73
+
74
+ stages << Stage.new(
75
+ index: stage_index,
76
+ pairs:,
77
+ )
78
+
79
+ stage_index += 1
80
+ end
81
+
82
+ Ladder.new(
83
+ export_url:,
84
+ stages:,
85
+ )
86
+ end
87
+ end
88
+
89
+ def self.build(data)
90
+ detail_element = Nokogiri::HTML5.fragment(data['detail'])
91
+ id = Addressable::URI.parse(detail_element.search('a').first.attr('href')).query_values['k']
92
+ category_element = Nokogiri::HTML5.fragment(data['kategoria'])
93
+ category_span_elements = category_element.search('span')
94
+ name = category_span_elements[0].text.strip
95
+ detail_element = category_span_elements[1]
96
+ location = nil
97
+ location_color = nil
98
+ time_range = nil
99
+ if detail_element
100
+ location_color = detail_element.attr('class').match(/c-(\w+)/)[1]
101
+ detail_match = detail_element.text.strip.match(/(.+) \/(.+) - (.+)\//)
102
+
103
+ location = detail_match[1]
104
+ time_range = Time.zone.parse(detail_match[2])..Time.zone.parse(detail_match[3])
105
+ end
106
+ discipline = Nokogiri::HTML5.fragment(data['disciplina']).text.strip
107
+
108
+ new(
109
+ id:,
110
+ position: data['pc'],
111
+ name:,
112
+ discipline:,
113
+ location:,
114
+ location_color:,
115
+ time_range:,
116
+ )
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,10 @@
1
+ module Sutazekarate
2
+ class Club
3
+ include ActiveModel::Model
4
+ include ActiveModel::Attributes
5
+ include ActiveModel::Serializers::JSON
6
+
7
+ attribute :id
8
+ attribute :name
9
+ end
10
+ end
@@ -0,0 +1,75 @@
1
+ module Sutazekarate
2
+ class Competition
3
+ include ActiveModel::Model
4
+ include ActiveModel::Attributes
5
+ include ActiveModel::Serializers::JSON
6
+
7
+ include Logging
8
+ class << self
9
+ include Logging
10
+ end
11
+
12
+ include Concurrent::Async
13
+
14
+ attribute :id
15
+ attribute :starts_at
16
+ attribute :name
17
+ attribute :club
18
+ attribute :location
19
+ attribute :note
20
+ attribute :registration_starts_at
21
+ attribute :registration_ends_at
22
+
23
+ def categories
24
+ @categories ||= begin
25
+ logger.debug("Fetching categories for competition #{id}")
26
+ response = HTTP.get("https://www.sutazekarate.sk/ajax/av_sutazkat.php?lang=sk&sutaz=#{id}&order=asc&limit=1000&offset=0")
27
+ rows = JSON.parse(response.body.to_s)
28
+ rows.map do |row|
29
+ Category.build(row)
30
+ end
31
+ end
32
+ end
33
+
34
+ def self.all(year: Date.today.year)
35
+ logger.debug("Fetching competitions for year #{year}")
36
+ response = HTTP.get("https://www.sutazekarate.sk/ajax/av_sutaze.php?lang=sk&r=#{year}&sort=datum&order=desc&limit=1000&offset=0")
37
+ rows = JSON.parse(response.body.to_s)['rows']
38
+ rows.map do |row|
39
+ starts_at = Date.parse(row['datum'])
40
+ content = Nokogiri::HTML5.fragment(row['obsah'])
41
+
42
+ name = content.search('h4').first.text.strip
43
+ club_element = content.search('h5').first
44
+ club = club_element.text.strip
45
+ id = Addressable::URI.parse(content.search('a').first.attr('href')).query_values['sutaz']
46
+ location = club_element.next_sibling.text.strip
47
+ note_element = content.search('br').first.next_sibling
48
+ note = if note_element.text?
49
+ note_element.text
50
+ else
51
+ nil
52
+ end
53
+
54
+ registration_info_element = content.search('span').find do |elem|
55
+ elem.text.include?('Registrácia: ')
56
+ end
57
+ registration_info = registration_info_element.text
58
+ registration_info_match = registration_info.match(/Registrácia: (.+) - (.+)/)
59
+ registration_starts_at = Date.parse(registration_info_match[1])
60
+ registration_ends_at = Date.parse(registration_info_match[2])
61
+
62
+ Competition.new(
63
+ id:,
64
+ starts_at:,
65
+ name:,
66
+ club:,
67
+ location:,
68
+ note:,
69
+ registration_starts_at:,
70
+ registration_ends_at:,
71
+ )
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,30 @@
1
+ module Sutazekarate
2
+ class Competitor
3
+ include ActiveModel::Model
4
+ include ActiveModel::Attributes
5
+ include ActiveModel::Serializers::JSON
6
+
7
+ attribute :id
8
+ attribute :name
9
+ attribute :club
10
+
11
+ def self.build(data)
12
+ name_element = Nokogiri::HTML5.fragment(data['meno'])
13
+ name_link_element = name_element.search('a')
14
+ id = Addressable::URI.parse(name_link_element.attr('href')).query_values['k']
15
+ name = name_link_element.text.strip
16
+
17
+ club_link_element = Nokogiri::HTML5.fragment(data['klub']).search('a')
18
+
19
+ club_id = Addressable::URI.parse(club_link_element.attr('href')).query_values['klub']
20
+ club_name = club_link_element.text.strip
21
+ club = Club.new(id: club_id, name: club_name)
22
+
23
+ new(
24
+ id:,
25
+ name:,
26
+ club:,
27
+ )
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,10 @@
1
+ module Sutazekarate
2
+ class Ladder
3
+ include ActiveModel::Model
4
+ include ActiveModel::Attributes
5
+ include ActiveModel::Serializers::JSON
6
+
7
+ attribute :export_url
8
+ attribute :stages
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ require 'logger'
2
+
3
+ module Sutazekarate
4
+ module Logging
5
+ def logger
6
+ @logger ||= ActiveSupport::Logger.new(STDERR)
7
+ .tap { |logger| logger.formatter = ::Logger::Formatter.new }
8
+ .then { |logger| ActiveSupport::TaggedLogging.new(logger) }
9
+ .then { |logger| logger.tagged(self.class.to_s) }
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ module Sutazekarate
2
+ class Pair
3
+ include ActiveModel::Model
4
+ include ActiveModel::Attributes
5
+ include ActiveModel::Serializers::JSON
6
+
7
+ attribute :index
8
+ attribute :competitor1
9
+ attribute :competitor2
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ module Sutazekarate
2
+ class Stage
3
+ include ActiveModel::Model
4
+ include ActiveModel::Attributes
5
+ include ActiveModel::Serializers::JSON
6
+
7
+ attribute :index
8
+ attribute :pairs
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ module Sutazekarate
2
+ VERSION = '0.0.1'.freeze
3
+ end
@@ -0,0 +1,15 @@
1
+ require 'zeitwerk'
2
+ require 'active_support/all'
3
+ require 'active_model'
4
+ require 'http'
5
+ require 'nokogiri'
6
+
7
+ Time.zone_default = Time.find_zone!(ENV.fetch('TZ', 'Europe/Bratislava'))
8
+
9
+ loader = Zeitwerk::Loader.for_gem
10
+ loader.setup
11
+
12
+ module Sutazekarate
13
+ end
14
+
15
+ loader.eager_load
metadata ADDED
@@ -0,0 +1,155 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sutazekarate
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ahmed Al Hafoudh
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-09-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: http
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: nokogiri
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.16'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.16'
41
+ - !ruby/object:Gem::Dependency
42
+ name: zeitwerk
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.6'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.6'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activesupport
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '7.2'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '7.2'
69
+ - !ruby/object:Gem::Dependency
70
+ name: activemodel
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '7.2'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '7.2'
83
+ - !ruby/object:Gem::Dependency
84
+ name: concurrent-ruby
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.3'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.3'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.14.2
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.14.2
111
+ description: Web scraper for www.sutazekarate.sk what read all competition data and
112
+ allows to explore them in structured way.
113
+ email:
114
+ - alhafoudh@freevision.sk
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - README.md
120
+ - lib/sutazekarate.rb
121
+ - lib/sutazekarate/category.rb
122
+ - lib/sutazekarate/club.rb
123
+ - lib/sutazekarate/competition.rb
124
+ - lib/sutazekarate/competitor.rb
125
+ - lib/sutazekarate/ladder.rb
126
+ - lib/sutazekarate/logging.rb
127
+ - lib/sutazekarate/pair.rb
128
+ - lib/sutazekarate/stage.rb
129
+ - lib/sutazekarate/version.rb
130
+ homepage: https://www.freevision.sk
131
+ licenses:
132
+ - MIT
133
+ metadata:
134
+ homepage_uri: https://www.freevision.sk
135
+ rubygems_mfa_required: 'true'
136
+ post_install_message:
137
+ rdoc_options: []
138
+ require_paths:
139
+ - lib
140
+ required_ruby_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: 3.2.1
145
+ required_rubygems_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ requirements: []
151
+ rubygems_version: 3.5.17
152
+ signing_key:
153
+ specification_version: 4
154
+ summary: Web scraper for www.sutazekarate.sk
155
+ test_files: []