Day24 - 将台湾证券交易所的除权除息计算结果表存入 DB

前言

前面已经知道如何抓「台湾证券交易所」的除权除息计算结果表 CSV 档,接下来要处理资料,并存入 DB

说明

需要考量的情境,与前一篇是一样的 (描述越来越精简 XD)

实作

# app/features/twse/twt_49u/save_to_db.rb

module Twse::Twt49u
  class SaveToDb

    include Twse::Helpers

    def execute
      start_time = Time.current
      puts "#{self.class}, start_time: #{start_time.to_s}"

      latest_data_date = find_latest_data_date
      return puts "#{self.class}, 已经是最新的资料" if latest_data_date == start_time.to_date

      is_linux = `uname -a`[/Linux/].present?
      file_paths = Dir["data/twse/TWT49U/*"]
      file_paths.each do |file_path|
        rows = decode_data(file_path, is_linux)
        first_year, end_year = time_range(rows)
        row_index, end_index = rows_range(rows)
        all_rows, data_date_infos = filter_rows(rows, row_index, end_index)
        next if not_process?(data_date_infos, latest_data_date)

        filtered_stocks = filter_by_stocks(all_rows)
        Stock.import(filtered_stocks) if filtered_stocks.present?

        import_ex_stocks(all_rows)
      end
      puts "#{self.class}, done_time:#{Time.current}, #{(Time.current - start_time).to_s} sec"
    rescue StandardError => e
      puts "errors: #{e.inspect}, #{e.backtrace}"
    end

    private

    def find_latest_data_date
      ExStock.latest_data_date
    end

    def time_range(rows)
      first_year, first_month, first_day, end_year, end_month, end_day = rows[0].scan(/\d+/)
      [first_year, end_year].each_with_index do |year, index|
        year = "20" + (year.to_i + 11).to_s[1..2]
        if index.zero?
          first_year = year
        else
          end_year =year
        end
      end

      return [first_year, end_year]
    end

    def rows_range(rows)
      row_index = nil
      rows.each_with_index do |row, index|
        if row.include?("资料日期")
          row_index = index
          break
        end
      end

      end_index = nil
      rows.each_with_index do |row, index|
        if row.include?("公式")
          end_index = index
          break
        end
      end

      return [row_index, end_index]
    end

    def filter_rows(rows, row_index, end_index)
      all_rows = []
      data_date_infos = []
      rows[(row_index + 1)..(end_index - 1)].each do |row_string|
        row_item = []
        row_string.split('",').each { |row| row_item << row.gsub(/[=|,|"]/, '') }

        year, month, day = row_item[0].scan(/\d+/)
        year = "20" + (year.to_i + 11).to_s[1..2]
        data_date = year + month + day
        row_item[0] = data_date.to_date
        all_rows << row_item
        data_date_infos << row_item[0]
      end

      return [all_rows, data_date_infos]
    end

    def not_process?(data_date_infos, latest_data_date)
      data_date_infos.uniq!
      latest_data_date.present? && data_date_infos.max <= latest_data_date
    end

    def filter_by_stocks(all_rows)
      stock_infos = []
      all_rows.each { |rows| stock_infos << { code: rows[1], name: rows[2] } }
      stocks = Stock.all.select(:code).index_by(&:code)

      need_create_stocks = []
      stock_infos.each do |stock_info|
        stock = stocks[stock_info[:code]]
        next if stock
        next if need_create_stocks.any? { |item| item.code == stock_info[:code] }

        need_create_stocks << Stock.new(code: stock_info[:code], name: stock_info[:name])
      end
      need_create_stocks
    end

    def import_ex_stocks(all_rows)
      stocks = Stock.all.select(:code).index_by(&:code)

      need_create_ex_stocks = []
      all_rows.each do |row|
        stock = stocks[row[1]]

        need_create_ex_stocks << stock.exs.new(
          data_date: row[0],
          closing_price_before: row[3],
          reference_price: row[4],
          dr_value: row[5],
          dividend_right: ExStock::DIVIDEND_RIGHT[row[6]],
          limit_up: row[7],
          limit_down: row[8],
          opening_reference_price: row[9],
          ex_dividend_reference_price: row[10],
          reporting_day: row[12],
          price_book: row[13],
          eps: row[14],
        )
      end
      ExStock.import(need_create_ex_stocks) if need_create_ex_stocks.present?
    end

  end
end

检查是否有存入 DB

小结

我猜看到这,已经不少人看不懂或没在看了 (笑)

说明越写越精简,想说 code 已经直接贴出来了,直接看 code 比较快,若发现有更好的写法,欢迎留言和我说~


铁人赛文章连结:https://ithelp.ithome.com.tw/articles/10272913
medium 文章连结:https://link.medium.com/VfJfmvMuTjb
本文同步发布於 小菜的 Blog https://riverye.com/

备注:之後文章修改更新,以个人部落格为主


<<:  DAY22-EXCEL统计分析:双因子变异数分析介绍

>>:  Day28回圈(JavaScript)

30天轻松学会unity自制游戏-Boss死亡问题跟通关画面

现在Boss死亡时,招唤出来的小兵不会一起死亡,来让Boss的程序加一个死亡布林值,开启一下boss...

[ Day 24 ] React 中的样式设定

今天要介绍的内容是如何在 React.js 中撰写我们的 CSS 样式?除了相关套件的应用之外,还...

【Day23】导航元件 - Pagination

元件介绍 Pagination 是一个分页元件,当页面中一次要载入过多的资料时,载入及渲染将会花费更...

TailwindCSS 从零开始 - CSS 传统撰写方式与功能优先的差异

Utility First 功能优先 官方文件给的定义 从组合过的原生功能,来建立起复杂的元件。 ...

Vue CLI建置 & GitHub上传

昨天我们安装好Vue cli今天要来新增一个专案啦~在官网中它介绍了两种方法,那这里会用第二种方法实...