Select Git revision
personal_notifications.js
Forked from
Stud.IP / Stud.IP
Source project has a limited visibility.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
TandemMatching.class.php 10.85 KiB
<?php
/**
* This file is part of the TandemPlugin for Stud.IP
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* @author Moritz Strohm <strohm@data-quest.de>
* @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
* @category Plugin
**/
require_once(__DIR__ . '/../models/TandemUserMotherLanguage.class.php');
/**
* This class implements the matching functionality.
*/
class TandemMatching
{
static public function getEqualOrHigherLevels(string $level)
{
$all_levels = ['A1', 'A2', 'B1', 'B2', 'C1', 'C2'];
if (!in_array($level, $all_levels)) {
return [];
}
if ($level == 'A1') {
return $all_levels;
}
return array_slice($all_levels, array_search($level, $all_levels));
}
/**
* Helper method for building the SQL query for findMatches and countMatches.
* Since both methods use the same query and differ only in the result
* the query can be built in this helper method.
*
* @return Array Associative array with two attributes:
* - 'query': The SQL query
* - 'params': Parameters for the SQL query
*/
static protected function buildMatchSqlQuery(TandemProfile $tandem_request, $limit = 0, $offer_user_id = null)
{
//if the gender attribute is set we must convert its value to
//the Stud.IP system values: 1 = male, 2 = female, 0 = unspecified:
$request_gender = null;
if($tandem_request->gender and TandemPlugin::isGenderSearchEnabled()) {
//If gender search is enabled and a gender preference is set
//we must filter for those settings.
if($tandem_request->gender == 'm') {
$request_gender = 1;
} elseif($tandem_request->gender == 'w') {
$request_gender = 2;
}
}
$sql = 'INNER JOIN tandem_user_mother_languages as tuml
ON tuml.user_id = tandem_profiles.user_id ';
$sql_params = [];
if($request_gender) {
//If the gender attribute from the request was converted correctly
//we must also search for the gender information using the user_info table.
$sql .= 'INNER JOIN user_info
ON tuml.user_id = user_info.user_id ';
}
$sql .= 'WHERE tuml.language_id = :target_language_id
AND tuml.user_id <> :user_id
AND tandem_profiles.target_language_id IN ( :request_user_mother_language_ids ) ';
if (Config::get()->TANDEMPLUGIN_USE_LEVEL) {
$sql .= "AND tuml.`level` IN ( :other_minimum_levels ) ";
//Check which level is higher: The requested one or the one of the user.
$user_and_higher_levels = self::getEqualOrHigherLevels($tandem_request->level);
if ($tandem_request->requested_level) {
$requested_and_higher_levels = self::getEqualOrHigherLevels($tandem_request->requested_level);
//Use the level array that has the least entries so that no lower levels as the requested
//ones are used.
$sql_params['other_minimum_levels'] = count($user_and_higher_levels) < count($requested_and_higher_levels)
? $user_and_higher_levels : $requested_and_higher_levels;
} else {
//Easy: Use the level of the user.
$sql_params['other_minimum_levels'] = $user_and_higher_levels;
}
}
//We must check, if there are tandem pairs. Profiles in tandem pairs
//with pair status 1 (accepted) and 0 (requested) are filtered out.
//Also all profiles where a tandem request was made to are filtered out.
$pairs_exist = (TandemPair::countBySql('TRUE') > 0);
if($pairs_exist) {
//QUERY EXPLAINATION:
// tandem_profiles.id NOT IN:
// We must filter all profiles that are bound to a pair matching
// the following criteria.
//
// SELECT request_profile_id FROM tandem_pairs WHERE status > '-1'
// AND request_profile_id = :profile_id:
// The profile-ID of the profile we're searching matches for
// must match either the request_profile_id or the
// offer_profile_id. The latter filters out all requests
// and established pairs that have been made with that profile.
//
// SELECT offer_profile_id FROM tandem_pairs WHERE status > '-1':
// The same as with request_profile_id but here we're selecting
// the offer_profile_ids and use them in a UNION.
//
// SELECT request_profile_id FROM tandem_pairs WHERE status = '1'
// and
// SELECT offer_profile_id FROM tandem_pairs WHERE status = '1':
// We select the request_profile_ids and offer_profile_ids
// of all established pairs.
// )
$sql .= "AND tandem_profiles.id NOT IN (
SELECT request_profile_id FROM tandem_pairs WHERE request_profile_id = :profile_id OR offer_profile_id = :profile_id
UNION
SELECT offer_profile_id FROM tandem_pairs WHERE offer_profile_id = :profile_id OR request_profile_id = :profile_id
UNION
SELECT request_profile_id FROM tandem_pairs WHERE status = '1'
UNION
SELECT offer_profile_id FROM tandem_pairs WHERE status = '1'
) ";
}
$request_user_mother_language_ids = [];
$rumls = TandemUserMotherLanguage::findBySql(
'user_id = :user_id',
['user_id' => $tandem_request->user_id]
);
foreach($rumls as $ruml) {
$request_user_mother_language_ids[] = $ruml->language_id;
}
$sql_params['target_language_id'] = $tandem_request->target_language_id;
$sql_params['user_id'] = $tandem_request->user_id;
$sql_params['request_user_mother_language_ids'] = $request_user_mother_language_ids;
if($request_gender) {
//The offerer must have the gender the requester wishes.
$sql .= 'AND user_info.geschlecht = :request_gender ';
$sql_params['request_gender'] = $request_gender;
}
if(TandemPlugin::isGenderSearchEnabled()) {
//The requester must have the gender the offerer wishes.
$requester_gender = $tandem_request->user->geschlecht;
//convert Stud.IP gender to TandemPlugin gender:
if($requester_gender == '1') {
$requester_gender = 'm';
} elseif($requester_gender == '2') {
$requester_gender = 'w';
} else {
$requester_gender = '';
}
if($requester_gender != '') {
//In cases where the gender is relevant
//we must extend the SQL query and check if the offerer
//wants requests from users with the requester's gender:
$sql .= "AND tandem_profiles.gender IN ( '', :requester_gender ) ";
$sql_params['requester_gender'] = $requester_gender;
} else {
//If the requester has no gender stored in the user data
//the matching profiles must not have a gender preference.
$sql .= "AND tandem_profiles.gender = '' ";
}
}
if($offer_user_id != null) {
$sql .= 'AND tandem_profiles.user_id = :offer_user_id ';
$sql_params['offer_user_id'] = $offer_user_id;
}
if($pairs_exist) {
$sql_params['profile_id'] = $tandem_request->id;
}
$sql .= ' GROUP BY tandem_profiles.id, tandem_profiles.user_id ';
if($limit > 0) {
$sql .= 'LIMIT :limit';
$sql_params['limit'] = $limit;
}
return [
'query' => $sql,
'params' => $sql_params
];
}
/**
* Finds matches for a given tandem request.
*
* @param TandemProfile $tandem_request A tandem profile
* that shall be checked for matches.
* @param int $limit Limits the number of matches.
* @param string $offer_user_id Limits the search for profiles for one offerer.
*
* @return TandemProfile[] List of tandem profiles that match the criteria
* specified in $tandem_request.
*/
static public function findMatches(TandemProfile $tandem_request, $limit = 0, $offer_user_id = null)
{
//Check, if the profile is already linked to an established tandem pair:
$pair_count = TandemPair::countBySql(
"(request_profile_id = :profile_id
OR offer_profile_id = :profile_id)
AND status = '1'",
[
'profile_id' => $tandem_request->id
]
);
if($pair_count > 0) {
//We don't look for matches if there is already a pair!
return [];
}
$query = self::buildMatchSqlQuery($tandem_request, $limit, $offer_user_id);
return TandemProfile::findBySql($query['query'], $query['params']);
}
/**
* Counts matches for a given tandem profile.
*
* @param TandemProfile $tandem_request A tandem profile
* that shall be checked for matches.
* @param string $offer_user_id Limits the search for profiles for one offerer.
*
* @return int Amount of matches for the profile.
*/
static public function countMatches(TandemProfile $tandem_request, $offer_user_id = null)
{
//Check, if the profile is already linked to an established tandem pair:
$pair_count = TandemPair::countBySql(
"(request_profile_id = :profile_id
OR offer_profile_id = :profile_id)
AND status > '-1'",
[
'profile_id' => $tandem_request->id
]
);
if($pair_count > 0) {
//We don't count matches if there is already a pair!
return 0;
}
$query = self::buildMatchSqlQuery($tandem_request, 0, $offer_user_id);
return TandemProfile::countBySql($query['query'], $query['params']);
}
static public function matchesExist(Array $profiles = [])
{
if(!$profiles) {
//Empty array means we can't look for matches!
return false;
}
foreach($profiles as $profile) {
$count_result = self::countMatches($profile);
if($count_result > 0) {
//We have found one match: That's enough to see if a match exists:
return true;
}
}
//No matches found:
return false;
}
}