cli_nbadraft2019 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9cfa23e6bc57d3e16c909d8fd6128f2bcd63e5d68d2ebfa5f35b547c5658dab2
4
+ data.tar.gz: 23825f0afda64ce7435cb18e37cb13df42c4d0ca4af2235ca2de1c028b35123d
5
+ SHA512:
6
+ metadata.gz: 79fe1606c9fb483479febcce67acede3fa7b933c3058507d994553068d82a5258a3c98f5f550495586418ca7843a727df038ed180643218417544738b37387e7
7
+ data.tar.gz: 47799b3be73a3722b6369c36195ed950c07d92dd67bae8f17a76cee5d567c15997f05eefc87003764e9930019882946b87f8fcc0c3ea121c1abf0db960e038a2
data/bin/run ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../lib/cli_nbadraft19.rb"
4
+
5
+ # create new run object and run the code
6
+ CLI_NBADraft19.new.run
@@ -0,0 +1,107 @@
1
+ require 'nokogiri'
2
+ require 'open-uri'
3
+ require 'pry'
4
+
5
+ require_relative "../lib/scraper.rb"
6
+ require_relative "../lib/player.rb"
7
+
8
+ # Make URL a class constant
9
+ URL = "https://www.si.com/nba/2019/nba-draft-big-board-top-100-player-rankings"
10
+
11
+ # class for CLI for NBA Draft 19 application
12
+ class CLI_NBADraft19
13
+ # run method to first scrape, then put the results into a Player object
14
+ def run
15
+ playarray=Scraper.scrape_player(URL)
16
+ #binding.pry
17
+
18
+ # add players to Player class from playarray (player array)
19
+ Player.add_players(playarray)
20
+
21
+ # use the player_menu to give user a CLI
22
+ player_menu
23
+ end
24
+
25
+ # Player menu -- displays a basic menu and asks for a user choice
26
+ def player_menu
27
+ #call menu_marquee
28
+ menu_marquee
29
+ choice = 0
30
+
31
+ # keep asking for input until an X or x is received (for Exit)
32
+ until (choice == "X" || choice == 'x') do
33
+ puts "\nEnter choice>"
34
+ choice=gets.chomp
35
+
36
+ # case statement highlighting different choices for the menu
37
+ case choice
38
+
39
+ when "1" # choice 1: lists all players (not paginated)
40
+ Player.all.each_with_index {|player,i| puts "#{i+1} #{player.name}"}
41
+ when "2" # choice 2: ask for a player name and display more info for that player
42
+ # calls method for player display
43
+ player_submenu
44
+ when "3" # choice 3 calls method for submenu for searching on a player's name and display more info
45
+ # for that player
46
+ player_name_submenu
47
+ when "4"
48
+ # Choice 4 asks for player position to search on and lists all players that play a given position
49
+ puts "Enter player position"
50
+ posn_choice=gets.chomp
51
+ puts "\nThese are all players that play #{posn_choice}"
52
+ (Player.find_by_posn (posn_choice)).each {|player| puts "#{player.name}"}
53
+ when "5"
54
+ # Choice 5 Enter the school or club name
55
+ puts "Enter the school/club name"
56
+ sclub_choice = gets.chomp
57
+ puts "\nThese are the players from school or club: #{sclub_choice} :"
58
+ # calls class method for finding by school or club (sclub)
59
+ (Player.find_by_sclub (sclub_choice)).each {|player| puts "#{player.name}"}
60
+ when "X","x"
61
+ puts "\nExiting the CLI Program"
62
+ else
63
+ # calls method for displaying the menu again...displaying the choices for
64
+ menu_marquee
65
+ end
66
+ end
67
+
68
+ end
69
+
70
+ # method for executing code that asks for a player number
71
+ def player_submenu
72
+ # Asks for the player number in the Top 100 list
73
+ puts "Enter the number (e.g. 1, 2,...100) of the player>"
74
+ # uses get.chomp to get the player number, convert to integer
75
+ choice=gets.chomp.to_i
76
+ # finds the player
77
+ player=Player.all[choice-1]
78
+ # displays player_info (2 steps for easier debugging)
79
+ player.display_info
80
+ end
81
+
82
+ # method for executing code that asks for a player name and returns more detailed info on
83
+ # that player
84
+ def player_name_submenu
85
+ puts "Enter player name (can be only first or last name)>"
86
+ # get user input for name
87
+ name_choice = gets.chomp
88
+ # searches for first occurrence of name, displays player info
89
+ (Player.find_by_name (name_choice)).display_info
90
+ end
91
+
92
+ private #make menu_marquee so that it can't be called by other methods
93
+ # clears the screen and displays the user choices
94
+ def menu_marquee
95
+ system ("clear")
96
+ puts"---------------------------------------------------"
97
+ puts "Welcome to the 2019 NBA Draft Top 100 Prospect List"
98
+ puts "\tData (c) 2019 Sports Illustrated"
99
+ puts "(1) List Top 100 by name"
100
+ puts "(2) Get in-depth info about player by number on list"
101
+ puts "(3) Get in-depth info about player by name"
102
+ puts "(4) Search for All Players by Position"
103
+ puts "(5) Search for All Players by School/Club"
104
+ puts "X to Exit"
105
+ end
106
+
107
+ end
@@ -0,0 +1,73 @@
1
+ require 'nokogiri'
2
+ require 'open-uri'
3
+ require 'pry'
4
+
5
+ #class Player for objects that each represent one player
6
+
7
+
8
+ class Player
9
+
10
+ # attr_accessors (macros for setter and getter methods) for different attribues of Player
11
+
12
+ attr_accessor :name,:position,:schoolclub,:class_year,:height,:weight,:age,:last_rank,:blurb,:rank
13
+ @@all=[]
14
+
15
+ # initialize a new instance of Player...reads in a player hash, creates a new instance
16
+ def initialize(phashrow)
17
+
18
+ phashrow.each do |key, val|
19
+ self.send("#{key}=", "#{val}")
20
+ end
21
+ # then shovels the instance into @@all
22
+ @@all << self
23
+
24
+ end
25
+
26
+ # iterate through an array of player hashes: for each player, instantiate a new Player instance
27
+ def self.add_players(playerarray)
28
+ playerarray.each do |player_hash|
29
+ self.new(player_hash)
30
+ end
31
+ end
32
+
33
+ #return all Player elements
34
+ def self.all
35
+ @@all
36
+ end
37
+
38
+ #return first player by name
39
+ def self.find_by_name(name)
40
+ playr=@@all.find {|player| player.name.downcase.include?(name.downcase)}
41
+ # binding.pry
42
+ playr
43
+ end
44
+
45
+ # return an array of all players in the Player Class by school/club
46
+ def self.find_by_sclub(sclub)
47
+ @@all.select {|player| player.schoolclub == sclub}
48
+ end
49
+
50
+ #return an array of all players in the Player Class by a given position
51
+
52
+ def self.find_by_posn(posn)
53
+ @@all.select {|player| player.position.include?(posn)}
54
+ end
55
+ # displays detailed info for an instance of Player
56
+ def display_info
57
+
58
+ puts "\nPlayer Name: #{self.name}"
59
+ puts "Current Rank: #{self.rank}"
60
+ puts "Position: #{self.position}"
61
+ puts "School/Club: #{self.schoolclub}"
62
+ # binding.pry
63
+ #if there is no class_year, nothing is displayed
64
+ puts "Year: #{self.class_year}"
65
+ #
66
+ puts "Height/Weight: #{self.height}, #{self.weight} "
67
+ puts "Age: #{self.age}"
68
+ puts "Last rank: #{self.last_rank}"
69
+ puts "Scouting Report: #{self.blurb}"
70
+ puts "------------------------------------"
71
+ end
72
+
73
+ end
@@ -0,0 +1,116 @@
1
+ require 'nokogiri'
2
+ require 'open-uri'
3
+ require 'pry'
4
+ # require_relative '../lib/player.rb'
5
+
6
+ NBSP = 160.chr("UTF-8")
7
+
8
+ class Scraper
9
+
10
+ @@all=[]
11
+
12
+ # scrape the player data from url / html file
13
+ def self.scrape_player(url)
14
+ newarray=[]
15
+ # I decided to scrape twice because I found too many irregular HTML elements and an irregular number of elements between the headline block and the particular heading-container
16
+ # I was looking to scrape. Sometimes this included a second heading-container containing a NBSP or other elements.
17
+ # There was no easy way to go from the block containing the name to the correct heading-container block (maybe iterate until you can find the next correct heading-container block.)
18
+
19
+ #scrape the name, school, year, etc for all players
20
+ player_id_arr=scrape_page_head(url)
21
+ #scrape the height, weight, scouting report
22
+ player_info_arr=scrape_phys_meas(url)
23
+
24
+ # merge the two parts of data -- the player info and some of the additional data
25
+ newarray=(player_id_arr.zip (player_info_arr)).map do |row|
26
+ row[0].merge(row[1])
27
+
28
+ end
29
+ newarray
30
+ end
31
+
32
+ # the two scraper methods are made Class private so that they cannot be called from outside the Class
33
+ class << self
34
+ private
35
+
36
+ # had to scrape the data from the page in two passes because of too many irregularities in the HTML/CSS code
37
+ # first scrape the names
38
+ def scrape_page_head(url)
39
+ i=0
40
+ html = open(url)
41
+ # binding.pry
42
+ playerindex = Nokogiri::HTML(html)
43
+
44
+ # initialize array of player info hashes
45
+ playhasharr = []
46
+
47
+ # sxcrape to get all the player information headline-blocks (player name, position, school/club)
48
+ pheadarr=playerindex.css ("div.component.headline-block")
49
+
50
+ # put the name, position, school/club, year elements into a hash, then put the hash into an array
51
+ pheadarr.each do |phrow|
52
+ playhashrow={}
53
+ # create playhashrow -- a hash for name, position, school/club, year
54
+
55
+ # Get the name from div.head (div tag, class=head). The split(",").first is to remove a comma
56
+ # in the last name on the list
57
+ playhashrow[:name]=(phrow.css("div.text-block div.head").text).split(",").first
58
+
59
+ # schoolclub variable here actually is "position|schoolclub, year"
60
+ schoolclub=phrow.css("div.text-block div.subhead").text
61
+ # get the player's position here in the first part of schoolclub (originally named split1)
62
+ playhashrow[:position]=schoolclub.split(",").first
63
+ # split1=prevexp.split(",").last.split("|").first
64
+
65
+ # Get the school / club name from the headline block
66
+ playhashrow[:schoolclub]=schoolclub.split(",").last.split("|").first.strip
67
+ # Get the class year
68
+ class_year = schoolclub.split(",").last.split("|").last.strip
69
+ # The way that the schoolclub variabkle
70
+ if class_year !=playhashrow[:schoolclub]
71
+ playhashrow[:class_year]=class_year
72
+
73
+ end
74
+ # put the current rank into the player hash (to avoid having to search later)
75
+ playhashrow[:rank]=i+1
76
+ # binding.pry
77
+ playhasharr << playhashrow
78
+ # puts ("number #{i+1}")
79
+ i+=1
80
+ end
81
+
82
+ playhasharr
83
+ end
84
+
85
+ # need to scrape in two passes because of odd extra div nodes /lines in the HTML
86
+ def scrape_phys_meas(url)
87
+ html = open(url)
88
+ playerindex = Nokogiri::HTML(html)
89
+ containarr=playerindex.css ("div.component.heading-container")
90
+
91
+ containarr_mod=containarr.select {|prow| (prow.text).include?("Height")}
92
+ containhasharr=[]
93
+ containarr_mod.each do |pmeas_node|
94
+ row_hash={}
95
+ pm_node_text=pmeas_node.text
96
+ # binding.pry
97
+ htwt = pm_node_text.gsub(NBSP,"").strip
98
+ blurb=pmeas_node.next_element.text
99
+
100
+ row_hash[:height]=(htwt.split("|").first).split(":").last.strip
101
+ row_hash[:weight]=(htwt.split("|")[1]).split(":").last.strip
102
+ row_hash[:age]=((htwt.split("|")[2]).split(":").last.strip).to_i
103
+ lastitem=(htwt.split("|")[3])
104
+ if lastitem != nil # this is because most entries with no last rank have "Last Rank: NR" BUT one does not have any entries
105
+ row_hash[:last_rank]=lastitem.split(":").last.strip
106
+ # binding.pry
107
+ end
108
+ row_hash[:blurb] = blurb
109
+ containhasharr << row_hash
110
+ end
111
+ # binding.pry
112
+ containhasharr
113
+ end
114
+ end
115
+
116
+ end
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cli_nbadraft2019
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kane Koo
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-06-10 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: CLI gem reads/scrapes Top NBA draft prospect page. Allows user to get
14
+ more in-depth information by name or by rank.
15
+ email: rubycoder@example.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - bin/run
21
+ - lib/cli_nbadraft19.rb
22
+ - lib/player.rb
23
+ - lib/scraper.rb
24
+ homepage: https://rubygems.org/gems/cli_nbadraft2019
25
+ licenses:
26
+ - MIT
27
+ metadata: {}
28
+ post_install_message:
29
+ rdoc_options: []
30
+ require_paths:
31
+ - lib
32
+ required_ruby_version: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ required_rubygems_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ requirements: []
43
+ rubygems_version: 3.0.3
44
+ signing_key:
45
+ specification_version: 4
46
+ summary: CLI for reading SI's top 100 NBA draft prospect page
47
+ test_files: []