Top Level Namespace

Defined Under Namespace

Modules: Fifadat Classes: Fifa

Instance Method Summary collapse

Instance Method Details

#_deep_stringify_keys(obj) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
# File 'lib/fifadat/json.rb', line 45

def _deep_stringify_keys(obj)
  case obj
  when Hash
    obj.transform_keys(&:to_s)
       .transform_values { |v| _deep_stringify_keys(v) }
  when Array
    obj.map { |v| _deep_stringify_keys(v) }
  else
    obj
  end
end

say hello



26
# File 'lib/fifadat.rb', line 26

puts Fifadat.banner

#cocos ⇒ Object

check if incl webclient already?



6
# File 'lib/fifadat.rb', line 6

require 'cocos'

#fetch_json(url, path = nil, headers: nil) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/fifadat/json.rb', line 75

def fetch_json( url, path=nil, headers: nil )
  res = if headers
            Webclient.get( url, headers: headers )
        else
            Webclient.get( url )
        end

  if res.status.ok?
    data = res.json
    ## pp data

    puts "OK - fetching #{url}"

    write_json_v2( path, data )    if path

    data
  else
    puts "   #{url}"
    puts "!! ERROR  #{res.status.code} #{res.status.message}"
    exit 1
  end
end

#fetch_json_if(url, outpath, force: false) ⇒ Object



100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/fifadat/json.rb', line 100

def fetch_json_if( url, outpath, force: false )
   delay_in_secs = 1   ## 1 sec


   if force == false && File.exist?( outpath )
     puts "CACHE HIT - #{outpath}"
     return nil
   end

   data = fetch_json( url, outpath )
   puts "  sleeping #{delay_in_secs} sec(s)..."
   sleep( delay_in_secs )

   data
end

#prepare(name:, season:, outdir: '.', force: false) ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/fifadat/prepare.rb', line 7

def prepare( name:,
             season:,
             outdir: '.',
             force: false )

 ###

 ### rename name to slug or such

 ##   e.g.   worldcup, clubworldcup, interconticup or such expected!!!


    season = Season(season)

    idComp   = Fifa._idComp_by!( name: name )

    idSeason = Fifa._idSeason_by!( name: name, season: season )

    fetch_json_if( Fifa::Metal.matches_url( idSeason: idSeason ),
                "#{outdir}/#{name}/#{season.to_path}_matches.json", force: force )

    fetch_json_if( Fifa::Metal.stages_url( idSeason: idSeason ),
               "#{outdir}/#{name}/misc/#{season.to_path}_stages.json", force: force )

    fetch_json_if( Fifa::Metal.squads_url( idCompetition: idComp,
                                         idSeason:      idSeason ),
               "#{outdir}/#{name}/misc/#{season.to_path}_squads.json", force: force )

end

#prepare_reports(name:, season:, outdir: '.', force: false) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/fifadat/prepare.rb', line 36

def prepare_reports( name:,
                     season:,
                     outdir: '.',
                     force: false )

  ## download match reports (via live/football)

    season = Season(season)

     data = read_json( "#{outdir}/#{name}/#{season.to_path}_matches.json" )
     matches = data['Results']


    puts "  #{matches.size} match(es) in season #{season}"


    matches.each_with_index do |m, i|
      idCompetition = m['IdCompetition']
      idSeason      = m['IdSeason']
      idStage       = m['IdStage']
      idMatch       = m['IdMatch']

      stageName   = desc( m['StageName'] )


      ## note - skip if teams not yet know

      ##  e.g. Home & Away is null

      ##  e.g.    "Home": null,

      ##          "Away": null,


      ## todo/fix - check for MatchStatus and ResultType too

      ##  e.g. MatchStatus = 1  -- future

      ##       ResultType  = 0  -- not played yet


      if m['Home'].nil? || m['Away'].nil?
         puts "[#{i+1}/#{matches.size}]  ??  ??, #{stageName}  (SKIPPED - TO BE DONE)"
         next
      end


      dateTime       = parse_date( m['Date'] )    ## utc

      localDateTime  = parse_date( m['LocalDate'] )


      ## pp m['Home']

      teamName1   = desc( m['Home']['TeamName'] )
      teamCode1   = m['Home']['Abbreviation']

      ## pp m['Away']

      teamName2   = desc( m['Away']['TeamName'] )
      teamCode2   = m['Away']['Abbreviation']


      puts "[#{i+1}/#{matches.size}]  #{teamName1} #{teamName2}, #{stageName}, #{localDateTime}"


      outpath = "#{outdir}/#{name}/matches/#{season.to_path}/#{localDateTime.strftime('%Y-%m-%d')}_#{teamCode1}-#{teamCode2}__#{idMatch}.json"

      url = Fifa::Metal.live_url( idCompetition: idCompetition,
                                  idSeason:      idSeason,
                                  idStage:       idStage,
                                 idMatch:       idMatch )

      fetch_json_if( url, outpath, force: force )


      ###

      ##   add timeline (only)  if score incl. penalty shoot-out

      resultType = m['ResultType']
      if resultType == 2 ## (aet??) win on pens


        ## download timeline

        outpath = "#{outdir}/#{name}/timelines/#{season.to_path}/#{localDateTime.strftime('%Y-%m-%d')}_#{teamCode1}-#{teamCode2}__#{idMatch}.json"

        url = Fifa::Metal.timeline_url( idCompetition: idCompetition,
                                idSeason:      idSeason,
                                idStage:       idStage,
                                idMatch:       idMatch )

       fetch_json_if( url, outpath, force: force )
    end
  end
end

#read_json_v2(path) ⇒ Object



2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/fifadat/json.rb', line 2

def read_json_v2( path )
  txt = read_text( path )

  ## \u00A0 - non-breaking space

  txt = txt.gsub( /[\u00A0]/, ' ' )

  ## (auto-)remove ™ (e.g. FIFA World Cup™)

  txt = txt.gsub( '™', '' )

  ## check for non-breaking space

  ## fix-fix-fix add unicode normalize!!

  txt = txt.unicode_normalize(:nfc)


## !! ASSERT FAILED - official name alpha expected; got "Luí­s Carlos Mateus Filipe"

##   Luí­s Carlos Mateus Filipe

##    "Luí­s Carlos


##   Luí­s Carlos Mateus Filipe

#        [{"Locale": "en-gb", "Description": "Luí­s Carlos Mateus Filipe"}],

## !! ASSERT FAILED - official name alpha expected;

##   got >Luí­s Carlos Mateus Filipe<

## => L (76) | u (117) | í (237) | ­ (173) | s (115) |   (32) | C (67) | a (97) | r (114) | l (108) | o (111) | s (115) |   (32) | M (77) | a (97) | t (116) | e (101) | u (117) | s (115) |   (32) | F (70) | i (105) | l (108) | i (105) | p (112) | e (101) |


  ## You have run straight into a textbook encoding double-decode bug.

  ## The actual issue is even more interesting than it looks:

  ##  Character 173 (0xAD) shouldn't even be there.

  ## It only exists in your text because a clean UTF-8 string

  ## was forced through a bad translation process,

  ##  ripping the letter í completely in half.

  ##

  # Clean the string by removing character 173

  ##    encoding error in   pt/2025-26_matches.json

  ##     double check upstream source

  txt = txt.gsub(/\u{AD}/, '')
  ### # Or using delete

  ##  txt = txt.delete("\u{AD}")


  parse_json( txt )
end

#write_json_v2(path, data) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/fifadat/json.rb', line 57

def write_json_v2( path, data )
  ## note - always stringify keys for now

  ##            e.g. { name: "" } => { "name": "" }

    data = _deep_stringify_keys( data )

   ## hack - use pretty_inspect for json pretty print

    txtjson =  data.pretty_inspect

    txtjson = txtjson.gsub( '=>', ': ' )
    txtjson = txtjson.gsub( /\bnil\b/, 'null' )

    ## double check for syntax errors

    json = JSON.parse( txtjson )

    write_text( path, txtjson )
end