diff --git a/lib/utils/math.dart b/lib/utils/math.dart index 594a4e0..9648959 100644 --- a/lib/utils/math.dart +++ b/lib/utils/math.dart @@ -1,6 +1,6 @@ import 'dart:math'; -import 'package:flutter/foundation.dart' show kDebugMode, debugPrint; import '../enumerations.dart'; +import '../models/language.dart'; import '../models/user_profile.dart'; /// Approximate determination of age @@ -95,96 +95,102 @@ double shortestDistanceBetweenUsers( /// Calculates the matching score of [otherUser] for [currentUser]. double calculateMatchScore(UserProfile currentUser, UserProfile otherUser) { - // weights - const double distanceWeight = 0.55; - const double skillWeight = 0.25; - const double availabilityWeight = 0.065; - const double visionWeight = 0.04; - const double riskWeight = 0.035; - const double workWeight = 0.025; - const double cultureWeight = 0.02; - const double communicationWeight = 0.015; - - if (kDebugMode) { - double weightSum = (distanceWeight + - skillWeight + - availabilityWeight + - visionWeight + - riskWeight + - workWeight + - cultureWeight + - communicationWeight); - if (weightSum != 1) { - debugPrint( - 'DEBUG Info --> calculateMatchScore : Weights Sum $weightSum != 1'); - } - } - // Score on locations distance double distance = shortestDistanceBetweenUsers(currentUser, otherUser); - double distanceScore = _distanceToPercentage(distance); + double distanceScore = _distanceToPoints(distance); + + // Score on common languages + List currentLang = currentUser.languages; + List otherLang = otherUser.languages; + int matchingLanguages = + currentLang.toSet().intersection(otherLang.toSet()).length; + int langScore = 0; + if (matchingLanguages >= 3) { + langScore = 10; + } else if (matchingLanguages == 2) { + langScore = 9; + } else if (matchingLanguages == 1) { + langScore = 8; + } + + // Score on common Sectors + List currentSectors = currentUser.sectors; + List otherSectors = otherUser.sectors; + int matchingSectors = + currentSectors.toSet().intersection(otherSectors.toSet()).length; + int sectorScore = 0; + if (matchingSectors >= 4) { + sectorScore = 10; + } else if (matchingSectors == 3) { + sectorScore = 9; + } else if (matchingSectors == 2) { + sectorScore = 7; + } else if (matchingSectors == 1) { + sectorScore = 5; + } // Score on skills int matchingSkillsSought = currentUser.skillsSought .toSet() .intersection(otherUser.skills.toSet()) .length; - int matchingSkillsOffered = otherUser.skills + int matchingSkillsOffered = currentUser.skills .toSet() - .intersection(currentUser.skillsSought.toSet()) + .intersection(otherUser.skillsSought.toSet()) .length; - int skillsSought = currentUser.skillsSought.length; int skillsOffered = otherUser.skills.length; - // Idea: Calculate sum of matching skills divided by amount of skills listed. - // As each list can have up to 3 skills max, this gives the following equation - // with 3 skills: 0 1/3 2/3 3/3; with 2 skills: 0 1/2 2/2; with 1 skill: 0 1. - // In total this will result in a total of 5 different states: - // [0], [1/3], [1/2], [2/3], and [1]. - - double valueSought = matchingSkillsSought / skillsSought; - int scoreSought; - if (valueSought == 1) { - scoreSought = 4; - } else if (valueSought == 2 / 3) { - scoreSought = 3; - } else if (valueSought == 1 / 2) { - scoreSought = 2; - } else if (valueSought == 1 / 3) { - scoreSought = 1; - } else { - scoreSought = 0; + int scoreSought = 0; + if (matchingSkillsSought >= 3) { + scoreSought = 15; + } else if (matchingSkillsSought == 2) { + scoreSought = 12; + } else if (matchingSkillsSought == 1) { + scoreSought = 9; } double valueOffered = matchingSkillsOffered / skillsOffered; int scoreOffered; if (valueOffered == 1) { - scoreOffered = 4; + scoreOffered = 15; + } else if (valueOffered == 4 / 5) { + scoreOffered = 14; + } else if (valueOffered == 3 / 4) { + scoreOffered = 13; } else if (valueOffered == 2 / 3) { - scoreOffered = 3; + scoreOffered = 12; + } else if (valueOffered == 3 / 5) { + scoreOffered = 11; } else if (valueOffered == 1 / 2) { - scoreOffered = 2; + scoreOffered = 10; + } else if (valueOffered == 2 / 5) { + scoreOffered = 9; } else if (valueOffered == 1 / 3) { - scoreOffered = 1; + scoreOffered = 8; + } else if (valueOffered == 1 / 4) { + scoreOffered = 7; + } else if (valueOffered == 1 / 5) { + scoreOffered = 6; } else { scoreOffered = 0; } - int skillsScore = scoreSought + scoreOffered; // 8 points max + double skillsScore = (scoreSought + scoreOffered) / 1.5; // Score on availability int availabilityScore; AvailabilityOption currentAvail = currentUser.availability; AvailabilityOption otherAvail = otherUser.availability; - if (currentAvail == AvailabilityOption.flexible || - otherAvail == AvailabilityOption.flexible || - currentAvail == otherAvail) { - availabilityScore = 3; + if (currentAvail != otherAvail && + (currentAvail == AvailabilityOption.flexible || + otherAvail == AvailabilityOption.flexible)) { + availabilityScore = 4; } else { int availabilityDifference = (currentAvail.index - otherAvail.index).abs(); - availabilityScore = 3 - availabilityDifference; + // map possible values [0 1 2 3] to points {0 1 3 5] + availabilityScore = (3 - availabilityDifference) * 2 - 1; } // Score on Vision @@ -223,15 +229,20 @@ double calculateMatchScore(UserProfile currentUser, UserProfile otherUser) { } // Score on Risk + RiskTolerance currentRisk = currentUser.risk; + RiskTolerance otherRisk = currentUser.risk; int riskScore = 0; if (currentUser.risk == otherUser.risk) { - riskScore = 1; + riskScore = 3; + } else { + int riskDifference = (currentRisk.index - otherRisk.index).abs(); + riskScore = 3 - riskDifference; } // Score on CorporateCulture int cultureScore = 0; if (currentUser.culture == otherUser.culture) { - cultureScore = 1; + cultureScore = 2; } // Score on Communication @@ -242,43 +253,45 @@ double calculateMatchScore(UserProfile currentUser, UserProfile otherUser) { // Calc total score. Sum of each score divided by its own max score value, // multiplied with its own weight. - double totalScore = (distanceWeight * distanceScore / 100) + - (skillWeight * skillsScore / 8) + - (availabilityWeight * availabilityScore / 3) + - (visionWeight * visionScore / 4) + - (workWeight * workScore / 4) + - (riskWeight * riskScore) + - (cultureWeight * cultureScore) + - (communicationWeight * communicationScore); + double totalScore = distanceScore + + langScore + + sectorScore + + skillsScore + + availabilityScore + + riskScore + + visionScore + + workScore + + cultureScore + + communicationScore; - return totalScore * 100; + return min(totalScore, 99.9); } -double _distanceToPercentage(double distance) { +double _distanceToPoints(double distance) { // Self predefined data points - final List distances = [0, 5, 50, 100, 200]; - final List percentages = [100, 99.5, 95, 90, 50]; + final List distances = [0, 10, 100, 200, 500, 700, 1000]; + final List points = [60, 55, 40, 20, 10, 5, 0]; if (distance.isNaN || distance >= distances.last) { - return percentages.last; + return points.last; } else if (distance <= distances[0]) { - return percentages[0]; + return points[0]; } // Linear interpolation for (int i = 1; i < distances.length; i++) { if (distances[i] >= distance) { double x0 = distances[i - 1]; - double y0 = percentages[i - 1]; + double y0 = points[i - 1]; double x1 = distances[i]; - double y1 = percentages[i]; + double y1 = points[i]; // Interpolate - double percentage = y0 + (y1 - y0) * (distance - x0) / (x1 - x0); - return percentage; + double result = y0 + (y1 - y0) * (distance - x0) / (x1 - x0); + return result; } } // Fallback return value, though code should never reach here - return percentages.last; // 50 + return points.last; // 50 }