genelastic 0.7.0__py3-none-any.whl → 0.9.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. genelastic/api/.env +4 -0
  2. genelastic/api/cli_start_api.py +18 -0
  3. genelastic/api/errors.py +52 -0
  4. genelastic/api/extends/example.py +0 -6
  5. genelastic/api/extends/example.yml +0 -0
  6. genelastic/api/routes.py +313 -181
  7. genelastic/api/server.py +34 -26
  8. genelastic/api/settings.py +5 -9
  9. genelastic/api/specification.yml +512 -0
  10. genelastic/common/__init__.py +0 -39
  11. genelastic/common/cli.py +100 -0
  12. genelastic/common/elastic.py +374 -46
  13. genelastic/common/exceptions.py +34 -2
  14. genelastic/common/server.py +59 -0
  15. genelastic/common/types.py +1 -14
  16. genelastic/import_data/__init__.py +0 -27
  17. genelastic/import_data/checker.py +99 -0
  18. genelastic/import_data/checker_observer.py +13 -0
  19. genelastic/import_data/cli/__init__.py +0 -0
  20. genelastic/import_data/cli/cli_check.py +136 -0
  21. genelastic/import_data/cli/gen_data.py +143 -0
  22. genelastic/import_data/cli/import_data.py +346 -0
  23. genelastic/import_data/cli/info.py +247 -0
  24. genelastic/import_data/{cli_integrity.py → cli/integrity.py} +29 -7
  25. genelastic/import_data/cli/validate.py +146 -0
  26. genelastic/import_data/collect.py +185 -0
  27. genelastic/import_data/constants.py +136 -11
  28. genelastic/import_data/import_bundle.py +102 -59
  29. genelastic/import_data/import_bundle_factory.py +70 -149
  30. genelastic/import_data/importers/__init__.py +0 -0
  31. genelastic/import_data/importers/importer_base.py +131 -0
  32. genelastic/import_data/importers/importer_factory.py +85 -0
  33. genelastic/import_data/importers/importer_types.py +223 -0
  34. genelastic/import_data/logger.py +2 -1
  35. genelastic/import_data/models/__init__.py +0 -0
  36. genelastic/import_data/models/analyses.py +178 -0
  37. genelastic/import_data/models/analysis.py +144 -0
  38. genelastic/import_data/models/data_file.py +110 -0
  39. genelastic/import_data/models/process.py +45 -0
  40. genelastic/import_data/models/processes.py +84 -0
  41. genelastic/import_data/models/tags.py +170 -0
  42. genelastic/import_data/models/unique_list.py +109 -0
  43. genelastic/import_data/models/validate.py +26 -0
  44. genelastic/import_data/patterns.py +90 -0
  45. genelastic/import_data/random_bundle.py +79 -54
  46. genelastic/import_data/resolve.py +157 -0
  47. genelastic/ui/.env +1 -0
  48. genelastic/ui/cli_start_ui.py +20 -0
  49. genelastic/ui/routes.py +333 -0
  50. genelastic/ui/server.py +9 -82
  51. genelastic/ui/settings.py +2 -6
  52. genelastic/ui/static/cea-cnrgh.ico +0 -0
  53. genelastic/ui/static/cea.ico +0 -0
  54. genelastic/ui/static/layout.ico +0 -0
  55. genelastic/ui/static/novaseq6000.png +0 -0
  56. genelastic/ui/static/style.css +430 -0
  57. genelastic/ui/static/ui.js +458 -0
  58. genelastic/ui/templates/analyses.html +98 -0
  59. genelastic/ui/templates/analysis_detail.html +44 -0
  60. genelastic/ui/templates/bi_process_detail.html +129 -0
  61. genelastic/ui/templates/bi_processes.html +116 -0
  62. genelastic/ui/templates/explorer.html +356 -0
  63. genelastic/ui/templates/home.html +207 -0
  64. genelastic/ui/templates/layout.html +153 -0
  65. genelastic/ui/templates/version.html +21 -0
  66. genelastic/ui/templates/wet_process_detail.html +131 -0
  67. genelastic/ui/templates/wet_processes.html +116 -0
  68. genelastic-0.9.0.dist-info/METADATA +686 -0
  69. genelastic-0.9.0.dist-info/RECORD +76 -0
  70. genelastic-0.9.0.dist-info/WHEEL +4 -0
  71. genelastic-0.9.0.dist-info/entry_points.txt +10 -0
  72. genelastic-0.9.0.dist-info/licenses/LICENSE +519 -0
  73. genelastic/import_data/analyses.py +0 -69
  74. genelastic/import_data/analysis.py +0 -205
  75. genelastic/import_data/bi_process.py +0 -27
  76. genelastic/import_data/bi_processes.py +0 -49
  77. genelastic/import_data/cli_gen_data.py +0 -116
  78. genelastic/import_data/cli_import.py +0 -379
  79. genelastic/import_data/cli_info.py +0 -256
  80. genelastic/import_data/cli_validate.py +0 -54
  81. genelastic/import_data/data_file.py +0 -87
  82. genelastic/import_data/filename_pattern.py +0 -57
  83. genelastic/import_data/tags.py +0 -123
  84. genelastic/import_data/wet_process.py +0 -28
  85. genelastic/import_data/wet_processes.py +0 -53
  86. genelastic-0.7.0.dist-info/METADATA +0 -105
  87. genelastic-0.7.0.dist-info/RECORD +0 -40
  88. genelastic-0.7.0.dist-info/WHEEL +0 -5
  89. genelastic-0.7.0.dist-info/entry_points.txt +0 -6
  90. genelastic-0.7.0.dist-info/top_level.txt +0 -1
genelastic/api/.env ADDED
@@ -0,0 +1,4 @@
1
+ GENAPI_ES_URL="https://192.168.73.11:9200"
2
+ GENAPI_ES_CERT_FP="CB:8F:94:61:DB:57:65:38:9E:A2:1E:C4:75:AA:3E:9B:03:5C:2A:04:74:06:96:3E:B5:CE:81:7C:64:3C:D4:C5"
3
+ GENAPI_ES_ENCODED_API_KEY="YlNSWjY1Z0JoN1BKQ1BHTW95OGI6VExoTUxPc1VaRVFJazRnMWdwdGtNdw=="
4
+ GENAPI_ES_INDEX_PREFIX="genelastic"
@@ -0,0 +1,18 @@
1
+ from genelastic.common.cli import parse_server_launch_args
2
+ from genelastic.common.server import start_dev_server, start_prod_server
3
+
4
+
5
+ def main() -> None:
6
+ app_module = "genelastic.api.server:app"
7
+ args = parse_server_launch_args("Start API server.", 8000)
8
+ if args.env == "dev":
9
+ start_dev_server(app_module, args, reload_includes=["*.yml"])
10
+ elif args.env == "prod":
11
+ start_prod_server(app_module, args)
12
+ else:
13
+ msg = f"Environment '{args.env}' is not implemented."
14
+ raise NotImplementedError(msg)
15
+
16
+
17
+ if __name__ == "__main__":
18
+ main()
@@ -0,0 +1,52 @@
1
+ import elasticsearch
2
+ from flask import Flask, Response, jsonify
3
+
4
+
5
+ def register_error_handlers(app: Flask) -> None:
6
+ """Register handlers for elasticsearch ApiError and TransportError
7
+ exceptions.
8
+ """
9
+
10
+ @app.errorhandler(elasticsearch.ApiError)
11
+ def handle_api_error(e: elasticsearch.ApiError) -> tuple[Response, int]:
12
+ # See https://elasticsearch-py.readthedocs.io/en/latest/exceptions.html#api-errors
13
+ app.logger.error("Elasticsearch ApiError: %s", e, exc_info=True)
14
+
15
+ return (
16
+ jsonify(
17
+ {
18
+ "error": {
19
+ "message": str(e),
20
+ "type": e.__class__.__name__,
21
+ }
22
+ }
23
+ ),
24
+ e.status_code,
25
+ )
26
+
27
+ @app.errorhandler(elasticsearch.TransportError)
28
+ def handle_transport_error(
29
+ e: elasticsearch.TransportError,
30
+ ) -> tuple[Response, int]:
31
+ # See https://elasticsearch-py.readthedocs.io/en/latest/exceptions.html#transport-and-connection-errors
32
+ app.logger.error("Elasticsearch TransportError: %s", e, exc_info=True)
33
+
34
+ status_code = 502
35
+ if (
36
+ e.errors
37
+ and hasattr(e.errors[0], "status")
38
+ and e.errors[0].status is not None
39
+ ):
40
+ status_code = e.errors[0].status
41
+
42
+ return (
43
+ jsonify(
44
+ {
45
+ "error": {
46
+ "message": str(e),
47
+ "type": e.__class__.__name__,
48
+ }
49
+ }
50
+ ),
51
+ status_code,
52
+ )
@@ -1,6 +0,0 @@
1
- from flask import Response, jsonify
2
-
3
-
4
- def ping_2() -> Response:
5
- """Test route to verify that the server is online."""
6
- return jsonify({"message": "pong_2"})
File without changes
genelastic/api/routes.py CHANGED
@@ -1,221 +1,353 @@
1
- from importlib.metadata import version
2
- from pathlib import Path
3
- from typing import Any
1
+ import typing
2
+ from importlib.metadata import version as get_version
4
3
 
5
4
  from flask import Response, current_app, jsonify
6
5
 
6
+ if typing.TYPE_CHECKING:
7
+ from genelastic.common.elastic import ElasticQueryConn
7
8
 
8
- def ping() -> Response:
9
- """Test route to verify that the server is online."""
10
- return jsonify({"message": "pong"})
11
9
 
10
+ def indexes() -> tuple[Response, int]:
11
+ """List all Elasticsearch indexes."""
12
+ es_query_conn: ElasticQueryConn = current_app.elastic_query_conn # type: ignore[attr-defined]
13
+ return jsonify({"result": es_query_conn.get_indices()}), 200
12
14
 
13
- def list_indices() -> Response:
14
- """Route to list Elasticsearch indexes."""
15
- return current_app.elastic_query_conn.get_indices() # type: ignore[attr-defined, no-any-return]
16
15
 
16
+ def retrieve_document_by_index_and_id(
17
+ index_id: str, document_id: str
18
+ ) -> tuple[Response, int]:
19
+ """Retrieve a document by its index name and ID."""
20
+ es_query_conn: ElasticQueryConn = current_app.elastic_query_conn # type: ignore[attr-defined]
21
+ result = es_query_conn.get_document_by_id(index_id, document_id)
22
+ return jsonify({"result": result}), 200
17
23
 
18
- def retrieve_document(index_id: str, document_id: str) -> Response:
19
- """Route to retrieve a document by its ID."""
20
- document = current_app.elastic_query_conn.get_document_by_id( # type: ignore[attr-defined]
21
- index_id, document_id
22
- )
23
- return jsonify(document)
24
-
25
-
26
- def list_wet_processes() -> Response:
27
- """Route to list wet processes."""
28
- wet_processes_index = (
29
- f"{current_app.config['GENAPI_ES_INDEX_PREFIX']}-wet_processes"
30
- )
31
- result = current_app.elastic_query_conn.get_field_values( # type: ignore[attr-defined]
32
- wet_processes_index, "proc_id"
33
- )
34
- return jsonify(list(result))
35
24
 
25
+ def wet_processes() -> tuple[Response, int]:
26
+ """List wet processes."""
27
+ es_query_conn: ElasticQueryConn = current_app.elastic_query_conn # type: ignore[attr-defined]
36
28
 
37
- def list_bi_processes() -> Response:
38
- """Route to list bi processes."""
39
- bi_processes_index = (
40
- f"{current_app.config['GENAPI_ES_INDEX_PREFIX']}-bi_processes"
29
+ result = es_query_conn.get_field_values(
30
+ es_query_conn.wet_processes_index,
31
+ "proc_id",
41
32
  )
42
- result = current_app.elastic_query_conn.get_field_values( # type: ignore[attr-defined]
43
- bi_processes_index, "proc_id"
44
- )
45
- return jsonify(list(result))
33
+ return jsonify({"result": list(result)}), 200
46
34
 
47
35
 
48
- def list_analyses() -> Response:
49
- """Route to list analyses."""
50
- analyses_index = f"{current_app.config['GENAPI_ES_INDEX_PREFIX']}-analyses"
51
- result = current_app.elastic_query_conn.get_field_values( # type: ignore[attr-defined]
52
- analyses_index, "path"
36
+ def get_wet_process(proc_id: str) -> tuple[Response, int]:
37
+ """Retrieve details of a specific wet process by its ID."""
38
+ es_query_conn: ElasticQueryConn = current_app.elastic_query_conn # type: ignore[attr-defined]
39
+ result = es_query_conn.get_process(
40
+ es_query_conn.wet_processes_index, proc_id
53
41
  )
54
- filenames = [Path(path).name for path in result]
55
- return jsonify(filenames)
56
-
57
-
58
- def list_analyses_wet_processes(proc_id: str) -> Response:
59
- """Route to list analyses one of specific wet process"""
60
- analyses_index = f"{current_app.config['GENAPI_ES_INDEX_PREFIX']}-analyses"
61
-
62
- search_query = {
63
- "query": {
64
- "term": {
65
- "metadata.wet_process.keyword": proc_id,
66
- }
67
- }
68
- }
69
- response = current_app.elastic_query_conn.client.search( # type: ignore[attr-defined]
70
- index=analyses_index, body=search_query
71
- )
72
- result = [hit["_source"]["path"] for hit in response["hits"]["hits"]]
73
-
74
- return jsonify(result)
75
-
76
-
77
- def list_analyses_bi_processes(proc_id: str) -> Response:
78
- """Route to list analyses one of specific bi process"""
79
- analyses_index = f"{current_app.config['GENAPI_ES_INDEX_PREFIX']}-analyses"
80
-
81
- search_query = {
82
- "query": {
83
- "term": {
84
- "metadata.bi_process.keyword": proc_id,
85
- }
86
- }
87
- }
88
- response = current_app.elastic_query_conn.client.search( # type: ignore[attr-defined]
89
- index=analyses_index, body=search_query
90
- )
91
- result = [hit["_source"]["path"] for hit in response["hits"]["hits"]]
92
-
93
- return jsonify(result)
94
-
95
42
 
96
- def list_snv_documents() -> Response:
97
- """Route to list all documents containing a mutation at a single position (SNV)."""
98
- index_pattern = "genelastic-file-*"
99
- target_value = "SNV"
100
-
101
- search_query = {
102
- "aggs": {
103
- "snv_docs": {
104
- "composite": {
105
- "sources": [
106
- {"alt_value": {"terms": {"field": "alt.keyword"}}}
107
- ],
108
- "size": 1000,
43
+ if not result:
44
+ return jsonify(
45
+ {
46
+ "error": {
47
+ "message": f"Wet process with proc_id "
48
+ f"'{proc_id}' not found.",
49
+ "type": "NotFoundError",
109
50
  }
110
51
  }
111
- },
112
- "query": {"term": {"alt.keyword": target_value}},
113
- "size": 0,
114
- }
115
-
116
- all_documents = []
117
- buckets = current_app.elastic_query_conn.run_composite_aggregation( # type: ignore[attr-defined]
118
- index_pattern, search_query
119
- )
120
-
121
- for bucket in buckets:
122
- alt_value = bucket["key"]["alt_value"]
123
-
124
- search_query_docs = {
125
- "query": {"term": {"alt.keyword": alt_value}},
126
- "size": 1000,
127
- }
128
-
129
- response = current_app.elastic_query_conn.client.search( # type: ignore[attr-defined]
130
- index=index_pattern, body=search_query_docs
131
- )
52
+ ), 404
132
53
 
133
- all_documents.extend(response["hits"]["hits"])
54
+ return jsonify({"result": result}), 200
134
55
 
135
- return jsonify(all_documents)
136
56
 
57
+ def list_analyses_by_wet_process(proc_id: str) -> tuple[Response, int]:
58
+ """List analyses that contain the specified wet process."""
59
+ es_query_conn: ElasticQueryConn = current_app.elastic_query_conn # type: ignore[attr-defined]
60
+ result = es_query_conn.list_analyses_by_process("wet_process", proc_id)
61
+ return jsonify({"result": result}), 200
137
62
 
138
- def build_snv_search_query(
139
- target_alt: str, target_svtype: str
140
- ) -> dict[str, Any]:
141
- """Helper function to build the search query for SNV documents with specified alt and SVTYPE."""
142
- return {
143
- "query": {
144
- "bool": {
145
- "must": [
146
- {"term": {"alt.keyword": target_alt}},
147
- {"term": {"info.SVTYPE.keyword": target_svtype}},
148
- ]
149
- }
150
- },
151
- "size": 1000,
152
- }
153
-
154
-
155
- def build_snv_mutation_search_query(
156
- target_svtypes: list[str],
157
- ) -> dict[str, Any]:
158
- """Helper function to build the search query for SNV mutations with specified SVTYPE values."""
159
- return {
160
- "query": {
161
- "bool": {
162
- "must": [
163
- {"term": {"alt.keyword": "SNV"}},
164
- {"terms": {"info.SVTYPE.keyword": target_svtypes}},
165
- ]
166
- }
167
- },
168
- "size": 1000,
169
- }
170
63
 
64
+ def bi_processes() -> tuple[Response, int]:
65
+ """List bioinformatics processes."""
66
+ es_query_conn: ElasticQueryConn = current_app.elastic_query_conn # type: ignore[attr-defined]
67
+ result = es_query_conn.get_field_values(
68
+ es_query_conn.bi_processes_index, "proc_id"
69
+ )
70
+ return jsonify({"result": list(result)}), 200
171
71
 
172
- def list_snv_insertion_documents() -> Response:
173
- """Route to list all documents containing an insertion (INS) at a single position (SNV)."""
174
- index_pattern = "genelastic-file-*"
175
- search_query = build_snv_search_query(target_alt="SNV", target_svtype="INS")
176
72
 
177
- response = current_app.elastic_query_conn.client.search( # type: ignore[attr-defined]
178
- index=index_pattern, body=search_query
73
+ def get_bi_process(proc_id: str) -> tuple[Response, int]:
74
+ """Retrieve details of a specific bioinformatics process by its ID."""
75
+ es_query_conn: ElasticQueryConn = current_app.elastic_query_conn # type: ignore[attr-defined]
76
+ result = es_query_conn.get_process(
77
+ es_query_conn.bi_processes_index, proc_id
179
78
  )
180
79
 
181
- all_documents = [hit["_source"] for hit in response["hits"]["hits"]]
80
+ if not result:
81
+ return jsonify(
82
+ {
83
+ "error": {
84
+ "message": f"Bioinformatics process with proc_id "
85
+ f"'{proc_id}' not found.",
86
+ "type": "NotFoundError",
87
+ }
88
+ }
89
+ ), 404
182
90
 
183
- return jsonify(all_documents)
91
+ return jsonify({"result": result}), 200
184
92
 
185
93
 
186
- def list_snv_deletion_documents() -> Response:
187
- """Route to list all documents containing a deletion (DEL) at a single position (SNV)."""
188
- index_pattern = "genelastic-file-*"
189
- search_query = build_snv_search_query(target_alt="SNV", target_svtype="DEL")
94
+ def list_analyses_by_bi_process(proc_id: str) -> tuple[Response, int]:
95
+ """List analyses that contain the specified bioinformatics process"""
96
+ es_query_conn: ElasticQueryConn = current_app.elastic_query_conn # type: ignore[attr-defined]
97
+ result = es_query_conn.list_analyses_by_process("bi_process", proc_id)
98
+ return jsonify({"result": result}), 200
190
99
 
191
- response = current_app.elastic_query_conn.client.search( # type: ignore[attr-defined]
192
- index=index_pattern, body=search_query
193
- )
194
100
 
195
- all_documents = [hit["_source"] for hit in response["hits"]["hits"]]
101
+ def list_analyses_by_bi_process_esql(
102
+ proc_id: str,
103
+ ) -> tuple[Response, int]:
104
+ """List analyses that contain the specified bioinformatics process using
105
+ an ES|QL query.
106
+ """
107
+ es_query_conn: ElasticQueryConn = current_app.elastic_query_conn # type: ignore[attr-defined]
108
+ result = es_query_conn.list_analyses_by_process_esql("bi_process", proc_id)
109
+ return jsonify({"result": result}), 200
196
110
 
197
- return jsonify(all_documents)
198
111
 
112
+ def list_analyses_by_bi_process_sql(
113
+ proc_id: str,
114
+ ) -> tuple[Response, int]:
115
+ """List analyses that contain the specified bioinformatics process using
116
+ an SQL query.
117
+ """
118
+ es_query_conn: ElasticQueryConn = current_app.elastic_query_conn # type: ignore[attr-defined]
119
+ result = es_query_conn.list_analyses_by_process_sql("bi_process", proc_id)
120
+ return jsonify({"result": result}), 200
199
121
 
200
- def list_snv_mutation_documents() -> Response:
201
- """Route to list all documents containing a mutation at a single position (SNV)."""
202
- index_pattern = "genelastic-file-*"
203
- target_svtypes = ["INS", "DEL"]
204
122
 
205
- search_query = build_snv_mutation_search_query(
206
- target_svtypes=target_svtypes
207
- )
123
+ def analyses() -> tuple[Response, int]:
124
+ """List analyses."""
125
+ es_query_conn: ElasticQueryConn = current_app.elastic_query_conn # type: ignore[attr-defined]
208
126
 
209
- response = current_app.elastic_query_conn.client.search( # type: ignore[attr-defined]
210
- index=index_pattern, body=search_query
127
+ result = es_query_conn.get_field_values(
128
+ es_query_conn.analyses_index, "analysis_id"
211
129
  )
130
+ return jsonify({"result": list(result)}), 200
131
+
132
+
133
+ # TODO(?): All the following functions are commented because it is currently
134
+ # unclear what they do and if they are still needed.
135
+
136
+ # def list_snv_documents() -> Response:
137
+ # """Route to list all documents containing a mutation at a single position (SNV)."""
138
+ # es_query_conn: ElasticQueryConn = current_app.elastic_query_conn # type: ignore[attr-defined]
139
+ # index_pattern = "genelastic-file-*"
140
+ # target_value = "SNV"
141
+ #
142
+ # search_query = {
143
+ # "aggs": {
144
+ # "snv_docs": {
145
+ # "composite": {
146
+ # "sources": [
147
+ # {"alt_value": {"terms": {"field": "alt.keyword"}}}
148
+ # ],
149
+ # "size": 1000,
150
+ # }
151
+ # }
152
+ # },
153
+ # "query": {"term": {"alt.keyword": target_value}},
154
+ # "size": 0,
155
+ # }
156
+ #
157
+ # all_documents = []
158
+ # buckets = es_query_conn.run_composite_aggregation(
159
+ # index_pattern, search_query
160
+ # )
161
+ #
162
+ # for bucket in buckets:
163
+ # alt_value = bucket["key"]["alt_value"]
164
+ #
165
+ # search_query_docs = {
166
+ # "query": {"term": {"alt.keyword": alt_value}},
167
+ # "size": 1000,
168
+ # }
169
+ #
170
+ # response = es_query_conn.client.search(
171
+ # index=index_pattern, body=search_query_docs
172
+ # )
173
+ #
174
+ # all_documents.extend(response["hits"]["hits"])
175
+ #
176
+ # return jsonify(all_documents)
177
+ #
178
+ #
179
+ # def build_snv_search_query(
180
+ # target_alt: str, target_svtype: str
181
+ # ) -> dict[str, Any]:
182
+ # """Helper function to build the search query for SNV documents with specified alt and SVTYPE."""
183
+ # return {
184
+ # "query": {
185
+ # "bool": {
186
+ # "must": [
187
+ # {"term": {"alt.keyword": target_alt}},
188
+ # {"term": {"info.SVTYPE.keyword": target_svtype}},
189
+ # ]
190
+ # }
191
+ # },
192
+ # "size": 1000,
193
+ # }
194
+ #
195
+ #
196
+ # def build_snv_mutation_search_query(
197
+ # target_svtypes: list[str],
198
+ # ) -> dict[str, Any]:
199
+ # """Helper function to build the search query for SNV mutations with specified SVTYPE values."""
200
+ # return {
201
+ # "query": {
202
+ # "bool": {
203
+ # "must": [
204
+ # {"term": {"alt.keyword": "SNV"}},
205
+ # {"terms": {"info.SVTYPE.keyword": target_svtypes}},
206
+ # ]
207
+ # }
208
+ # },
209
+ # "size": 1000,
210
+ # }
211
+ #
212
+ #
213
+ # def list_snv_insertion_documents() -> Response:
214
+ # """Route to list all documents containing an insertion (INS) at a single position (SNV)."""
215
+ # es_query_conn: ElasticQueryConn = current_app.elastic_query_conn # type: ignore[attr-defined]
216
+ #
217
+ # index_pattern = "genelastic-file-*"
218
+ # search_query = build_snv_search_query(target_alt="SNV", target_svtype="INS")
219
+ #
220
+ # response = es_query_conn.client.search(
221
+ # index=index_pattern, body=search_query
222
+ # )
223
+ #
224
+ # all_documents = [hit["_source"] for hit in response["hits"]["hits"]]
225
+ #
226
+ # return jsonify(all_documents)
227
+ #
228
+ #
229
+ # def list_snv_deletion_documents() -> Response:
230
+ # """Route to list all documents containing a deletion (DEL) at a single position (SNV)."""
231
+ # es_query_conn: ElasticQueryConn = current_app.elastic_query_conn # type: ignore[attr-defined]
232
+ # index_pattern = "genelastic-file-*"
233
+ # search_query = build_snv_search_query(target_alt="SNV", target_svtype="DEL")
234
+ #
235
+ # response = es_query_conn.client.search(
236
+ # index=index_pattern, body=search_query
237
+ # )
238
+ #
239
+ # all_documents = [hit["_source"] for hit in response["hits"]["hits"]]
240
+ #
241
+ # return jsonify(all_documents)
242
+ #
243
+ #
244
+ # def list_snv_mutation_documents() -> Response:
245
+ # """Route to list all documents containing a mutation at a single position (SNV)."""
246
+ # es_query_conn: ElasticQueryConn = current_app.elastic_query_conn # type: ignore[attr-defined]
247
+ # index_pattern = "genelastic-file-*"
248
+ # target_svtypes = ["INS", "DEL"]
249
+ #
250
+ # search_query = build_snv_mutation_search_query(
251
+ # target_svtypes=target_svtypes
252
+ # )
253
+ #
254
+ # response = es_query_conn.client.search(
255
+ # index=index_pattern, body=search_query
256
+ # )
257
+ #
258
+ # all_documents = [hit["_source"] for hit in response["hits"]["hits"]]
259
+ #
260
+ # return jsonify(all_documents)
261
+
262
+
263
+ def version() -> tuple[Response, int]:
264
+ """Obtain genelastic package version used by the server."""
265
+ top_level_package = __package__.split(".")[0]
266
+ return jsonify({"result": {"version": get_version(top_level_package)}}), 200
267
+
268
+
269
+ def key_value_analyses_index() -> tuple[Response, int]:
270
+ """Route to return all key-values in an index."""
271
+ es_query_conn: ElasticQueryConn = current_app.elastic_query_conn # type: ignore[attr-defined]
272
+ result = es_query_conn.get_all_documents_kv(es_query_conn.analyses_index)
273
+ return jsonify({"result": result}), 200
274
+
275
+
276
+ def count_key_value_analyses_index() -> tuple[Response, int]:
277
+ """Route to return the key-value counts for fields in an index."""
278
+ es_query_conn: ElasticQueryConn = current_app.elastic_query_conn # type: ignore[attr-defined]
279
+ fields = [
280
+ "metadata.barcode",
281
+ "metadata.reference_genome",
282
+ "metadata.sample_name",
283
+ "metadata.source",
284
+ ]
285
+
286
+ result: dict[str, dict[str, int]] = {}
287
+ for field in fields:
288
+ result[field] = es_query_conn.get_all_documents_kv_count(
289
+ es_query_conn.analyses_index, field
290
+ )
212
291
 
213
- all_documents = [hit["_source"] for hit in response["hits"]["hits"]]
214
-
215
- return jsonify(all_documents)
292
+ return jsonify({"result": result}), 200
293
+
294
+
295
+ def count_wet_process_fields() -> tuple[Response, int]:
296
+ """Return key-value counts for each field in wet_process documents."""
297
+ # BUG: this does not work for numeric values.
298
+ # error_rate_expected, fragmentation, reads_size will always return an
299
+ # empty dict.
300
+ es_query_conn: ElasticQueryConn = current_app.elastic_query_conn # type: ignore[attr-defined]
301
+ wet_fields = [
302
+ "metadata.amplification",
303
+ "metadata.error_rate_expected",
304
+ "metadata.flowcell_type",
305
+ "metadata.fragmentation",
306
+ "metadata.generic_kit",
307
+ "metadata.input_type",
308
+ "metadata.library_kit",
309
+ "metadata.manufacturer",
310
+ "metadata.reads_size",
311
+ "metadata.sequencer",
312
+ "metadata.sequencing_kit",
313
+ "metadata.sequencing_type",
314
+ "proc_id",
315
+ "type",
316
+ ]
317
+
318
+ result: dict[str, dict[str, int]] = {}
319
+ for field in wet_fields:
320
+ result[field] = es_query_conn.get_all_documents_kv_count(
321
+ es_query_conn.wet_processes_index, field
322
+ )
216
323
 
324
+ return jsonify({"result": result}), 200
325
+
326
+
327
+ def count_bi_process_fields() -> tuple[Response, int]:
328
+ """Return key-value counts for each field in bi_process documents, including steps."""
329
+ es_query_conn: ElasticQueryConn = current_app.elastic_query_conn # type: ignore[attr-defined]
330
+ # Champs de niveau racine
331
+ bi_fields = [
332
+ "metadata.name",
333
+ "metadata.pipeline_version",
334
+ "metadata.sequencing_type",
335
+ "proc_id",
336
+ "type",
337
+ ]
338
+
339
+ # Champs des steps (dans la liste steps[])
340
+ step_fields = [
341
+ "metadata.steps.name",
342
+ "metadata.steps.cmd",
343
+ "metadata.steps.version",
344
+ "metadata.steps.output", # optionnel
345
+ ]
346
+
347
+ result: dict[str, dict[str, int]] = {}
348
+ for field in bi_fields + step_fields:
349
+ result[field] = es_query_conn.get_all_documents_kv_count(
350
+ es_query_conn.bi_processes_index, field
351
+ )
217
352
 
218
- def get_genelastic_version() -> Response:
219
- """Retourne la version du package genelastic."""
220
- top_level_package = __package__.split(".")[0]
221
- return jsonify({"version": version(top_level_package)})
353
+ return jsonify({"result": result}), 200