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

askomics / flaskomics / 4132887853

pending completion
4132887853

push

github-actions

GitHub
Add 'anonymous_query' mode (#384)

357 of 357 new or added lines in 16 files covered. (100.0%)

6166 of 7374 relevant lines covered (83.62%)

0.84 hits per line

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

85.13
/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

13
class Result(Params):
1✔
14
    """Result represent a query result file
15

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

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

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

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

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

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

76
        Parameters
77
        ----------
78
        node : dict
79
            A graph node
80

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

93
        return node
1✔
94

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

98
        Parameters
99
        ----------
100
        link : dict
101
            A graph link
102

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

112
        # link["source"] = self.clean_node(link["source"])
113
        # link["target"] = self.clean_node(link["target"])
114

115
        link["source"] = link["source"]["id"]
×
116
        link["target"] = link["target"]["id"]
×
117

118
        return link
×
119

120
    def format_graph_state(self, d3_graph_state):
1✔
121
        """Format Graph state
122

123
        Remove coordinates and other things
124

125
        Parameters
126
        ----------
127
        d3_graph_state : dict
128
            The d3 graph state
129

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

139
        try:
1✔
140

141
            for node in d3_graph_state["nodes"]:
1✔
142

143
                if node["suggested"]:
1✔
144
                    continue
1✔
145

146
                new_node = self.clean_node(node)
1✔
147
                new_nodes.append(new_node)
1✔
148

149
            for link in d3_graph_state["links"]:
1✔
150

151
                if link["suggested"]:
1✔
152
                    continue
1✔
153

154
                new_link = self.clean_link(link)
×
155
                new_links.append(new_link)
×
156

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

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

167
    def get_file_name(self):
1✔
168
        """Get file name
169

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

177
    def get_dir_path(self):
1✔
178
        """Get directory path
179

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

187
    def get_graph_state(self, formated=False):
1✔
188
        """Get get_graph_state
189

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

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

206
        return graph
1✔
207

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

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

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

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

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

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

236
    def set_celery_id(self, celery_id):
1✔
237
        """Set celery id
238

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

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

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

275
            rows = database.execute_sql_query(query, params)
1✔
276

277
        if not rows:
1✔
278
            return False
×
279

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

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

296
        return True
1✔
297

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

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

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

329
            return headers, data
1✔
330

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

334
        Parameters
335
        ----------
336
        headers : list
337
            List of results headers
338
        results : list
339
            Query results
340

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

353
        return os.path.getsize(self.file_path)
1✔
354

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

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

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

388
        self.id = database.execute_sql_query(query, (
1✔
389
            self.session["user"]["id"],
390
            self.celery_id,
391
            self.start,
392
            json.dumps(self.graph_state),
393
            False,
394
            "Query",
395
            self.sparql_query,
396
            json.dumps({"graphs": self.graphs, "endpoints": self.endpoints}),
397
            False,
398
            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,
399
            False
400
        ), get_id=True)
401

402
        return self.id
1✔
403

404
    def populate_db(self, graphs, endpoints):
1✔
405
        """Update status of results in db
406

407
        Parameters
408
        ----------
409
        query : bool, optional
410
            True if error during integration
411
        error_message : bool, optional
412
            Error string if error is True
413
        """
414

415
        database = Database(self.app, self.session)
×
416

417
        query = '''
×
418
        UPDATE results SET
419
        graphs_and_endpoints=?
420
        WHERE user_id=? AND id=?
421
        '''
422

423
        variables = [
×
424
            json.dumps({"graphs": graphs, "endpoints": endpoints}),
425
            self.session["user"]["id"],
426
            self.id
427
        ]
428

429
        database.execute_sql_query(query, tuple(variables))
×
430

431
    def update_public_status(self, public):
1✔
432
        """Change public status
433

434
        Parameters
435
        ----------
436
        public : bool
437
            New public status
438
        """
439
        database = Database(self.app, self.session)
×
440

441
        query = '''
×
442
        UPDATE results SET
443
        public=?
444
        WHERE user_id=? AND id=?
445
        '''
446

447
        database.execute_sql_query(query, (
×
448
            public,
449
            self.session["user"]["id"],
450
            self.id
451
        ))
452

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

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

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

470
        size_string = ""
1✔
471
        if size:
1✔
472
            size_string = "size=?,"
1✔
473

474
        self.end = int(time.time())
1✔
475

476
        database = Database(self.app, self.session)
1✔
477

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

492
        variables = [
1✔
493
            status,
494
            self.end,
495
            self.file_path,
496
            self.nrows,
497
            message,
498
            traceback,
499
            self.session["user"]["id"],
500
            self.id
501
        ]
502

503
        if size:
1✔
504
            variables.insert(0, size)
1✔
505

506
        if update_celery:
1✔
507
            variables.insert(0, self.celery_id)
×
508

509
        database.execute_sql_query(query, tuple(variables))
1✔
510

511
    def rollback(self):
1✔
512
        """Delete file"""
513
        self.delete_file_from_filesystem()
×
514

515
    def delete_result(self, admin=False):
1✔
516
        """Remove results from db and filesystem"""
517
        self.delete_db_entry(admin=admin)
1✔
518
        self.delete_file_from_filesystem()
1✔
519

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

536
        database.execute_sql_query(query, args)
1✔
537

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

545
    def publish_query(self, public, admin=False):
1✔
546
        """Set public to True or False, and template to True if public is True"""
547
        database = Database(self.app, self.session)
1✔
548

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

565
        query = '''
1✔
566
        UPDATE results SET
567
        {}
568
        public=?
569
        WHERE id=?
570
        {}
571
        '''.format(sql_substr, where_query)
572

573
        database.execute_sql_query(query, sql_var)
1✔
574

575
    def template_query(self, template):
1✔
576
        """Set template to True or False, and public to False if template and form are False"""
577
        database = Database(self.app, self.session)
1✔
578

579
        sql_substr = ''
1✔
580
        sql_var = (template, self.session["user"]["id"], self.id)
1✔
581

582
        if template and self.form:
1✔
583
            sql_substr = 'form=?,'
×
584
            sql_var = (False, template, self.session["user"]["id"], self.id)
×
585

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

590
        query = '''
1✔
591
        UPDATE results SET
592
        {}
593
        template=?
594
        WHERE user_id=? AND id=?
595
        '''.format(sql_substr)
596

597
        database.execute_sql_query(query, sql_var)
1✔
598

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

605
        sql_substr = ''
1✔
606
        sql_var = (form, self.session["user"]["id"], self.id)
1✔
607

608
        if form and self.template:
1✔
609
            sql_substr = 'template=?,'
1✔
610
            sql_var = (False, form, self.session["user"]["id"], self.id)
1✔
611

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

616
        query = '''
1✔
617
        UPDATE results SET
618
        {}
619
        form=?
620
        WHERE user_id=? AND id=?
621
        '''.format(sql_substr)
622

623
        database.execute_sql_query(query, sql_var)
1✔
624

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

635
            database.execute_sql_query(query, (
1✔
636
                description,
637
                self.id
638
            ))
639
        else:
640
            query = '''
1✔
641
            UPDATE results SET
642
            description=?
643
            WHERE user_id=? AND id=?
644
            '''
645

646
            database.execute_sql_query(query, (
1✔
647
                description,
648
                self.session["user"]["id"],
649
                self.id
650
            ))
651

652
    def update_graph(self, newGraph):
1✔
653
        """Change the result description"""
654
        database = Database(self.app, self.session)
1✔
655

656
        query = '''
1✔
657
        UPDATE results SET
658
        graph_state=?
659
        WHERE user_id=? AND id=?
660
        '''
661

662
        database.execute_sql_query(query, (
1✔
663
            json.dumps(newGraph),
664
            self.session["user"]["id"],
665
            self.id
666
        ))
667

668
    def send2galaxy(self, file2send):
1✔
669
        """Send files to Galaxy"""
670
        if file2send == "result":
1✔
671
            self.send_result_to_galaxy()
1✔
672
        elif file2send == "query":
×
673
            self.send_query_to_galaxy()
×
674

675
    def send_result_to_galaxy(self):
1✔
676
        """Send a result file to Galaxy"""
677
        filename = "AskOmics_result_{}.tsv".format(self.id)
1✔
678

679
        galaxy_instance = galaxy.GalaxyInstance(self.session["user"]["galaxy"]["url"], self.session["user"]["galaxy"]["apikey"])
1✔
680
        last_history = galaxy_instance.histories.get_most_recently_used_history()
1✔
681
        galaxy_instance.tools.upload_file(self.file_path, last_history['id'], file_name=filename, file_type='tabular')
1✔
682

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

688
        # Name of the json file
689
        name = "AskOmics_query_{}.json".format(self.id)
×
690

691
        # Load the file into Galaxy
692
        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