diff --git a/.github/workflows/ruby-unit-tests.yml b/.github/workflows/ruby-unit-tests.yml index 0d74271e..13dd5c3f 100644 --- a/.github/workflows/ruby-unit-tests.yml +++ b/.github/workflows/ruby-unit-tests.yml @@ -7,6 +7,7 @@ on: jobs: test: strategy: + fail-fast: false matrix: backend: ['api', 'api-agraph'] # api runs tests with 4store backend and api-agraph runs with AllegroGraph backend runs-on: ubuntu-latest diff --git a/Capfile b/Capfile index f86314f3..7ecc995c 100644 --- a/Capfile +++ b/Capfile @@ -22,6 +22,6 @@ require 'capistrano/bundler' # require 'capistrano/rails/assets' # require 'capistrano/rails/migrations' require 'capistrano/locally' - +require 'new_relic/recipes' # announce deployments in NewRelic # Loads custom tasks from `lib/capistrano/tasks' if you have any defined. Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r } diff --git a/Gemfile b/Gemfile index 81c6c48d..e1050f63 100644 --- a/Gemfile +++ b/Gemfile @@ -3,7 +3,6 @@ source 'https://rubygems.org' gem 'activesupport', '~> 3.2' # see https://github.com/ncbo/ontologies_api/issues/69 gem 'bigdecimal', '1.4.2' -gem 'faraday', '~> 2.0' gem 'json-schema', '~> 2.0' gem 'multi_json', '~> 1.0' gem 'oj', '~> 3.0' @@ -31,7 +30,7 @@ gem 'redis-store', '~>1.10' # Monitoring gem 'cube-ruby', require: 'cube' -gem 'newrelic_rpm' +gem 'newrelic_rpm', group: [:default, :deployment] # HTTP server gem 'unicorn' @@ -50,6 +49,12 @@ gem 'ontologies_linked_data', github: 'ncbo/ontologies_linked_data', branch: 'de gem 'sparql-client', github: 'ncbo/sparql-client', branch: 'develop' group :development do + # bcrypt_pbkdf and ed35519 is required for capistrano deployments when using ed25519 keys; see https://github.com/miloserdow/capistrano-deploy/issues/42 + gem 'shotgun', github: 'palexander/shotgun', branch: 'ncbo' + gem 'rubocop' +end + +group :deployment do # bcrypt_pbkdf and ed35519 is required for capistrano deployments when using ed25519 keys; see https://github.com/miloserdow/capistrano-deploy/issues/42 gem 'bcrypt_pbkdf', '>= 1.0', '< 2.0', require: false gem 'capistrano', '~> 3', require: false @@ -57,8 +62,6 @@ group :development do gem 'capistrano-locally', require: false gem 'capistrano-rbenv', require: false gem 'ed25519', '>= 1.2', '< 2.0', require: false - gem 'pry' - gem 'shotgun', github: 'palexander/shotgun', branch: 'ncbo' end group :profiling do diff --git a/Gemfile.lock b/Gemfile.lock index b1ccb91b..cfcedf63 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,6 @@ GIT remote: https://github.com/ncbo/goo.git - revision: fb203b0396d03c1df61abfcdbc4070787010f052 + revision: 83425ba6c05d051d86c6f5775540727ce4238443 branch: develop specs: goo (0.0.2) @@ -15,7 +15,7 @@ GIT GIT remote: https://github.com/ncbo/ncbo_annotator.git - revision: 3ae6bfb56dc59a670b5bc1a513ff4929f8cf3756 + revision: 067104ae94c0e9d058cfbf419364fbf03f34de43 branch: develop specs: ncbo_annotator (0.0.1) @@ -26,7 +26,7 @@ GIT GIT remote: https://github.com/ncbo/ncbo_cron.git - revision: 8c8ddb6c7f9f1d2584a5fb3220f1d2aa37b66cb2 + revision: ed14911ccb28375298c63e7ca1b388ed0c638abb branch: develop specs: ncbo_cron (0.0.1) @@ -42,7 +42,7 @@ GIT GIT remote: https://github.com/ncbo/ncbo_ontology_recommender.git - revision: 818be7ee0d6b21f52a13dc1d6068505e6e17e0c5 + revision: e6d4449d8b854f17bb54af6de142bc64bff22ab3 branch: develop specs: ncbo_ontology_recommender (0.0.1) @@ -53,7 +53,7 @@ GIT GIT remote: https://github.com/ncbo/ontologies_linked_data.git - revision: 13bb138aa40ca72bfafc91a1a7416c162ba8d325 + revision: 711ebf27722355a25a0e53a8204b4ae472902bd0 branch: develop specs: ontologies_linked_data (0.0.1) @@ -103,11 +103,13 @@ GEM activesupport (3.2.22.5) i18n (~> 0.6, >= 0.6.4) multi_json (~> 1.0) - addressable (2.8.4) + addressable (2.8.5) public_suffix (>= 2.0.2, < 6.0) - airbrussh (1.4.1) + airbrussh (1.4.2) sshkit (>= 1.6.1, != 1.7.0) + ast (2.4.2) backports (3.24.1) + base64 (0.1.1) bcrypt (3.1.19) bcrypt_pbkdf (1.1.0) bigdecimal (1.4.2) @@ -141,7 +143,7 @@ GEM faraday-retry (2.2.0) faraday (~> 2.0) ffi (1.15.5) - gapic-common (0.19.1) + gapic-common (0.20.0) faraday (>= 1.9, < 3.a) faraday-retry (>= 1.0, < 3.a) google-protobuf (~> 3.14) @@ -154,7 +156,7 @@ GEM google-analytics-data (0.4.0) google-analytics-data-v1beta (>= 0.7, < 2.a) google-cloud-core (~> 1.6) - google-analytics-data-v1beta (0.8.0) + google-analytics-data-v1beta (0.8.1) gapic-common (>= 0.19.1, < 2.a) google-cloud-errors (~> 1.0) google-cloud-core (1.6.0) @@ -163,30 +165,28 @@ GEM google-cloud-env (1.6.0) faraday (>= 0.17.3, < 3.0) google-cloud-errors (1.3.1) - google-protobuf (3.23.4-aarch64-linux) - google-protobuf (3.23.4-arm64-darwin) - google-protobuf (3.23.4-x86_64-darwin) - google-protobuf (3.23.4-x86_64-linux) + google-protobuf (3.24.3-aarch64-linux) + google-protobuf (3.24.3-x86_64-darwin) + google-protobuf (3.24.3-x86_64-linux) googleapis-common-protos (1.4.0) google-protobuf (~> 3.14) googleapis-common-protos-types (~> 1.2) grpc (~> 1.27) - googleapis-common-protos-types (1.7.0) - google-protobuf (~> 3.14) - googleauth (1.7.0) + googleapis-common-protos-types (1.8.0) + google-protobuf (~> 3.18) + googleauth (1.8.0) faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) - memoist (~> 0.16) multi_json (~> 1.11) os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) - grpc (1.56.2) + grpc (1.58.0) google-protobuf (~> 3.23) googleapis-common-protos-types (~> 1.0) - grpc (1.56.2-x86_64-darwin) + grpc (1.58.0-x86_64-darwin) google-protobuf (~> 3.23) googleapis-common-protos-types (~> 1.0) - grpc (1.56.2-x86_64-linux) + grpc (1.58.0-x86_64-linux) google-protobuf (~> 3.23) googleapis-common-protos-types (~> 1.0) haml (5.2.2) @@ -203,6 +203,7 @@ GEM json_pure (2.6.3) jwt (2.7.1) kgio (2.11.4) + language_server-protocol (3.17.0.3) libxml-ruby (4.1.1) logger (1.5.3) macaddr (1.7.2) @@ -212,19 +213,18 @@ GEM net-imap net-pop net-smtp - memoist (0.16.2) method_source (1.0.0) - mime-types (3.4.1) + mime-types (3.5.1) mime-types-data (~> 3.2015) - mime-types-data (3.2023.0218.1) - mini_mime (1.1.2) + mime-types-data (3.2023.0808) + mini_mime (1.1.5) minitest (4.7.5) minitest-stub_any_instance (1.0.3) mlanett-redis-lock (0.2.7) redis multi_json (1.15.0) net-http-persistent (2.9.4) - net-imap (0.3.6) + net-imap (0.3.7) date net-protocol net-pop (0.1.2) @@ -235,20 +235,25 @@ GEM net-ssh (>= 2.6.5, < 8.0.0) net-smtp (0.3.3) net-protocol - net-ssh (7.1.0) + net-ssh (7.2.0) netrc (0.11.0) - newrelic_rpm (9.3.1) - oj (3.15.0) + newrelic_rpm (9.4.2) + oj (3.16.1) omni_logger (0.1.4) logger os (1.1.4) + parallel (1.23.0) parseconfig (1.1.2) + parser (3.2.2.3) + ast (~> 2.4.1) + racc pony (1.13.1) mail (>= 2.0) pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) public_suffix (5.0.3) + racc (1.7.1) rack (1.6.13) rack-accept (0.4.5) rack (>= 0.4) @@ -258,13 +263,14 @@ GEM rack (>= 0.4) rack-cors (1.0.6) rack (>= 1.6.0) - rack-mini-profiler (3.1.0) + rack-mini-profiler (3.1.1) rack (>= 1.2.0) rack-protection (1.5.5) rack rack-test (2.1.0) rack (>= 1.3) rack-timeout (0.6.3) + rainbow (3.1.1) raindrops (0.20.1) rake (10.5.0) rdf (1.0.8) @@ -284,16 +290,31 @@ GEM http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 4.0) netrc (~> 0.8) - rexml (3.2.5) + rexml (3.2.6) rsolr (2.5.0) builder (>= 2.1.2) faraday (>= 0.9, < 3, != 2.0.0) + rubocop (1.56.2) + base64 (~> 0.1.1) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.2.2.3) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.28.1, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.29.0) + parser (>= 3.2.1.0) + ruby-progressbar (1.13.0) ruby-xxHash (0.4.0.2) ruby2_keywords (0.0.5) rubyzip (2.3.2) rufus-scheduler (2.0.24) tzinfo (>= 0.3.22) - signet (0.17.0) + signet (0.18.0) addressable (~> 2.8) faraday (>= 0.17.5, < 3.a) jwt (>= 1.5, < 3.0) @@ -332,6 +353,7 @@ GEM unf (0.1.4) unf_ext unf_ext (0.0.8.2) + unicode-display_width (2.4.2) unicorn (6.1.0) kgio (~> 2.6) raindrops (~> 0.7) @@ -343,8 +365,6 @@ GEM PLATFORMS aarch64-linux - arm64-darwin-22 - x86_64-darwin-18 x86_64-darwin-21 x86_64-linux @@ -358,7 +378,6 @@ DEPENDENCIES capistrano-rbenv cube-ruby ed25519 (>= 1.2, < 2.0) - faraday (~> 2.0) ffi goo! haml (~> 5.2.2) @@ -373,7 +392,6 @@ DEPENDENCIES oj (~> 3.0) ontologies_linked_data! parseconfig - pry rack rack-accept (~> 0.4) rack-attack (~> 6.6.1) @@ -388,6 +406,7 @@ DEPENDENCIES redis redis-rack-cache (~> 2.0) redis-store (~> 1.10) + rubocop shotgun! simplecov simplecov-cobertura diff --git a/config/deploy.rb b/config/deploy.rb index 23a982cd..6b051216 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -108,6 +108,7 @@ after :publishing, :get_config after :get_config, :restart # after :deploy, :smoke_test + after :restart, "newrelic:notice_deployment" after :restart, :clear_cache do on roles(:web), in: :groups, limit: 3, wait: 10 do diff --git a/config/environments/test.rb b/config/environments/test.rb index 4bcdf63e..6f3f0c44 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -5,7 +5,7 @@ GOO_PATH_DATA = ENV.include?("GOO_PATH_DATA") ? ENV["GOO_PATH_DATA"] : "/data/" GOO_PATH_QUERY = ENV.include?("GOO_PATH_QUERY") ? ENV["GOO_PATH_QUERY"] : "/sparql/" GOO_PATH_UPDATE = ENV.include?("GOO_PATH_UPDATE") ? ENV["GOO_PATH_UPDATE"] : "/update/" -GOO_PORT = ENV.include?("GOO_PORT") ? ENV["GOO_PORT"] : 9000 +GOO_PORT = ENV.include?("GOO_PORT") ? ENV["GOO_PORT"] : 8080 MGREP_DICTIONARY_FILE = ENV.include?("MGREP_DICTIONARY_FILE") ? ENV["MGREP_DICTIONARY_FILE"] : "./test/data/dictionary.txt" MGREP_HOST = ENV.include?("MGREP_HOST") ? ENV["MGREP_HOST"] : "localhost" MGREP_PORT = ENV.include?("MGREP_PORT") ? ENV["MGREP_PORT"] : 55555 diff --git a/config/rack_attack.rb b/config/rack_attack.rb index 88a3e8d6..4612b493 100644 --- a/config/rack_attack.rb +++ b/config/rack_attack.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true -puts "(API) >> Throttling enabled at #{LinkedData::OntologiesAPI.settings.req_per_second_per_ip} req/sec" +limit_req_ip = LinkedData::OntologiesAPI.settings.req_per_second_per_ip +limit_req_ip_heavy = limit_req_ip / 5 +puts "(API) >> Throttling enabled at #{limit_req_ip} req/sec" require 'rack/attack' use Rack::Attack @@ -27,11 +29,12 @@ request.env['REMOTE_USER']&.admin? end -Rack::Attack.throttle('requests by ip', - limit: LinkedData::OntologiesAPI.settings.req_per_second_per_ip, - period: 1.second -) do |request| - request.ip +Rack::Attack.throttle('req/ip/heavy', limit: limit_req_ip_heavy, period: 1.second) do |req| + req.ip if req.path.include?('/recommender') || req.path.include?('/annotator') +end + +Rack::Attack.throttle('req/ip', limit: limit_req_ip, period: 1.second) do |req| + req.ip end Rack::Attack.throttled_responder = lambda do |request| diff --git a/controllers/metrics_controller.rb b/controllers/metrics_controller.rb index 4b034de4..cfc476bf 100644 --- a/controllers/metrics_controller.rb +++ b/controllers/metrics_controller.rb @@ -85,7 +85,6 @@ class MetricsController < ApplicationController get "/ontologies/:ontology/metrics" do check_last_modified_collection(LinkedData::Models::Metric) ont, sub = get_ontology_and_submission - ont = Ontology.find(params["ontology"]).first error 404, "Ontology #{params['ontology']} not found" unless ont sub.bring(ontology: [:acronym], metrics: LinkedData::Models::Metric.goo_attrs_to_load(includes_param)) reply sub.metrics || {} @@ -106,10 +105,9 @@ class MetricsController < ApplicationController # reply {} end - get "/ontologies/:ontology/submissions/:submissionId/metrics" do + get "/ontologies/:ontology/submissions/:ontology_submission_id/metrics" do check_last_modified_collection(LinkedData::Models::Metric) ont, sub = get_ontology_and_submission - ont = Ontology.find(params["ontology"]).first error 404, "Ontology #{params['ontology']} not found" unless ont sub.bring(ontology: [:acronym], metrics: LinkedData::Models::Metric.goo_attrs_to_load(includes_param)) reply sub.metrics || {} diff --git a/controllers/ontology_analytics_controller.rb b/controllers/ontology_analytics_controller.rb index 8ecd77d5..829708c1 100644 --- a/controllers/ontology_analytics_controller.rb +++ b/controllers/ontology_analytics_controller.rb @@ -28,7 +28,11 @@ class OntologyAnalyticsController < ApplicationController expires 86400, :public ont = Ontology.find(params["acronym"]).first error 404, "No ontology exists with the acronym: #{params["acronym"]}" if ont.nil? - analytics = ont.analytics + year = year_param(params) + error 400, "The year you supplied is invalid. Valid years start with 2 and contain 4 digits." if params["year"] && !year + month = month_param(params) + error 400, "The month you supplied is invalid. Valid months are 1-12." if params["month"] && !month + analytics = ont.analytics(year, month) if params["format"].to_s.downcase.eql?("csv") tf = Tempfile.new("analytics-#{params['acronym']}") @@ -39,7 +43,7 @@ class OntologyAnalyticsController < ApplicationController years.each do |year| months = analytics[params["acronym"]][year].keys.sort months.each do |month| - next if now.year == year && now.month <= month || (year == 2013 && month < 10) # we don't have good data going back past Oct 2013 + next if year && month && (now.year == year.to_i && now.month <= month.to_i || year.to_i == 2013 && month.to_i < 10) # we don't have good data going back past Oct 2013 visits = analytics[params["acronym"]][year][month] month = DateTime.parse("#{year}/#{month}").strftime("%b %Y") csv << [month, visits] diff --git a/docker-compose.yml b/docker-compose.yml index 0997779b..76c65de5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -114,7 +114,8 @@ services: retries: 5 agraph-ut: - image: franzinc/agraph:v7.3.1 + #image: franzinc/agraph:v7.3.1 + image: ontoportal/agraph:v7.3.1-patch1 platform: linux/amd64 environment: - AGRAPH_SUPER_USER=test diff --git a/helpers/application_helper.rb b/helpers/application_helper.rb index 18163099..b7d26990 100644 --- a/helpers/application_helper.rb +++ b/helpers/application_helper.rb @@ -261,9 +261,10 @@ def month_param(params=nil) if params["month"] month = params["month"].strip if %r{(?^(0[1-9]|[1-9]|1[0-2])$)}x === month - month.to_i + return month.to_i.to_s end end + nil end # validates year for starting with 1 or 2 and containing 4 digits @@ -272,9 +273,10 @@ def year_param(params=nil) if params["year"] year = params["year"].strip if %r{(?^([1-2]\d{3})$)}x === year - year.to_i + return year.to_i.to_s end end + nil end ## diff --git a/test/controllers/test_ontology_analytics_controller.rb b/test/controllers/test_ontology_analytics_controller.rb index 67ab5529..b8e36dce 100644 --- a/test/controllers/test_ontology_analytics_controller.rb +++ b/test/controllers/test_ontology_analytics_controller.rb @@ -3,195 +3,195 @@ class TestOntologyAnalyticsController < TestCase ANALYTICS_DATA = { "NCIT" => { - 2013 => { - 1 => 0, 2 => 0, 3 => 0, 4 => 0, 5 => 0, 6 => 0, 7 => 0, 8 => 0, 9 => 0, 10 => 2850, 11 => 1631, 12 => 1323 + "2013" => { + "1" => 0, "2" => 0, "3" => 0, "4" => 0, "5" => 0, "6" => 0, "7" => 0, "8" => 0, "9" => 0, "10" => 2850, "11" => 1631, "12" => 1323 }, - 2014 => { - 1 => 1004, 2 => 1302, 3 => 2183, 4 => 2191, 5 => 1005, 6 => 1046, 7 => 1261, 8 => 1329, 9 => 1100, 10 => 956, 11 => 1105, 12 => 893 + "2014" => { + "1" => 1004, "2" => 1302, "3" => 2183, "4" => 2191, "5" => 1005, "6" => 1046, "7" => 1261, "8" => 1329, "9" => 1100, "10" => 956, "11" => 1105, "12" => 893 }, - 2015 => { - 1 => 840, 2 => 30, 3 => 50, 4 => 20, 5 => 30, 6 => 10, 7 => 100, 8 => 80, 9 => 20, 10 => 90, 11 => 200, 12 => 50 + "2015" => { + "1" => 840, "2" => 30, "3" => 50, "4" => 20, "5" => 30, "6" => 10, "7" => 100, "8" => 80, "9" => 20, "10" => 90, "11" => 200, "12" => 50 }, - 2016 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 520, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 730 + "2016" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 520, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 730 }, - 2017 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 540, 5 => 220, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 170, 11 => 750, 12 => 730 + "2017" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 540, "5" => 220, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 170, "11" => 750, "12" => 730 }, - 2018 => { - 1 => 2000, 2 => 220, 3 => 640, 4 => 540, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 120, 11 => 750, 12 => 730 + "2018" => { + "1" => 2000, "2" => 220, "3" => 640, "4" => 540, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 120, "11" => 750, "12" => 730 }, - 2019 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 540, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 730 + "2019" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 540, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 730 }, - 2020 => { - 1 => 2000, 2 => 210, 3 => 640, 4 => 540, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 730 + "2020" => { + "1" => 2000, "2" => 210, "3" => 640, "4" => 540, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 730 }, - 2021 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 550, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 730 + "2021" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 550, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 730 }, - 2022 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 540, 5 => 270, 6 => 990, 7 => 340, 8 => 320, 9 => 610, 10 => 180, 11 => 750, 12 => 730 + "2022" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 540, "5" => 270, "6" => 990, "7" => 340, "8" => 320, "9" => 610, "10" => 180, "11" => 750, "12" => 730 } }, "ONTOMA" => { - 2013 => { - 1 => 0, 2 => 0, 3 => 0, 4 => 0, 5 => 0, 6 => 0, 7 => 0, 8 => 0, 9 => 0, 10 => 6, 11 => 15, 12 => 0 + "2013" => { + "1" => 0, "2" => 0, "3" => 0, "4" => 0, "5" => 0, "6" => 0, "7" => 0, "8" => 0, "9" => 0, "10" => 6, "11" => 15, "12" => 0 }, - 2014 => { - 1 => 2, 2 => 0, 3 => 0, 4 => 2, 5 => 2, 6 => 0, 7 => 6, 8 => 8, 9 => 0, 10 => 0, 11 => 0, 12 => 2 + "2014" => { + "1" => 2, "2" => 0, "3" => 0, "4" => 2, "5" => 2, "6" => 0, "7" => 6, "8" => 8, "9" => 0, "10" => 0, "11" => 0, "12" => 2 }, - 2015 => { - 1 => 30, 2 => 90, 3 => 90, 4 => 50, 5 => 30, 6 => 20, 7 => 80, 8 => 90, 9 => 250, 10 => 230, 11 => 120, 12 => 70 + "2015" => { + "1" => 30, "2" => 90, "3" => 90, "4" => 50, "5" => 30, "6" => 20, "7" => 80, "8" => 90, "9" => 250, "10" => 230, "11" => 120, "12" => 70 }, - 2016 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 520, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 730 + "2016" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 520, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 730 }, - 2017 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 540, 5 => 220, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 170, 11 => 750, 12 => 730 + "2017" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 540, "5" => 220, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 170, "11" => 750, "12" => 730 }, - 2018 => { - 1 => 2000, 2 => 220, 3 => 640, 4 => 540, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 120, 11 => 750, 12 => 730 + "2018" => { + "1" => 2000, "2" => 220, "3" => 640, "4" => 540, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 120, "11" => 750, "12" => 730 }, - 2019 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 540, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 730 + "2019" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 540, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 730 }, - 2020 => { - 1 => 2000, 2 => 210, 3 => 640, 4 => 540, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 730 + "2020" => { + "1" => 2000, "2" => 210, "3" => 640, "4" => 540, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 730 }, - 2021 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 550, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 730 + "2021" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 550, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 730 }, - 2022 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 540, 5 => 270, 6 => 990, 7 => 340, 8 => 320, 9 => 610, 10 => 180, 11 => 750, 12 => 730 + "2022" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 540, "5" => 270, "6" => 990, "7" => 340, "8" => 320, "9" => 610, "10" => 180, "11" => 750, "12" => 730 } }, "CMPO" => { - 2013 => { - 1 => 0, 2 => 0, 3 => 0, 4 => 0, 5 => 0, 6 => 0, 7 => 0, 8 => 0, 9 => 0, 10 => 64, 11 => 75, 12 => 22 + "2013" => { + "1" => 0, "2" => 0, "3" => 0, "4" => 0, "5" => 0, "6" => 0, "7" => 0, "8" => 0, "9" => 0, "10" => 64, "11" => 75, "12" => 22 }, - 2014 => { - 1 => 15, 2 => 15, 3 => 19, 4 => 12, 5 => 13, 6 => 14, 7 => 22, 8 => 12, 9 => 36, 10 => 6, 11 => 8, 12 => 10 + "2014" => { + "1" => 15, "2" => 15, "3" => 19, "4" => 12, "5" => 13, "6" => 14, "7" => 22, "8" => 12, "9" => 36, "10" => 6, "11" => 8, "12" => 10 }, - 2015 => { - 1 => 7, 2 => 40, 3 => 140, 4 => 320, 5 => 560, 6 => 320, 7 => 210, 8 => 230, 9 => 220, 10 => 10, 11 => 220, 12 => 880 + "2015" => { + "1" => 7, "2" => 40, "3" => 140, "4" => 320, "5" => 560, "6" => 320, "7" => 210, "8" => 230, "9" => 220, "10" => 10, "11" => 220, "12" => 880 }, - 2016 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 520, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 730 + "2016" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 520, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 730 }, - 2017 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 540, 5 => 220, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 170, 11 => 750, 12 => 30 + "2017" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 540, "5" => 220, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 170, "11" => 750, "12" => 30 }, - 2018 => { - 1 => 2000, 2 => 220, 3 => 640, 4 => 540, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 120, 11 => 750, 12 => 430 + "2018" => { + "1" => 2000, "2" => 220, "3" => 640, "4" => 540, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 120, "11" => 750, "12" => 430 }, - 2019 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 540, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 830 + "2019" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 540, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 830 }, - 2020 => { - 1 => 2000, 2 => 210, 3 => 640, 4 => 540, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 380 + "2020" => { + "1" => 2000, "2" => 210, "3" => 640, "4" => 540, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 380 }, - 2021 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 550, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 794 + "2021" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 550, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 794 }, - 2022 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 540, 5 => 270, 6 => 990, 7 => 340, 8 => 320, 9 => 610, 10 => 180, 11 => 750, 12 => 738 + "2022" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 540, "5" => 270, "6" => 990, "7" => 340, "8" => 320, "9" => 610, "10" => 180, "11" => 750, "12" => 738 } }, "AEO" => { - 2013 => { - 1 => 0, 2 => 0, 3 => 0, 4 => 0, 5 => 0, 6 => 0, 7 => 0, 8 => 0, 9 => 0, 10 => 129, 11 => 142, 12 => 70 + "2013" => { + "1" => 0, "2" => 0, "3" => 0, "4" => 0, "5" => 0, "6" => 0, "7" => 0, "8" => 0, "9" => 0, "10" => 129, "11" => 142, "12" => 70 }, - 2014 => { - 1 => 116, 2 => 93, 3 => 85, 4 => 132, 5 => 96, 6 => 137, 7 => 69, 8 => 158, 9 => 123, 10 => 221, 11 => 163, 12 => 43 + "2014" => { + "1" => 116, "2" => 93, "3" => 85, "4" => 132, "5" => 96, "6" => 137, "7" => 69, "8" => 158, "9" => 123, "10" => 221, "11" => 163, "12" => 43 }, - 2015 => { - 1 => 25, 2 => 230, 3 => 330, 4 => 220, 5 => 650, 6 => 320, 7 => 840, 8 => 440, 9 => 220, 10 => 110, 11 => 210, 12 => 270 + "2015" => { + "1" => 25, "2" => 230, "3" => 330, "4" => 220, "5" => 650, "6" => 320, "7" => 840, "8" => 440, "9" => 220, "10" => 110, "11" => 210, "12" => 270 }, - 2016 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 520, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 335 + "2016" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 520, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 335 }, - 2017 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 540, 5 => 220, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 170, 11 => 750, 12 => 732 + "2017" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 540, "5" => 220, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 170, "11" => 750, "12" => 732 }, - 2018 => { - 1 => 2000, 2 => 220, 3 => 640, 4 => 540, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 120, 11 => 750, 12 => 734 + "2018" => { + "1" => 2000, "2" => 220, "3" => 640, "4" => 540, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 120, "11" => 750, "12" => 734 }, - 2019 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 540, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 790 + "2019" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 540, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 790 }, - 2020 => { - 1 => 2000, 2 => 210, 3 => 640, 4 => 540, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 350 + "2020" => { + "1" => 2000, "2" => 210, "3" => 640, "4" => 540, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 350 }, - 2021 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 550, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 730 + "2021" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 550, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 730 }, - 2022 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 540, 5 => 270, 6 => 990, 7 => 340, 8 => 320, 9 => 610, 10 => 180, 11 => 750, 12 => 730 + "2022" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 540, "5" => 270, "6" => 990, "7" => 340, "8" => 320, "9" => 610, "10" => 180, "11" => 750, "12" => 730 } }, "SNOMEDCT" => { - 2013 => { - 1 => 0, 2 => 0, 3 => 0, 4 => 0, 5 => 0, 6 => 0, 7 => 0, 8 => 0, 9 => 0, 10 => 20721, 11 => 22717, 12 => 18565 + "2013" => { + "1" => 0, "2" => 0, "3" => 0, "4" => 0, "5" => 0, "6" => 0, "7" => 0, "8" => 0, "9" => 0, "10" => 20721, "11" => 22717, "12" => 18565 }, - 2014 => { - 1 => 17966, 2 => 17212, 3 => 20942, 4 => 20376, 5 => 21063, 6 => 18734, 7 => 18116, 8 => 18676, 9 => 15728, 10 => 16348, 11 => 13933, 12 => 9533 + "2014" => { + "1" => 17966, "2" => 17212, "3" => 20942, "4" => 20376, "5" => 21063, "6" => 18734, "7" => 18116, "8" => 18676, "9" => 15728, "10" => 16348, "11" => 13933, "12" => 9533 }, - 2015 => { - 1 => 9036, 2 => 430, 3 => 550, 4 => 110, 5 => 990, 6 => 320, 7 => 630, 8 => 250, 9 => 270, 10 => 880, 11 => 330, 12 => 280 + "2015" => { + "1" => 9036, "2" => 430, "3" => 550, "4" => 110, "5" => 990, "6" => 320, "7" => 630, "8" => 250, "9" => 270, "10" => 880, "11" => 330, "12" => 280 }, - 2016 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 520, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 230 + "2016" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 520, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 230 }, - 2017 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 540, 5 => 220, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 170, 11 => 750, 12 => 130 + "2017" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 540, "5" => 220, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 170, "11" => 750, "12" => 130 }, - 2018 => { - 1 => 2000, 2 => 220, 3 => 640, 4 => 540, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 120, 11 => 750, 12 => 330 + "2018" => { + "1" => 2000, "2" => 220, "3" => 640, "4" => 540, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 120, "11" => 750, "12" => 330 }, - 2019 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 540, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 920 + "2019" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 540, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 920 }, - 2020 => { - 1 => 2000, 2 => 210, 3 => 640, 4 => 540, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 130 + "2020" => { + "1" => 2000, "2" => 210, "3" => 640, "4" => 540, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 130 }, - 2021 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 550, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 930 + "2021" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 550, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 930 }, - 2022 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 540, 5 => 270, 6 => 990, 7 => 340, 8 => 320, 9 => 610, 10 => 180, 11 => 750, 12 => 230 + "2022" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 540, "5" => 270, "6" => 990, "7" => 340, "8" => 320, "9" => 610, "10" => 180, "11" => 750, "12" => 230 } }, "TST" => { - 2013 => { - 1 => 0, 2 => 0, 3 => 23, 4 => 0, 5 => 0, 6 => 0, 7 => 20, 8 => 0, 9 => 0, 10 => 234, 11 => 7654, 12 => 2311 + "2013" => { + "1" => 0, "2" => 0, "3" => 23, "4" => 0, "5" => 0, "6" => 0, "7" => 20, "8" => 0, "9" => 0, "10" => 234, "11" => 7654, "12" => 2311 }, - 2014 => { - 1 => 39383, 2 => 239, 3 => 40273, 4 => 3232, 5 => 2, 6 => 58734, 7 => 11236, 8 => 23, 9 => 867, 10 => 232, 11 => 1111, 12 => 8 + "2014" => { + "1" => 39383, "2" => 239, "3" => 40273, "4" => 3232, "5" => 2, "6" => 58734, "7" => 11236, "8" => 23, "9" => 867, "10" => 232, "11" => 1111, "12" => 8 }, - 2015 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 540, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 710 + "2015" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 540, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 710 }, - 2016 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 520, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 720 + "2016" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 520, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 720 }, - 2017 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 540, 5 => 220, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 170, 11 => 750, 12 => 73 + "2017" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 540, "5" => 220, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 170, "11" => 750, "12" => 73 }, - 2018 => { - 1 => 2000, 2 => 220, 3 => 640, 4 => 540, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 120, 11 => 750, 12 => 60 + "2018" => { + "1" => 2000, "2" => 220, "3" => 640, "4" => 540, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 120, "11" => 750, "12" => 60 }, - 2019 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 540, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 70 + "2019" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 540, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 70 }, - 2020 => { - 1 => 2000, 2 => 210, 3 => 640, 4 => 540, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 790 + "2020" => { + "1" => 2000, "2" => 210, "3" => 640, "4" => 540, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 790 }, - 2021 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 550, 5 => 270, 6 => 980, 7 => 340, 8 => 320, 9 => 630, 10 => 110, 11 => 750, 12 => 70 + "2021" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 550, "5" => 270, "6" => 980, "7" => 340, "8" => 320, "9" => 630, "10" => 110, "11" => 750, "12" => 70 }, - 2022 => { - 1 => 2000, 2 => 230, 3 => 640, 4 => 540, 5 => 270, 6 => 990, 7 => 340, 8 => 320, 9 => 610, 10 => 180, 11 => 750, 12 => 30 + "2022" => { + "1" => 2000, "2" => 230, "3" => 640, "4" => 540, "5" => 270, "6" => 990, "7" => 340, "8" => 320, "9" => 610, "10" => 180, "11" => 750, "12" => 30 } } }