fred-metadata 0.0.23__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 (44) hide show
  1. fred/__init__.py +0 -0
  2. fred/config/config.yaml +11 -0
  3. fred/config/mamplan_config.yaml +10 -0
  4. fred/images/chicken.png +0 -0
  5. fred/images/human.png +0 -0
  6. fred/images/medaka.png +0 -0
  7. fred/images/mouse.png +0 -0
  8. fred/images/pig.png +0 -0
  9. fred/images/rat.png +0 -0
  10. fred/images/zebrafish.png +0 -0
  11. fred/metaTools.py +546 -0
  12. fred/src/__init__.py +0 -0
  13. fred/src/autogenerate.py +377 -0
  14. fred/src/edit_file.py +509 -0
  15. fred/src/file_reading.py +187 -0
  16. fred/src/find_metafiles.py +557 -0
  17. fred/src/generate.py +437 -0
  18. fred/src/git_whitelists.py +61 -0
  19. fred/src/heatmap/create_heatmap.py +439 -0
  20. fred/src/heatmap/dash_utils.py +633 -0
  21. fred/src/heatmap/heatmap_dash.py +235 -0
  22. fred/src/input_functions.py +907 -0
  23. fred/src/utils.py +1311 -0
  24. fred/src/validate_yaml.py +850 -0
  25. fred/src/web_interface/__init__.py +0 -0
  26. fred/src/web_interface/editing.py +422 -0
  27. fred/src/web_interface/factors_and_conditions.py +962 -0
  28. fred/src/web_interface/file_io.py +43 -0
  29. fred/src/web_interface/html_output.py +220 -0
  30. fred/src/web_interface/searching.py +294 -0
  31. fred/src/web_interface/validation.py +362 -0
  32. fred/src/web_interface/whitelist_parsing.py +199 -0
  33. fred/src/web_interface/wi_object_to_yaml.py +809 -0
  34. fred/src/web_interface/wi_utils.py +277 -0
  35. fred/src/web_interface/yaml_to_wi_object.py +310 -0
  36. fred/src/wi_functions.py +303 -0
  37. fred/structure/keys.yaml +1061 -0
  38. fred/structure/mamplan_keys.yaml +409 -0
  39. fred_metadata-0.0.23.dist-info/METADATA +20 -0
  40. fred_metadata-0.0.23.dist-info/RECORD +44 -0
  41. fred_metadata-0.0.23.dist-info/WHEEL +5 -0
  42. fred_metadata-0.0.23.dist-info/entry_points.txt +2 -0
  43. fred_metadata-0.0.23.dist-info/licenses/LICENSE +21 -0
  44. fred_metadata-0.0.23.dist-info/top_level.txt +1 -0
fred/__init__.py ADDED
File without changes
@@ -0,0 +1,11 @@
1
+ structure: fred # fred, mampok or path to yaml file
2
+ whitelist_repository: https://gitlab.gwdg.de/loosolab/software/metadata_whitelists.git
3
+ private_access:
4
+ name: # github: username for access token; gitlab: oauth2 for project access token
5
+ token: # personal (github) or project (gitlab) access token
6
+ branch: v1.2.*
7
+ whitelist_path: fred
8
+ update_whitelists: True
9
+ output_path: .
10
+ filename: '_metadata' # example_metadata.yaml
11
+ email: bcu.support@mpi-bn.mpg.de
@@ -0,0 +1,10 @@
1
+ structure: mampok
2
+ whitelist_repository: https://gitlab.gwdg.de/loosolab/software/metadata_whitelists.git
3
+ private_access:
4
+ name: # github: username for access token; gitlab: oauth2 for project access token
5
+ token: # personal (github) or project (gitlab) access token
6
+ branch: v1.2.*
7
+ whitelist_path: fred
8
+ update_whitelists: True
9
+ output_path: .
10
+ filename: 'mamplan' # example_metadata.yaml
Binary file
fred/images/human.png ADDED
Binary file
fred/images/medaka.png ADDED
Binary file
fred/images/mouse.png ADDED
Binary file
fred/images/pig.png ADDED
Binary file
fred/images/rat.png ADDED
Binary file
Binary file
fred/metaTools.py ADDED
@@ -0,0 +1,546 @@
1
+ import argparse
2
+ import copy
3
+ import pathlib
4
+ import sys
5
+ import os
6
+ import time
7
+
8
+ sys.path.append(os.path.dirname(__file__))
9
+ from fred.src.generate import Generate
10
+ from fred.src import find_metafiles
11
+ from fred.src import validate_yaml
12
+ from fred.src import file_reading
13
+ from fred.src import utils
14
+ from fred.src import git_whitelists
15
+ from fred.src.heatmap import create_heatmap
16
+
17
+
18
+ class FRED:
19
+
20
+ def __init__(self, config):
21
+ (
22
+ self.whitelist_repo,
23
+ self.whitelist_branch,
24
+ self.whitelist_path,
25
+ self.username,
26
+ self.password,
27
+ structure,
28
+ self.update_whitelists,
29
+ self.output_path,
30
+ self.filename,
31
+ self.email,
32
+ ) = utils.parse_config(config)
33
+ self.fetch_whitelists()
34
+ self.structure = utils.read_in_yaml(structure)
35
+
36
+ def fetch_whitelists(self):
37
+ git_whitelists.get_whitelists(
38
+ self.whitelist_path,
39
+ self.whitelist_repo,
40
+ self.whitelist_branch,
41
+ self.update_whitelists,
42
+ )
43
+
44
+ def find(self, search_path, search, output, output_filename, skip_validation):
45
+ result = find_metafiles.find_projects(
46
+ self.structure, search_path, search, True, skip_validation
47
+ )
48
+ if output == "print":
49
+ if len(result) > 0:
50
+
51
+ # print summary of matching files
52
+ print(find_metafiles.print_summary(result, output))
53
+
54
+ else:
55
+
56
+ # print information that there are no matching files
57
+ print("No matches found")
58
+ elif output == "json":
59
+ if not output_filename:
60
+ output_filename = "search_result"
61
+ json_filename = f"{output_filename}.json"
62
+ utils.save_as_json(
63
+ {"data": find_metafiles.print_summary(result, output)}, json_filename
64
+ )
65
+ print(f"The report was saved to the file '{json_filename}'.")
66
+
67
+ def generate(self, path, project_id, mandatory_only):
68
+ gen = Generate(
69
+ path, project_id, mandatory_only, self.filename, self.structure, self.email
70
+ )
71
+ gen.generate()
72
+
73
+ def validate(
74
+ self, logical_validation, path, output, output_filename, save_empty=False
75
+ ):
76
+ validation_reports = {
77
+ "all_files": 1,
78
+ "corrupt_files": {"count": 0, "report": []},
79
+ "error_count": 0,
80
+ "warning_count": 0,
81
+ }
82
+ if os.path.isdir(path):
83
+ metafiles, validation_reports = file_reading.iterate_dir_metafiles(
84
+ self.structure,
85
+ [path],
86
+ filename=self.filename,
87
+ logical_validation=logical_validation,
88
+ yaml=copy.deepcopy(self.structure),
89
+ whitelist_path=self.whitelist_path,
90
+ )
91
+ else:
92
+ metafile = utils.read_in_yaml(path)
93
+ file_reports = {"file": metafile, "error": None, "warning": None}
94
+ (
95
+ valid,
96
+ missing_mandatory_keys,
97
+ invalid_keys,
98
+ invalid_entries,
99
+ invalid_values,
100
+ logical_warn,
101
+ ) = validate_yaml.validate_file(
102
+ metafile,
103
+ self.structure,
104
+ self.filename,
105
+ logical_validation=logical_validation,
106
+ yaml=copy.deepcopy(self.structure),
107
+ whitelist_path=self.whitelist_path,
108
+ )
109
+ metafile["path"] = path
110
+ if not valid:
111
+ validation_reports["corrupt_files"]["count"] = 1
112
+ validation_reports["error_count"] += (
113
+ len(missing_mandatory_keys)
114
+ + len(invalid_keys)
115
+ + len(invalid_entries)
116
+ + len(invalid_values)
117
+ )
118
+ file_reports["error"] = (
119
+ missing_mandatory_keys,
120
+ invalid_keys,
121
+ invalid_entries,
122
+ invalid_values,
123
+ )
124
+ if len(logical_warn) > 0:
125
+ validation_reports["corrupt_files"]["count"] = 1
126
+ validation_reports["warning_count"] += len(logical_warn)
127
+ file_reports["warning"] = logical_warn
128
+ validation_reports["corrupt_files"]["report"].append(file_reports)
129
+
130
+ print(f'{validation_reports["all_files"]} files were validated.')
131
+ print(
132
+ f'Found {validation_reports["error_count"]} errors and {validation_reports["warning_count"]} warnings in {validation_reports["corrupt_files"]["count"]} of those files.'
133
+ )
134
+
135
+ if validation_reports["corrupt_files"]["count"] > 0 or save_empty is True:
136
+
137
+ res = None
138
+ if output is not None:
139
+ if output == "print":
140
+ res = ["print report"]
141
+ elif output == "txt":
142
+ res = ["save report to txt file"]
143
+ elif output == "json":
144
+ res = ["save report to json file"]
145
+ elif output == "yaml":
146
+ res = ["save report to yaml file"]
147
+ else:
148
+ options = [
149
+ "print report",
150
+ "save report to txt file",
151
+ "save report to json file",
152
+ "save report to yaml file",
153
+ ]
154
+ print(
155
+ f"Do you want to see a report? Choose from the following options (1,...,{len(options)} or n)"
156
+ )
157
+ ask = Generate("", "", False, self.filename, self.structure, self.email)
158
+ ask.print_option_list(options, "")
159
+ res = ask.parse_input_list(options, True)
160
+
161
+ try:
162
+ output_report = {
163
+ "report": copy.deepcopy(validation_reports)["corrupt_files"][
164
+ "report"
165
+ ]
166
+ }
167
+ except KeyError:
168
+ output_report = {"report": []}
169
+ for elem in output_report["report"]:
170
+ id = list(utils.find_keys(elem["file"], "id"))
171
+ if len(id) > 0:
172
+ elem["id"] = id[0]
173
+ else:
174
+ elem["id"] = "missing"
175
+ elem["path"] = elem["file"]["path"]
176
+ errors = (
177
+ list(elem["error"])
178
+ if "error" in elem and elem["error"] is not None
179
+ else []
180
+ )
181
+ elem["error"] = {}
182
+ elem.pop("file")
183
+ for i in range(len(errors)):
184
+
185
+ if len(errors[i]) > 0:
186
+ if i == 0:
187
+ elem["error"]["missing_mandatory_keys"] = errors[i]
188
+ elif i == 1:
189
+ elem["error"]["invalid_keys"] = errors[i]
190
+ elif i == 2:
191
+ whitelist_values = []
192
+ for v in errors[i]:
193
+ key = ":".join(v.split(":")[:-1])
194
+ entry = v.split(":")[-1]
195
+ whitelist_values.append(entry + " in " + key + "\n")
196
+ elem["error"]["invalid_entries"] = whitelist_values
197
+ elif i == 3:
198
+ value = []
199
+ for v in errors[i]:
200
+ value.append(f"{v[0]}: {v[1]} -> {v[2]}")
201
+ elem["error"]["invalid_values"] = value
202
+
203
+ if "warning" in elem:
204
+ if elem["warning"] is not None:
205
+ for i in range(len(elem["warning"])):
206
+ elem["warning"][
207
+ i
208
+ ] = f'{elem["warning"][i][0]}: {elem["warning"][i][1]}'
209
+ else:
210
+ elem.pop("warning")
211
+
212
+ if res is not None:
213
+ if output_filename is None:
214
+ timestamp = time.time()
215
+ output_filename = (
216
+ f'validation_report_{str(timestamp).split(".")[0]}'
217
+ )
218
+
219
+ rep = ""
220
+ for report in validation_reports["corrupt_files"]["report"]:
221
+ rep += f'{"".center(80, "_")}\n\n'
222
+ rep += validate_yaml.print_full_report(
223
+ report["file"], report["error"], report["warning"]
224
+ )
225
+ rep += f'{"".center(80, "_")}\n\n'
226
+
227
+ if "save report to txt file" in res:
228
+ txt_filename = f"{output_filename}.txt"
229
+ txt_f = open(txt_filename, "w")
230
+ txt_f.write(rep)
231
+ print(f"The report was saved to the file '{txt_filename}'.")
232
+ txt_f.close()
233
+
234
+ if "save report to json file" in res:
235
+ json_filename = f"{output_filename}.json"
236
+ utils.save_as_json(output_report, json_filename)
237
+ print(f"The report was saved to the file '{json_filename}'.")
238
+
239
+ if "save report to yaml file" in res:
240
+ yaml_filename = f"{output_filename}.yaml"
241
+ utils.save_as_yaml(output_report, yaml_filename)
242
+ print(f"The report was saved to the file '{yaml_filename}'.")
243
+
244
+ if "print report" in res:
245
+ print(rep)
246
+
247
+ return validation_reports["error_count"], validation_reports["warning_count"]
248
+
249
+ # def edit(self, path, mandatory_only):
250
+ # try:
251
+ # size = os.get_terminal_size()
252
+ # size = size.columns
253
+ # except OSError:
254
+ # size = 80
255
+
256
+ # edit_file.edit_file(path, self.filename, mandatory_only, size)
257
+
258
+ def add_value(self, path, position, value, edit_existing):
259
+ files, errors = file_reading.iterate_dir_metafiles(
260
+ self.structure,
261
+ [path],
262
+ self.filename,
263
+ False,
264
+ return_false=True,
265
+ whitelist_path=self.whitelist_path,
266
+ )
267
+ position = position.split(":")
268
+ # TODO: type
269
+ for file in files:
270
+ file = utils.add_value_at_pos(
271
+ self.structure, file, position, value, edit_existing
272
+ )
273
+ save_path = file["path"]
274
+ file.pop("path")
275
+ print(f"edited file {save_path}")
276
+ utils.save_as_yaml(file, save_path)
277
+
278
+
279
+ def find(args):
280
+ """
281
+ calls script find_metafiles to find matching files and print results
282
+ :param args:
283
+ path: a path of a folder that should be searched for metadata files
284
+ search: a string specifying search parameters linked via 'and', 'or'
285
+ and 'not'
286
+ """
287
+ finding = FRED(args.config)
288
+ finding.find(
289
+ args.path, args.search, args.output, args.filename, args.skip_validation
290
+ )
291
+
292
+
293
+ def generate(args):
294
+ """
295
+ calls script generate_metafile to start dialog
296
+ :param args:
297
+ """
298
+ generating = FRED(args.config)
299
+ generating.generate(args.path, args.id, args.mandatory_only)
300
+
301
+
302
+ def validate(args):
303
+ validating = FRED(args.config)
304
+ errors, warnings = validating.validate(
305
+ not args.skip_logic, args.path, args.output, args.filename
306
+ )
307
+
308
+
309
+ def plot(args):
310
+ fred_object = FRED(args.config)
311
+ input_file = utils.read_in_yaml(args.path)
312
+ plots = create_heatmap.get_heatmap(
313
+ input_file,
314
+ fred_object.structure,
315
+ mode=args.mode,
316
+ labels=args.labels,
317
+ background=args.background,
318
+ sample_labels=args.sample_labels,
319
+ condition_labels=args.condition_labels,
320
+ transpose=args.transpose,
321
+ drop_defaults=args.drop_defaults,
322
+ )
323
+ output_filename = args.filename if args.filename is not None else "fig1"
324
+ if len(plots) > 0:
325
+ try:
326
+ plot = plots[args.setting - 1][1]
327
+ except IndexError:
328
+ print(f"Setting exp{args.setting} does not exist. Defaulting to exp1.")
329
+ plot = plots[0][1]
330
+
331
+ if plot is not None:
332
+ if args.output == "png":
333
+ plot.write_image(f"{output_filename}.{args.output}", format="png")
334
+ print(f"Plot was saved to {output_filename}.{args.output}")
335
+ elif args.output == "html":
336
+ with open(f"{output_filename}.{args.output}", "w") as file:
337
+ file.write(plot.to_html(full_html=False, include_plotlyjs="cdn"))
338
+ print(f"Plot was saved to {output_filename}.{args.output}")
339
+ else:
340
+ plot.show()
341
+ else:
342
+ print("Plot could not be created due to missing samples or conditions.")
343
+ else:
344
+ print("No settings found.")
345
+
346
+
347
+ # def edit(args):
348
+ # editing = FRED(args.config)
349
+ # editing.edit(args.path, args.mandatory_only)
350
+
351
+
352
+ def add_value(args):
353
+ adding = FRED(args.config)
354
+ adding.add_value(args.path, args.position, args.value, args.edit_existing)
355
+
356
+
357
+ def main():
358
+
359
+ parser = argparse.ArgumentParser(prog="metaTools.py")
360
+ subparsers = parser.add_subparsers(title="commands")
361
+
362
+ find_function = subparsers.add_parser(
363
+ "find",
364
+ help="This command is used to find "
365
+ "projects by searching the "
366
+ "metadata files.",
367
+ )
368
+
369
+ find_group = find_function.add_argument_group("mandatory arguments")
370
+ find_group.add_argument(
371
+ "-p", "--path", type=pathlib.Path, required=True, help="The path to be searched"
372
+ )
373
+ find_group.add_argument(
374
+ "-s", "--search", type=str, required=True, help="The search parameters"
375
+ )
376
+ find_group.add_argument(
377
+ "-c",
378
+ "--config",
379
+ type=pathlib.Path,
380
+ help="Config file",
381
+ default=os.path.join(os.path.dirname(__file__), "config", "config.yaml"),
382
+ )
383
+ find_group.add_argument(
384
+ "-o", "--output", default="print", choices=["json", "print"]
385
+ )
386
+ find_group.add_argument("-f", "--filename", default=None)
387
+ find_group.add_argument(
388
+ "-sv", "--skip_validation", default=False, action="store_true"
389
+ )
390
+ find_function.set_defaults(func=find)
391
+
392
+ create_function = subparsers.add_parser(
393
+ "generate", help="This command is used to " "create a metadata file."
394
+ )
395
+ create_group = create_function.add_argument_group("mandatory arguments")
396
+ create_group.add_argument(
397
+ "-p",
398
+ "--path",
399
+ type=pathlib.Path,
400
+ required=True,
401
+ help="The path to save the yaml",
402
+ )
403
+ create_group.add_argument(
404
+ "-c",
405
+ "--config",
406
+ type=pathlib.Path,
407
+ help="Config file",
408
+ default=os.path.join(os.path.dirname(__file__), "config", "config.yaml"),
409
+ )
410
+ create_group.add_argument(
411
+ "-id", "--id", type=str, required=True, help="The ID of the experiment"
412
+ )
413
+ create_function.add_argument(
414
+ "-mo",
415
+ "--mandatory_only",
416
+ default=False,
417
+ action="store_true",
418
+ help="If True, only mandatory keys will " "be filled out",
419
+ )
420
+ create_function.add_argument(
421
+ "-m", "--mode", default="metadata", choices=["metadata", "mamplan"]
422
+ )
423
+ create_function.set_defaults(func=generate)
424
+
425
+ validate_function = subparsers.add_parser("validate", help="")
426
+ validate_group = validate_function.add_argument_group("mandatory arguments")
427
+ validate_group.add_argument("-p", "--path", type=pathlib.Path, required=True)
428
+ validate_function.add_argument(
429
+ "-l", "--skip_logic", default=False, action="store_true"
430
+ )
431
+ validate_function.add_argument(
432
+ "-m", "--mode", default="metadata", choices=["metadata", "mamplan"]
433
+ )
434
+ validate_function.add_argument(
435
+ "-o", "--output", default=None, choices=["json", "txt", "print", "yaml"]
436
+ )
437
+ validate_function.add_argument("-f", "--filename", default=None)
438
+ validate_function.add_argument(
439
+ "-c",
440
+ "--config",
441
+ type=pathlib.Path,
442
+ help="Config file",
443
+ default=os.path.join(os.path.dirname(__file__), "config", "config.yaml"),
444
+ )
445
+ validate_function.set_defaults(func=validate)
446
+
447
+ # edit_function = subparsers.add_parser('edit', help='')
448
+ # edit_group = edit_function.add_argument_group('mandatory_arguments')
449
+ # edit_group.add_argument('-p', '--path', type=pathlib.Path, required=True)
450
+ # edit_function.add_argument('-mo', '--mandatory_only', default=False,
451
+ # action='store_true',
452
+ # help='If True, only mandatory keys will '
453
+ # 'be filled out')
454
+ # edit_function.add_argument('-c', '--config', type=pathlib.Path,
455
+ # help='Config file', default='config.yaml')
456
+ # edit_function.add_argument('-m', '--mode', default='metadata', choices=['metadata', 'mamplan'])
457
+ # edit_function.set_defaults(func=edit)
458
+
459
+ add_value_function = subparsers.add_parser("add_value", help="")
460
+ add_value_function.add_argument(
461
+ "-m", "--mode", default="metadata", choices=["metadata", "mamplan"]
462
+ )
463
+ add_value_function.add_argument("-pos", "--position", required=True)
464
+ add_value_function.add_argument("-v", "--value", required=True)
465
+ add_value_function.add_argument(
466
+ "-t", "--type", default="str", choices=["str", "int", "float", "bool"]
467
+ )
468
+ add_value_function.add_argument("-p", "--path", type=pathlib.Path, required=True)
469
+ add_value_function.add_argument(
470
+ "-e", "--edit_existing", default=False, action="store_true"
471
+ )
472
+ add_value_function.add_argument(
473
+ "-c",
474
+ "--config",
475
+ type=pathlib.Path,
476
+ help="Config file",
477
+ default=os.path.join(os.path.dirname(__file__), "config", "config.yaml"),
478
+ )
479
+ add_value_function.set_defaults(func=add_value)
480
+
481
+ plot_function = subparsers.add_parser("plot", help="")
482
+ plot_function.add_argument("-p", "--path", type=pathlib.Path, required=True)
483
+ plot_function.add_argument(
484
+ "-c",
485
+ "--config",
486
+ type=pathlib.Path,
487
+ help="Config file",
488
+ default=os.path.join(os.path.dirname(__file__), "config", "config.yaml"),
489
+ )
490
+ plot_function.add_argument(
491
+ "-m", "--mode", default="samples", choices=["samples", "conditions"]
492
+ )
493
+ plot_function.add_argument("-s", "--setting", type=int, default=1)
494
+ plot_function.add_argument(
495
+ "-l", "--labels", default="factors", choices=["factors", "all", "none"]
496
+ )
497
+ plot_function.add_argument(
498
+ "-o", "--output", default="show", choices=["show", "png", "html", "dash"]
499
+ )
500
+ plot_function.add_argument("-f", "--filename", type=pathlib.Path)
501
+ plot_function.add_argument(
502
+ "-b",
503
+ "--background",
504
+ default=False,
505
+ action="store_true",
506
+ help="If stated, the background will be displayed in white. Per default it is transparent.",
507
+ )
508
+ plot_function.add_argument(
509
+ "-cl",
510
+ "--condition_labels",
511
+ default=False,
512
+ action="store_true",
513
+ help="If stated, the label of the condition will be displayed as a name. Per default an index is stated. ",
514
+ )
515
+ plot_function.add_argument(
516
+ "-sl",
517
+ "--sample_labels",
518
+ default=False,
519
+ action="store_true",
520
+ help="If stated, the label of the sample will be displayed as a name. Per default an index is stated. ",
521
+ )
522
+ plot_function.add_argument(
523
+ "-t",
524
+ "--transpose",
525
+ default=False,
526
+ action="store_true",
527
+ )
528
+ plot_function.add_argument(
529
+ "-d",
530
+ "--drop_defaults",
531
+ default=False,
532
+ action="store_true",
533
+ )
534
+ plot_function.set_defaults(func=plot)
535
+
536
+ args = parser.parse_args()
537
+
538
+ try:
539
+ args.func(args)
540
+ except AttributeError:
541
+ parser.print_help()
542
+
543
+
544
+ if __name__ == "__main__":
545
+
546
+ main()
fred/src/__init__.py ADDED
File without changes