Create autocomplete for entites, create a service, use it for projects in claim projects module

git-svn-id: https://svn.driver.research-infrastructures.eu/driver/dnet40/modules/uoa-services-portal/trunk@44475 d315682c-612b-4755-9ff5-7f18f6832af3
This commit is contained in:
argiro.kokogiannaki 2016-11-14 12:57:42 +00:00
parent 2df5fc8570
commit 51846c9917
7 changed files with 376 additions and 61 deletions

View File

@ -29,11 +29,6 @@ import { ErrorCodes} from '../../utils/properties/openaireProperties';
</div>
<div *ngIf="dataciteResultsNum == null || dataciteResultsNum == 0" class="alert alert-info" role="alert">No results found</div>
<div >
<!-- <p *ngFor=" let item of dataciteResults ">
<publication-title [title]="item.title" [url]="'http://dx.doi.org/'+item.doi" > </publication-title>
<button (click)="add(item, item.doi,'dataset','datacite',item.title,'http://dx.doi.org/'+item.doi)" type="button" class="btn btn-default">Select</button>
</p>
-->
<ul *ngIf="dataciteResults.length > 0 " class="list-group">
<li *ngFor=" let item of dataciteResults " [class]="(isSelected(item.doi))?'list-group-item panel-footer':'list-group-item'" >
<div >

View File

@ -1,6 +1,6 @@
import {Component, Input,Output, ElementRef, EventEmitter, ViewChild} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import {OpenaireProjectsService} from '../../services/openaireProjects.service';
import {SearchProjectsService} from '../../services/searchProjects.service';
import {ProjectService} from '../../services/project.service';
import {ModalLoading} from '../../utils/modal/loading.component';
import { Subject } from 'rxjs/Subject';
@ -17,20 +17,21 @@ import {ClaimProject} from '../../utils/entities/claimEntities.class';
<option [ngValue]="'0'">Funder:</option>
<option *ngFor="let funder of funders" [ngValue]="funder.field.field[1]['@value_original']">{{funder.field.field[1]['@value']}}</option>
</select> -->
<form><!-- *ngIf=" !inline "-->
<div><!-- class="input-group"-->
<div> <!-- class="input-group-btn" -->
<form class="form-group form-inline"><!-- *ngIf=" !inline "-->
<div class="input-group">
<div class="input-group-btn" >
<button type="button" class="btn btn-info dropdown-toggle" id="dropdownFunder" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
{{selectedFunderName}}
</button>
<ul class="dropdown-menu " aria-labelledby="dropdownFunder">
<li (click)="funderChanged('0','Select funder:')"><a >Select funder:</a></li>
<li *ngFor="let funder of funders" (click)="funderChanged(funder.field.field[1]['@value_original'],funder.field.field[1]['@value'])" ><a >{{funder.field.field[1]['@value']}}</a></li>
<li *ngFor="let funder of funders" (click)="funderChanged(funder.id,funder.name)" ><a >{{funder.name}}</a></li>
</ul>
</div>
<entities-autocomplete entityName="project" [funderId]="selectedFunderId" [allowDuplicates]=true [showSelected]=false [placeHolderMessage] = "'Search for Projects'" title = "Projects:" [multipleSelections]=true (addItem) = "select($event)" ></entities-autocomplete>
<!--input id="community" type="text" class="form-control" placeholder="Search for Projects" [(ngModel)]=query name="query" (keyup)="search()"-->
<!--autocomplete -->
<!--autocomplete
<div class="bs-docs-grid">
<div class = "row-fluid form-inline auto-complete-box panel panel-default">
<div class="panel-heading">Projects</div>
@ -42,7 +43,7 @@ import {ClaimProject} from '../../utils/entities/claimEntities.class';
</span>
</span>
<input name="searchkeyword" type="text" class="auto-complete-input validate filter-input input-sm form-control " placeholder="Search for Projects" [(ngModel)]=query (keyup)=search() >
<div class="suggestions" ><!--*ngIf="filtered.length > 0"-->
<div class="suggestions" >
<ul class="list-group" >
<li class="list-group-item" *ngFor=" let item of filtered | async">
<a (click)="select(item)">{{showItem(item)}}</a>
@ -54,7 +55,7 @@ import {ClaimProject} from '../../utils/entities/claimEntities.class';
</div>
</div>
</div>
<!--autocomplete -->
autocomplete -->
</div>
</form>
@ -110,13 +111,13 @@ export class ClaimProjectsComponent {
public infoMessage = "";
private searchTermStream = new Subject<string>();
filtered: Observable<{}> = this.searchTermStream
filtered: Observable<{}> = this.searchTermStream
.debounceTime(300).distinctUntilChanged()
.switchMap((term: string) => this._projectService.searchForProjectsObs(term, this.selectedFunderId));
private tries:number = 0 ;
private keywordlimit = 3;
constructor(private _service: ProjectService, private _projectService: OpenaireProjectsService,myElement: ElementRef) {
constructor(private _service: ProjectService, private _projectService: SearchProjectsService, myElement: ElementRef) {
this.elementRef = myElement;
}
@ -144,46 +145,19 @@ search() {
}
}
// filter() {
// console.info("Search projects");
// if (this.selectedFunderId == '0' ){
// this.warningMessage = "Please select a funder first";
// this.infoMessage = "";
// }else if ( this.query == "" || this.query.length < 3){
// this.warningMessage = "Please type a keyword, containing at least 3 characters"
// this.infoMessage = "";
// }else{
// this.warningMessage = "";
// this.infoMessage = "";
// this.loading.open();
// this._projectService.searchForProjects(this.query, this.selectedFunderId).subscribe(
// data => {
// this.filteredList =(data == null)?[]:data;
// this.infoMessage = (data == null)?"No Results found":"";
// this.loading.close();
// },
// err => {
// console.error(err);
// this.loading.close();
// this.warningMessage = "An error occured";
//
// }
// );
// }
// }
select(item){
this.query = "";
this.searchTermStream.next(this.query); //clear
var project: ClaimProject = { funderId: this.selectedFunderId,funderName: this.selectedFunderName, projectId: item.field[0]['@value'], projectName: item.field[3]['@value'] , projectAcronym: item.field[1]['@value'], startDate: null, endDate: null };
this._service.getProjectDates(project.projectId).subscribe(
data => {
project.startDate = data.startDate;
project.endDate = data.endDate;
},
err => console.error(err)
);
item = item.value;
var project: ClaimProject = { funderId: this.selectedFunderId,funderName: this.selectedFunderName, projectId: item.id, projectName: item.projectName , projectAcronym: item.projectAcronym, startDate: item.startDate, endDate: item.endDate };
console.log(item);
// this._service.getProjectDates(project.projectId).subscribe(
// data => {
// project.startDate = data.startDate;
// project.endDate = data.endDate;
// },
// err => console.error(err)
// );
var index:number =this.selectedProjects.indexOf(project);
var found:boolean = false;
this.warningMessage = "";
@ -220,19 +194,13 @@ handleClick(event){
}
clickedComponent = clickedComponent.parentNode;
} while (clickedComponent);
// if(!inside){
// this.query = "";
// this.filteredList = this.searchTermStream
// .debounceTime(300).
// distinctUntilChanged()
// .switchMap((term: string) => this._projectService.searchForProjectsObs(term, this.selectedFunderId));
// }
}
getFunders () {
console.info("Getting Funders....");
this._projectService.getFunders().subscribe(
data => {
this.funders = data;
this.funders = data[1];
},
err => console.error(err)
);

View File

@ -0,0 +1,100 @@
import {Injectable} from '@angular/core';
import {Http, Response} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import {AutoCompleteValue} from '../searchPages/searchUtils/searchHelperClasses.class';
import {OpenaireProperties} from '../utils/properties/openaireProperties';
@Injectable()
export class EntitiesSearchService {
private api ="https://beta.services.openaire.eu/provision/mvc/vocabularies/";
constructor(private http: Http) {}
searchByType(keyword:string,type:string,funderId:string){
if( type = "project"){
return this.searchProjectsByFunder(keyword,funderId);
}
// else if ( type == "hostedBy" || type == "collectedFrom"){
// return this.searchDataproviders(keyword);
// }
}
searchProjectsByFunder(keyword:string, funderId:string):any {
let url = OpenaireProperties.getSearchAPIURL()+"projects?"+((keyword && keyword.length > 0)?("q=" +keyword+"&op=and"):"")+"&funder="+funderId+"&fn=and&size=10&page=1";
return this.http.get(url).toPromise()
.then(request =>
{
request = request.json().results;
return this.parse(request,"oaf:project","project");
});
}
// searchDataproviders (keyword: string):any {
//
// let link = OpenaireProperties.getSearchAPIURL()+"projects";
// this.search(link,keyword,"oaf:datasource")
//
// }
// searchProjects (keyword: string):any {
//
// console.info("In searchProjects");
//
// let link = OpenaireProperties.getSearchAPIURL()+"datasources";
// this.search(link,keyword,"oaf:project")
//
// }
// search (link,keyword,type){
// let url = link+"?";
// if(keyword!= null && keyword != '' ) {
// url += "q="+ keyword+"&op=and";
// }
//
// url += "&page="+1+"&size="+10;
//
//
// return this.http.get(url)
// .map(res => <any> res.json())
// //.do(res => console.info(res))
// .map(res => this.parse(res['results'],type));
// }
parse(data: any,oafEntityType:string, type:string){
var array:any =[]
let length = Array.isArray(data) ? data.length : 1;
for(let i=0; i<length; i++) {
let resData = Array.isArray(data) ? data[i]['result']['metadata']['oaf:entity'][oafEntityType] : data['result']['metadata']['oaf:entity'][oafEntityType];
var value:any={} ;
if(Array.isArray(resData['title'])) {
value.label = resData['title'][0];
} else {
value.label = resData['title'];
}
value.id = length > 1 ? data[i]['result']['header']['dri:objIdentifier'] : data['result']['header']['dri:objIdentifier'];
if(type=="project"){
value.projectAcronym = resData['acronym'];
value.projectName = value.label;
value.endDate = null;
value.startDate = null;
if(resData.hasOwnProperty("startdate")) {
value.startDate = resData.startdate.split('-')[0];
}
if(resData.hasOwnProperty("enddate")) {
value.endDate = resData.enddate.split('-')[0];
}
}
console.log("value:"+value);
array.push(value);
}
return array;
}
// http://scoobydoo.di.uoa.gr:8181/dnet-functionality-services-2.0.0-SNAPSHOT/rest/v2/api/projects?refine=true&fields=funderid&page=1&size=0
private handleError (error: Response) {
// in a real world app, we may send the error to some remote logging infrastructure
// instead of just logging it to the console
console.error(error);
return Observable.throw(error || 'Server error');
}
}

View File

@ -33,7 +33,29 @@ export class SearchProjectsService {
.map(res => [res['meta'].total, this.parseResults(res['results']),RefineResultsUtils.parse(res['refineResults'],refineFields)]);
}
getFunders():any {
let url = OpenaireProperties.getSearchAPIURL()+"projects?refine=true&fields=funderid&size=0";
return this.http.get(url)
.map(res => <any> res.json())
.map(res => [res['meta'].total, res['refineResults']['funderid']]);
}
searchForProjectsObs(keyword:string, funderId:string):any {
let url = 'search?action=search&sTransformer=projects_openaire&query='+
'%28oaftype+exact+project%29+and+%28%28projecttitle+%3D+%22'+keyword+'%22%29+or+%28projectacronym+%3D+%22'+keyword+'%22%29+or+%28projectcode+%3D+%22'+keyword+'%22%29%29+and+%28funderid+exact+'+funderId+'%29&size=10&locale=en_GB&format=json';
return this.http.get(url).toPromise()
.then(request =>{
// var valid:boolean= this.isJsonString(request);
// if(valid==true){
return (request.json().response.results)?request.json().response.results.result:request.json().response.result;
// }else{
// return [];
// }
})
}
parseResults(data: any): SearchResult[] {
let results: SearchResult[] = [];

View File

@ -22,6 +22,7 @@ import {SearchDatasetsService} from './searchDatasets.service';
import {SearchOrganizationsService} from './searchOrganizations.service';
import {SearchPeopleService} from './searchPeople.service';
import {SearchProjectsService} from './searchProjects.service';
import {EntitiesSearchService} from './entitySearch.service';
import {ISVocabulariesService} from './ISVocabularies.service';
import {RefineFieldResultsService} from './refineFieldResults.service'
@ -40,7 +41,8 @@ import {RefineFieldResultsService} from './refineFieldResults.service'
SearchPublicationsService, SearchDataprovidersService, DataProviderService,
SearchProjectsService, SearchDatasetsService, SearchOrganizationsService,
SearchPeopleService, ISVocabulariesService,
RefineFieldResultsService
RefineFieldResultsService,
EntitiesSearchService
],
exports: [
]

View File

@ -0,0 +1,225 @@
import {Component, ElementRef, Input, Output, EventEmitter} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import {Subject} from 'rxjs/Subject';
import {Value} from '../searchPages/searchUtils/searchHelperClasses.class';
import {EntitiesSearchService} from '../services/entitySearch.service';
//Usage example
//<static-autocomplete [(filtered)] =filtered [(selected)] =selected placeHolderMessage = "Search for countries" title = "Countries:" (keywordChange)="keywordChanged($event)"></static-autocomplete>
@Component({
selector: 'entities-autocomplete',
styleUrls: ['autoComplete.component.css'],
host: {
'(document:click)': 'handleClick($event)',
},
template: `
<div class="custom-autocomplete">
<div *ngIf = "showSelected && selectedValue != ''">
<div class="row-fluid show-grid auto-complete-choice" *ngFor="let item of selected" >
<span >{{showItem(item)}} </span>
<span (click)="remove(item)" aria-hidden="true" title="Remove selection" class=" remove glyphicon glyphicon-remove"></span>
</div>
</div>
<input *ngIf = "showInput" type="text" class="auto-complete-input validate filter-input input-sm form-control " [placeholder]=placeHolderMessage [(ngModel)]=keyword (keyup)=search() >
<div *ngIf = "keyword != null && keyword.length > 0 " class="suggestions" >
<ul class="list-group" >
<li class="list-group-item" >
Select:
</li>
<li class="list-group-item" *ngFor=" let item of filtered | async">
<a (click)="select(item)">{{showItem(item)}}</a>
</li>
</ul>
</div>
<div class="messages">
<div *ngIf="warningMessage.length > 0" class="alert alert-warning row-fluid " role="alert"> <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>{{warningMessage}}</div>
<div *ngIf="filtered.length == 0 && keyword.length >=3 " class="alert alert-info row-fluid " role="alert"> <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>No results found</div>
</div>
</div>
`
})
export class EntitiesAutocompleteComponent {
@Input() placeHolderMessage = "Search for entries";
@Input() title = "Autocomplete";
@Output() addItem = new EventEmitter(); // when selected list changes update parent component
@Output() selectedValueChanged = new EventEmitter(); // when changed a method for filtering will be called
@Input() public list = []; // the entries resulted after filtering function
@Input() public selected = []; // the entries selected from user
@Input() public keywordlimit = 3; // the minimum length of keyword
@Input() public showSelected = true; // the minimum length of keyword
@Input() public multipleSelections:boolean = true;
@Input() public allowDuplicates:boolean = false;
@Input() public selectedValue:string = '';
@Input() public keyword = '';
@Input() public type = 'search' //search, result, context, project
private warningMessage = "";
private infoMessage = "";
private tries = 0;
private showInput = true;
private sub;
private done = false;
private searchTermStream = new Subject<string>();
filtered: Observable<{}> ;
// = this.searchTermStream
// .debounceTime(300).distinctUntilChanged()
// .switchMap((term: string) => this._search.searchProjectsByFunder(term, this.funderId));
@Input() public funderId:string ="0";
@Input() public entityName:string ;
constructor (private _search:EntitiesSearchService, private myElement: ElementRef) {
// if(this.entityName == "project" ){
// this.filtered = this.searchTermStream
// .debounceTime(300).distinctUntilChanged()
// .switchMap((term: string) => this._search.searchProjectsByFunder(term, this.funderId));
// }
}
ngOnInit () {
if(this.entityName == "project" ){
this.filtered = this.searchTermStream
.debounceTime(300).distinctUntilChanged()
.switchMap((term: string) => this._search.searchProjectsByFunder(term, this.funderId));
}
}
ngOnDestroy(){
}
search() {
this.infoMessage = "";
if(this.keyword == ""){
this.tries = 0;
this.warningMessage = "";
} else if(this.keyword && this.keyword.length < this.keywordlimit){
this.tries++;
if(this.tries == this.keywordlimit -1 ){
this.warningMessage = "Type at least " + this.keywordlimit + " characters";
this.tries = 0;
}
}else{
console.info("doo the search "+this.keyword );
this.tries = 0;
this.warningMessage = "";
this.searchTermStream.next(this.keyword);
}
}
remove(item:any){
var index:number =this.checkIfExists(item,this.selected);
if (index > -1) {
this.selected.splice(index, 1);
}
if(!this.multipleSelections && this.selected.length == 0 ){
this.showInput = true;
this.selectedValue = "";
this.selectedValueChanged.emit({
value: this.selectedValue
});
}
}
select(item:any){
if(this.multipleSelections){
var index:number =this.checkIfExists(item,this.selected);
if (index > -1 && !this.allowDuplicates) {
// this.keyword = "";
// this.filtered.splice(0, this.filtered.length);
return;
}
else{
this.selected.push(item);
// this.keyword = "";
// this.filtered.splice(0, this.filtered.length);
this.addItem.emit({
value: item
});
}
}else{
this.selected.splice(0, this.selected.length);
this.selected.push(item);
// this.filtered.splice(0, this.filtered.length);
this.keyword = "";
this.showInput = false;
this.selectedValue = item.id;
this.selectedValueChanged.emit({
value: this.selectedValue
});
}
console.log("selected"+this.selected.length );
}
private checkIfExists(item:any,list):number{
if(item.concept && item.concept.id ){
for (var _i = 0; _i < list.length; _i++) {
let itemInList = list[_i];
if(item.concept.id == itemInList.concept.id){
return _i;
}
}
}else if(item.id){
for (var _i = 0; _i < list.length; _i++) {
let itemInList = list[_i];
if(item.id == itemInList.id){
return _i;
}
}
}
return -1;
}
showItem(item:any):string{
if (item.name){ //search
return item.name;
}else if( item.concept && item.concept.label){ //context
return item.concept.label;
}else if (item.label){ //simple
return item.label;
}
}
private getSelectedNameFromGivenId(){
if(this.list == null ){
return;
}
for( var i = 0; i < this.list.length; i++){
if(this.list[i].id == this.selectedValue){
this.selectedValue = this.list[i].label;
this.selected.push(this.list[i]);
this.showInput = false;
}
}
}
handleClick(event){
var clickedComponent = event.target;
var inside = false;
do {
if (clickedComponent === this.myElement.nativeElement) {
inside = true;
}
clickedComponent = clickedComponent.parentNode;
} while (clickedComponent);
if(!inside){
this.keyword = "";
this.searchTermStream.next(this.keyword);
}
}
}

View File

@ -10,6 +10,7 @@ import {PublicationTitleFormatter} from './publicationTitleFormatter.component';
import {PagingFormatter} from './pagingFormatter.component';
import {StaticAutocompleteComponent} from './staticAutoComplete.component';
import {StaticAutocomplete2Component} from './staticAutoComplete2.component';
import {EntitiesAutocompleteComponent} from './entitiesAutoComplete.component';
import {DynamicAutocompleteComponent} from './dynamicAutoComplete.component';
import {ShowDataProvidersComponent} from './showDataProviders.component';
import {ExportCSVComponent} from './exportCSV.component';
@ -30,6 +31,7 @@ import {ModalLoading} from './modal/loading.component';
StaticAutocompleteComponent,
StaticAutocomplete2Component,
DynamicAutocompleteComponent,
EntitiesAutocompleteComponent,
ShowDataProvidersComponent,
ExportCSVComponent,
IFrameComponent,
@ -44,6 +46,7 @@ import {ModalLoading} from './modal/loading.component';
StaticAutocompleteComponent,
StaticAutocomplete2Component,
DynamicAutocompleteComponent,
EntitiesAutocompleteComponent,
ShowDataProvidersComponent,
ExportCSVComponent,
IFrameComponent,