<file_sep># Show differences between two playlists.
# Playlists are created using gtkpod with two different ishuffle devices (green and silver)
# In order to sync files use gtkpod instead of the rhythmbox, because dragging files between two devices
# does not works.
# Create a list in gtkpod using Tools->Export Tracks -> export to playlist

require 'rubygems'


class PlayListDifference

  def show_diff(master, slave, options)
    count_item = 0
    slave_name = options.has_key?(:slave_name) ? options[:slave_name] : "slave"
    File.open(master, 'r').each_line do |line|
      if line =~ /EXTINF/
        title = line.split(',')[1].strip.gsub('\n', '')
        #puts title
        present_in_slave_info = check_title_in_list(slave, title)
        present_in_slave = present_in_slave_info[:exact] > 0
        partial_in_slave = present_in_slave_info[:partial] > 0
        if !present_in_slave
          if !partial_in_slave
          	puts "'#{title}' is missed in #{slave_name}"
          elsif options[:print_partial] == true
            puts "'#{title}' is partial in #{slave_name}: '#{present_in_slave_info[:partial_titles]}'"
          end
        end
        count_item = count_item + 1
      end
    end
    puts "Found #{count_item} titles"
  end
  
  def check_title_in_list(fname, tobe_found)
    count_item = 0
    partial = 0
    partial_arr = []
    arr = tobe_found.split('- ')
    song_title_only = arr.length > 0 ? arr[arr.length - 1].gsub('.mp3', "") : nil
    File.open(fname, 'r').each_line do |line|
      if line =~ /EXTINF/
        title = line.split(',')[1].strip.gsub('\n', '')
        #if title.include?(tobe_found)
        if title == tobe_found
          #puts "Title '#{tobe_found}' found in '#{title}'"
          count_item += 1
        else
          if song_title_only != nil
          	if title.include?(song_title_only)
          		partial += 1
          		partial_arr << title
          	end
          else
            #p tobe_found
        	end
        end
      end
    end
    if partial == 0 and count_item == 0
      #p song_title_only
    end
    return {:exact => count_item, :partial => partial, :partial_titles => partial_arr} 
  end
  
end


if $0 == __FILE__
  silver_file = 'silver_playlist_2015_08_19'
  green_file = 'green_playlist_2015_08_19'
  diff = PlayListDifference.new
  
  diff.show_diff(green_file, silver_file, {:print_partial => false, :slave_name => "silver"})
  diff.show_diff(silver_file, green_file,  {:print_partial => false, :slave_name => "green"})
end
<file_sep># -*- coding: ISO-8859-1 -*-

require 'erb'


fullname = File.dirname(__FILE__) + "/bs_import.rtemp"
file = File.new(fullname, "r")
template = ERB.new(file.read)
file.close

(31..60).each do |ix|
  @bsname = "BS"
	@bsname += ix < 10 ? "0#{ix}" : "#{ix}"
	puts aString = template.result(binding)
end<file_sep>ruby "2.3.1"
source "https://rubygems.org"

gem "pg", '~>0.19.0'
gem "log4r"
gem 'mechanize', '~>2.7.7'<file_sep>ruby_scratch
============

some ruby scripts files
<file_sep>-- statistiche dell'anno
-- Questo script non va lanciato in modo completo, ma vanno selezionate le righe che interressano e poi si preme F5.
-- Uso pgAdmin4 Tools -> Query Tool e qui si carica questo script
-- Il db si aggiorna usando ruby race_picker.rb sotto WLC

-- gare a partire da una data, statistica annuale:
--COPY (
select r.id, r.name, r.title, r.km_length, r.ascending_meter, r.race_date, s.name, r.race_time, r.rank_global, r.rank_class, r.id from race as r
inner join race_subtype s on r.race_subtype_id = s.id
where r.race_date > '2022-01-01' order by r.race_date asc 
--) TO 'D:\scratch\postgres\corsa\race-2018-02.csv' With CSV DELIMITER ';' HEADER; -- export in excel con COPY e TO
-- Nota che excel interpreta questo export come ansi. Per l'utf-8, che è lo standard usato dall'export, vuole il BOM.
-- Quindi con notepad++ o converto in ansi o converto in utf8-bom. Poi apro con excel.

-- delete from race where id = 287 (usa questo delete per cancellare una corsa che risulta in conflitto nel db e poi ricaricala di nuovo con race_picker.rb)

-- sommatorie km e Hm percorsi nell'anno
select SUM(r.km_length) as Km_Tot, SUM(r.ascending_meter) as HM_Tot  from race as r
inner join race_subtype s on r.race_subtype_id = s.id
where r.race_date >= '2016-01-01' AND  r.race_date <= '2016-12-31'

-- mostra tutti i tipi di gara (serve per poi vedere quale filtro usare sul tipo)
SELECT * from race_subtype;
-- Numero di ultra
--select SUM(r.km_length) as Km_Tot, SUM(r.ascending_meter) as HM_Tot  from race as r
-- Ultra sopra i cento km
select r.name, r.title, r.km_length, r.ascending_meter as HM, r.race_date, s.name, r.race_time, r.rank_global from race as r
inner join race_subtype s on r.race_subtype_id = s.id
where
(s.id = 5 OR s.id = 6) 
--AND r.km_length >= 100
--AND r.race_date > '2017-01-01'
order by r.race_date asc 

-- Chilometri totali ultra su asfalto
select SUM(r.km_length) as Km_Tot, SUM(r.ascending_meter) as HM_Tot  from race as r
--select r.name, r.title, r.km_length, r.ascending_meter as HM, r.race_date, s.name, r.race_time, r.rank_global from race as r
inner join race_subtype s on r.race_subtype_id = s.id
where
(s.id = 5)
--order by r.race_date asc 

-- Chilometri totali ultra su trail
select SUM(r.km_length) as Km_Tot, SUM(r.ascending_meter) as HM_Tot  from race as r
--select r.name, r.title, r.km_length, r.ascending_meter as HM, r.race_date, s.name, r.race_time, r.rank_global from race as r
inner join race_subtype s on r.race_subtype_id = s.id
where
(s.id = 6) 
--order by r.race_date asc 

-- Numero di Maratone (id = 4)
--select SUM(r.km_length) as Km_Tot, SUM(r.ascending_meter) as HM_Tot  from race as r
select r.name, r.title, r.km_length, r.ascending_meter as HM, r.race_date, s.name, r.race_time, r.rank_global, r.id from race as r
inner join race_subtype s on r.race_subtype_id = s.id
where
(s.id = 4) 
order by r.race_date asc 

-- <NAME> (id = 3)
--select SUM(r.km_length) as Km_Tot, SUM(r.ascending_meter) as HM_Tot  from race as r
select r.name, r.title, r.km_length, r.ascending_meter as HM, r.race_date, s.name, r.race_time, r.rank_global, r.id from race as r
inner join race_subtype s on r.race_subtype_id = s.id
where
(s.id = 3) 
order by r.race_date asc 

-- delete from race where id = 145 or id = 259
-- select * from race where id = 191

-- Single Marathon
select r.name, r.title, r.km_length, r.ascending_meter as HM, r.race_date, s.name, r.race_time, r.rank_global, r.id from race as r
inner join race_subtype s on r.race_subtype_id = s.id
where
(s.id = 4) 
--AND r.name LIKE '%Welsch%'
AND r.name LIKE '%VCM%'
order by r.race_date asc 

-- silverster lauf id = 2 fino ai 10km
--select * from race

select r.name, r.title, r.km_length, r.pace_minkm, r.race_date, s.name, r.race_time, r.rank_global, r.id from race as r
inner join race_subtype s on r.race_subtype_id = s.id
where
(s.id = 2) 
AND r.title LIKE '%Silve%'
order by r.race_date asc 

select r.title as titolo, r.km_length as distanza, r.race_date as data, r.race_time as tempo, r.rank_global as pos from race as r
inner join race_subtype s on r.race_subtype_id = s.id
where
(s.id = 2) 
AND r.title LIKE '%Silve%'
order by r.race_date asc 

-- gare da 10km
select r.name, r.title, r.km_length, r.ascending_meter as HM, r.race_date, s.name, r.race_time, r.rank_global, r.id from race as r
inner join race_subtype s on r.race_subtype_id = s.id
where
(s.id = 2) AND r.km_length = 10

--alter table race alter column race_time SET DATA TYPE Time USING race_time::time without time zone
-- Prove con il tempo, per le somme dei tempi si usa interval
SELECT ('13:08:57'::interval + '12:07:48'::interval)
SELECT ('09:30:00'::time - '06:00:00'::time) -- risultato interval
--  SELECT (DATE_PART('hour', '08:56:10'::time - '08:54:55'::time) * 3600 +
--               DATE_PART('minute', '08:56:10'::time - '08:54:55'::time)) * 60 +
--               DATE_PART('second', '08:56:10'::time - '08:54:55'::time);


--- Irdning 2017 dettaglio laps
SELECT rd.lap_number, rd.lap_time, rd.lap_pace_minkm, rd.tot_race_time, rd.tot_km_race, rd.tot_race_minkm, 
(rd.tot_km_race / r.km_length * 100) as proc_race_percent, --accumulo della gara
(rd.tot_race_time + r.race_start) as time_in_race -- ora di gara con data
FROM racelap_detail as rd 
inner join race as r
on r.id = rd.race_id
where rd.race_id = 249
-- and (CAST (rd.lap_pace_minkm as interval)  >= '06:30'::interval) 
-- AND (CAST (rd.lap_pace_minkm as interval)  < '07:00'::interval) 
--AND (CAST (rd.lap_pace_minkm as interval)  >= '07:00'::interval) 
--and rd.lap_time <= '00:07:18'::interval -- 7:18 come lap è il tempo dei 06:00 min/km

--select 205.156 - 109 
-- delete from racelap_detail
select * from race
<file_sep>require 'rubygems'
require 'mechanize'

class EtfEasyChart
  
  def initialize
    @agent = Mechanize::Mechanize.new
    @agent.user_agent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1"
    @picked_quote = {}
  end
    
  def pick_from_easychart(url)
    page = @agent.get(url)
    data_found = []
    #'body//div//div//div//div//table//tbody//tr//td//table//tbody//tr//td//table//tbody//tr//td//table//tbody//tr//td'
    #page.search('body//div//div//div//div//td//b').set('class','last').each do |element|
    element_with_quote = false
    # devo trovare il tr con la classe 'toptitle' e poi vedere il successivo tr. Nei children td ci sono le info che  mi servono
    page.search('body//div//div//div//div//table').set('class','datatable').search('tr').each do |element|
      #p element.inner_html
      if element_with_quote
        #p element.inner_html
        i = 0
        # qui una linea separata da td di questo tipo: LU0292106167 XetraETF 18,39 24.02./17 36 -0,05/-0,27%
        element.search('td').each do |field|
          if i == 0 || i == 1 || i == 4 || i == 5
            item = field.inner_html.gsub(' ','')
            data_found << item.gsub(/\r\n/,'')
          elsif i == 2
            data_found << field.search('b')[0].inner_html
          end
          i = i + 1
          #p fields.inner_html  
        end
        break
      end
      if element.attributes['class'] && (element.attributes['class'].value == 'toptitle')
        #p element.inner_html
        element_with_quote = true # mark the next item  
      end
    end
    #p data_found
    data_found << url
    @picked_quote[data_found[0]] = data_found
  end
  
  def write_quotes(fname)
    File.open(fname,'w') do |file|
      @picked_quote.each do |k,v|
        file.write(v.join(';'))
        file.write("\n")
        puts "ISIN: #{v[0]}, quote: #{v[2]}"
      end
    end
    puts "File written #{fname}"
    #p @picked_quote
  end
  
end



if $0 == __FILE__
  #url = 'http://www.finanzen.net/etf/db_x-trackers_DBLCI_-_OY_BALANCED_UCITS_ETF_EUR_1C'
  #picker.pick_from_complete_url(url)
  puts "get_quote.rb is using hard coded urls..."
  
  urls = []
  # 1 LU0290357929  db x-trackers II iBoxx Global Infl.-linked UCITS ETF 1C
  urls << 'http://www.easycharts.at/index.asp?action=securities_securityDetails&id=tts-10904993&menuId=1&pathName=DB%20X-TR.II-IB.GL.IN.-L.1C'
  # 2 LU0290355717  db x-trackers II iBoxx Sov. Eurozone UCITS ETF 1C
  urls << 'http://www.easycharts.at/index.asp?action=securities_securityDetails&id=tts-10904984&menuId=1&pathName=DBXTR.II-EUROZ.G.B.(DR)1C'
  # 3 LU0292106167  db x-trackers DBLCI - OY Balanced UCITS ETF 1C
  urls << 'http://www.easycharts.at/index.asp?action=securities_securityDetails&id=tts-11057070&menuId=1&pathName=DB%20X-TR.DBLCI-OY%20BAL.%201C'
  # 4 LU0489337690 db x-trackers FTSE EPRA/NAREIT Dev. Europe Real Estate
  urls << 'http://www.easycharts.at/index.asp?action=securities_securityDetails&id=tts-23270949&menuId=1&pathName=DB%20X-T.FTSE%20E/N%20DE%20RE%201C'
  # 5 IE00B1FZS350  iShares Developed Markets Property Yield UCITS ETF
  urls << 'http://www.easycharts.at/index.asp?action=securities_securityDetails&id=tts-106098910&menuId=1&pathName=ISHSII-DEV.MKT.PR.Y.DLDIS'
  # 6 IE00B3VWM098  iShares MSCI USA Small Cap UCITS ETF B
  urls << 'http://www.easycharts.at/index.asp?action=securities_securityDetails&id=tts-20534544&menuId=1&pathName=ISHSVII-MSCI%20USA%20SC%20DL%20AC'
  # 7 IE00B2QWDY88  iShares MSCI Japan Small Cap UCITS ETF (Acc) B
  urls << 'http://www.easycharts.at/index.asp?action=securities_securityDetails&id=tts-75100612&menuId=1&pathName=ISHSIII-MSCI%20J.SM.CAP%20DLD'
  # 8 IE00B3VWMM18  iShares MSCI EMU Small Cap UCITS ETF B
  urls << 'http://www.easycharts.at/index.asp?action=securities_securityDetails&id=tts-20534543&menuId=1&pathName=ISHSVII-MSCI%20EMU%20SC%20EOACC'
  # 9 DE000A0D8Q49  iShares DJ U.S. Select Dividend (DE)
  urls << 'http://www.easycharts.at/index.asp?action=securities_securityDetails&id=tts-3372758&menuId=1&pathName=IS.DJ%20U.S.SELEC.DIV.U.ETF'
  #10 LU0292107645  db x-trackers MSCI EM TRN UCITS ETF 1C
  urls << 'http://www.easycharts.at/index.asp?action=securities_securityDetails&id=tts-10980122&menuId=1&pathName=DB%20X-TR.MSCI%20EM%20IDX.1C'
  #11 IE00B0M63060  iShares UK Dividend UCITS ETF
  urls << 'http://www.easycharts.at/index.asp?action=securities_securityDetails&id=tts-105446142&menuId=1&pathName=ISHS-UK%20DIVIDEND%20LS%20D'
  #12 DE000A0H0744  iShares DJ Asia Pacific Select Dividend 30
  urls << 'http://www.easycharts.at/index.asp?action=securities_securityDetails&id=tts-4076332&menuId=1&pathName=IS.DJ%20AS.PAC.S.D.30%20U.ETF'
  #13 DE0002635281  iShares EURO STOXX Select Dividend 30 (DE)
  urls << 'http://www.easycharts.at/index.asp?action=securities_securityDetails&id=tts-2794238&menuId=1&pathName=IS.EO%20ST.SEL.DIV.30%20U.ETF'
  #14 IE00BM67HM91  db x-tr.MSCI Wld.Energy I.ETF
  urls << 'http://www.easycharts.at/index.asp?action=securities_securityDetails&id=tts-102824320&menuId=1&pathName=DB-XTR.MSCI%20WEIU(PD)%201CDL'
  #15 DE000ETF9074  ComStage 1-MDAX UCITS ETF: Inhaber-Anteile IXETRA-Frankfurt
  urls << 'http://www.easycharts.at/index.asp?action=securities_chart&id=tts-97685758&menuId=1&pathName=COMSTAGE%201-MDAX%20UCI.ETF%20I'
  #16 	IE00B1FZS574  ComStage iShsII-MSCI Turkey UCITS ETF: Registered Shs USD (Dist) o.N.
  urls << 'http://www.easycharts.at/index.asp?action=securities_chart&id=tts-106098900&menuId=1&pathName=ISHSII-MSCI%20TURKEY%20DLDIS'
  
  picker = EtfEasyChart.new
  urls.each do |url|
    picker.pick_from_easychart(url)
  end
  
  picker.write_quotes('D:\scratch\csharp\PortfolioExcelChecker\PortfolioExcelChecker\bin\Debug\quote.csv')
  
end<file_sep># file: race_detail_insert.rb
$:.unshift File.dirname(__FILE__)

require 'rubygems'
require 'pg'
require 'db_baseitem'
require 'time'

############################# RaceDetailItem

class RaceDetailItem < DbBaseItem
  attr_accessor :lap_number, :lap_time, :lap_meter, :lap_pace_minkm, :tot_meter_race, :tot_race_minkm, :race_id, :tot_race_time, :tot_km_race, :completed

  def initialize
    @changed_fields = [:lap_number, :lap_time, :lap_meter, :lap_pace_minkm, :tot_meter_race, :tot_race_minkm, :race_id, :tot_race_time, :tot_km_race, :completed]
    @field_types = {:lap_number => :int, :lap_meter => :int, :tot_meter_race => :int, :completed => :boolean}
  end

end

################################ RaceDetailInsert

class RaceDetailInsert < DbConnectBase
  def initialize
    @log = Log4r::Logger["RaceDetailInsert"]
    super
  end

  def get_race_reference(title, date)
    query = "SELECT id, race_date, title, km_length, lap_meter, tot_race_time_sec  FROM race WHERE race_date = '#{date}' AND title = '#{title}' LIMIT 1"
    result = exec_query(query)
    if result.ntuples == 1
      #p result[0]
      id_res = result[0]["id"]
      km_res = result[0]["km_length"].to_f
      lap_meter = result[0]["lap_meter"].to_f
      raise "Lap is null (lap_meter), please insert it manually (race id #{id_res})" if lap_meter == 0
      tot_race_time_sec = result[0]["tot_race_time_sec"].to_i
      raise "Race time is null (tot_race_time_sec), please insert it manually (race id #{id_res})" if tot_race_time_sec == 0

      @log.debug "Referenced race is #{title} at #{date} with id #{id_res}, km #{km_res}, lap #{lap_meter}, tot time sec #{format_seconds(tot_race_time_sec)}"
      return [id_res, km_res, lap_meter, tot_race_time_sec] 
    else
      raise "Race #{title} at #{date} not found"
    end
  end

  def format_seconds(tot_time_sec)
    hh = (tot_time_sec / 3600).floor
    min = ((tot_time_sec - (3600 * hh)) / 60).floor
    ss = tot_time_sec - (3600 * hh) - (60 * min)
    time_str = pad_to_col(hh.to_s, "0", 2) + ':' + pad_to_col(min.to_s, "0", 2) + ':' + pad_to_col(ss.to_s, "0", 2) ;
  end

  #Provides a pace string. Something like "06:41" for a velocity of 8.98 km/h
  def calc_pace_in_min_sec(meter, sec)
    dist_km = meter / 1000.0
    vel_m_sec = meter / sec
    vel_med_in_kmh = vel_m_sec * 3.6
    min_part = 1000 / (vel_med_in_kmh / 3.6) / 60  # qualcosa come 4,3 min ma noi interessa tipo 4min 20sec
    pace_str = make_min_form(min_part)
  end

  # Transform the minute velocity  fraction in mm:sec format. E.g 4.3 => "04:20"
  def  make_min_form(min_part) 
    min_only = min_part.floor;
    sec_perc = min_part - min_only;
    sec_only = 60 * sec_perc;
    if (sec_only > 59.5)
        min_only += 1
        sec_only = 0
    end
    mm_formatted = pad_to_col(min_only.to_s, "0", 2) + ':' + pad_to_col(sec_only.round.to_s, "0", 2);
    return mm_formatted;
  end

  def pad_to_col(str, pad, width)
    str_res = "" + str;
    while (str_res.length < width) 
      str_res = pad + str_res
    end
    return str_res.slice(0, width)
  end 

  # lap_meter: lunghezza del lap in metri
  # tot_race_time_sec: durata della gara, esempio irdning 3600 * 24
  # km_race: km percorsi al termine della gara (finale)
  # data_file: file con i tempi parziali di ogni giro percorso
  # race_id : referenza della gara (esempio irdning id = 249)
  def create_laps(race_id, data_file, lap_meter, km_race, tot_race_time_sec, insert_last_partial)
    fname = File.join(File.dirname(__FILE__), data_file)
    lap_nr = 1
    tot_meter = 0
    tot_time_sec = 0
    @items = []
    File.open(fname, 'r').each_line do |line|
      arr_laps_str = line.split(" ")
      arr_laps_str.each do |value_s|
        #p value_s
        t1 = Time.parse(value_s)
        #p t1.methods
        lap_sec = t1.hour * 3600 + t1.min * 60 + t1.sec
        tot_time_sec += lap_sec
        tot_meter = lap_meter * lap_nr  
        item = RaceDetailItem.new
        item.lap_number = lap_nr
        item.lap_time = value_s
        item.lap_meter = lap_meter
        item.lap_pace_minkm = calc_pace_in_min_sec(lap_meter, lap_sec) 
        item.tot_meter_race = tot_meter
        item.tot_race_minkm = calc_pace_in_min_sec(tot_meter, tot_time_sec) 
        item.race_id = race_id
        item.tot_race_time = format_seconds(tot_time_sec)
        item.tot_km_race = tot_meter / 1000
        item.completed = true
        #p item
        @items << item
        lap_nr += 1
        #exit if lap_nr == 9
      end
      #p line
    end
    @log.debug "Collected #{@items.size} laps. Completed laps km #{@items.last.tot_km_race} in #{@items.last.tot_race_time} "

    # last lap is not completed but it is the difference between total km and the last completed sum
    if insert_last_partial
      t1 = Time.parse(@items.last.tot_race_time)
      completed_lap_sec = t1.hour * 3600 + t1.min * 60 + t1.sec
      lap_sec = tot_race_time_sec - completed_lap_sec
      item = RaceDetailItem.new
      item.lap_number = lap_nr
      item.lap_time = format_seconds(lap_sec)
      item.lap_meter = km_race * 1000 - @items.last.tot_km_race * 1000
      item.lap_pace_minkm = calc_pace_in_min_sec(item.lap_meter, lap_sec) 
      item.tot_meter_race = km_race * 1000
      item.tot_race_minkm = calc_pace_in_min_sec(km_race * 1000, tot_race_time_sec) 
      item.race_id = race_id
      item.tot_race_time = format_seconds(tot_race_time_sec)
      item.tot_km_race = km_race
      item.completed = false
      #p item
      @items << item
    end
    
  end #end create_laps

  def store_laps_indb
    @log.debug "Store laps into the db."
    @items.each do |item|
      query = "INSERT INTO racelap_detail (#{item.get_field_title}) VALUES (#{item.get_field_values(@dbpg_conn)})" 
      exec_query(query)
    end
    @log.debug "Processed #{@items.size} items, all items are successfuly inserted into the db."
  end

end

if $0 == __FILE__
  require 'log4r'
  include Log4r
  @log = Log4r::Logger.new("RaceDetailInsert")
  Log4r::Logger['RaceDetailInsert'].outputters << Outputter.stdout

  inserter = RaceDetailInsert.new
  arr_res = inserter.get_race_reference('24h-Einzel-Bewerb','2017-06-30')
  race_id = arr_res[0]
  km_race = arr_res[1]
  lap_meter = arr_res[2]
  tot_race_time_sec = arr_res[3]
  #lap_meter = 1217.75
  #tot_race_time_sec = 3600 * 24
  insert_last_partial = true
  inserter.create_laps(race_id, 'data/2017-06-30-24h-irdning.txt', lap_meter, km_race, tot_race_time_sec, insert_last_partial)
  #inserter.store_laps_indb # caution, store reinsert previous items
end<file_sep># -*- coding: ISO-8859-1 -*-
require 'rubygems'
require 'fileutils'
require 'filescandir'


class ExtraSPMSLogCreator
  def create_structure(low_ix, up_ix, root_dir, src_files_dir)
    fscd = FileScanDir.new
    fscd.scan_dir(src_files_dir)
    
    (low_ix..up_ix).each do |ix|
  	  bsname = "BS"
		  bsname += ix < 10 ? "0#{ix}" : "#{ix}"
  	  fullname_bs_dir = File.join(root_dir,bsname)
		  FileUtils::mkdir_p fullname_bs_dir
		  FileUtils::mkdir_p  File.join(fullname_bs_dir, "Archive")
		  FileUtils::mkdir_p  File.join(fullname_bs_dir, "Error")
		  target_logs_dir = File.join(fullname_bs_dir, "Logs")
		  FileUtils::mkdir_p target_logs_dir
			fscd.result_list.each do |line|
    		dst_item = File.join(target_logs_dir, File.basename(line))
    		FileUtils.cp line, dst_item
    	end  
		  puts "Created structure in #{fullname_bs_dir}"
		end
	end
end

if $0 == __FILE__
	root_dir = 'D:/tmp/cancme'
	src_call_logs_dir = 'C:/temp/SPMS/BS03/Logs'
	log_build = ExtraSPMSLogCreator.new
	log_build.create_structure(1,3,root_dir, src_call_logs_dir)
end


<file_sep>#file: db_baseitem.rb

class DbBaseItem
  def get_field_title
    arr = @changed_fields.map{|e| e.to_s}
    arr.join(',')
  end

  def get_field_values(dbpg_conn)
    arr = @changed_fields.map{|f| "'" + dbpg_conn.escape_string(serialize_field_value(f,send(f))) + "'"}
    arr.join(',')
  end

  def serialize_field_value(field, value)
    if @field_types[field] == :datetime
      res = value ? value.strftime("%Y-%m-%d %H:%M:%S") : nil
    elsif @field_types[field] == :boolean
      res = value ? 1 : 0
    elsif @field_types[field] == :int
      res = value ? value : 0
    elsif @field_types[field] == :numeric
      res = value ? value : 0.0
    else
      res = value.to_s
    end
    res = res.to_s
  end

end #end DbBaseItem

############################################## DbConnectBase

class DbConnectBase
  def initialize
    @dbpg_conn = nil
    @use_debug_sql = true
    connect_to_local_db
  end

  def connect_to_local_db
    # su WSL2 localhost non sembra funzionare.
    # Si usa su WSL2 
    # grep -m 1 nameserver /etc/resolv.conf | awk '{print $2}'
    # che genera il seguente IP: 172.17.208.1
    host_to_connect = `grep -m 1 nameserver /etc/resolv.conf | awk '{print $2}'`
    p host_to_connect = host_to_connect.gsub("\n","")
    @log.debug "Wanna connect to db on IP #{host_to_connect}"
    @dbpg_conn = PG::Connection.open(:dbname => 'corsadb', 
                                    :user => 'corsa_user', 
                                    :password => '<PASSWORD>', 
                                    :host => host_to_connect, 
                                    :port => 5432)
    @log.debug "Connected to the db"
  end

  def exec_query(query)
    @log.debug query if @use_debug_sql
    @dbpg_conn.async_exec(query)  
  end

end
<file_sep># -*- coding: ISO-8859-1 -*-
require 'rubygems'
require 'log4r'

include Log4r

class WindowsServiesStartOrStop
  
  def initialize
    @log = Log4r::Logger.new("WindowsServiesStartOrStop")
    @log.outputters << Outputter.stdout
    FileOutputter.new('WindowsServiesStartOrStop', :filename=> 'services.log') 
    Logger['WindowsServiesStartOrStop'].add 'WindowsServiesStartOrStop'
  end
  
  def StopServices(serv_arr)
    @log.debug "Stopping #{serv_arr}"
    submit_net_cmd(:stop, serv_arr)
  end
  
  def StartServices(serv_arr)
    @log.debug "Starting #{serv_arr}"
    submit_net_cmd(:start, serv_arr)
  end
  
  private
  
  def submit_net_cmd(cmd_type,serv_arr )
    net_cmd = "start"
    if cmd_type == :stop
      net_cmd = "stop"
    end
    serv_arr.each do |service|
      cmd = "net #{net_cmd} \"#{service}\""
      ExecCmd(cmd)
    end
    @log.debug "All #{net_cmd} requests submitted" 
  end
  
  def ExecCmd(cmd)
    IO.popen(cmd, "r") do |io|
    	io.each_line do |line|
       	@log.debug line
      end
    end
  rescue
   	@log.error "Error: #{$!}"
  end
  
end


if $0 == __FILE__
  services = []
  services << 'SPMS - Administration Service'
  services << 'SPMS - Aggregation Service'
  services << 'SPMS - Authorisation Service'
  services << 'SPMS - DNS Service' 
  services << 'SPMS - ETL Service' 
  services << 'SPMS - LogFile Watcher Service'
  services << 'SPMS - Reporting Service'
  services << 'SPMS - Scheduler Service'
  
  # SINGLE SERVICE
  #services = ['SPMS - Authorisation Service']
  #services = ['SPMS - Reporting Service']
  #services = ['SPMS - LogFile Watcher Service']
  #services = ['SPMS - Aggregation Service']
  #services = ['SPMS - ETL Service']
  #services = ['SPMS - Administration Service']
  #services = ['SPMS - Scheduler Service']
  #services = ['SPMS - Reporting Service', 'SPMS - ETL Service']
  #services = ['SPMS - LogFile Watcher Service', 'SPMS - ETL Service']
  
  ####
  starter = WindowsServiesStartOrStop.new
  
  starter.StopServices(services)
  #starter.StartServices(services)
  
 
end
<file_sep># -*- coding: ISO-8859-1 -*-
require 'rubygems'
require 'rexml/document'


class GPorterConvGpx
  
  def parse_gpx(fname)
    p doc_raw = File.open(fname, 'r').read
    doc = REXML::Document.new(doc_raw)
    titles = []
    links = []
    p doc
    doc.elements.each('trk') do |ele|
       titles << ele.name
       p ele
    end
    p titles
  end
  
  # Per fare andare il file gpx su garmin connect partendo da un file esportato
  # da canway bisogna:
  # - Cambiare il nodo gpx, penso che occorra la versione 1.1 anziché la 1.0
  # - Usare il nodo metadata
  # - Togliere il nodo speed in ogni punto
  def remove_speed(fname)
    puts "Open file #{fname}"
    result = []
    line_count = 0
    state = :find_gpx
    old_time = nil
    point_time_insec = nil
    space_in_m = nil
    accumulate_space = 0
    km_tot = 0
    File.open(fname, 'r').each_line do |line|
      insert_line = true
      if state == :insert_metadata
        result << "\n<metadata>\n"
        state = :find_trk
      elsif state == :find_trk and line =~ /trk/
        result << "</metadata>\n"
        state = :normal
      elsif state == :find_gpx and line =~ /gpx/
        insert_line = false
        result << '<gpx xmlns="http://www.topografix.com/GPX/1/1" xmlns:gpsies="http://www.gpsies.com/GPX/1/0" creator="Igor" version="1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.gpsies.com/GPX/1/0 http://www.gpsies.com/gpsies.xsd">'
        state = :insert_metadata
      elsif line =~ /speed/
        insert_line = false
        var = '<speed>'
        var2 = "</speed>\n"
        str2 = line.gsub( /#{Regexp.escape(var)}/, '' )
        str_speed = str2.gsub( /#{Regexp.escape(var2)}/, '' )
        p speed = str_speed.to_f
        p point_time_insec
        if point_time_insec != nil
        	p space_in_m = speed * point_time_insec
        	accumulate_space += space_in_m
        	if accumulate_space >= 1000
            p "Kilometro!!!!!!"
            accumulate_space = 0
            km_tot += 1
          end
        end
      elsif line =~ /time/
        var = '<time>'
        var2 = "</time>\n"
        str2 = line.gsub( /#{Regexp.escape(var)}/, '' )
        str_time = str2.gsub( /#{Regexp.escape(var2)}/, '' )
        mytime = DateTime::strptime(str=str_time)
        if old_time != nil
          diff_time = (mytime - old_time)
          point_time_insec = (diff_time * 24 * 60 * 60).to_i
        end
        old_time = mytime
      end
      if insert_line
        result << line
      end
      line_count += 1
    end
    puts "Percorsi km #{km_tot}"
    out_fname = File.join(File.dirname(fname), "garmin_#{File.basename(fname)}")
    puts "Create a new file without speed item #{out_fname}"
    File.open(out_fname, 'w'){|outfile| result.each{ |item| outfile << item } }
    puts "File created OK - original count #{line_count}, now #{result.size}"
  end
  
end



if $0 == __FILE__
  # NOTA: in questa versione ho provato ad inserire gli split nel file gpx. 
  # Purtroppo non funziona nell'import in garmin connect, anche se si fa il downlad 
  # da garmin connect mi da lo stesso file
  gp = GPorterConvGpx.new
  fname = 'E:\documenti\Tracce_Gps\export_2015-10-18 09-08.gpx'
  gp.remove_speed(fname)
  #gp.parse_gpx(fname)
end<file_sep>$:.unshift File.dirname(__FILE__)

require 'rubygems'
require 'mechanize'
require 'db_baseitem'
require 'pg'

#Script che uso per fare l'aggiornamento del mio db delle gare memorizzate sul sito pentek
#In questo modo non devo editare le gare due volte, ma solo sul sito Pentek.

#Note: Ho cominciato con arachno e ruby 1.8.6
# il db però usa le stringe in formato utf8 quindi va usato un ruby tipo 2.3.1 quando si va scrivere nel db

class RaceItem < DbBaseItem
  attr_accessor :name,:title,:meter_length,:ascending_meter,:descending,:rank_global,:rank_gender,:rank_class,:class_name,:race_date,:sport_type_id,
        :race_time,:pace_kmh,:pace_minkm,:comment,:race_subtype_id,:runner_id,:km_length
  
  def initialize
    @runner_id = 1
    @sport_type_id = 0 # duathlon is 1. Informazione presente solo nel css background del div //*[@id="zeile_zwei_dua_tacho"]
                       # l'ho messa anche nel titolo
    @ascending_meter = 0
    @changed_fields = [:name,:title,:meter_length,:ascending_meter,:descending,:rank_global,:rank_gender,:rank_class,:class_name,:race_date,:sport_type_id,
          :race_time,:pace_kmh,:pace_minkm,:comment,:race_subtype_id,:runner_id,:km_length]
    @field_types = {:race_date => :datetime, :meter_length => :int, :ascending_meter => :int, :descending => :int, :rank_global => :int,
                    :rank_gender => :int, :rank_class => :int, :sport_type_id => :int, :race_subtype_id => :int, :runner_id => :int, :km_length => :numeric }
  end
  
  def title=(tt)
    # title format example: "race1 (100HM)"
    if tt =~ /\(*.[H,h][m,M]/
      arr = tt.split('(')
      @title = arr[0].strip
      arr[1] = arr[1].downcase
      @ascending_meter = arr[1].gsub("(","").gsub(")","").gsub('hm',"").gsub(" ","").to_i 
    else
      @title = tt.strip
    end
    if @title =~ /Duathlon/
      @sport_type_id = 1
    end
  end
  
  def km_length=(kl)
    @km_length = kl.gsub("k","").to_f
    @meter_length = (@km_length * 1000).to_i 
    if @ascending_meter > 300 and @meter_length != 42195 
      @race_subtype_id = 1
      if @meter_length > 10999 and @meter_length < 42195 
        @race_subtype_id = 7
      elsif @meter_length > 42195
        @race_subtype_id = 6
      end   
    elsif @meter_length < 10999
      @race_subtype_id = 2
    elsif @meter_length == 21097 
      @race_subtype_id = 3
    elsif @meter_length == 42195 
      @race_subtype_id = 4
    elsif @meter_length > 42195 
      @race_subtype_id = 5
    else
      @race_subtype_id = 8
    end 
  end
  
  def rank_global=(rg)
    @rank_global = rg.gsub(".","").to_i
  end
  
  def rank_gender=(rg)
    rank_gender = rg.gsub(".","").to_i
  end
  
  def rank_class=(rg)
    @rank_class = rg.gsub(".","").to_i
  end

  def race_date=(dt)
    @race_date = Time.parse(dt)
  end

  def is_item_recent?(latest_date_in_db)
    @race_date > latest_date_in_db
  end
end

################################################## RACEPICKER

class RacePicker < DbConnectBase
  def initialize
    @log = Log4r::Logger["RacePicker"]
    @agent = Mechanize.new
    @agent.user_agent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1"
    @picked_races = {}
    if RUBY_VERSION != '1.8.6'
      @str_nbs = "\uc2a0".force_encoding('ISO-8859-1')
      @str_nbs2 = "\u00A0"
    else
      @str_nbs = "\240"
      @str_nbs2 = "\302\240"
    end
    super
    #@str_nbs = "\240" #http://www.utf8-chartable.de/ non breaking space
  end

  def get_latest_racedate_indb
    query = "SELECT race_date, title  FROM race ORDER BY race_date DESC LIMIT 1"
    result = exec_query(query)
    if result.ntuples == 0
      return Time.parse("2000-01-01 00:00:00")
    end
    #p result[0]
    p res = Time.parse(result[0]["race_date"])
    return res
  end

  def is_race_item_indb?(item)
    #p item
    date_str = item.serialize_field_value(:race_date, item.race_date)
    query = "SELECT race_date, title  FROM race WHERE race_date = '#{date_str}' AND title = '#{item.title}' LIMIT 1"
    result = exec_query(query)
    return result.ntuples != 0
  end

  def insert_indb(race_item)
    p race_item
    query = "INSERT INTO race (#{race_item.get_field_title}) VALUES (#{race_item.get_field_values(@dbpg_conn)})" 
    exec_query(query)
  end

  def pick_races(url, latest_date_in_db)
    @log.debug "Url: #{url}, date last race in db #{latest_date_in_db}"
    @log.debug "Using parser #{Mechanize.html_parser}"   # Nokogiri::HTML
    page = @agent.get(url)
    #puts page.body
    i = 1
    @races = []
    #per avere l'elemento che si cerca, si usa select che è un nokogiri con attributes e children
    # per capire cosa bisogna selezionare, basta usare p link all'interno del block select
    page.search("body//div//div//div//div//div").select{|link| link.attributes["id"].value == "ergebnis_container_tacho"}.each do |tacho|   
      #p link
      #p link.inner_html
      @log.debug "Found a new item #{i}..."
      @race_item = RaceItem.new 
      tacho.search('div').select{|ldate| ldate.attributes["id"].value == "ergebnis_bewerbdatum"}.each do |date_race|
        @race_item.race_date = date_race.inner_html.gsub(@str_nbs, "").gsub(/[[:space:]]+\z/,"").strip #remove all spaces, also  &nbsp at the end -> "2016-10-29\xA0\xA0"
      end
      #//*[@id="ergebnis_bewerbname"]
      tacho.search('div').select{|litem| litem.attributes["id"].value == "ergebnis_bewerbname"}.each do |item|
        name = nil
        item.search('a').each do |link_a|
          name = link_a.inner_html.gsub(@str_nbs, "") 
        end
        name = item.inner_html if name == nil
        @race_item.name = name.gsub(@str_nbs, "") 
        @race_item.name.strip!
      end 
      #//*[@id="race_tacho"]     
      tacho.search('div').select{|litem| litem.attributes["id"].value == "race_tacho"}.each do |item_value|
        @race_item.title = item_value.inner_html.gsub(@str_nbs, "") #remove non breaking spaces -> &nbsp
      end
      if !@race_item.is_item_recent?(latest_date_in_db)
        #p @race_item.race_date
        @log.debug "Ignore item #{i} - date: #{@race_item.race_date} - #{@race_item.title}.\nSearch is terminated because now all races should be already into the db, but consider to make a consistency check."
        break;
      end
      #//*[@id="distanz_tacho"]
      tacho.search('div').select{|litem| litem.attributes["id"].value == "distanz_tacho"}.each do |item_value|
        @race_item.km_length = item_value.children.last.text.strip
      end
      #//*[@id="zeit_tacho"]
      tacho.search('div').select{|litem| litem.attributes["id"].value == "zeit_tacho"}.each do |item_value|
        @race_item.race_time = item_value.children.last.text.strip 
      end
      #//*[@id="minprokm_tacho"]
      tacho.search('div').select{|litem| litem.attributes["id"].value == "minprokm_tacho"}.each do |item_value|
        @race_item.pace_minkm = item_value.children.last.text.strip.gsub(@str_nbs2,"")
      end
      #//*[@id="kmproh_tacho"]
      tacho.search('div').select{|litem| litem.attributes["id"].value == "kmproh_tacho"}.each do |item_value|
        @race_item.pace_kmh = item_value.children.last.text.gsub(@str_nbs2, "")
      end
      #//*[@id="gesamtrang_tacho"]
      tacho.search('div').select{|litem| litem.attributes["id"].value == "gesamtrang_tacho"}.each do |item_value|
        @race_item.rank_global = item_value.children.first.text
      end
      #rank_gender
      #//*[@id="mwrang_tacho"]
      tacho.search('div').select{|litem| litem.attributes["id"].value == "mwrang_tacho"}.each do |item_value|
        @race_item.rank_gender = item_value.children.first.text
      end
      #rank_class
      #//*[@id="klassenrang_tacho"]
      tacho.search('div').select{|litem| litem.attributes["id"].value == "klassenrang_tacho"}.each do |item_value|
        @race_item.rank_class = item_value.children.first.text
        @race_item.class_name = item_value.children.last.text.gsub("Kl.","")
      end
      #//*[@id="container_kommentar_tacho"]
      tacho.search('div').select{|litem| litem.attributes["id"].value == "container_kommentar_tacho"}.each do |item_value|
        @race_item.comment = item_value.children.last.text
      end
      
      #p @race_item
      @races << @race_item 
      @log.debug "#{@race_item.title} - #{@race_item.race_date}"
      i += 1
      #ergebnis_bewerbdatum
    end
    @log.debug "Found #{@races.length} items that need to be inserted into the db"
    #p @races
  end

  def insert_races_indb
    @races.sort{|a,b| a.race_date <=> b.race_date}.each do |item|
      #insert the oldest new item first
      #p item
      insert_indb(item) 
    end
    if @races.length == 0
      @log.debug "Db is already updated, no new races to insert found"
    else
      @log.debug "Inserted #{@races.length} new races successfully."
    end 
  end

  def check_races(opt)
    insert_missed = opt[:insert_missed]
    @missed_items = []
    @inserted = []
    @races.sort{|a,b| a.race_date <=> b.race_date}.each do |item|
      if is_race_item_indb?(item)
        @log.debug "race item #{item.title} - #{item.race_date} is stored OK"
      elsif insert_missed
        insert_indb(item)
        @inserted << item
        @log.debug "#{item.title} - #{item.race_date} inserted successfully."
      else
        @log.debug "race item #{item.title} - #{item.race_date} is not in the db."
        @missed_items << item
      end
    end
    @log.debug "Checked #{@races.size} races: #{@missed_items.size} are missed"
    if @missed_items.size == 0 && @inserted.size == 0
      @log.debug "Race table in Db is OK (race_date and title, if you have changed other fields, please delete the record into the db and run this check again)"
    elsif @inserted.size > 0 && @missed_items.size == 0
      @log.debug "Inserted #{@inserted.size} races, now db should be sync"
    else
      @log.debug "Please run again check_races with the insert missed option set to true, or use insert_races_indb"
    end
  end

end

if $0 == __FILE__
  # Nota: non è necessario cambiare questo script, basta usare la linea di comando
  #       Quello che fa questo script è andare sul sito di Pentenk, prendere le gare e 
  #       importarle nel database. È in grado di importare solo quelle nuove, ma anche tutte le altre e di fare un check tra il db e il sito.
  require 'log4r'
  include Log4r
  @log = Log4r::Logger.new("RacePicker")
  Log4r::Logger['RacePicker'].outputters << Outputter.stdout

  url = "https://www.membersclub.at/ccmc_showprofile.php?unr=9671&show_tacho=1&pass=008"
  picker = RacePicker.new

  check_consistency = false
  insert_new_races = true
  insert_missed = false
  if ARGV.size > 0
    if ARGV[0] == '/help'
      @log.debug 'Advanced Usage : ruby race_picker.rb "{:check_consistency => true, :insert_missed => false, :insert_new_races => false}"'
      @log.debug 'Advanced Usage2: ruby race_picker.rb "{:check_consistency => true, :insert_missed => true, :insert_new_races => false}"'
      @log.debug "Usual usage: ruby race_picker.rb  (default values: check_consistency = false, insert_new_races = true, insert_missed = false)" 
      exit
    else
      #p ARGV[0]
      opt = eval(ARGV[0])
      check_consistency = opt[:check_consistency]
      insert_missed = opt[:insert_missed]
      insert_new_races = opt[:insert_new_races]
    end
  end
  if insert_missed && !check_consistency
    @log.error ":insert_missed works only with :check_consistency"
  end
  @log.debug "Options are: check_consistency: #{check_consistency} (insert_missed: #{insert_missed}),  insert_new_races: #{insert_new_races}"

  if check_consistency
    picker.pick_races(url, Time.parse("2000-01-01 00:00:00")) 
    picker.check_races({:insert_missed => insert_missed})
  end
  if insert_new_races
    latest_date_in_db = picker.get_latest_racedate_indb
    picker.pick_races(url, latest_date_in_db) 
    picker.insert_races_indb
  end
end
<file_sep># -*- coding: ISO-8859-1 -*-

require 'rubygems'

require 'log4r'

include Log4r

class FileAppenderExtraSim
  
  def initialize
    @log = Log4r::Logger.new("FileAppenderExtraSim")
    @log.outputters << Outputter.stdout
    #FileOutputter.new('FileAppenderExtraSim', :filename=> 'FileAppenderExtraSim.log') 
    #Logger['FileAppenderExtraSim'].add 'FileAppenderExtraSim'
  end
  
  def getnumLinesWritten(target_file)
    res = 0
    if File.exist?(target_file)
    	res = File.open(target_file).readlines.size
    end
    return res
  end
  
  def write_some_lines(src_file, target_file, num_to_write)
    @log.debug("process #{src_file}")
    src_lines = []
    num_in_target = getnumLinesWritten(target_file)
    @log.debug("Num lines written in target #{num_in_target}")
    i = 0 
    File.open(src_file).each_line do |line|
      if i >= num_in_target and src_lines.length < num_to_write
        src_lines << line
      end
      i += 1
    end
    @log.debug("Source file has #{i} lines, jump the first #{num_in_target} lines and writes #{num_to_write} lines.")
    File.open(target_file, 'a'){|f| src_lines.each{|x| f << x}}
    @log.debug("Target file #{target_file} has been updated with #{num_to_write} lines")
  end
  
  ##
  # Append lines into the target file using the src_file. Timeout in seconds is used to do the append. 
  # Lines already written into the target are skipped, so you can restart and stop this function and the append process continue.
  def write_lines_after_timeout(src_file, target_file, numrecords, timeout)
    @log.debug("process #{src_file}")
    src_lines = []
    num_in_target = getnumLinesWritten(target_file)
    @log.debug("Num lines written in target #{num_in_target}")
    i = 0 
    File.open(src_file).each_line do |line|
      if i >= num_in_target
        src_lines << line
      end
      i += 1
    end
    
    @log.debug("Start to writes lines after #{timeout} seconds, lines to be written are #{src_lines.size}")
    written = 0
    arr_num = numrecords.dup
    while src_lines.size > 0
     	sleep timeout
     	num_to_write = arr_num.pop 
     	curr_slice = src_lines.slice!(0..num_to_write - 1)
     	File.open(target_file, 'a'){|f| curr_slice.each{|x| f << x}}
     	written += curr_slice.size
     	@log.debug("#{curr_slice.size} lines appended, written in this session until now are #{written}")
     	if arr_num.length == 0
        arr_num = numrecords.dup
      end  
    end
  end
  
  
end

if $0 == __FILE__
  app = FileAppenderExtraSim.new
  
  numrec = 5
  #src_file = 'C:\temp\SPMS\bs01\Archive\calls_2014.11.03.log'
  #target_file = 'C:\temp\KPI\bs01\logs\calls_2014.11.03.log'
  #app.write_some_lines(src_file, target_file, numrec)
  
  #change the update file
  src_file = 'C:\temp\SPMS\bs01\Archive\calls_2014.11.04.log'
  target_file = 'C:\temp\KPI\bs01\logs\xcalls_2015.11.06.log'
  app.write_some_lines(src_file, target_file, numrec)
  
  
  timeout = 1
  numrecords = [5,4,3,2,1]
  #numrecords = [1]
  #app.write_lines_after_timeout(src_file, target_file, numrecords, timeout)
end<file_sep># file: gpx_redux.rb

##
# Used to reduce gpx tracks or split into more files
class GpxReducer
  
  def initialize
    @header = []
    @footer = []
    @all_points = []
    @gpx_fname = ''
  end
     
  def points_counter(fname)
    @gpx_fname = fname
    header = []
    footer = []
    curr_point = []
    all_points = []
    state = :init_seg
    points_count = 0
    File.open(fname).each_line do |line|
      if line =~ /<trkseg/
        header << line
        state = :init_point
      elsif line =~ /<trkpt/
        # inizio punto
        if state == :init_point
          state = :on_point
          points_count += 1
          curr_point = []
          curr_point << line
        end
      elsif line =~ /<\/trkpt/
        # fine punto
        if state == :on_point
          curr_point << line
          #puts"current point\n#{curr_point}"
          all_points << curr_point
          state = :init_point
        end
      elsif line =~ /<\/trkseg/
        # fine traccia
        footer << line
        state = :end_file
      else
        if state == :init_seg
          header << line
        elsif state == :end_file
          footer << line
        elsif state == :on_point
          curr_point << line
        end
      end
    end # file open
    puts "Header:\n#{header}"
    puts "Footer:\n#{footer}"
    
    puts "Points count: #{points_count}(#{all_points.size}) for file #{fname}"
    @all_points = all_points
    @footer = footer
    @header = header
  end
  
  def gpx_splitter(num_of_tracks, out_dir)
    if @all_points.size == 0
      puts "Call firts points_counter() to get points information. No points recognized"
      return
    end
    if num_of_tracks < 2
      puts "Nothing to do, use the souce track if you want"
    end
    
    if !File.directory?(out_dir)
    	Dir.mkdir(out_dir)
    end
    
    points_per_track = @all_points.size / num_of_tracks
    tracks = []
    in_track_points = []
    @all_points.each do |point|
      in_track_points << point
      if in_track_points.size >= points_per_track and tracks.size < num_of_tracks - 1 
        tracks << in_track_points
        in_track_points = []
      end
    end
    tracks << in_track_points
    
    ix_track = 1
    tracks.each do |track|
      trackname = File.join(out_dir, "Track#{ix_track}.gpx")
      complete_track = []
      complete_track << @header
      complete_track << track
      complete_track << @footer
      #puts "Track #{ix_track}:\n#{complete_track}"
      puts "Points in #{trackname} are: #{track.size}"
      File.open(trackname, 'w'){|f| complete_track.flatten.each{|x| f << x}}
      puts "File written #{trackname}"
      ix_track += 1
    end
    
  end
  
  def reduce_half(fname, out_dir, out_fname)
    arr_res = []
    state = :init_point
    count_lines = 0
    File.open(fname).each_line do |line|
      #p line
      count_lines += 1
      if line =~ /<trkpt/
        # inizio punto
        if state == :init_point
          state = :on_point
          arr_res << line
        else
          p "skip IP (#{state}): #{line}"
        end
      elsif line =~ /<\/trkpt/
        # fine punto
        if state == :on_point
          arr_res << line
          state = :jump__next_point
        elsif state == :jump__next_point
          p "skip OP (#{state}):  #{line}"
          state = :init_point
        end
      elsif line =~ /<\/trkseg/
        # fine traccia
        state = :end_file
        arr_res << line     
      else
        if state != :jump__next_point
        	arr_res << line
        else
          p "skip OTHer (#{state}): #{line}"
        end 
      end
    end
    jumped_points = (count_lines - arr_res.size) / 4
    puts "Lines on file now #{arr_res.size}, original lines #{count_lines}, jumped points #{jumped_points}"
    res_fname = File.join(out_dir, out_fname)
    File.open(res_fname, 'w'){|f| arr_res.each{|x| f << x}}
    puts "File created #{res_fname}"
  end
	
end



if $0 == __FILE__
  # Usa questo script per ridurre una traccia della metà e poi magari farne uno split.
  # Con Canmore è meglio avere dei files con intorno 700 punti massimo, per aprirli e scalarli
  # in tempi ragionevoli. I nomi dei files vanno anche cambiati manualmente usando notepad.
  # Usa qualcosa tipo 1- desc traccia1 e così via. Basta cambiare il tag <trk><name> (1 punto)
  # Usa poi Canway Planner e importa tutte le traccie in un colpo prima di sincronizzare col device.
  # Se fai degli errori, chiudi Canway Planner e ricomincia. Le traccie sul device vanno cancellate manulamente
  # usando file explorer nel subfolder trips.
  # Con il file del Dirndltal Extrem scaricato da gpsies, aveva 1924 punti che ho splitato in 3 files da 641 punti.
  gr = GpxReducer.new
  #fname = 'D:\Corsa\2020\SpineRace\track\CP5+-+FIN+PADON.gpx'
  #fname = 'D:\Corsa\2020\SpineRace\track\reduced\06-cp5-FIN-hpC.gpx'
  out_fname = '04-cp3-cp4-hpC.gpx'
  out_dir = 'D:\Corsa\2020\SpineRace\track\half-orig\04'
  #gr.reduce_half(fname, out_dir, out_fname)
  #gr.points_counter(fname)
  gr.points_counter(File.join(out_dir, out_fname))
  #gr.gpx_splitter(2, out_dir)
end<file_sep># -*- coding: ISO-8859-1 -*-

require 'rubygems'
require 'filescandir'
require 'fileutils'

class JsUIPublisher
  def sync_src_with_dest(src_dir, dst_dir, force_dir_arr)
    @robocopy_path = 'C:\Windows\SysWOW64\Robocopy.exe'
    p "scan src dir"
    src_items = scan_path(src_dir)
    p "scan dst dir"
    dst_items = scan_path(dst_dir)
    
    puts "Src items #{src_items.size}, Dst items #{dst_items.size}" 
    
    p "check for missed files in dst"
    items_not_in_dst = check_presence(src_items, dst_items)
    puts "Items(#{items_not_in_dst.size}) not in destination dir:"
    items_not_in_dst.each do |item|
      puts item
    end
    
    p "check for missed files in src"
    items_not_in_src = check_presence(dst_items, src_items)
    puts "Items(#{items_not_in_src.size}) not in source dir (delete it manually if you do not need it):"
    items_not_in_src.each do |item|
      puts item
    end
    
    p " - COPY STUFF starts here --"
    p "copy src files to dst"
    exclude = ['puesraWebUI.csproj','puesraWebUI.csproj.vspscc','app/config.json', 'packages.config']
    copy_newfiles_to_dest(dst_dir, exclude, items_not_in_dst, src_dir)
    exclude << items_not_in_dst
    exclude.flatten!
    copy_files_to_dest(dst_dir, exclude, src_items, src_dir, force_dir_arr)
    puts "Terminated at #{Time.now}"
  end
  
private
	def copy_files_to_dest(dst_dir, filter_items, src_items, src_dir, force_dir_arr)
		count = 0
		src_items.each do |src_item|
			unless filter_items.index(src_item)
				dst_fname = File.join(dst_dir, src_item)
				src_fname = File.join(src_dir, src_item)
				stat_dst = File.stat(dst_fname)
				stat_src = File.stat(src_fname)
				is_force_item = calc_is_forceitem(src_item, force_dir_arr)
				if (stat_dst.mtime != stat_src.mtime and
					stat_dst.mtime < stat_src.mtime) or is_force_item
					#FileUtils.cp_r(src_fname, dst_fname, :verbose => true)
					robocopy(src_dir, dst_dir, src_item)
					count += 1
					#p stat_dst.mtime
					#p stat_src.mtime
					p "copy #{src_fname} to  #{dst_fname}"
				end
				
			end
		end
		puts "#{count} modified files copied."
	end
	
	def robocopy(src_dir, dst_dir, src_item)
		arr_names = src_item.split('/')
		name = src_item
		name_src_dir = src_dir
		name_dst_dir = dst_dir
		if arr_names.size > 0
			last_ix = arr_names.size - 1
			name = arr_names[last_ix]
			arr_names.delete_at(last_ix)
		end
		arr_names.each do |nn|
			name_src_dir = File.join(name_src_dir, nn)
			name_dst_dir = File.join(name_dst_dir, nn)
		end
		cmd = "#{@robocopy_path} \"#{name_src_dir}\" \"#{name_dst_dir}\" #{name}"
		#puts cmd
		result_cmd = []
		on_error = false
		IO.popen(cmd, "r") do |io|
    	io.each_line do |line|
        on_error = true if line =~ /ERROR/
      	result_cmd << line	 
      end
    end
    if on_error
      puts "ERROR executing #{cmd}"
      result_cmd.each do |line|
        puts line
      end
      puts "!!!!!!! Exit because ERROR on robocopy error,  FIX this error first please!!!!!"
    	exit
    end
	end
		
	
	def calc_is_forceitem(src_item, force_dir_arr)
		item_slash_count = src_item.count('/')
		force_dir_arr.each do |dir|
			dir.gsub!("\\",'/')
			dir_slash_count = dir.count('/') + 1
			#p src_item, dir
			if src_item.include?(dir) and item_slash_count ==  dir_slash_count
				 puts "force #{src_item}"
				 return true
			end
		end
		return false
	end
	
	def copy_newfiles_to_dest(dst_dir, filter_items, items_not_in_dst, src_dir)
		count = 0
		items_not_in_dst.each do |src_item|
			unless filter_items.index(src_item)
				dst_fname = File.join(dst_dir, src_item)
				src_fname = File.join(src_dir, src_item)
				p "new copy #{src_fname} to  #{dst_fname}"
				#FileUtils.cp_r(src_fname, dst_fname)
				robocopy(src_dir, dst_dir, src_item)
				count += 1
			end
		end
		puts "#{count} new files copied." if count > 0
	end

  def scan_path(path_to_scan)
    fscd = FileScanDir.new
    fscd.is_silent = true
    fscd.add_extension_filter(['.cache'])
    fscd.scan_dir(path_to_scan)
    dir_result = []
    
    fscd.result_list.each do |line|
    	dir_result <<  line.gsub(path_to_scan + '/', '')
    end
    return dir_result
  end
  
  def check_presence(src_items, dst_items)
    result = []
    src_items.each do |item|
      is_included = check_included_item(item, dst_items)
      if !is_included
        result << item
      end
    end
    return result
  end
  
  def check_included_item(item_to_find, dst_items)
    dst_items.each do |item|
      if item == item_to_find
        return true
      end
    end
    return false
  end
  
end

if $0 == __FILE__
  src_dir = 'D:\ws\SPMS\user_sartorii_001\Application\puesraWebUI'
  dst_dir = 'C:\Program Files (x86)\apache-tomcat-7.0.57\webapps\puesraWebUI'
  #src_dir = 'D:\ws\openam_custom\12.0.0\war_from_zip\edited'
  #dst_dir = 'C:\Program Files (x86)\apache-tomcat-7.0.57\webapps\openam'
  
  #force_dir = ['Scripts']
  force_dir = []
  sync = JsUIPublisher.new
  sync.sync_src_with_dest(src_dir, dst_dir, force_dir)
end