require 'tty/prompt' require 'tty/progressbar' require 'tty/progressbar/multi' require 'string/builder' require 'benchmark' module Limeta class Table using String::Builder FIELDS = { Event: {type: :string}, Site: {type: :string, size: 50}, White: {type: :string, size: 30}, Black: {type: :string, size: 30}, Result: {type: :string, size: 10}, UTCDate: {type: :date, size: 15}, UTCTime: {type: :time, size: 8}, WhiteElo: {type: :integer}, BlackElo: {type: :integer}, WhiteRatingDiff: {type: :integer}, BlackRatingDiff: {type: :integer}, WhiteTitle: {type: :string, size: 5}, BlackTitle: {type: :string, size: 5}, ECO: {type: :string, size: 3}, Opening: {type: :string}, TimeControl: {type: :string, size: 15}, Termination: {type: :string, size: 20}, Variant: {type: :string} } def initialize(name, database:) @name = name @database = database prepare end def populate!(files) progress = TTY::ProgressBar::Multi.new("\e[1mAll files\e[0m: [:bar] :percent", incomplete: "\e[90m=\e[0m", complete: "\e[32;1m=\e[0m") bars = {} files.each do |file| bars[file] = progress.register("#{file} [:bar] :percent (of :total bytes)", total: File.size(file), complete: "\e[32;2m=\e[0m") end puts progress.start bm = Benchmark.measure do files.each do |file| record = {} prev = '' File.open(file, 'r').each do |line| if prev =~ /\A\[.*\Z/ && line =~ /\A[\n\r].*\Z/ @database[@name.to_sym].insert(**record) record = {} prev = line bars[file].advance(line.bytesize) # Increment next end if line =~ /\A([\n\r]|[0-9]).*\Z/ prev = line bars[file].advance(line.bytesize) # Increment next end subbed = line.gsub(%r{[\r\n\[\]\"]},'') field, value = subbed.split(' ', 2) field = field.to_sym if FIELDS[field] record[field] = case FIELDS[field][:type] when :string then value when :integer then value.to_i when :date then Date.parse(value) when :time then value end end prev = line bars[file].advance(line.bytesize) # Increment end end end bm_out = String.build("Finished in ") do |s| s << "#{bm.utime.round(4)}s (cpu), " s << "#{bm.stime.round(4)}s (system), " s << "#{bm.total.round(4)}s (total), " s << "#{bm.real.round(4)}s (real)" end puts puts bm_out @database.disconnect end def prepare prompt = TTY::Prompt.new if exist? if prompt.yes? "Overwrite table '\e[1m#{@name}\e[0m'?" if prompt.yes? "Are you sure? This action cannot be reverted." create force: true end end else create end end def exist? @database.table_exists? @name end def create(force: false) @database.send((force ? :create_table! : :create_table), @name) do primary_key :id FIELDS.each do |name, options| case options[:type] when :string then String name, size: (options[:size] || 255) when :integer then Integer name when :date then Date name when :time then Time name, only_time: true end end end end end end