diff --git a/app/assets/javascripts/channels/broadcasts.coffee b/app/assets/javascripts/channels/broadcasts.coffee index 7b5367f..9d49aaf 100644 --- a/app/assets/javascripts/channels/broadcasts.coffee +++ b/app/assets/javascripts/channels/broadcasts.coffee @@ -17,7 +17,6 @@ $(document).on "turbolinks:load", -> switch data.event when "broadcast_stream_update" then @refreshBroadcastVideo(data) when "stream_recording_ready" then @showBroadcastRecordings(data) - when "file_upload_update" then @refreshFilesTab(data) refreshBroadcastVideo: (data) -> $("#broadcast_updates").html data.status_content @@ -38,8 +37,3 @@ $(document).on "turbolinks:load", -> $(".flash-message").html data.flash_content $("#broadcast_recordings").html data.recordings_content $("#broadcast_recordings_nav").html data.recordings_nav_content - - refreshFilesTab: (data) -> - $("#broadcast_file_list").html data.files_content - $("#broadcast_files_pagination").html data.pagination_content - \ No newline at end of file diff --git a/app/assets/javascripts/channels/projects.coffee b/app/assets/javascripts/channels/projects.coffee index 41061c9..c0e4037 100644 --- a/app/assets/javascripts/channels/projects.coffee +++ b/app/assets/javascripts/channels/projects.coffee @@ -16,7 +16,6 @@ $(document).on "turbolinks:load", -> switch data.event when "video_status_update" then @showVideoStatusUpdate(data.content) when "download_status_update" then @showDownloadStatusUpdate(data.content) - when "conference_recording_ready" then @showDownloadStatusUpdate(data.content) showVideoStatusUpdate: (content) -> $("[data-ujs-target='video-analysis-msg']").replaceWith content diff --git a/app/channels/broadcasts_channel.rb b/app/channels/broadcasts_channel.rb index e3014e6..36d1d95 100644 --- a/app/channels/broadcasts_channel.rb +++ b/app/channels/broadcasts_channel.rb @@ -28,20 +28,10 @@ class BroadcastsChannel < ApplicationCable::Channel recordings_content = ApplicationController.render partial: "broadcasts/broadcast_recordings", locals: { recordings: recordings, broadcast: broadcast } recordings_nav_content = ApplicationController.render partial: "broadcasts/broadcast_recording_nav", collection: recordings, as: :broadcast_recording - broadcast_to broadcast, - event: :stream_recording_ready, - flash_content: flash_content, + broadcast_to broadcast, + event: :stream_recording_ready, + flash_content: flash_content, recordings_content: recordings_content, recordings_nav_content: recordings_nav_content end - - def self.file_upload_updates(broadcast, files, pagination_content) - files_content = ApplicationController.render partial: "broadcasts/file", collection: files - - broadcast_to broadcast, { - event: :file_upload_update, - files_content: files_content, - pagination_content: pagination_content - } - end end diff --git a/app/channels/projects_channel.rb b/app/channels/projects_channel.rb index 68ee885..9b2ebbd 100644 --- a/app/channels/projects_channel.rb +++ b/app/channels/projects_channel.rb @@ -1,5 +1,5 @@ class ProjectsChannel < ApplicationCable::Channel - + def subscribed # TODO: How can we get the current account in this context project = current_user.accessible_projects_for(current_user.primary_account).find(params[:id]) @@ -25,12 +25,4 @@ class ProjectsChannel < ApplicationCable::Channel content = ApplicationController.render partial: "application/flash", locals: { flash: flash } broadcast_to download.project, event: :download_status_update, content: content end - - def self.conference_recording_ready(project, recording) - link = ApplicationController.helpers.link_to('Download here.', recording.service_url, target: '_blank', class: 'alert-link') - notification = "A recording of your video conference is now available. #{link}" - flash = OpenStruct.new(notice: notification) - content = ApplicationController.render partial: 'application/flash', locals: { flash: flash } - broadcast_to project, event: :conference_recording_ready, content: content - end end diff --git a/app/controllers/admin/task_requests_controller.rb b/app/controllers/admin/task_requests_controller.rb new file mode 100644 index 0000000..89c8ba3 --- /dev/null +++ b/app/controllers/admin/task_requests_controller.rb @@ -0,0 +1,32 @@ +class Admin::TaskRequestsController < Admin::ApplicationController + before_action :set_task_request, only: [:edit, :update] + + def index + @task_requests = task_requests.order_by_recent.paginate(page: params[:page]) + end + + def edit + end + + def update + if @task_request.update(task_request_params) + redirect_to [:admin, :task_requests], notice: t(".notice") + else + render :edit + end + end + + private + + def task_request_params + params.require(:task_request).permit(:status, :deliverable_url) + end + + def task_requests + policy_scope TaskRequest + end + + def set_task_request + @task_request = authorize policy_scope(TaskRequest).find(params[:id]) + end +end diff --git a/app/controllers/appearance_release_imports_controller.rb b/app/controllers/appearance_release_imports_controller.rb index 3dee19b..40a0857 100644 --- a/app/controllers/appearance_release_imports_controller.rb +++ b/app/controllers/appearance_release_imports_controller.rb @@ -3,6 +3,7 @@ class AppearanceReleaseImportsController < ApplicationController include AppearanceReleaseContext include ProjectContext + include CreateReleasableJobs before_action :set_project, only: [:create] @@ -10,16 +11,24 @@ 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 - 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 + attachments.each do |attachment| + create_imported_appearance_release attachment + end 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 @@ -36,18 +45,45 @@ class AppearanceReleaseImportsController < ApplicationController params.require(:attachments) end - def filter_attachments(attachments) - filtered_attachments = [] - attachments.each do |attachment| - blob = ActiveStorage::Blob.find_signed attachment - next if blob.nil? + def build_appearance_release(params = {}) + authorize appearance_releases.build(params) + end - extension = blob.filename.extension - filtered_attachments << attachment if blob.image? || extension == 'pdf' + 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 end - - def acceptable_extensions - AppearanceRelease.acceptable_import_file_extensions - end end diff --git a/app/controllers/broadcasts_controller.rb b/app/controllers/broadcasts_controller.rb index 2d69383..3ae5e41 100644 --- a/app/controllers/broadcasts_controller.rb +++ b/app/controllers/broadcasts_controller.rb @@ -27,9 +27,6 @@ class BroadcastsController < ApplicationController def update @broadcast.update(broadcast_params) @files = @broadcast.files.order("created_at DESC").paginate(page: 1) - - pagination_content = ApplicationController.render html: helpers.will_paginate(@files, params: { active_tab: params[:active_tab] }) - BroadcastsChannel.file_upload_updates(@broadcast, @files, pagination_content) end def show diff --git a/app/controllers/password_resets_controller.rb b/app/controllers/password_resets_controller.rb index b956781..bca7a3b 100644 --- a/app/controllers/password_resets_controller.rb +++ b/app/controllers/password_resets_controller.rb @@ -18,7 +18,6 @@ class PasswordResetsController < ApplicationController end def edit - redirect_to new_session_path, notice: t(".notice") if @user.nil? end def update diff --git a/app/controllers/public/broadcasts_controller.rb b/app/controllers/public/broadcasts_controller.rb index b71c338..314e6c3 100644 --- a/app/controllers/public/broadcasts_controller.rb +++ b/app/controllers/public/broadcasts_controller.rb @@ -14,9 +14,6 @@ class Public::BroadcastsController < Public::BaseController def update @broadcast.update(broadcast_params) @files = @broadcast.files.order("created_at DESC").paginate(page: 1) - - pagination_content = ApplicationController.render html: helpers.will_paginate(@files, params: { active_tab: params[:active_tab] }) - BroadcastsChannel.file_upload_updates(@broadcast, @files, pagination_content) end private diff --git a/app/controllers/task_requests_controller.rb b/app/controllers/task_requests_controller.rb new file mode 100644 index 0000000..f50389e --- /dev/null +++ b/app/controllers/task_requests_controller.rb @@ -0,0 +1,74 @@ +class TaskRequestsController < ApplicationController + layout "project" + + before_action :set_project + before_action :build_task_request, only: [:new, :create] + before_action :set_task_request, only: [:show, :edit, :update, :cancel] + + def index + if params[:completed_only] + @task_requests = task_requests.completed.order_by_recent.paginate(page: params[:page]) + else + @task_requests = task_requests.order_by_recent.paginate(page: params[:page]) + end + end + + def new + end + + def create + @task_request.attributes = task_request_params + + if @task_request.save + log_create_analytics + redirect_to [@project, :task_requests], notice: t(".notice") + else + render :new + end + end + + def show + end + + def edit + end + + def update + if @task_request.update(task_request_params) + redirect_to [@project, :task_requests], notice: t(".notice") + else + render :edit + end + end + + def cancel + @task_request.cancelled! + redirect_to [@project, :task_requests], notice: t(".notice") + end + + private + + def task_request_params + params.require(:task_request).permit(:description, :deadline, :time_allowed, :additional_notes, files: []) + end + + def set_project + @project = policy_scope(Project).find(params[:project_id]) + end + + def set_task_request + @task_request = authorize policy_scope(TaskRequest).find(params[:id]) + end + + def task_requests + authorize policy_scope(@project.task_requests) + end + + def build_task_request + @task_request = authorize @project.task_requests.build + end + + def log_create_analytics + TrackAnalyticsJob.perform_later(Current.user, Current.account, :track_create_task_request, user_agent: request.user_agent, user_ip: request.remote_ip) + end +end diff --git a/app/controllers/zoom_notifications_controller.rb b/app/controllers/zoom_notifications_controller.rb index 9006c04..0550250 100644 --- a/app/controllers/zoom_notifications_controller.rb +++ b/app/controllers/zoom_notifications_controller.rb @@ -13,11 +13,8 @@ class ZoomNotificationsController < ApplicationController @zoom_meeting.started! when 'meeting.ended' @zoom_meeting.ended! - when 'recording.completed' - recording = notification.dig(:payload, :object, :recording_files).first - AttachRecordingToZoomMeetingJob.perform_later(@zoom_meeting, recording, notification['download_token']) else - Rails.logger.info notification_event + Rails.logger.info notification_type Rails.logger.info notification end @@ -26,20 +23,16 @@ class ZoomNotificationsController < ApplicationController private - def notification - params.to_unsafe_h - end - def notification_event - notification.dig(:event) + params.dig(:event) end def notification_meeting_id - notification.dig(:payload, :object, :id) + params.dig(:payload, :object, :id) end def notification_host_id - notification.dig(:payload, :object, :host_id) + params.dig(:payload, :object, :host_id) end def set_zoom_meeting diff --git a/app/helpers/contact_info_helper.rb b/app/helpers/contact_info_helper.rb index b79dd07..77e4def 100644 --- a/app/helpers/contact_info_helper.rb +++ b/app/helpers/contact_info_helper.rb @@ -41,7 +41,7 @@ module ContactInfoHelper end def email_abbr(email) - content_tag(:abbr, "E: ", title: "Email") + mail_to(email) + content_tag(:abbr, "E: ", title: "Email") + email end def phone_abbr(phone) diff --git a/app/jobs/attach_recording_to_zoom_meeting_job.rb b/app/jobs/attach_recording_to_zoom_meeting_job.rb deleted file mode 100644 index 2a3017e..0000000 --- a/app/jobs/attach_recording_to_zoom_meeting_job.rb +++ /dev/null @@ -1,26 +0,0 @@ -require 'zoom_gateway' -class AttachRecordingToZoomMeetingJob < ApplicationJob - queue_as :default - - def perform(zoom_meeting, recording_hash, download_token) - download_url = "#{recording_hash['download_url']}?access_token=#{download_token}" - file = URI.open(download_url) - if zoom_meeting.recording.attach(io: file, filename: file_name(zoom_meeting, recording_hash), content_type: 'video/mp4') - # Temorarily disabling notifications - # if zoom_meeting.project.present? - # ProjectsChannel.conference_recording_ready(zoom_meeting.project, zoom_meeting.recording) - # end - - gateway = ZoomGateway.new - gateway.delete_recording(zoom_meeting.api_meeting_id, recording_hash['id']) - end - end - - private - - def file_name(zoom_meeting, recording_hash) - start = recording_hash['recording_start'].to_datetime - prefix = zoom_meeting.project.present? ? "#{zoom_meeting.project.name}_" : '' - "#{prefix}video_conference_date#{start.strftime("%Y-%m-%d")}_Time_#{start.strftime("%T")}.mp4" - end -end diff --git a/app/jobs/match_appearance_releases_job.rb b/app/jobs/match_appearance_releases_job.rb deleted file mode 100644 index e087277..0000000 --- a/app/jobs/match_appearance_releases_job.rb +++ /dev/null @@ -1,75 +0,0 @@ -# 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 - # BrayniacAI::QrMatching.enable_logging - # response = BrayniacAI::QrMatching.create! payload - - matches = response[:matches] || [] - - handle_matches matches, project - matching_request.destroy - end - - private - - def handle_matches(matches, project) - matches.each do |match| - contract = match[:contracts].blank? ? nil : match[:contracts].first - headshot = match[:headshots].blank? ? nil : match[:headshots].first - identifier = match[:identifier] - - 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 -end diff --git a/app/models/account.rb b/app/models/account.rb index 9590bd6..bd7e51b 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -54,7 +54,7 @@ class Account < ApplicationRecord Download.where(project: projects), User.joins(:project_memberships).where(project_memberships: { project: projects }), Broadcast.where(project: projects), - ZoomMeeting.where(project: projects), + TaskRequest.where(project: projects), self ])).sum(:byte_size).to_f end @@ -79,6 +79,10 @@ class Account < ApplicationRecord plan_uid.to_s == "me_suite" || plan_uid.to_s == "releaseme" end + def assistme_enabled? + plan_uid.to_s == "me_suite" || plan_uid.to_s == "assistme" + end + def plan_name case plan_uid.to_s when "deliverme" @@ -87,6 +91,8 @@ class Account < ApplicationRecord "DirectME" when "releaseme" "ReleaseME" + when "assistme" + "AssistME" when "me_suite" "ME Suite" end diff --git a/app/models/broadcast.rb b/app/models/broadcast.rb index 564fd41..a74f105 100644 --- a/app/models/broadcast.rb +++ b/app/models/broadcast.rb @@ -7,6 +7,8 @@ class Broadcast < ApplicationRecord has_secure_token + scope :order_by_recent, -> { order(created_at: :desc) } + validates :name, presence: true enum status: [:created, :active, :idle] diff --git a/app/models/broadcast_recording.rb b/app/models/broadcast_recording.rb index fe4cd9c..179ea49 100644 --- a/app/models/broadcast_recording.rb +++ b/app/models/broadcast_recording.rb @@ -1,6 +1,8 @@ class BroadcastRecording < ApplicationRecord belongs_to :broadcast + scope :order_by_recent, -> { order(created_at: :desc) } + delegate :name, to: :broadcast, prefix: :broadcast validates :asset_uid, uniqueness: true diff --git a/app/models/concerns/attachable.rb b/app/models/concerns/attachable.rb deleted file mode 100644 index 7d4860a..0000000 --- a/app/models/concerns/attachable.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -module Attachable - extend ActiveSupport::Concern - - included do - has_many_attached :attachments - end -end diff --git a/app/models/concerns/releasable.rb b/app/models/concerns/releasable.rb index 08ccf4a..3d024e4 100644 --- a/app/models/concerns/releasable.rb +++ b/app/models/concerns/releasable.rb @@ -6,6 +6,8 @@ module Releasable included do belongs_to :project, touch: true belongs_to :contract_template, optional: true + + scope :order_by_recent, -> { order(created_at: :desc) } end def release_number diff --git a/app/models/headshot_collection.rb b/app/models/headshot_collection.rb index e0612e5..6a5c94f 100644 --- a/app/models/headshot_collection.rb +++ b/app/models/headshot_collection.rb @@ -5,7 +5,7 @@ class HeadshotCollection def self.for_project(project) appearance_releases_with_photo = project.appearance_releases.with_person_photo - new(project.headshot_collection_uid, appearance_releases_with_photo + project.talent_releases) + new(project.id, appearance_releases_with_photo + project.talent_releases) end def initialize(collection_uid, releasables) @@ -23,7 +23,7 @@ class HeadshotCollection collection_uid: collection_uid.to_s, bucket_name: aws_bucket_name, ids_to_images: map_ids_to_images, - }.reject { |_, v| v.blank? } + } end private diff --git a/app/models/matching_request.rb b/app/models/matching_request.rb deleted file mode 100644 index c719d57..0000000 --- a/app/models/matching_request.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -class MatchingRequest < ApplicationRecord - include Attachable - - belongs_to :project -end diff --git a/app/models/note.rb b/app/models/note.rb index 062b329..927c2a9 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -4,4 +4,6 @@ class Note < ApplicationRecord belongs_to :notable, polymorphic: true validates :content, presence: true + + scope :order_by_recent, -> { order(created_at: :desc) } end diff --git a/app/models/project.rb b/app/models/project.rb index 29ab01c..1d9e43e 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -22,6 +22,7 @@ class Project < ApplicationRecord has_many :downloads, dependent: :destroy has_many :broadcasts, dependent: :destroy has_many :zoom_meetings, dependent: :destroy + has_many :task_requests, dependent: :destroy accepts_nested_attributes_for :project_memberships diff --git a/app/models/task_request.rb b/app/models/task_request.rb new file mode 100644 index 0000000..458e86a --- /dev/null +++ b/app/models/task_request.rb @@ -0,0 +1,8 @@ +class TaskRequest < ApplicationRecord + belongs_to :project + has_many_attached :files + + enum status: [:pending, :completed, :cancelled] + + scope :order_by_recent, -> { order(created_at: :desc) } +end diff --git a/app/models/zoom_meeting.rb b/app/models/zoom_meeting.rb index dadf30f..deb3730 100644 --- a/app/models/zoom_meeting.rb +++ b/app/models/zoom_meeting.rb @@ -4,9 +4,6 @@ class ZoomMeeting < ApplicationRecord belongs_to :project, optional: true belongs_to :zoom_user - has_one_attached :recording - validates :recording, content_type: ['video/mp4'] - enum status: [:created, :started, :ended] after_create :create_api_meeting, if: -> { api_meeting_id.nil? } diff --git a/app/models/zoom_user.rb b/app/models/zoom_user.rb index 6185ea1..823d6e9 100644 --- a/app/models/zoom_user.rb +++ b/app/models/zoom_user.rb @@ -1,4 +1,3 @@ -require 'zoom_gateway' class ZoomUser < ApplicationRecord has_many :zoom_meetings, dependent: :nullify diff --git a/app/policies/contract_template_policy.rb b/app/policies/contract_template_policy.rb index e6bb8d1..5c05abf 100644 --- a/app/policies/contract_template_policy.rb +++ b/app/policies/contract_template_policy.rb @@ -3,8 +3,10 @@ class ContractTemplatePolicy < ApplicationPolicy def resolve if user.account_manager? scope.left_outer_joins(:project).where(projects: {account: user.account}) - else + elsif user.manager? scope.left_outer_joins(project: :project_memberships).where(project_memberships: { user_id: user.id }) + else + scope.none end end end @@ -12,9 +14,8 @@ class ContractTemplatePolicy < ApplicationPolicy def create? user.manager? || user.account_manager? end - def show? - true + create? end def destroy? diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index fafbb5f..32c0c70 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -36,4 +36,8 @@ class ProjectPolicy < ApplicationPolicy def show_downloads? show? end + + def show_task_results? + show? + end end diff --git a/app/policies/task_request_policy.rb b/app/policies/task_request_policy.rb new file mode 100644 index 0000000..4845b5d --- /dev/null +++ b/app/policies/task_request_policy.rb @@ -0,0 +1,25 @@ +class TaskRequestPolicy < ApplicationPolicy + def index? + true + end + + def show? + true + end + + def create? + true + end + + def destroy? + true + end + + def update? + true + end + + def cancel? + true + end +end diff --git a/app/telemetry/analytics.rb b/app/telemetry/analytics.rb index 1f95495..11b2db0 100644 --- a/app/telemetry/analytics.rb +++ b/app/telemetry/analytics.rb @@ -151,6 +151,24 @@ class Analytics ) end end + + def track_create_task_request(user_agent:, user_ip:) + if analytics_enabled? + identify + track( + { + user_id: user.id, + event: "Task request created", + properties: { + account: account.try(:name), + account_id: account.try(:id), + user_agent: user_agent, + ip: user_ip, + }, + } + ) + end + end private diff --git a/app/views/admin/application/_side_nav.html.erb b/app/views/admin/application/_side_nav.html.erb index 00ea144..23ca54c 100644 --- a/app/views/admin/application/_side_nav.html.erb +++ b/app/views/admin/application/_side_nav.html.erb @@ -7,6 +7,9 @@ + diff --git a/app/views/admin/task_requests/_form.html.erb b/app/views/admin/task_requests/_form.html.erb new file mode 100644 index 0000000..34dde41 --- /dev/null +++ b/app/views/admin/task_requests/_form.html.erb @@ -0,0 +1,13 @@ +<%= errors_summary_for task_request %> + +<%= bootstrap_form_with model: model, local: true do |form| %> + <%= form.select :status, options_for_select(TaskRequest.statuses.except(:cancelled).keys, task_request.status), { class: "form-control" } %> + <%= form.text_field :deliverable_url %> + +
+ <%= link_to t("shared.cancel"), [:admin, :task_requests], class: "col-3 text-reset" %> +
+ <%= form.submit class: class_string("btn btn-block", ["btn-success", "btn-primary"] => task_request.new_record?), data: { disable_with: t("shared.disable_with") } %> +
+
+<% end %> diff --git a/app/views/admin/task_requests/_task_request.html.erb b/app/views/admin/task_requests/_task_request.html.erb new file mode 100644 index 0000000..cdc777e --- /dev/null +++ b/app/views/admin/task_requests/_task_request.html.erb @@ -0,0 +1,25 @@ + + + <%= task_request.id %> + + + <%= task_request.created_at.strftime("%D") %> + + + <%= task_request.deadline.try(:strftime, '%D') %> + + + <%= task_request.time_allowed %> + + + <%= task_request.status.titleize %> + + +
+ <%= button_tag "Manage", class: "btn btn-light btn-sm dropdown-toggle border", data: { toggle: "dropdown", boundary: "window" }, aria: { haspopup: true, expanded: false } %> + +
+ + diff --git a/app/views/admin/task_requests/edit.html.erb b/app/views/admin/task_requests/edit.html.erb new file mode 100644 index 0000000..acacdf1 --- /dev/null +++ b/app/views/admin/task_requests/edit.html.erb @@ -0,0 +1,6 @@ +
+ <%= card_header text: "Edit Task Request", close_action_path: [:admin, :task_requests] %> +
+ <%= render "form", model: [:admin, @task_request], task_request: @task_request %> +
+
diff --git a/app/views/admin/task_requests/index.html.erb b/app/views/admin/task_requests/index.html.erb new file mode 100644 index 0000000..bc445da --- /dev/null +++ b/app/views/admin/task_requests/index.html.erb @@ -0,0 +1,23 @@ +
+ + + + + + + + + + + + + <% if @task_requests.any? %> + <%= render @task_requests %> + <% else %> + + + + <% end %> + +
Task IDCreated OnDeadlineTime AllowedStatus
<%= t(".empty") %>
+
diff --git a/app/views/appearance_releases/_appearance_release.html.erb b/app/views/appearance_releases/_appearance_release.html.erb index 9779356..b73f3b7 100644 --- a/app/views/appearance_releases/_appearance_release.html.erb +++ b/app/views/appearance_releases/_appearance_release.html.erb @@ -2,7 +2,7 @@ <%= check_box_tag "appearance_release_ids[]", appearance_release.id, false %> <% if appearance_release.photo.attached? %> - <%= image_tag medium_variant(appearance_release.photo) %> + <%= image_tag medium_variant(appearance_release.photo), class: "img-fluid" %> <% else %> <%= fa_icon("warning", text: t(".no_photos"), class: "text-danger") %> <% end %> @@ -11,11 +11,10 @@ <%= appearance_release.name %> - <%= contact_info( - address: appearance_release.person_address, - phone: appearance_release.person_phone, - email: appearance_release.person_email - ) %> + <%= number_to_phone appearance_release.person_phone %> + + + <%= mail_to appearance_release.person_email %> <%= notes_preview appearance_release.notes.order_by_recent %> diff --git a/app/views/appearance_releases/_form.html.erb b/app/views/appearance_releases/_form.html.erb index 0f9d150..fd392a8 100644 --- a/app/views/appearance_releases/_form.html.erb +++ b/app/views/appearance_releases/_form.html.erb @@ -52,7 +52,7 @@ <% end %>
<%= form.hidden_field :person_photo, value: form.object.person_photo.signed_id if appearance_release.person_photo.attached?%> - <%= form.file_field :person_photo, hide_label: true, data: { ujs_target: "person-photo-input" }, help: "PNG or JPG only", accept: appearance_release.class.face_photo_acceptable_content_types.join(",") %> + <%= form.file_field :person_photo, wrapper_class: "required", hide_label: true, required: !appearance_release.person_photo.attached?, data: { ujs_target: "person-photo-input" }, help: "PNG or JPG only", accept: appearance_release.class.face_photo_acceptable_content_types.join(",") %>
diff --git a/app/views/appearance_releases/index.html.erb b/app/views/appearance_releases/index.html.erb index f496ca2..5515977 100644 --- a/app/views/appearance_releases/index.html.erb +++ b/app/views/appearance_releases/index.html.erb @@ -46,7 +46,8 @@ <%= check_box_tag "appearance_release_ids[]", false, false %> <%= AppearanceRelease.human_attribute_name(:person_name) %> - <%= AppearanceRelease.human_attribute_name(:contact_info) %> + <%= AppearanceRelease.human_attribute_name(:person_phone) %> + <%= AppearanceRelease.human_attribute_name(:person_email) %> <%= t(".table_headers.notes") %> <%= t(".table_headers.tags") %> <%= t(".table_headers.signed_at") %> diff --git a/app/views/application/_sidebar.html.erb b/app/views/application/_sidebar.html.erb index a78879e..5e0d200 100644 --- a/app/views/application/_sidebar.html.erb +++ b/app/views/application/_sidebar.html.erb @@ -35,6 +35,12 @@ <%= product_wordmark :deliver_me, class: class_string("d-inline-block", "disabled" => !Current.account.deliverme_enabled?) %> <% end %> +
diff --git a/app/views/broadcasts/_file.html.erb b/app/views/broadcasts/_file.html.erb index b0c90a8..ede860f 100644 --- a/app/views/broadcasts/_file.html.erb +++ b/app/views/broadcasts/_file.html.erb @@ -1,4 +1,4 @@ -
  • +
  • <% if file.variable? %> <%= link_to image_tag(file.variant(resize_and_pad: [300, 300, background: "#F7F8F9"]), class: "bg-light img-thumbnail img-fluid"), file, target: "_blank" %> <% else %> diff --git a/app/views/broadcasts/show.html.erb b/app/views/broadcasts/show.html.erb index 561ce71..c136236 100644 --- a/app/views/broadcasts/show.html.erb +++ b/app/views/broadcasts/show.html.erb @@ -126,6 +126,7 @@ <%= render partial: "broadcasts/file_form", locals: { model: [@project, @broadcast] } %> <% end %> +

    <%= fa_icon("warning", text: "You may need to refresh the page to see new files uploaded by other team members") %>

    - <%= will_paginate(@files, params: { active_tab: 'files' }) if @files.present? %> + <%= will_paginate(@files, params: { active_tab: 'files' }) if @files.present? %>
    diff --git a/app/views/broadcasts/update.js.erb b/app/views/broadcasts/update.js.erb index 92d4bbb..8345207 100644 --- a/app/views/broadcasts/update.js.erb +++ b/app/views/broadcasts/update.js.erb @@ -1,9 +1,5 @@ $("#broadcast_file_form").html("<%= j render(partial: "broadcasts/file_form", locals: { model: [@project, @broadcast] }) %>"); - -var file_id = "#<%= dom_id(@files.first) %>" -if ($("#broadcast_file_list").has(file_id).length == 0) { - $("#broadcast_file_list").html("<%= j render(partial: "broadcasts/file", collection: @files) %>"); - $("#broadcast_files_pagination").html("<%= j will_paginate(@files) %>"); -} +$("#broadcast_file_list").html("<%= j render(partial: "broadcasts/file", collection: @files) %>"); +$("#broadcast_files_pagination").html("<%= j will_paginate(@files) %>"); bsCustomFileInput.init(); \ No newline at end of file diff --git a/app/views/projects/show.html.erb b/app/views/projects/show.html.erb index 6aac1c8..1a95b20 100644 --- a/app/views/projects/show.html.erb +++ b/app/views/projects/show.html.erb @@ -16,6 +16,11 @@ <%= link_to t("projects.show.downloads"), [@project, :downloads], class: "text-decoration-none text-reset stretched-link" %> <% end %> <% end %> + <% if policy(Project).show_task_results? %> + <%= render "folder_card" do %> + <%= link_to t("projects.show.task_requests"), [@project, :task_requests, completed_only: true], class: "text-decoration-none text-reset stretched-link" %> + <% end %> + <% end %>
    diff --git a/app/views/public/broadcasts/update.js.erb b/app/views/public/broadcasts/update.js.erb index 5497ff5..fa75fc2 100644 --- a/app/views/public/broadcasts/update.js.erb +++ b/app/views/public/broadcasts/update.js.erb @@ -1,9 +1,5 @@ $("#broadcast_file_form").html("<%= j render(partial: "public/broadcasts/file_form", locals: { model: [@project, @broadcast], token: @broadcast.token }) %>"); - -var file_id = "#<%= dom_id(@files.first) %>" -if ($("#broadcast_file_list").has(file_id).length == 0) { - $("#broadcast_file_list").html("<%= j render(partial: "broadcasts/file", collection: @files) %>"); - $("#broadcast_files_pagination").html("<%= j will_paginate(@files) %>"); -} +$("#broadcast_file_list").html("<%= j render(partial: "broadcasts/file", collection: @files) %>"); +$("#broadcast_files_pagination").html("<%= j will_paginate(@files) %>"); bsCustomFileInput.init(); \ No newline at end of file diff --git a/app/views/task_requests/_form.html.erb b/app/views/task_requests/_form.html.erb new file mode 100644 index 0000000..64b7645 --- /dev/null +++ b/app/views/task_requests/_form.html.erb @@ -0,0 +1,34 @@ +<%= errors_summary_for task_request %> + +<%= bootstrap_form_with model: model, local: true do |form| %> + <%= form.text_area :description %> + <%= form.text_field :deadline, class: "datepicker-control" %> + <%= form.text_field :time_allowed %> + <%= form.text_area :additional_notes %> + <%= field_set_tag content_tag(:span, "Files", class: "h6 text-muted text-uppercase") do %> +
    + <%= form.label :files %> + <%= form.file_field :files, disable: true, direct_upload: true, multiple: true, id: "task_request_files", hide_label: true %> + <% task_request.files.each do |file| %> + <% unless file.persisted? %> + <%= hidden_field_tag "#{task_request.model_name.param_key}[files][]", file.signed_id %> + <% end %> + <% end %> +
    + +
    + <% end %> + +
    + <%= link_to t("shared.cancel"), [project, :task_requests], class: "col-3 text-reset" %> +
    + <%= form.submit class: class_string("btn btn-block", ["btn-success", "btn-primary"] => task_request.new_record?), data: { disable_with: t("shared.disable_with") } %> +
    +
    +<% end %> diff --git a/app/views/task_requests/_task_request.html.erb b/app/views/task_requests/_task_request.html.erb new file mode 100644 index 0000000..9d626ab --- /dev/null +++ b/app/views/task_requests/_task_request.html.erb @@ -0,0 +1,35 @@ + + + <%= task_request.created_at.strftime('%D') %> + + + <%= task_request.deadline.try(:strftime, '%D') %> + + + <%= task_request.time_allowed %> + + + <%= task_request.status.titleize %> + + <% if params[:completed_only] %> + + <%= task_request.deliverable_url %> + + <% end %> + +
    + <%= button_tag t(".actions.manage"), class: "btn btn-light btn-sm dropdown-toggle border", data: { toggle: "dropdown", boundary: "window" }, aria: { haspopup: true, expanded: false } %> + +
    + + diff --git a/app/views/task_requests/edit.html.erb b/app/views/task_requests/edit.html.erb new file mode 100644 index 0000000..ef1c172 --- /dev/null +++ b/app/views/task_requests/edit.html.erb @@ -0,0 +1,6 @@ +
    + <%= card_header text: t(".heading"), close_action_path: [@project, :task_requests] %> +
    + <%= render "form", model: [@project, @task_request], task_request: @task_request, project: @project %> +
    +
    \ No newline at end of file diff --git a/app/views/task_requests/index.html.erb b/app/views/task_requests/index.html.erb new file mode 100644 index 0000000..18804b1 --- /dev/null +++ b/app/views/task_requests/index.html.erb @@ -0,0 +1,41 @@ +<%= product_wordmark :assist_me, class: "small mb-3" %> + +
    +
    +
    + <% if policy(TaskRequest).new? %> + <%= link_to fa_icon("plus", text: t(".actions.new")), [:new, @project, :task_request], class: "btn btn-primary mb-2" %> + <% end %> +
    +
    +
    + +
    + + + + + + + + <% if params[:completed_only] %> + + <% end %> + + + + + <% if @task_requests.any? %> + <%= render @task_requests %> + <% else %> + + + + <% end %> + +
    <%= t(".table_headers.task_request_created_on") %><%= t(".table_headers.task_request_deadline") %><%= t(".table_headers.task_request_time_allowed") %><%= t(".table_headers.task_request_status") %><%= t(".table_headers.task_request_results") %>
    <%= t(".empty") %>
    +
    + +
    + <%= will_paginate @task_requests %> +
    diff --git a/app/views/task_requests/new.html.erb b/app/views/task_requests/new.html.erb new file mode 100644 index 0000000..5dae236 --- /dev/null +++ b/app/views/task_requests/new.html.erb @@ -0,0 +1,6 @@ +
    + <%= card_header text: t(".heading"), close_action_path: [@project, :task_requests] %> +
    + <%= render "form", model: [@project, @task_request], task_request: @task_request, project: @project %> +
    +
    diff --git a/app/views/task_requests/show.html.erb b/app/views/task_requests/show.html.erb new file mode 100644 index 0000000..727be89 --- /dev/null +++ b/app/views/task_requests/show.html.erb @@ -0,0 +1,18 @@ +
    + <%= description_list_pair_for @task_request, :description, append: ":" %> + <%= description_list_pair_for @task_request, :created_at, append: ":" %> + <%= description_list_pair_for @task_request, :deadline, append: ":" %> + <%= description_list_pair_for @task_request, :time_allowed, append: ":" %> + <%= description_list_pair_for @task_request, :additional_notes, append: ":" %> + <%= description_list_pair_for @task_request, :status, append: ":" %> +
    Files:
    +
    + <% if @task_request.files.present? %> + <% @task_request.files.each do |file| %> + <%= file.filename %>
    + <% end %> + <% else %> + "No files attached." + <% end %> +
    +
    diff --git a/config/locales/en.yml b/config/locales/en.yml index b5de0df..e6890be 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -93,6 +93,11 @@ en: application: header: sign_out: Sign Out + task_requests: + index: + empty: Task requests will appear here + update: + notice: The task request has been updated successfully users: create: notice: The user was created @@ -112,7 +117,6 @@ 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 @@ -142,8 +146,6 @@ 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 @@ -642,7 +644,6 @@ en: create: notice: Check your email for password reset instructions edit: - notice: This password reset URL has expired or is invalid. Try again by clicking "Forgot your password?" below submit: Save New Password title: Password Reset new: @@ -733,6 +734,7 @@ en: music_release: Music Releases (%{count}) report: Reports talent_release: Talent Releases (%{count}) + task_requests: Tasks public: acquired_media_releases: new: @@ -873,6 +875,7 @@ en: title: Sign In shared: ago: ago + assist_me: Assist back: Back cancel: Cancel clear: Clear @@ -933,6 +936,31 @@ en: manage: Manage update: notice: The talent release has been updated + task_requests: + cancel: + notice: Task has been cancelled successfully. + create: + notice: Task request created succussfully. + edit: + heading: + Edit Task Request + index: + actions: + new: Create Task Request + empty: Task requests will appear here. + table_headers: + task_request_created_on: Created On + task_request_deadline: Deadline + task_request_results: Task Results + task_request_status: Status + task_request_time_allowed: Time Allowed + new: + heading: New Task Request + task_request: + actions: + manage: Manage + update: + notice: Task request updated successfully. user_mailer: existing_account: subject: You've been added as a ME Suite Account Manager diff --git a/config/locales/es.yml b/config/locales/es.yml index 7ba33af..13abd12 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -25,7 +25,6 @@ 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: @@ -38,8 +37,6 @@ 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/config/routes.rb b/config/routes.rb index a844490..15c008f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -31,6 +31,7 @@ Rails.application.routes.draw do resources :users, only: [:index, :new, :create, :edit, :update, :destroy] do resource :masquerade, only: :create end + resources :task_requests, only: [:index, :edit, :update] root to: "accounts#index", as: :signed_in_root end @@ -97,6 +98,11 @@ Rails.application.routes.draw do delete :destroy_file end end + resources :task_requests, except: :destroy do + member do + post :cancel + end + end end resource :profile, only: [:show, :update] resources :videos, only: [] do diff --git a/db/migrate/20200430151828_create_matching_requests.rb b/db/migrate/20200430151828_create_matching_requests.rb deleted file mode 100644 index cc4e4cb..0000000 --- a/db/migrate/20200430151828_create_matching_requests.rb +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index a1ff2fa..0000000 --- a/db/migrate/20200430190412_add_identifier_to_appearance_releases.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddIdentifierToAppearanceReleases < ActiveRecord::Migration[6.0] - def change - add_column :appearance_releases, :identifier, :string, default: nil - end -end diff --git a/db/migrate/20200518200245_create_task_requests.rb b/db/migrate/20200518200245_create_task_requests.rb new file mode 100644 index 0000000..e169ea5 --- /dev/null +++ b/db/migrate/20200518200245_create_task_requests.rb @@ -0,0 +1,14 @@ +class CreateTaskRequests < ActiveRecord::Migration[6.0] + def change + create_table :task_requests do |t| + t.references :project + t.text :description + t.datetime :deadline + t.string :time_allowed + t.text :additional_notes + t.integer :status, default: 0 + + t.timestamps + end + end +end diff --git a/db/migrate/20200519191908_add_deliverable_url_to_task_requests.rb b/db/migrate/20200519191908_add_deliverable_url_to_task_requests.rb new file mode 100644 index 0000000..2c127f3 --- /dev/null +++ b/db/migrate/20200519191908_add_deliverable_url_to_task_requests.rb @@ -0,0 +1,5 @@ +class AddDeliverableUrlToTaskRequests < ActiveRecord::Migration[6.0] + def change + add_column :task_requests, :deliverable_url, :string + end +end diff --git a/db/structure.sql b/db/structure.sql index b748069..96c7870 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -37,15 +37,15 @@ CREATE EXTENSION IF NOT EXISTS pg_trgm WITH SCHEMA public; COMMENT ON EXTENSION pg_trgm IS 'text similarity measurement and index searching based on trigrams'; --- --- Name: pg_search_dmetaphone(text); Type: FUNCTION; Schema: public; Owner: - --- - -CREATE FUNCTION public.pg_search_dmetaphone(text) RETURNS text - LANGUAGE sql IMMUTABLE STRICT - AS $_$ - SELECT array_to_string(ARRAY(SELECT dmetaphone(unnest(regexp_split_to_array($1, E'\\s+')))), ' ') -$_$; +-- +-- Name: pg_search_dmetaphone(text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.pg_search_dmetaphone(text) RETURNS text + LANGUAGE sql IMMUTABLE STRICT + AS $_$ + SELECT array_to_string(ARRAY(SELECT dmetaphone(unnest(regexp_split_to_array($1, E'\\s+')))), ' ') +$_$; SET default_tablespace = ''; @@ -1326,6 +1326,43 @@ CREATE SEQUENCE public.talent_releases_id_seq ALTER SEQUENCE public.talent_releases_id_seq OWNED BY public.talent_releases.id; +-- +-- Name: task_requests; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.task_requests ( + id bigint NOT NULL, + project_id bigint, + description text, + deadline timestamp without time zone, + time_allowed character varying, + additional_notes text, + status integer DEFAULT 0, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL, + deliverable_url character varying +); + + +-- +-- Name: task_requests_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.task_requests_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: task_requests_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.task_requests_id_seq OWNED BY public.task_requests.id; + + -- -- Name: terms; Type: TABLE; Schema: public; Owner: - -- @@ -1559,9 +1596,9 @@ CREATE TABLE public.zoom_meetings ( created_at timestamp(6) without time zone NOT NULL, updated_at timestamp(6) without time zone NOT NULL, broadcast_id bigint, + status integer DEFAULT 0, zoom_user_id bigint, - project_id bigint, - status integer DEFAULT 0 + project_id bigint ); @@ -1832,6 +1869,13 @@ ALTER TABLE ONLY public.tags ALTER COLUMN id SET DEFAULT nextval('public.tags_id ALTER TABLE ONLY public.talent_releases ALTER COLUMN id SET DEFAULT nextval('public.talent_releases_id_seq'::regclass); +-- +-- Name: task_requests id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.task_requests ALTER COLUMN id SET DEFAULT nextval('public.task_requests_id_seq'::regclass); + + -- -- Name: terms id; Type: DEFAULT; Schema: public; Owner: - -- @@ -2160,6 +2204,14 @@ ALTER TABLE ONLY public.talent_releases ADD CONSTRAINT talent_releases_pkey PRIMARY KEY (id); +-- +-- Name: task_requests task_requests_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.task_requests + ADD CONSTRAINT task_requests_pkey PRIMARY KEY (id); + + -- -- Name: terms terms_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -2763,6 +2815,13 @@ CREATE INDEX index_talent_releases_on_term_id ON public.talent_releases USING bt CREATE INDEX index_talent_releases_on_territory_id ON public.talent_releases USING btree (territory_id); +-- +-- Name: index_task_requests_on_project_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_task_requests_on_project_id ON public.task_requests USING btree (project_id); + + -- -- Name: index_unreleased_appearances_on_video_id; Type: INDEX; Schema: public; Owner: - -- @@ -3499,6 +3558,6 @@ INSERT INTO "schema_migrations" (version) VALUES ('20200427073429'), ('20200428091105'), ('20200507110804'), -('20200512161738'); - - +('20200512161738'), +('20200518200245'), +('20200519191908'); diff --git a/lib/brayniac_ai.rb b/lib/brayniac_ai.rb index 7e02af0..ce9bc72 100644 --- a/lib/brayniac_ai.rb +++ b/lib/brayniac_ai.rb @@ -3,7 +3,6 @@ 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 deleted file mode 100644 index 4c19339..0000000 --- a/lib/brayniac_ai/appearance_release_matching.rb +++ /dev/null @@ -1,71 +0,0 @@ -# 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 = [] - - # Use first file for the error entry - first_attachment = attachments.shift - - if first_attachment.present? - matches << { - headshots: [], - contracts: [], - unknowns: [first_attachment], - identifier: '' - } - 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 - headshots = image.present? ? [image] : [] - contracts = pdf.present? ? [pdf] : [] - - matches << { - headshots: headshots, - contracts: contracts, - unknowns: [], - identifier: '' - } - end - - { - request_id: request_data[:request_id], - matches: matches - } - end - end - end -end diff --git a/lib/zoom_gateway.rb b/lib/zoom_gateway.rb index 00cd249..0e947fc 100644 --- a/lib/zoom_gateway.rb +++ b/lib/zoom_gateway.rb @@ -23,10 +23,6 @@ class ZoomGateway "#{self.USER_TYPE_NAME}-directme-host" end - def enable_recordings? - ENV['ZOOM_ENABLE_RECORDINGS'] == '1' - end - def apply_limits? self.USER_TYPE_NAME == 'pro' end @@ -37,14 +33,12 @@ class ZoomGateway end def create_meeting(host_id, **kwargs) - recording_type = self.class.enable_recordings? ? 'cloud' : 'none' meeting = @client.meeting_create({ user_id: host_id, topic: kwargs[:topic], type: 1, # Instant meeting settings: { host_video: true, - participant_video: true, - auto_recording: recording_type, + participant_video: true } }) meeting["id"] end @@ -84,10 +78,6 @@ class ZoomGateway @client.user_delete(id: host_id) end - def delete_recording(meeting_id, recording_id) - @client.recording_delete(meeting_id: meeting_id, recording_id: recording_id) - end - private def parse_zoom_error(error) diff --git a/spec/channels/projects_channel_spec.rb b/spec/channels/projects_channel_spec.rb index 9a8aea5..92dbfc1 100644 --- a/spec/channels/projects_channel_spec.rb +++ b/spec/channels/projects_channel_spec.rb @@ -24,20 +24,4 @@ RSpec.describe ProjectsChannel, type: :channel do }.to have_broadcasted_to(project).with(event: "video_status_update", content: content) end end - - describe ".conference_recording_ready" do - it "broadcasts to the project channel" do - recording_file = Rack::Test::UploadedFile.new(Rails.root.join("spec", "fixtures", "files", "video_file.mp4"), "video/mp4") - zoom_meeting = create(:zoom_meeting, project: project, recording: recording_file) - video_url = 'http://url.to.video/video.mp4' - allow(zoom_meeting.recording).to receive(:service_url).and_return(video_url) - - expect { - ProjectsChannel.conference_recording_ready(project, zoom_meeting.recording) - }.to have_broadcasted_to(project).with { |data| - expect(data['content']).to include(video_url) - expect(data['event']).to eq('conference_recording_ready') - } - end - end end diff --git a/spec/controllers/admin/task_requests_controller_spec.rb b/spec/controllers/admin/task_requests_controller_spec.rb new file mode 100644 index 0000000..0d0f1f1 --- /dev/null +++ b/spec/controllers/admin/task_requests_controller_spec.rb @@ -0,0 +1,68 @@ +require "rails_helper" + +RSpec.describe Admin::TaskRequestsController, type: :controller do + render_views + + let!(:current_user) { create(:user, :admin) } + + before do + sign_in(current_user) + end + + describe "#index" do + it "returns a successful response" do + get :index + + expect(response).to be_successful + end + end + + describe "#edit" do + let(:task_request) { create(:task_request) } + + it "returns a successful response" do + get :edit, params: { id: task_request } + + expect(response).to be_successful + end + + it "assigns task request" do + get :edit, params: { id: task_request } + + expect(assigns(:task_request)).to eq task_request + end + end + + describe "#update" do + let(:task_request) { create(:task_request) } + + it "redirects to task requests page" do + patch :update, params: { id: task_request, task_request: update_params } + + expect(response).to be_redirect + expect(response).to redirect_to admin_task_requests_path + end + + it "sets a flash notice" do + patch :update, params: { id: task_request, task_request: update_params } + + expect(flash.notice).to eq "The task request has been updated successfully" + end + + it "updates task request" do + patch :update, params: { id: task_request, task_request: update_params } + + expect(task_request.reload.status).to eq("completed") + expect(task_request.reload.deliverable_url).to eq("example.com/deliverables") + end + end + + private + + def update_params + { + status: "completed", + deliverable_url: "example.com/deliverables" + } + end +end diff --git a/spec/controllers/appearance_releases_controller_spec.rb b/spec/controllers/appearance_releases_controller_spec.rb index e7bc676..f926def 100644 --- a/spec/controllers/appearance_releases_controller_spec.rb +++ b/spec/controllers/appearance_releases_controller_spec.rb @@ -26,7 +26,8 @@ RSpec.describe AppearanceReleasesController, tye: :controller do get :index, params: { project_id: project } expect(response.body).to have_content "John Doe" - expect(response.body).to have_content "P: 5551234567E: jane.doe@test.com" + expect(response.body).to have_content "555-123-4567" + expect(response.body).to have_content "jane.doe@test.com" expect(response.body).to have_content "Some notes here" expect(response.body).to have_button "Import Release" expect(response.body).to have_content "Manage" diff --git a/spec/controllers/password_resets_controller_spec.rb b/spec/controllers/password_resets_controller_spec.rb index b664891..48ebc3f 100644 --- a/spec/controllers/password_resets_controller_spec.rb +++ b/spec/controllers/password_resets_controller_spec.rb @@ -56,16 +56,6 @@ RSpec.describe PasswordResetsController, type: :controller do expect(response.body).to have_content("Password Reset") expect(response.body).to have_selector("input[name='password_reset[password]']") end - - context "when reset token is invalid" do - it "redirects to the sign in page" do - get :edit, params: { id: "bad token" } - - expect(response).to be_redirect - expect(response).to redirect_to(new_session_path) - expect(flash.notice).to be_present - end - end end describe "#update" do diff --git a/spec/controllers/task_requests_controller_spec.rb b/spec/controllers/task_requests_controller_spec.rb new file mode 100644 index 0000000..177059d --- /dev/null +++ b/spec/controllers/task_requests_controller_spec.rb @@ -0,0 +1,130 @@ +require 'rails_helper' + +RSpec.describe TaskRequestsController, type: :controller do + render_views + + let(:user) { create(:user) } + let(:account) { user.primary_account } + let(:project) { create(:project, account: user.primary_account) } + + before do + sign_in user + end + + describe "#index" do + it "responds successfully" do + get :index, params: { project_id: project } + + expect(response).to be_successful + end + + it "renders content" do + create(:task_request, project: project, description: "Another Request") + + get :index, params: { project_id: project } + + expect(response.body).to have_link "Create Task Request" + expect(response.body).to have_content "Pending" + end + + context "when there are many records" do + it "paginates the table" do + create_list(:task_request, 20, project: project) + + get :index, params: { project_id: project } + + expect(response.body).to have_link("2", href: project_task_requests_path(project, page: 2)) + end + end + end + + describe "#new" do + it "responds successfully" do + get :new, params: { project_id: project } + + expect(response).to be_successful + expect(assigns(:task_request)).to be_a_new(TaskRequest) + expect(response).to render_template(:new) + end + end + + describe "#create" do + it "responds with a redirect" do + post :create, params: { project_id: project.id, task_request: task_request_params } + + expect(response).to be_redirect + expect(flash.notice).not_to be_nil + end + + it "does create a new record" do + expect { + post :create, params: { project_id: project.id, task_request: task_request_params } + }.to change(TaskRequest, :count) + end + + it "logs an event" do + expect { + post :create, params: { project_id: project.id, task_request: task_request_params } + }.to have_enqueued_job(TrackAnalyticsJob).with(user, account, :track_create_task_request, user_agent: "Rails Testing", user_ip: "0.0.0.0") + end + end + + describe "#update" do + let!(:task_request) { create(:task_request, project: project, description: "My description" ) } + + it "updates task request" do + patch :update, params: { project_id: project.id, id: task_request.id, task_request: update_params } + + expect(task_request.reload.description).to eq("This is updated description") + end + end + + describe "#show" do + let!(:task_request) { create(:task_request, project: project, description: "Task Request") } + + it "responds successfully" do + get :show, params: { project_id: project.id, id: task_request.id } + + expect(response).to be_successful + expect(assigns(:task_request)).to eq(task_request) + end + + it "renders content" do + get :show, params: { project_id: project.id, id: task_request.id } + + expect(response.body).to have_content "Task Request" + expect(response.body).to have_content "pending" + end + end + + describe "#cancel" do + let!(:task_request) { create(:task_request, project: project, description: "Task to be cancelled") } + + it "responds with redirect" do + post :cancel, params: { project_id: project.id, id: task_request.id } + + expect(response).to be_redirect + expect(response).to redirect_to(project_task_requests_path(project)) + expect(flash.notice).not_to be_nil + end + + it "updates the status to 'cancelled'" do + expect { + post :cancel, params: { project_id: project.id, id: task_request.id } + }.to change { task_request.reload.status }.from("pending").to("cancelled") + end + end + + private + + def task_request_params + attributes = attributes_for(:task_request).except(:status) + files = 2.times.map { Rack::Test::UploadedFile.new(file_fixture("location_photo.png"), "image/png") } + + attributes.merge({ files: files }) + end + + def update_params + { description: "This is updated description" } + end +end diff --git a/spec/controllers/zoom_notifications_controller_spec..rb b/spec/controllers/zoom_notifications_controller_spec..rb index 2686957..c8d48ea 100644 --- a/spec/controllers/zoom_notifications_controller_spec..rb +++ b/spec/controllers/zoom_notifications_controller_spec..rb @@ -69,13 +69,6 @@ RSpec.describe ZoomNotificationsController, type: :controller do expect(zoom_meeting.reload).to be_ended end - - it 'updates the recording when recording complete notification is received' do - expect { - post :create, params: recording_complete - }.to change { zoom_meeting.recording } - end - end end end diff --git a/spec/factories/task_requests.rb b/spec/factories/task_requests.rb new file mode 100644 index 0000000..2b206d8 --- /dev/null +++ b/spec/factories/task_requests.rb @@ -0,0 +1,14 @@ +FactoryBot.define do + factory :task_request do + association :project + deadline { 10.days.from_now } + time_allowed "10 days" + description "Task request" + additional_notes "Additional notes" + status 0 + + trait :with_files do + files { [Rack::Test::UploadedFile.new('spec/fixtures/files/contract.pdf', 'application/pdf')] } + end + end +end diff --git a/spec/features/user_managing_appearance_releases_spec.rb b/spec/features/user_managing_appearance_releases_spec.rb index 98bf17e..28eeb2b 100644 --- a/spec/features/user_managing_appearance_releases_spec.rb +++ b/spec/features/user_managing_appearance_releases_spec.rb @@ -140,7 +140,6 @@ 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 @@ -153,7 +152,6 @@ 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 @@ -166,7 +164,6 @@ 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 diff --git a/spec/jobs/attach_recording_to_zoom_meeting_job_spec.rb b/spec/jobs/attach_recording_to_zoom_meeting_job_spec.rb deleted file mode 100644 index 727bd51..0000000 --- a/spec/jobs/attach_recording_to_zoom_meeting_job_spec.rb +++ /dev/null @@ -1,45 +0,0 @@ -require "rails_helper" - -describe AttachRecordingToZoomMeetingJob do - let(:project) { create(:project) } - let(:zoom_meeting) { create(:zoom_meeting, project: project) } - let(:recording_hash) { {'id' => 'recording-id', 'download_url' => 'http://download.url', 'recording_start' => '2020-05-22 16:33:50 UTC', 'recording_end' => '2020-05-22 17:34:06 UTC'} } - let(:download_token) { 'download_token' } - - before :each do - allow_any_instance_of(ZoomGateway).to receive(:delete_recording) - end - - describe ".perform_now" do - it "downloads the video" do - allow(zoom_meeting.recording).to receive(:attach) - - expect(URI).to receive(:open).with("http://download.url?access_token=download_token") - AttachRecordingToZoomMeetingJob.perform_now zoom_meeting, recording_hash, download_token - end - - it "deletes the recording through the API" do - stub_uri_open - allow(zoom_meeting.recording).to receive(:attach).and_return(true) - - expect_any_instance_of(ZoomGateway).to receive(:delete_recording).with(zoom_meeting.api_meeting_id, 'recording-id') - AttachRecordingToZoomMeetingJob.perform_now zoom_meeting, recording_hash, download_token - end - - it "attaches downloaded video to recording" do - allow(zoom_meeting.recording).to receive(:attach) - stub_uri_open - - AttachRecordingToZoomMeetingJob.perform_now zoom_meeting, recording_hash, download_token - expect(zoom_meeting.recording).to have_received(:attach).with(hash_including(content_type: 'video/mp4')) - end - end - - private - - def stub_uri_open - url = "http://download.url?access_token=download_token" - file = double(:file, read: 'stubbed read') - allow(URI).to receive(:open).with(url).and_return(file) - end -end diff --git a/spec/lib/zoom_gateway_spec.rb b/spec/lib/zoom_gateway_spec.rb index a30887a..0809f37 100644 --- a/spec/lib/zoom_gateway_spec.rb +++ b/spec/lib/zoom_gateway_spec.rb @@ -64,21 +64,6 @@ RSpec.describe ZoomGateway do expect(ZoomGateway.HOST_ROLE).to eq('pro-directme-host') end end - - context '.enable_recordings?' do - it 'is truthy when ZOOM_ENABLE_RECORDINGS is set to 1' do - stub_env_variable('ZOOM_ENABLE_RECORDINGS', '1') - expect(ZoomGateway.enable_recordings?).to be_truthy - end - - it 'is falsey otherwise' do - stub_env_variable('ZOOM_ENABLE_RECORDINGS', '0') - expect(ZoomGateway.enable_recordings?).to be_falsey - - stub_env_variable('ZOOM_ENABLE_RECORDINGS', '2') - expect(ZoomGateway.enable_recordings?).to be_falsey - end - end end describe ".find_meeting" do @@ -126,13 +111,6 @@ RSpec.describe ZoomGateway do end end - describe '.delete_recording' do - it 'calls api client to delete recording' do - expect_any_instance_of(Zoom.new.class).to receive(:recording_delete) - gateway.delete_recording('meeting-id', 'recording-id') - end - end - private def stub_env_variable(name, value) diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index 476eb8e..5915227 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -66,16 +66,14 @@ RSpec.describe Account do end describe "#storage_total" do - it "sums videos, release photos, contracts, signatures, recordings" do + it "sums videos, release photos, contracts, signatures" do video_file = Rack::Test::UploadedFile.new(Rails.root.join("spec", "fixtures", "files", "video_file.mp4"), "video/mp4") - recording_file = Rack::Test::UploadedFile.new(Rails.root.join("spec", "fixtures", "files", "video_file.mp4"), "video/mp4") photo_file = Rack::Test::UploadedFile.new(Rails.root.join("spec", "fixtures", "files", "person_photo.png"), "image/png") contract_file = Rack::Test::UploadedFile.new(Rails.root.join("spec", "fixtures", "files", "contract.pdf"), "application/pdf") signature_file = Rack::Test::UploadedFile.new(Rails.root.join("spec", "fixtures", "files", "signature.png"), "image/png") edl_file = Rack::Test::UploadedFile.new(Rails.root.join("spec", "fixtures", "files", "sample-edl.edl"), "application/octet-stream") expect(video_file.size).to eq 1_055_736 - expect(recording_file.size).to eq 1_055_736 expect(edl_file.size).to eq 440 expect(photo_file.size).to eq 910 expect(contract_file.size).to eq 12 @@ -91,9 +89,8 @@ RSpec.describe Account do acquired_media_release = create(:acquired_media_release, project: project, contract: contract_file) import = create(:import, project: project, file: contract_file) music_release = create(:music_release, project: project, contract: contract_file) - zoom_meeting = create(:zoom_meeting, project: project, recording: recording_file) - expect(account.storage_total).to eq 2_125_672 + expect(account.storage_total).to eq 1_069_936 end it "sums only for projects tied to account" do @@ -129,8 +126,8 @@ RSpec.describe Account do Download, User, Broadcast, - Account, - ZoomMeeting + TaskRequest, + Account ] Rails.application.eager_load! ActiveRecord::Base.descendants.each do |model| diff --git a/spec/models/headshot_collection_spec.rb b/spec/models/headshot_collection_spec.rb index 014f108..e33912f 100644 --- a/spec/models/headshot_collection_spec.rb +++ b/spec/models/headshot_collection_spec.rb @@ -6,7 +6,6 @@ describe HeadshotCollection do project = create(:project, appearance_releases: create_list(:appearance_release, 1), talent_releases: create_list(:talent_release, 1), - headshot_collection_uid: "123abc" ) collection = HeadshotCollection.for_project(project) @@ -15,7 +14,7 @@ describe HeadshotCollection do expect(collection).to be_a(HeadshotCollection) expect(collection.releasables).to include(project.appearance_releases.first) expect(collection.releasables).to include(project.talent_releases.first) - expect(collection.collection_uid).to eq(project.headshot_collection_uid) + expect(collection.collection_uid).to eq(project.id) end context "when a release has no headshot photo attachment" do @@ -86,17 +85,6 @@ describe HeadshotCollection do expect(mapping["appearance_release_#{releases.first.id}"]).to include("123") expect(mapping["talent_release_#{releases.last.id}"]).to include("456") end - - context "when collection uid is blank" do - it "is not included in the hash" do - releases = [] - collection = HeadshotCollection.new(nil, releases) - - hash = collection.to_hash - - expect(hash.keys).not_to include(:collection_uid) - end - end end private diff --git a/spec/models/task_request_spec.rb b/spec/models/task_request_spec.rb new file mode 100644 index 0000000..cf5ea56 --- /dev/null +++ b/spec/models/task_request_spec.rb @@ -0,0 +1,16 @@ +require 'rails_helper' + +RSpec.describe TaskRequest, type: :model do + describe "associations" do + it { is_expected.to belong_to(:project) } + end + + describe "enums" do + it { is_expected.to define_enum_for(:status).with_values([:pending, :completed, :cancelled]) } + end + + describe "#order_by_recent" do + subject { described_class } + it { is_expected.to respond_to(:order_by_recent) } + end +end diff --git a/spec/models/zoom_meeting_spec.rb b/spec/models/zoom_meeting_spec.rb index ae07a39..b761ab0 100644 --- a/spec/models/zoom_meeting_spec.rb +++ b/spec/models/zoom_meeting_spec.rb @@ -16,17 +16,6 @@ RSpec.describe ZoomMeeting, type: :model do end - describe "attachments" do - it { is_expected.to respond_to(:recording) } - end - - describe "validations" do - context '#recording' do - it { is_expected.to allow_content_type("video/mp4").for(:recording) } - it { is_expected.not_to allow_content_types("image/png").for(:recording) } - end - end - describe 'associations' do it { is_expected.to belong_to(:zoom_user) } it { is_expected.to belong_to(:project).optional(true) } diff --git a/spec/policies/contract_template_policy_spec.rb b/spec/policies/contract_template_policy_spec.rb index 241ca38..703d70e 100644 --- a/spec/policies/contract_template_policy_spec.rb +++ b/spec/policies/contract_template_policy_spec.rb @@ -15,10 +15,6 @@ describe ContractTemplatePolicy do it { is_expected.not_to permit(user_context, :create) } end - permissions :show? do - it { is_expected.to permit(user_context, :show) } - end - permissions :destroy? do it { is_expected.not_to permit(user_context, contract_template) } @@ -37,32 +33,6 @@ describe ContractTemplatePolicy do it { is_expected.to permit(user_context, :create) } end - permissions :show? do - it { is_expected.to permit(user_context, :show) } - end - - permissions :destroy? do - it { is_expected.to permit(user_context, contract_template) } - - context "when there are associated releases" do - let(:contract_template) { create(:contract_template, appearance_releases: build_list(:appearance_release, 1)) } - - it { is_expected.to permit(user_context, contract_template) } - end - end - end - - context "for an account manager" do - let(:user) { create(:user, :account_manager) } - - permissions :create? do - it { is_expected.to permit(user_context, :create) } - end - - permissions :show? do - it { is_expected.to permit(user_context, :show) } - end - permissions :destroy? do it { is_expected.to permit(user_context, contract_template) } @@ -109,7 +79,7 @@ describe ContractTemplatePolicy do context "for associate" do let(:user) { create(:user, :associate, primary_account: account) } - it { is_expected.to include(member_project.contract_templates.first) } + it { is_expected.not_to include(member_project.contract_templates.first) } it { is_expected.not_to include(non_member_project.contract_templates.first) } it { is_expected.not_to include(outside_project.contract_templates.first) } end