diff --git a/app/models/contest.rb b/app/models/contest.rb index c5480120..2277e9fc 100644 --- a/app/models/contest.rb +++ b/app/models/contest.rb @@ -18,7 +18,7 @@ class Contest < ActiveRecord::Base scope :practicable, -> { where(:practicable => true) } scope :visible, -> { where(:visible => true) } - validates_presence_of :name, :duration, :start_time + validates_presence_of :name, :duration, :start_time, :end_time validates_numericality_of :duration validates_inclusion_of :runner_type, :in => RUNNER_TYPES diff --git a/app/models/problem.rb b/app/models/problem.rb index b29e762f..b1f7db65 100644 --- a/app/models/problem.rb +++ b/app/models/problem.rb @@ -8,6 +8,8 @@ class Problem < ActiveRecord::Base has_and_belongs_to_many :categories has_one :problem_stats, dependent: :destroy + has_many_attached :assets + validates_presence_of :name, :time_limit validates_numericality_of :time_limit, :memory_limit, :greater_than => 0 diff --git a/app/services/upload_problem_assets.rb b/app/services/upload_problem_assets.rb new file mode 100644 index 00000000..e9d01c26 --- /dev/null +++ b/app/services/upload_problem_assets.rb @@ -0,0 +1,13 @@ +class UploadProblemAssets + def initialize(problem) + @problem = problem + end + + def call + @problem.all_files.each do |file| + next if @problem.assets_blobs.any? { |blob| blob.filename == File.basename(file) } + + @problem.assets.attach(io: File.open(file), filename: File.basename(file)) + end + end +end \ No newline at end of file diff --git a/config/environments/development.rb b/config/environments/development.rb index 3c8d8312..2b893b6c 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -38,5 +38,5 @@ config.active_job.queue_adapter = :sidekiq # Active Storage config - config.active_storage.service = :local + config.active_storage.service = :minio end diff --git a/config/storage.yml b/config/storage.yml new file mode 100644 index 00000000..02996e6a --- /dev/null +++ b/config/storage.yml @@ -0,0 +1,13 @@ +local: + service: Disk + root: <%= Rails.root.join("storage") %> + +test: + service: Disk + root: <%= Rails.root.join("tmp/storage") %> + +minio: + service: S3 + bucket: "maycamp-files" + endpoint: http://minio:9000 + force_path_style: true diff --git a/db/seeds.rb b/db/seeds.rb index 4b6877d6..a632d059 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -5,12 +5,36 @@ # # cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }]) # Mayor.create(:name => 'Daley', :city => cities.first) -u = User.create( - :login => 'root', - :name => "The admin", - :email => "valentin.mihov@gmail.com", - :unencrypted_password => "123123", - :unencrypted_password_confirmation => "123123", - :role => User::ADMIN, - :city => "Sofia" -) +User + .create_with( + name: "The admin", + email: "valentin.mihov@gmail.com", + unencrypted_password: "123123", + unencrypted_password_confirmation: "123123", + role: User::ADMIN, + city: "Sofia") + .find_or_create_by!( + login: 'root' + ) + +group = Group.find_or_create_by!(name: "Група Е") + +contest = Contest.create_with( + duration: 300, + group: group, + start_time: 1.year.ago, + end_time: 1.year.ago, + runner_type: "fork" +).find_or_create_by!(name: "Пролетен турнир 2020") + +problem1 = Problem.create_with( + contest: contest, + time_limit: 1, + memory_limit: 128.megabytes +).find_or_create_by!(name: "Задача 1") + +problem2 = Problem.create_with( + contest: contest, + time_limit: 1, + memory_limit: 128.megabytes +).find_or_create_by!(name: "Задача 2") \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index 115255e9..e1a75c8d 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -17,6 +17,9 @@ services: - DATABASE_URL=mysql2://arena:arena@mysql:3306/arena?encoding=utf8&collation=utf8_general_ci - REDIS_PROVIDER=redis://redis:6379 - MEMCACHE_URL=memcached:11211 + - AWS_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE + - AWS_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY + - AWS_REGION=us-east-1 - SECRET_KEY_BASE=123 - SECRET_TOKEN=123 - SIDEKIQ_USERNAME=arena @@ -44,6 +47,9 @@ services: - RAILS_ENV=development - DATABASE_URL=mysql2://arena:arena@mysql:3306/arena?encoding=utf8&collation=utf8_general_ci - REDIS_PROVIDER=redis://redis:6379 + - AWS_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE + - AWS_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY + - AWS_REGION=us-east-1 tmpfs: - /app/tmp grader: @@ -59,6 +65,9 @@ services: - RAILS_ENV=development - DATABASE_URL=mysql2://arena:arena@mysql:3306/arena?encoding=utf8&collation=utf8_general_ci - REDIS_PROVIDER=redis://redis:6379 + - AWS_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE + - AWS_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY + - AWS_REGION=us-east-1 volumes: - ./sets:/app/sets:ro - ./sandbox:/app/sandbox @@ -67,6 +76,16 @@ services: image: redis:alpine memcached: image: memcached:alpine + minio: + image: minio/minio + environment: + - MINIO_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE + - MINIO_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY + ports: + - 9000:9000 + command: "server /data" + volumes: + - ./sets:/data mysql: image: mariadb volumes: diff --git a/spec/services/upload_problem_assets_spec.rb b/spec/services/upload_problem_assets_spec.rb new file mode 100644 index 00000000..2d71f437 --- /dev/null +++ b/spec/services/upload_problem_assets_spec.rb @@ -0,0 +1,31 @@ +describe UploadProblemAssets do + let(:problem) { create(:problem) } + let(:file_zip) { fixture_file_upload(file_fixture("archive.zip")) } + let(:file_in) { fixture_file_upload(file_fixture("task_input.in00")) } + + it "creates assets for all uploaded files" do + ProcessUploadedFile.new(file_zip, problem).extract + + expect(problem.all_files.length).to be > 0 + + UploadProblemAssets.new(problem).call + + expect(problem.assets.count).to eq(problem.all_files.count) + + expect(problem.assets_blobs.map(&:filename).sort + ["test"]).to eq(problem.all_files.map { |file| File.basename(file) }.sort) + end + + it "is idempotent" do + ProcessUploadedFile.new(file_in, problem).extract + + expect(problem.all_files.length).to eq(1) + + UploadProblemAssets.new(problem).call + + expect(problem.assets.count).to eq(1) + + UploadProblemAssets.new(problem).call + + expect(problem.assets.reload.count).to eq(1) + end +end \ No newline at end of file