Project

General

Profile

1
package eu.dnetlib.data.claims.handler;
2

    
3
import eu.dnetlib.data.claims.entity.*;
4
import eu.dnetlib.data.claims.sql.SQLStoreException;
5
import eu.dnetlib.data.claims.sql.SqlDAO;
6
import eu.dnetlib.data.claims.utils.ClaimUtils;
7
import eu.dnetlib.data.claims.utils.JsonldBuilder;
8
import eu.dnetlib.data.claims.utils.QueryGenerator;
9
import org.apache.logging.log4j.LogManager;
10
import org.apache.logging.log4j.Logger;
11

    
12
import java.sql.ResultSet;
13
import java.sql.SQLException;
14
import java.text.DateFormat;
15
import java.text.SimpleDateFormat;
16
import java.util.ArrayList;
17
import java.util.Date;
18
import java.util.List;
19

    
20
/**
21
 * Created by argirok on 9/3/2016.
22
 */
23
public class FetchClaimHandler {
24
    private Logger log = LogManager.getLogger(this.getClass());
25

    
26
    SqlDAO sqlDAO = null;
27
    QueryGenerator queryGenerator = null;
28

    
29
    public FetchClaimHandler(){
30
//        ApplicationContext context = new ClassPathXmlApplicationContext("../claims/migration/springContext-claims.xml");
31
//        sqlDAO = context.getBean(SqlDAO.class);
32

    
33
    }
34

    
35
    public String claims2JSON(List<Claim> claims) throws Exception {
36
        return JsonldBuilder.toJsonld(claims);
37
    }
38
    public String claim2JSON(Claim claim) throws Exception {
39
        return JsonldBuilder.toJsonld(claim);
40
    }
41

    
42
    /**
43
     *
44
     * @param user - user e-mail stored in claims DB
45
     * @param limit returns at most 'limit' claims  (use -1 or null for no limit)
46
     * @param offset returns claims after 'offset' position (null for no offset)
47
     * @return
48
     * @throws Exception
49
     */
50
    public List<Claim> fetchClaimsByUser(String user, Integer limit, Integer offset, List<String> types, boolean addCurationInfo) throws Exception, SQLStoreException {
51
        return fetchClaimsByUser(user,limit,offset,null,"claim.claim_date",true, types, addCurationInfo);
52
    }
53
    /**
54
     *
55
     * @param user - user e-mail stored in claims DB
56
     * @param limit returns at most 'limit' claims  (use -1 or null for no limit)
57
     * @param offset returns claims after 'offset' position (null for no offset)
58
     * @param keyword filters in specific fields per type (result: {title,doi},project: {name.acronym. funder_name, funder_acronym}, context: {name})
59
     * @param orderField
60
     * @param descending
61
     * @return
62
     * @throws Exception
63
     */
64
    public List<Claim> fetchClaimsByUser(String user, Integer limit, Integer offset,String keyword,String orderField,boolean descending, List<String> types, boolean addCurationInfo) throws Exception, SQLStoreException {
65
        log.info("Fetching claims for user"+user);
66
        ArrayList<Object> params = new ArrayList<>();
67
        String query = queryGenerator.generateFetchClaimsByUser(user, limit, offset,keyword,orderField,descending, types, params);
68
        ResultSet rs = sqlDAO.executePreparedQuery(query, params);
69
        return fetchClaimsByResultSet(rs, addCurationInfo);
70
    }
71
    /**
72
     *
73
     * @param limit returns at most 'limit' claims  (use -1 or null for no limit)
74
     * @param offset returns claims after 'offset' position (null for no offset)
75
     * @return
76
     * @throws Exception
77
     */
78
    public List<Claim> fetchAllClaims( Integer limit, Integer offset, boolean addCurationInfo) throws Exception, SQLStoreException {
79
        log.info("Fetching all claims");
80
         return fetchAllClaims(limit,offset,null,"claim.claim_date",true,new ArrayList<String>(), addCurationInfo);
81
    }
82

    
83
    /**
84
     *
85
     * @param limit returns at most 'limit' claims  (use -1 or null for no limit)
86
     * @param offset returns claims after 'offset' position (null for no offset)
87
     * @param keyword  filters in specific fields per type (result: {title,doi},project: {name.acronym. funder_name, funder_acronym}, context: {name})
88
     * @param orderField
89
     * @param descending
90
     * @return
91
     * @throws Exception
92
     */
93
    public List<Claim> fetchAllClaims( Integer limit, Integer offset, String keyword, String orderField, boolean descending, List<String> types, boolean addCurationInfo) throws Exception, SQLStoreException {
94
        log.info("Fetching all claims");
95
        ArrayList<Object> params = new ArrayList<>();
96
        String query = queryGenerator.generateFetchClaims(limit, offset,keyword,orderField,descending,types, params);
97
        ResultSet rs = sqlDAO.executePreparedQuery(query, params);
98
        return fetchClaimsByResultSet(rs, addCurationInfo);
99
    }
100

    
101
    /**
102
     *
103
     * @param dateFrom
104
     * @param dataTo
105
     * @param limit returns at most 'limit' claims  (use -1 or null for no limit)
106
     * @param offset returns claims after 'offset' position (null for no offset)
107
     * @return
108
     * @throws Exception
109
     */
110
    public List<Claim> fetchClaimsByDate(String dateFrom, String dataTo, Integer limit, Integer offset, boolean addCurationInfo) throws Exception, SQLStoreException {
111
         return fetchClaimsByDate(dateFrom, dataTo, limit, offset, null, "claim.claim_date",true, new ArrayList<String>(),addCurationInfo);
112
    }
113
    /**
114
     *
115
     * @param dateFrom
116
     * @param dataTo
117
     * @param limit returns at most 'limit' claims  (use -1 or null for no limit)
118
     * @param offset returns claims after 'offset' position (null for no offset)
119
     * @return
120
     * @throws Exception
121
     */
122
    public List<Claim> fetchClaimsByDate(String dateFrom, String dataTo, Integer limit, Integer offset, String keyword, String orderField, boolean descending, List<String> types, boolean addCurationInfo) throws Exception, SQLStoreException {
123
        ArrayList<Object> params = new ArrayList<>();
124
        String query = queryGenerator.generateFetchClaimsByDate(dateFrom, dataTo, limit, offset,keyword, orderField, descending,types, params);
125
        ResultSet rs = sqlDAO.executePreparedQuery(query, params);
126
        return fetchClaimsByResultSet(rs, addCurationInfo);
127
    }
128

    
129
    public List<Claim> fetchClaimsByDateForDashboards(String dateFrom, String dataTo, Integer limit, Integer offset, boolean addCurationInfo, ArrayList<String> dashboards) throws Exception, SQLStoreException {
130
        ArrayList<Object> params = new ArrayList<>();
131
        String query = queryGenerator.generateFetchClaimsByDateForDashboards(dateFrom, dataTo, limit, offset,null, "claim.claim_date", true,new ArrayList<String>(), params, dashboards);
132
        ResultSet rs = sqlDAO.executePreparedQuery(query, params);
133
        return fetchClaimsByResultSet(rs, addCurationInfo);
134
    }
135
    /**
136
     *
137
     * @param dateFrom
138
     * @param dataTo
139
     * @param openaireId for project
140
     * @param limit returns at most 'limit' claims  (use -1 or null for no limit)
141
     * @param offset returns claims after 'offset' position (null for no offset)
142
     * @return
143
     * @throws Exception
144
     */
145
    public int fetchNumberOfClaimsByDateAndOpenaireId(String dateFrom, String dataTo, String openaireId, Integer limit, Integer offset, String keyword, String orderField, boolean descending, List<String> types, boolean addCurationInfo) throws Exception, SQLStoreException {
146
        ArrayList<Object> params = new ArrayList<>();
147
        String query = queryGenerator.generateFetchNumberOfClaimsByDateAndOpenaireId(dateFrom, dataTo, openaireId, limit, offset,keyword, orderField, descending,types, params);
148
        ResultSet rs = sqlDAO.executePreparedQuery(query, params);
149
        return countClaimsByResultSet(rs);
150
    }
151

    
152
    /**
153
     *
154
     * @param openaireId for project
155
     * @param limit returns at most 'limit' claims  (use -1 or null for no limit)
156
     * @param offset returns claims after 'offset' position (null for no offset)
157
     * @return
158
     * @throws Exception
159
     */
160
    public List<Claim> fetchClaimsByProject(String openaireId, Integer limit, Integer offset, boolean addCurationInfo) throws Exception, SQLStoreException {
161

    
162
        return fetchClaimsByProject(openaireId,limit, offset, null, "claim.claim_date",true, new ArrayList<String>(), addCurationInfo);
163
    }
164
    /**
165
     *
166
     * @param openaireId for project
167
     * @param limit returns at most 'limit' claims  (use -1 or null for no limit)
168
     * @param offset returns claims after 'offset' position (null for no offset)
169
     * @param orderField
170
     * @param descending
171
     * @return
172
     * @throws Exception
173
     */
174
    public List<Claim> fetchClaimsByProject(String openaireId, Integer limit, Integer offset, String keyword, String orderField, boolean descending, List<String> types, boolean addCurationInfo) throws Exception, SQLStoreException {
175
        log.info("Fetching claims by project ID:"+openaireId);
176
        ArrayList<Object> params = new ArrayList<>();
177
        String query = queryGenerator.generateFetchClaimsByProject(openaireId, limit, offset, keyword, orderField,descending,types, params);
178
        ResultSet rs = sqlDAO.executePreparedQuery(query, params);
179
        return fetchClaimsByResultSet(rs, addCurationInfo);
180
    }
181
    /**
182
     *
183
     * @param openaireId of Funder
184
     * @param limit
185
     * @param offset
186
     * @return
187
     * @throws Exception
188
     */
189
    public List<Claim> fetchClaimsByFunder(String openaireId, Integer limit, Integer offset, boolean addCurationInfo) throws Exception, SQLStoreException {
190
         return fetchClaimsByFunder(openaireId,limit,offset,null,"claim.claim_date",true, new ArrayList<String>(), addCurationInfo);
191
    }
192
    /**
193
     *
194
     * @param openaireId of Funder
195
     * @param limit
196
     * @param offset
197
     * @param orderField
198
     * @param descending
199
     * @return
200
     * @throws Exception
201
     */
202
    public List<Claim> fetchClaimsByFunder(String openaireId, Integer limit, Integer offset,String keyword, String orderField, boolean descending, List<String> types, boolean addCurationInfo) throws Exception, SQLStoreException {
203
        ArrayList<Object> params = new ArrayList<>();
204
        String query = queryGenerator.generateFetchClaimsByFunder(openaireId, limit, offset,keyword, orderField,descending,types, params);
205
        ResultSet rs = sqlDAO.executePreparedQuery(query, params);
206
        return fetchClaimsByResultSet(rs, addCurationInfo);
207
    }
208
    /**
209
     *
210
     * @param openaireId for result
211
     * @param limit returns at most 'limit' claims  (use -1 or null for no limit)
212
     * @param offset returns claims after 'offset' position (null for no offset)
213
     * @return
214
     * @throws Exception
215
     */
216
    public List<Claim> fetchClaimsByResult(String openaireId, Integer limit, Integer offset, boolean addCurationInfo) throws Exception, SQLStoreException {
217
         return fetchClaimsByResult(openaireId,limit,offset,null, "claim.claim_date",true, new ArrayList<String>(), addCurationInfo);
218
    }
219
    /**
220
     *
221
     * @param openaireId for result
222
     * @param limit returns at most 'limit' claims  (use -1 or null for no limit)
223
     * @param offset returns claims after 'offset' position (null for no offset)
224
     * @param orderField
225
     * @param descending
226
     * @return
227
     * @throws Exception
228
     */
229
    public List<Claim> fetchClaimsByResult(String openaireId, Integer limit, Integer offset,String keyword, String orderField, boolean descending, List<String> types, boolean addCurationInfo) throws Exception, SQLStoreException {
230
        log.info("Fetching claims by result ID:"+openaireId);
231
        ArrayList<Object> params = new ArrayList<>();
232
        String query = queryGenerator.generateFetchClaimsByResult(openaireId, limit, offset,keyword, orderField, descending,types, params);
233
        ResultSet rs = sqlDAO.executePreparedQuery(query, params);
234
        return fetchClaimsByResultSet(rs, addCurationInfo);
235
    }
236
    /**
237
     *
238
     * @param openaireId
239
     * @param limit returns at most 'limit' claims  (use -1 or null for no limit)
240
     * @param offset returns claims after 'offset' position (null for no offset)
241
     * @return
242
     * @throws Exception
243
     */
244
    public List<Claim> fetchClaimsByContext(String openaireId, Integer limit, Integer offset, boolean addCurationInfo) throws Exception, SQLStoreException {
245
         return fetchClaimsByContext(openaireId,limit,offset,null,"claim.claim_date",true, new ArrayList<String>(), addCurationInfo);
246
    }
247
    /**
248
     *
249
     * @param openaireId
250
     * @param limit returns at most 'limit' claims  (use -1 or null for no limit)
251
     * @param offset returns claims after 'offset' position (null for no offset)
252
     * @param orderField
253
     * @param descending
254
     * @return
255
     * @throws Exception
256
     */
257
    public List<Claim> fetchClaimsByContext(String openaireId, Integer limit, Integer offset, String keyword, String orderField, boolean descending, List<String> types, boolean addCurationInfo) throws Exception, SQLStoreException {
258
        log.info("Fetching claims by context ID:"+openaireId);
259
        ArrayList<Object> params = new ArrayList<>();
260
        String query = queryGenerator.generateFetchClaimsByContext(openaireId, limit, offset,keyword,orderField, descending,types, params);
261
        ResultSet rs = sqlDAO.executePreparedQuery(query, params);
262
        return fetchClaimsByResultSet(rs, addCurationInfo);
263
    }
264
    public Claim fetchClaimById(String claimId, boolean addCurationInfo) throws Exception, SQLStoreException {
265
        ArrayList<Object> params = new ArrayList<>();
266
        String query = queryGenerator.generateSelectClaimQuery(claimId, params);
267
        ResultSet rs = sqlDAO.executePreparedQuery(query, params);
268
        if(rs.next()) {
269
            String sourceType =rs.getString(2);
270
//            String sourceId =rs.getString(3);
271
            String targetType =rs.getString(4);
272
//            String targetId =rs.getString(5);
273
            ArrayList<Object> params2 = new ArrayList<>();
274
            String query2 = queryGenerator.generateFetchClaimsByClaimId(claimId,sourceType,targetType, params2);
275
           rs = sqlDAO.executePreparedQuery(query2, params2);
276
        }else{
277
            log.info("No claim with id : "+ claimId+"\n");
278
        }
279
        Claim claim = null;
280
        List<Claim> claims=fetchClaimsByResultSet(rs, addCurationInfo);
281
        if(claims.size()==1){
282
            claim=claims.get(0);
283
        }
284
        return claim;
285
    }
286

    
287
    /*
288
    public List<Claim> fetchClaimsByToken(String token, String email, Integer limit, Integer offset, boolean addCurationInfo) throws Exception {
289
        return fetchClaimsByToken(token, email, limit, offset, null, "claim.claim_date",true, new ArrayList<String>(), addCurationInfo);
290
    }
291

    
292
    public List<Claim> fetchClaimsByToken(String token, String email, Integer limit, Integer offset, String keyword, String orderField, boolean descending, List<String> types, boolean addCurationInfo) throws Exception {
293
        ResultSet rs = sqlDAO.executePreparedQuery(queryGenerator.generateFetchClaimsByProjectToken(token, email, limit, offset, keyword, orderField,descending,types));
294
        return fetchClaimsByResultSet(rs, addCurationInfo);
295
    }
296
    */
297

    
298

    
299
    public List<Claim> fetchClaimsByResultSet(ResultSet rs, boolean addCurationInfo) throws Exception {
300
        List<Claim> claims= new ArrayList<Claim>();
301
        while(rs.next()) {
302
            Claim claim= new Claim();
303
            claims.add(claim);
304
            Integer i = 1;
305
            claim.setId(rs.getString(i++));
306
            DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
307
            Date date = format.parse(rs.getString(i++));
308
            claim.setDate(date);
309
            claim.setUserMail(rs.getString(i++));
310
            claim.setSourceType(rs.getString(i++));
311
            claim.setTargetType(rs.getString(i++));
312
            claim.setSemantics(rs.getString(i++));
313
            if(addCurationInfo) {
314
                String curationDate =rs.getString(i++);
315
                if(curationDate != null) {
316
                    date = format.parse(curationDate);
317
                    claim.setCurationDate(date);
318
                }
319
                claim.setCuratedBy(rs.getString(i++));
320
            }else{
321
                i+=2;
322
            }
323
            claim.setApproved(rs.getBoolean(i++));
324
            claim.setClaimedInDashboard(rs.getString(i++));
325
            claim.setSource(buildEntity(rs,i,claim.getSourceType()));
326
            claim.setTarget(buildEntity(rs,i+12,claim.getTargetType()));
327

    
328
        }
329
        return claims;
330
    }
331
    public Integer countClaimsByResultSet(ResultSet rs) throws Exception {
332

    
333
        while(rs.next()) {
334
            return rs.getInt(1);
335
        }
336
        return null;
337
    }
338
    public List<Project> getProjectsByResultSet(ResultSet rs, boolean addCurationInfo) throws Exception {
339
        List<Project> projects= new ArrayList<Project>();
340
        while(rs.next()) {
341
            Project project=(Project)(buildEntity(rs, 1, ClaimUtils.PROJECT));
342
             projects.add(project);
343
        }
344
        return projects;
345
    }
346
    public List<Context> getContextsByResultSet(ResultSet rs, boolean addCurationInfo) throws Exception {
347
        List<Context> contexts= new ArrayList<Context>();
348
        while(rs.next()) {
349
            Context context=(Context)(buildEntity(rs, 1, ClaimUtils.CONTEXT));
350
            contexts.add(context);
351
        }
352
        return contexts;
353
    }
354
    public Integer countAllClaims(String keyword, List<String> types) throws Exception, SQLStoreException {
355
        ArrayList<Object> params = new ArrayList<>();
356
        String query = queryGenerator.generateCountAllClaims(keyword,types, params);
357
        ResultSet rs = sqlDAO.executePreparedQuery(query, params);
358
        return countClaimsByResultSet(rs);
359
    }
360
    public Integer countClaimsByUser(String user,String keyword, List<String> types) throws Exception, SQLStoreException {
361
        ArrayList<Object> params = new ArrayList<>();
362
        String query = queryGenerator.generateCountByUser(user,keyword,types, params);
363
        ResultSet rs = sqlDAO.executePreparedQuery(query, params);
364
        return countClaimsByResultSet(rs);
365
    }
366
    public Integer countClaimsByProject(String projectId,String keyword, List<String> types) throws Exception, SQLStoreException {
367
        ArrayList<Object> params = new ArrayList<>();
368
        String query = queryGenerator.generateCountByProject(projectId,keyword,types, params);
369
        ResultSet rs = sqlDAO.executePreparedQuery(query, params);
370
        return countClaimsByResultSet(rs);
371
    }
372
    /*
373
    public Integer countClaimsByProjectToken(String projectToken,String email,String keyword, List<String> types)throws Exception {
374
        ResultSet rs = sqlDAO.executePreparedQuery(queryGenerator.generateCountByProjectToken(projectToken,email,keyword,types));
375
        return countClaimsByResultSet(rs);
376
    }
377
    */
378
    public Integer countClaimsByFunder(String funderId,String keyword, List<String> types) throws Exception, SQLStoreException {
379
        ArrayList<Object> params = new ArrayList<>();
380
        String query = queryGenerator.generateCountByFunder(funderId,keyword,types, params);
381
        ResultSet rs = sqlDAO.executePreparedQuery(query, params);
382
        return countClaimsByResultSet(rs);
383
    }
384
    public Integer countClaimsByResult(String resultId,String keyword, List<String> types) throws Exception, SQLStoreException {
385
        ArrayList<Object> params = new ArrayList<>();
386
        String query = queryGenerator.generateCountByResult(resultId,keyword,types, params);
387
        ResultSet rs = sqlDAO.executePreparedQuery(query, params);
388
        return countClaimsByResultSet(rs);
389
    }
390
    public Integer countClaimsByContext(String contextId,String keyword, List<String> types) throws Exception, SQLStoreException {
391
        ArrayList<Object> params = new ArrayList<>();
392
        String query = queryGenerator.generateCountByContext(contextId,keyword,types, params);
393
        ResultSet rs = sqlDAO.executePreparedQuery(query, params);
394
        return countClaimsByResultSet(rs);
395
    }
396
    public Integer countClaimsByDate(String dateFrom,String dateTo,String keyword, List<String> types) throws Exception, SQLStoreException {
397
        ArrayList<Object> params = new ArrayList<>();
398
        String query = queryGenerator.generateCountByDate(dateFrom,dateTo,keyword,types, params);
399
        ResultSet rs = sqlDAO.executePreparedQuery(query, params);
400
        return countClaimsByResultSet(rs);
401
    }
402

    
403
    public List<Project> getAllclaimedProjects(boolean addCurationInfo) throws Exception, SQLStoreException {
404
        ResultSet rs = sqlDAO.executePreparedQuery(queryGenerator.generateFetchAllProjectsQuery());
405
        return getProjectsByResultSet(rs,addCurationInfo);
406
    }
407
    public List<Context> getAllclaimedContexts( boolean addCurationInfo) throws Exception, SQLStoreException {
408
        ResultSet rs = sqlDAO.executePreparedQuery(queryGenerator.generateFetchAllContextsQuery());
409
        return getContextsByResultSet(rs,addCurationInfo);
410
    }
411
    /**
412
     *
413
     * @param rs
414
     * @param index
415
     * @return
416
     * @throws SQLException
417
     */
418
    private static Result buildResult(ResultSet rs, Integer index) throws SQLException {
419
        Result result = new Result();
420
        result.setOpenaireId(rs.getString(index));
421
        result.setTitle(rs.getString(index + 1));
422
        result.setResultType(rs.getString(index + 2));
423
        result.setDoi(rs.getString(index + 3));
424
        result.setOrcidworkid(rs.getString(index + 4));
425
        result.setAccessRights(rs.getString(index + 5));
426
        result.setEmbargoEndDate(rs.getString(index + 6));
427
        result.setBestLicense(rs.getString(index + 7));
428
        result.setExternalUrl(rs.getString(index + 8));
429
        result.setCollectedFrom(rs.getString(index + 9));
430
        result.setRecordPath(rs.getString(index + 10));
431
        result.setRecordFormat(rs.getString(index + 11));
432

    
433
        return result;
434

    
435
    }
436
    private static Project buildProject(ResultSet rs, Integer index) throws SQLException {
437
        Project project = new Project();
438
        project.setOpenaireId(rs.getString(index));
439
        project.setName(rs.getString(index + 1));
440
        project.setAcronym(rs.getString(index + 2));
441
        project.setFunderId(rs.getString(index + 3));
442
        project.setFunderName(rs.getString(index + 4));
443
        project.setFunderShortName(rs.getString(index + 5));
444
        String contactP = rs.getString(index + 7);
445
        project.setContactEmails(new ArrayList<String>());
446
        if(contactP != null) {
447
            for (String s : contactP.split(",")) {
448
                project.getContactEmails().add(s);
449
            }
450
        }
451

    
452
        return project;
453

    
454
    }
455
    private static Context buildContext(ResultSet rs, Integer index) throws SQLException {
456
        Context context = new Context();
457
        context.setOpenaireId(rs.getString(index));
458
        context.setTitle(rs.getString(index + 1));
459
        return context;
460

    
461
    }
462
    private static OpenaireEntity buildEntity(ResultSet rs, Integer index, String type) throws SQLException {
463
        OpenaireEntity openaireEntity = null;
464
        if(type == null){
465
            openaireEntity = null;
466
        }else if(type.equals(ClaimUtils.PUBLICATION)||type.equals(ClaimUtils.DATASET)||type.equals(ClaimUtils.SOFTWARE)||type.equals(ClaimUtils.OTHER)) {
467
            openaireEntity= buildResult(rs, index);
468
        }else if(type.equals(ClaimUtils.PROJECT)){
469
            openaireEntity = buildProject(rs,index);
470
        }else if(type.equals(ClaimUtils.CONTEXT)){
471
            openaireEntity = buildContext(rs,index);
472
        }
473
        return openaireEntity;
474
    }
475

    
476
    public SqlDAO getSqlDAO() {
477
        return sqlDAO;
478
    }
479

    
480
    public void setSqlDAO(SqlDAO sqlDAO) {
481
        this.sqlDAO = sqlDAO;
482
    }
483

    
484
    public QueryGenerator getQueryGenerator() {
485
        return queryGenerator;
486
    }
487

    
488
    public void setQueryGenerator(QueryGenerator queryGenerator) {
489
        this.queryGenerator = queryGenerator;
490
    }
491
}
(5-5/13)