Project

General

Profile

1
import {Component, Input, ViewChild} from '@angular/core';
2
import {SearchCrossrefService} from '../../claim-utils/service/searchCrossref.service';
3
import {SearchDataciteService} from '../../claim-utils/service/searchDatacite.service';
4

    
5
import {ModalLoading} from '../../../utils/modal/loading.component';
6
import {Dates, DOI} from '../../../utils/string-utils.class';
7
import {EnvProperties} from '../../../utils/properties/env-properties';
8
import {ClaimEntity} from "../../claim-utils/claimHelper.class";
9
import {Subscriber} from "rxjs";
10

    
11
declare var UIkit: any;
12

    
13

    
14
@Component({
15
  selector: 'bulk-claim',
16
  template: `
17
    <div class="uk-animation uk-text-center" style=" ">
18
      <form class=" ">
19

    
20
        <div>
21
          <div>
22
            <!--div class="uk-text-lead">Upload a DOI csv file <helper  div="link-result-bulk" tooltip=true ></helper></div>
23
            <label for="exampleInputFile">Select a file: </label-->
24
            <div class="js-upload" uk-form-custom>
25
              <input id="exampleInputFile" class="uk-width-medium" type="file" (change)="fileChangeEvent($event)"/>
26
              <span class="uk-link " style="text-decoration: underline;">Upload a DOI's CSV file </span>
27
              <!--button class="uk-button  portal-button" type="button" tabindex="-1" [class.disabled]="!enableUpload" ><span class="uk-margin-small-right uk-icon"  >
28
              <svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"> <polyline fill="none" stroke="#000" points="5 8 9.5 3.5 14 8 "></polyline> <rect x="3" y="17" width="13" height="1"></rect>
29
               <line fill="none" stroke="#000" x1="9.5" y1="15" x2="9.5" y2="4"></line></svg></span> Select</button-->
30
              <!--helper  div="link-result-bulk" tooltip=true ></helper-->
31
            </div>
32
            <span class=" " title="{{tooltip}}" uk-tooltip><span class="uk-icon" uk-icon="icon: info; ratio: 0.8">&nbsp; </span> </span>
33
            <div *ngIf="showReport" uk-alert class="uk-alert-primary">
34
              <a class="uk-alert-close" uk-close></a>
35
              <div>Uploaded file contains <span
36
                class="uk-text-bold">{{allIds.length}} {{((allIds.length == 1) ? 'DOI' : 'DOIs')}}</span>.
37
                <span
38
                  *ngIf="exceedsLimit">
39
                  <div class="uk-text-danger">Basket exceeds  the size limit.</div>
40
                  <span *ngIf="allIds.length > 0 && foundIds.length > 0">Only </span>
41
                  <span *ngIf="allIds.length > 0 && foundIds.length == 0">No results added.</span>
42
                   </span>
43
                <span
44
                  *ngIf="allIds.length > 0 && foundIds.length > 0">{{foundIds.length}} {{((foundIds.length == 1) ? 'result was' : 'results were')}} succefully  fetched from 
45
                  <span class="uk-text-bold">CrossRef</span>{{ ' and ' }}<span
46
                    class="uk-text-bold">Datacite</span>.</span>
47

    
48
              </div>
49
              <div
50
                *ngIf="duplicateIds.length > 0">{{duplicateIds.length | number}} duplicate DOIs in {{((duplicateIds.length == 1) ? 'line' : 'lines')}} {{duplicateIdsRow}}.
51
              </div>
52
              <div *ngIf="notFoundIds.length > 0">Couldn't be fetched:
53
                <ul class="">
54
                  <li *ngFor="let id of notFoundIds; let i = index">"{{id}}" in line {{notFoundIdsRow[i]}}</li>
55
                </ul>
56
              </div>
57
              <div *ngIf="noValidIds.length > 0">No valid DOIs:
58
                <ul class="">
59
                  <li *ngFor="let id of noValidIds; let i = index">"{{id}}" in line {{noValidIdsRow[i]}}</li>
60
                </ul>
61
              </div>
62
              <div
63
                *ngIf="allIds.length == 0 || (foundIds.length == 0 && !exceedsLimit)"> Please make sure that the uploaded file, is a csv file with the proper format.
64
              </div>
65

    
66
            </div>
67
            <div *ngIf="errorMessage.length > 0 " uk-alert class="uk-alert uk-alert-danger" role="alert">
68
              <a class="uk-alert-close" uk-close></a>
69
              {{errorMessage}}</div>
70

    
71
          </div>
72
          <!--helper  div="link-result-bulk" ></helper-->
73
        </div>
74
      </form>
75

    
76

    
77
    </div>
78
    <div class="uk-width-1-1">
79
      <modal-loading
80
        [message]="'Uploading, reading your documet and fetching results. Please give us a moment..'"></modal-loading>
81
    </div>
82
  `
83

    
84
})
85
export class BulkClaimComponent {
86
  filesToUpload: Array<File>;
87
  source: string = "crossref";
88
  type: string = "publication";
89
  @Input() public select: boolean = true;
90
  @Input() public results;
91
  @Input() public properties: EnvProperties;
92

    
93
  allIds: string[] = [];
94
  foundIds: string[] = [];
95
  existedIds: string[] = [];
96
  duplicateIds: string[] = [];
97
  duplicateIdsRow: number[] = [];
98
  notFoundIds: string[] = [];
99
  notFoundIdsRow: number[] = [];
100
  noValidIds: string[] = [];
101
  noValidIdsRow: number[] = [];
102
  showReport: boolean = false;
103
  @ViewChild(ModalLoading) loading: ModalLoading;
104
  errorMessage = "";
105
  enableUpload: boolean = true;
106
  @Input() localStoragePrefix: string = "";
107
  exceedsLimit = false;
108
  @Input() basketLimit ;
109
  tooltip = `
110
  <div class="uk-padding-small uk-padding-remove-right uk-padding-remove-left">
111
    <div >
112
         <div><span class="uk-text-bold">CSV format:</span> <br>&quot;DOI&quot;,&quot;ACCESS_MODE&quot;,&quot;DATE&quot;</div>
113
         <br>
114
      <div class="  uk-text-small">
115
        <div>- DOI is required</div>
116
        <div>- Access mode: <br> OPEN, CLOSED, EMBARGO</div>
117
        <div>- Embargo end date:<br> YYYY-MM-DD </div>
118
      </div>     
119
    </div>
120
</div>`;
121
  constructor(private _searchCrossrefService: SearchCrossrefService, private _searchDataciteService: SearchDataciteService) {
122
    this.filesToUpload = [];
123
  }
124

    
125
  ngOnInit() {
126
  }
127
  subscriptions = [];
128
  ngOnDestroy() {
129
    this.subscriptions.forEach(subscription => {
130
      if (subscription instanceof Subscriber) {
131
        subscription.unsubscribe();
132
      }
133
    });
134
  }
135
  upload() {
136
    this.enableUpload = false;
137
    this.showReport = false;
138
    this.errorMessage = "";
139
    console.log(this.filesToUpload);
140
    if (this.filesToUpload.length == 0) {
141
      this.errorMessage = "There is no selected file to upload.";
142
      return;
143
    } else {
144
      if (this.filesToUpload[0].name.indexOf(".csv") == -1 ||
145
        (this.filesToUpload[0].type != "text/csv" && this.filesToUpload[0].type != "application/vnd.ms-excel")) {
146
        this.errorMessage = "No valid file type. The required type is CSV";
147
        return;
148
      }
149
    }
150
    this.loading.open();
151

    
152
    this.makeFileRequest(this.properties.utilsService + '/upload', [], this.filesToUpload).then((result) => {
153
      const rows = (result as any).split('\n');  // I have used space, you can use any thing.
154
      this.exceedsLimit = false;
155
      let invalid_rows = 0;
156
      this.duplicateIds = [];
157
      this.existedIds = [];
158
      this.allIds = [];
159
      this.foundIds = [];
160
      this.noValidIds = [];
161
      this.results.slice(0, this.results.length);
162
      this.notFoundIds = [];
163
      let currentLength = this.results.length;
164
      for (let i = 0; i < ( rows.length); i++) {
165
        if (rows[i] && rows[i] != null && rows[i]!="") {
166
          const values = rows[i].split(',');
167

    
168
          const id = BulkClaimComponent.removeDoubleQuotes(values[0]);
169
          if (DOI.isValidDOI(id)) {
170
            let accessMode = (values[1] != undefined) ? BulkClaimComponent.removeDoubleQuotes(values[1]) : "OPEN";
171
            accessMode = (BulkClaimComponent.validateAccessMode(accessMode) ? accessMode : "OPEN");
172
            let embargoDate = (values[2] != undefined) ? Dates.getDateFromString(BulkClaimComponent.removeDoubleQuotes(values[2])) : Dates.getDateToday();
173
            if (this.allIds.indexOf(id) > -1) {
174
              this.duplicateIds.push(id);
175
              this.duplicateIdsRow.push(i + 1);
176
            } else {
177
              this.allIds.push(id);
178
              if (currentLength < this.basketLimit){
179
                currentLength++;
180
                this.fetchResult(id, accessMode, Dates.getDateToString(embargoDate), i + 1);
181
              }else{
182
                this.exceedsLimit = true;
183
              }
184
            }
185
          } else {
186
            this.noValidIds.push(id);
187
            this.noValidIdsRow.push(i + 1);
188
          }
189
        } else {
190
          invalid_rows++;
191
        }
192

    
193
      }
194
      if (rows.length == 0 || rows.length == invalid_rows || rows.length == (invalid_rows + this.noValidIds.length) || this.basketLimit <= this.results.length) {
195
        this.endOfFetching();
196
      }
197

    
198
    }, (error) => {
199
      this.enableUpload = true;
200
      this.loading.close();
201
      this.errorMessage = "An error occured.";
202
      BulkClaimComponent.handleError("Error uploading file", error);
203
    });
204
  }
205

    
206
  private static removeDoubleQuotes(value) {
207
    if (value.indexOf('"') == 0) {
208
      value = value.substring(1, value.length);
209
    }
210
    const index = +value.indexOf('"');
211
    if (index == (value.length - 1) || index == (value.length - 2)) {
212
      value = value.substring(0, index);
213
    }
214
    return value;
215
  }
216

    
217
  private static validateAccessMode(value) {
218
    const accessModes = ["OPEN", "CLOSED", "EMBARGO"];
219
    return accessModes.indexOf(value) > -1;
220
  }
221

    
222
  fileChangeEvent(fileInput: any) {
223
    this.filesToUpload = <Array<File>>fileInput.target.files;
224
    this.upload();
225
  }
226

    
227
  makeFileRequest(url: string, params: Array<string>, files: Array<File>) {
228
    return new Promise((resolve, reject) => {
229
      const formData: any = new FormData();
230
      const xhr = new XMLHttpRequest();
231
      for (let i = 0; i < files.length; i++) {
232
        formData.append("uploads[]", files[i], files[i].name);
233
      }
234
      xhr.onreadystatechange = function () {
235
        if (xhr.readyState == 4) {
236
          if (xhr.status == 200) {
237
            resolve(xhr.response);
238
          } else {
239
            reject(xhr.response);
240
          }
241
        }
242
      }
243
      xhr.open("POST", url, true);
244
      xhr.send(formData);
245
    });
246
  }
247

    
248
  fetchResult(id: string, accessMode: string, date: string, row: number) {
249
    this.subscriptions.push(this._searchCrossrefService.searchCrossrefByDOIs([id], this.properties, true).subscribe(
250
      data => {
251

    
252
        const result:ClaimEntity = data[1][0];
253
        if (data[1].length > 0) {
254
          this.foundIds.push(id);
255
          result.result.accessRights = accessMode;
256
          result.result.embargoEndDate = date;
257
          if(!this.isSelected(result)){
258
            this.results.push(result);
259
          }else{
260
            this.existedIds.push(id);
261
          }
262
          this.endOfFetching();
263
        } else {
264
          this.searchInDatacite(id, accessMode, date, row);
265
          // this.notFoundIds.push(id);
266
        }
267
      },
268
      err => {
269
        //console.log(err);
270
        BulkClaimComponent.handleError("Error getting crossref by DOIs: " + id, err);
271
        this.notFoundIds.push(id);
272
        this.notFoundIdsRow.push(row);
273
        this.endOfFetching();
274
      }
275
    ));
276
  }
277

    
278
  searchInDatacite(id: string, accessMode: string, date: string, row: number) {
279
    this.subscriptions.push(this._searchDataciteService.getDataciteResultByDOI(id, this.properties, true).subscribe(
280
      result => {
281

    
282
        if (result) {
283
          this.foundIds.push(id);
284
          result.result.accessRights = accessMode;
285
          result.result.embargoEndDate = date;
286
          if(!this.isSelected(result)){
287
            this.results.push(result);
288
          }else{
289
            this.existedIds.push(id);
290
          }
291
        } else {
292
          this.notFoundIds.push(id);
293
          this.notFoundIdsRow.push(row);
294
        }
295
        this.endOfFetching();
296
      },
297
      err => {
298
        //console.log(err);
299
        BulkClaimComponent.handleError("Error getting datacite resultentityI: " + id, err);
300
        this.notFoundIds.push(id);
301
        this.notFoundIdsRow.push(row);
302
        this.endOfFetching();
303
      }
304
    ));
305
  }
306

    
307
  endOfFetching() {
308
    // if (this.basketLimit <= this.results.length) {
309
    //   this.enableUpload = true;
310
    //   this.loading.close();
311
    //   return;
312
    // }
313
    // console.log(this.allIds.length+" "+this.foundIds.length +" "+ this.notFoundIds.length+" "+this.existedIds.length + " " + this.results.length);
314
    if (this.allIds.length == this.foundIds.length + this.notFoundIds.length || this.basketLimit <= (this.results.length+this.existedIds.length+this.notFoundIds.length)) {
315
      this.showReport = true;
316
      this.enableUpload = true;
317
      this.loading.close();
318
      if (this.results != null) {
319
        localStorage.setItem(this.localStoragePrefix, JSON.stringify(this.results));
320
      }
321
    }
322

    
323
  }
324

    
325
  private static handleError(message: string, error) {
326
    console.error("Bulk Claim (component): " + message, error);
327
  }
328

    
329
  private isSelected(result: ClaimEntity) {
330

    
331
    let found: boolean = false;
332
    const id = result.id;
333
    for (let _i = 0; _i < this.results.length; _i++) {
334
      let item = this.results[_i];
335
      if (item.id && item.id == id) {
336
        found = true;
337
        break;
338
      }
339
    }
340
    return found;
341
    // indexOf doesn't work when results came from
342
    // return this.selectedResults.indexOf(entity)!=-1;
343
  }
344
}
(1-1/2)