class PredictionsView <<Boundary>>
class "Prediction::\nRegisterView" <<Boundary>>

class PredictionChannel {
  + subscribed() : void
}

class "Prediction::\nRegisterForm" {
  - model : Object
  - input_type : String
  - race : Prediction::RegisterForm::RaceForm
  - entries : Prediction::RegisterForm::EntryForm
  - url : String
  + to_model() : Prediction
  + input_directly?() : boolean
  + attributes() : HashMap
  - valid_model?() : void
  - valid_input_type?() : void
  - valid_race?() : void
  - valid_entries?() : void
  - valid_url?() : void
  - test_data() : String
}

class "Prediction::\nRegisterForm::\nRaceForm" {
  - direction : String
  - distance : String
  - grade : String
  - place : String
  - round : String
  - start_date : String
  - track : String
  - weather : String
  + attributes() : HashMap
  - valid_direction?() : void
  - valid_distance?() : void
  - valid_grade?() : void
  - valid_place?() : void
  - valid_round?() : void
  - valid_start_date?() : void
  - valid_track?() : void
  - valid_weather?() : void
}

class "Prediction::\nRegisterForm::\nEntryForm" {
  - entries : ArrayList
  + attributes() : HashMap
  - valid_entries?() : void
  - valid_entry?() : void
  - validate_entry() : void
  - validate_age() : void
  - validate_burden_weight() : void
  - validate_horse_id() : void
  - validate_horse_name() : void
  - validate_jockey_id() : void
  - validate_jockey_name() : void
  - validate_sex() : void
  - validate_weight() : void
}

class ApplicationController {
  + check_schema(schema : HashMap, request_parameter : HashMap, resource : String) : void
  + remove_old_files() : void
}

class PredictionsController {
  + index(request : Object) : Object
  + new(request : Object) : Object
  + create(request : Object) : Object
  - register_form() : Prediction::RegisterForm
  - create_params() : HashMap
  - save_model(prediction : Prediction) : void
  - output_form_values(prediction : Prediction) : void
}

class ApplicationJob {
  + execute_script(filename : String, args: ArrayList) : void
}

class PredictionJob {
  + perform(prediction : Prediction)
}

class Prediction {
  - prediction_id : String
  - model : String
  - test_data : String
  - state : String
  - performed_at : Datetime
  + set_analysis!() : void
  + import_results(result_file : String) : void
  + start!() : void
  + completed!() : void
  + failed!() : void
  + tmp_dir() : String
  - broadcast(attribute : HashMap) : void
}

class PredictionResult {
  - number : Integer
  - won : Boolean
}

class "Denebola::\nBase" {
  + {static} generate_feature(race : Denebola::Race, entries : ArrayList) : HashMap
  - {static} generate_horse_feature(horse : Denebola::Horse, results : ArrayList, race : Denebola::Race, entry : Denebola::Race::Entry) : HashMap
  - {static} generate_jockey_feature(results : ArrayList) : HashMap
  - {static} entry_weight_diff(entry : Denebola::Race::Entry, results : ArrayList) : Integer
  - {static} horse_average_prize_money(results : ArrayList) : Float
  - {static} horse_blank(race : Denebola::Race, results : ArrayList) : Integer
  - {static} horse_distance_diff(race : Denebola::Race, results : ArrayList) : Float
  - {static} horse_last_race_order(results : ArrayList) : Integer
  - {static} horse_rate_within_third(results : ArrayList) : Float
  - {static} horse_second_last_race_order(results : ArrayList) : Float
  + slice(attribute_names : ArrayList) : HashMap
}

class "Denebola::\nRace" {
  - direction : String
  - distance : String
  - grade : String
  - place : String
  - race_id : String
  - race_name : String
  - round : String
  - start_time : String
  - track : String
  - weather : String
  - payoff : HashMap
  + to_feature() : HashMap
  - month() : Integer
}

class "Denebola::\nRace::\nEntry" {
  - age : String
  - burden_weight : String
  - final_600m_time : Float
  - number : Integer
  - order : String
  - prize_money : Integer
  - sex : String
  - weight : String
  - weight_diff : Integer
  + to_feature() : HashMap
  - weight_per() : Float
}

class "Denebola::\nHorse" {
  - horse_id : String
  - horse_name : String
  - running_style : String
  + to_feature() : HashMap
}

class "Denebola::\nHorse::\nResult" {
  - race_distance : Integer
  - race_start_time : String
  - entry_order : String
  - entry_prize_money : Integer
  - entry_weight : Integer
}

class "Denebola::\nJockey::\nResult" {
  - race_start_time : String
  - entry_order : String
  - entry_prize_money : Integer
}

class "Denebola::\nFeature" {
  - entry_age : Integer
  - entry_burden_weight : Integer
  - entry_number : Integer
  - entry_sex : String
  - entry_weight : Integer
  - entry_weight_diff : Integer
  - entry_weight_per : Float
  - horse_average_prize_money : Float
  - horse_blank : Integer
  - horse_distance_diff : Float
  - horse_entry_times : Integer
  - horse_id : String
  - horse_last_race_order : Integer
  - horse_rate_within_third : Float
  - horse_running_style : String
  - horse_second_last_race_order : Integer
  - horse_win_times : Integer
  - jockey_average_prize_money : Float
  - jockey_win_rate : Float
  - jockey_win_rate_last_four_races : Float
  - race_direction : String
  - race_distace : Integer
  - race_grade : String
  - race_id : String
  - race_month : Integer
  - race_place : String
  - race_round : Integer
  - race_track : String
  - race_weather : String
  - won : Boolean
  + attributes() : HashMap
}

class "Netkeiba::\nBase"

class "Netkeiba::\nRace" {
  - direction : String
  - distance : Integer
  - grade : String
  - month : Integer
  - place : String
  - race_id : String
  - race_name : String
  - round : Integer
  - track : String
  - weather : String
  + to_feature() : HashMap
}

class "Netkeiba::\nRace::\nEntry" {
  - age : Integer
  - burden_weight : Float
  - number : Integer
  - order : Integer
  - sex : String
  - weight : Float
  - weight_diff : Float
  - weight_per : Float
  - horse_link : String
  - jockey_link : String
  + weight_per() : Float
  + to_feature() : HashMap
}

class DenebolaClient {
  + http_get_race(race_id : String) : DenebolaClient::Response::Race
  + http_get_random_races(query : HashMap) : DenebolaClient::Response::RandomRaces
  + http_get_race_entries(race_id : String) : DenebolaClient::Response::Race::Entries
  + http_get_horses(query : HashMap) : DenebolaClient::Response::Horses
  + http_get_horse_results(horse_id : String) : DenebolaClient::Response::Horse::Results
  + http_get_jockey_results(jockey_id : String) : DenebolaClient::Response::Jockey::Results
  + http_get_features(race_id : String) : DenebolaClient::Response::Features
  - send_request(url : String, query : HashMap) : void
  - authorization_header() : HashMap
  - parsed_json() : HashMap
  - handle_common_error() : void
}

class "DenebolaClient::\nResponse::\nRace"

class "DenebolaClient::\nResponse::\nRandomRaces" {
  - index_id : String
  - races : ArrayList
}

class "DenebolaClient::\nResponse::\nRace::\nEntries" {
  - entries : ArrayList
}

class "DenebolaClient::\nResponse::\nHorses" {
  - horses : ArrayList
}

class "DenebolaClient::\nResponse::\nHorse::\nResults" {
  - results : ArrayList
  + results_before(date : String) : ArrayList
}

class "DenebolaClient::\nResponse::\nJockey::\nResults" {
  - results : ArrayList
  + results_before(date : String) : ArrayList
}

class "DenebolaClient::\nResponse::\nFeatures" {
  - features : ArrayList
}

class NetkeibaClient {
  + http_get_race_top() : ArrayList
  + http_get_race(path : String) : NetkeibaClient::Response::Race
  - send_request(path : String) : void
  - parsed_html() : Object
}

class "NetkeibaClient::\nResponse::\nRace" {
  - race : Netkeiba::Race
  - entries : ArrayList
  - extract_race(place : Object, race_data : Object, race_date : Object) : HashMap
  - extract_entries(race_data : Object) : ArrayList
}

class PredictionUtil {
  + {static} generate_features(test_data : String) : HashMap
  - {static} fetch_race(test_data : String) : HashMap
  - {static} generate_feature(entry : HashMap) : HashMap
}

class ModelUtil <<module>> {
  + unzip_model(zip_path : String, output_dir : String) : void
  + read_analysis_id(metadata_file : String) : String
}

PredictionsView --> PredictionsController
"Prediction::\nRegisterView" --> PredictionsController

PredictionChannel --> PredictionsView

ApplicationController <|-- PredictionsController
PredictionsController --> "Prediction::\nRegisterForm"
PredictionsController --> PredictionJob
PredictionsController --> Prediction

"Prediction::\nRegisterForm" "1" -- "1" "Prediction::\nRegisterForm::\nRaceForm"
"Prediction::\nRegisterForm" "1" -- "1" "Prediction::\nRegisterForm::\nEntryForm"
"Prediction::\nRegisterForm" --> Prediction

ApplicationJob <|-- PredictionJob
PredictionJob --> Prediction
PredictionJob --> PredictionUtil
PredictionJob ..> ModelUtil : <<include>>

Prediction --> PredictionChannel
Prediction "1" -- "*" PredictionResult
Prediction ..> ModelUtil : <<include>>

PredictionUtil --> DenebolaClient
PredictionUtil --> "Denebola::\nBase"
PredictionUtil --> NetkeibaClient
PredictionUtil --> "Netkeiba::\nBase"

"Denebola::\nBase" <|-- "Denebola::\nRace"
"Denebola::\nBase" <|-- "Denebola::\nRace::\nEntry"
"Denebola::\nBase" <|-- "Denebola::\nHorse"
"Denebola::\nBase" <|-- "Denebola::\nHorse::\nResult"
"Denebola::\nBase" <|-- "Denebola::\nJockey::\nResult"
"Denebola::\nBase" <|-- "Denebola::\nFeature"

"Netkeiba::\nBase" <|-- "Netkeiba::\nRace"
"Netkeiba::\nBase" <|-- "Netkeiba::\nRace::\nEntry"

DenebolaClient -- "DenebolaClient::\nResponse::\nRace"
DenebolaClient -- "DenebolaClient::\nResponse::\nRace::\nEntries"
DenebolaClient -- "DenebolaClient::\nResponse::\nRandomRaces"
DenebolaClient -- "DenebolaClient::\nResponse::\nHorses"
DenebolaClient -- "DenebolaClient::\nResponse::\nHorse::\nResults"
DenebolaClient -- "DenebolaClient::\nResponse::\nJockey::\nResults"
DenebolaClient -- "DenebolaClient::\nResponse::\nFeatures"

"Denebola::\nRace" <|-- "DenebolaClient::\nResponse::\nRace"
"DenebolaClient::\nResponse::\nRace::\nEntries" "1" -- "1 .. *" "Denebola::\nRace::\nEntry"
"DenebolaClient::\nResponse::\nRandomRaces" "1" -- "1 .. *" "Denebola::\nRace"
"DenebolaClient::\nResponse::\nHorses" "1" -- "0 .. *" "Denebola::\nHorse"
"DenebolaClient::\nResponse::\nHorse::\nResults" "1" -- "0 .. *" "Denebola::\nHorse::\nResult"
"DenebolaClient::\nResponse::\nJockey::\nResults" "1" -- "0 .. *" "Denebola::\nJockey::\nResult"
"DenebolaClient::\nResponse::\nFeatures" "1" -- "1 .. *" "Denebola::\nFeature"

NetkeibaClient -- "NetkeibaClient::\nResponse::\nRace"

"NetkeibaClient::\nResponse::\nRace" "1" -- "1" "Netkeiba::\nRace"
