Skip to content

Instantly share code, notes, and snippets.

@maksar
Last active January 4, 2016 14:29
Show Gist options
  • Save maksar/8634372 to your computer and use it in GitHub Desktop.
Save maksar/8634372 to your computer and use it in GitHub Desktop.
identification service
class IdentificationStrategy
def initialize origin, priorities
@origin = origin
@priorities = [priorities].flatten
end
def branch candidates
case candidates.length
when 0
zero
when 1
return candidates.first
when 2...Float::INFINITY
many candidates
end
end
def search company
database_results = query(:PAYINDEX, extract_attributes(company).collect(&extract_from(company)))
candidates = database_results.select(&confidently_identified(company))
branch candidates
end
def extract_attributes company
@origin.attributes.select(&by_priority_from(company))
end
def classify weight
case weight
when 0...@origin.suspicious_threshold
NegativeIdentification
when @origin.suspicious_threshold...@origin.identification_threshold
SuspiciousIdentification
when @origin.identification_threshold...Float::INFINITY
ConfidentIdentification
end
end
private
def extract_from company
->(attribute) {
[attribute.name, company.send(attribute.name)]
}
end
def matching candidate, company
->(attribute) {
company.send(attribute.name) == candidate.send(attribute.name)
}
end
def by_priority_from candidate
->(attribute) {
@priorities.include?(attribute.priority) && candidate.send(attribute.name)
}
end
def confidently_identified company
-> (candidate) {
equal_attributes = extract_attributes(candidate).select(&matching(candidate, company))
ConfidentIdentification == classify(equal_attributes.sum(&:weight))
}
end
end
require_relative '../spec_helper'
require_relative '../../lib/strategies/identification_strategy'
require_relative '../../lib/classification/confident_identification'
require_relative '../../lib/classification/suspicious_identification'
require_relative '../../lib/classification/negative_identification'
require_relative '../../lib/company'
require_relative '../../lib/company_attribute'
describe IdentificationStrategy do
describe 'branching' do
let(:candidate) { Object.new }
subject { IdentificationStrategy.new(nil, nil) }
it 'returns single candidate' do
subject.branch([candidate]).must_equal candidate
end
it 'executes zero branch when no candidates provided' do
zero = Object.new
stub(subject).zero { zero }
subject.branch([]).must_equal zero
end
it 'executes many branch when many candidates provided' do
candidates = [candidate, candidate]
many = Object.new
stub(subject).many(candidates) { many }
subject.branch(candidates).must_equal many
end
end
describe 'attribute extraction' do
let(:candidate) { Company.new inn: '123', kpp: nil, okpo: '123', short_name: '123' }
let(:origin) { OpenStruct.new attributes: [
CompanyAttribute.new(name: :inn, weight: 20, priority: 1),
CompanyAttribute.new(name: :kpp, weight: 10, priority: 1),
CompanyAttribute.new(name: :okpo, weight: 5, priority: 1),
CompanyAttribute.new(name: :short_name, weight: 3, priority: 2),
CompanyAttribute.new(name: :full_name, weight: 1, priority: 2)]}
it 'extracts non empty attributes with single priority' do
IdentificationStrategy.new(origin, 1).extract_attributes(candidate).sum(&:weight).must_equal 25
end
it 'extracts non empty attributes with multiple priorities' do
IdentificationStrategy.new(origin, [1, 2]).extract_attributes(candidate).sum(&:weight).must_equal 28
end
end
describe 'classification' do
let(:origin) { OpenStruct.new identification_threshold: 10, suspicious_threshold: 5 }
subject { IdentificationStrategy.new(origin, nil) }
it 'confidently identifies candidate' do
subject.classify(11).must_equal ConfidentIdentification
end
it 'suspiciously identifies candidate' do
subject.classify(7).must_equal SuspiciousIdentification
end
it 'cannot identify candidate' do
subject.classify(3).must_equal NegativeIdentification
end
end
describe 'searching' do
let(:company) { Company.new inn: '123', kpp: nil, okpo: 'abc', short_name: 'xyz' }
let(:candidate1) { Company.new inn: '123', kpp: nil, okpo: '___', short_name: 'xyz' } #Will be scored 23, which makes it confidently identified
let(:candidate2) { Company.new inn: '___', kpp: nil, okpo: 'abc', short_name: '__' } #Will be scored 5, which makes it suspiciously identified
let(:candidate3) { Company.new inn: '___', kpp: nil, okpo: '___', short_name: 'xyz' } #Will be scored 3, which makes it negatively identified
let(:origin) { OpenStruct.new identification_threshold: 10, suspicious_threshold: 5, attributes: [
CompanyAttribute.new(name: :inn, weight: 20, priority: 1),
CompanyAttribute.new(name: :kpp, weight: 10, priority: 1),
CompanyAttribute.new(name: :okpo, weight: 5, priority: 1),
CompanyAttribute.new(name: :short_name, weight: 3, priority: 2)]}
subject { IdentificationStrategy.new(origin, [1, 2]) }
it 'identifies only first company' do
stub(subject).query(:PAYINDEX, {inn: '123', okpo: 'abc', short_name: 'xyz'}.to_a) { [candidate1, candidate2, candidate3] }
subject.search(company).must_equal candidate1
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment