genelastic 0.6.1__py3-none-any.whl → 0.8.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 (56) hide show
  1. genelastic/api/cli_start_api.py +18 -0
  2. genelastic/api/extends/example.py +2 -3
  3. genelastic/api/extends/example.yml +20 -0
  4. genelastic/api/routes.py +160 -23
  5. genelastic/api/server.py +42 -31
  6. genelastic/api/settings.py +5 -8
  7. genelastic/api/specification.yml +350 -0
  8. genelastic/common/__init__.py +41 -9
  9. genelastic/common/cli.py +103 -23
  10. genelastic/common/elastic.py +80 -49
  11. genelastic/common/exceptions.py +0 -2
  12. genelastic/common/server.py +51 -0
  13. genelastic/common/types.py +20 -15
  14. genelastic/import_data/__init__.py +23 -5
  15. genelastic/import_data/analyses.py +17 -20
  16. genelastic/import_data/analysis.py +69 -65
  17. genelastic/import_data/bi_process.py +7 -5
  18. genelastic/import_data/bi_processes.py +8 -8
  19. genelastic/import_data/cli_gen_data.py +143 -0
  20. genelastic/import_data/cli_import.py +379 -0
  21. genelastic/import_data/{info.py → cli_info.py} +104 -75
  22. genelastic/import_data/cli_integrity.py +384 -0
  23. genelastic/import_data/cli_validate.py +54 -0
  24. genelastic/import_data/constants.py +11 -32
  25. genelastic/import_data/data_file.py +23 -20
  26. genelastic/import_data/filename_pattern.py +26 -32
  27. genelastic/import_data/import_bundle.py +56 -47
  28. genelastic/import_data/import_bundle_factory.py +166 -158
  29. genelastic/import_data/logger.py +22 -18
  30. genelastic/import_data/random_bundle.py +425 -0
  31. genelastic/import_data/tags.py +46 -26
  32. genelastic/import_data/wet_process.py +8 -4
  33. genelastic/import_data/wet_processes.py +13 -8
  34. genelastic/ui/__init__.py +0 -0
  35. genelastic/ui/cli_start_ui.py +18 -0
  36. genelastic/ui/routes.py +86 -0
  37. genelastic/ui/server.py +14 -0
  38. genelastic/ui/settings.py +7 -0
  39. genelastic/ui/templates/analyses.html +11 -0
  40. genelastic/ui/templates/bi_processes.html +11 -0
  41. genelastic/ui/templates/home.html +4 -0
  42. genelastic/ui/templates/layout.html +34 -0
  43. genelastic/ui/templates/version.html +9 -0
  44. genelastic/ui/templates/wet_processes.html +11 -0
  45. genelastic-0.8.0.dist-info/METADATA +109 -0
  46. genelastic-0.8.0.dist-info/RECORD +52 -0
  47. {genelastic-0.6.1.dist-info → genelastic-0.8.0.dist-info}/WHEEL +1 -1
  48. genelastic-0.8.0.dist-info/entry_points.txt +8 -0
  49. genelastic/import_data/gen_data.py +0 -194
  50. genelastic/import_data/import_data.py +0 -292
  51. genelastic/import_data/integrity.py +0 -290
  52. genelastic/import_data/validate_data.py +0 -43
  53. genelastic-0.6.1.dist-info/METADATA +0 -41
  54. genelastic-0.6.1.dist-info/RECORD +0 -36
  55. genelastic-0.6.1.dist-info/entry_points.txt +0 -6
  56. {genelastic-0.6.1.dist-info → genelastic-0.8.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,18 @@
1
+ from genelastic.common 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)
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()
@@ -1,7 +1,6 @@
1
- # pylint: disable=missing-module-docstring
2
- from flask import jsonify, Response
1
+ from flask import Response, jsonify
3
2
 
4
3
 
5
4
  def ping_2() -> Response:
6
5
  """Test route to verify that the server is online."""
7
- return jsonify({'message': 'pong_2'})
6
+ return jsonify({"message": "pong_2"})
@@ -0,0 +1,20 @@
1
+ ---
2
+ paths:
3
+ /ping_2:
4
+ get:
5
+ operationId: genelastic.api.extends.example.ping_2
6
+ tags:
7
+ - Tests
8
+ summary: Vérification du statut du serveur
9
+ description: Route de test pour vérifier que le serveur est en ligne
10
+ responses:
11
+ 200:
12
+ description: Le serveur est en ligne
13
+ content:
14
+ application/json:
15
+ schema:
16
+ type: object
17
+ properties:
18
+ message:
19
+ type: string
20
+ example: pong_2
genelastic/api/routes.py CHANGED
@@ -1,45 +1,56 @@
1
- # pylint: disable=missing-module-docstring
1
+ from importlib.metadata import version
2
2
  from pathlib import Path
3
- from flask import jsonify, current_app, Response
3
+ from typing import Any
4
+
5
+ from flask import Response, current_app, jsonify
4
6
 
5
7
 
6
8
  def ping() -> Response:
7
9
  """Test route to verify that the server is online."""
8
- return jsonify({'message': 'pong'})
10
+ return jsonify({"message": "pong"})
9
11
 
10
12
 
11
13
  def list_indices() -> Response:
12
14
  """Route to list Elasticsearch indexes."""
13
- return current_app.elastic_query_conn.get_indices() # type: ignore
15
+ return current_app.elastic_query_conn.get_indices() # type: ignore[attr-defined, no-any-return]
14
16
 
15
17
 
16
18
  def retrieve_document(index_id: str, document_id: str) -> Response:
17
19
  """Route to retrieve a document by its ID."""
18
- document = (current_app.elastic_query_conn # type: ignore
19
- .get_document_by_id(index_id, document_id))
20
+ document = current_app.elastic_query_conn.get_document_by_id( # type: ignore[attr-defined]
21
+ index_id, document_id
22
+ )
20
23
  return jsonify(document)
21
24
 
22
25
 
23
26
  def list_wet_processes() -> Response:
24
27
  """Route to list wet processes."""
25
- wet_processes_index = f"{current_app.config['GENAPI_ES_INDEX_PREFIX']}-wet_processes"
26
- result = (current_app.elastic_query_conn # type: ignore
27
- .get_field_values(wet_processes_index, "proc_id"))
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
+ )
28
34
  return jsonify(list(result))
29
35
 
30
36
 
31
37
  def list_bi_processes() -> Response:
32
38
  """Route to list bi processes."""
33
- bi_processes_index = f"{current_app.config['GENAPI_ES_INDEX_PREFIX']}-bi_processes"
34
- result = (current_app.elastic_query_conn # type: ignore
35
- .get_field_values(bi_processes_index, "name"))
39
+ bi_processes_index = (
40
+ f"{current_app.config['GENAPI_ES_INDEX_PREFIX']}-bi_processes"
41
+ )
42
+ result = current_app.elastic_query_conn.get_field_values( # type: ignore[attr-defined]
43
+ bi_processes_index, "proc_id"
44
+ )
36
45
  return jsonify(list(result))
37
46
 
38
47
 
39
48
  def list_analyses() -> Response:
40
49
  """Route to list analyses."""
41
50
  analyses_index = f"{current_app.config['GENAPI_ES_INDEX_PREFIX']}-analyses"
42
- result = current_app.elastic_query_conn.get_field_values(analyses_index, "path") # type: ignore
51
+ result = current_app.elastic_query_conn.get_field_values( # type: ignore[attr-defined]
52
+ analyses_index, "path"
53
+ )
43
54
  filenames = [Path(path).name for path in result]
44
55
  return jsonify(filenames)
45
56
 
@@ -55,11 +66,10 @@ def list_analyses_wet_processes(proc_id: str) -> Response:
55
66
  }
56
67
  }
57
68
  }
58
- result = []
59
- response = (current_app.elastic_query_conn # type: ignore
60
- .client.search(index=analyses_index, body=search_query))
61
- for hit in response['hits']['hits']:
62
- result.append(hit['_source']['path'])
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"]]
63
73
 
64
74
  return jsonify(result)
65
75
 
@@ -75,10 +85,137 @@ def list_analyses_bi_processes(proc_id: str) -> Response:
75
85
  }
76
86
  }
77
87
  }
78
- result = []
79
- response = (current_app.elastic_query_conn # type: ignore
80
- .client.search(index=analyses_index, body=search_query))
81
- for hit in response['hits']['hits']:
82
- result.append(hit['_source']['path'])
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"]]
83
92
 
84
93
  return jsonify(result)
94
+
95
+
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,
109
+ }
110
+ }
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
+ )
132
+
133
+ all_documents.extend(response["hits"]["hits"])
134
+
135
+ return jsonify(all_documents)
136
+
137
+
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
+
171
+
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
+
177
+ response = current_app.elastic_query_conn.client.search( # type: ignore[attr-defined]
178
+ index=index_pattern, body=search_query
179
+ )
180
+
181
+ all_documents = [hit["_source"] for hit in response["hits"]["hits"]]
182
+
183
+ return jsonify(all_documents)
184
+
185
+
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")
190
+
191
+ response = current_app.elastic_query_conn.client.search( # type: ignore[attr-defined]
192
+ index=index_pattern, body=search_query
193
+ )
194
+
195
+ all_documents = [hit["_source"] for hit in response["hits"]["hits"]]
196
+
197
+ return jsonify(all_documents)
198
+
199
+
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
+
205
+ search_query = build_snv_mutation_search_query(
206
+ target_svtypes=target_svtypes
207
+ )
208
+
209
+ response = current_app.elastic_query_conn.client.search( # type: ignore[attr-defined]
210
+ index=index_pattern, body=search_query
211
+ )
212
+
213
+ all_documents = [hit["_source"] for hit in response["hits"]["hits"]]
214
+
215
+ return jsonify(all_documents)
216
+
217
+
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)})
genelastic/api/server.py CHANGED
@@ -1,15 +1,17 @@
1
- # pylint: disable=missing-module-docstring
2
- from typing import Any
3
1
  from pathlib import Path
2
+ from typing import Any
3
+
4
+ import connexion
4
5
  import yaml
5
- import connexion # type: ignore
6
+ from connexion import FlaskApp
7
+
6
8
  from genelastic.common import ElasticQueryConn
7
9
 
8
10
 
9
- def load_yaml(file_path: Path) -> Any:
11
+ def load_yaml(file_path: Path) -> Any: # noqa: ANN401
10
12
  """Load a YAML file and return its content."""
11
13
  content = None
12
- with open(file_path, encoding='utf-8') as f:
14
+ with file_path.open(encoding="utf-8") as f:
13
15
  try:
14
16
  content = yaml.safe_load(f)
15
17
  except yaml.YAMLError as exc:
@@ -17,56 +19,65 @@ def load_yaml(file_path: Path) -> Any:
17
19
  return content
18
20
 
19
21
 
20
- def aggregate_openapi_specs(main_spec_file: Path, additional_spec_path: Path) -> Any:
22
+ def aggregate_openapi_specs(
23
+ main_spec_file: Path, additional_spec_path: Path
24
+ ) -> Any: # noqa: ANN401
21
25
  """Aggregate OpenAPI specifications from a main file and a directory
22
- of additional specifications."""
26
+ of additional specifications.
27
+ """
23
28
  main_spec = load_yaml(main_spec_file)
24
29
  try:
25
30
  entries = additional_spec_path.iterdir()
26
31
  except OSError as exc:
27
32
  raise SystemExit(exc) from exc
28
33
 
29
- if not 'paths' in main_spec:
30
- main_spec['paths'] = []
34
+ if "paths" not in main_spec:
35
+ main_spec["paths"] = []
31
36
 
32
37
  for entry in entries:
33
38
  if not entry.is_file():
34
39
  continue
35
40
 
36
- if not entry.suffix in [".yml", ".yaml"]:
41
+ if entry.suffix not in [".yml", ".yaml"]:
37
42
  continue
38
43
 
39
44
  content = load_yaml(entry)
40
45
 
41
- if 'paths' in content:
42
- main_spec['paths'].update(content['paths'])
46
+ if "paths" in content:
47
+ main_spec["paths"].update(content["paths"])
43
48
 
44
49
  return main_spec
45
50
 
46
51
 
47
- # Initialiser l'application Connexion
48
- connexion_app = connexion.FlaskApp(__name__)
49
- connexion_app.app.config.from_object('src.genelastic.api.settings.Config')
52
+ def create_app() -> FlaskApp:
53
+ # Initialiser l'application Connexion
54
+ connexion_app = connexion.FlaskApp(__name__)
55
+ connexion_app.app.config.from_object("genelastic.api.settings")
56
+
57
+ # Initialiser le client Elasticsearch
58
+ es_url = connexion_app.app.config["GENAPI_ES_URL"]
59
+ es_cert_fp = connexion_app.app.config["GENAPI_ES_CERT_FP"]
60
+ es_api_key = connexion_app.app.config["GENAPI_ES_ENCODED_API_KEY"]
50
61
 
51
- # Initialiser le client Elasticsearch
52
- es_url = connexion_app.app.config['GENAPI_ES_URL']
53
- es_cert_fp = connexion_app.app.config['GENAPI_ES_CERT_FP']
54
- es_api_key = connexion_app.app.config['GENAPI_ES_ENCODED_API_KEY']
62
+ connexion_app.app.elastic_query_conn = ElasticQueryConn(
63
+ es_url, es_cert_fp, api_key=es_api_key
64
+ )
55
65
 
56
- connexion_app.app.elastic_query_conn = ElasticQueryConn(es_url, es_cert_fp, api_key=es_api_key)
66
+ connexion_app.app.logger.debug(
67
+ "Successfully connected to Elasticsearch server: %s",
68
+ connexion_app.app.elastic_query_conn.client.info(),
69
+ )
57
70
 
58
- connexion_app.app.logger.debug("Successfully connected to Elasticsearch server: %s",
59
- connexion_app.app.elastic_query_conn.client.info())
71
+ # Chemins des fichiers YAML
72
+ main_yaml_file = Path(__file__).parents[0] / "specification.yml"
73
+ additional_yaml_dir = Path(__file__).parents[0] / "extends"
60
74
 
61
- # Chemins des fichiers YAML
62
- main_yaml_file = Path(__file__).parents[0] / 'specification.yml'
63
- additional_yaml_dir = Path(__file__).parents[0] / 'extends'
75
+ # Charger et combiner les fichiers YAML
76
+ yaml_spec = aggregate_openapi_specs(main_yaml_file, additional_yaml_dir)
64
77
 
65
- # Charger et combiner les fichiers YAML
66
- yaml_spec = aggregate_openapi_specs(main_yaml_file, additional_yaml_dir)
78
+ # Ajouter la spécification vers OpenAPI
79
+ connexion_app.add_api(yaml_spec)
80
+ return connexion_app
67
81
 
68
- # Ajouter la spécification vers OpenAPI
69
- connexion_app.add_api(yaml_spec)
70
82
 
71
- if __name__ == '__main__':
72
- connexion_app.run(debug=True)
83
+ app = create_app()
@@ -1,13 +1,10 @@
1
- # pylint: disable=missing-module-docstring
2
1
  from environs import Env
3
2
 
4
3
  env = Env()
5
4
  env.read_env()
6
5
 
7
- # pylint: disable=missing-class-docstring,too-few-public-methods
8
- class Config:
9
- # Charger toutes les variables d'environnement nécessaires
10
- GENAPI_ES_URL = env.url("GENAPI_ES_URL").geturl()
11
- GENAPI_ES_ENCODED_API_KEY = env.str("GENAPI_ES_ENCODED_API_KEY")
12
- GENAPI_ES_INDEX_PREFIX = env.str("GENAPI_ES_INDEX_PREFIX")
13
- GENAPI_ES_CERT_FP = env.str("GENAPI_ES_CERT_FP")
6
+ # Load required environment variables.
7
+ GENAPI_ES_URL = env.url("GENAPI_ES_URL").geturl()
8
+ GENAPI_ES_ENCODED_API_KEY = env.str("GENAPI_ES_ENCODED_API_KEY")
9
+ GENAPI_ES_INDEX_PREFIX = env.str("GENAPI_ES_INDEX_PREFIX")
10
+ GENAPI_ES_CERT_FP = env.str("GENAPI_ES_CERT_FP")