From 907a9edbc9b38f16d053b9c57b54843708a8f8e8 Mon Sep 17 00:00:00 2001 From: Alexandre MARY Date: Tue, 7 Jan 2025 11:17:21 +0100 Subject: [PATCH] another round of moves to cli --- pyproject.toml | 8 +- src/epygram/cli/delfield.py | 148 ++++++++++++++ src/epygram/cli/fa_sp2gp.py | 5 +- src/epygram/cli/histogram.py | 371 +++++++++++++++++++++++++++++++++++ src/epygram/cli/movefield.py | 188 ++++++++++++++++++ src/epygram/cli/what.py | 78 ++++++++ 6 files changed, 793 insertions(+), 5 deletions(-) create mode 100644 src/epygram/cli/delfield.py create mode 100644 src/epygram/cli/histogram.py create mode 100644 src/epygram/cli/movefield.py create mode 100644 src/epygram/cli/what.py diff --git a/pyproject.toml b/pyproject.toml index f174b7e..61a2949 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,11 +49,15 @@ dependencies=[ [project.scripts] epygram = "epygram.cli:main" epy_cartoplot = "epygram.cli.cartoplot:main" +epy_convert = "epygram.cli.convert:main" epy_ddhlfa_plot = "epygram.cli.ddhlfa_plot:main" +epy_delfield = "epygram.cli.delfield:main" epy_domain_maker = "epygram.cli.domain_maker:main" -epy_what_the_grib = "epygram.cli.what_the_grib:main" epy_fa_sp2gp = "epygram.cli.fa_sp2gp:main" -epy_convert = "epygram.cli.convert:main" +epy_histogram = "epygram.cli.histogram:main" +epy_movefield = "epygram.cli.movefield:main" +epy_what = "epygram.cli.what:main" +epy_what_the_grib = "epygram.cli.what_the_grib:main" [tool.setuptools] include-package-data = true diff --git a/src/epygram/cli/delfield.py b/src/epygram/cli/delfield.py new file mode 100644 index 0000000..ed42655 --- /dev/null +++ b/src/epygram/cli/delfield.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright (c) Météo France (2014-) +# This software is governed by the CeCILL-C license under French law. +# http://www.cecill.info + +import argparse + +from bronx.fancies.display import printstatus + +import epygram +from epygram import epylog, epygramError +from .args_catalog import (add_arg_to_parser, + files_management, fields_management, + runtime_options) +from epygram.extra import griberies + + +def main(): + epygram.init_env() + args = get_args() + if args.verbose: + epylog.setLevel('INFO') + else: + epylog.setLevel('WARNING') + delfield(args.filename, + args.fieldseed, + reverse=args.reverse, + progressmode=args.progressmode, + in_place=args.in_place) + + +def delfield(filename, + fieldseed, + reverse=False, + progressmode=None, + in_place=False): + """ + Delete fields. + + :param filename: name of the file to be processed. + :param fieldseed: either a fid or a list of fid, used as a seed for + generating the list of fields to be processed. + :param reverse: if True, reverse the field selection: deletes all but + selected fields. + :param progressmode: among ('verbose', 'percentage', None) + :param in_place: if True, the field(s) is(are) deleted "in place", + not in a new file. + """ + source = epygram.formats.resource(filename, openmode='a') + fidlist = source.find_fields_in_resource(seed=fieldseed) + if source.format not in ('GRIB', 'FA', 'LFI'): + epylog.warning(" ".join(["tool NOT TESTED with format", + source.format, "!"])) + + if in_place: + if not (hasattr(source, "delfield") and callable(getattr(source, "delfield"))): + raise epygramError('unable to remove messages from this format' + + ' resource "in place".') + if reverse: + for f in source.listfields(): + if f not in fidlist: + source.delfield(f) + else: + for f in fidlist: + source.delfield(f) + else: + tobecopied = {'fmt':source.format} + if source.format == 'FA': + tobecopied.update({'headername':source.headername, + 'validity':source.validity, + 'cdiden':source.cdiden, + 'default_compression':source.default_compression, + 'processtype':source.processtype}) + elif source.format == 'LFI': + tobecopied.update({'compressed':source.compressed}) + output = epygram.formats.resource(filename + '.delfield.out', + openmode='w', + **tobecopied) + outputfidlist = source.listfields() + if reverse: + outputfidlist = fidlist + else: + for f in fidlist: + if f in outputfidlist: + outputfidlist.remove(f) + numfields = len(outputfidlist) + n = 1 + for f in outputfidlist: + if progressmode == 'verbose': + epylog.info(str(f)) + elif progressmode == 'percentage': + printstatus(n, numfields) + n += 1 + if source.format == 'GRIB': + read_misc_metadata = set(griberies.defaults.GRIB1_packing.keys()) + read_misc_metadata.update(set(griberies.defaults.GRIB2_keyvalue[5].keys())) + read_misc_metadata.update(set(griberies.defaults.GRIB1_ordering.keys())) + read_misc_metadata.add('centre') + options = {'read_misc_metadata':list(read_misc_metadata)} + else: + options = {} + field = source.readfield(f, **options) + if source.format == 'FA': + options = {'compression':source.fieldscompression.get(f, None)} + elif source.format == 'GRIB': + options = {'sample':'file:' + source.container.abspath} + output.writefield(field, **options) + + +def get_args(): + + # 1. Parse arguments + #################### + parser = argparse.ArgumentParser(description='An EPyGrAM tool for removing field(s) from a resource.', + epilog='End of help for: %(prog)s (EPyGrAM-' + epygram.__version__ + ')') + add_arg_to_parser(parser, files_management['principal_file']) + add_arg_to_parser(parser, files_management['in_place']) + flds = parser.add_mutually_exclusive_group() + add_arg_to_parser(flds, fields_management['field']) + add_arg_to_parser(flds, fields_management['list_of_fields']) + add_arg_to_parser(parser, fields_management['reverse_fields_selection']) + status = parser.add_mutually_exclusive_group() + add_arg_to_parser(status, runtime_options['verbose']) + add_arg_to_parser(status, runtime_options['percentage']) + args = parser.parse_args() + + # 2. Initializations + #################### + # 2.1 options + if args.verbose: + args.progressmode = 'verbose' + elif args.percentage: + args.progressmode = 'percentage' + else: + args.progressmode = None + # 2.2 list of fields to be processed + if args.field is not None: + args.fieldseed = args.field + elif args.listoffields is not None: + listfile = epygram.containers.File(filename=args.listoffields) + with open(listfile.abspath, 'r') as lf: + args.fieldseed = [line.replace('\n', '').strip() for line in lf.readlines()] + else: + args.fieldseed = None + + return args + diff --git a/src/epygram/cli/fa_sp2gp.py b/src/epygram/cli/fa_sp2gp.py index 5370473..85f9c41 100644 --- a/src/epygram/cli/fa_sp2gp.py +++ b/src/epygram/cli/fa_sp2gp.py @@ -10,12 +10,11 @@ import epygram from epygram import epylog -from epygram.cli.args_catalog import (add_arg_to_parser, files_management, - fields_management, runtime_options) +from .args_catalog import (add_arg_to_parser, files_management, + fields_management, runtime_options) def main(): - epygram.init_env() args = get_args() if args.verbose: diff --git a/src/epygram/cli/histogram.py b/src/epygram/cli/histogram.py new file mode 100644 index 0000000..c572032 --- /dev/null +++ b/src/epygram/cli/histogram.py @@ -0,0 +1,371 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright (c) Météo France (2014-) +# This software is governed by the CeCILL-C license under French law. +# http://www.cecill.info + +import argparse +import numpy + +from bronx.syntax.parsing import str2dict +from bronx.syntax.pretty import smooth_string + +import epygram +from epygram import epylog, epygramError +from .args_catalog import (add_arg_to_parser, + files_management, fields_management, + misc_options, output_options, + runtime_options, graphical_options) + +import matplotlib.pyplot as plt + + +def main(): + epygram.init_env() + args = get_args() + if args.verbose: + epylog.setLevel('INFO') + else: + epylog.setLevel('WARNING') + histogram(args.filename, + args.fieldseed, + refname=args.refname, + diffonly=args.diffonly, + computewind=args.computewind, + subzone=args.subzone, + operation=args.operation, + diffoperation=args.diffoperation, + legend=args.legend, + output=args.output if args.output != 'X' else None, + outputfilename=args.outputfilename, + minmax=args.minmax, + diffminmax=args.diffminmax, + bins=args.bins, + diffbins=args.diffbins, + center_hist_on_0=args.center_hist_on_0, + diff_center_hist_on_0=args.diffcenter_hist_on_0, + zoom=args.zoom, + figures_dpi=args.figures_dpi, + mask_threshold=args.mask_threshold) + + +def histogram(filename, + fieldseed, + refname=None, + diffonly=False, + computewind=False, + subzone=None, + operation=None, + diffoperation=None, + legend=None, + output=False, + outputfilename=None, + minmax=None, + diffminmax=None, + bins=50, + diffbins=50, + center_hist_on_0=False, + diff_center_hist_on_0=True, + zoom=None, + figures_dpi=epygram.config.default_figures_dpi, + mask_threshold=None + ): + """ + Build histograms. + + :param filename: name of the file to be processed. + :param fieldseed: field identifier. + :param refname: name of the reference file to be compared to. + :param diffonly: if True, only plots the difference histogram. + :param computewind: from fieldseed, gets U and V components of wind, and + computes the module; plots barbs and module together. + :param subzone: LAM zone among ('C', 'CI', None). + :param operation: makes the requested operation + (e.g. {'operation':'-','operand':273.15} or + {'operation':'exp'}) on the field before hist. + :param diffoperation: makes the requested operation + (e.g. {'operation':'-','operand':273.15} or + {'operation':'exp'}) on the difference field before hist. + :param legend: legend to be written over plot. + :param output: output format, among ('png', 'pdf', False). + :param outputfilename: specify an output filename for the plot + (completed by output format). + :param minmax: tuple giving (or not) min and max fields values to be selected. + :param diffminmax: idem for difference fields. + :param bins: number of bins or bins edges. + :param diffbins: idem for difference fields. + :param zoom: a dict(lonmin, lonmax, latmin, latmax) on which to build the hist. + :param figures_dpi: quality of saved figures. + :param mask_threshold: dict with min and/or max value(s) to mask outside. + """ + if outputfilename and not output: + raise epygramError('*output* format must be defined if outputfilename is supplied.') + + resource = epygram.formats.resource(filename, openmode='r') + if resource.format not in ('GRIB', 'FA', 'LFI'): + epylog.warning(" ".join(["tool NOT TESTED with format", + resource.format, "!"])) + diffmode = refname is not None + if diffmode: + reference = epygram.formats.resource(refname, openmode='r') + + if not diffmode: + if not computewind: + field = resource.readfield(fieldseed) + assert isinstance(field, epygram.fields.H2DField), \ + ' '.join(['Oops ! Looks like', + str(fieldseed), + 'is not known as a horizontal 2D Field by epygram.', + 'Add it to ~/.epygram/user_Field_Dict_FA.csv ?']) + if not field.geometry.grid.get('LAMzone', False): + subzone = None + if field.spectral: + field.sp2gp() + if operation is not None: + field.operation(**operation) + if legend is not None: + title = legend + else: + title = str(fieldseed) + "\n" + str(field.validity.get()) + if zoom is not None: + field = field.extract_zoom(zoom) + plot, _ = field.histogram(subzone=subzone, + title=title, + mask_threshold=mask_threshold, + range=minmax, + bins=bins, + center_hist_on_0=center_hist_on_0) + if not output: + plt.show() + else: + assert len(fieldseed) == 2 + (Ufid, Vfid) = fieldseed + U = resource.readfield(Ufid) + field = U + assert isinstance(field, epygram.fields.H2DField), \ + ' '.join(['Oops ! Looks like', + str(fieldseed), + 'is not known as a horizontal 2D Field by epygram.', + 'Add it to ~/.epygram/user_Field_Dict_FA.csv ?']) + if not field.geometry.grid.get('LAMzone', False): + subzone = None + if U.spectral: + U.sp2gp() + if operation is not None: + U.operation(**operation) + V = resource.readfield(Vfid) + if V.spectral: + V.sp2gp() + if operation is not None: + V.operation(**operation) + vectwind = epygram.fields.make_vector_field(U, V) + field = vectwind.to_module() + if legend is not None: + title = legend + else: + title = 'module ' + str(fieldseed) + "\n" + str(U.validity.get()) + if zoom is not None: + field = field.extract_zoom(zoom) + plot, _ = field.histogram(subzone=subzone, + title=title, + mask_threshold=mask_threshold, + range=minmax, + bins=bins, + center_hist_on_0=center_hist_on_0) + if not output: + plt.show() + else: + field = resource.readfield(fieldseed) + assert isinstance(field, epygram.fields.H2DField), \ + ' '.join(['Oops ! Looks like', + str(fieldseed), + 'is not known as a horizontal 2D Field by epygram.', + 'Add it to ~/.epygram/user_Field_Dict_FA.csv ?']) + if not field.geometry.grid.get('LAMzone', False): + subzone = None + if field.spectral: + field.sp2gp() + if operation is not None: + field.operation(**operation) + if not diffonly: + if legend is not None: + title = legend + else: + title = resource.container.basename + " : " + str(fieldseed) + "\n" + str(field.validity.get()) + if zoom is not None: + field = field.extract_zoom(zoom) + reffield = reference.readfield(fieldseed) + if reffield.spectral: + reffield.sp2gp() + if operation is not None: + reffield.operation(**operation) + if not diffonly: + if legend is not None: + title = legend + else: + title = str(fieldseed) + if zoom is not None: + reffield = reffield.extract_zoom(zoom) + label = [resource.container.basename, reference.container.basename] + plot, _ = field.histogram(subzone=subzone, + title=title, + mask_threshold=mask_threshold, + together_with=reffield, + center_hist_on_0=center_hist_on_0, + range=minmax, + bins=bins, + label=label) + if legend is not None: + title = legend + else: + title = resource.container.basename + " - " + reference.container.basename + " : " + str(fieldseed) + diff = field - reffield + if diffoperation is not None: + diff.operation(**diffoperation) + diffplot, _ = diff.histogram(subzone=subzone, + title=title, + mask_threshold=mask_threshold, + range=diffminmax, + bins=diffbins, + center_hist_on_0=diff_center_hist_on_0) + if not output: + plt.show() + + # Output + if output: + epylog.info("save plots...") + suffix = '.'.join(['hist', output]) + parameter = smooth_string(fieldseed) + # main resource + if not diffonly: + if not diffmode and outputfilename: + outputfile = '.'.join([outputfilename, output]) + else: + outputfile = '.'.join([resource.container.abspath, + parameter, + suffix]) + plot.savefig(outputfile, bbox_inches='tight', dpi=figures_dpi) + # diff + if diffmode: + if not outputfilename: + outputfile = resource.container.absdir + \ + '.'.join(['diff', + resource.container.basename + '-' + reference.container.basename, + parameter, + suffix]) + else: + outputfile = '.'.join([outputfilename, output]) + diffplot.savefig(outputfile, bbox_inches='tight', dpi=figures_dpi) + + +def get_args(): + + # 1. Parse arguments + #################### + parser = argparse.ArgumentParser(description='An EPyGrAM tool for making histograms \ + of meteorological fields from a resource.', + epilog='End of help for: %(prog)s (EPyGrAM-' + epygram.__version__ + ')') + add_arg_to_parser(parser, files_management['principal_file']) + add_arg_to_parser(parser, fields_management['field']) + add_arg_to_parser(parser, fields_management['windfieldU']) + add_arg_to_parser(parser, fields_management['windfieldV']) + diffmodes = parser.add_mutually_exclusive_group() + add_arg_to_parser(diffmodes, files_management['file_to_refer_in_diff']) + add_arg_to_parser(diffmodes, files_management['file_to_refer_in_diffonly']) + add_arg_to_parser(parser, output_options['output']) + add_arg_to_parser(parser, output_options['outputfilename']) + add_arg_to_parser(parser, misc_options['LAMzone']) + add_arg_to_parser(parser, graphical_options['bins']) + add_arg_to_parser(parser, graphical_options['diffbins']) + add_arg_to_parser(parser, graphical_options['minmax']) + add_arg_to_parser(parser, graphical_options['diffminmax']) + add_arg_to_parser(parser, graphical_options['center_hist_on_0']) + add_arg_to_parser(parser, graphical_options['diff_center_hist_on_0']) + add_arg_to_parser(parser, graphical_options['legend']) + add_arg_to_parser(parser, graphical_options['lonlat_zoom']) + add_arg_to_parser(parser, graphical_options['figures_dpi']) + add_arg_to_parser(parser, misc_options['operation_on_field']) + add_arg_to_parser(parser, misc_options['diffoperation_on_field']) + add_arg_to_parser(parser, misc_options['mask_threshold']) + add_arg_to_parser(parser, runtime_options['verbose']) + args = parser.parse_args() + + # 2. Initializations + #################### + # 2.1 options + if args.Drefname is not None: + args.refname = args.Drefname + args.diffonly = True + else: + args.refname = args.refname + args.diffonly = False + args.diffmode = refname is not None + if args.zone in ('C', 'CI'): + args.subzone = args.zone + elif args.zone == 'CIE': + args.subzone = None + if args.bins is not None: + if 'range' in args.bins: + args.bins = list(numpy.arange( + *[float(b) for b in args.bins.strip('range').strip('(').strip(')').split(',')])) + else: + args.bins = [float(b) for b in args.bins.split(',')] + if len(args.bins) == 1: + args.bins = int(args.bins[0]) + else: + args.bins = None + if args.diffbins is not None: + if 'range' in args.diffbins: + args.diffbins = list(numpy.arange( + *[float(b) for b in args.diffbins.strip('range').strip('(').strip(')').split(',')])) + else: + args.diffbins = [float(b) for b in args.diffbins.split(',')] + if len(args.diffbins) == 1: + args.diffbins = int(args.diffbins[0]) + else: + args.diffbins = None + if args.minmax is not None: + args.minmax = args.minmax.split(',') + else: + args.minmax = None + if args.diffminmax is not None: + diffminmax = args.diffminmax.split(',') + else: + args.diffminmax = None + if args.zoom is not None: + args.zoom = str2dict(args.zoom, float) + else: + args.zoom = None + if args.operation is not None: + _operation = args.operation.split(',') + args.operation = {'operation':_operation.pop(0).strip()} + if len(_operation) > 0: + args.operation['operand'] = float(_operation.pop(0).strip()) + else: + args.operation = None + if args.diffoperation is not None: + _diffoperation = args.diffoperation.split(',') + args.diffoperation = {'operation':_diffoperation.pop(0).strip()} + if len(_diffoperation) > 0: + args.diffoperation['operand'] = float(_diffoperation.pop(0).strip()) + else: + args.diffoperation = None + if args.mask_threshold is not None: + args.mask_threshold = str2dict(args.mask_threshold, float) + else: + args.mask_threshold = None + # 2.2 field to be processed + args.computewind = False + if args.field is not None: + args.fieldseed = args.field + elif args.Ucomponentofwind is not None or args.Vcomponentofwind is not None: + args.fieldseed = (args.Ucomponentofwind, args.Vcomponentofwind) + if None in args.fieldseed: + raise epygramError("wind mode: both U & V components of wind must be supplied") + args.computewind = True + if args.diffmode: + raise NotImplementedError("diffmode (-d/D) AND wind mode (--wU/wV) options together.") + else: + raise epygramError("Need to specify a field (-f) or two wind fields (--wU/--wV).") + + return args diff --git a/src/epygram/cli/movefield.py b/src/epygram/cli/movefield.py new file mode 100644 index 0000000..19adb67 --- /dev/null +++ b/src/epygram/cli/movefield.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright (c) Météo France (2014-) +# This software is governed by the CeCILL-C license under French law. +# http://www.cecill.info + +import argparse + +from bronx.fancies.display import printstatus + +import epygram +from epygram import epylog, epygramError +from epygram.extra import griberies +from .args_catalog import (add_arg_to_parser, + files_management, fields_management, + runtime_options) + + +def main(): + epygram.init_env() + args = get_args() + if args.verbose: + epylog.setLevel('INFO') + else: + epylog.setLevel('WARNING') + movefield(args.filename, + args.refname, + args.fieldseed, + progressmode=args.progressmode, + operation=args.replace_op, + in_place=args.in_place) + + +def movefield(filename, + refname, + fieldseed, + progressmode=None, + operation=False, + in_place=False): + """ + Move fields. + + :param filename: name of the file to be processed. + :param refname: name of the source file from which to add fields. + :param fieldseed: either a fid or a list of fid, used as a seed for + generating the list of fields to be processed. + :param progressmode: among ('verbose', 'percentage', None). + :param operation: among ('diff', 'reversediff', 'add', 'multiply') for + operation to be done between fields of file and source. + :param in_place: if True, the field(s) is(are) replaced "in place", + not in a new file. + """ + target = epygram.formats.resource(filename, openmode='a') + if target.format not in ('GRIB', 'FA', 'LFI'): + epylog.warning(" ".join(["tool NOT TESTED with format", + target.format, "!"])) + source = epygram.formats.resource(refname, openmode='r') + fidlist = source.find_fields_in_resource(seed=fieldseed) + + def treat_one_field(f, s, nt, operation=False): + """ + Unitary task to do on one field. + f = fid + s = source, + nt = newtarget + """ + # set reading options + if s.format == 'GRIB': + read_misc_metadata = set(griberies.defaults.GRIB1_packing.keys()) + read_misc_metadata.update(set(griberies.defaults.GRIB2_keyvalue[5].keys())) + read_misc_metadata.update(set(griberies.defaults.GRIB1_ordering.keys())) + read_misc_metadata.add('centre') + options = {'read_misc_metadata':list(read_misc_metadata)} + else: + options = {} + # read source field + field = s.readfield(f, **options) + # read target field and perform operation + if operation and f in target.listfields(): + originfield = target.readfield(f) + if operation == 'diff': + field.operation('*', -1.) + field.operation('+', originfield) + elif operation == 'reversediff': + field.operation('-', originfield) # filename - source + elif operation == 'add': + field.operation('+', originfield) + elif operation == 'multiply': + field.operation('*', originfield) + # set writing options + if s.format == 'FA': + options = {'compression':source.fieldscompression.get(f, None)} + elif s.format == 'GRIB': + options = {'sample':'file:' + s.container.abspath} + # write field + nt.writefield(field, **options) + + # process fields + if in_place: + numfields = len(fidlist) + n = 1 + for f in fidlist: + if progressmode == 'verbose': + epylog.info(f) + elif progressmode == 'percentage': + printstatus(n, numfields) + n += 1 + if source.format == 'GRIB' and f in target.listfields(): + raise epygramError('field ' + str(f) + ' is already in' + + ' target resource: unable to modify GRIB' + + ' messages "in place".') + treat_one_field(f, source, target, operation=operation) + else: + # open new target + tobecopied = {'fmt':target.format} + if source.format == 'FA': + tobecopied.update({'headername':target.headername, + 'validity':target.validity, + 'cdiden':target.cdiden, + 'default_compression':target.default_compression, + 'processtype':target.processtype}) + elif source.format == 'LFI': + tobecopied.update({'compressed':target.compressed}) + newtarget = epygram.formats.resource(filename + '.movefield.out', + openmode='w', + **tobecopied) + targetfields = target.listfields() + to_add_fields = [f for f in fidlist if f not in targetfields] + numfields = len(targetfields) + len(to_add_fields) + n = 1 + for f in targetfields: + if progressmode == 'verbose': + epylog.info(f) + elif progressmode == 'percentage': + printstatus(n, numfields) + n += 1 + if f in fidlist: + treat_one_field(f, source, newtarget, operation=operation) # replace + else: + treat_one_field(f, target, newtarget) # copy (untouched) + for f in to_add_fields: + treat_one_field(f, source, newtarget) # add + + +def get_args(): + + # ## 1. Parse arguments + ###################### + parser = argparse.ArgumentParser(description="An EPyGrAM tool for moving field(s) from a resource to another\ + (fields are not removed from the source file).", + epilog='End of help for: %(prog)s (EPyGrAM-' + epygram.__version__ + ')') + add_arg_to_parser(parser, files_management['principal_file']) + add_arg_to_parser(parser, files_management['source_file']) + add_arg_to_parser(parser, files_management['in_place']) + flds = parser.add_mutually_exclusive_group() + add_arg_to_parser(flds, fields_management['field']) + add_arg_to_parser(flds, fields_management['list_of_fields']) + diff = parser.add_mutually_exclusive_group() + add_arg_to_parser(diff, files_management['replace_by_diff']) + add_arg_to_parser(diff, files_management['replace_by_reversediff']) + add_arg_to_parser(diff, files_management['replace_by_addition']) + add_arg_to_parser(diff, files_management['replace_by_product']) + status = parser.add_mutually_exclusive_group() + add_arg_to_parser(status, runtime_options['verbose']) + add_arg_to_parser(status, runtime_options['percentage']) + args = parser.parse_args() + + # 2. Initializations + #################### + # 2.1 options + if args.verbose: + args.progressmode = 'verbose' + elif args.percentage: + args.progressmode = 'percentage' + else: + args.progressmode = None + # 2.2 list of fields to be processed + if args.field is not None: + args.fieldseed = args.field + elif args.listoffields is not None: + listfile = epygram.containers.File(filename=args.listoffields) + with open(listfile.abspath, 'r') as l: + args.fieldseed = [line.replace('\n', '').strip() for line in l.readlines()] + else: + args.fieldseed = None + + return args + diff --git a/src/epygram/cli/what.py b/src/epygram/cli/what.py new file mode 100644 index 0000000..02fec39 --- /dev/null +++ b/src/epygram/cli/what.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright (c) Météo France (2014-) +# This software is governed by the CeCILL-C license under French law. +# http://www.cecill.info + +import sys +import argparse + +import epygram +from epygram import epylog +from .args_catalog import (add_arg_to_parser, + files_management, fields_management, + runtime_options, output_options) + + +def main(): + epygram.init_env() + args = get_args() + if args.verbose: + epylog.setLevel('INFO') + else: + epylog.setLevel('WARNING') + what(args.filename, + details=args.details, + sortfields=args.sortfields, + stdoutput=args.stdout, + mode=args.mode) + + +def what(filename, + details=None, + sortfields=None, + stdoutput=False, + mode='fid_list'): + """ + Get info about resource. + + :param filename: name of the file to be processed. + :param details: if not None, gives some more details about the fields. + :param sortfields: sort fields. Cf. formats what() method for further details. + :param stdoutput: if True, output is redirected to stdout. + :param mode: for GRIB only, among ('one+list', 'fid_list', 'what', 'ls', 'mars'), \n + - 'one+list' = gives the validity/geometry of the first field in + GRIB, plus the list of fid. + - 'fid_list' = gives only the fid of each field in GRIB. + - 'what' = gives the values of the keys from each GRIB message that + are used to generate an **epygram** field from the message (slower). + - 'ls' = gives the values of the 'ls' keys from each GRIB message. + - 'mars' = gives the values of the 'mars' keys from each GRIB message. + """ + resource = epygram.formats.resource(filename, openmode='r') + if resource.format not in ('GRIB', 'FA', 'DDHLFA', 'LFA', 'LFI', 'TIFFMF'): + epylog.warning(" ".join(["tool NOT TESTED with format", + resource.format, "!"])) + if stdoutput: + out = sys.stdout + else: + out = open(resource.container.abspath + '.info', 'w') + resource.what(out, + details=details, + sortfields=sortfields, + mode=mode) + + +def get_args(): + + parser = argparse.ArgumentParser(description="An EPyGrAM tool for asking what's inside a resource.", + epilog='End of help for: %(prog)s (EPyGrAM-' + epygram.__version__ + ')') + add_arg_to_parser(parser, files_management['principal_file']) + add_arg_to_parser(parser, output_options['get_field_details']) + add_arg_to_parser(parser, fields_management['GRIB_what_mode']) + add_arg_to_parser(parser, fields_management['GRIB_sort']) + add_arg_to_parser(parser, fields_management['sort_fields']) + add_arg_to_parser(parser, output_options['stdout']) + add_arg_to_parser(parser, runtime_options['verbose']) + return parser.parse_args() +