Merge pull request #16 from jedmund/job-skills

Implement job skills
This commit is contained in:
Justin Edmund 2022-12-03 19:14:29 -08:00 committed by GitHub
commit 16920c6373
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 999 additions and 351 deletions

View file

@ -11,9 +11,15 @@ module Api::V1
rescue_from ActiveRecord::RecordNotUnique, with: :render_unprocessable_entity_response
rescue_from Api::V1::SameFavoriteUserError, with: :render_unprocessable_entity_response
rescue_from Api::V1::FavoriteAlreadyExistsError, with: :render_unprocessable_entity_response
rescue_from Api::V1::NoJobProvidedError, with: :render_unprocessable_entity_response
rescue_from Api::V1::TooManySkillsOfTypeError, with: :render_unprocessable_entity_response
rescue_from Api::V1::UnauthorizedError, with: :render_unauthorized_response
rescue_from ActionController::ParameterMissing, with: :render_unprocessable_entity_response
rescue_from GranblueError do |e|
render_error(e)
end
##### Hooks
before_action :current_user
before_action :set_default_content_type
@ -43,13 +49,21 @@ module Api::V1
end
### Error response methods
def render_error(error)
if error
render action: 'errors', json: error.to_hash, status: error.http_status
else
render action: 'errors'
end
end
def render_unprocessable_entity_response(exception)
@exception = exception
render action: 'errors', status: :unprocessable_entity
end
def render_not_found_response
response = { errors: [{ message: "Record could not be found.", code: "not_found" }]}
response = { errors: [{ message: "Record could not be found.", code: "not_found" }] }
render 'not_found', status: :not_found
end

View file

@ -0,0 +1,13 @@
class Api::V1::JobSkillsController < Api::V1::ApiController
def all
@skills = JobSkill.all()
render :all, status: :ok
end
def job
job = Job.find(params[:id])
@skills = JobSkill.where(job: job).or(JobSkill.where(sub: true))
render :all, status: :ok
end
end

View file

@ -1,6 +1,160 @@
class Api::V1::JobsController < Api::V1::ApiController
before_action :set, only: %w[update_job update_job_skills]
def all
@jobs = Job.all()
render :all, status: :ok
end
def update_job
raise NoJobProvidedError unless job_params[:job_id].present?
# Extract job and find its main skills
job = Job.find(job_params[:job_id])
main_skills = JobSkill.where(job: job.id, main: true)
# Update the party
@party.job = job
main_skills.each_with_index do |skill, index|
@party["skill#{index}_id"] = skill.id
end
# Check for incompatible Base and EMP skills
%w[skill1_id skill2_id skill3_id].each do |key|
@party[key] = nil if @party[key] && mismatched_skill(@party.job, JobSkill.find(@party[key]))
end
render :update, status: :ok if @party.save!
end
def update_job_skills
throw NoJobSkillProvidedError unless job_params[:skill1_id] || job_params[:skill2_id] || job_params[:skill3_id]
# Determine which incoming keys contain new skills
skill_keys = %w[skill1_id skill2_id skill3_id]
new_skill_keys = job_params.keys.select { |key| skill_keys.include?(key) }
# If there are new skills, merge them with the existing skills
unless new_skill_keys.empty?
existing_skills = {
1 => @party.skill1,
2 => @party.skill2,
3 => @party.skill3
}
new_skill_ids = new_skill_keys.map { |key| job_params[key] }
new_skill_ids.map do |id|
skill = JobSkill.find(id)
raise Api::V1::IncompatibleSkillError.new(job: @party.job, skill: skill) if mismatched_skill(@party.job, skill)
end
positions = extract_positions_from_keys(new_skill_keys)
new_skills = merge_skills_with_existing_skills(existing_skills, new_skill_ids, positions)
new_skill_ids = new_skills.each_with_object({}) do |(index, skill), memo|
memo["skill#{index}_id"] = skill.id if skill
end
@party.attributes = new_skill_ids
end
render :update, status: :ok if @party.save!
end
private
def merge_skills_with_existing_skills(
existing_skills,
new_skill_ids,
positions
)
new_skills = new_skill_ids.map { |id| JobSkill.find(id) }
new_skills.each_with_index do |skill, index|
existing_skills = place_skill_in_existing_skills(existing_skills, skill, positions[index])
end
existing_skills
end
def place_skill_in_existing_skills(existing_skills, skill, position)
# Test if skill will exceed allowances of skill types
skill_type = skill.sub ? 'sub' : 'emp'
unless can_add_skill_of_type(existing_skills, position, skill_type)
raise Api::V1::TooManySkillsOfTypeError.new(skill_type: skill_type)
end
if !existing_skills[position]
existing_skills[position] = skill
else
value = existing_skills.compact.detect { |_, value| value && value.id == skill.id }
old_position = existing_skills.key(value[1]) if value
if old_position
existing_skills = swap_skills_at_position(existing_skills, skill, position, old_position)
else
existing_skills[position] = skill
end
end
existing_skills
end
def swap_skills_at_position(skills, new_skill, position1, position2)
# Check desired position for a skill
displaced_skill = skills[position1] if skills[position1].present?
# Put skill in new position
skills[position1] = new_skill
skills[position2] = displaced_skill
skills
end
def extract_positions_from_keys(keys)
# Subtract by 1 because we won't operate on the 0th skill, so we don't pass it
keys.map { |key| key['skill'.length].to_i }
end
def can_add_skill_of_type(skills, position, type)
if skills.values.compact.length.positive?
max_skill_of_type = 2
skills_to_check = skills.compact.reject { |key, _| key == position }
sum = skills_to_check.values.count { |value| value.send(type) }
sum + 1 <= max_skill_of_type
else
true
end
end
def mismatched_skill(job, skill)
mismatched_main = (skill.job.id != job.id) && skill.main && !skill.sub
mismatched_emp = (skill.job.id != job.id) && skill.emp
mismatched_base = skill.job.base_job && (job.row != 'ex2' || skill.job.base_job.id != job.base_job.id) && skill.base
if %w[4 5 ex2].include?(job.row)
true if mismatched_emp || mismatched_base || mismatched_main
elsif mismatched_emp || mismatched_main
true
else
false
end
end
def set
@party = Party.where('id = ?', params[:id]).first
end
def job_params
params.require(:party).permit(
:job_id,
:skill0_id,
:skill1_id,
:skill2_id,
:skill3_id
)
end
end

View file

@ -1,15 +1,20 @@
class Api::V1::PartiesController < Api::V1::ApiController
before_action :set_from_slug, except: ['create', 'destroy', 'update', 'index', 'favorites']
before_action :set, only: ['update', 'destroy']
before_action :set_from_slug,
except: %w[create destroy update index favorites]
before_action :set, only: %w[update destroy]
def create
@party = Party.new(shortcode: random_string)
@party.extra = party_params['extra']
if current_user
@party.user = current_user
job = Job.find(party_params['job_id']) if party_params['job_id'].present?
job_skills = JobSkill.where(job: job.id, main: true)
job_skills.each_with_index do |skill, index|
@party["skill#{index}_id"] = skill.id
end
@party.user = current_user if current_user
render :show, status: :created if @party.save!
end
@ -17,25 +22,46 @@ class Api::V1::PartiesController < Api::V1::ApiController
render_not_found_response if @party.nil?
end
def update
if @party.user != current_user
render_unauthorized_response
else
@party.attributes = party_params.except(:skill1_id, :skill2_id, :skill3_id)
end
render :update, status: :ok if @party.save!
end
def index
@per_page = 15
now = DateTime.current
start_time = (now - request.params['recency'].to_i.seconds).to_datetime.beginning_of_day unless request.params['recency'].blank?
start_time =
(
now - request.params["recency"].to_i.seconds
).to_datetime.beginning_of_day unless request.params["recency"].blank?
conditions = {}
conditions[:element] = request.params['element'] unless request.params['element'].blank?
conditions[:raid] = request.params['raid'] unless request.params['raid'].blank?
conditions[:created_at] = start_time..now unless request.params['recency'].blank?
conditions[:element] = request.params["element"] unless request.params[
"element"
].blank?
conditions[:raid] = request.params["raid"] unless request.params[
"raid"
].blank?
conditions[:created_at] = start_time..now unless request.params[
"recency"
].blank?
conditions[:weapons_count] = 5..13
@parties = Party
@parties =
Party
.where(conditions)
.order(created_at: :desc)
.paginate(page: request.params[:page], per_page: @per_page)
.each { |party|
party.favorited = (current_user) ? party.is_favorited(current_user) : false
}
.each do |party|
party.favorited =
current_user ? party.is_favorited(current_user) : false
end
@count = Party.where(conditions).count
render :all, status: :ok
@ -47,41 +73,40 @@ class Api::V1::PartiesController < Api::V1::ApiController
@per_page = 15
now = DateTime.current
start_time = (now - params['recency'].to_i.seconds).to_datetime.beginning_of_day unless request.params['recency'].blank?
start_time =
(
now - params["recency"].to_i.seconds
).to_datetime.beginning_of_day unless request.params["recency"].blank?
conditions = {}
conditions[:element] = request.params['element'] unless request.params['element'].blank?
conditions[:raid] = request.params['raid'] unless request.params['raid'].blank?
conditions[:created_at] = start_time..now unless request.params['recency'].blank?
conditions[:element] = request.params["element"] unless request.params[
"element"
].blank?
conditions[:raid] = request.params["raid"] unless request.params[
"raid"
].blank?
conditions[:created_at] = start_time..now unless request.params[
"recency"
].blank?
conditions[:favorites] = { user_id: current_user.id }
@parties = Party
@parties =
Party
.joins(:favorites)
.where(conditions)
.order('favorites.created_at DESC')
.order("favorites.created_at DESC")
.paginate(page: request.params[:page], per_page: @per_page)
.each { |party|
party.favorited = party.is_favorited(current_user)
}
.each { |party| party.favorited = party.is_favorited(current_user) }
@count = Party.joins(:favorites).where(conditions).count
render :all, status: :ok
end
def update
if @party.user != current_user
render_unauthorized_response
else
@party.attributes = party_params
render :update, status: :ok if @party.save!
end
end
def destroy
if @party.user != current_user
render_unauthorized_response
else
render :destroyed, status: :ok if @party.destroy
elsif @party.destroy
render :destroyed, status: :ok
end
end
@ -104,13 +129,14 @@ class Api::V1::PartiesController < Api::V1::ApiController
def random_string
numChars = 6
o = [('a'..'z'), ('A'..'Z'), (0..9)].map(&:to_a).flatten
o = [("a".."z"), ("A".."Z"), (0..9)].map(&:to_a).flatten
return (0...numChars).map { o[rand(o.length)] }.join
end
def set_from_slug
@party = Party.where("shortcode = ?", params[:id]).first
@party.favorited = (current_user && @party) ? @party.is_favorited(current_user) : false
@party.favorited =
current_user && @party ? @party.is_favorited(current_user) : false
end
def set
@ -118,6 +144,17 @@ class Api::V1::PartiesController < Api::V1::ApiController
end
def party_params
params.require(:party).permit(:user_id, :extra, :name, :description, :raid_id, :job_id)
params.require(:party).permit(
:user_id,
:extra,
:name,
:description,
:raid_id,
:job_id,
:skill0_id,
:skill1_id,
:skill2_id,
:skill3_id
)
end
end

View file

@ -12,14 +12,14 @@ class Api::V1::SearchController < Api::V1::ApiController
# conditions[:series] = filters['series'] unless filters['series'].blank? || filters['series'].empty?
end
if search_params[:query].present? && search_params[:query].length >= 2
@characters = if search_params[:query].present? && search_params[:query].length >= 2
if locale == 'ja'
@characters = Character.jp_search(search_params[:query]).where(conditions)
Character.jp_search(search_params[:query]).where(conditions)
else
@characters = Character.en_search(search_params[:query]).where(conditions)
Character.en_search(search_params[:query]).where(conditions)
end
else
@characters = Character.where(conditions)
Character.where(conditions)
end
@count = @characters.length
@ -38,14 +38,14 @@ class Api::V1::SearchController < Api::V1::ApiController
conditions[:series] = filters['series'] unless filters['series'].blank? || filters['series'].empty?
end
if search_params[:query].present? && search_params[:query].length >= 2
@weapons = if search_params[:query].present? && search_params[:query].length >= 2
if locale == 'ja'
@weapons = Weapon.jp_search(search_params[:query]).where(conditions)
Weapon.jp_search(search_params[:query]).where(conditions)
else
@weapons = Weapon.en_search(search_params[:query]).where(conditions)
Weapon.en_search(search_params[:query]).where(conditions)
end
else
@weapons = Weapon.where(conditions)
Weapon.where(conditions)
end
@count = @weapons.length
@ -62,20 +62,74 @@ class Api::V1::SearchController < Api::V1::ApiController
conditions[:element] = filters['element'] unless filters['element'].blank? || filters['element'].empty?
end
if search_params[:query].present? && search_params[:query].length >= 2
@summons = if search_params[:query].present? && search_params[:query].length >= 2
if locale == 'ja'
@summons = Summon.jp_search(search_params[:query]).where(conditions)
Summon.jp_search(search_params[:query]).where(conditions)
else
@summons = Summon.en_search(search_params[:query]).where(conditions)
Summon.en_search(search_params[:query]).where(conditions)
end
else
@summons = Summon.where(conditions)
Summon.where(conditions)
end
@count = @summons.length
@summons = @summons.paginate(page: search_params[:page], per_page: 10)
end
def job_skills
raise Api::V1::NoJobProvidedError unless search_params[:job].present?
# Set up basic parameters we'll use
job = Job.find(search_params[:job])
locale = search_params[:locale] || 'en'
# Set the conditions based on the group requested
conditions = {}
if search_params[:filters].present? && search_params[:filters]['group'].present?
group = search_params[:filters]['group'].to_i
if group >= 0 && group < 4
conditions[:color] = group
conditions[:emp] = false
conditions[:base] = false
elsif group == 4
conditions[:emp] = true
elsif group == 5
conditions[:base] = true
end
end
# Perform the query
@skills = if search_params[:query].present? && search_params[:query].length >= 2
JobSkill.method("#{locale}_search").call(search_params[:query])
.where(conditions)
.where(job: job.id, main: false)
.or(
JobSkill.method("#{locale}_search").call(search_params[:query])
.where(conditions)
.where(sub: true)
)
else
JobSkill.all
.where(conditions)
.where(job: job.id, main: false)
.or(
JobSkill.all
.where(conditions)
.where(sub: true)
)
.or(
JobSkill.all
.where(conditions)
.where(job: job.base_job.id, base: true)
.where.not(job: job.id)
)
end
@count = @skills.length
@skills = @skills.paginate(page: search_params[:page], per_page: 10)
end
private
# Specify whitelisted properties that can be modified.

View file

@ -1,5 +1,5 @@
module Api::V1
class FavoriteAlreadyExistsError < StandardError
class FavoriteAlreadyExistsError < GranblueError
def http_status
422
end

View file

@ -0,0 +1,31 @@
# frozen_string_literal: true
module Api
module V1
# This is the base error that we inherit from for application errors
class GranblueError < StandardError
def initialize(data)
@data = data
end
def http_status
422
end
def code
'granblue_error'
end
def message
'Something went wrong'
end
def to_hash
{
message: message,
code: code
}
end
end
end
end

View file

@ -0,0 +1,29 @@
module Api::V1
class IncompatibleSkillError < GranblueError
def initialize(data)
@data = data
end
def http_status
422
end
def code
'incompatible_skill'
end
def message
'The selected skill cannot be added to the current job'
end
def to_hash
ap @data
{
message: message,
code: code,
job: @data[:job],
skill: @data[:skill]
}
end
end
end

View file

@ -0,0 +1,22 @@
module Api::V1
class NoJobProvidedError < GranblueError
def http_status
422
end
def code
"no_job_provided"
end
def message
"A job ID must be provided"
end
def to_hash
{
message: message,
code: code
}
end
end
end

View file

@ -0,0 +1,22 @@
module Api::V1
class NoJobSkillProvidedError < GranblueError
def http_status
422
end
def code
"no_job_skill_provided"
end
def message
"A job skill ID must be provided"
end
def to_hash
{
message: message,
code: code
}
end
end
end

View file

@ -1,15 +1,12 @@
module Api::V1
class SameFavoriteUserError < StandardError
def http_status
422
end
module Api
module V1
class SameFavoriteUserError < GranblueError
def code
"same_favorite_user"
'same_favorite_user'
end
def message
"Users cannot favorite their own parties"
'Users cannot favorite their own parties'
end
def to_hash
@ -19,4 +16,5 @@ module Api::V1
}
end
end
end
end

View file

@ -0,0 +1,23 @@
# frozen_string_literal: true
module Api
module V1
class TooManySkillsOfTypeError < GranblueError
def code
'too_many_skills_of_type'
end
def message
'You can only have up to 2 skills of type'
end
def to_hash
{
message: message,
code: code,
skill_type: @data[:skill_type]
}
end
end
end
end

View file

@ -1,6 +1,11 @@
class Job < ApplicationRecord
belongs_to :party
belongs_to :base_job,
foreign_key: 'base_job_id',
class_name: 'Job',
optional: true
def display_resource(job)
job.name_en
end

33
app/models/job_skill.rb Normal file
View file

@ -0,0 +1,33 @@
class JobSkill < ApplicationRecord
alias eql? ==
include PgSearch::Model
belongs_to :job
pg_search_scope :en_search,
against: :name_en,
using: {
tsearch: {
prefix: true,
dictionary: "simple",
},
}
pg_search_scope :jp_search,
against: :name_jp,
using: {
tsearch: {
prefix: true,
dictionary: "simple",
},
}
def display_resource(skill)
skill.name_en
end
def ==(o)
self.class == o.class && id == o.id
end
end

View file

@ -1,9 +1,29 @@
class Party < ApplicationRecord
##### ActiveRecord Associations
##### ActiveRecord Associations
belongs_to :user, optional: true
belongs_to :raid, optional: true
belongs_to :job, optional: true
belongs_to :skill0,
foreign_key: "skill0_id",
class_name: "JobSkill",
optional: true
belongs_to :skill1,
foreign_key: "skill1_id",
class_name: "JobSkill",
optional: true
belongs_to :skill2,
foreign_key: "skill2_id",
class_name: "JobSkill",
optional: true
belongs_to :skill3,
foreign_key: "skill3_id",
class_name: "JobSkill",
optional: true
has_many :characters,
foreign_key: "party_id",
class_name: "GridCharacter",
@ -18,11 +38,35 @@ class Party < ApplicationRecord
foreign_key: "party_id",
class_name: "GridSummon",
dependent: :destroy
has_many :favorites
##### ActiveRecord Validations
validate :skills_are_unique
attr_accessor :favorited
def is_favorited(user)
user.favorite_parties.include? self
end
private
def skills_are_unique
skills = [skill0, skill1, skill2, skill3].compact
if skills.uniq.length != skills.length
errors.add(:skill1, "must be unique") if skill0 == skill1
if skill0 == skill2 || skill1 == skill2
errors.add(:skill2, "must be unique")
end
if skill0 == skill3 || skill1 == skill3 || skill2 == skill3
errors.add(:skill3, "must be unique")
end
errors.add(:job_skills, "must be unique")
end
end
end

View file

@ -0,0 +1,3 @@
collection @skills, object_root: false
extends 'job_skills/base'

View file

@ -0,0 +1,10 @@
object :job_skill
attributes :id, :job, :slug, :color, :main, :base, :sub, :emp, :order
node :name do |w|
{
:en => w.name_en,
:ja => w.name_jp
}
end

View file

@ -0,0 +1,20 @@
object @party
attributes :id, :user_id, :shortcode
node :is_extra do |p|
p.extra
end
node :job do |p|
partial("jobs/base", object: p.job)
end
node :job_skills do |p|
{
"0" => partial("job_skills/base", object: p.skill0),
"1" => partial("job_skills/base", object: p.skill1),
"2" => partial("job_skills/base", object: p.skill2),
"3" => partial("job_skills/base", object: p.skill3),
}
end

View file

@ -1,31 +1,47 @@
object :party
attributes :id, :name, :description, :element, :favorited, :shortcode, :created_at, :updated_at
attributes :id,
:name,
:description,
:element,
:favorited,
:shortcode,
:created_at,
:updated_at
node :extra do |p|
p.extra
end
node :user do |p|
partial('users/base', :object => p.user)
partial("users/base", object: p.user)
end
node :raid do |p|
partial('raids/base', :object => p.raid)
partial("raids/base", object: p.raid)
end
node :job do |p|
partial('jobs/base', :object => p.job)
partial("jobs/base", object: p.job)
end
node :job_skills do |p|
{
"0" => partial("job_skills/base", object: p.skill0),
"1" => partial("job_skills/base", object: p.skill1),
"2" => partial("job_skills/base", object: p.skill2),
"3" => partial("job_skills/base", object: p.skill3),
}
end
node :characters do |p|
partial('grid_characters/base', :object => p.characters)
partial("grid_characters/base", object: p.characters)
end
node :weapons do |p|
partial('grid_weapons/base', :object => p.weapons)
partial("grid_weapons/base", object: p.weapons)
end
node :summons do |p|
partial('grid_summons/base', :object => p.summons)
partial("grid_summons/base", object: p.summons)
end

View file

@ -5,3 +5,16 @@ attributes :id, :user_id, :shortcode
node :is_extra do |p|
p.extra
end
node :job do |p|
partial("jobs/base", object: p.job)
end
node :job_skills do |p|
{
"0" => partial("job_skills/base", object: p.skill0),
"1" => partial("job_skills/base", object: p.skill1),
"2" => partial("job_skills/base", object: p.skill2),
"3" => partial("job_skills/base", object: p.skill3),
}
end

View file

@ -0,0 +1,11 @@
node :count do
@count
end
node :total_pages do
(@count.to_f / 10 > 1) ? (@count.to_f / 10).ceil() : 1
end
node(:results) {
partial('job_skills/base', object: @skills)
} unless @skills.empty?

View file

@ -19,14 +19,22 @@ Rails.application.routes.draw do
get 'parties/:id/summons', to: 'parties#summons'
get 'parties/:id/characters', to: 'parties#characters'
put 'parties/:id/jobs', to: 'jobs#update_job'
put 'parties/:id/job_skills', to: 'jobs#update_job_skills'
post 'check/email', to: 'users#check_email'
post 'check/username', to: 'users#check_username'
post 'search/characters', to: 'search#characters'
post 'search/weapons', to: 'search#weapons'
post 'search/summons', to: 'search#summons'
post 'search/job_skills', to: 'search#job_skills'
get 'jobs', to: 'jobs#all'
get 'jobs/skills', to: 'job_skills#all'
get 'jobs/:id/skills', to: 'job_skills#job'
get 'raids', to: 'raids#all'
get 'weapon_keys', to: 'weapon_keys#all'

View file

@ -0,0 +1,14 @@
class AddJobSkillsTable < ActiveRecord::Migration[6.1]
def change
create_table :job_skills, id: :uuid, default: -> { "gen_random_uuid()" } do |t|
t.references :job, type: :uuid
t.string :name_en, null: false, unique: true
t.string :name_jp, null: false, unique: true
t.string :slug, null: false, unique: true
t.integer :color, null: false
t.boolean :main, default: false
t.boolean :sub, default: false
t.boolean :emp, default: false
end
end
end

View file

@ -0,0 +1,6 @@
class RemoveNullConstraintFromJobSkills < ActiveRecord::Migration[6.1]
def change
change_column :job_skills, :name_en, :string, unique: false
change_column :job_skills, :name_jp, :string, unique: false
end
end

View file

@ -0,0 +1,5 @@
class AddOrderToJobSkills < ActiveRecord::Migration[6.1]
def change
add_column :job_skills, :order, :integer
end
end

View file

@ -0,0 +1,6 @@
class AddBaseAndGroupToJobSkills < ActiveRecord::Migration[6.1]
def change
add_column :job_skills, :base, :boolean, default: false
add_column :job_skills, :group, :integer
end
end

View file

@ -0,0 +1,5 @@
class RemoveGroupFromJobSkills < ActiveRecord::Migration[6.1]
def change
remove_column :job_skills, :group, :integer
end
end

View file

@ -0,0 +1,9 @@
class AddJobSkillsToParty < ActiveRecord::Migration[6.1]
def change
change_table(:parties) do |t|
t.references :skill1, type: :uuid, foreign_key: { to_table: 'job_skills' }
t.references :skill2, type: :uuid, foreign_key: { to_table: 'job_skills' }
t.references :skill3, type: :uuid, foreign_key: { to_table: 'job_skills' }
end
end
end

View file

@ -0,0 +1,7 @@
class AddSkill0ToParty < ActiveRecord::Migration[6.1]
def change
change_table(:parties) do |t|
t.references :skill0, type: :uuid, foreign_key: { to_table: "job_skills" }
end
end
end

View file

@ -0,0 +1,7 @@
class AddBaseJobToJobs < ActiveRecord::Migration[6.1]
def change
change_table(:jobs) do |t|
t.references :base_job, type: :uuid, foreign_key: { to_table: 'jobs' }
end
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2022_11_17_070255) do
ActiveRecord::Schema.define(version: 2022_12_03_112452) do
# These are extensions that must be enabled in order to support this database
enable_extension "btree_gin"
@ -102,6 +102,20 @@ ActiveRecord::Schema.define(version: 2022_11_17_070255) do
t.index ["weapon_id"], name: "index_grid_weapons_on_weapon_id"
end
create_table "job_skills", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "job_id"
t.string "name_en", null: false
t.string "name_jp", null: false
t.string "slug", null: false
t.integer "color", null: false
t.boolean "main", default: false
t.boolean "sub", default: false
t.boolean "emp", default: false
t.integer "order"
t.boolean "base", default: false
t.index ["job_id"], name: "index_job_skills_on_job_id"
end
create_table "jobs", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "name_en"
t.string "name_jp"
@ -110,6 +124,8 @@ ActiveRecord::Schema.define(version: 2022_11_17_070255) do
t.string "row"
t.boolean "ml", default: false
t.integer "order"
t.uuid "base_job_id"
t.index ["base_job_id"], name: "index_jobs_on_base_job_id"
end
create_table "oauth_access_grants", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
@ -163,7 +179,15 @@ ActiveRecord::Schema.define(version: 2022_11_17_070255) do
t.integer "weapons_count"
t.uuid "job_id"
t.integer "ml"
t.uuid "skill1_id"
t.uuid "skill2_id"
t.uuid "skill3_id"
t.uuid "skill0_id"
t.index ["job_id"], name: "index_parties_on_job_id"
t.index ["skill0_id"], name: "index_parties_on_skill0_id"
t.index ["skill1_id"], name: "index_parties_on_skill1_id"
t.index ["skill2_id"], name: "index_parties_on_skill2_id"
t.index ["skill3_id"], name: "index_parties_on_skill3_id"
t.index ["user_id"], name: "index_parties_on_user_id"
end
@ -257,8 +281,13 @@ ActiveRecord::Schema.define(version: 2022_11_17_070255) do
add_foreign_key "grid_weapons", "parties"
add_foreign_key "grid_weapons", "weapon_keys", column: "weapon_key3_id"
add_foreign_key "grid_weapons", "weapons"
add_foreign_key "jobs", "jobs", column: "base_job_id"
add_foreign_key "oauth_access_grants", "oauth_applications", column: "application_id"
add_foreign_key "oauth_access_tokens", "oauth_applications", column: "application_id"
add_foreign_key "parties", "job_skills", column: "skill0_id"
add_foreign_key "parties", "job_skills", column: "skill1_id"
add_foreign_key "parties", "job_skills", column: "skill2_id"
add_foreign_key "parties", "job_skills", column: "skill3_id"
add_foreign_key "parties", "jobs"
add_foreign_key "parties", "raids"
add_foreign_key "parties", "users"