Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Enhancement] Upgrade changes in search functionality #4362

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ae85514
[Frontend] Add cache busting to frontend (#4037)
Suryansh5545 Jul 21, 2023
dc1becf
Merge branch 'master' of https://github.com/Suryansh5545/EvalAI
Suryansh5545 Jul 21, 2023
5291c3a
add forntend control for search and filter
Suryansh5545 Jul 21, 2023
4338624
Fix test case
Suryansh5545 Jul 28, 2023
f7f4e1d
Merge branch 'master' into SearchandFilter
Suryansh5545 Aug 9, 2023
929effe
Merge branch 'master' into SearchandFilter
gchhablani Aug 9, 2023
b6cf637
removed junk folder
Suryansh5545 Aug 11, 2023
7d56df2
improved filter appearance
Suryansh5545 Aug 11, 2023
1e3a773
Merge branch 'master' into SearchandFilter
Suryansh5545 Aug 11, 2023
0be52e4
remove useless css changes
Suryansh5545 Aug 11, 2023
a02ce27
Merge branch 'master' into SearchandFilter
gchhablani Aug 28, 2023
bc5fadf
Merge branch 'master' into SearchandFilter
gchhablani Aug 29, 2023
2b9d0e8
Fix domain not being clear
Suryansh5545 Aug 31, 2023
8fd302b
Fix search being exact
Suryansh5545 Aug 31, 2023
3ade03c
Add "all" and "none" category
Suryansh5545 Sep 1, 2023
87a8c6f
Merge branch 'master' into SearchandFilter
Suryansh5545 Sep 1, 2023
bf5c3c8
Merge branch 'master' of https://github.com/Cloud-CV/EvalAI into Sear…
Suryansh5545 Sep 21, 2023
1d1a3bd
Merge branch 'SearchandFilter' of https://github.com/Suryansh5545/Eva…
Suryansh5545 Sep 21, 2023
e2b0a7b
Fix test case
Suryansh5545 Sep 21, 2023
7429533
Merge branch 'master' into SearchandFilter
Suryansh5545 Dec 10, 2023
833d59f
Resolved conflicts from previous merge attempt
Harshit28j May 30, 2024
84f6451
update: search word by word functionality
Harshit28j May 31, 2024
69eb2fc
Add tests for search filter feature
Harshit28j Jun 5, 2024
bbafcb7
Fix all selection to show all challenges
Harshit28j Jun 7, 2024
397cbc9
Fix drop down screen
Harshit28j Jun 20, 2024
186c6b1
Update challengeCtrl.test.js
Harshit28j Jul 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions frontend/src/css/modules/challenge.scss
Original file line number Diff line number Diff line change
Expand Up @@ -370,4 +370,16 @@ md-select .md-select-value span:first-child:after {

.filter-icon {
padding: 10px;
}

.domain-filter {
margin-top: 45px;
}

.filter-icon {
padding: 10px;
}
.md-select-menu-container{
z-index: 1000;
margin-top: 10px;
}
30 changes: 28 additions & 2 deletions frontend/src/js/controllers/challengeListCtrl.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
.module('evalai')
.controller('ChallengeListCtrl', ChallengeListCtrl);

ChallengeListCtrl.$inject = ['utilities', '$window', 'moment'];
ChallengeListCtrl.$inject = ['utilities', '$window', 'moment', '$rootScope'];

function ChallengeListCtrl(utilities, $window, moment) {
function ChallengeListCtrl(utilities, $window, moment, $rootScope) {
var vm = this;
var userKey = utilities.getData('userKey');
var gmtOffset = moment().utcOffset();
Expand All @@ -23,6 +23,8 @@
vm.currentList = [];
vm.upcomingList = [];
vm.pastList = [];
vm.searchTitle = [];
vm.selecteddomain = [];

vm.noneCurrentChallenge = false;
vm.noneUpcomingChallenge = false;
Expand Down Expand Up @@ -112,6 +114,30 @@
}
});
};

parameters.url = "challenges/challenge/get_domain_choices/";
parameters.method = 'GET';
parameters.data = {};
vm.domain_choices = [];
parameters.callback = {
onSuccess: function(response) {
vm.domain_choices.push(["All", "All"]);
for(var i=0; i<response.data.length; i++) {
vm.domain_choices.push([response.data[i][0], response.data[i][1]]);
}
vm.domain_choices.push(["None", "None"]);
},
onError: function(response) {
var error = response.data;
$rootScope.notify("error", error);
}
};
utilities.sendRequest(parameters);

vm.resetFilter = function() {
vm.selecteddomain = [];
vm.searchTitle = [];
};
}

})();
30 changes: 28 additions & 2 deletions frontend/src/js/controllers/hostedChallengeCtrl.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
.module('evalai')
.controller('HostedChallengesCtrl', HostedChallengesCtrl);

HostedChallengesCtrl.$inject = ['utilities'];
HostedChallengesCtrl.$inject = ['utilities', '$rootScope'];

function HostedChallengesCtrl(utilities) {
function HostedChallengesCtrl(utilities, $rootScope) {
var vm = this;
var userKey = utilities.getData('userKey');

Expand All @@ -21,6 +21,9 @@

vm.challengeList = [];
vm.challengeCreator = {};
vm.searchTitle = [];
vm.selecteddomain = [];
vm.domain_choices = [];

var parameters = {};
parameters.url = 'hosts/challenge_host_team/';
Expand Down Expand Up @@ -59,5 +62,28 @@
}
};
utilities.sendRequest(parameters);

parameters.url = "challenges/challenge/get_domain_choices/";
parameters.method = 'GET';
parameters.data = {};
parameters.callback = {
onSuccess: function(response) {
vm.domain_choices.push(["All", "All"]);
for(var i=0; i<response.data.length; i++) {
vm.domain_choices.push([response.data[i][0], response.data[i][1]]);
}
vm.domain_choices.push(["None", "None"]);
},
onError: function(response) {
var error = response.data;
$rootScope.notify("error", error);
}
};
utilities.sendRequest(parameters);

vm.resetFilter = function() {
vm.selecteddomain = [];
vm.searchTitle = [];
};
}
})();
45 changes: 45 additions & 0 deletions frontend/src/js/filters/filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,49 @@
};
}

angular.module('evalai')
.filter('customTitleFilter', customTitleFilter);

function customTitleFilter() {
return function(challenges, searchText) {
if (searchText === undefined) {
return challenges;
}
searchText = searchText.toString().toLowerCase();
var searchWords = searchText.split(' ');
return challenges.filter(function(challenge) {
var title = challenge.title.toLowerCase();
var tags = challenge.list_tags.join(' ').toLowerCase();
var domain = challenge.domain ? challenge.domain.toLowerCase() : '';
var regex = new RegExp("^" + searchWords.join('|'));
return title.split(' ').some(item => regex.test(item)) || tags.split(' ').some(item => regex.test(item)) || domain.split(' ').some(item => regex.test(item));
});
};
}

angular.module('evalai')
.filter('customDomainFilter', customDomainFilter);

function customDomainFilter() {
return function(challenges, selecteddomain) {
selecteddomain = selecteddomain.toString().toLowerCase();
if (selecteddomain === "all") {
return challenges;
}
else if (selecteddomain === "none") {
return challenges.filter(function(challenge) {
return challenge.domain_name === null;
});
}
return challenges.filter(function(challenge) {
if (selecteddomain === "") {
return true;
}
if (challenge.domain_name !== null) {
return challenge.domain_name.toLowerCase().indexOf(selecteddomain) !== -1;
}
});
};
}

})();
21 changes: 18 additions & 3 deletions frontend/src/views/web/challenge-list.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
<section class="ev-sm-container ev-view challenge-container">
<div class="row">
<div class="col s8 m6">
<i class="material-icons prefix"></i>
<strong class="text-med-black fs-18">Search</strong>
<input type="text" ng-model="challengeList.searchTitle" placeholder="Search challenges by title, tag or domain" style="font-weight: bold;">
</div>
<div class="col s12 m4 domain-filter right">
<button class="btn ev-btn-dark waves-effect waves-dark grad-btn grad-btn-dark fs-14" ng-click="challengeList.resetFilter()">Reset Filter</button>
<md-select class="right" id="domain" name="domain" placeholder="Domain Filter" aria-label="Domain Filter" ng-model="challengeList.selecteddomain">
<md-option ng-repeat="option in challengeList.domain_choices" value="{{ option[1] }}">{{ option[1] }}</md-option>
</md-select>
<i class="fa fa-filter right filter-icon" aria-hidden="true"></i>
</div>
</div>

<!-- ongoing challenges -->
<div class="challenge-page-title" id = "ongoing-challenges"><strong class="text-med-black fs-18">Ongoing Challenges</strong></div>
<div ng-if="challengeList.noneCurrentChallenge">None</div>
<div class="row">
<div class="col s12 m3" ng-repeat="challenge in challengeList.currentList"><a class="ev-card-hover" ui-sref="web.challenge-main.challenge-page({challengeId:challenge.id})">
<div class="col s12 m3" ng-repeat="challenge in challengeList.currentList | customTitleFilter:challengeList.searchTitle | customDomainFilter:challengeList.selecteddomain"><a class="ev-card-hover" ui-sref="web.challenge-main.challenge-page({challengeId:challenge.id})">
<div class="card ev-card-panel ev-challenge-card ev-card-hover">
<div class="card-image ev-card-image">
<img class="bg-img" ng-src="{{challenge.image}}">
Expand Down Expand Up @@ -46,7 +61,7 @@
<div class="challenge-page-title"><strong class="text-med-black fs-18">Upcoming Challenges</strong></div>
<div ng-if="challengeList.noneUpcomingChallenge">None</div>
<div class="row">
<div class="col s12 m3" ng-repeat="challenge in challengeList.upcomingList"><a class="ev-card-hover" ui-sref="web.challenge-main.challenge-page({challengeId:challenge.id})">
<div class="col s12 m3" ng-repeat="challenge in challengeList.upcomingList | customTitleFilter:challengeList.searchTitle | customDomainFilter:challengeList.selecteddomain"><a class="ev-card-hover" ui-sref="web.challenge-main.challenge-page({challengeId:challenge.id})">
<div class="card ev-card-panel ev-challenge-card ev-card-hover">
<div class="card-image ev-card-image">
<img class="bg-img" ng-src="{{challenge.image}}">
Expand All @@ -71,7 +86,7 @@
<div class="challenge-page-title"><strong class="text-med-black fs-18">Past Challenges</strong></div>
<div ng-if="challengeList.nonePastChallenge">None</div>
<div class="row">
<div class="col s12 m3" ng-repeat="challenge in challengeList.pastList">
<div class="col s12 m3" ng-repeat="challenge in challengeList.pastList | customTitleFilter:challengeList.searchTitle | customDomainFilter:challengeList.selecteddomain">
<a class="ev-card-hover" ui-sref="web.challenge-main.challenge-page({challengeId:challenge.id})">
<div class="card ev-card-panel ev-challenge-card ev-card-hover">
<div class="card-image ev-card-image">
Expand Down
16 changes: 15 additions & 1 deletion frontend/src/views/web/hosted-challenges.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
<section class="ev-sm-container ev-view challenge-container">
<div class="row">
<div class="col s8 m6">
<i class="material-icons prefix"></i>
<strong class="text-med-black fs-18">Search</strong>
<input type="text" ng-model="challengeListhosted.searchTitle" placeholder="Search challenges by title, tag or domain" style="font-weight: bold;">
</div>
<div class="col s12 m4 domain-filter right">
<button class="btn ev-btn-dark waves-effect waves-dark grad-btn grad-btn-dark fs-14" ng-click="challengeListhosted.resetFilter()">Reset Filter</button>
<md-select class="right" id="domain" name="domain" placeholder="Domain Filter" aria-label="Domain Filter" ng-model="challengeListhosted.selecteddomain">
<md-option ng-repeat="option in challengeListhosted.domain_choices" value="{{ option[1] }}">{{ option[1] }}</md-option>
</md-select>
<i class="fa fa-filter right filter-icon" aria-hidden="true"></i>
</div>
</div>

<!-- my challenges -->
<div class="challenge-page-title" id="ongoing-challenges"><strong class="text-med-black fs-18">My Hosted Challenges</strong></div>
<div ng-if="!hostedChallenges.challengeList.length">
You haven't hosted any challenge. Please <a ui-sref="web.challenge-host-teams" class="highlight-link">click here</a> to host one!
</div>
<div class="row" ng-if="hostedChallenges.challengeList.length">
<div class="col s12 m3" ng-repeat="challenge in hostedChallenges.challengeList"><a class="ev-card-hover" ui-sref="web.challenge-main.challenge-page({challengeId:challenge.id})">
<div class="col s12 m3" ng-repeat="challenge in hostedChallenges.challengeList | customTitleFilter:challengeListhosted.searchTitle | customDomainFilter:challengeList.selecteddomain"><a class="ev-card-hover" ui-sref="web.challenge-main.challenge-page({challengeId:challenge.id})">
<div class="card ev-card-panel ev-challenge-card ev-card-hover">
<div class="card-image ev-card-image">
<img class="bg-img" ng-src="{{challenge.image}}">
Expand Down
64 changes: 64 additions & 0 deletions frontend/tests/controllers-test/challengeCtrl.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2715,3 +2715,67 @@ describe('Unit tests for challenge controller', function () {
});
});
});

describe('Filters', function() {
beforeEach(angular.mock.module('evalai'));

var ceilFilter, formatExecutionTimeFilter, customTitleFilter, customDomainFilter;

beforeEach(inject(function ($injector) {
ceilFilter = $injector.get('$filter')('ceil');
formatExecutionTimeFilter = $injector.get('$filter')('format_execution_time');
customTitleFilter = $injector.get('$filter')('customTitleFilter');
customDomainFilter = $injector.get('$filter')('customDomainFilter');
}));

describe('ceil filter', function() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Harshit28j What is this change for ?

it('should round up input numbers', function() {
expect(ceilFilter(1.23)).toEqual(2);
expect(ceilFilter(-1.23)).toEqual(-1);
expect(ceilFilter(0)).toEqual(0);
});
});

describe('format_execution_time filter', function() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To check if time is formatted properly or not

it('should format execution times correctly', function() {
expect(formatExecutionTimeFilter(0)).toEqual('00 sec');
expect(formatExecutionTimeFilter(60)).toEqual('01 min ');
expect(formatExecutionTimeFilter(3661)).toEqual('01 hr 01 min 01 sec');
expect(formatExecutionTimeFilter(86461)).toEqual('01 day 01 min 01 sec');
});

});

describe('customTitleFilter', function() {
it('should filter challenges based on title search text', function() {
var challenges = [
{title: 'AngularJS', domain_name: 'web', list_tags: ['js', 'tag2']},
{title: 'React', domain_name: 'web', list_tags: ['tag3', 'tag4']},
{title: 'Nodejs', domain_name: 'server', list_tags: ['js', 'tag6']},
{title: 'ebay facebook', domain_name: 'web', list_tags: ['tag7', 'tag8']},

];
expect(customTitleFilter(challenges, 'A')).toEqual([challenges[0]]);
expect(customTitleFilter(challenges, 'e f')).toEqual([challenges[3]]);
expect(customTitleFilter(challenges, 'ebay face')).toEqual([challenges[3]]);
expect(customTitleFilter(challenges, 'React')).toEqual([challenges[1]]);
expect(customTitleFilter(challenges, 'Ang rea')).toEqual([challenges[0],challenges[1]]);
expect(customTitleFilter(challenges, 'js')).toEqual([challenges[0],challenges[2]]);

});
});

describe('customDomainFilter', function() {
it('should filter challenges based on domain', function() {
var challenges = [
{title: 'AngularJS', domain_name: 'webdev'},
{title: 'React', domain_name: 'web'},
{title: 'Node.js', domain_name: null}
];
expect(customDomainFilter(challenges, 'none')).toEqual([challenges[2]]);
expect(customDomainFilter(challenges, 'web')).toEqual([challenges[0], challenges[1]]);
expect(customDomainFilter(challenges, 'webdev')).toEqual([challenges[0]]);
expect(customDomainFilter(challenges, '')).toEqual([challenges[0], challenges[1], challenges[2]]);
});
});
});