diff --git a/app/assets/stylesheets/contract_pdf.scss b/app/assets/stylesheets/contract_pdf.scss index 59c6abc..8c2571f 100644 --- a/app/assets/stylesheets/contract_pdf.scss +++ b/app/assets/stylesheets/contract_pdf.scss @@ -31,7 +31,7 @@ u { } .page { - page-break-before: always; + page-break-before: always; } .logo { @@ -44,6 +44,14 @@ u { text-align: right; } +.qr-code { + margin-right: -30px; +} + +.do-not-copy-warning { + padding-right: 15px; +} + .heading-table td { width: 50%; } diff --git a/app/controllers/appearance_release_imports_controller.rb b/app/controllers/appearance_release_imports_controller.rb index 40a0857..b80a7d2 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 + MatchAppearanceReleasesJob.perform_later(@project, 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 += '
' - end - - redirect_to [@project, :appearance_releases], alert: alert_message end private @@ -45,45 +36,7 @@ class AppearanceReleaseImportsController < ApplicationController params.require(:attachments) end - def build_appearance_release(params = {}) - authorize appearance_releases.build(params) - 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..5136170 --- /dev/null +++ b/app/jobs/match_appearance_releases_job.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +class MatchAppearanceReleasesJob < ApplicationJob + queue_as :default + + def perform(project, attachments) + filtered_attachments_object = filter_attachments attachments + + return if filtered_attachments_object[:keys].blank? + + matching_request = MatchingRequest.create project: project, attachments: filtered_attachments_object[:signed_ids] + + payload = { request_id: matching_request.id, bucket: aws_bucket_name, files: filtered_attachments_object[:keys]} + response = BrayniacAI::QrMatching.create! payload + matches = response.matches || [] + key_signed_id_hash = Hash[filtered_attachments_object[:keys].zip(filtered_attachments_object[:signed_ids])] + handle_matches matches, project, key_signed_id_hash + matching_request.destroy + end + + private + + def handle_matches(matches, project, key_signed_id_hash) + matches.each do |match| + contract_key = Array.wrap(match.contracts).first + headshot_key = Array.wrap(match.headshots).first + identifier = match.identifier + + contract = key_signed_id_hash[contract_key] + headshot = key_signed_id_hash[headshot_key] + + next if contract.nil? && headshot.nil? + + 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 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 + + def filter_attachments(attachments) + filtered_attachments_keys = [] + filtered_attachments_signed_ids = [] + + attachments.each do |attachment| + blob = ActiveStorage::Blob.find_signed attachment + next if blob.nil? + + extension = blob.filename.extension + next unless blob.image? || extension == 'pdf' + + filtered_attachments_keys << blob.key + filtered_attachments_signed_ids << attachment + end + + { + keys: filtered_attachments_keys, + signed_ids: filtered_attachments_signed_ids + } + end +end diff --git a/app/models/account.rb b/app/models/account.rb index 188dd71..5c82e51 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -56,6 +56,7 @@ class Account < ApplicationRecord Broadcast.where(project: projects), ZoomMeeting.where(project: projects), MedicalRelease.where(project: projects), + MatchingRequest.where(project: projects), self ])).sum(:byte_size).to_f end diff --git a/app/models/blank_contract.rb b/app/models/blank_contract.rb index ee36282..cde5d8a 100644 --- a/app/models/blank_contract.rb +++ b/app/models/blank_contract.rb @@ -8,7 +8,7 @@ class BlankContract end def to_pdf - kit = PDFKit.new(as_html) + kit = PDFKit.new(as_html, margin_right: 1, margin_left: 1, margin_top: 10, margin_bottom: 1) kit.to_file("tmp/#{filename}") 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/app/models/qr_code.rb b/app/models/qr_code.rb index 637fc6c..a38e0df 100644 --- a/app/models/qr_code.rb +++ b/app/models/qr_code.rb @@ -24,7 +24,7 @@ class QrCode end end - def to_base64_png(width = 100, height = 100) + def to_base64_png(width = 200, height = 200) _qr_code.as_png.resize(width, height).to_data_url end diff --git a/app/views/blank_contracts/pdf.html.erb b/app/views/blank_contracts/pdf.html.erb index e70dbc3..87cc5f1 100644 --- a/app/views/blank_contracts/pdf.html.erb +++ b/app/views/blank_contracts/pdf.html.erb @@ -2,6 +2,10 @@
<% has_logo = local_assigns[:logo] %> + + + + - - - - - - - -
 <%= t '.do_not_copy_warning' %>
<% if has_logo %> @@ -11,17 +15,9 @@ <% end %> - +
 <%= serial_numbers[copy_index] %>
 <%= t '.do_not_copy_warning' %>

<% if contract_template.body.present? %> diff --git a/app/views/contract_templates/_form.html.erb b/app/views/contract_templates/_form.html.erb index 9bd9b4a..dc74784 100644 --- a/app/views/contract_templates/_form.html.erb +++ b/app/views/contract_templates/_form.html.erb @@ -26,7 +26,7 @@ <% end %>
<% end %> - + <%= field_set_tag content_tag(:span, t(".custom_fields.heading"), class: "h6 text-muted text-uppercase"), id: "custom_fields", style: "display: none;" do %>

<%= fa_icon("info-circle", text: t(".custom_fields.instructions")) %>

<%= render "shared/custom_fields", form: form %> 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/db/structure.sql b/db/structure.sql index 204f848..f6a1918 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -9,20 +9,6 @@ SET xmloption = content; SET client_min_messages = warning; SET row_security = off; --- --- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: - --- - -CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog; - - --- --- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: - --- - -COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language'; - - -- -- Name: fuzzystrmatch; Type: EXTENSION; Schema: -; Owner: - -- @@ -331,6 +317,7 @@ CREATE TABLE public.appearance_releases ( person_last_name character varying, guardian_first_name character varying, guardian_last_name character varying, + identifier character varying, guardian_email character varying ); @@ -886,6 +873,37 @@ CREATE SEQUENCE public.location_releases_id_seq ALTER SEQUENCE public.location_releases_id_seq OWNED BY public.location_releases.id; +-- +-- Name: matching_requests; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.matching_requests ( + id bigint NOT NULL, + project_id bigint, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL +); + + +-- +-- Name: matching_requests_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.matching_requests_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: matching_requests_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.matching_requests_id_seq OWNED BY public.matching_requests.id; + + -- -- Name: material_releases; Type: TABLE; Schema: public; Owner: - -- @@ -1253,6 +1271,7 @@ CREATE TABLE public.settings ( -- CREATE SEQUENCE public.settings_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -1288,6 +1307,7 @@ CREATE TABLE public.taggings ( -- CREATE SEQUENCE public.taggings_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -1318,6 +1338,7 @@ CREATE TABLE public.tags ( -- CREATE SEQUENCE public.tags_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -1630,7 +1651,6 @@ CREATE TABLE public.zoom_meetings ( api_meeting_id character varying, created_at timestamp(6) without time zone NOT NULL, updated_at timestamp(6) without time zone NOT NULL, - broadcast_id bigint, zoom_user_id bigint, project_id bigint, status integer DEFAULT 0 @@ -1829,6 +1849,13 @@ ALTER TABLE ONLY public.imports ALTER COLUMN id SET DEFAULT nextval('public.impo ALTER TABLE ONLY public.location_releases ALTER COLUMN id SET DEFAULT nextval('public.location_releases_id_seq'::regclass); +-- +-- Name: matching_requests id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.matching_requests ALTER COLUMN id SET DEFAULT nextval('public.matching_requests_id_seq'::regclass); + + -- -- Name: material_releases id; Type: DEFAULT; Schema: public; Owner: - -- @@ -2137,6 +2164,14 @@ ALTER TABLE ONLY public.location_releases ADD CONSTRAINT location_releases_pkey PRIMARY KEY (id); +-- +-- Name: matching_requests matching_requests_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.matching_requests + ADD CONSTRAINT matching_requests_pkey PRIMARY KEY (id); + + -- -- Name: material_releases material_releases_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -2606,6 +2641,13 @@ CREATE INDEX index_location_releases_on_term_id ON public.location_releases USIN CREATE INDEX index_location_releases_on_territory_id ON public.location_releases USING btree (territory_id); +-- +-- Name: index_matching_requests_on_project_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_matching_requests_on_project_id ON public.matching_requests USING btree (project_id); + + -- -- Name: index_material_releases_on_applicable_medium_id; Type: INDEX; Schema: public; Owner: - -- @@ -2935,13 +2977,6 @@ CREATE INDEX index_videos_on_audio_analysis_uid ON public.videos USING btree (au CREATE INDEX index_videos_on_project_id ON public.videos USING btree (project_id); --- --- Name: index_zoom_meetings_on_broadcast_id; Type: INDEX; Schema: public; Owner: - --- - -CREATE INDEX index_zoom_meetings_on_broadcast_id ON public.zoom_meetings USING btree (broadcast_id); - - -- -- Name: index_zoom_meetings_on_project_id; Type: INDEX; Schema: public; Owner: - -- @@ -3250,6 +3285,14 @@ ALTER TABLE ONLY public.material_releases ADD CONSTRAINT fk_rails_6b945b36b9 FOREIGN KEY (contract_template_id) REFERENCES public.contract_templates(id); +-- +-- Name: matching_requests fk_rails_71d16e64c8; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.matching_requests + ADD CONSTRAINT fk_rails_71d16e64c8 FOREIGN KEY (project_id) REFERENCES public.projects(id); + + -- -- Name: appearance_releases fk_rails_7a58302526; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -3282,14 +3325,6 @@ ALTER TABLE ONLY public.music_releases ADD CONSTRAINT fk_rails_890d967673 FOREIGN KEY (territory_id) REFERENCES public.territories(id); --- --- Name: zoom_meetings fk_rails_8d814ea729; Type: FK CONSTRAINT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.zoom_meetings - ADD CONSTRAINT fk_rails_8d814ea729 FOREIGN KEY (broadcast_id) REFERENCES public.broadcasts(id); - - -- -- Name: medical_releases fk_rails_98aa92daa9; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -3609,6 +3644,8 @@ INSERT INTO "schema_migrations" (version) VALUES ('20200424161117'), ('20200427073429'), ('20200428091105'), +('20200430151828'), +('20200430190412'), ('20200507110804'), ('20200512161738'), ('20200526113516'), @@ -3616,6 +3653,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('20200606044747'), ('20200610085411'), ('20200610140459'), -('20200612121539'); +('20200612121539'), +('20200615131722'); diff --git a/lib/brayniac_ai.rb b/lib/brayniac_ai.rb index ce9bc72..a6a7143 100644 --- a/lib/brayniac_ai.rb +++ b/lib/brayniac_ai.rb @@ -10,5 +10,6 @@ require_relative "./brayniac_ai/edl_parse" require_relative "./brayniac_ai/edl_parse_result" require_relative "./brayniac_ai/facial_recognition" require_relative "./brayniac_ai/facial_recognition_result" +require_relative "./brayniac_ai/qr_matching" require_relative "./brayniac_ai/tag" require_relative "./brayniac_ai/validation" diff --git a/lib/brayniac_ai/qr_matching.rb b/lib/brayniac_ai/qr_matching.rb new file mode 100644 index 0000000..4328870 --- /dev/null +++ b/lib/brayniac_ai/qr_matching.rb @@ -0,0 +1,4 @@ +module BrayniacAI + class QrMatching < Base + end +end diff --git a/spec/factories/appearance_releases.rb b/spec/factories/appearance_releases.rb index a64c4a9..afcec4e 100644 --- a/spec/factories/appearance_releases.rb +++ b/spec/factories/appearance_releases.rb @@ -67,5 +67,24 @@ FactoryBot.define do appearance_release.contract_template = build(:appearance_release_contract_template) end end + + factory :appearance_release_import do + person_photo nil + + trait :with_headshot do + person_photo do + path = Rails.root.join("spec", "fixtures", "files", "person_photo.png") + Rack::Test::UploadedFile.new(path, "image/png") + end + end + + trait :with_contract do + contract do + path = Rails.root.join("spec", "fixtures", "files", "AppearanceRelease.pdf") + Rack::Test::UploadedFile.new(path, "application/pdf") + end + 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..d4f8c70 100644 --- a/spec/features/user_managing_appearance_releases_spec.rb +++ b/spec/features/user_managing_appearance_releases_spec.rb @@ -138,48 +138,25 @@ feature 'User managing appearance releases' do end scenario 'progress bar shows when user imports a release', js: true do - skip "TODO" + visit project_appearance_releases_path(project) + + expect(page).not_to have_css('#upload-progress-container') + large_pdf_file = build_large_pdf_file + attach_file import_appearance_release_field, Rails.root.join(large_pdf_file.path), visible: false + expect(page).to have_css('#upload-progress-container') + expect(page).to have_content submit_create_button, wait: 30 + click_button submit_create_button + expect(page).to have_content matching_started + end + + scenario 'MatchAppearanceReleasesJob is started when import is finished', js: true do visit project_appearance_releases_path(project) attach_file import_appearance_release_field, Rails.root.join(file_fixture('person_photo.png')), visible: false - expect(page).to have_content importing_label + allow(MatchAppearanceReleasesJob).to receive(:perform_later).with(project, anything) click_button submit_create_button - expect(page).to have_css('.progress') - end - - scenario 'importing a releases works when image is selected', js: true do + expect(page).to have_content matching_started visit project_appearance_releases_path(project) - - expect(page).to have_content submit_create_button - expect(page).to have_content no_appearance_releases - attach_file import_appearance_release_field, Rails.root.join(file_fixture('person_photo.png')), visible: false - expect(page).to have_content importing_label - click_button submit_create_button - expect(page).not_to have_content no_appearance_releases - expect(page).to have_content /Imported Headshot\s+\d{7}/ - end - - scenario 'importing a releases works when pdf is selected', js: true do - visit project_appearance_releases_path(project) - - expect(page).to have_content submit_create_button - expect(page).to have_content no_appearance_releases - attach_file import_appearance_release_field, Rails.root.join(file_fixture('contract.pdf')), visible: false - expect(page).to have_content importing_label - click_button submit_create_button - expect(page).not_to have_content no_appearance_releases - expect(page).to have_content /Imported Contract\s+\d{7}/ - end - - scenario 'importing a releases fails when file other than image or pdf is selected', js: true do - visit project_appearance_releases_path(project) - - expect(page).to have_content submit_create_button - expect(page).to have_content no_appearance_releases - attach_file import_appearance_release_field, Rails.root.join(file_fixture('audio.mp3')), visible: false - expect(page).to have_content importing_label - click_button submit_create_button - expect(page).to have_content failed_to_import_notice expect(page).to have_content no_appearance_releases end @@ -435,6 +412,10 @@ feature 'User managing appearance releases' do tempfile end + def matching_started + t 'appearance_releases.create.matching_started' + end + def filter_type_all t 'appearance_releases.type_filter_actions.all_releases' end diff --git a/spec/jobs/match_appearance_releases_job_spec.rb b/spec/jobs/match_appearance_releases_job_spec.rb new file mode 100644 index 0000000..bd35985 --- /dev/null +++ b/spec/jobs/match_appearance_releases_job_spec.rb @@ -0,0 +1,216 @@ +require "rails_helper" + +describe MatchAppearanceReleasesJob do + let(:project) { create(:project) } + let(:dummy_appearance_release) { create(:appearance_release_import, :with_headshot, :with_contract) } + let(:dummy_matching_request) { instance_double(MatchingRequest, id: 999) } + + before :all do + ENV["AWS_BUCKET"] = "" + end + + describe ".perform_now" do + it "returns if no attachment is sent" do + expect(MatchingRequest).not_to receive(:create) + attachments = [] + MatchAppearanceReleasesJob.perform_now project, attachments + end + + it "returns if no valid attachment is sent" do + expect(MatchingRequest).not_to receive(:create) + dummy_video = create(:video) + attachments = [dummy_video.file.blob.signed_id] + MatchAppearanceReleasesJob.perform_now project, attachments + end + + it "does not create new appearance release if BrayniacAI returns empty matches array" do + signed_ids = [dummy_appearance_release.person_photo.blob.signed_id] + keys = [dummy_appearance_release.person_photo.key] + payload = { + project: project, + attachments: signed_ids + } + qr_matching_payload = { + bucket: '', + files: keys, + request_id: dummy_matching_request.id + } + qr_matching_mock_response = double( + request_id: dummy_matching_request.id, + matches: [] + ) + + expect(MatchingRequest).to receive(:create).with(payload).and_return(dummy_matching_request) + expect(BrayniacAI::QrMatching).to receive(:create!).with(qr_matching_payload).and_return(qr_matching_mock_response) + expect(dummy_matching_request).to receive(:destroy) + + MatchAppearanceReleasesJob.perform_now project, signed_ids + + expect(AppearanceRelease.last).to eq dummy_appearance_release + end + + it "creates new incomplete appearance release if BrayniacAI returns single headshot match" do + signed_ids = [dummy_appearance_release.person_photo.blob.signed_id] + keys = [dummy_appearance_release.person_photo.key] + payload = { + project: project, + attachments: signed_ids + } + qr_matching_payload = { + bucket: '', + files: keys, + request_id: dummy_matching_request.id + } + mock_match = double( + headshots: keys, + contracts: [], + unknowns: [], + identifier: 'some/identifier/123' + ) + matches = [mock_match] + qr_matching_mock_response = double( + request_id: dummy_matching_request.id, + matches: matches + ) + + expect(MatchingRequest).to receive(:create).with(payload).and_return(dummy_matching_request) + expect(BrayniacAI::QrMatching).to receive(:create!).with(qr_matching_payload).and_return(qr_matching_mock_response) + expect(dummy_matching_request).to receive(:destroy) + + MatchAppearanceReleasesJob.perform_now project, signed_ids + + expect(AppearanceRelease.last.identifier).to eq mock_match.identifier + expect(AppearanceRelease.last.person_photo).to be_attached + expect(AppearanceRelease.last.contract).not_to be_attached + end + + it "creates new incomplete appearance release if BrayniacAI returns single contract match" do + signed_ids = [dummy_appearance_release.contract.blob.signed_id] + keys = [dummy_appearance_release.contract.key] + payload = { + project: project, + attachments: signed_ids + } + qr_matching_payload = { + bucket: '', + files: keys, + request_id: dummy_matching_request.id + } + mock_match = double( + headshots: [], + contracts: keys, + unknowns: [], + identifier: 'some/identifier/123' + ) + matches = [mock_match] + qr_matching_mock_response = double( + request_id: dummy_matching_request.id, + matches: matches + ) + + expect(MatchingRequest).to receive(:create).with(payload).and_return(dummy_matching_request) + expect(BrayniacAI::QrMatching).to receive(:create!).with(qr_matching_payload).and_return(qr_matching_mock_response) + expect(dummy_matching_request).to receive(:destroy) + + MatchAppearanceReleasesJob.perform_now project, signed_ids + + expect(AppearanceRelease.last.identifier).to eq mock_match.identifier + expect(AppearanceRelease.last.person_photo.attached?).to eq false + expect(AppearanceRelease.last.contract.attached?).to eq true + end + + it "creates new complete appearance release if BrayniacAI returns match for headshot and contract" do + signed_ids = [ + dummy_appearance_release.person_photo.blob.signed_id, + dummy_appearance_release.contract.blob.signed_id + ] + keys = [ + dummy_appearance_release.person_photo.key, + dummy_appearance_release.contract.key + ] + payload = { + project: project, + attachments: signed_ids + } + qr_matching_payload = { + bucket: '', + files: keys, + request_id: dummy_matching_request.id + } + mock_match = double( + headshots: [keys[0]], + contracts: [keys[1]], + unknowns: [], + identifier: 'some/identifier/123' + ) + matches = [mock_match] + qr_matching_mock_response = double( + request_id: dummy_matching_request.id, + matches: matches + ) + + expect(MatchingRequest).to receive(:create).with(payload).and_return(dummy_matching_request) + expect(BrayniacAI::QrMatching).to receive(:create!).with(qr_matching_payload).and_return(qr_matching_mock_response) + expect(dummy_matching_request).to receive(:destroy) + + MatchAppearanceReleasesJob.perform_now project, signed_ids + + expect(AppearanceRelease.last.identifier).to eq mock_match.identifier + expect(AppearanceRelease.last.person_photo.attached?).to eq true + expect(AppearanceRelease.last.contract.attached?).to eq true + end + + it "creates two new incomplete appearance releases if BrayniacAI returns two matches for headshot and contract" do + signed_ids = [ + dummy_appearance_release.person_photo.blob.signed_id, + dummy_appearance_release.contract.blob.signed_id + ] + keys = [ + dummy_appearance_release.person_photo.key, + dummy_appearance_release.contract.key + ] + payload = { + project: project, + attachments: signed_ids + } + qr_matching_payload = { + bucket: '', + files: keys, + request_id: dummy_matching_request.id + } + mock_match1 = double( + headshots: [keys[0]], + contracts: [], + unknowns: [], + identifier: 'some/identifier/123' + ) + mock_match2 = double( + headshots: [], + contracts: [keys[1]], + unknowns: [], + identifier: 'some/identifier/789' + ) + matches = [mock_match1, mock_match2] + qr_matching_mock_response = double( + request_id: dummy_matching_request.id, + matches: matches + ) + + expect(MatchingRequest).to receive(:create).with(payload).and_return(dummy_matching_request) + expect(BrayniacAI::QrMatching).to receive(:create!).with(qr_matching_payload).and_return(qr_matching_mock_response) + expect(dummy_matching_request).to receive(:destroy) + + MatchAppearanceReleasesJob.perform_now project, signed_ids + + releases = AppearanceRelease.last(2) + + expect(releases[0].identifier).to eq mock_match1.identifier + expect(releases[0].person_photo.attached?).to eq true + expect(releases[0].contract.attached?).to eq false + + expect(releases[1].identifier).to eq mock_match2.identifier + expect(releases[1].person_photo.attached?).to eq false + expect(releases[1].contract.attached?).to eq true + end + end +end \ No newline at end of file diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index 3fbd316..8147964 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -131,7 +131,8 @@ RSpec.describe Account do Broadcast, Account, ZoomMeeting, - MedicalRelease + MedicalRelease, + MatchingRequest ] Rails.application.eager_load! ActiveRecord::Base.descendants.each do |model| diff --git a/spec/models/blank_contract_spec.rb b/spec/models/blank_contract_spec.rb index d3c2d20..9b7e421 100644 --- a/spec/models/blank_contract_spec.rb +++ b/spec/models/blank_contract_spec.rb @@ -42,7 +42,6 @@ describe BlankContract do material_release = create(:material_release_with_contract_template, project: project, person_name: 'Jane Doe') result = render_contract_html_for(material_release) - expect(result).to include 'serial-number' expect(result).to include 'DO NOT COPY' end end