• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

askomics / flaskomics / 6590008757

20 Oct 2023 03:58PM UTC coverage: 83.758% (+0.4%) from 83.31%
6590008757

push

github-actions

web-flow
Merge pull request #420 from askomics/dev

Release 4.5.0

633 of 633 new or added lines in 29 files covered. (100.0%)

6240 of 7450 relevant lines covered (83.76%)

0.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.19
/askomics/libaskomics/Result.py
1
import os
1✔
2
import csv
1✔
3
import json
1✔
4
import time
1✔
5

6
from bioblend import galaxy
1✔
7

8
from askomics.libaskomics.Database import Database
1✔
9
from askomics.libaskomics.Params import Params
1✔
10
from askomics.libaskomics.Utils import Utils
1✔
11

12
from pkg_resources import get_distribution
1✔
13

14

15
class Result(Params):
1✔
16
    """Result represent a query result file
17

18
    Attributes
19
    ----------
20
    celery_id : str
21
        Celery job id
22
    file_name : str
23
        file name
24
    file_path : str
25
        file path
26
    graph_state : dict
27
        The json query graph state
28
    id : int
29
        database id
30
    result_path : str
31
        results directory path
32
    """
33

34
    def __init__(self, app, session, result_info, force_no_db=False, owner=False, admin=False):
1✔
35
        """init object
36

37
        Parameters
38
        ----------
39
        app : Flask
40
            flask app
41
        session :
42
            AskOmics session, contain the user
43
        result_info : dict
44
            Result file info
45
        """
46
        Params.__init__(self, app, session)
1✔
47

48
        if "user" in self.session:
1✔
49
            self.result_path = "{}/{}_{}/results".format(
1✔
50
                self.settings.get("askomics", "data_directory"),
51
                self.session['user']['id'],
52
                self.session['user']['username']
53
            )
54

55
        if "id" in result_info and not force_no_db:
1✔
56
            self.id = result_info["id"]
1✔
57
            if not self.set_info_from_db_with_id(owner=owner, admin=admin):
1✔
58
                return None
×
59
        else:
60
            self.id = result_info["id"] if "id" in result_info else None
1✔
61
            self.graph_state = result_info["graph_state"] if "graph_state" in result_info else None
1✔
62
            self.graphs = result_info["graphs"] if "graphs" in result_info else []
1✔
63
            self.endpoints = result_info["endpoints"] if "endpoints" in result_info else []
1✔
64
            self.sparql_query = result_info["sparql_query"] if "sparql_query" in result_info else None
1✔
65
            self.celery_id = result_info["celery_id"] if "celery_id" in result_info else None
1✔
66
            self.file_name = result_info["file_name"] if "file_name" in result_info else Utils.get_random_string(10)
1✔
67
            self.file_path = "{}/{}".format(self.result_path, self.file_name)
1✔
68
            self.start = None
1✔
69
            self.end = None
1✔
70
            self.nrows = 0
1✔
71
            self.has_form_attr = False
1✔
72
            self.template = False
1✔
73
            self.form = False
1✔
74

75
    def clean_node(self, node):
1✔
76
        """Clean a node by removing coordinates and other stuff
77

78
        Parameters
79
        ----------
80
        node : dict
81
            A graph node
82

83
        Returns
84
        -------
85
        dict
86
            Cleaned node
87
        """
88
        node.pop("__indexColor")
1✔
89
        node.pop("index")
1✔
90
        node.pop("x")
1✔
91
        node.pop("y")
1✔
92
        node.pop("vx")
1✔
93
        node.pop("vy")
1✔
94

95
        return node
1✔
96

97
    def clean_link(self, link):
1✔
98
        """Clean a link by removing coordinates and other stuff
99

100
        Parameters
101
        ----------
102
        link : dict
103
            A graph link
104

105
        Returns
106
        -------
107
        dict
108
            Cleaned link
109
        """
110
        link.pop("__indexColor")
×
111
        link.pop("__controlPoints")
×
112
        link.pop("index")
×
113

114
        # link["source"] = self.clean_node(link["source"])
115
        # link["target"] = self.clean_node(link["target"])
116

117
        link["source"] = link["source"]["id"]
×
118
        link["target"] = link["target"]["id"]
×
119

120
        return link
×
121

122
    def format_graph_state(self, d3_graph_state):
1✔
123
        """Format Graph state
124

125
        Remove coordinates and other things
126

127
        Parameters
128
        ----------
129
        d3_graph_state : dict
130
            The d3 graph state
131

132
        Returns
133
        -------
134
        dict
135
            formatted graph state
136
        """
137
        new_nodes = []
1✔
138
        new_links = []
1✔
139
        new_attr = []
1✔
140

141
        try:
1✔
142

143
            for node in d3_graph_state["nodes"]:
1✔
144

145
                if node["suggested"]:
1✔
146
                    continue
1✔
147

148
                new_node = self.clean_node(node)
1✔
149
                new_nodes.append(new_node)
1✔
150

151
            for link in d3_graph_state["links"]:
1✔
152

153
                if link["suggested"]:
1✔
154
                    continue
1✔
155

156
                new_link = self.clean_link(link)
×
157
                new_links.append(new_link)
×
158

159
            new_attr = d3_graph_state["attr"]
1✔
160
        except Exception:
×
161
            return {}
×
162

163
        return {
1✔
164
            "nodes": new_nodes,
165
            "links": new_links,
166
            "attr": new_attr
167
        }
168

169
    def get_file_name(self):
1✔
170
        """Get file name
171

172
        Returns
173
        -------
174
        str
175
            file name
176
        """
177
        return self.file_name
1✔
178

179
    def get_dir_path(self):
1✔
180
        """Get directory path
181

182
        Returns
183
        -------
184
        str
185
            directory path
186
        """
187
        return self.result_path
1✔
188

189
    def get_graph_state(self, formated=False):
1✔
190
        """Get get_graph_state
191

192
        Returns
193
        -------
194
        dict
195
            graph state
196
        """
197
        if formated:
1✔
198
            graph = self.format_graph_state(self.graph_state)
1✔
199
        else:
200
            graph = self.graph_state
1✔
201

202
        # Retrocompatibility with < 4.1.0
203
        if 'attr' in graph:
1✔
204
            for val in graph['attr']:
1✔
205
                if 'entityUri' in val and 'entityUris' not in val:
1✔
206
                    val['entityUris'] = [val['entityUri']]
×
207

208
        return graph
1✔
209

210
    def get_sparql_query(self):
1✔
211
        """Get the sparql query if exists
212

213
        Returns
214
        -------
215
        string
216
            The sparql query
217
        """
218
        return self.sparql_query
1✔
219

220
    def update_celery(self, celery_id):
1✔
221
        """Update celery id of result in database
222

223
        Parameters
224
        ----------
225
        celery_id : string
226
            DescriThe celery idption
227
        """
228
        database = Database(self.app, self.session)
1✔
229

230
        query = '''
1✔
231
        UPDATE results SET
232
        celery_id=?
233
        WHERE user_id = ? AND id = ?
234
        '''
235

236
        database.execute_sql_query(query, (celery_id, self.session['user']['id'], self.id))
1✔
237

238
    def set_celery_id(self, celery_id):
1✔
239
        """Set celery id
240

241
        Parameters
242
        ----------
243
        celery_id : string
244
            The celery id
245
        """
246
        self.celery_id = celery_id
×
247

248
    def set_info_from_db_with_id(self, owner=False, admin=False):
1✔
249
        """Set result info from the db"""
250
        database = Database(self.app, self.session)
1✔
251

252
        if 'user' not in self.session:
1✔
253
            if owner:
×
254
                return False
×
255
            query = '''
×
256
            SELECT celery_id, path, graph_state, start, end, nrows, sparql_query, graphs_and_endpoints, has_form_attr, template, form
257
            FROM results
258
            WHERE public = ? AND id = ?
259
            '''
260
            rows = database.execute_sql_query(query, (True, self.id))
×
261
        else:
262
            if admin:
1✔
263
                select_subquery = "WHERE id = ?"
1✔
264
                params = (self.id,)
1✔
265
            elif owner:
1✔
266
                select_subquery = "WHERE user_id = ? AND id = ?"
1✔
267
                params = (self.session["user"]["id"], self.id,)
1✔
268
            else:
269
                select_subquery = "WHERE (user_id = ? OR public = ?) AND id = ?"
1✔
270
                params = (self.session["user"]["id"], True, self.id,)
1✔
271
            query = '''
1✔
272
            SELECT celery_id, path, graph_state, start, end, nrows, sparql_query, graphs_and_endpoints, has_form_attr, template, form
273
            FROM results
274
            {}
275
            '''.format(select_subquery)
276

277
            rows = database.execute_sql_query(query, params)
1✔
278

279
        if not rows:
1✔
280
            return False
×
281

282
        self.celery_id = rows[0][0] if rows[0][0] else ''
1✔
283
        self.file_path = rows[0][1] if rows[0][1] else ''
1✔
284
        self.file_name = os.path.basename(self.file_path)
1✔
285
        self.graph_state = json.loads(rows[0][2])
1✔
286
        self.start = rows[0][3]
1✔
287
        self.end = rows[0][4]
1✔
288
        self.nrows = rows[0][5]
1✔
289
        self.sparql_query = rows[0][6]
1✔
290
        self.has_form_attr = rows[0][8] if rows[0][8] else False
1✔
291
        self.template = rows[0][9] if rows[0][9] else False
1✔
292
        self.form = rows[0][10] if rows[0][10] else False
1✔
293

294
        gne = json.loads(rows[0][7]) if rows[0][7] else {"graphs": [], "endpoints": []}
1✔
295
        self.graphs = gne["graphs"]
1✔
296
        self.endpoints = gne["endpoints"]
1✔
297

298
        return True
1✔
299

300
    def get_file_preview(self):
1✔
301
        """Get a preview of the results file
302

303
        Returns
304
        -------
305
        list, list
306
            headers and preview
307
        """
308
        with open(self.file_path) as file:
1✔
309
            spamreader = csv.reader(file, delimiter='\t')
1✔
310
            first = True
1✔
311
            preview_limit = self.settings.getint("triplestore", "preview_limit")
1✔
312
            row_number = 0
1✔
313
            headers = []
1✔
314
            data = []
1✔
315
            for row in spamreader:
1✔
316
                # header
317
                if first:
1✔
318
                    headers = row
1✔
319
                    first = False
1✔
320
                    continue
1✔
321

322
                # rows
323
                row_dict = {}
1✔
324
                for i in range(len(row)):
1✔
325
                    row_dict[headers[i]] = row[i]
1✔
326
                data.append(row_dict)
1✔
327
                row_number += 1
1✔
328
                if row_number >= preview_limit:
1✔
329
                    break
×
330

331
            return headers, data
1✔
332

333
    def save_result_in_file(self, headers, results):
1✔
334
        """Save query results in a csv file
335

336
        Parameters
337
        ----------
338
        headers : list
339
            List of results headers
340
        results : list
341
            Query results
342

343
        Returns
344
        -------
345
        int
346
            File size
347
        """
348
        with open(self.file_path, 'w') as file:
1✔
349
            writer = csv.DictWriter(file, delimiter="\t", fieldnames=headers)
1✔
350
            writer.writeheader()
1✔
351
            for row in results:
1✔
352
                self.nrows += 1
1✔
353
                writer.writerow(row)
1✔
354

355
        return os.path.getsize(self.file_path)
1✔
356

357
    def save_in_db(self, start=None):
1✔
358
        """Save results file info into the database"""
359
        database = Database(self.app, self.session)
1✔
360

361
        if not start:
1✔
362
            self.start = int(time.time())
1✔
363
        else:
364
            self.start = start
1✔
365

366
        query = '''
1✔
367
        INSERT INTO results VALUES(
368
            NULL,
369
            ?,
370
            ?,
371
            "queued",
372
            NULL,
373
            ?,
374
            NULL,
375
            ?,
376
            NULL,
377
            NULL,
378
            ?,
379
            ?,
380
            NULL,
381
            ?,
382
            NULL,
383
            ?,
384
            ?,
385
            ?,
386
            ?,
387
            ?
388
        )
389
        '''
390

391
        self.id = database.execute_sql_query(query, (
1✔
392
            self.session["user"]["id"],
393
            self.celery_id,
394
            self.start,
395
            json.dumps(self.graph_state),
396
            False,
397
            "Query",
398
            self.sparql_query,
399
            json.dumps({"graphs": self.graphs, "endpoints": self.endpoints}),
400
            False,
401
            self.session["user"]["admin"] and any([attrib.get("form") for attrib in self.graph_state["attr"]]) if (self.graph_state and self.graph_state.get("attr")) else False,
402
            False,
403
            get_distribution('askomics').version
404
        ), get_id=True)
405

406
        return self.id
1✔
407

408
    def populate_db(self, graphs, endpoints):
1✔
409
        """Update status of results in db
410

411
        Parameters
412
        ----------
413
        query : bool, optional
414
            True if error during integration
415
        error_message : bool, optional
416
            Error string if error is True
417
        """
418

419
        database = Database(self.app, self.session)
×
420

421
        query = '''
×
422
        UPDATE results SET
423
        graphs_and_endpoints=?
424
        WHERE user_id=? AND id=?
425
        '''
426

427
        variables = [
×
428
            json.dumps({"graphs": graphs, "endpoints": endpoints}),
429
            self.session["user"]["id"],
430
            self.id
431
        ]
432

433
        database.execute_sql_query(query, tuple(variables))
×
434

435
    def update_public_status(self, public):
1✔
436
        """Change public status
437

438
        Parameters
439
        ----------
440
        public : bool
441
            New public status
442
        """
443
        database = Database(self.app, self.session)
×
444

445
        query = '''
×
446
        UPDATE results SET
447
        public=?
448
        WHERE user_id=? AND id=?
449
        '''
450

451
        database.execute_sql_query(query, (
×
452
            public,
453
            self.session["user"]["id"],
454
            self.id
455
        ))
456

457
    def update_db_status(self, status, size=None, update_celery=False, update_date=False, error=False, error_message=None, traceback=None):
1✔
458
        """Update status of results in db
459

460
        Parameters
461
        ----------
462
        error : bool, optional
463
            True if error during integration
464
        error_message : bool, optional
465
            Error string if error is True
466
        """
467
        message = error_message if error else ""
1✔
468
        update_celery_substr = ""
1✔
469
        if update_celery:
1✔
470
            update_celery_substr = "celery_id=?,"
×
471

472
        update_date_substr = "start=strftime('%s', 'now')," if update_date else ""
1✔
473

474
        size_string = ""
1✔
475
        if size:
1✔
476
            size_string = "size=?,"
1✔
477

478
        self.end = int(time.time())
1✔
479

480
        database = Database(self.app, self.session)
1✔
481

482
        query = '''
1✔
483
        UPDATE results SET
484
        {celery}
485
        {size}
486
        {date}
487
        status=?,
488
        end=?,
489
        path=?,
490
        nrows=?,
491
        error=?,
492
        traceback=?
493
        WHERE user_id=? AND id=?
494
        '''.format(celery=update_celery_substr, size=size_string, date=update_date_substr)
495

496
        variables = [
1✔
497
            status,
498
            self.end,
499
            self.file_path,
500
            self.nrows,
501
            message,
502
            traceback,
503
            self.session["user"]["id"],
504
            self.id
505
        ]
506

507
        if size:
1✔
508
            variables.insert(0, size)
1✔
509

510
        if update_celery:
1✔
511
            variables.insert(0, self.celery_id)
×
512

513
        database.execute_sql_query(query, tuple(variables))
1✔
514

515
    def rollback(self):
1✔
516
        """Delete file"""
517
        self.delete_file_from_filesystem()
×
518

519
    def delete_result(self, admin=False):
1✔
520
        """Remove results from db and filesystem"""
521
        self.delete_db_entry(admin=admin)
1✔
522
        self.delete_file_from_filesystem()
1✔
523

524
    def delete_db_entry(self, admin=False):
1✔
525
        """Delete results from db"""
526
        database = Database(self.app, self.session)
1✔
527
        if admin:
1✔
528
            query = '''
1✔
529
            DELETE FROM results
530
            WHERE id = ?
531
            '''
532
            args = (self.id,)
1✔
533
        else:
534
            query = '''
1✔
535
            DELETE FROM results
536
            WHERE id = ? AND user_id = ?
537
            '''
538
            args = (self.id, self.session["user"]["id"],)
1✔
539

540
        database.execute_sql_query(query, args)
1✔
541

542
    def delete_file_from_filesystem(self):
1✔
543
        """Remove result file from filesystem"""
544
        try:
1✔
545
            os.remove(self.file_path)
1✔
546
        except Exception:
×
547
            self.log.debug("Impossible to delete {}".format(self.file_path))
×
548

549
    def publish_query(self, public, admin=False):
1✔
550
        """Set public to True or False, and template to True if public is True"""
551
        database = Database(self.app, self.session)
1✔
552

553
        # If query is set to public, template or form (if available) have to be True
554
        sql_substr = ''
1✔
555
        if admin and self.session['user']['admin']:
1✔
556
            sql_var = (public, self.id)
1✔
557
            where_query = ""
1✔
558
        # Should not happen
559
        else:
560
            sql_var = (public, self.id, self.session["user"]["id"])
1✔
561
            where_query = "AND user_id=?"
1✔
562
        if public:
1✔
563
            if self.has_form_attr and not self.template:
1✔
564
                sql_substr = 'form=?,'
×
565
            else:
566
                sql_substr = 'template=?,'
1✔
567
            sql_var = (public,) + sql_var
1✔
568

569
        query = '''
1✔
570
        UPDATE results SET
571
        {}
572
        public=?
573
        WHERE id=?
574
        {}
575
        '''.format(sql_substr, where_query)
576

577
        database.execute_sql_query(query, sql_var)
1✔
578

579
    def template_query(self, template):
1✔
580
        """Set template to True or False, and public to False if template and form are False"""
581
        database = Database(self.app, self.session)
1✔
582

583
        sql_substr = ''
1✔
584
        sql_var = (template, self.session["user"]["id"], self.id)
1✔
585

586
        if template and self.form:
1✔
587
            sql_substr = 'form=?,'
×
588
            sql_var = (False, template, self.session["user"]["id"], self.id)
×
589

590
        if not (template or self.form):
1✔
591
            sql_substr = 'public=?,'
1✔
592
            sql_var = (template, template, self.session["user"]["id"], self.id)
1✔
593

594
        query = '''
1✔
595
        UPDATE results SET
596
        {}
597
        template=?
598
        WHERE user_id=? AND id=?
599
        '''.format(sql_substr)
600

601
        database.execute_sql_query(query, sql_var)
1✔
602

603
    def form_query(self, form):
1✔
604
        """Set form to True or False, Set Template to False if True, public to False if template and form are False"""
605
        database = Database(self.app, self.session)
1✔
606
        if not self.has_form_attr:
1✔
607
            raise Exception("This query does not has any form template attribute")
1✔
608

609
        sql_substr = ''
1✔
610
        sql_var = (form, self.session["user"]["id"], self.id)
1✔
611

612
        if form and self.template:
1✔
613
            sql_substr = 'template=?,'
1✔
614
            sql_var = (False, form, self.session["user"]["id"], self.id)
1✔
615

616
        if not (form or self.template):
1✔
617
            sql_substr = 'public=?,'
1✔
618
            sql_var = (form, form, self.session["user"]["id"], self.id)
1✔
619

620
        query = '''
1✔
621
        UPDATE results SET
622
        {}
623
        form=?
624
        WHERE user_id=? AND id=?
625
        '''.format(sql_substr)
626

627
        database.execute_sql_query(query, sql_var)
1✔
628

629
    def update_description(self, description, admin=False):
1✔
630
        """Change the result description"""
631
        database = Database(self.app, self.session)
1✔
632
        if admin:
1✔
633
            query = '''
1✔
634
            UPDATE results SET
635
            description=?
636
            WHERE id=?
637
            '''
638

639
            database.execute_sql_query(query, (
1✔
640
                description,
641
                self.id
642
            ))
643
        else:
644
            query = '''
1✔
645
            UPDATE results SET
646
            description=?
647
            WHERE user_id=? AND id=?
648
            '''
649

650
            database.execute_sql_query(query, (
1✔
651
                description,
652
                self.session["user"]["id"],
653
                self.id
654
            ))
655

656
    def update_graph(self, newGraph):
1✔
657
        """Change the result description"""
658
        database = Database(self.app, self.session)
1✔
659

660
        query = '''
1✔
661
        UPDATE results SET
662
        graph_state=?
663
        WHERE user_id=? AND id=?
664
        '''
665

666
        database.execute_sql_query(query, (
1✔
667
            json.dumps(newGraph),
668
            self.session["user"]["id"],
669
            self.id
670
        ))
671

672
    def send2galaxy(self, file2send):
1✔
673
        """Send files to Galaxy"""
674
        if file2send == "result":
1✔
675
            self.send_result_to_galaxy()
1✔
676
        elif file2send == "query":
×
677
            self.send_query_to_galaxy()
×
678

679
    def send_result_to_galaxy(self):
1✔
680
        """Send a result file to Galaxy"""
681
        filename = "AskOmics_result_{}.tsv".format(self.id)
1✔
682

683
        galaxy_instance = galaxy.GalaxyInstance(self.session["user"]["galaxy"]["url"], self.session["user"]["galaxy"]["apikey"])
1✔
684
        last_history = galaxy_instance.histories.get_most_recently_used_history()
1✔
685
        galaxy_instance.tools.upload_file(self.file_path, last_history['id'], file_name=filename, file_type='tabular')
1✔
686

687
    def send_query_to_galaxy(self):
1✔
688
        """Send the json query to a galaxy dataset"""
689
        galaxy_instance = galaxy.GalaxyInstance(self.session["user"]["galaxy"]["url"], self.session["user"]["galaxy"]["apikey"])
×
690
        last_history_id = galaxy_instance.histories.get_most_recently_used_history()['id']
×
691

692
        # Name of the json file
693
        name = "AskOmics_query_{}.json".format(self.id)
×
694

695
        # Load the file into Galaxy
696
        galaxy_instance.tools.paste_content(json.dumps(self.get_graph_state(formated=True)), last_history_id, file_type='json', file_name=name)
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc