pysprint_cli/
codegen.rs

1use lazy_static::lazy_static;
2use std::collections::HashMap;
3use std::io;
4use std::io::Write;
5use std::path::PathBuf;
6use tempfile::Builder;
7use tera::{Context, Tera};
8
9lazy_static! {
10    pub static ref TEMPLATES: Tera = {
11        let mut tera = Tera::default();
12        let _ = tera.add_raw_template(
13            "pstemplate.py_t",
14            r#"ifg = ps.{{ methodname }}.parse_raw(
15    "{{ filename }}",
16{%- if filename2 %} "{{ filename2 }}", {% endif %}
17{%- if filename3 %} "{{ filename3 }}", {% endif %}
18{% if skiprows %} skiprows={{ skiprows }}, {%- else %} skiprows=0, {% endif %}
19{% if decimal %} decimal="{{ decimal }}", {%- else %} ".", {% endif %}
20{% if delimiter %} delimiter="{{ delimiter }}", {%- else %} ",", {% endif %}
21{% if meta_len %} meta_len={{ meta_len }} {%- else %} meta_len=0 {% endif %}
22)
23
24SKIP_IF = ("ref", "sam", "reference", "sample", "noeval")
25
26for entry in SKIP_IF:
27    try:
28        if entry in ifg.meta['comment']:
29            import sys
30            sys.exit("file skipped due to user comment")
31    except KeyError:
32        pass
33
34{% if chdomain -%} ifg.chdomain() {%- endif %}
35
36{% if slice_start and slice_stop -%} ifg.slice({{ slice_start }}, {{ slice_stop }}){%- endif %}
37{%- if slice_start and not slice_stop -%} ifg.slice(start={{ slice_start }}){%- endif %}
38{%- if not slice_start and slice_stop -%} ifg.slice(stop={{ slice_stop }}){%- endif %}
39
40x_before_transform = np.copy(ifg.x)
41y_before_transform = np.copy(ifg.y_norm)
42
43{%if detach %}
44ifg.plot()
45plt.show(block=True)
46{% endif %}
47
48
49{% for cmd in before_evaluate_triggers %}
50{{ cmd }}
51{% endfor %}
52
53{% if methodname == "FFTMethod" %}
54ifg.autorun({{ reference_frequency }}, {{ order }}, show_graph=False, enable_printing=False)
55{% elif methodname == "WFTMethod" %}
56ifg.cover(
57    {% if windows %}{{ windows }}{% else %}300{% endif %},
58    {% if fwhm and not std %}fwhm={{ fwhm }},{% endif %}
59    {% if std and not fwhm %}std={{ std }},{% endif %}
60    {%if not std and not fwhm %}fwhm=0.05{% endif %}
61)
62
63ifg.calculate({{ reference_frequency }}, {{ order }}, parallel={% if parallel %}True{% else %}False{% endif %}, fastmath=False)
64{% elif methodname == "MinMaxMethod" %}
65ifg.init_edit_session(
66    {% if min and max %}
67    side="both"
68    {% elif min %}
69    side="min"
70    {% elif max %}
71    side="max"
72    {% else %}
73    side="both"
74    {% endif %}
75)
76plt.show(block=True)
77ifg.calculate({{ reference_frequency }}, {{ order }}, scan=True,
78    {% if min and max %}
79    onesided=False
80    {% elif min %}
81    onesided=True
82    {% elif max %}
83    onesided=True
84    {% else %}
85    onesided=False
86{% endif %})
87{% else %}
88print("{{ methodname }} is not yet implemented..")
89{% endif %}
90
91{% if heatmap and methodname == "WFTMethod" %}
92ifg.heatmap()
93plt.show(block=True)
94{% endif %}
95
96fragment = ps.utils._prepare_json_fragment(ifg, "{{ filename_raw }}", x_before_transform, y_before_transform, verbosity={{ verbosity }})
97ps.utils._write_or_update_json_fragment("{{ workdir }}/{{ result_file }}", fragment, "{{ filename_raw }}")
98
99{% for cmd in after_evaluate_triggers %}
100{{ cmd }}
101{% endfor %}"#,
102        );
103        tera
104    };
105}
106
107pub fn write_tempfile(name: &str, content: &str, path: &str) -> std::io::Result<()> {
108    let tempfile = Builder::new().tempfile_in(path)?;
109
110    let mut _file = tempfile.persist(format!("{}/{}_pstemp.py", path, name))?;
111    writeln!(_file, "{}", content)?;
112
113    Ok(())
114}
115
116pub fn render_template(
117    file: &str,
118    path: &str,
119    text_options: &HashMap<String, String>,
120    number_options: &HashMap<String, Box<f64>>,
121    bool_options: &HashMap<String, Box<bool>>,
122    before_evaluate_triggers: &[String],
123    after_evaluate_triggers: &[String],
124    result_file: &str,
125    verbosity: u8,
126) -> Result<std::string::String, tera::Error> {
127    let mut context = Context::new();
128
129    for (key, entry) in number_options {
130        context.insert(key, &entry);
131    }
132
133    for (key, entry) in text_options {
134        context.insert(key, &entry);
135    }
136
137    for (key, entry) in bool_options {
138        context.insert(key, &entry);
139    }
140    // Specials
141    context.insert("verbosity", &verbosity);
142    context.insert("result_file", result_file);
143    context.insert("filename_raw", &file);
144    context.insert("workdir", &path);
145    context.insert("filename", &format!("{}/{}", path, file));
146
147    // other
148    context.insert("before_evaluate_triggers", &before_evaluate_triggers);
149    context.insert("after_evaluate_triggers", &after_evaluate_triggers);
150
151    // render as String
152    TEMPLATES.render("pstemplate.py_t", &context)
153}
154
155fn write_default_yaml(path: &str) -> std::io::Result<()> {
156    let cfg_path = PathBuf::from(path).join("eval.yaml");
157    std::fs::write(
158        cfg_path,
159        r#"load_options:
160  - extensions:
161      - "trt"
162      - "txt"
163  - exclude_patterns:
164      - "*_randomfile.trt"
165      - "*_to_skip.trt"
166  - skip:
167      - "filename.trt"
168      - "file_to_skip.trt"
169  - skiprows: 8 # lines
170  - decimal: ","
171  - delimiter: ";"
172  - meta_len: 6 # lines
173
174preprocess:
175  - input_unit: "nm"
176  - chdomain: true
177  - slice_start: 2 # PHz
178  - slice_stop: 4 # PHz
179
180method:
181  - wft
182
183method_details:
184  # globally available options
185  # - auto
186  # - only_phase
187  # - detach
188
189  # options for -- MinMaxMethod --
190  # - min
191  # - max
192  # - both
193
194  # options for -- WFTMethod --
195  # - heatmap
196  # - windows: 200
197  # - fwhm: 0.05 # PHz
198  # - std: 0.05 # PHz
199  # - parallel
200
201  # options for -- FFTMethod --
202  # there is no option currently available
203
204  # options for -- CosFitMethod --
205  # not implemented yet
206
207  # options for -- SPPMethod --
208  # not implemented yet
209
210before_evaluate:
211  - "print('before_evaluate')"
212
213evaluate:
214  - reference_frequency: 2.355 # PHz
215  - order: 3 # up to TOD
216
217after_evaluate:
218  - "print('and after..')"
219
220"#
221        .as_bytes(),
222    )?;
223    Ok(())
224}
225
226pub fn maybe_write_default_yaml(path: &str) {
227    println!(
228        "[INFO] No `eval.yaml` file was detected in the target path. 
229       If you named it something different, use the `-c` option.
230       Type 'y' or 'yes' if you want to generate a default one, or anything else to quit."
231    );
232
233    let mut input_text = String::new();
234    io::stdin()
235        .read_line(&mut input_text)
236        .expect("failed to read from stdin");
237
238    match input_text.to_lowercase().trim() {
239        "yes" | "y" => {
240            let _r = write_default_yaml(path);
241            println!("[INFO] Created `eval.yaml` config file.");
242        }
243        _ => {}
244    };
245}