diff --git a/db/migrate/20250930215317_add_two_terms.rb b/db/migrate/20250930215317_add_two_terms.rb new file mode 100644 index 0000000..9ae49ac --- /dev/null +++ b/db/migrate/20250930215317_add_two_terms.rb @@ -0,0 +1,13 @@ +# Migration to add "Digital" and "Drawing" terms to the "Medium" facet. +class AddTwoTerms < ActiveRecord::Migration[7.0] + def up + facet = Facet.find_by!(name: "Medium") + Term.create!(facet: facet, value: "Digital") + Term.create!(facet: facet, value: "Drawing") + end + + def down + facet = Facet.find_by!(name: "Medium") + Term.where(facet: facet, value: ["Digital", "Drawing"]).delete_all + end +end diff --git a/db/migrate/20250930220140_add_three_ordered_terms.rb b/db/migrate/20250930220140_add_three_ordered_terms.rb new file mode 100644 index 0000000..9f0ee83 --- /dev/null +++ b/db/migrate/20250930220140_add_three_ordered_terms.rb @@ -0,0 +1,36 @@ +# Migration summary: +# After running `up`, the "Decade" facet will have terms: +# - "2000-2009" (ord: 11) +# - "2010-2019" (ord: 12) +# - "After 2020" (ord: 13) +# - "No Date" (ord: 14) +# - "After 1999" (ord: 15, to be removed after updating items_terms) +# After running `down`, the "Decade" facet will have: +# - "No Date" (ord: 12) +# - "After 1999" (ord: 11) +# - The three new terms will be deleted. + +class AddThreeOrderedTerms < ActiveRecord::Migration[7.0] + def up + facet = Facet.find_by!(name: "Decade") + term = Term.find_by!(facet: facet, value: "After 1999") + # NOTE: Ensure all related items_terms records are updated before removing "After 1999" to prevent orphaned references. + term.update!(ord: 15) + term = Term.find_by!(facet: facet, value: "No Date") + term.update!(ord: 14) + + Term.create!(facet: facet, value: "2000-2009", ord: 11) + Term.create!(facet: facet, value: "2010-2019", ord: 12) + Term.create!(facet: facet, value: "After 2020",ord: 13) + end + + def down + facet = Facet.find_by!(name: "Decade") + Term.where(facet: facet, value: ["2000-2009", "2010-2019", "After 2020"]).delete_all + term = Term.find_by!(facet: facet, value: "After 1999") + term.update!(ord: 11) + term = Term.find_by!(facet: facet, value: "No Date") + term.update!(ord: 12) + end +end + diff --git a/db/migrate/20250930220504_update_term_item_relation.rb b/db/migrate/20250930220504_update_term_item_relation.rb new file mode 100644 index 0000000..517019f --- /dev/null +++ b/db/migrate/20250930220504_update_term_item_relation.rb @@ -0,0 +1,74 @@ +# Migration to reassign items with the term "After 1999" to a newly categorized term. +class UpdateTermItemRelation < ActiveRecord::Migration[7.0] + def up + facet = Facet.find_by!(name: 'Decade') + t_after_2019 = Term.find_by!(facet: facet, value: 'After 1999') + + new_terms = { + (2000..2009) => Term.find_by!(facet: facet, value: '2000-2009'), + (2010..2019) => Term.find_by!(facet: facet, value: '2010-2019'), + (2020..) => Term.find_by!(facet: facet, value: 'After 2020') + } + + invalid_items = [] + + t_after_2019.items.find_each do |item| + year = integer_year(item.date) + unless year + invalid_items << { id: item.id, date: item.date } + next + end + + target_term = new_terms.find { |range, _| range.cover?(year) }&.last + next unless target_term + + re_assign_term_to_item(item, t_after_2019, target_term) + end + + if invalid_items.any? + Rails.logger.warn("Skipped #{invalid_items.size} items due to invalid or missing dates. Example: #{invalid_items.first(5).map { |i| "ID #{i[:id]}: '#{i[:date]}'" }.join(', ')}") + end + end + + def down + facet = Facet.find_by!(name: 'Decade') + target_term = Term.find_by!(facet: facet, value: 'After 1999') + revert_term_assignment(facet, '2000-2009', target_term) + revert_term_assignment(facet, '2010-2019', target_term) + revert_term_assignment(facet, 'After 2020', target_term) + end + + private + + def integer_year(val) + return nil if val.nil? + + # Try direct integer conversion + year = nil + begin + year = Integer(val) + rescue ArgumentError, TypeError + # Try to extract year from common date formats (e.g., "YYYY-MM-DD", "YYYY/MM/DD") + if val.is_a?(String) + if val =~ /\A(\d{4})[-\/]/ + year = $1.to_i + end + end + end + return year + end + + # Reassigns an item from the origin term to the target term, and removes the origin term from the item. + def re_assign_term_to_item(item, origin_term, target_term) + item.terms << target_term + item.terms.delete(origin_term) + end + + # Rolls back term assignment from origin_term_value to target_term for all items. + def revert_term_assignment(facet, origin_term_value, target_term) + origin_term = Term.find_by!(facet: facet, value: origin_term_value) + origin_term.items.find_each do |item| + re_assign_term_to_item(item, origin_term, target_term) + end + end +end diff --git a/db/migrate/20250930230620_update_fix_term_item_relation.rb b/db/migrate/20250930230620_update_fix_term_item_relation.rb new file mode 100644 index 0000000..e4be1d8 --- /dev/null +++ b/db/migrate/20250930230620_update_fix_term_item_relation.rb @@ -0,0 +1,47 @@ +# Migration to update three items with nil or invalid date/year values +class UpdateFixTermItemRelation < ActiveRecord::Migration[7.0] + def up + facet = Facet.find_by!(name: 'Decade') + t_after_1999 = Term.find_by!(facet: facet, value: 'After 1999') + t2000_2009 = Term.find_by!(facet: facet, value: '2000-2009') + t2020_plus = Term.find_by!(facet: facet, value: 'After 2020') + + # Items with nil date + update_item_term(t_after_1999.id, t2000_2009.id, "i.date is null AND i.title in ('Loading a Cannon', 'Dense Populations')") + # Items with date = '[2020]' + update_item_term(t_after_1999.id, t2020_plus.id, "date = '[2020]'") + end + + def down + facet = Facet.find_by!(name: 'Decade') + t_after_1999 = Term.find_by!(facet: facet, value: 'After 1999') + t2000_2009 = Term.find_by!(facet: facet, value: '2000-2009') + t2020_plus = Term.find_by!(facet: facet, value: 'After 2020') + + # revert items with nil date + update_item_term(t2000_2009.id,t_after_1999.id, "i.date is null AND i.title in ('Loading a Cannon', 'Dense Populations')") + # revert items with date = '[2020]' + update_item_term(t2020_plus.id, t_after_1999.id, "date = '[2020]'") + end + + private + + def update_item_term(old_term_id, new_term_id, condition_sql) + + execute <<~SQL.squish + WITH target AS ( + SELECT it.term_id, it.item_id + FROM items i + JOIN items_terms it ON i.id = it.item_id + JOIN terms t ON it.term_id = t.id + WHERE t.id = #{old_term_id} + AND #{condition_sql} + ) + UPDATE items_terms it + SET term_id = #{new_term_id} + FROM target + WHERE it.item_id = target.item_id + AND it.term_id = target.term_id; + SQL + end +end diff --git a/db/migrate/20251001223022_delete_term_after1999.rb b/db/migrate/20251001223022_delete_term_after1999.rb new file mode 100644 index 0000000..1934246 --- /dev/null +++ b/db/migrate/20251001223022_delete_term_after1999.rb @@ -0,0 +1,11 @@ +class DeleteTermAfter1999 < ActiveRecord::Migration[7.0] + def up + facet = Facet.find_by!(name: 'Decade') + exec_delete("DELETE FROM terms WHERE facet_id = #{facet.id} and value = 'After 1999' and ord = 15") + end + + def down + facet = Facet.find_by!(name: 'Decade') + Term.create!(facet: facet, value: "After 1999", ord: 15) + end +end diff --git a/db/schema.rb b/db/schema.rb index 7e064a9..8993802 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[7.0].define(version: 2022_12_08_181611) do +ActiveRecord::Schema[7.0].define(version: 2025_10_01_223022) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql"