Sync of the branch
This commit is contained in:
@@ -3,7 +3,6 @@
|
|||||||
class AppearanceReleaseImportsController < ApplicationController
|
class AppearanceReleaseImportsController < ApplicationController
|
||||||
include AppearanceReleaseContext
|
include AppearanceReleaseContext
|
||||||
include ProjectContext
|
include ProjectContext
|
||||||
include CreateReleasableJobs
|
|
||||||
|
|
||||||
before_action :set_project, only: [:create]
|
before_action :set_project, only: [:create]
|
||||||
|
|
||||||
@@ -11,24 +10,16 @@ class AppearanceReleaseImportsController < ApplicationController
|
|||||||
|
|
||||||
def create
|
def create
|
||||||
authorize AppearanceRelease
|
authorize AppearanceRelease
|
||||||
@failed_files = []
|
|
||||||
attachments = appearance_release_params
|
attachments = appearance_release_params
|
||||||
if attachments.nil?
|
if attachments.nil?
|
||||||
alert_message = t 'appearance_releases.create.no_attachments'
|
alert_message = t 'appearance_releases.create.no_attachments'
|
||||||
|
redirect_to [@project, :appearance_releases], alert: alert_message
|
||||||
else
|
else
|
||||||
attachments.each do |attachment|
|
filtered_attachments = filter_attachments attachments
|
||||||
create_imported_appearance_release attachment
|
MatchAppearanceReleasesJob.perform_later(@project, filtered_attachments)
|
||||||
end
|
notice_message = t 'appearance_releases.create.matching_started'
|
||||||
|
redirect_to [@project, :appearance_releases], notice: notice_message
|
||||||
end
|
end
|
||||||
|
|
||||||
unless @failed_files.empty?
|
|
||||||
alert_message = t 'appearance_releases.create.failed_import'
|
|
||||||
alert_message += '<br><ul>'
|
|
||||||
@failed_files.each { |file_name| alert_message += "<li>#{file_name}</li>" }
|
|
||||||
alert_message += '</ul>'
|
|
||||||
end
|
|
||||||
|
|
||||||
redirect_to [@project, :appearance_releases], alert: alert_message
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@@ -45,45 +36,18 @@ class AppearanceReleaseImportsController < ApplicationController
|
|||||||
params.require(:attachments)
|
params.require(:attachments)
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_appearance_release(params = {})
|
def filter_attachments(attachments)
|
||||||
authorize appearance_releases.build(params)
|
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
|
end
|
||||||
|
|
||||||
def log_create_analytics
|
def acceptable_extensions
|
||||||
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)
|
AppearanceRelease.acceptable_import_file_extensions
|
||||||
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
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
79
app/jobs/match_appearance_releases_job.rb
Normal file
79
app/jobs/match_appearance_releases_job.rb
Normal file
@@ -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
|
||||||
9
app/models/concerns/attachable.rb
Normal file
9
app/models/concerns/attachable.rb
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Attachable
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
has_many_attached :attachments
|
||||||
|
end
|
||||||
|
end
|
||||||
7
app/models/matching_request.rb
Normal file
7
app/models/matching_request.rb
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class MatchingRequest < ApplicationRecord
|
||||||
|
include Attachable
|
||||||
|
|
||||||
|
belongs_to :project
|
||||||
|
end
|
||||||
@@ -112,6 +112,7 @@ en:
|
|||||||
no_photos: Needs Photo
|
no_photos: Needs Photo
|
||||||
create:
|
create:
|
||||||
failed_import: Failed to create appearance release for files listed below
|
failed_import: Failed to create appearance release for files listed below
|
||||||
|
matching_started: Matching started
|
||||||
no_attachments: Failed to import - no attachments
|
no_attachments: Failed to import - no attachments
|
||||||
edit:
|
edit:
|
||||||
heading: Edit Appearance Release
|
heading: Edit Appearance Release
|
||||||
@@ -141,6 +142,8 @@ en:
|
|||||||
shared:
|
shared:
|
||||||
imported_appearance_release_contract_name: Imported Contract
|
imported_appearance_release_contract_name: Imported Contract
|
||||||
imported_appearance_release_headshot_name: Imported Headshot
|
imported_appearance_release_headshot_name: Imported Headshot
|
||||||
|
incomplete_match: Incomplete Match
|
||||||
|
matched_import: Complete Match
|
||||||
type_filter_actions:
|
type_filter_actions:
|
||||||
all_releases: All Releases
|
all_releases: All Releases
|
||||||
complete_releases: Complete Releases
|
complete_releases: Complete Releases
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ es:
|
|||||||
appearance_releases:
|
appearance_releases:
|
||||||
create:
|
create:
|
||||||
failed_import: Failed to create appearance release for files listed below (ES)
|
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)
|
no_attachments: Failed to import - no attachments (ES)
|
||||||
form:
|
form:
|
||||||
photos:
|
photos:
|
||||||
@@ -37,6 +38,8 @@ es:
|
|||||||
shared:
|
shared:
|
||||||
imported_appearance_release_contract_name: Contrato Importado
|
imported_appearance_release_contract_name: Contrato Importado
|
||||||
imported_appearance_release_headshot_name: Retrato Importado
|
imported_appearance_release_headshot_name: Retrato Importado
|
||||||
|
incomplete_match: Incomplete Match (ES)
|
||||||
|
matched_import: Complete Match (ES)
|
||||||
type_filter_actions:
|
type_filter_actions:
|
||||||
all_releases: All Releases (ES)
|
all_releases: All Releases (ES)
|
||||||
complete_releases: Complete Releases (ES)
|
complete_releases: Complete Releases (ES)
|
||||||
|
|||||||
9
db/migrate/20200430151828_create_matching_requests.rb
Normal file
9
db/migrate/20200430151828_create_matching_requests.rb
Normal file
@@ -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
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
class AddIdentifierToAppearanceReleases < ActiveRecord::Migration[6.0]
|
||||||
|
def change
|
||||||
|
add_column :appearance_releases, :identifier, :string, default: nil
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -3,6 +3,7 @@ require_relative "./brayniac_ai/aws_request_signing"
|
|||||||
require_relative "./brayniac_ai/aws_signed_connection"
|
require_relative "./brayniac_ai/aws_signed_connection"
|
||||||
|
|
||||||
require_relative "./brayniac_ai/base"
|
require_relative "./brayniac_ai/base"
|
||||||
|
require_relative "./brayniac_ai/appearance_release_matching"
|
||||||
require_relative "./brayniac_ai/audio_recognition"
|
require_relative "./brayniac_ai/audio_recognition"
|
||||||
require_relative "./brayniac_ai/collection"
|
require_relative "./brayniac_ai/collection"
|
||||||
require_relative "./brayniac_ai/document_analysis"
|
require_relative "./brayniac_ai/document_analysis"
|
||||||
|
|||||||
64
lib/brayniac_ai/appearance_release_matching.rb
Normal file
64
lib/brayniac_ai/appearance_release_matching.rb
Normal file
@@ -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
|
||||||
@@ -148,6 +148,7 @@ feature 'User managing appearance releases' do
|
|||||||
end
|
end
|
||||||
|
|
||||||
scenario 'importing a releases works when image is selected', js: true do
|
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)
|
visit project_appearance_releases_path(project)
|
||||||
|
|
||||||
expect(page).to have_content submit_create_button
|
expect(page).to have_content submit_create_button
|
||||||
@@ -160,6 +161,7 @@ feature 'User managing appearance releases' do
|
|||||||
end
|
end
|
||||||
|
|
||||||
scenario 'importing a releases works when pdf is selected', js: true do
|
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)
|
visit project_appearance_releases_path(project)
|
||||||
|
|
||||||
expect(page).to have_content submit_create_button
|
expect(page).to have_content submit_create_button
|
||||||
@@ -172,6 +174,7 @@ feature 'User managing appearance releases' do
|
|||||||
end
|
end
|
||||||
|
|
||||||
scenario 'importing a releases fails when file other than image or pdf is selected', js: true do
|
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)
|
visit project_appearance_releases_path(project)
|
||||||
|
|
||||||
expect(page).to have_content submit_create_button
|
expect(page).to have_content submit_create_button
|
||||||
|
|||||||
Reference in New Issue
Block a user