diff --git a/app/controllers/appearance_release_imports_controller.rb b/app/controllers/appearance_release_imports_controller.rb
index 40a0857..3dee19b 100644
--- a/app/controllers/appearance_release_imports_controller.rb
+++ b/app/controllers/appearance_release_imports_controller.rb
@@ -3,7 +3,6 @@
class AppearanceReleaseImportsController < ApplicationController
include AppearanceReleaseContext
include ProjectContext
- include CreateReleasableJobs
before_action :set_project, only: [:create]
@@ -11,24 +10,16 @@ class AppearanceReleaseImportsController < ApplicationController
def create
authorize AppearanceRelease
- @failed_files = []
attachments = appearance_release_params
if attachments.nil?
alert_message = t 'appearance_releases.create.no_attachments'
+ redirect_to [@project, :appearance_releases], alert: alert_message
else
- attachments.each do |attachment|
- create_imported_appearance_release attachment
- end
+ filtered_attachments = filter_attachments attachments
+ MatchAppearanceReleasesJob.perform_later(@project, filtered_attachments)
+ notice_message = t 'appearance_releases.create.matching_started'
+ redirect_to [@project, :appearance_releases], notice: notice_message
end
-
- unless @failed_files.empty?
- alert_message = t 'appearance_releases.create.failed_import'
- alert_message += '
'
- @failed_files.each { |file_name| alert_message += "- #{file_name}
" }
- alert_message += '
'
- end
-
- redirect_to [@project, :appearance_releases], alert: alert_message
end
private
@@ -45,45 +36,18 @@ class AppearanceReleaseImportsController < ApplicationController
params.require(:attachments)
end
- def build_appearance_release(params = {})
- authorize appearance_releases.build(params)
+ def filter_attachments(attachments)
+ filtered_attachments = []
+ attachments.each do |attachment|
+ blob = ActiveStorage::Blob.find_signed attachment
+ next if blob.nil?
+
+ extension = blob.filename.extension
+ filtered_attachments << attachment if blob.image? || extension == 'pdf'
+ end
end
- def log_create_analytics
- TrackAnalyticsJob.perform_later(Current.user, Current.account, :track_create_non_native_release, release_type: AppearanceRelease.to_s, user_agent: request.user_agent, user_ip: request.remote_ip)
- end
-
- def create_imported_appearance_release(attachment)
- blob = ActiveStorage::Blob.find_signed(attachment)
- return if blob.nil?
-
- extension = blob.filename.extension_with_delimiter
- unless AppearanceRelease.acceptable_import_file_extensions.include? extension
- blob.purge
- @failed_files << blob.filename
- return
- end
-
- random_contract_no = AppearanceRelease.random_contract_number.to_s
- appearance_release_params = {
- person_last_name: random_contract_no
- }
-
- if blob.image?
- appearance_release_params[:person_photo] = attachment
- appearance_release_params[:person_first_name] = I18n.t('appearance_releases.shared.imported_appearance_release_headshot_name')
- elsif extension == '.pdf'
- appearance_release_params[:contract] = attachment
- appearance_release_params[:person_first_name] = I18n.t('appearance_releases.shared.imported_appearance_release_contract_name')
- end
-
- appearance_release = build_appearance_release(appearance_release_params)
-
- if appearance_release.save(context: :non_native)
- log_create_analytics
- after_create appearance_release
- else
- @failed_files << blob.filename
- end
+ def acceptable_extensions
+ AppearanceRelease.acceptable_import_file_extensions
end
end
diff --git a/app/jobs/match_appearance_releases_job.rb b/app/jobs/match_appearance_releases_job.rb
new file mode 100644
index 0000000..b3fdd71
--- /dev/null
+++ b/app/jobs/match_appearance_releases_job.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+class MatchAppearanceReleasesJob < ApplicationJob
+ queue_as :default
+
+ def perform(project, attachments)
+ matching_request = MatchingRequest.create project: project, attachments: attachments
+
+ payload = { request_id: matching_request.id, bucket: aws_bucket_name, files: attachments}
+ response = BrayniacAI::AppearanceReleaseMatching.match_attachments payload
+
+ matches = response[:matches] || []
+ errors = response[:errors] || []
+
+ handle_matches matches, project
+ handle_errors errors
+ end
+
+ private
+
+ def handle_matches(matches, project)
+ matches.each do |match|
+ contract = match[:contract]
+ headshot = match[:headshot]
+ identifier = match[:identifier]
+
+ identified_release = identifier.blank? ? nil : AppearanceRelease.find_by(identifier: identifier)
+ if identified_release.nil?
+ create_release project, contract, headshot, identifier
+ else
+ update_release identified_release, contract, headshot
+ end
+ end
+ end
+
+ def handle_errors(errors)
+ logger.error "== MATCHING ERRORS ==" unless errors.empty?
+ errors.each do |error|
+ logger.error "[#{error[:error_code]}] #{error[:file]}"
+ end
+ end
+
+ def create_release(project, contract, headshot, identifier)
+ random_contract_no = AppearanceRelease.random_contract_number.to_s
+ is_incomplete = contract.nil? || headshot.nil?
+ params = {
+ project: project,
+ person_first_name: appearance_first_name(is_incomplete),
+ person_last_name: random_contract_no
+ }
+
+ params[:person_photo] = headshot unless headshot.nil?
+ params[:contract] = contract unless contract.nil?
+ params[:identifier] = identifier unless blank?
+
+ return if AppearanceRelease.create(params)
+
+ logger.error "Failed to create AppearanceRelease with params : \r\n#{params}"
+ end
+
+ def update_release(release, contract, headshot)
+ release.contract = contract unless contract.nil?
+ release.person_photo = headshot unless headshot.nil?
+
+ release.save
+ end
+
+ def appearance_first_name(incomplete)
+ if incomplete
+ I18n.t('appearance_releases.shared.incomplete_match')
+ else
+ I18n.t('appearance_releases.shared.matched_import')
+ end
+ end
+
+ def aws_bucket_name
+ ENV.fetch 'AWS_BUCKET'
+ end
+end
diff --git a/app/models/concerns/attachable.rb b/app/models/concerns/attachable.rb
new file mode 100644
index 0000000..7d4860a
--- /dev/null
+++ b/app/models/concerns/attachable.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module Attachable
+ extend ActiveSupport::Concern
+
+ included do
+ has_many_attached :attachments
+ end
+end
diff --git a/app/models/matching_request.rb b/app/models/matching_request.rb
new file mode 100644
index 0000000..c719d57
--- /dev/null
+++ b/app/models/matching_request.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class MatchingRequest < ApplicationRecord
+ include Attachable
+
+ belongs_to :project
+end
diff --git a/config/locales/en.yml b/config/locales/en.yml
index a45e867..7073de2 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -112,6 +112,7 @@ en:
no_photos: Needs Photo
create:
failed_import: Failed to create appearance release for files listed below
+ matching_started: Matching started
no_attachments: Failed to import - no attachments
edit:
heading: Edit Appearance Release
@@ -141,6 +142,8 @@ en:
shared:
imported_appearance_release_contract_name: Imported Contract
imported_appearance_release_headshot_name: Imported Headshot
+ incomplete_match: Incomplete Match
+ matched_import: Complete Match
type_filter_actions:
all_releases: All Releases
complete_releases: Complete Releases
diff --git a/config/locales/es.yml b/config/locales/es.yml
index 674a7f0..be353f4 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -25,6 +25,7 @@ es:
appearance_releases:
create:
failed_import: Failed to create appearance release for files listed below (ES)
+ matching_started: Matching started (ES)
no_attachments: Failed to import - no attachments (ES)
form:
photos:
@@ -37,6 +38,8 @@ es:
shared:
imported_appearance_release_contract_name: Contrato Importado
imported_appearance_release_headshot_name: Retrato Importado
+ incomplete_match: Incomplete Match (ES)
+ matched_import: Complete Match (ES)
type_filter_actions:
all_releases: All Releases (ES)
complete_releases: Complete Releases (ES)
diff --git a/db/migrate/20200430151828_create_matching_requests.rb b/db/migrate/20200430151828_create_matching_requests.rb
new file mode 100644
index 0000000..cc4e4cb
--- /dev/null
+++ b/db/migrate/20200430151828_create_matching_requests.rb
@@ -0,0 +1,9 @@
+class CreateMatchingRequests < ActiveRecord::Migration[6.0]
+ def change
+ create_table :matching_requests do |t|
+ t.belongs_to :project, foreign_key: true
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20200430190412_add_identifier_to_appearance_releases.rb b/db/migrate/20200430190412_add_identifier_to_appearance_releases.rb
new file mode 100644
index 0000000..a1ff2fa
--- /dev/null
+++ b/db/migrate/20200430190412_add_identifier_to_appearance_releases.rb
@@ -0,0 +1,5 @@
+class AddIdentifierToAppearanceReleases < ActiveRecord::Migration[6.0]
+ def change
+ add_column :appearance_releases, :identifier, :string, default: nil
+ end
+end
diff --git a/lib/brayniac_ai.rb b/lib/brayniac_ai.rb
index ce9bc72..7e02af0 100644
--- a/lib/brayniac_ai.rb
+++ b/lib/brayniac_ai.rb
@@ -3,6 +3,7 @@ require_relative "./brayniac_ai/aws_request_signing"
require_relative "./brayniac_ai/aws_signed_connection"
require_relative "./brayniac_ai/base"
+require_relative "./brayniac_ai/appearance_release_matching"
require_relative "./brayniac_ai/audio_recognition"
require_relative "./brayniac_ai/collection"
require_relative "./brayniac_ai/document_analysis"
diff --git a/lib/brayniac_ai/appearance_release_matching.rb b/lib/brayniac_ai/appearance_release_matching.rb
new file mode 100644
index 0000000..4f89c69
--- /dev/null
+++ b/lib/brayniac_ai/appearance_release_matching.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+module BrayniacAI
+ class AppearanceReleaseMatching
+ class << self
+ def match_attachments(request_data)
+ # TODO: Send request with request_data and receive real response
+ mock_response request_data
+ end
+
+ private
+
+ def mock_response(request_data)
+ attachments = request_data[:files]
+ pdfs = []
+ images = []
+ matches = []
+ errors = []
+
+ # Use first file for the error entry
+ first_attachment = attachments.shift
+ unless first_attachment.nil?
+ errors << { file: first_attachment, error_code: 1 }
+ end
+
+ attachments.each do |attachment|
+ blob = ActiveStorage::Blob.find_signed attachment
+ next if blob.nil?
+
+ if blob.image?
+ images << attachment
+ else
+ pdfs << attachment
+ end
+ end
+
+ # Create pairs of matches and single headshot/contract after pairs are exhausted
+ more_pdfs = pdfs.length > images.length
+ pairs = more_pdfs ? pdfs.zip(images) : images.zip(pdfs)
+ pairs.each do |pair_element1, pair_element2|
+ if more_pdfs
+ pdf = pair_element1
+ image = pair_element2
+ else
+ pdf = pair_element2
+ image = pair_element1
+ end
+ if pdf.nil? || image.nil?
+ matches << { headshot: image } if pdf.nil?
+ matches << { contract: pdf } if image.nil?
+ else
+ matches << { headshot: image, contract: pdf, identifier: '' }
+ end
+ end
+
+ {
+ request_id: request_data[:request_id],
+ matches: matches,
+ errors: errors
+ }
+ end
+ end
+ end
+end
diff --git a/spec/features/user_managing_appearance_releases_spec.rb b/spec/features/user_managing_appearance_releases_spec.rb
index 17e2ae1..728757e 100644
--- a/spec/features/user_managing_appearance_releases_spec.rb
+++ b/spec/features/user_managing_appearance_releases_spec.rb
@@ -148,6 +148,7 @@ feature 'User managing appearance releases' do
end
scenario 'importing a releases works when image is selected', js: true do
+ skip 'Will be changed according to new matching capability'
visit project_appearance_releases_path(project)
expect(page).to have_content submit_create_button
@@ -160,6 +161,7 @@ feature 'User managing appearance releases' do
end
scenario 'importing a releases works when pdf is selected', js: true do
+ skip 'Will be changed according to new matching capability'
visit project_appearance_releases_path(project)
expect(page).to have_content submit_create_button
@@ -172,6 +174,7 @@ feature 'User managing appearance releases' do
end
scenario 'importing a releases fails when file other than image or pdf is selected', js: true do
+ skip 'Will be changed according to new matching capability'
visit project_appearance_releases_path(project)
expect(page).to have_content submit_create_button