├── go ├── bin │ └── .gitkeep ├── .gitignore ├── server │ └── config.yaml ├── db │ └── init.sql ├── tools │ └── tools.go ├── Dockerfile ├── Dockerfile.prod ├── scripts │ ├── install_go_tools.sh │ └── setup_db.sh ├── .octocov.yml ├── models │ ├── industrycategory_extend.go │ ├── fixture.go │ ├── market_extend.go │ └── industry_extend.go ├── go.mod ├── middleware │ └── logger.go ├── Makefile ├── cmd │ └── server │ │ └── main.go ├── .air.toml ├── .golangci.yaml └── cloudbuild.yaml ├── ruby ├── log │ └── .keep ├── storage │ └── .keep ├── tmp │ ├── .keep │ └── pids │ │ └── .keep ├── vendor │ └── .keep ├── lib │ ├── tasks │ │ ├── .keep │ │ ├── update_industry_category.rake │ │ ├── destroy_old_documents.rake │ │ ├── adjust_salary.rake │ │ ├── one_shot │ │ │ ├── 20240716_delete_securities_invalid_code.rake │ │ │ └── 20220618_change_market_name.rake │ │ ├── save_document_summary.rake │ │ ├── save_document_detail.rake │ │ └── save_securities.rake │ ├── documents │ │ └── .keep │ └── security_lists │ │ └── .keep ├── .ruby-version ├── app │ ├── models │ │ ├── concerns │ │ │ └── .keep │ │ ├── application_record.rb │ │ ├── market.rb │ │ ├── security.rb │ │ ├── industry_category.rb │ │ ├── element_util.rb │ │ ├── security_list.rb │ │ ├── document_parser │ │ │ └── header.rb │ │ └── industry.rb │ └── jobs │ │ └── application_job.rb ├── .rspec ├── config │ ├── initializers │ │ ├── date_formats.rb │ │ ├── backtrace_silencers.rb │ │ ├── filter_parameter_logging.rb │ │ └── inflections.rb │ ├── spring.rb │ ├── environment.rb │ ├── boot.rb │ ├── credentials.yml.enc │ ├── locales │ │ └── en.yml │ ├── storage.yml │ ├── application.rb │ ├── environments │ │ ├── development.rb │ │ ├── test.rb │ │ └── production.rb │ └── database.yml ├── db │ ├── seeds.rb │ └── migrate │ │ ├── 20210730111150_add_reference_to_industry.rb │ │ ├── 20210724084822_remove_unique_index_edinet_code.rb │ │ ├── 20240616082548_add_foreign_key_to_documents.rb │ │ ├── 20240616082100_remove_foreign_key_from_documents.rb │ │ ├── 20230627125325_add_capital_columns_to_documents.rb │ │ ├── 20210720221251_add_markets.rb │ │ ├── 20230602000314_add_industry_categories.rb │ │ ├── 20210720221250_add_security_lists.rb │ │ ├── 20210720221252_add_industries.rb │ │ ├── 20240616082152_change_code_type_in_securities_and_documents.rb │ │ ├── 20210720221801_add_securities.rb │ │ ├── 20210802234100_add_financial_columns_to_documents.rb │ │ └── 20210720225721_add_documents.rb ├── bin │ ├── rake │ ├── rails │ ├── spring │ └── setup ├── public │ └── robots.txt ├── spec │ ├── factories │ │ ├── market.rb │ │ ├── industry.rb │ │ ├── industry_category.rb │ │ ├── security.rb │ │ └── document.rb │ ├── models │ │ └── document_parser │ │ │ └── header_spec.rb │ └── rails_helper.rb ├── .gitattributes ├── Rakefile ├── Dockerfile ├── Dockerfile.prod ├── cloudbuild.yaml ├── .gitignore ├── Gemfile └── .rubocop.yml ├── supabase ├── seed.sql ├── .gitignore └── README.md ├── .envrc_sample ├── .gitignore ├── terraform ├── prod │ ├── .terraform-version │ ├── backent.tf │ ├── provider.tf │ ├── google_workflows_workflow.tf │ ├── versions.tf │ ├── google_compute_network.tf │ ├── workflows │ │ └── save_documents.yaml │ ├── google_secret_manager_secret.tf │ ├── google_artifact_registry_repository.tf │ ├── google_cloudbuild_trigger.tf │ ├── triggers │ ├── google_storage_bucket.tf │ ├── .terraform.lock.hcl │ ├── google_cloud_run_v2_job.tf │ ├── google_project_service.tf │ └── google_cloud_scheduler_job.tf └── .gitignore ├── typescript ├── .prettierignore ├── client │ ├── .openapi-generator │ │ ├── VERSION │ │ └── FILES │ ├── apis │ │ └── index.ts │ ├── index.ts │ ├── models │ │ ├── index.ts │ │ ├── ResponseMarketIDs.ts │ │ ├── ResponseIndustryIDs.ts │ │ ├── Market.ts │ │ ├── ResponseSecurityCodes.ts │ │ ├── ResponseMarket.ts │ │ ├── ResponseCompany.ts │ │ ├── ResponseIndustry.ts │ │ ├── ResponseMarkets.ts │ │ ├── ResponseIndustries.ts │ │ ├── Industry.ts │ │ ├── EachMarket.ts │ │ ├── EachIndustry.ts │ │ ├── ResponseCompanies.ts │ │ └── Meta.ts │ └── .openapi-generator-ignore ├── styles │ └── globals.css ├── lib │ ├── client.utility.ts │ ├── gtag.ts │ └── utility.ts ├── .eslintrc.json ├── types │ └── gtag.d.ts ├── app │ ├── favicon.ico │ ├── layout.tsx │ ├── error.tsx │ ├── not-found.tsx │ └── [lang] │ │ ├── error.tsx │ │ ├── contact │ │ └── page.tsx │ │ ├── layout.tsx │ │ ├── terms_of_use │ │ └── page.tsx │ │ ├── page.tsx │ │ └── companies │ │ └── page.tsx ├── postcss.config.js ├── next.config.js ├── Dockerfile ├── .prettierrc.json ├── dictionaries │ └── i18n-config.ts ├── constant │ └── index.ts ├── hooks │ ├── GetDictionary.ts │ ├── CreateQueryString.tsx │ └── GetData.ts ├── types.ts ├── .gitignore ├── public │ ├── vercel.svg │ └── next.svg ├── tailwind.config.js ├── components │ ├── NumberOfResults.tsx │ ├── BreadCrumbs.tsx │ ├── Pagination.tsx │ ├── ThemeChangeMenu.tsx │ ├── GoogleAnalytics.tsx │ ├── SortTypes.tsx │ ├── LocaleSwitcher.tsx │ ├── SearchInput.tsx │ ├── Footer.tsx │ └── Header.tsx ├── tsconfig.json ├── package.json ├── README.md └── middleware.ts ├── .gitattributes ├── SECURITY.md ├── openapitools.json ├── .github ├── workflows │ ├── go_build.yml │ ├── rubocop.yml │ ├── rspec.yml │ ├── golangci-lint.yml │ ├── go_test.yml │ └── nextjs_ci.yml └── dependabot.yml ├── LICENSE ├── .env_sample ├── Makefile ├── compose.yaml ├── AGENTS.md ├── CONTRIBUTING.md └── GEMINI.md /go/bin/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ruby/log/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ruby/storage/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ruby/tmp/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ruby/vendor/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /supabase/seed.sql: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ruby/lib/tasks/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ruby/tmp/pids/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.envrc_sample: -------------------------------------------------------------------------------- 1 | dotenv 2 | -------------------------------------------------------------------------------- /ruby/lib/documents/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.env 2 | /.envrc 3 | -------------------------------------------------------------------------------- /ruby/.ruby-version: -------------------------------------------------------------------------------- 1 | ruby-3.1.4 2 | -------------------------------------------------------------------------------- /ruby/app/models/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ruby/lib/security_lists/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /terraform/prod/.terraform-version: -------------------------------------------------------------------------------- 1 | 1.7.4 2 | -------------------------------------------------------------------------------- /typescript/.prettierignore: -------------------------------------------------------------------------------- 1 | client/ 2 | .next/ 3 | -------------------------------------------------------------------------------- /typescript/client/.openapi-generator/VERSION: -------------------------------------------------------------------------------- 1 | 7.6.0 2 | -------------------------------------------------------------------------------- /go/.gitignore: -------------------------------------------------------------------------------- 1 | bin/* 2 | !bin/.gitkeep 3 | tmp 4 | coverage.out 5 | -------------------------------------------------------------------------------- /ruby/.rspec: -------------------------------------------------------------------------------- 1 | --require spec_helper 2 | --format documentation 3 | -------------------------------------------------------------------------------- /supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | .env 5 | -------------------------------------------------------------------------------- /ruby/config/initializers/date_formats.rb: -------------------------------------------------------------------------------- 1 | Date::DATE_FORMATS[:ymd] = '%Y年%m月%d日' 2 | -------------------------------------------------------------------------------- /typescript/styles/globals.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | @plugin "daisyui"; 3 | -------------------------------------------------------------------------------- /typescript/lib/client.utility.ts: -------------------------------------------------------------------------------- 1 | export const isMobile = window.innerWidth < 576 2 | -------------------------------------------------------------------------------- /typescript/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "prettier"] 3 | } 4 | -------------------------------------------------------------------------------- /ruby/db/seeds.rb: -------------------------------------------------------------------------------- 1 | Market.create_seed 2 | IndustryCategory.create_seed 3 | Industry.create_seed 4 | -------------------------------------------------------------------------------- /typescript/types/gtag.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module "gtag.js" 4 | -------------------------------------------------------------------------------- /typescript/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuki0920/company-ranking/HEAD/typescript/app/favicon.ico -------------------------------------------------------------------------------- /typescript/client/apis/index.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | export * from './DefaultApi'; 4 | -------------------------------------------------------------------------------- /ruby/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative "../config/boot" 3 | require "rake" 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /typescript/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | "@tailwindcss/postcss": {}, 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /ruby/public/robots.txt: -------------------------------------------------------------------------------- 1 | # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | -------------------------------------------------------------------------------- /terraform/prod/backent.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "gcs" { 3 | bucket = "company-ranking-prod-terraform" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /terraform/prod/provider.tf: -------------------------------------------------------------------------------- 1 | provider "google" { 2 | project = "company-ranking-prod" 3 | region = "asia-northeast1" 4 | } 5 | -------------------------------------------------------------------------------- /typescript/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {} 3 | 4 | module.exports = nextConfig 5 | -------------------------------------------------------------------------------- /ruby/config/spring.rb: -------------------------------------------------------------------------------- 1 | Spring.watch( 2 | ".ruby-version", 3 | ".rbenv-vars", 4 | "tmp/restart.txt", 5 | "tmp/caching-dev.txt" 6 | ) 7 | -------------------------------------------------------------------------------- /go/server/config.yaml: -------------------------------------------------------------------------------- 1 | package: server 2 | generate: 3 | std-http-server: true 4 | models: true 5 | embedded-spec: true 6 | output: server/server.gen.go 7 | -------------------------------------------------------------------------------- /typescript/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:22-bookworm-slim 2 | 3 | ARG WORKDIR 4 | WORKDIR /${WORKDIR} 5 | 6 | COPY package*.json ./ 7 | RUN npm install 8 | COPY . . 9 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Mark any test files as documents 2 | ruby/spec/fixture/documents/** linguist-documentation 3 | ruby/spec/fixture/documents/** linguist-vendored 4 | -------------------------------------------------------------------------------- /ruby/app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ApplicationRecord < ActiveRecord::Base 4 | self.abstract_class = true 5 | end 6 | -------------------------------------------------------------------------------- /typescript/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "tabWidth": 2, 4 | "semi": false, 5 | "jsxSingleQuote": true, 6 | "printWidth": 100 7 | } 8 | -------------------------------------------------------------------------------- /ruby/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path("../config/application", __dir__) 3 | require_relative "../config/boot" 4 | require "rails/commands" 5 | -------------------------------------------------------------------------------- /ruby/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative "application" 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /ruby/spec/factories/market.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | FactoryBot.define do 4 | factory :market, class: 'Market' do 5 | name { '市場第一部' } 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /typescript/client/index.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | export * from './runtime'; 4 | export * from './apis/index'; 5 | export * from './models/index'; 6 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | ## Reporting a Vulnerability 2 | If you discover any security related issues, please contact [@_yuki0920_](https://twitter.com/_yuki0920_) instead of using the issue. 3 | -------------------------------------------------------------------------------- /ruby/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.backtrace_cleaner.remove_silencers! if ENV["BACKTRACE"] 4 | -------------------------------------------------------------------------------- /openapitools.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json", 3 | "spaces": 2, 4 | "generator-cli": { 5 | "version": "7.6.0" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /typescript/dictionaries/i18n-config.ts: -------------------------------------------------------------------------------- 1 | export const i18n = { 2 | defaultLocale: "ja", 3 | locales: ["ja", "en"], 4 | } as const 5 | 6 | export type Locale = (typeof i18n)["locales"][number] 7 | -------------------------------------------------------------------------------- /ruby/spec/factories/industry.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | FactoryBot.define do 4 | factory :industry, class: 'Industry' do 5 | name { '情報・通信業' } 6 | code { 5250 } 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /ruby/db/migrate/20210730111150_add_reference_to_industry.rb: -------------------------------------------------------------------------------- 1 | class AddReferenceToIndustry < ActiveRecord::Migration[6.1] 2 | def change 3 | add_belongs_to :industries, :industry_category 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /ruby/spec/factories/industry_category.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | FactoryBot.define do 4 | factory :industry_category, class: 'IndustryCategory' do 5 | name { 'IT・通信系' } 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /ruby/spec/factories/security.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | FactoryBot.define do 4 | factory :security, class: 'Security' do 5 | code { 1000 } 6 | name { 'NECネッツエスアイ' } 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /terraform/prod/google_workflows_workflow.tf: -------------------------------------------------------------------------------- 1 | resource "google_workflows_workflow" "save_documents" { 2 | name = "save-documents" 3 | source_contents = file("${path.module}/workflows/save_documents.yaml") 4 | } 5 | -------------------------------------------------------------------------------- /terraform/prod/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.7" 3 | required_providers { 4 | google = { 5 | source = "hashicorp/google" 6 | version = "~> 6.18.1" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ruby/db/migrate/20210724084822_remove_unique_index_edinet_code.rb: -------------------------------------------------------------------------------- 1 | class RemoveUniqueIndexEdinetCode < ActiveRecord::Migration[6.1] 2 | def change 3 | remove_index :documents, :edinet_code, unique: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /go/db/init.sql: -------------------------------------------------------------------------------- 1 | DROP DATABASE IF EXISTS :POSTGRES_NAME; 2 | 3 | DROP USER IF EXISTS :POSTGRES_USER; 4 | 5 | CREATE USER :POSTGRES_USER WITH PASSWORD :'POSTGRES_PASSWORD'; 6 | 7 | CREATE DATABASE :POSTGRES_NAME OWNER :POSTGRES_USER; 8 | -------------------------------------------------------------------------------- /ruby/db/migrate/20240616082548_add_foreign_key_to_documents.rb: -------------------------------------------------------------------------------- 1 | class AddForeignKeyToDocuments < ActiveRecord::Migration[7.0] 2 | def change 3 | add_foreign_key :documents, :securities, column: :security_code, primary_key: :code 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /ruby/config/boot.rb: -------------------------------------------------------------------------------- 1 | require 'logger' 2 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 3 | 4 | require "bundler/setup" # Set up gems listed in the Gemfile. 5 | require "bootsnap/setup" # Speed up boot time by caching expensive operations. 6 | -------------------------------------------------------------------------------- /ruby/db/migrate/20240616082100_remove_foreign_key_from_documents.rb: -------------------------------------------------------------------------------- 1 | class RemoveForeignKeyFromDocuments < ActiveRecord::Migration[6.1] 2 | def change 3 | remove_foreign_key :documents, :securities, column: :security_code, primary_key: :code 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /ruby/.gitattributes: -------------------------------------------------------------------------------- 1 | # See https://git-scm.com/docs/gitattributes for more about git attribute files. 2 | 3 | # Mark the database schema as having been generated. 4 | db/schema.rb linguist-generated 5 | 6 | 7 | # Mark any vendored files as having been vendored. 8 | vendor/* linguist-vendored 9 | -------------------------------------------------------------------------------- /ruby/db/migrate/20230627125325_add_capital_columns_to_documents.rb: -------------------------------------------------------------------------------- 1 | class AddCapitalColumnsToDocuments < ActiveRecord::Migration[6.1] 2 | def change 3 | add_column :documents, :total_number_of_issued_shares, :bigint 4 | add_column :documents, :payout_ratio, :float 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /ruby/Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Add your own tasks in files placed in lib/tasks ending in .rake, 4 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 5 | 6 | require_relative 'config/application' 7 | 8 | Rails.application.load_tasks 9 | -------------------------------------------------------------------------------- /typescript/lib/gtag.ts: -------------------------------------------------------------------------------- 1 | export const GA_MEASUREMENT_ID = process.env.NEXT_PUBLIC_GA_ID || "" 2 | 3 | export const existsGaId = GA_MEASUREMENT_ID !== "" 4 | 5 | export const pageview = (path: string) => { 6 | window.gtag("config", GA_MEASUREMENT_ID, { 7 | page_path: path, 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /ruby/db/migrate/20210720221251_add_markets.rb: -------------------------------------------------------------------------------- 1 | class AddMarkets < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :markets do |t| 4 | t.string :name, null: false 5 | 6 | t.timestamps 7 | end 8 | 9 | add_index :markets, :name, unique: true 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /go/tools/tools.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | _ "github.com/cosmtrek/air" 5 | _ "github.com/golangci/golangci-lint/v2/cmd/golangci-lint" 6 | _ "github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen" 7 | _ "github.com/xo/usql" 8 | _ "github.com/xo/xo" 9 | _ "golang.org/x/tools/cmd/goimports" 10 | ) 11 | -------------------------------------------------------------------------------- /typescript/constant/index.ts: -------------------------------------------------------------------------------- 1 | export const NEXT_PUBLIC_API_URL: string = `${process.env.NEXT_PUBLIC_API_URL}` 2 | export const NEXT_PUBLIC_TWITTER_ID: string = `${process.env.NEXT_PUBLIC_TWITTER_ID}` 3 | 4 | export const SORT_TYPES = [ 5 | { label: "売上順", value: "net_sales" }, 6 | { label: "給与順", value: "average_annual_salary" }, 7 | ] 8 | -------------------------------------------------------------------------------- /ruby/db/migrate/20230602000314_add_industry_categories.rb: -------------------------------------------------------------------------------- 1 | class AddIndustryCategories < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :industry_categories do |t| 4 | t.string :name, null: false 5 | 6 | t.timestamps 7 | end 8 | 9 | add_index :industry_categories, :name, unique: true 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /ruby/app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ApplicationJob < ActiveJob::Base 4 | # Automatically retry jobs that encountered a deadlock 5 | # retry_on ActiveRecord::Deadlocked 6 | 7 | # Most jobs are safe to ignore if the underlying records are no longer available 8 | # discard_on ActiveJob::DeserializationError 9 | end 10 | -------------------------------------------------------------------------------- /ruby/db/migrate/20210720221250_add_security_lists.rb: -------------------------------------------------------------------------------- 1 | class AddSecurityLists < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :security_lists do |t| 4 | t.string :file_title, null: false 5 | t.date :downloaded_at, null: false 6 | 7 | t.timestamps 8 | end 9 | 10 | add_index :security_lists, :file_title, unique: true 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /typescript/hooks/GetDictionary.ts: -------------------------------------------------------------------------------- 1 | import "server-only" 2 | 3 | const dictionaries = { 4 | ja: () => import("@/dictionaries/ja.json").then((module) => module.default), 5 | en: () => import("@/dictionaries/en.json").then((module) => module.default), 6 | } 7 | 8 | export const getDictionary = async (locale: string) => 9 | dictionaries[locale as keyof typeof dictionaries]() 10 | -------------------------------------------------------------------------------- /ruby/db/migrate/20210720221252_add_industries.rb: -------------------------------------------------------------------------------- 1 | class AddIndustries < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :industries do |t| 4 | t.string :name, null: false 5 | t.integer :code, null: false 6 | 7 | t.timestamps 8 | end 9 | 10 | add_index :industries, :name, unique: true 11 | add_index :industries, :code, unique: true 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /ruby/app/models/market.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Market < ApplicationRecord 4 | INITIAL_DATA = [ 5 | { name: 'プライム' }, 6 | { name: 'スタンダード' }, 7 | { name: 'グロース' } 8 | ].freeze 9 | 10 | has_many :securities 11 | 12 | class << self 13 | def create_seed 14 | INITIAL_DATA.each do |data| 15 | create!(data) 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /go/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.23 2 | 3 | # Set destination for COPY 4 | WORKDIR /go/src 5 | 6 | # Download Go modules 7 | COPY ./go.* ./ 8 | RUN go mod download 9 | 10 | COPY . . 11 | 12 | # Set GOBIN and add it to PATH 13 | ENV GOBIN /go/bin 14 | ENV PATH $GOBIN:$PATH 15 | 16 | RUN make install/tools 17 | 18 | RUN CGO_ENABLED=0 GOOS=linux go build -o /go/bin/server ./cmd/server 19 | 20 | CMD ["/go/bin/server"] 21 | -------------------------------------------------------------------------------- /go/Dockerfile.prod: -------------------------------------------------------------------------------- 1 | FROM golang:1.23 2 | 3 | # Set destination for COPY 4 | WORKDIR /go/src 5 | 6 | # Download Go modules 7 | COPY go/go.* ./ 8 | RUN go mod download 9 | 10 | # Copy the rest of the source code 11 | COPY go/ ./ 12 | 13 | RUN CGO_ENABLED=0 GOOS=linux go build -o /go/bin/server ./cmd/server 14 | 15 | # TODO: Add a multi-stage build to reduce the size of the final image 16 | 17 | CMD ["/go/bin/server"] 18 | -------------------------------------------------------------------------------- /ruby/app/models/security.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Security < ApplicationRecord 4 | belongs_to :market 5 | belongs_to :industry, foreign_key: :industry_code, primary_key: :code 6 | has_many :documents, foreign_key: :security_code, primary_key: :code, dependent: :destroy 7 | 8 | validates :code, uniqueness: true, presence: true 9 | validates :name, uniqueness: true, presence: true 10 | end 11 | -------------------------------------------------------------------------------- /go/scripts/install_go_tools.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | echo "Installing go tools..." 4 | echo "to GOBIN: $(go env GOBIN)" 5 | 6 | tools=() 7 | while IFS='' read -r line; do tools+=("$line"); done < <(sed -En 's/[[:space:]]+_ "(.*)"/\1/p' tools/tools.go) 8 | 9 | pushd tools 10 | 11 | echo "install go tools" 12 | for tool in "${tools[@]}" 13 | do 14 | echo " - $tool" 15 | go install "$tool" 16 | done 17 | 18 | popd 19 | -------------------------------------------------------------------------------- /ruby/db/migrate/20240616082152_change_code_type_in_securities_and_documents.rb: -------------------------------------------------------------------------------- 1 | class ChangeCodeTypeInSecuritiesAndDocuments < ActiveRecord::Migration[6.1] 2 | def up 3 | change_column :securities, :code, :string 4 | change_column :documents, :security_code, :string 5 | end 6 | 7 | def down 8 | change_column :securities, :code, :integer 9 | change_column :documents, :security_code, :integer 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /.github/workflows/go_build.yml: -------------------------------------------------------------------------------- 1 | name: Go build 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | defaults: 9 | run: 10 | working-directory: ./go 11 | steps: 12 | - uses: actions/checkout@v4 13 | - name: Set up Go 14 | uses: actions/setup-go@v5 15 | with: 16 | go-version-file: go/go.mod 17 | cache: true 18 | - name: Build 19 | run: go build -v ./... 20 | -------------------------------------------------------------------------------- /ruby/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure parameters to be filtered from the log file. Use this to limit dissemination of 4 | # sensitive information. See the ActiveSupport::ParameterFilter documentation for supported 5 | # notations and behaviors. 6 | Rails.application.config.filter_parameters += [ 7 | :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn 8 | ] 9 | -------------------------------------------------------------------------------- /go/scripts/setup_db.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu 4 | 5 | echo "Creating database..." 6 | usql "$DATABASE_HOST_URL" \ 7 | --set=POSTGRES_NAME="$POSTGRES_TEST_NAME" \ 8 | --set=POSTGRES_USER="$POSTGRES_TEST_USER" \ 9 | --set=POSTGRES_PASSWORD="$POSTGRES_PASSWORD" \ 10 | -f db/init.sql 11 | 12 | echo "Loading schema..." 13 | usql "$DATABASE_TEST_URL" \ 14 | -f db/schema.sql 15 | 16 | echo "Loading seed data..." 17 | usql "$DATABASE_TEST_URL" \ 18 | -f db/seed.sql 19 | -------------------------------------------------------------------------------- /ruby/config/credentials.yml.enc: -------------------------------------------------------------------------------- 1 | qoT30DMuwvDGa07r/zg9pCT7eYMDx2+bRIV3dfj+SL1WV+Uk6InInpWF2JkV08dskxXrMHjONw6o8a9bRy2T8GKxytD4VsCLE28tEaU+ey2L3W/5yhPfzm0OOkX92rf7G7wa7pLoDTd+avkGNXQLqztPL6DnKw51VXy8WiFKCw3mrAPfvMJXM+AGzXJnR/DkK70CiZQCGO/ac+9ArCqVSFD8fKUr/oB8ijk4h+AG0AgpxqdbGwX7lGhLkz53feVku4JmWQZa5UlWky7OQCr7x6Zvt98kjyHb2Drd29XcccJ+iYMkbw1bBuewpWydsJjwX20pHEzq+AH45y1UW8xO6sEqBvGpGsVa3OGV3Xk+DBJm/yamMF1acRTrAkIgsrZRp+6wZ7wYO2tKEC2FQSaoGMnHrGvpavLogqhT--I/CV3kiOGLKqLEKp--bjgV85CaPCVGQn47s+GuHw== -------------------------------------------------------------------------------- /ruby/lib/tasks/update_industry_category.rake: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | namespace :update_industry_category do 4 | desc '書類一覧取得APIからdocumentを取得する' 5 | 6 | task execute: :environment do 7 | Industry.find_each do |industry| 8 | Industry::INITIAL_DATA.each do |data| 9 | if industry.name == data[:name] 10 | industry.update!(industry_category_id: data[:industry_category_id]) 11 | end 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /typescript/types.ts: -------------------------------------------------------------------------------- 1 | export interface EachCompanyJSON { 2 | security_code: number 3 | security_name: string 4 | net_sales: number | null 5 | ordinary_income: number | null 6 | average_annual_salary: number | null 7 | industry_name: string 8 | market_name: string 9 | } 10 | 11 | export interface MetaJSON { 12 | from: number 13 | count: number 14 | items: number 15 | page: number 16 | pages: number 17 | prev: number | null 18 | next: number | null 19 | } 20 | -------------------------------------------------------------------------------- /supabase/README.md: -------------------------------------------------------------------------------- 1 | # How to update test db 2 | 3 | schema.sql is used to update test db 4 | 5 | ## Prerequisites 6 | - Prod DB: Maintained by rails 7 | - Test DB: Maintained by supabase cli 8 | 9 | ## Dump shema in production 10 | 11 | ``` 12 | supabase login 13 | supabase db dump -f supabase/production_schema.sql --db-url 14 | ``` 15 | 16 | ## Update production shema to test db 17 | ``` 18 | supabase link --project-ref company-ranking-test 19 | supabase db diff -f apply_production_schema 20 | supabase db push 21 | ``` 22 | -------------------------------------------------------------------------------- /typescript/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | -------------------------------------------------------------------------------- /ruby/bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | if !defined?(Spring) && [nil, "development", "test"].include?(ENV["RAILS_ENV"]) 3 | gem "bundler" 4 | require "bundler" 5 | 6 | # Load Spring without loading other gems in the Gemfile, for speed. 7 | Bundler.locked_gems&.specs&.find { |spec| spec.name == "spring" }&.tap do |spring| 8 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path 9 | gem "spring", spring.version 10 | require "spring/binstub" 11 | rescue Gem::LoadError 12 | # Ignore when Spring is not installed. 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /.github/workflows/rubocop.yml: -------------------------------------------------------------------------------- 1 | name: RuboCop 2 | on: [push] 3 | jobs: 4 | rubocop: 5 | name: Run rubocop 6 | runs-on: ubuntu-latest 7 | defaults: 8 | run: 9 | working-directory: ./ruby 10 | env: 11 | RAILS_ENV: test 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Set up Ruby 15 | uses: ruby/setup-ruby@v1 16 | with: 17 | ruby-version: 3.1.4 18 | bundler-cache: true 19 | working-directory: ./ruby 20 | - name: Run rubocop 21 | run: bundle exec rubocop 22 | -------------------------------------------------------------------------------- /go/.octocov.yml: -------------------------------------------------------------------------------- 1 | # generated by octocov init 2 | coverage: 3 | if: true 4 | paths: 5 | - coverage.out 6 | codeToTestRatio: 7 | code: 8 | - '**/*.go' 9 | - '!/models/**/*.go' 10 | - '!/server/server.gen.go' 11 | - '!**/*_test.go' 12 | test: 13 | - '**/*_test.go' 14 | testExecutionTime: 15 | if: true 16 | diff: 17 | datastores: 18 | - artifact://${GITHUB_REPOSITORY} 19 | comment: 20 | if: is_pull_request 21 | deletePrevious: true 22 | report: 23 | if: is_default_branch 24 | datastores: 25 | - artifact://${GITHUB_REPOSITORY} 26 | -------------------------------------------------------------------------------- /ruby/lib/tasks/destroy_old_documents.rake: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | namespace :destroy_old_documents do 4 | desc '古いDocumentを削除する' 5 | 6 | task execute: :environment do 7 | Document.group(:security_code).count.each do |code, count| 8 | next unless count > 1 9 | 10 | old_document = Document.where(security_code: code).order(:id).first 11 | Rails.logger.info("Deleting id: #{old_document.id} security_code: #{old_document.security_code} name: #{old_document.filer_name}") 12 | old_document.destroy! 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /typescript/client/.openapi-generator/FILES: -------------------------------------------------------------------------------- 1 | apis/DefaultApi.ts 2 | apis/index.ts 3 | index.ts 4 | models/Company.ts 5 | models/EachCompany.ts 6 | models/EachIndustry.ts 7 | models/EachMarket.ts 8 | models/Industry.ts 9 | models/Market.ts 10 | models/Meta.ts 11 | models/ResponseCompanies.ts 12 | models/ResponseCompany.ts 13 | models/ResponseIndustries.ts 14 | models/ResponseIndustry.ts 15 | models/ResponseIndustryIDs.ts 16 | models/ResponseMarket.ts 17 | models/ResponseMarketIDs.ts 18 | models/ResponseMarkets.ts 19 | models/ResponseSecurityCodes.ts 20 | models/index.ts 21 | runtime.ts 22 | -------------------------------------------------------------------------------- /terraform/prod/google_compute_network.tf: -------------------------------------------------------------------------------- 1 | resource "google_compute_network" "default" { 2 | name = "default" 3 | auto_create_subnetworks = true 4 | description = "Default network for the project" 5 | network_firewall_policy_enforcement_order = "AFTER_CLASSIC_FIREWALL" 6 | project = "company-ranking-prod" 7 | routing_mode = "REGIONAL" 8 | } 9 | 10 | import { 11 | id = "company-ranking-prod/default" 12 | to = google_compute_network.default 13 | } 14 | -------------------------------------------------------------------------------- /typescript/hooks/CreateQueryString.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback } from "react" 2 | import { useSearchParams } from "next/navigation" 3 | 4 | export function useCreateQueryString() { 5 | const searchParams = useSearchParams()! 6 | const createQueryString = useCallback( 7 | // https://nextjs.org/docs/app/api-reference/functions/use-search-params#updating-searchparams 8 | (name: string, value: string) => { 9 | // @ts-ignore 10 | const params = new URLSearchParams() 11 | params.set(name, value) 12 | 13 | return params.toString() 14 | }, 15 | [], 16 | ) 17 | 18 | return createQueryString 19 | } 20 | -------------------------------------------------------------------------------- /typescript/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ruby/db/migrate/20210720221801_add_securities.rb: -------------------------------------------------------------------------------- 1 | class AddSecurities < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :securities do |t| 4 | t.integer :code, null: false 5 | t.string :name, null: false 6 | t.belongs_to :market, null: false, index: true 7 | t.integer :industry_code, null: false 8 | 9 | t.timestamps 10 | end 11 | 12 | add_foreign_key :securities, :industries, column: :industry_code, primary_key: :code 13 | add_index :securities, :code, unique: true 14 | add_index :securities, :name, unique: true 15 | add_index :securities, :industry_code 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /typescript/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./pages/**/*.{js,ts,jsx,tsx,mdx}", 5 | "./components/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./app/**/*.{js,ts,jsx,tsx,mdx}", 7 | ], 8 | theme: { 9 | extend: { 10 | backgroundImage: { 11 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", 12 | "gradient-conic": "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", 13 | }, 14 | }, 15 | }, 16 | daisyui: { 17 | themes: ["light", "dark"], 18 | }, 19 | plugins: [require("@tailwindcss/typography"), require("daisyui")], 20 | } 21 | -------------------------------------------------------------------------------- /ruby/lib/tasks/adjust_salary.rake: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative '../../app/models/element_util' 4 | 5 | namespace :adjust_salary do 6 | include ElementUtil 7 | 8 | desc 'Adjust salary' 9 | 10 | task execute: :environment do 11 | Document.where('average_annual_salary > 5000000000').find_each do |document| 12 | new_salaly = adjust_salary(document.average_annual_salary) 13 | Rails.logger.info("Adjusting security_code: #{document.security_code} name: #{document.filer_name} salary: #{document.average_annual_salary} -> #{new_salaly}") 14 | document.update!(average_annual_salary: new_salaly) 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /typescript/client/models/index.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | export * from './Company'; 4 | export * from './EachCompany'; 5 | export * from './EachIndustry'; 6 | export * from './EachMarket'; 7 | export * from './Industry'; 8 | export * from './Market'; 9 | export * from './Meta'; 10 | export * from './ResponseCompanies'; 11 | export * from './ResponseCompany'; 12 | export * from './ResponseIndustries'; 13 | export * from './ResponseIndustry'; 14 | export * from './ResponseIndustryIDs'; 15 | export * from './ResponseMarket'; 16 | export * from './ResponseMarketIDs'; 17 | export * from './ResponseMarkets'; 18 | export * from './ResponseSecurityCodes'; 19 | -------------------------------------------------------------------------------- /ruby/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:3.1.4-alpine 2 | 3 | ARG WORKDIR 4 | 5 | ENV RUNTIME_PACKAGES="linux-headers libxml2-dev make gcc libc-dev nodejs tzdata postgresql-dev postgresql git less gcompat" \ 6 | DEV_PACKAGES="build-base curl-dev" \ 7 | LANG=C.UTF-8 \ 8 | TZ=Asia/Tokyo 9 | 10 | WORKDIR ${WORKDIR} 11 | 12 | COPY Gemfile* ./ 13 | 14 | RUN apk update && \ 15 | apk upgrade && \ 16 | apk add --no-cache ${RUNTIME_PACKAGES} && \ 17 | apk add --virtual build-dependencies --no-cache ${DEV_PACKAGES} && \ 18 | gem install bundler -v 2.1.4 && \ 19 | bundle _2.1.4_ install -j4 --path vendor/bundle && \ 20 | apk del build-dependencies 21 | 22 | COPY . . 23 | -------------------------------------------------------------------------------- /terraform/prod/workflows/save_documents.yaml: -------------------------------------------------------------------------------- 1 | main: 2 | steps: 3 | - init: 4 | assign: 5 | - project_id: ${sys.get_env("GOOGLE_CLOUD_PROJECT_ID")} 6 | - job_location: asia-northeast1 7 | - save_document_summary: 8 | call: googleapis.run.v1.namespaces.jobs.run 9 | args: 10 | name: ${"namespaces/" + project_id + "/jobs/" + "save-document-summary-daily-job"} 11 | location: ${job_location} 12 | - save_document_detail: 13 | call: googleapis.run.v1.namespaces.jobs.run 14 | args: 15 | name: ${"namespaces/" + project_id + "/jobs/" + "save-document-detail-job"} 16 | location: ${job_location} 17 | -------------------------------------------------------------------------------- /typescript/components/NumberOfResults.tsx: -------------------------------------------------------------------------------- 1 | export default function NumberOfResults({ 2 | currentPage, 3 | lastPage, 4 | offsetCount, 5 | limitCount, 6 | totalCount, 7 | unit, 8 | }: { 9 | currentPage: number 10 | lastPage: number 11 | offsetCount: number 12 | limitCount: number 13 | totalCount: number 14 | unit: string 15 | }) { 16 | if (currentPage === lastPage) { 17 | return ( 18 | 19 | ({offsetCount} - {totalCount} / {totalCount} {unit}) 20 | 21 | ) 22 | } else { 23 | return ( 24 | 25 | ({offsetCount} - {offsetCount + limitCount - 1} / {totalCount} {unit}) 26 | 27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ruby/Dockerfile.prod: -------------------------------------------------------------------------------- 1 | FROM ruby:3.1.4-alpine 2 | 3 | ENV RUNTIME_PACKAGES="linux-headers libxml2-dev make gcc libc-dev nodejs tzdata postgresql-dev postgresql git less gcompat" \ 4 | DEV_PACKAGES="build-base curl-dev" \ 5 | LANG=C.UTF-8 \ 6 | TZ=Asia/Tokyo 7 | 8 | WORKDIR /myapp 9 | 10 | COPY ./ruby/Gemfile* ./ 11 | 12 | RUN apk update && \ 13 | apk upgrade && \ 14 | apk add --no-cache ${RUNTIME_PACKAGES} && \ 15 | apk add --virtual build-dependencies --no-cache ${DEV_PACKAGES} && \ 16 | gem install bundler -v 2.1.4 && \ 17 | bundle config set without development test && \ 18 | bundle _2.1.4_ install -j4 && \ 19 | apk del build-dependencies 20 | 21 | COPY ./ruby . 22 | -------------------------------------------------------------------------------- /ruby/app/models/industry_category.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class IndustryCategory < ApplicationRecord 4 | has_many :industries 5 | 6 | INITIAL_DATA = [ 7 | { id: 1, name: 'IT・通信系' }, 8 | { id: 2, name: 'メーカー系(食料・繊維・医薬品)' }, 9 | { id: 3, name: 'メーカー系(素材)' }, 10 | { id: 4, name: 'メーカー系(鉄鋼・金属)' }, 11 | { id: 5, name: 'メーカー系(電気・機械)' }, 12 | { id: 6, name: '運輸・物流系' }, 13 | { id: 7, name: '卸売・小売・サービス系' }, 14 | { id: 8, name: '金融系' }, 15 | { id: 9, name: '不動産・建設系' }, 16 | { id: 10, name: 'その他' } 17 | ].freeze 18 | 19 | class << self 20 | def create_seed 21 | INITIAL_DATA.each do |data| 22 | create!(data) 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /ruby/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, "\\1en" 8 | # inflect.singular /^(ox)en/i, "\\1" 9 | # inflect.irregular "person", "people" 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym "RESTful" 16 | # end 17 | -------------------------------------------------------------------------------- /typescript/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import "@/styles/globals.css" 2 | import { Inter } from "next/font/google" 3 | import GoogleAnalytics from "@/components/GoogleAnalytics" 4 | import { Suspense } from "react" 5 | 6 | const inter = Inter({ subsets: ["latin"] }) 7 | export default async function RootLayout(props: { 8 | children: React.ReactNode 9 | params: Promise<{ lang: string }> 10 | }) { 11 | const params = await props.params 12 | 13 | const { lang } = params 14 | 15 | const { children } = props 16 | 17 | return ( 18 | 19 | 20 | 21 | 22 | 23 | {children} 24 | 25 | 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /typescript/app/error.tsx: -------------------------------------------------------------------------------- 1 | // NOTE: https://nextjs.org/docs/app/building-your-application/routing/error-handling 2 | "use client" // Error components must be Client Components 3 | 4 | import { useEffect } from "react" 5 | 6 | export default function Error({ error, reset }: { error: Error; reset: () => void }) { 7 | useEffect(() => { 8 | // Log the error to an error reporting service 9 | console.error(error) 10 | }, [error]) 11 | 12 | return ( 13 |
14 |

Something went wrong!

15 | 23 |
24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /ruby/lib/tasks/one_shot/20240716_delete_securities_invalid_code.rake: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | INVALID_CODES = %w[ 4 | 211 5 | 206 6 | 208 7 | 205 8 | 202 9 | 196 10 | 194 11 | 197 12 | 198 13 | 195 14 | 192 15 | 190 16 | 189 17 | 186 18 | 187 19 | 184 20 | 177 21 | 176 22 | 175 23 | 173 24 | 168 25 | 160 26 | 165 27 | 166 28 | 167 29 | 156 30 | 157 31 | 153 32 | 155 33 | 146 34 | 149 35 | 151 36 | 145 37 | 147 38 | 148 39 | 150 40 | 143 41 | 142 42 | 141 43 | 137 44 | 138 45 | 135 46 | 130 47 | ].freeze 48 | 49 | namespace :delete_securities do 50 | task invalid_code: :environment do 51 | Security.where(code: INVALID_CODES).destroy_all 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /typescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true, 17 | "plugins": [ 18 | { 19 | "name": "next" 20 | } 21 | ], 22 | "paths": { 23 | "@/*": ["./*"] 24 | } 25 | }, 26 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 27 | "exclude": ["node_modules"] 28 | } 29 | -------------------------------------------------------------------------------- /ruby/lib/tasks/one_shot/20220618_change_market_name.rake: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | MARKET_NAME_MAPPING = { 4 | '市場第一部' => 'プライム', 5 | '市場第二部' => 'スタンダード', 6 | 'マザーズ' => 'グロース' 7 | }.freeze 8 | 9 | namespace :change_market_name do 10 | task security: :environment do 11 | market = Market.find_by(name: 'マザーズ') 12 | Security.includes(:market).find_each.each do |security| 13 | if security.market.name == 'JASDAQ' 14 | security.update!(market: market) 15 | end 16 | end 17 | end 18 | 19 | task market_name: :environment do 20 | Market.find_each do |market| 21 | new_name = MARKET_NAME_MAPPING[market.name] 22 | market.update!(name: new_name) 23 | end 24 | 25 | Market.find_by(name: 'JASDAQ').destroy! 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /ruby/app/models/element_util.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ElementUtil 4 | def calculate_scale(element) 5 | return nil unless element.respond_to?(:text) 6 | 7 | sign = element.attr('sign') == '-' ? -1 : 1 8 | scale = element.attr('scale').to_i 9 | decimals = element.attr('decimals').to_i 10 | base_number = element.text.delete(',').to_f 11 | 12 | if scale 13 | sign * base_number * (10**scale) 14 | elsif decimals 15 | sign * base_number * (10**(-1 * decimals)) 16 | else 17 | sign * base_number 18 | end 19 | end 20 | 21 | def adjust_salary(number) 22 | return if number.nil? 23 | return number / 1_000_000 if number > 5_000_000_000_000 24 | return number / 1_000 if number > 5_000_000_000 25 | 26 | number 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /ruby/cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | # build the container image 3 | - id: 'Build container image' 4 | name: 'gcr.io/cloud-builders/docker' 5 | args: 6 | - 'build' 7 | - '-t' 8 | - 'gcr.io/$PROJECT_ID/company-ranking-job:$COMMIT_SHA' 9 | - '-t' 10 | - 'gcr.io/$PROJECT_ID/company-ranking-job:latest' 11 | - '-f' 12 | - 'ruby/Dockerfile.prod' 13 | - '.' 14 | - id: 'Push container image with COMMIT_SHA tag' 15 | name: 'gcr.io/cloud-builders/docker' 16 | args: ['push', 'gcr.io/$PROJECT_ID/company-ranking-job:$COMMIT_SHA'] 17 | - id: 'Push container image with latest tag' 18 | name: 'gcr.io/cloud-builders/docker' 19 | args: ['push', 'gcr.io/$PROJECT_ID/company-ranking-job:latest'] 20 | images: 21 | - 'gcr.io/$PROJECT_ID/company-ranking-job:$COMMIT_SHA' 22 | -------------------------------------------------------------------------------- /terraform/prod/google_secret_manager_secret.tf: -------------------------------------------------------------------------------- 1 | resource "google_secret_manager_secret" "database_url" { 2 | project = "company-ranking-prod" 3 | replication { 4 | auto {} 5 | } 6 | secret_id = "DATABASE_URL" 7 | } 8 | 9 | resource "google_secret_manager_secret" "database_test_url" { 10 | project = "company-ranking-prod" 11 | replication { 12 | auto {} 13 | } 14 | secret_id = "DATABASE_TEST_URL" 15 | } 16 | 17 | resource "google_secret_manager_secret" "secret_key_base" { 18 | project = "company-ranking-prod" 19 | replication { 20 | auto {} 21 | } 22 | secret_id = "SECRET_KEY_BASE" 23 | } 24 | 25 | resource "google_secret_manager_secret" "edinet_api_key" { 26 | project = "company-ranking-prod" 27 | replication { 28 | auto {} 29 | } 30 | secret_id = "EDINET_API_KEY" 31 | } 32 | -------------------------------------------------------------------------------- /typescript/app/not-found.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link" 2 | 3 | export default function NotFound() { 4 | return ( 5 |
6 |
7 | 13 | 19 | 20 | 404 Not Found. 21 |
22 | 23 | Top 24 | 25 |
26 |
27 |
28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /terraform/prod/google_artifact_registry_repository.tf: -------------------------------------------------------------------------------- 1 | resource "google_artifact_registry_repository" "cloud_run_source_deploy" { 2 | description = "Cloud Run Source Deployments" 3 | format = "DOCKER" 4 | location = "asia-northeast1" 5 | project = "company-ranking-prod" 6 | repository_id = "cloud-run-source-deploy" 7 | } 8 | 9 | resource "google_artifact_registry_repository" "gcr_io" { 10 | format = "DOCKER" 11 | location = "us" 12 | project = "company-ranking-prod" 13 | repository_id = "gcr.io" 14 | 15 | cleanup_policies { 16 | action = "DELETE" 17 | id = "deletion-policy" 18 | 19 | condition { 20 | tag_state = "ANY" 21 | } 22 | } 23 | cleanup_policies { 24 | action = "KEEP" 25 | id = "store-policy" 26 | 27 | most_recent_versions { 28 | keep_count = 5 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ruby/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore all logfiles and tempfiles. 11 | /log/* 12 | /tmp/* 13 | !/log/.keep 14 | !/tmp/.keep 15 | 16 | # Ignore pidfiles, but keep the directory. 17 | /tmp/pids/* 18 | !/tmp/pids/ 19 | !/tmp/pids/.keep 20 | 21 | # Ignore uploaded files in development. 22 | /storage/* 23 | !/storage/.keep 24 | .byebug_history 25 | 26 | # Ignore master key for decrypting credentials and more. 27 | /config/master.key 28 | 29 | .ash_history 30 | .lesshst 31 | .local/ 32 | /.cache/* 33 | *.DS_Store 34 | 35 | # Temp files 36 | ./ruby/lib/documents 37 | -------------------------------------------------------------------------------- /typescript/components/BreadCrumbs.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react" 2 | import Link from "next/link" 3 | 4 | export type CrumbItem = { 5 | label: ReactNode 6 | path: string 7 | } 8 | export type BreadcrumbsProps = { 9 | items: CrumbItem[] 10 | } 11 | 12 | export default function Breadcrumbs({ items }: BreadcrumbsProps) { 13 | return ( 14 |
15 | {items.map((crumb, i) => { 16 | const isLastItem = i === items.length - 1 17 | if (!isLastItem) { 18 | return ( 19 | <> 20 | 21 | {crumb.label} 22 | 23 | {/* separator */} 24 | / 25 | 26 | ) 27 | } else { 28 | return crumb.label 29 | } 30 | })} 31 |
32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /go/models/industrycategory_extend.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | // IndustryCategoryALL returns a slice of all IndustryCategory. 8 | func IndustryCategoryALL(ctx context.Context, db DB) ([]*IndustryCategory, error) { 9 | // query 10 | const sqlstr = `SELECT ` + 11 | `id, name ` + 12 | `FROM public.industry_categories` 13 | // run 14 | logf(sqlstr) 15 | rows, err := db.QueryContext(ctx, sqlstr) 16 | if err != nil { 17 | return nil, logerror(err) 18 | } 19 | defer rows.Close() 20 | // process 21 | var categories []*IndustryCategory 22 | for rows.Next() { 23 | var category IndustryCategory 24 | err := rows.Scan(&category.ID, &category.Name) 25 | if err != nil { 26 | return nil, logerror(err) 27 | } 28 | categories = append(categories, &category) 29 | } 30 | if err := rows.Err(); err != nil { 31 | return nil, logerror(err) 32 | } 33 | return categories, nil 34 | } 35 | -------------------------------------------------------------------------------- /ruby/db/migrate/20210802234100_add_financial_columns_to_documents.rb: -------------------------------------------------------------------------------- 1 | class AddFinancialColumnsToDocuments < ActiveRecord::Migration[6.1] 2 | def change 3 | add_column :documents, :capital_stock, :bigint 4 | add_column :documents, :net_assets, :bigint 5 | add_column :documents, :total_assets, :bigint 6 | add_column :documents, :equity_to_asset_ratio, :float 7 | add_column :documents, :rate_of_return_on_equity, :float 8 | add_column :documents, :price_earnings_ratio, :float 9 | add_column :documents, :net_cash_provided_by_used_in_operating_activities, :bigint 10 | add_column :documents, :net_cash_provided_by_used_in_investing_activities, :bigint 11 | add_column :documents, :net_cash_provided_by_used_in_financing_activities, :bigint 12 | add_column :documents, :cash_and_cash_equivalents, :bigint 13 | add_column :documents, :consolidated_number_of_employees, :integer 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /go/models/fixture.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/google/uuid" 7 | ) 8 | 9 | func SecurityFixture(setter func(security *Security)) *Security { 10 | security := &Security{ 11 | Code: uuid.New().String(), 12 | Name: uuid.New().String(), 13 | MarketID: int64(uuid.New().ID() % 100000), 14 | IndustryCode: int(uuid.New().ID() % 100000), 15 | CreatedAt: time.Now(), 16 | UpdatedAt: time.Now(), 17 | } 18 | 19 | setter(security) 20 | 21 | return security 22 | } 23 | 24 | func DocumentFixture(setter func(document *Document)) *Document { 25 | document := &Document{ 26 | SecurityCode: uuid.New().String(), 27 | DocumentID: uuid.New().String(), 28 | EdinetCode: uuid.New().String(), 29 | FilerName: uuid.New().String(), 30 | CreatedAt: time.Now(), 31 | UpdatedAt: time.Now(), 32 | } 33 | 34 | setter(document) 35 | 36 | return document 37 | } 38 | -------------------------------------------------------------------------------- /typescript/app/[lang]/error.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | export default function Error({ error, reset }: { error: Error; reset: () => void }) { 4 | return ( 5 |
6 |
7 | 13 | 19 | 20 | {error.message} 21 |
22 | 25 |
26 |
27 |
28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /go/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/yuki0920/company-ranking/go 2 | 3 | go 1.23.4 4 | 5 | require ( 6 | github.com/getkin/kin-openapi v0.129.0 7 | github.com/go-chi/chi v4.1.2+incompatible 8 | github.com/google/go-cmp v0.7.0 9 | github.com/google/uuid v1.6.0 10 | github.com/lib/pq v1.10.9 11 | github.com/oapi-codegen/runtime v1.1.1 12 | github.com/rs/cors v1.11.1 13 | ) 14 | 15 | require ( 16 | github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect 17 | github.com/go-openapi/jsonpointer v0.21.0 // indirect 18 | github.com/go-openapi/swag v0.23.0 // indirect 19 | github.com/josharian/intern v1.0.0 // indirect 20 | github.com/mailru/easyjson v0.7.7 // indirect 21 | github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect 22 | github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 // indirect 23 | github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 // indirect 24 | github.com/perimeterx/marshmallow v1.1.5 // indirect 25 | gopkg.in/yaml.v3 v3.0.1 // indirect 26 | ) 27 | -------------------------------------------------------------------------------- /ruby/app/models/security_list.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class SecurityList < ApplicationRecord 4 | validates :file_title, uniqueness: true, presence: true 5 | validates :downloaded_at, presence: true 6 | 7 | class << self 8 | def download 9 | agent = Mechanize.new 10 | page = agent.get('https://www.jpx.co.jp/markets/statistics-equities/misc/01.html') 11 | element = page.at('.component-file') 12 | title = element.at('th').inner_text 13 | src_path = element.at('a').get_attribute('href') 14 | return if same(title) 15 | 16 | agent.download(src_path, dest_path) 17 | create!(file_title: title, downloaded_at: Time.zone.now) 18 | end 19 | 20 | def delete 21 | FileUtils.rm_f(dest_path) 22 | end 23 | 24 | def same(title) 25 | SecurityList.find_by(file_title: title) 26 | end 27 | 28 | def dest_path 29 | Rails.root.join('lib/security_lists/data_j.xls').to_s 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /typescript/components/Pagination.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link" 2 | import { UrlObject } from "url" 3 | 4 | export default function Pagination({ 5 | prevRef, 6 | nextRef, 7 | prev, 8 | next, 9 | dict, 10 | }: { 11 | prevRef: UrlObject 12 | nextRef: object 13 | prev: number | null 14 | next: number | null 15 | dict: { 16 | prev: string 17 | next: string 18 | } 19 | }) { 20 | return ( 21 |
22 |
23 | 28 | {dict.prev} 29 | 30 | 35 | {dict.next} 36 | 37 |
38 |
39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /go/middleware/logger.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "log/slog" 5 | "net/http" 6 | "regexp" 7 | "time" 8 | 9 | "github.com/go-chi/chi/middleware" 10 | ) 11 | 12 | func LoggingMiddleware() func(next http.Handler) http.Handler { 13 | return func(next http.Handler) http.Handler { 14 | fn := func(w http.ResponseWriter, r *http.Request) { 15 | ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor) 16 | t1 := time.Now() 17 | defer func() { 18 | re := regexp.MustCompile(`/api/v1/companies/\d+`) 19 | path := r.URL.Path 20 | if !re.MatchString(path) { 21 | slog.Info("Served", 22 | slog.String("proto", r.Proto), 23 | slog.String("method", r.Method), 24 | slog.String("path", r.URL.Path), 25 | slog.Duration("lat", time.Since(t1)), 26 | slog.Int("status", ww.Status()), 27 | slog.Int("size", ww.BytesWritten()), 28 | ) 29 | } 30 | }() 31 | 32 | next.ServeHTTP(ww, r) 33 | } 34 | return http.HandlerFunc(fn) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ruby/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # The following keys must be escaped otherwise they will not be retrieved by 20 | # the default I18n backend: 21 | # 22 | # true, false, on, off, yes, no 23 | # 24 | # Instead, surround them with single quotes. 25 | # 26 | # en: 27 | # 'true': 'foo' 28 | # 29 | # To learn more, please read the Rails Internationalization guide 30 | # available at https://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | -------------------------------------------------------------------------------- /terraform/.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # Crash log files 9 | crash.log 10 | crash.*.log 11 | 12 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as 13 | # password, private keys, and other secrets. These should not be part of version 14 | # control as they are data points which are potentially sensitive and subject 15 | # to change depending on the environment. 16 | *.tfvars 17 | *.tfvars.json 18 | 19 | # Ignore override files as they are usually used to override resources locally and so 20 | # are not checked in 21 | override.tf 22 | override.tf.json 23 | *_override.tf 24 | *_override.tf.json 25 | 26 | # Include override files you do wish to add to version control using negated pattern 27 | # !example_override.tf 28 | 29 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 30 | # example: *tfplan* 31 | 32 | # Ignore CLI configuration files 33 | .terraformrc 34 | terraform.rc 35 | -------------------------------------------------------------------------------- /typescript/components/ThemeChangeMenu.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { useState, useEffect } from "react" 4 | import { themeChange } from "theme-change" 5 | 6 | type Option = { 7 | value: string 8 | content: string 9 | } 10 | 11 | export default function ThemeChangeMenu() { 12 | const [currentSelected, setCurrentSelected] = useState("light") 13 | const changeSelect = (newSelect: string) => { 14 | setCurrentSelected(newSelect) 15 | } 16 | const options: Option[] = [ 17 | { value: "light", content: "Light" }, 18 | { value: "dark", content: "Dark" }, 19 | ] 20 | 21 | useEffect(() => { 22 | themeChange(false) 23 | // false parameter is required for react project 24 | }, []) 25 | 26 | return ( 27 | 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Yuki Watanabe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ruby/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "fileutils" 3 | 4 | # path to your application root. 5 | APP_ROOT = File.expand_path("..", __dir__) 6 | 7 | def system!(*args) 8 | system(*args) || abort("\n== Command #{args} failed ==") 9 | end 10 | 11 | FileUtils.chdir APP_ROOT do 12 | # This script is a way to set up or update your development environment automatically. 13 | # This script is idempotent, so that you can run it at any time and get an expectable outcome. 14 | # Add necessary setup steps to this file. 15 | 16 | puts "== Installing dependencies ==" 17 | system! "gem install bundler --conservative" 18 | system("bundle check") || system!("bundle install") 19 | 20 | # puts "\n== Copying sample files ==" 21 | # unless File.exist?("config/database.yml") 22 | # FileUtils.cp "config/database.yml.sample", "config/database.yml" 23 | # end 24 | 25 | puts "\n== Preparing database ==" 26 | system! "bin/rails db:prepare" 27 | 28 | puts "\n== Removing old logs and tempfiles ==" 29 | system! "bin/rails log:clear tmp:clear" 30 | 31 | puts "\n== Restarting application server ==" 32 | system! "bin/rails restart" 33 | end 34 | -------------------------------------------------------------------------------- /terraform/prod/google_cloudbuild_trigger.tf: -------------------------------------------------------------------------------- 1 | resource "google_cloudbuild_trigger" "company_ranking_job_deploy" { 2 | filename = "ruby/cloudbuild.yaml" 3 | include_build_logs = "INCLUDE_BUILD_LOGS_WITH_STATUS" 4 | included_files = ["ruby/**"] 5 | location = "global" 6 | name = "company-ranking-job-deploy" 7 | project = "company-ranking-prod" 8 | github { 9 | name = "company-ranking" 10 | owner = "yuki0920" 11 | push { 12 | branch = "^main$" 13 | } 14 | } 15 | } 16 | 17 | resource "google_cloudbuild_trigger" "company_ranking_server_deploy" { 18 | filename = "go/cloudbuild.yaml" 19 | include_build_logs = "INCLUDE_BUILD_LOGS_WITH_STATUS" 20 | included_files = ["go/**"] 21 | location = "global" 22 | name = "company-ranking-server-deploy" 23 | project = "company-ranking-prod" 24 | github { 25 | enterprise_config_resource_name = null 26 | name = "company-ranking" 27 | owner = "yuki0920" 28 | push { 29 | branch = "^main$" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ruby/spec/factories/document.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | FactoryBot.define do 4 | factory :document, class: 'Document' do 5 | document_id { 'S100LMHG' } 6 | edinet_code { 'E00210' } 7 | filer_name { 'NECネッツエスアイ株式会社' } 8 | period_started_at { Date.new(2021, 4, 1) } 9 | period_ended_at { Date.new(2022, 3, 31) } 10 | details_searched_at { Date.new(2022, 6, 30) } 11 | company_name { 'NECネッツエスアイ株式会社' } 12 | company_name_en { 'NEC Networks & System Integration Corporation' } 13 | head_office_location { '東京都文京区後楽二丁目6番1号' } 14 | submitted_at { Date.new(2022, 6, 24) } 15 | fiscal_year { '第89期(自 2020年 4月 1日 至 2021年 3月31日)' } 16 | representative { '代表取締役執行役員社長 牛島 祐之' } 17 | number_of_employees { 4996 } 18 | average_age_years { 44.1 } 19 | average_length_of_service_years { 16.8 } 20 | average_annual_salary { 7_749_000 } 21 | last_year_net_sales { 303_616_000_000 } 22 | net_sales { 339_109_000_000 } 23 | last_year_operating_income { nil } 24 | operating_income { nil } 25 | last_year_ordinary_income { 15_938_000_000 } 26 | ordinary_income { 25_493_000_000 } 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /typescript/client/.openapi-generator-ignore: -------------------------------------------------------------------------------- 1 | # OpenAPI Generator Ignore 2 | # Generated by openapi-generator https://github.com/openapitools/openapi-generator 3 | 4 | # Use this file to prevent files from being overwritten by the generator. 5 | # The patterns follow closely to .gitignore or .dockerignore. 6 | 7 | # As an example, the C# client generator defines ApiClient.cs. 8 | # You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: 9 | #ApiClient.cs 10 | 11 | # You can match any string of characters against a directory, file or extension with a single asterisk (*): 12 | #foo/*/qux 13 | # The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux 14 | 15 | # You can recursively match patterns against a directory, file or extension with a double asterisk (**): 16 | #foo/**/qux 17 | # This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux 18 | 19 | # You can also negate patterns with an exclamation (!). 20 | # For example, you can ignore all files in a docs folder with the file extension .md: 21 | #docs/*.md 22 | # Then explicitly reverse the ignore rule for a single file: 23 | #!docs/README.md 24 | -------------------------------------------------------------------------------- /go/Makefile: -------------------------------------------------------------------------------- 1 | export 2 | 3 | # Setup Variables for Go 4 | GOPATH := $(shell go env GOPATH) 5 | 6 | # Setup PATH 7 | PATH := $(GOBIN):$(PATH) 8 | 9 | .PHONY: install/tools 10 | install/tools: 11 | @./scripts/install_go_tools.sh 12 | 13 | .PHONY: generate/models 14 | generate/models: 15 | @xo schema ${DATABASE_URL} -o models 16 | @find models -type f -name "*.go" | xargs sed -i 's/Timestamp6WithoutTimeZone/time.Time/g' 17 | @find models -type f -name "*.go" | xargs sed -i '/import (/a \\t"time"' 18 | @find models -type f -name "*.go" | xargs goimports -w 19 | @find models -type f -name "*.go" | xargs gofmt -s -w 20 | rm -f models/arinternalmetadatum.xo.go models/schemamigration.xo.go 21 | 22 | .PHONY: generate/schema 23 | generate/schema: 24 | @xo schema ${DATABASE_URL} -t createdb -o db/sql -S schema.sql 25 | 26 | .PHONY: generate/server 27 | generate/server: 28 | @oapi-codegen -config server/config.yaml openapi.yaml 29 | 30 | .PHONY: test 31 | test: 32 | @./scripts/setup_db.sh 33 | @go test -v -race -shuffle=on ./... -coverprofile=coverage.out 34 | 35 | .PHONY: lint 36 | lint: 37 | @golangci-lint run ./... 38 | 39 | .PHONY: lint/fix 40 | lint/fix: 41 | @golangci-lint run ./... --fix 42 | -------------------------------------------------------------------------------- /typescript/components/GoogleAnalytics.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { usePathname, useSearchParams } from "next/navigation" 4 | import Script from "next/script" 5 | import { useEffect } from "react" 6 | import { existsGaId, GA_MEASUREMENT_ID, pageview } from "@/lib/gtag" 7 | 8 | export default function GoogleAnalytics() { 9 | const pathname = usePathname() 10 | const searchParams = useSearchParams() 11 | 12 | useEffect(() => { 13 | if (!existsGaId) { 14 | return 15 | } 16 | const url = pathname + searchParams.toString() 17 | pageview(url) 18 | }, [pathname, searchParams]) 19 | 20 | return ( 21 | <> 22 | 36 | 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /.github/workflows/rspec.yml: -------------------------------------------------------------------------------- 1 | name: Rspec 2 | on: [push] 3 | jobs: 4 | rspec: 5 | name: Run rspec 6 | runs-on: ubuntu-latest 7 | defaults: 8 | run: 9 | working-directory: ./ruby 10 | services: 11 | postgres: 12 | image: postgres:12.7 13 | env: 14 | POSTGRES_USER: postgres 15 | POSTGRES_PASSWORD: postgres 16 | POSTGRES_DB: test 17 | options: >- 18 | --health-cmd pg_isready 19 | --health-interval 10s 20 | --health-timeout 5s 21 | --health-retries 5 22 | ports: 23 | - 5432:5432 24 | env: 25 | RAILS_ENV: test 26 | POSTGRES_HOST: postgres 27 | RAILS_DATABASE_USER: postgres 28 | RAILS_DATABASE_PASSWORD: password 29 | DATABASE_URL: postgres://postgres:postgres@localhost:5432/test 30 | steps: 31 | - uses: actions/checkout@v4 32 | - name: Set up Ruby 33 | uses: ruby/setup-ruby@v1 34 | with: 35 | ruby-version: 3.1.4 36 | bundler-cache: true 37 | working-directory: ./ruby 38 | - name: setup DB 39 | run: | 40 | bundle exec rails db:migrate 41 | - name: Run tests 42 | run: bundle exec rspec 43 | -------------------------------------------------------------------------------- /ruby/config/storage.yml: -------------------------------------------------------------------------------- 1 | test: 2 | service: Disk 3 | root: <%= Rails.root.join("tmp/storage") %> 4 | 5 | local: 6 | service: Disk 7 | root: <%= Rails.root.join("storage") %> 8 | 9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) 10 | # amazon: 11 | # service: S3 12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> 13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> 14 | # region: us-east-1 15 | # bucket: your_own_bucket 16 | 17 | # Remember not to checkin your GCS keyfile to a repository 18 | # google: 19 | # service: GCS 20 | # project: your_project 21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> 22 | # bucket: your_own_bucket 23 | 24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) 25 | # microsoft: 26 | # service: AzureStorage 27 | # storage_account_name: your_account_name 28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> 29 | # container: your_container_name 30 | 31 | # mirror: 32 | # service: Mirror 33 | # primary: local 34 | # mirrors: [ amazon, google, microsoft ] 35 | -------------------------------------------------------------------------------- /typescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript", 3 | "version": "0.1.0", 4 | "private": true, 5 | "engines": { 6 | "node": "22.x" 7 | }, 8 | "scripts": { 9 | "dev": "next dev", 10 | "build": "next build", 11 | "start": "next start", 12 | "lint": "next lint", 13 | "format": "prettier --write --ignore-path .prettierignore './**/*.{js,jsx,ts,tsx,json}'" 14 | }, 15 | "dependencies": { 16 | "@formatjs/intl-localematcher": "^0.6.1", 17 | "@tailwindcss/postcss": "^4.1.11", 18 | "@types/node": "24.0.13", 19 | "@types/react": "19.1.8", 20 | "@types/react-dom": "19.1.6", 21 | "autoprefixer": "10.4.21", 22 | "eslint-config-next": "15.3.5", 23 | "negotiator": "^1.0.0", 24 | "next": "15.3.5", 25 | "postcss": "8.5.6", 26 | "react": "19.1.0", 27 | "react-dom": "19.1.0", 28 | "tailwindcss": "4.1.11", 29 | "theme-change": "^2.5.0", 30 | "typescript": "5.1.3" 31 | }, 32 | "devDependencies": { 33 | "@tailwindcss/typography": "^0.5.16", 34 | "@types/gtag.js": "^0.0.20", 35 | "@types/negotiator": "^0.6.4", 36 | "daisyui": "^5.0.46", 37 | "eslint": "^9.31.0", 38 | "eslint-config-prettier": "^10.1.5", 39 | "prettier": "^3.6.2" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /terraform/prod/triggers: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "createTime": "2024-02-24T23:17:24.327176308Z", 4 | "filename": "ruby/cloudbuild.yaml", 5 | "github": { 6 | "name": "company-ranking", 7 | "owner": "yuki0920", 8 | "push": { 9 | "branch": "^main$" 10 | } 11 | }, 12 | "id": "45c768f5-b53f-4ed9-bb7e-357bde1c473f", 13 | "includeBuildLogs": "INCLUDE_BUILD_LOGS_WITH_STATUS", 14 | "includedFiles": [ 15 | "ruby/**" 16 | ], 17 | "name": "company-ranking-job-deploy", 18 | "resourceName": "projects/company-ranking-prod/locations/global/triggers/45c768f5-b53f-4ed9-bb7e-357bde1c473f" 19 | }, 20 | { 21 | "createTime": "2024-02-24T15:29:06.905992361Z", 22 | "filename": "go/cloudbuild.yaml", 23 | "github": { 24 | "name": "company-ranking", 25 | "owner": "yuki0920", 26 | "push": { 27 | "branch": "^main$" 28 | } 29 | }, 30 | "id": "cb2ff461-eed1-4e1e-95d0-f37e53ff1426", 31 | "includeBuildLogs": "INCLUDE_BUILD_LOGS_WITH_STATUS", 32 | "includedFiles": [ 33 | "go/**" 34 | ], 35 | "name": "company-ranking-server-deploy", 36 | "resourceName": "projects/company-ranking-prod/locations/global/triggers/cb2ff461-eed1-4e1e-95d0-f37e53ff1426" 37 | } 38 | ] 39 | -------------------------------------------------------------------------------- /terraform/prod/google_storage_bucket.tf: -------------------------------------------------------------------------------- 1 | resource "google_storage_bucket" "company_ranking_prod_cloudbuild" { 2 | force_destroy = false 3 | location = "US" 4 | name = "company-ranking-prod_cloudbuild" 5 | project = "company-ranking-prod" 6 | public_access_prevention = "inherited" 7 | storage_class = "STANDARD" 8 | } 9 | 10 | resource "google_storage_bucket" "company_ranking_prod_terraform" { 11 | force_destroy = false 12 | lifecycle_rule { 13 | action { 14 | type = "Delete" 15 | } 16 | condition { 17 | num_newer_versions = 10 18 | with_state = "ARCHIVED" 19 | } 20 | } 21 | lifecycle_rule { 22 | action { 23 | type = "Delete" 24 | } 25 | condition { 26 | days_since_noncurrent_time = 3 27 | with_state = "ANY" 28 | } 29 | } 30 | location = "ASIA-NORTHEAST1" 31 | name = "company-ranking-prod-terraform" 32 | project = "company-ranking-prod" 33 | public_access_prevention = "enforced" 34 | storage_class = "STANDARD" 35 | uniform_bucket_level_access = true 36 | versioning { 37 | enabled = true 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /terraform/prod/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/google" { 5 | version = "6.18.1" 6 | constraints = "~> 6.18.1" 7 | hashes = [ 8 | "h1:9hflnSuKoJDbgRd3Lr7YEn2DKCcD9vyLwvDHRHEmp/o=", 9 | "zh:43543160dc2cee6f05b37eadc49e0da2ed99b1d16ca40dcb74de4ec17bf30430", 10 | "zh:44e92661b6b2e7823f931c459780eaa844c7ee8fecca676aa632ededfc0d6180", 11 | "zh:504cc9967f9e51969d012338e7b36bf689a672e0c780d821ea36bbad0d1bd4c4", 12 | "zh:6e3c24761dd073984274dcdb5e5a7f81619c2665c2aef5b35769b31cb1c72bb8", 13 | "zh:86ce6f0049a4d243574f5c3a31b6e405cc48e203f3d97722615779a5f06143e4", 14 | "zh:bf2b79a89ea02d146a3ea0c1c1232bb065ba2283c54f4a3d4ac8b04e11f2119d", 15 | "zh:ca5e3a2758c92a934e91a5d1919947300e0645e2ba71aeb9884b896e6b123d3f", 16 | "zh:d8f4f55faea7250226839a02c6134d193f3d072293452be58e8181aab925b1ad", 17 | "zh:e5189f66c2c4e1264092c79d11bc07e7dc82d99701dc0592dcd879a746ec2910", 18 | "zh:e6471441d4565910a67d79f480dafc8c1d19e29ae1588b0515269f7fb815f40f", 19 | "zh:e6514660a85b8f921b968576250ac3d983cda1c06aaef801c21e908a8b9f873b", 20 | "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /typescript/components/SortTypes.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import Link from "next/link" 4 | import { SORT_TYPES } from "@/constant" 5 | import { useCreateQueryString } from "@/hooks/CreateQueryString" 6 | import { usePathname } from "next/navigation" 7 | 8 | export default function SearchInput({ 9 | currentSortType, 10 | dict, 11 | }: { 12 | currentSortType: string 13 | dict: { 14 | net_sales: string 15 | average_annual_salary: string 16 | } 17 | }) { 18 | const createQueryString = useCreateQueryString() 19 | const pathname = usePathname() 20 | 21 | return ( 22 |
23 |
24 | {SORT_TYPES.map((sortType, index) => ( 25 |
26 | 27 | 34 | 35 |
36 | ))} 37 |
38 |
39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /.env_sample: -------------------------------------------------------------------------------- 1 | # common 2 | WORKDIR=myapp 3 | 4 | # for ruby 5 | SLACK_WEBHOOK_URL="https://hooks.slack.com/services/xxx" 6 | EDINET_API_KEY="xxx" 7 | 8 | # for go 9 | API_HOST=localhost 10 | DOCKER_API_HOST=go 11 | API_HOST_PORT=3003 12 | PORT="${API_HOST_PORT}" # $PORT is used by cloud run 13 | 14 | # for next 15 | FRONT_HOST=localhost 16 | FRONT_HOST_PORT=8888 17 | FRONT_URL="http://${FRONT_HOST}:${FRONT_HOST_PORT}" 18 | NEXT_PUBLIC_API_URL="http://${API_HOST}:${API_HOST_PORT}" 19 | NEXT_PUBLIC_DOCKER_API_URL="http://${DOCKER_API_HOST}:${API_HOST_PORT}" 20 | NEXT_PUBLIC_TWITTER_ID="example" 21 | NEXT_PUBLIC_GA_ID="" 22 | 23 | # db 24 | POSTGRES_HOST=db # for docker 25 | # POSTGRES_HOST=0.0.0.0 # for local 26 | POSTGRES_PORT=5432 27 | POSTGRES_PASSWORD=password 28 | POSTGRES_NAME=myapp_development 29 | POSTGRES_USER=postgres 30 | DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_NAME}?sslmode=disable" 31 | 32 | # test db 33 | POSTGRES_TEST_NAME=company_ranking_test 34 | POSTGRES_TEST_USER=postgres_test 35 | DATABASE_HOST_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/" 36 | DATABASE_TEST_URL="postgres://${POSTGRES_TEST_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_TEST_NAME}?sslmode=disable" 37 | -------------------------------------------------------------------------------- /typescript/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ruby/db/migrate/20210720225721_add_documents.rb: -------------------------------------------------------------------------------- 1 | class AddDocuments < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :documents do |t| 4 | t.integer :security_code, null: false 5 | t.string :document_id, null: false 6 | t.string :edinet_code, null: false 7 | t.string :filer_name, null: false 8 | t.date :period_started_at, null: false 9 | t.date :period_ended_at, null: false 10 | t.datetime :details_searched_at 11 | t.string :company_name 12 | t.string :company_name_en 13 | t.string :head_office_location 14 | t.date :submitted_at 15 | t.string :fiscal_year 16 | t.string :representative 17 | t.integer :number_of_employees 18 | t.float :average_age_years 19 | t.float :average_length_of_service_years 20 | t.bigint :average_annual_salary 21 | t.bigint :last_year_net_sales 22 | t.bigint :net_sales 23 | t.bigint :last_year_operating_income 24 | t.bigint :operating_income 25 | t.bigint :last_year_ordinary_income 26 | t.bigint :ordinary_income 27 | 28 | t.timestamps 29 | end 30 | 31 | add_foreign_key :documents, :securities, column: :security_code, primary_key: :code 32 | add_index :documents, :document_id, unique: true 33 | add_index :documents, :edinet_code, unique: true 34 | add_index :documents, :security_code 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "bundler" 4 | directory: "./ruby" 5 | schedule: 6 | interval: "monthly" 7 | ignore: 8 | - dependency-name: "rails" 9 | - dependency-name: "nokogiri" 10 | groups: 11 | ruby-dependencies: 12 | patterns: 13 | - "*" 14 | - package-ecosystem: "npm" 15 | directory: "./typescript" 16 | schedule: 17 | interval: "monthly" 18 | groups: 19 | javascript-dependencies: 20 | patterns: 21 | - "*" 22 | - package-ecosystem: "gomod" 23 | directory: "./go" 24 | schedule: 25 | interval: "monthly" 26 | groups: 27 | go-dependencies: 28 | patterns: 29 | - "*" 30 | - package-ecosystem: "gomod" 31 | directory: "./go/tools" 32 | schedule: 33 | interval: "monthly" 34 | groups: 35 | go-tools-dependencies: 36 | patterns: 37 | - "*" 38 | - package-ecosystem: "github-actions" 39 | directory: "./.github/workflows" 40 | schedule: 41 | interval: "monthly" 42 | groups: 43 | github-actions-dependencies: 44 | patterns: 45 | - "*" 46 | - package-ecosystem: terraform 47 | directory: "./terraform/prod" 48 | schedule: 49 | interval: monthly 50 | groups: 51 | terraform-dependencies: 52 | patterns: 53 | - "*" 54 | -------------------------------------------------------------------------------- /typescript/components/LocaleSwitcher.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { usePathname, useRouter } from "next/navigation" 4 | import { i18n } from "@/dictionaries/i18n-config" 5 | 6 | export default function LocaleSwitcher() { 7 | // const [currentSelected, setCurrentSelected] = useState('ja') 8 | const pathName = usePathname() 9 | const router = useRouter() 10 | const redirectedPathName = (locale: string) => { 11 | if (!pathName) return "/" 12 | const segments = pathName.split("/") 13 | segments[1] = locale 14 | return segments.join("/") 15 | } 16 | 17 | let currentSelected = "ja" 18 | if (pathName) { 19 | const tmpLocale = pathName.split("/")[1] 20 | i18n.locales.forEach((locale) => { 21 | if (tmpLocale === locale) { 22 | currentSelected = locale 23 | } 24 | }) 25 | } 26 | 27 | return ( 28 | <> 29 | 46 | 47 | ) 48 | } 49 | -------------------------------------------------------------------------------- /typescript/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | ``` 14 | 15 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 16 | 17 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 18 | 19 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 20 | 21 | ## Learn More 22 | 23 | To learn more about Next.js, take a look at the following resources: 24 | 25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 27 | 28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 29 | 30 | ## Deploy on Vercel 31 | 32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 33 | 34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 35 | -------------------------------------------------------------------------------- /go/models/market_extend.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | // MarketIDs returns a slice of all Market IDs. 8 | func MarketIDs(ctx context.Context, db DB) ([]int64, error) { 9 | // query 10 | const sqlstr = `SELECT ` + 11 | `id ` + 12 | `FROM public.markets` 13 | // run 14 | logf(sqlstr) 15 | rows, err := db.QueryContext(ctx, sqlstr) 16 | if err != nil { 17 | return nil, logerror(err) 18 | } 19 | defer rows.Close() 20 | 21 | // process 22 | var ids []int64 23 | for rows.Next() { 24 | var id int64 25 | err := rows.Scan(&id) 26 | if err != nil { 27 | return nil, logerror(err) 28 | } 29 | ids = append(ids, id) 30 | } 31 | if err := rows.Err(); err != nil { 32 | return nil, logerror(err) 33 | } 34 | return ids, nil 35 | } 36 | 37 | // MarketALL returns a slice of all Market. 38 | func MarketALL(ctx context.Context, db DB) ([]*Market, error) { 39 | // query 40 | const sqlstr = `SELECT ` + 41 | `id, name ` + 42 | `FROM public.markets` 43 | // run 44 | logf(sqlstr) 45 | rows, err := db.QueryContext(ctx, sqlstr) 46 | if err != nil { 47 | return nil, logerror(err) 48 | } 49 | defer rows.Close() 50 | // process 51 | var markets []*Market 52 | for rows.Next() { 53 | var market Market 54 | err := rows.Scan(&market.ID, &market.Name) 55 | if err != nil { 56 | return nil, logerror(err) 57 | } 58 | markets = append(markets, &market) 59 | } 60 | if err := rows.Err(); err != nil { 61 | return nil, logerror(err) 62 | } 63 | return markets, nil 64 | } 65 | -------------------------------------------------------------------------------- /ruby/lib/tasks/save_document_summary.rake: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | namespace :save_document_summary do 4 | desc '書類一覧取得APIからdocumentを取得する' 5 | 6 | # NOTE: 初期化時のみ実行される 7 | task year: :environment do 8 | (Time.zone.now.yesterday.years_ago(1).to_date..Time.zone.now.to_date).each do |date| 9 | save_summary(date) 10 | end 11 | end 12 | 13 | # NOTE: 証券リストの更新のタイミングでsecurityがなく保存できなかったdocumentを再取得する 14 | task month: :environment do 15 | (Time.zone.now.weeks_ago(5).to_date..Time.zone.now.to_date).each do |date| 16 | save_summary(date) 17 | end 18 | end 19 | 20 | # NOTE: データ取得確認用 21 | task weeks: :environment do 22 | (Time.zone.now.weeks_ago(2).to_date..Time.zone.now.to_date).each do |date| 23 | save_summary(date) 24 | end 25 | end 26 | 27 | # NOTE: 毎日の定時実行 28 | task day: :environment do 29 | today = Time.zone.now.to_date 30 | start_date = today.day == 1 ? today.months_ago(1).beginning_of_month : today.yesterday 31 | 32 | (start_date..Time.zone.now.to_date).each do |date| 33 | save_summary(date) 34 | end 35 | end 36 | 37 | # NOTE: 指定した月から実行 38 | # rake save_document_summary:before[3] 39 | task :before, ['month'] => :environment do |_, args| 40 | month = args.month.to_i 41 | 42 | (Time.zone.now.yesterday.months_ago(month).to_date..Time.zone.now.to_date).each do |date| 43 | save_summary(date) 44 | end 45 | end 46 | 47 | def save_summary(date) 48 | Rails.logger.info("#{date}分の書類取得開始") 49 | Document.save_summary(date) 50 | Rails.logger.info("#{date}分の書類取得終了") 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /.github/workflows/golangci-lint.yml: -------------------------------------------------------------------------------- 1 | name: golangci-lint 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | permissions: 8 | contents: read 9 | # Optional: allow read access to pull request. Use with `only-new-issues` option. 10 | pull-requests: read 11 | jobs: 12 | golangci: 13 | name: lint 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: actions/setup-go@v5 18 | with: 19 | go-version-file: go/go.mod 20 | cache: true 21 | - name: golangci-lint 22 | uses: golangci/golangci-lint-action@v7 23 | with: 24 | # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version 25 | version: latest 26 | 27 | # Optional: working directory, useful for monorepos 28 | working-directory: go 29 | 30 | # Optional: golangci-lint command line arguments. 31 | # args: --issues-exit-code=0 32 | 33 | # Optional: show only new issues if it's a pull request. The default value is `false`. 34 | # only-new-issues: true 35 | 36 | # Optional: if set to true then the all caching functionality will be complete disabled, 37 | # takes precedence over all other caching options. 38 | # skip-cache: true 39 | 40 | # Optional: if set to true then the action don't cache or restore ~/go/pkg. 41 | # skip-pkg-cache: true 42 | 43 | # Optional: if set to true then the action don't cache or restore ~/.cache/go-build. 44 | # skip-build-cache: true 45 | -------------------------------------------------------------------------------- /go/models/industry_extend.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | // IndustriesIDs returns a slice of all Industry IDs. 8 | func IndustryIDs(ctx context.Context, db DB) ([]int64, error) { 9 | // query 10 | const sqlstr = `SELECT ` + 11 | `id ` + 12 | `FROM public.industries` 13 | // run 14 | logf(sqlstr) 15 | rows, err := db.QueryContext(ctx, sqlstr) 16 | if err != nil { 17 | return nil, logerror(err) 18 | } 19 | defer rows.Close() 20 | 21 | // process 22 | var ids []int64 23 | for rows.Next() { 24 | var id int64 25 | err := rows.Scan(&id) 26 | if err != nil { 27 | return nil, logerror(err) 28 | } 29 | ids = append(ids, id) 30 | } 31 | if err := rows.Err(); err != nil { 32 | return nil, logerror(err) 33 | } 34 | return ids, nil 35 | } 36 | 37 | // IndustryALL returns a slice of all Industry. 38 | func IndustryALL(ctx context.Context, db DB) ([]*Industry, error) { 39 | // query 40 | const sqlstr = `SELECT ` + 41 | `id, name, code, industry_category_id ` + 42 | `FROM public.industries` 43 | // run 44 | logf(sqlstr) 45 | rows, err := db.QueryContext(ctx, sqlstr) 46 | if err != nil { 47 | return nil, logerror(err) 48 | } 49 | defer rows.Close() 50 | // process 51 | var industries []*Industry 52 | for rows.Next() { 53 | var industry Industry 54 | err := rows.Scan(&industry.ID, &industry.Name, &industry.Code, &industry.IndustryCategoryID) 55 | if err != nil { 56 | return nil, logerror(err) 57 | } 58 | industries = append(industries, &industry) 59 | } 60 | if err := rows.Err(); err != nil { 61 | return nil, logerror(err) 62 | } 63 | return industries, nil 64 | } 65 | -------------------------------------------------------------------------------- /go/cmd/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "log" 7 | "log/slog" 8 | "os" 9 | 10 | _ "github.com/lib/pq" 11 | 12 | "github.com/yuki0920/company-ranking/go/models" 13 | "github.com/yuki0920/company-ranking/go/server" 14 | ) 15 | 16 | func main() { 17 | log.Fatal(initServer()) 18 | } 19 | 20 | func initServer() error { 21 | // setup logger 22 | handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{}) 23 | logger := slog.New(handler) 24 | slog.SetDefault(logger) 25 | 26 | dbLogger := func(s string, v ...any) { 27 | logger.Info( 28 | "Query Executed", 29 | slog.String("Query", s), 30 | slog.Any("Value", v), 31 | ) 32 | } 33 | 34 | dbErrLogger := func(s string, v ...any) { 35 | msg := fmt.Sprintf(s, v) 36 | logger.Error( 37 | "Query Error", 38 | slog.String("message", msg), 39 | ) 40 | } 41 | models.SetLogger(dbLogger) 42 | models.SetErrorLogger(dbErrLogger) 43 | 44 | // setup db 45 | databaseURL := os.Getenv("DATABASE_URL") 46 | db, err := sql.Open("postgres", databaseURL) 47 | if err != nil { 48 | panic(err) 49 | } 50 | defer db.Close() 51 | logger.Info("Connected to database") 52 | 53 | // setup server 54 | s := server.NewServer(db) 55 | frontURL := os.Getenv("FRONT_URL") 56 | httpHandler := s.CreateHandler(frontURL) 57 | 58 | // NOTE: API_HOST is for local development, don't change this. 59 | apiHost := os.Getenv("API_HOST") 60 | // NOTE: Heroku provides the port to bind to $PORT, don't change this. 61 | apiPort := os.Getenv("PORT") 62 | addr := fmt.Sprintf("%s:%s", apiHost, apiPort) 63 | 64 | return server.StartServer(addr, httpHandler) 65 | } 66 | -------------------------------------------------------------------------------- /ruby/config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative "boot" 2 | 3 | require "rails" 4 | # Pick the frameworks you want: 5 | require "active_model/railtie" 6 | require "active_job/railtie" 7 | require "active_record/railtie" 8 | require "active_storage/engine" 9 | # require "action_controller/railtie" 10 | # require "action_mailer/railtie" 11 | # require "action_mailbox/engine" 12 | # require "action_text/engine" 13 | require "action_view/railtie" 14 | # require "action_cable/engine" 15 | require "rails/test_unit/railtie" 16 | 17 | # NOTE: https://github.com/googleapis/google-cloud-ruby/blob/main/google-cloud-logging/README.md 18 | # require "google/cloud/logging/rails" 19 | 20 | # Require the gems listed in Gemfile, including any gems 21 | # you've limited to :test, :development, or :production. 22 | Bundler.require(*Rails.groups) 23 | 24 | module Myapp 25 | class Application < Rails::Application 26 | # Initialize configuration defaults for originally generated Rails version. 27 | config.load_defaults 6.1 28 | 29 | # Configuration for the application, engines, and railties goes here. 30 | # 31 | # These settings can be overridden in specific environments using the files 32 | # in config/environments, which are processed later. 33 | # 34 | # config.time_zone = "Central Time (US & Canada)" 35 | # config.eager_load_paths << Rails.root.join("extras") 36 | 37 | # Only loads a smaller set of middleware suitable for API only apps. 38 | # Middleware like session, flash, cookies can be added back manually. 39 | # Skip views, helpers and assets when generating a new resource. 40 | config.api_only = true 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /.github/workflows/go_test.yml: -------------------------------------------------------------------------------- 1 | name: Go test 2 | 3 | on: [push] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | defaults: 9 | run: 10 | working-directory: ./go 11 | services: 12 | postgres: 13 | image: postgres 14 | env: 15 | POSTGRES_PASSWORD: password 16 | ports: 17 | - 5432:5432 18 | options: >- 19 | --health-cmd pg_isready 20 | --health-interval 10s 21 | --health-timeout 5s 22 | --health-retries 5 23 | steps: 24 | - uses: actions/checkout@v4 25 | - name: Set up Go 26 | uses: actions/setup-go@v5 27 | with: 28 | go-version-file: go/go.mod 29 | cache: true 30 | - name: Cache Tools 31 | id: cache-tools 32 | uses: actions/cache@v4 33 | with: 34 | path: ~/go/bin # /home/runner/go/bin 35 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 36 | - name: Install tools 37 | if: steps.cache-tools.outputs.cache-hit != 'true' 38 | run: make install/tools 39 | - name: Test 40 | run: make test 41 | env: 42 | POSTGRES_USER: postgres 43 | POSTGRES_PASSWORD: password 44 | DATABASE_HOST_URL: "postgres://postgres:password@localhost:5432/" 45 | POSTGRES_TEST_NAME: company_ranking_test 46 | POSTGRES_TEST_USER: postgres_test 47 | DATABASE_TEST_URL: "postgres://postgres_test:password@localhost:5432/company_ranking_test?sslmode=disable" 48 | - name: Coverage report 49 | uses: k1LoW/octocov-action@v1 50 | with: 51 | config: go/.octocov.yml 52 | -------------------------------------------------------------------------------- /typescript/app/[lang]/contact/page.tsx: -------------------------------------------------------------------------------- 1 | import { Metadata } from "next" 2 | import Breadcrumbs from "@/components/BreadCrumbs" 3 | 4 | export const metadata: Metadata = { 5 | title: "お問い合わせ", 6 | } 7 | 8 | import { NEXT_PUBLIC_TWITTER_ID } from "@/constant" 9 | 10 | export default function Contact() { 11 | return ( 12 |
13 | 25 |

お問い合わせ(Japanese Only)

26 |

本サービスに関するご意見・ご要望は下記のメッセージより受け付けております。

27 |
28 | 29 | 36 | 37 | {" "} 38 | Twitter 39 | 40 |
41 |
42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: go/install/tools 2 | go/install/tools: 3 | @docker compose run --rm go make install/tools 4 | 5 | .PHONY: go/generate/models 6 | go/generate/models: 7 | @docker compose run --rm go make generate/models 8 | 9 | .PHONY: go/generate/server 10 | go/generate/server: 11 | @cp openapi/openapi.yaml go/openapi.yaml 12 | @docker compose run --rm go make generate/server 13 | @rm -f go/openapi.yaml 14 | 15 | .PHONY: go/test 16 | go/test: 17 | @docker compose run --rm go make test 18 | 19 | .PHONY: go/lint 20 | go/lint: 21 | @docker compose run --rm go make lint 22 | 23 | .PHONY: go/lint/fix 24 | go/lint/fix: 25 | @docker compose run --rm go make lint/fix 26 | 27 | .PHONY: typescript/generate/client 28 | typescript/generate/client: 29 | @openapi-generator-cli generate -i openapi/openapi.yaml -g typescript-fetch -o typescript/client 30 | 31 | .PHONY: typescript/generate/client/old 32 | typescript/generate/client/old: 33 | @cp openapi/openapi.yaml typescript/openapi.yaml 34 | @cd typescript && npm run generate-client 35 | @cd .. 36 | @rm -f typescript/openapi.yaml 37 | 38 | .PHONY: typescript/lint 39 | typescript/lint: 40 | @docker compose run --rm typescript npm run lint 41 | 42 | .PHONY: typescript/lint/fix 43 | typescript/lint/fix: 44 | @docker compose run --rm typescript npm run lint --fix 45 | 46 | .PHONY: typescript/format 47 | typescript/format: 48 | @docker compose run --rm typescript npm run format 49 | 50 | .PHONY: ruby/test 51 | ruby/test: 52 | @docker compose run --rm ruby bundle exec rspec 53 | 54 | .PHONY: ruby/lint 55 | ruby/lint: 56 | @docker compose run --rm ruby bundle exec rubocop 57 | 58 | .PHONY: ruby/lint/fix 59 | ruby/lint/fix: 60 | @docker compose run --rm ruby bundle exec rubocop --fix 61 | -------------------------------------------------------------------------------- /typescript/client/models/ResponseMarketIDs.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Company Search API Document 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { mapValues } from '../runtime'; 16 | /** 17 | * 18 | * @export 19 | * @interface ResponseMarketIDs 20 | */ 21 | export interface ResponseMarketIDs { 22 | /** 23 | * 24 | * @type {Array} 25 | * @memberof ResponseMarketIDs 26 | */ 27 | marketIds: Array; 28 | } 29 | 30 | /** 31 | * Check if a given object implements the ResponseMarketIDs interface. 32 | */ 33 | export function instanceOfResponseMarketIDs(value: object): value is ResponseMarketIDs { 34 | if (!('marketIds' in value) || value['marketIds'] === undefined) return false; 35 | return true; 36 | } 37 | 38 | export function ResponseMarketIDsFromJSON(json: any): ResponseMarketIDs { 39 | return ResponseMarketIDsFromJSONTyped(json, false); 40 | } 41 | 42 | export function ResponseMarketIDsFromJSONTyped(json: any, ignoreDiscriminator: boolean): ResponseMarketIDs { 43 | if (json == null) { 44 | return json; 45 | } 46 | return { 47 | 48 | 'marketIds': json['market_ids'], 49 | }; 50 | } 51 | 52 | export function ResponseMarketIDsToJSON(value?: ResponseMarketIDs | null): any { 53 | if (value == null) { 54 | return value; 55 | } 56 | return { 57 | 58 | 'market_ids': value['marketIds'], 59 | }; 60 | } 61 | 62 | -------------------------------------------------------------------------------- /ruby/app/models/document_parser/header.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class DocumentParser::Header 4 | require 'open-uri' 5 | 6 | def initialize(src_path) 7 | @src_path = src_path 8 | @document = Document::Header.new 9 | end 10 | 11 | def parse 12 | html = URI.open(@src_path).read 13 | parsed_html = Nokogiri::HTML.parse(html) 14 | 15 | parsed_html.css('nonnumeric').each do |element| 16 | case element.attr('name') 17 | when 'jpcrp_cor:FilingDateCoverPage' 18 | @document.submitted_at = element.children.find { |child| child.text.present? }.text.strip 19 | when 'jpcrp_cor:FiscalYearCoverPage' 20 | @document.fiscal_year = element.children.find { |child| child.text.present? }.text.strip 21 | when 'jpcrp_cor:CompanyNameCoverPage' 22 | @document.company_name = element.children.find { |child| child.text.present? }.text.strip 23 | when 'jpcrp_cor:CompanyNameInEnglishCoverPage' 24 | @document.company_name_en = element.children.find { |child| child.text.present? }.text.strip 25 | when 'jpcrp_cor:TitleAndNameOfRepresentativeCoverPage' 26 | @document.representative = element.children.find { |child| child.text.present? }.text.strip 27 | when 'jpcrp_cor:AddressOfRegisteredHeadquarterCoverPage' 28 | @document.head_office_location = element.children.find { |child| child.text.present? }.text.strip 29 | end 30 | end 31 | 32 | @document 33 | end 34 | end 35 | 36 | class Document::Header 37 | include ActiveModel::Model 38 | include ActiveModel::Attributes 39 | 40 | attribute :submitted_at, :string 41 | attribute :fiscal_year, :string 42 | attribute :company_name, :string 43 | attribute :company_name_en, :string 44 | attribute :representative, :string 45 | attribute :head_office_location, :string 46 | end 47 | -------------------------------------------------------------------------------- /ruby/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 5 | 6 | ruby '3.1.4' 7 | 8 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails', branch: 'main' 9 | gem 'rails', '~> 7.0.7' 10 | # Use postgresql as the database for Active Record 11 | gem 'pg', '~> 1.5' 12 | # Use Puma as the app server 13 | # gem 'puma', '~> 5.6' 14 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder 15 | # gem 'jbuilder', '~> 2.7' 16 | # Use Redis adapter to run Action Cable in production 17 | # gem 'redis', '~> 4.0' 18 | # Use Active Model has_secure_password 19 | # gem 'bcrypt', '~> 3.1.7' 20 | 21 | # Use Active Storage variant 22 | # gem 'image_processing', '~> 1.2' 23 | 24 | # Reduces boot times through caching; required in config/boot.rb 25 | gem 'bootsnap', '>= 1.4.4', require: false 26 | 27 | # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible 28 | # gem 'rack-cors' 29 | 30 | group :development, :test do 31 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console 32 | gem 'byebug', platforms: %i[mri mingw x64_mingw] 33 | gem 'factory_bot_rails' 34 | gem 'pry-byebug' 35 | end 36 | 37 | group :development do 38 | gem 'listen', '~> 3.9' 39 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring 40 | gem 'rubocop-performance' 41 | gem 'rubocop-rails' 42 | gem 'rubocop-rspec' 43 | gem 'spring' 44 | end 45 | 46 | group :test do 47 | gem 'rspec-rails' 48 | end 49 | 50 | gem 'faraday' 51 | gem 'mechanize' 52 | gem 'nokogiri', '~> 1.13.10', '< 1.14.0' 53 | 54 | gem 'pry-rails' 55 | gem 'rubyzip' 56 | gem 'slack-notifier' 57 | gem 'spreadsheet' 58 | # gem "stackdriver" 59 | -------------------------------------------------------------------------------- /typescript/client/models/ResponseIndustryIDs.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Company Search API Document 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { mapValues } from '../runtime'; 16 | /** 17 | * 18 | * @export 19 | * @interface ResponseIndustryIDs 20 | */ 21 | export interface ResponseIndustryIDs { 22 | /** 23 | * 24 | * @type {Array} 25 | * @memberof ResponseIndustryIDs 26 | */ 27 | industryIds: Array; 28 | } 29 | 30 | /** 31 | * Check if a given object implements the ResponseIndustryIDs interface. 32 | */ 33 | export function instanceOfResponseIndustryIDs(value: object): value is ResponseIndustryIDs { 34 | if (!('industryIds' in value) || value['industryIds'] === undefined) return false; 35 | return true; 36 | } 37 | 38 | export function ResponseIndustryIDsFromJSON(json: any): ResponseIndustryIDs { 39 | return ResponseIndustryIDsFromJSONTyped(json, false); 40 | } 41 | 42 | export function ResponseIndustryIDsFromJSONTyped(json: any, ignoreDiscriminator: boolean): ResponseIndustryIDs { 43 | if (json == null) { 44 | return json; 45 | } 46 | return { 47 | 48 | 'industryIds': json['industry_ids'], 49 | }; 50 | } 51 | 52 | export function ResponseIndustryIDsToJSON(value?: ResponseIndustryIDs | null): any { 53 | if (value == null) { 54 | return value; 55 | } 56 | return { 57 | 58 | 'industry_ids': value['industryIds'], 59 | }; 60 | } 61 | 62 | -------------------------------------------------------------------------------- /typescript/client/models/Market.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Company Search API Document 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { mapValues } from '../runtime'; 16 | /** 17 | * 18 | * @export 19 | * @interface Market 20 | */ 21 | export interface Market { 22 | /** 23 | * 24 | * @type {number} 25 | * @memberof Market 26 | */ 27 | id: number; 28 | /** 29 | * 30 | * @type {string} 31 | * @memberof Market 32 | */ 33 | name: string; 34 | } 35 | 36 | /** 37 | * Check if a given object implements the Market interface. 38 | */ 39 | export function instanceOfMarket(value: object): value is Market { 40 | if (!('id' in value) || value['id'] === undefined) return false; 41 | if (!('name' in value) || value['name'] === undefined) return false; 42 | return true; 43 | } 44 | 45 | export function MarketFromJSON(json: any): Market { 46 | return MarketFromJSONTyped(json, false); 47 | } 48 | 49 | export function MarketFromJSONTyped(json: any, ignoreDiscriminator: boolean): Market { 50 | if (json == null) { 51 | return json; 52 | } 53 | return { 54 | 55 | 'id': json['id'], 56 | 'name': json['name'], 57 | }; 58 | } 59 | 60 | export function MarketToJSON(value?: Market | null): any { 61 | if (value == null) { 62 | return value; 63 | } 64 | return { 65 | 66 | 'id': value['id'], 67 | 'name': value['name'], 68 | }; 69 | } 70 | 71 | -------------------------------------------------------------------------------- /typescript/client/models/ResponseSecurityCodes.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Company Search API Document 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { mapValues } from '../runtime'; 16 | /** 17 | * 18 | * @export 19 | * @interface ResponseSecurityCodes 20 | */ 21 | export interface ResponseSecurityCodes { 22 | /** 23 | * 24 | * @type {Array} 25 | * @memberof ResponseSecurityCodes 26 | */ 27 | securityCodes: Array; 28 | } 29 | 30 | /** 31 | * Check if a given object implements the ResponseSecurityCodes interface. 32 | */ 33 | export function instanceOfResponseSecurityCodes(value: object): value is ResponseSecurityCodes { 34 | if (!('securityCodes' in value) || value['securityCodes'] === undefined) return false; 35 | return true; 36 | } 37 | 38 | export function ResponseSecurityCodesFromJSON(json: any): ResponseSecurityCodes { 39 | return ResponseSecurityCodesFromJSONTyped(json, false); 40 | } 41 | 42 | export function ResponseSecurityCodesFromJSONTyped(json: any, ignoreDiscriminator: boolean): ResponseSecurityCodes { 43 | if (json == null) { 44 | return json; 45 | } 46 | return { 47 | 48 | 'securityCodes': json['security_codes'], 49 | }; 50 | } 51 | 52 | export function ResponseSecurityCodesToJSON(value?: ResponseSecurityCodes | null): any { 53 | if (value == null) { 54 | return value; 55 | } 56 | return { 57 | 58 | 'security_codes': value['securityCodes'], 59 | }; 60 | } 61 | 62 | -------------------------------------------------------------------------------- /typescript/client/models/ResponseMarket.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Company Search API Document 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { mapValues } from '../runtime'; 16 | import type { Market } from './Market'; 17 | import { 18 | MarketFromJSON, 19 | MarketFromJSONTyped, 20 | MarketToJSON, 21 | } from './Market'; 22 | 23 | /** 24 | * 25 | * @export 26 | * @interface ResponseMarket 27 | */ 28 | export interface ResponseMarket { 29 | /** 30 | * 31 | * @type {Market} 32 | * @memberof ResponseMarket 33 | */ 34 | market: Market; 35 | } 36 | 37 | /** 38 | * Check if a given object implements the ResponseMarket interface. 39 | */ 40 | export function instanceOfResponseMarket(value: object): value is ResponseMarket { 41 | if (!('market' in value) || value['market'] === undefined) return false; 42 | return true; 43 | } 44 | 45 | export function ResponseMarketFromJSON(json: any): ResponseMarket { 46 | return ResponseMarketFromJSONTyped(json, false); 47 | } 48 | 49 | export function ResponseMarketFromJSONTyped(json: any, ignoreDiscriminator: boolean): ResponseMarket { 50 | if (json == null) { 51 | return json; 52 | } 53 | return { 54 | 55 | 'market': MarketFromJSON(json['market']), 56 | }; 57 | } 58 | 59 | export function ResponseMarketToJSON(value?: ResponseMarket | null): any { 60 | if (value == null) { 61 | return value; 62 | } 63 | return { 64 | 65 | 'market': MarketToJSON(value['market']), 66 | }; 67 | } 68 | 69 | -------------------------------------------------------------------------------- /ruby/lib/tasks/save_document_detail.rake: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | namespace :save_document_detail do 4 | desc '書類取得APIから書類ダウンロードし解析してレコードに保存' 5 | 6 | task batch: :environment do 7 | # NOTE: submitted_atの制約をつけて提出日1年以内を対象に毎日 or 毎週定期実行する 8 | Document.where(details_searched_at: nil).find_each do |document| 9 | execute(document) 10 | rescue StandardError => e 11 | Rails.logger.error("document_id: #{document.document_id}, #{e.class}: #{e.message}") 12 | end 13 | end 14 | 15 | # NOTE: rake save_document_detail:target\['S100LMNS'\] カッコをエスケープする 16 | task :target, ['document_id'] => :environment do |_, args| 17 | document_id = args.document_id 18 | 19 | document = Document.find_by(document_id: document_id) 20 | 21 | unless document 22 | Rails.logger.error("#{document_id}の書類はありません") 23 | exit 24 | end 25 | 26 | execute(document) 27 | end 28 | 29 | # NOTE: for updating net_sales 30 | task net_sales: :environment do 31 | Document.where(net_sales: nil).find_each do |document| 32 | execute(document) 33 | rescue StandardError => e 34 | Rails.logger.error("document_id: #{document.document_id}, #{e.class}: #{e.message}") 35 | end 36 | end 37 | 38 | def execute(document) 39 | meta = "[Info] code: #{document.security_code}, id: #{document.id}, document_id: #{document.document_id} filer_name: #{document.filer_name}" 40 | 41 | Rails.logger.info("#{meta} ダウンロード開始") 42 | document.download 43 | Rails.logger.info("#{meta} ダウンロード終了") 44 | 45 | Rails.logger.info("#{meta} 解凍開始") 46 | document.unzip 47 | Rails.logger.info("#{meta} 解凍終了") 48 | 49 | Rails.logger.info("#{meta} パース開始") 50 | document.save_detail! 51 | Rails.logger.info("#{meta} パース終了") 52 | 53 | Rails.logger.info("#{meta} ディレクトリ削除開始") 54 | document.remove_dir 55 | Rails.logger.info("#{meta} ディレクトリ削除終了") 56 | 57 | sleep 0.3 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /typescript/client/models/ResponseCompany.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Company Search API Document 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { mapValues } from '../runtime'; 16 | import type { Company } from './Company'; 17 | import { 18 | CompanyFromJSON, 19 | CompanyFromJSONTyped, 20 | CompanyToJSON, 21 | } from './Company'; 22 | 23 | /** 24 | * 25 | * @export 26 | * @interface ResponseCompany 27 | */ 28 | export interface ResponseCompany { 29 | /** 30 | * 31 | * @type {Company} 32 | * @memberof ResponseCompany 33 | */ 34 | company: Company; 35 | } 36 | 37 | /** 38 | * Check if a given object implements the ResponseCompany interface. 39 | */ 40 | export function instanceOfResponseCompany(value: object): value is ResponseCompany { 41 | if (!('company' in value) || value['company'] === undefined) return false; 42 | return true; 43 | } 44 | 45 | export function ResponseCompanyFromJSON(json: any): ResponseCompany { 46 | return ResponseCompanyFromJSONTyped(json, false); 47 | } 48 | 49 | export function ResponseCompanyFromJSONTyped(json: any, ignoreDiscriminator: boolean): ResponseCompany { 50 | if (json == null) { 51 | return json; 52 | } 53 | return { 54 | 55 | 'company': CompanyFromJSON(json['company']), 56 | }; 57 | } 58 | 59 | export function ResponseCompanyToJSON(value?: ResponseCompany | null): any { 60 | if (value == null) { 61 | return value; 62 | } 63 | return { 64 | 65 | 'company': CompanyToJSON(value['company']), 66 | }; 67 | } 68 | 69 | -------------------------------------------------------------------------------- /typescript/client/models/ResponseIndustry.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Company Search API Document 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { mapValues } from '../runtime'; 16 | import type { Industry } from './Industry'; 17 | import { 18 | IndustryFromJSON, 19 | IndustryFromJSONTyped, 20 | IndustryToJSON, 21 | } from './Industry'; 22 | 23 | /** 24 | * 25 | * @export 26 | * @interface ResponseIndustry 27 | */ 28 | export interface ResponseIndustry { 29 | /** 30 | * 31 | * @type {Industry} 32 | * @memberof ResponseIndustry 33 | */ 34 | industry: Industry; 35 | } 36 | 37 | /** 38 | * Check if a given object implements the ResponseIndustry interface. 39 | */ 40 | export function instanceOfResponseIndustry(value: object): value is ResponseIndustry { 41 | if (!('industry' in value) || value['industry'] === undefined) return false; 42 | return true; 43 | } 44 | 45 | export function ResponseIndustryFromJSON(json: any): ResponseIndustry { 46 | return ResponseIndustryFromJSONTyped(json, false); 47 | } 48 | 49 | export function ResponseIndustryFromJSONTyped(json: any, ignoreDiscriminator: boolean): ResponseIndustry { 50 | if (json == null) { 51 | return json; 52 | } 53 | return { 54 | 55 | 'industry': IndustryFromJSON(json['industry']), 56 | }; 57 | } 58 | 59 | export function ResponseIndustryToJSON(value?: ResponseIndustry | null): any { 60 | if (value == null) { 61 | return value; 62 | } 63 | return { 64 | 65 | 'industry': IndustryToJSON(value['industry']), 66 | }; 67 | } 68 | 69 | -------------------------------------------------------------------------------- /typescript/client/models/ResponseMarkets.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Company Search API Document 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | import { mapValues } from '../runtime'; 16 | import type { EachMarket } from './EachMarket'; 17 | import { 18 | EachMarketFromJSON, 19 | EachMarketFromJSONTyped, 20 | EachMarketToJSON, 21 | } from './EachMarket'; 22 | 23 | /** 24 | * 25 | * @export 26 | * @interface ResponseMarkets 27 | */ 28 | export interface ResponseMarkets { 29 | /** 30 | * 31 | * @type {Array} 32 | * @memberof ResponseMarkets 33 | */ 34 | markets: Array; 35 | } 36 | 37 | /** 38 | * Check if a given object implements the ResponseMarkets interface. 39 | */ 40 | export function instanceOfResponseMarkets(value: object): value is ResponseMarkets { 41 | if (!('markets' in value) || value['markets'] === undefined) return false; 42 | return true; 43 | } 44 | 45 | export function ResponseMarketsFromJSON(json: any): ResponseMarkets { 46 | return ResponseMarketsFromJSONTyped(json, false); 47 | } 48 | 49 | export function ResponseMarketsFromJSONTyped(json: any, ignoreDiscriminator: boolean): ResponseMarkets { 50 | if (json == null) { 51 | return json; 52 | } 53 | return { 54 | 55 | 'markets': ((json['markets'] as Array).map(EachMarketFromJSON)), 56 | }; 57 | } 58 | 59 | export function ResponseMarketsToJSON(value?: ResponseMarkets | null): any { 60 | if (value == null) { 61 | return value; 62 | } 63 | return { 64 | 65 | 'markets': ((value['markets'] as Array).map(EachMarketToJSON)), 66 | }; 67 | } 68 | 69 | -------------------------------------------------------------------------------- /typescript/app/[lang]/layout.tsx: -------------------------------------------------------------------------------- 1 | import "@/styles/globals.css" 2 | import Header from "@/components/Header" 3 | import Footer from "@/components/Footer" 4 | import { Metadata } from "next" 5 | import { NEXT_PUBLIC_TWITTER_ID } from "@/constant" 6 | import { getDictionary } from "@/hooks/GetDictionary" 7 | 8 | export async function generateMetadata(props: { 9 | params: Promise<{ lang: string }> 10 | }): Promise { 11 | const params = await props.params 12 | 13 | const { lang } = params 14 | 15 | const dict = await getDictionary(lang) 16 | 17 | const title = dict.metadata.title 18 | const description = dict.metadata.description 19 | const url = new URL(dict.metadata.url) 20 | 21 | return { 22 | metadataBase: url, 23 | title: { 24 | default: title, 25 | template: `%s ${title}`, 26 | }, 27 | description: description, 28 | openGraph: { 29 | title: title, 30 | description: description, 31 | url: url, 32 | locale: "ja_JP", 33 | type: "website", 34 | }, 35 | twitter: { 36 | card: "summary_large_image", 37 | title: title, 38 | description: description, 39 | creator: `@${NEXT_PUBLIC_TWITTER_ID}`, 40 | }, 41 | verification: { 42 | // google: 'search console verification code', 43 | }, 44 | alternates: { 45 | canonical: url, 46 | }, 47 | } 48 | } 49 | 50 | export default async function RootLayout(props: { 51 | children: React.ReactNode 52 | params: Promise<{ lang: string }> 53 | }) { 54 | const params = await props.params 55 | 56 | const { lang } = params 57 | 58 | const { children } = props 59 | 60 | const dictionary = await getDictionary(lang) 61 | 62 | return ( 63 | <> 64 |
65 |
66 |
{children}
67 |
68 |
69 |