sutazekarate 0.0.1

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