diff --git a/Gemfile.lock b/Gemfile.lock index 56b91820..25ae4b75 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -124,7 +124,7 @@ GEM minitest (>= 5.1) tzinfo (~> 2.0) zeitwerk (~> 2.3) - acts_as_list (1.0.4) + acts_as_list (1.1.0) activerecord (>= 4.2) addressable (2.8.0) public_suffix (>= 2.0.2, < 5.0) @@ -133,7 +133,7 @@ GEM autoprefixer-rails (10.4.7.0) execjs (~> 2) aws-eventstream (1.2.0) - aws-partitions (1.619.0) + aws-partitions (1.746.0) aws-sdk (3.1.0) aws-sdk-resources (~> 3) aws-sdk-accessanalyzer (1.23.0) @@ -331,10 +331,10 @@ GEM aws-sdk-connectparticipant (1.14.0) aws-sdk-core (~> 3, >= 3.120.0) aws-sigv4 (~> 1.1) - aws-sdk-core (3.132.0) + aws-sdk-core (3.171.0) aws-eventstream (~> 1, >= 1.0.2) - aws-partitions (~> 1, >= 1.525.0) - aws-sigv4 (~> 1.1) + aws-partitions (~> 1, >= 1.651.0) + aws-sigv4 (~> 1.5) jmespath (~> 1, >= 1.6.1) aws-sdk-costandusagereportservice (1.34.0) aws-sdk-core (~> 3, >= 3.120.0) @@ -594,8 +594,8 @@ GEM aws-sdk-kinesisvideosignalingchannels (1.13.0) aws-sdk-core (~> 3, >= 3.120.0) aws-sigv4 (~> 1.1) - aws-sdk-kms (1.58.0) - aws-sdk-core (~> 3, >= 3.127.0) + aws-sdk-kms (1.63.0) + aws-sdk-core (~> 3, >= 3.165.0) aws-sigv4 (~> 1.1) aws-sdk-lakeformation (1.17.0) aws-sdk-core (~> 3, >= 3.120.0) @@ -1102,8 +1102,8 @@ GEM aws-sdk-route53resolver (1.30.0) aws-sdk-core (~> 3, >= 3.120.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.114.0) - aws-sdk-core (~> 3, >= 3.127.0) + aws-sdk-s3 (1.120.1) + aws-sdk-core (~> 3, >= 3.165.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.4) aws-sdk-s3control (1.40.0) @@ -1260,7 +1260,7 @@ GEM aws-sdk-core (~> 3, >= 3.120.0) aws-sigv4 (~> 1.1) aws-sigv2 (1.1.0) - aws-sigv4 (1.5.1) + aws-sigv4 (1.5.2) aws-eventstream (~> 1, >= 1.0.2) babel-source (5.8.35) babel-transpiler (0.7.0) @@ -1293,15 +1293,15 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - concurrent-ruby (1.2.0) + concurrent-ruby (1.2.2) connection_pool (2.3.0) crass (1.0.6) - csv (3.2.5) + csv (3.2.6) d3-rails (5.9.2) railties (>= 3.1) - database_cleaner (2.0.1) - database_cleaner-active_record (~> 2.0.0) - database_cleaner-active_record (2.0.1) + database_cleaner (2.0.2) + database_cleaner-active_record (>= 2, < 3) + database_cleaner-active_record (2.1.0) activerecord (>= 5.a) database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) @@ -1328,7 +1328,7 @@ GEM docile (1.1.5) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) - engtagger (0.3.2) + engtagger (0.4.0) lru_redux erubi (1.12.0) event_emitter (0.2.6) @@ -1408,7 +1408,7 @@ GEM inline_svg (1.8.0) activesupport (>= 3.0) nokogiri (>= 1.6) - jmespath (1.6.1) + jmespath (1.6.2) json (2.5.1) jwt (2.2.3) kaminari (1.2.2) @@ -1459,7 +1459,7 @@ GEM mini_portile2 (2.8.1) mini_racer (0.6.3) libv8-node (~> 16.10.0.0) - minitest (5.17.0) + minitest (5.18.0) multi_json (1.15.0) multipart-post (2.3.0) mustache (1.1.1) @@ -1474,12 +1474,12 @@ GEM net-smtp (0.3.3) net-protocol netrc (0.11.0) - newrelic_rpm (8.16.0) + newrelic_rpm (9.2.0) nio4r (2.5.8) - nokogiri (1.14.2) + nokogiri (1.14.3) mini_portile2 (~> 2.8.0) racc (~> 1.4) - nokogiri (1.14.2-x86_64-linux) + nokogiri (1.14.3-x86_64-linux) racc (~> 1.4) opus-ruby (1.0.1) ffi @@ -1512,8 +1512,8 @@ GEM pundit (2.3.0) activesupport (>= 3.0.0) racc (1.6.2) - rack (2.2.6.2) - rack-mini-profiler (3.0.0) + rack (2.2.6.4) + rack-mini-profiler (3.1.0) rack (>= 1.2.0) rack-proxy (0.7.6) rack @@ -1579,7 +1579,7 @@ GEM netrc (~> 0.8) rexml (3.2.5) rinku (2.0.6) - rmagick (5.1.0) + rmagick (5.2.0) pkg-config (~> 1.4) ruby-vips (2.1.4) ffi (~> 1.12) @@ -1627,12 +1627,12 @@ GEM sqlite3 (1.6.0) mini_portile2 (~> 2.8.0) sqlite3 (1.6.0-x86_64-linux) - stackprof (0.2.21) + stackprof (0.2.25) statsd-ruby (1.5.0) stripe (5.55.0) - stripe_event (2.3.2) + stripe_event (2.7.0) activesupport (>= 3.1) - stripe (>= 2.8, < 6) + stripe (>= 2.8, < 9) terrapin (0.6.0) climate_control (>= 0.0.3, < 1.0) terser (1.1.13) diff --git a/app/controllers/basil_controller.rb b/app/controllers/basil_controller.rb index caf46b73..674e06e9 100644 --- a/app/controllers/basil_controller.rb +++ b/app/controllers/basil_controller.rb @@ -14,8 +14,10 @@ class BasilController < ApplicationController return raise "Invalid content type: #{params[:content_type]}" end - @content = @current_user_content[@content_type].sort_by(&:name) + @content = @current_user_content.fetch(@content_type, []).sort_by(&:name) end + + @generated_images_count = current_user.basil_commissions.with_deleted.count end def content @@ -199,45 +201,23 @@ class BasilController < ApplicationController .limit(10) .includes(:basil_feedbacks, :image_blob) @in_progress_commissions = @commissions.select { |c| c.completed_at.nil? } - @can_request_another = @in_progress_commissions.count < 3 - end + @generated_images_count = current_user.basil_commissions.with_deleted.count - def character - @character = current_user.characters.find(params[:id]) - @guidance = BasilFieldGuidance.find_or_initialize_by(entity: @character, user: current_user).try(:guidance) - @guidance ||= {} - - category_ids = AttributeCategory.where( - user_id: current_user.id, - entity_type: 'character', - label: ['Looks', 'Appearance'] - ).pluck(:id) - @appearance_fields = AttributeField.where(attribute_category_id: category_ids) - @attributes = Attribute.where( - attribute_field_id: @appearance_fields.pluck(:id), - entity_id: @character.id, - entity_type: 'Character' - ) - - @commissions = BasilCommission.where(entity_type: 'Character', entity_id: @character.id) - .order('id DESC') - .limit(20) - .includes(:basil_feedbacks) - @in_progress_commissions = BasilCommission.where(entity_type: 'Character', entity_id: @character.id, completed_at: nil) - @can_request_another = @in_progress_commissions.count < 3 + @can_request_another = current_user.on_premium_plan? || @generated_images_count < BasilService::FREE_IMAGE_LIMIT + @can_request_another = @can_request_another && @in_progress_commissions.count < BasilService::MAX_JOB_QUEUE_SIZE end def about end def stats - @commissions = BasilCommission.all + @commissions = BasilCommission.all.with_deleted @queued = BasilCommission.where(completed_at: nil) - @completed = BasilCommission.where.not(completed_at: nil) + @completed = BasilCommission.where.not(completed_at: nil).with_deleted @average_wait_time = @completed.where('completed_at > ?', 24.hours.ago) - .average(:cached_seconds_taken) + .average(:cached_seconds_taken) || 0 @seconds_over_time = @completed.where('completed_at > ?', 24.hours.ago) .group_by { |c| ((c.cached_seconds_taken || 0) / 60).round } .map { |minutes, list| [minutes, list.count] } @@ -295,14 +275,16 @@ class BasilController < ApplicationController 'painting2', 'painting3', 'anime' ].flatten.compact.uniq - @total_score_per_style = BasilCommission.where(style: active_styles) - .joins(:basil_feedbacks) - .group(:style) - .sum(:score_adjustment) - .map { |style, average| [style, average.round(1)] } - .sort_by(&:second) - .reverse - @average_score_per_style = BasilCommission.where(style: active_styles) + @total_score_per_style = BasilCommission.with_deleted + .where(style: active_styles) + .joins(:basil_feedbacks) + .group(:style) + .sum(:score_adjustment) + .map { |style, average| [style, average.round(1)] } + .sort_by(&:second) + .reverse + @average_score_per_style = BasilCommission.with_deleted + .where(style: active_styles) .joins(:basil_feedbacks) .group(:style) .average(:score_adjustment) @@ -310,7 +292,8 @@ class BasilController < ApplicationController .sort_by(&:second) .reverse - @average_score_per_page_type = BasilCommission.where.not(completed_at: nil) + @average_score_per_page_type = BasilCommission.with_deleted + .where.not(completed_at: nil) .joins(:basil_feedbacks) .group(:entity_type) .average(:score_adjustment) @@ -404,14 +387,31 @@ class BasilController < ApplicationController def review @recent_commissions = BasilCommission.all.includes(:entity, :user).order('id DESC').limit(100) + + @commissions_per_user_id = BasilCommission.with_deleted.where('created_at > ?', 48.hours.ago).group(:user_id).order('count_all DESC').limit(5).count + @unique_users_generating_count = BasilCommission.with_deleted.where('created_at > ?', 48.hours.ago).group(:user_id).count + + @current_queue_items = BasilCommission.where(completed_at: nil).order('created_at ASC') end def commission + @generated_images_count = current_user.basil_commissions.with_deleted.count + if !current_user.on_premium_plan? && @generated_images_count > BasilService::FREE_IMAGE_LIMIT + redirect_back fallback_location: basil_path, notice: "You've reached your free image limit. Please upgrade to generate more images." + return + end + # Fetch the related content @content = @current_user_content[commission_params.fetch(:entity_type)] .find { |c| c.id == commission_params.fetch(:entity_id).to_i } return raise "Invalid content commission params" if @content.nil? + current_queue_size = current_user.basil_commissions.where(completed_at: nil).where(entity: @content).count + if current_queue_size >= BasilService::MAX_JOB_QUEUE_SIZE + redirect_back fallback_location: basil_path, notice: "You can only have #{BasilService::MAX_JOB_QUEUE_SIZE} commissions per page in progress at a time. Please wait for one to complete before requesting another." + return + end + # Before creating the prompt, do a little config to tweak things to work well :) labels_to_omit_label_text = [ "Name", diff --git a/app/models/basil_commission.rb b/app/models/basil_commission.rb index 7b356993..cb6b08a6 100644 --- a/app/models/basil_commission.rb +++ b/app/models/basil_commission.rb @@ -1,4 +1,6 @@ class BasilCommission < ApplicationRecord + acts_as_paranoid + belongs_to :user belongs_to :entity, polymorphic: true diff --git a/app/services/basil_service.rb b/app/services/basil_service.rb index 402059e3..86017259 100644 --- a/app/services/basil_service.rb +++ b/app/services/basil_service.rb @@ -1,4 +1,7 @@ class BasilService < Service + FREE_IMAGE_LIMIT = 100 + MAX_JOB_QUEUE_SIZE = 3 + IGNORED_VALUES = ['', 'none', 'n/a', '.', '-', ' ', '?', '??', '???', 'x', nil] ENABLED_PAGE_TYPES = [ diff --git a/app/views/basil/content.html.erb b/app/views/basil/content.html.erb index 73d61aaa..8cb5bd3f 100644 --- a/app/views/basil/content.html.erb +++ b/app/views/basil/content.html.erb @@ -110,9 +110,7 @@ function commission_basil(style) { <% end %> - <% end %> - - <% if @can_request_another && @relevant_fields.any? %> + <% if BasilService.experimental_styles_for(@content.page_type).any? %>
Experimental styles
@@ -130,7 +128,26 @@ function commission_basil(style) { <% end %> <% end %> - <% if !@can_request_another %> + <% unless current_user.on_premium_plan? %> +
+ + Image generation is a Premium-only feature, but free accounts can still generate up + to <%= pluralize BasilService::FREE_IMAGE_LIMIT, 'image' %> for free. + +

+ You have generated <%= pluralize @generated_images_count, 'image' %> + and have <%= pluralize [0, BasilService::FREE_IMAGE_LIMIT - @generated_images_count].max, 'free image' %> remaining: +
+
+
+ + <% if @generated_images_count >= BasilService::FREE_IMAGE_LIMIT %> + <%= link_to 'Click here to manage your billing plan', subscription_path, class: 'blue-text text-darken-4' %> + <% end %> +
+ <% end %> + + <% if !@can_request_another && @in_progress_commissions.any? %>
Basil is working on your <%= pluralize @in_progress_commissions.count, 'requested commission' %>.

@@ -138,14 +155,6 @@ function commission_basil(style) {
<% end %> - - <% @commissions.each do |commission| %>
<% if commission.complete? %> diff --git a/app/views/basil/index.html.erb b/app/views/basil/index.html.erb index 9bb19693..5e391e4c 100644 --- a/app/views/basil/index.html.erb +++ b/app/views/basil/index.html.erb @@ -38,6 +38,26 @@

+ + <% unless current_user.on_premium_plan? %> +
+ + Image generation is a Premium-only feature, but free accounts can still generate up + to <%= pluralize BasilService::FREE_IMAGE_LIMIT, 'image' %> for free. + +

+ You have generated <%= pluralize @generated_images_count, 'image' %> + and have <%= pluralize [0, BasilService::FREE_IMAGE_LIMIT - @generated_images_count].max, 'free image' %> remaining: +
+
+
+ + <% if @generated_images_count >= BasilService::FREE_IMAGE_LIMIT %> + <%= link_to 'Click here to manage your billing plan', subscription_path, class: 'blue-text text-darken-4' %> + <% end %> +
+ <% end %> + <% if @universe_scope %>
<%= Universe.icon %> diff --git a/app/views/basil/review.html.erb b/app/views/basil/review.html.erb index fb080c77..182c65f5 100644 --- a/app/views/basil/review.html.erb +++ b/app/views/basil/review.html.erb @@ -1,53 +1,84 @@ -<% @recent_commissions.each do |commission| %> -
- <% if commission.complete? %> - <%# image_tag commission.image, style: 'width: 100%' %> - <% - s3 = Aws::S3::Resource.new(region: "us-east-1") - obj = s3.bucket(commission.s3_bucket).object("job-#{commission.job_id}.png") - %> -
-
- <%= link_to obj.presigned_url(:get) do %> - <%= image_tag obj.presigned_url(:get) %> - <% end %> -
-
-
-
- <%= commission.id %>. - <% if commission.entity.present? %> - <%= link_to commission.entity.name, commission.entity %> +
+
+
+
Most-active creators over 48h
+
    + <% @commissions_per_user_id.each do |user_id, count| %> + <%# This is an N+1 query, but we can deal with it later %> + <% user = User.find(user_id.to_i) %> +
  1. <%= link_to user.display_name, user %>: <%= pluralize count, 'image' %>
  2. + <% end %> +
+ +
+ <%= pluralize @unique_users_generating_count.count, 'unique user' %> over 48h +
+ +

+
Queue
+
    + <% @current_queue_items.each do |commission| %> +
  1. + <%= commission.entity_type %>-<%= commission.entity_id %> (<%= commission.style %>) + for U-<%= commission.user_id %> +
  2. + <% end %> +
+
+
+ <% @recent_commissions.each do |commission| %> +
+ <% if commission.complete? %> + <%# image_tag commission.image, style: 'width: 100%' %> + <% + s3 = Aws::S3::Resource.new(region: "us-east-1") + obj = s3.bucket(commission.s3_bucket).object("job-#{commission.job_id}.png") + %> +
+
+ <%= link_to obj.presigned_url(:get) do %> + <%= image_tag obj.presigned_url(:get) %> <% end %> - <% if commission.style? %> - (<%= commission.style.humanize %>) - <% end %> - by <%= link_to commission.user.name, commission.user %> -
<%= commission.job_id %>
-
    -
  • - Completed <%= time_ago_in_words commission.completed_at %> ago -
  • -
  • - Took <%= distance_of_time_in_words commission.completed_at - commission.created_at %> -
  • -
    -
  • -
    Preprompt:
    - <%= commission.prompt.inspect %> -
  • -
+
+
+
+ <%= commission.id %>. + <% if commission.entity.present? %> + <%= link_to commission.entity.name, commission.entity %> + <% end %> + <% if commission.style? %> + (<%= commission.style.humanize %>) + <% end %> + by <%= link_to commission.user.name, commission.user %> +
<%= commission.job_id %>
+
+
    +
  • + Completed <%= time_ago_in_words commission.completed_at %> ago +
  • +
  • + Took <%= distance_of_time_in_words commission.completed_at - commission.created_at %> +
  • +
    +
  • +
    Preprompt:
    + <%= commission.prompt.inspect %> +
  • +
+
+
-
-
- <% else %> -
- Basil is still working on this commission... -
- (Requested <%= time_ago_in_words(commission.created_at) %> ago) -
+ <% else %> +
+ Basil is still working on this commission... +
+ (Requested <%= time_ago_in_words(commission.created_at) %> ago) +
+
+ <% end %>
<% end %> +
-<% end %> \ No newline at end of file +
diff --git a/app/views/notice_dismissal/messages/_20.html.erb b/app/views/notice_dismissal/messages/_20.html.erb index a1c593ee..90c2eb92 100644 --- a/app/views/notice_dismissal/messages/_20.html.erb +++ b/app/views/notice_dismissal/messages/_20.html.erb @@ -1,20 +1,18 @@ -<% if Date.current <= 'April 15, 2023'.to_date %> -
-
- See what's new -
- <%= link_to basil_path do %> -
-
- camera -
- Bring your notebook pages to life with our new image generator -
+
+
+ See what's new +
+ <%= link_to basil_path do %> +
+
+ camera +
+ Bring your notebook pages to life with our new image generator
- <% end %> -
- <%= link_to 'dismiss message', notice_dismissal_dismiss_path(notice_id: 20), class: 'right' %>
+ <% end %> +
+ <%= link_to 'dismiss message', notice_dismissal_dismiss_path(notice_id: 20), class: 'right' %>
-<% end %> \ No newline at end of file +
\ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 3c4c9782..00cfde77 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -25,10 +25,6 @@ Rails.application.routes.draw do get '/:content_type', to: 'basil#index', as: :basil_content_index get '/:content_type/:id', to: 'basil#content', as: :basil_content post '/:content_type/:id', to: 'basil#commission', as: :basil_commission - - # URLs to migrate over - # get '/character/:id', to: 'basil#character', as: :basil_character - # post '/character/:id', to: 'basil#commission' end end diff --git a/db/migrate/20230414183606_add_deleted_at_column_to_basil_commissions.rb b/db/migrate/20230414183606_add_deleted_at_column_to_basil_commissions.rb new file mode 100644 index 00000000..91bf6604 --- /dev/null +++ b/db/migrate/20230414183606_add_deleted_at_column_to_basil_commissions.rb @@ -0,0 +1,5 @@ +class AddDeletedAtColumnToBasilCommissions < ActiveRecord::Migration[6.1] + def change + add_column :basil_commissions, :deleted_at, :datetime + end +end diff --git a/db/schema.rb b/db/schema.rb index d1adaaad..23c987c7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2023_03_23_231640) do +ActiveRecord::Schema.define(version: 2023_04_14_183606) do create_table "active_storage_attachments", force: :cascade do |t| t.string "name", null: false @@ -200,6 +200,7 @@ ActiveRecord::Schema.define(version: 2023_03_23_231640) do t.float "cached_seconds_taken" t.string "s3_bucket", default: "basil-commissions" t.datetime "saved_at" + t.datetime "deleted_at" t.index ["entity_type", "entity_id", "saved_at"], name: "basil_commissions_ees" t.index ["entity_type", "entity_id", "style"], name: "basil_commissions_ees2" t.index ["entity_type", "entity_id"], name: "basil_commissions_ee"