From ff9878940e10b108a5e7ff63cd4071df5b6b8388 Mon Sep 17 00:00:00 2001 From: Lan Le Date: Wed, 5 Jun 2024 10:48:09 +0200 Subject: [PATCH] feat: process DSC layout --- chem_spectra/controller/helper/share.py | 2 + chem_spectra/lib/composer/ni.py | 74 ++++++++++-------- chem_spectra/lib/converter/jcamp/base.py | 4 + .../lib/converter/jcamp/data_type.json | 3 +- .../converter/jcamp/data_type.json.example | 3 +- chem_spectra/lib/converter/jcamp/ni.py | 4 +- chem_spectra/lib/converter/share.py | 4 + environment.yml | 45 +++++------ requirements.txt | 5 +- setup.py | 2 +- tests/fixtures/source/bagit/dsc/dsc.zip | Bin 0 -> 13972 bytes .../bagit/test_bagit_base_converter.py | 10 +++ tests/lib/converter/test_share.py | 6 ++ 13 files changed, 102 insertions(+), 60 deletions(-) create mode 100644 tests/fixtures/source/bagit/dsc/dsc.zip diff --git a/chem_spectra/controller/helper/share.py b/chem_spectra/controller/helper/share.py index 684ac6f8..3ae0ff55 100644 --- a/chem_spectra/controller/helper/share.py +++ b/chem_spectra/controller/helper/share.py @@ -107,6 +107,7 @@ def extract_params(request): list_file_names = request.form.getlist('list_file_names[]') data_type_mapping = request.form.get('data_type_mapping', default='') detector = request.form.get('detector', default=None) + dsc_meta_data = request.form.get('dsc_meta_data', default=None) params = { 'peaks_str': request.form.get('peaks_str', default=None), @@ -131,6 +132,7 @@ def extract_params(request): 'axesUnits': axesUnits, 'data_type_mapping': data_type_mapping, 'detector': detector, + 'dsc_meta_data': dsc_meta_data, } has_params = ( params.get('peaks_str') or diff --git a/chem_spectra/lib/composer/ni.py b/chem_spectra/lib/composer/ni.py index 80269c0a..96cd1089 100644 --- a/chem_spectra/lib/composer/ni.py +++ b/chem_spectra/lib/composer/ni.py @@ -141,6 +141,24 @@ def __gen_header_sec(self): result.append(key_str) return result + def __gen_header_user_input_meta_data(self): + if self.core.is_dsc: + dsc_meta_data = self.core.params.get('dsc_meta_data', None) + melting_point, tg_value = '', '' + if dsc_meta_data is not None: + melting_point = dsc_meta_data.get('meltingPoint', '') + tg_value = dsc_meta_data.get('tg', '') + else: + melting_point_arr = self.core.dic.get('MELTINGPOINT', ['']) + tg_value_arr = self.core.dic.get('TG', ['']) + melting_point = melting_point_arr[0] + tg_value = tg_value_arr[0] + return [ + f'##MELTINGPOINT={melting_point}\n', + f'##TG={tg_value}\n' + ] + return [] + def __get_xy_of_peak(self, peak): if peak is None: return '', '' @@ -214,6 +232,7 @@ def __compose(self): meta.extend(self.__gen_headers_spectrum_orig()) if self.core.is_sec: meta.extend(self.__gen_header_sec()) + meta.extend(self.__gen_header_user_input_meta_data()) meta.extend(self.gen_spectrum_orig()) meta.extend(self.__gen_headers_im()) meta.extend(self.__gen_headers_integration()) @@ -243,35 +262,12 @@ def __compose(self): return meta def __plt_nbins(self): - typ = self.core.typ - if 'NMR' == typ: - return 20 - elif 'INFRARED' == typ: - return 20 - elif 'RAMAN' == typ: - return 20 - elif 'UVVIS' == typ: - return 20 - elif 'THERMOGRAVIMETRIC ANALYSIS' == typ: - return 20 - elif 'MS' == typ: - return 20 return 20 def __fakto(self): typ = self.core.typ - if 'NMR' == typ: - return 1 - elif 'MS' == typ: - return 1 - elif 'INFRARED' == typ: + if 'INFRARED' == typ: return -1 - elif 'RAMAN' == typ: - return 1 - elif 'UVVIS' == typ: - return 1 - elif 'THERMOGRAVIMETRIC ANALYSIS' == typ: - return 1 return 1 def tf_img(self): @@ -666,16 +662,32 @@ def __draw_peaks(self, plt, x_peaks, y_peaks, h, w, y_boundary_max): def __generate_info_box(self, plotlib): - if not self.core.is_sec: + if not (self.core.is_sec or self.core.is_dsc): return core_dic = self.core.dic - sec_data_key = ['MN', 'MW', 'MP', 'D'] result = [] - for key in sec_data_key: - dic_value = core_dic.get(key, []) - key_str = f"{key}={dic_value[0]}" if len(dic_value) > 0 else None - if key_str is not None: - result.append(key_str) + if self.core.is_sec: + sec_data_key = ['MN', 'MW', 'MP', 'D'] + for key in sec_data_key: + dic_value = core_dic.get(key, []) + key_str = f"{key}={dic_value[0]}" if len(dic_value) > 0 else None + if key_str is not None: + result.append(key_str) + else: + dsc_meta_data = self.core.params.get('dsc_meta_data', None) + melting_point, tg_value = '', '' + if dsc_meta_data is not None: + melting_point = dsc_meta_data.get('meltingPoint', '') + tg_value = dsc_meta_data.get('tg', '') + else: + melting_point_arr = self.core.dic.get('MELTINGPOINT', ['']) + tg_value_arr = self.core.dic.get('TG', ['']) + melting_point = melting_point_arr[0] + tg_value = tg_value_arr[0] + + melting_point_str = f"MELTING POINT={melting_point}" + tg_str = f"TG={tg_value}" + result.extend([melting_point_str, tg_str]) info_str = '\n'.join(result) props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) diff --git a/chem_spectra/lib/converter/jcamp/base.py b/chem_spectra/lib/converter/jcamp/base.py index 846c3610..78621ecd 100644 --- a/chem_spectra/lib/converter/jcamp/base.py +++ b/chem_spectra/lib/converter/jcamp/base.py @@ -34,6 +34,7 @@ def __init__(self, path, params=False): self.is_emissions = self.__is_emissions() self.is_dls_acf = self.__is_dls_acf() self.is_dls_intensity = self.__is_dls_intensity() + self.is_dsc = self.__is_dsc() self.non_nmr = self.__non_nmr() self.ncl = self.__ncl() self.simu_peaks = self.__read_simu_peaks() @@ -156,6 +157,9 @@ def __is_dls_acf(self): def __is_dls_intensity(self): return self.typ in ['DLS intensity'] + + def __is_dsc(self): + return self.typ in ['DIFFERENTIAL SCANNING CALORIMETRY'] def __ncl(self): try: diff --git a/chem_spectra/lib/converter/jcamp/data_type.json b/chem_spectra/lib/converter/jcamp/data_type.json index bd90604f..8cccc47f 100644 --- a/chem_spectra/lib/converter/jcamp/data_type.json +++ b/chem_spectra/lib/converter/jcamp/data_type.json @@ -14,6 +14,7 @@ "SORPTION-DESORPTION MEASUREMENT": ["SORPTION-DESORPTION MEASUREMENT"], "Emissions": ["Emissions", "EMISSIONS", "FLUORESCENCE SPECTRUM", "FL SPECTRUM"], "DLS ACF": ["DLS ACF"], - "DLS intensity": ["DLS INTENSITY", "DLS intensity"] + "DLS intensity": ["DLS INTENSITY", "DLS intensity"], + "DIFFERENTIAL SCANNING CALORIMETRY": ["DIFFERENTIAL SCANNING CALORIMETRY"] } } diff --git a/chem_spectra/lib/converter/jcamp/data_type.json.example b/chem_spectra/lib/converter/jcamp/data_type.json.example index 5c135ad6..26b5ff20 100644 --- a/chem_spectra/lib/converter/jcamp/data_type.json.example +++ b/chem_spectra/lib/converter/jcamp/data_type.json.example @@ -14,6 +14,7 @@ "SORPTION-DESORPTION MEASUREMENT": ["SORPTION-DESORPTION MEASUREMENT"], "Emissions": ["Emissions", "EMISSIONS", "FLUORESCENCE SPECTRUM", "FL SPECTRUM"], "DLS ACF": ["DLS ACF"], - "DLS intensity": ["DLS INTENSITY", "DLS intensity"] + "DLS intensity": ["DLS INTENSITY", "DLS intensity"], + "DIFFERENTIAL SCANNING CALORIMETRY": ["DIFFERENTIAL SCANNING CALORIMETRY"] } } diff --git a/chem_spectra/lib/converter/jcamp/ni.py b/chem_spectra/lib/converter/jcamp/ni.py index ce2bc6e8..7e016917 100644 --- a/chem_spectra/lib/converter/jcamp/ni.py +++ b/chem_spectra/lib/converter/jcamp/ni.py @@ -43,6 +43,7 @@ def __init__(self, base): self.is_emissions = base.is_emissions if hasattr(base, 'is_emissions') else False self.is_dls_acf = base.is_dls_acf if hasattr(base, 'is_dls_acf') else False self.is_dls_intensity = base.is_dls_intensity if hasattr(base, 'is_dls_intensity') else False + self.is_dsc = base.is_dsc if hasattr(base, 'is_dsc') else False self.non_nmr = base.non_nmr self.ncl = base.ncl self.is_dept = base.is_dept @@ -98,7 +99,8 @@ def __thres(self): "CYCLIC VOLTAMMETRY": THRESHOLD_XRD, "SORPTION-DESORPTION MEASUREMENT": THRESHOLD_XRD, "DLS intensity": THRESHOLD_XRD, - "Emissions": THRESHOLD_EMISSION + "Emissions": THRESHOLD_EMISSION, + "DIFFERENTIAL SCANNING CALORIMETRY": THRESHOLD_TGA, } if self.params.get('user_data_type_mapping'): diff --git a/chem_spectra/lib/converter/share.py b/chem_spectra/lib/converter/share.py index 7766d420..e98563d4 100644 --- a/chem_spectra/lib/converter/share.py +++ b/chem_spectra/lib/converter/share.py @@ -25,6 +25,7 @@ def parse_params(params): 'jcamp_idx': 0, 'axesUnits': None, 'detector': None, + 'dsc_meta_data': None, } select_x = params.get('select_x', None) @@ -68,6 +69,8 @@ def parse_params(params): user_data_type_mapping = params.get('data_type_mapping') detector = params.get('detector') detector = json.loads(detector) if detector else None + dsc_meta_data = params.get('dsc_meta_data') + dsc_meta_data = json.loads(dsc_meta_data) if dsc_meta_data else None if (cyclicvolta is not None): spectraList = cyclicvolta['spectraList'] if (len(spectraList) > 0): @@ -101,6 +104,7 @@ def parse_params(params): 'axesUnits': axesUnits, 'user_data_type_mapping': user_data_type_mapping, 'detector': detector, + 'dsc_meta_data': dsc_meta_data, } diff --git a/environment.yml b/environment.yml index 5addf1f1..c02dcb55 100644 --- a/environment.yml +++ b/environment.yml @@ -4,24 +4,25 @@ channels: dependencies: - _libgcc_mutex=0.1=main - _openmp_mutex=5.1=1_gnu - - ca-certificates=2024.3.11=h06a4308_0 + - ca-certificates=2023.08.22=h06a4308_0 - ld_impl_linux-64=2.38=h1181459_1 - - libffi=3.4.4=h6a678d5_1 + - libffi=3.4.4=h6a678d5_0 - libgcc-ng=11.2.0=h1234567_1 - libgomp=11.2.0=h1234567_1 - libstdcxx-ng=11.2.0=h1234567_1 - ncurses=6.4=h6a678d5_0 - - openssl=3.0.13=h7f8727e_1 - - pip=24.0=py38h06a4308_0 - - python=3.8.19=h955ad1f_0 + - openssl=3.0.11=h7f8727e_2 + - pip=23.3=py38h06a4308_0 + - python=3.8.18=h955ad1f_0 - readline=8.2=h5eee18b_0 - - setuptools=69.5.1=py38h06a4308_0 - - sqlite=3.45.3=h5eee18b_0 - - tk=8.6.14=h39e8969_0 - - wheel=0.43.0=py38h06a4308_0 - - xz=5.4.6=h5eee18b_1 - - zlib=1.2.13=h5eee18b_1 + - setuptools=68.0.0=py38h06a4308_0 + - sqlite=3.41.2=h5eee18b_0 + - tk=8.6.12=h1ccaba5_0 + - wheel=0.41.2=py38h06a4308_0 + - xz=5.4.2=h5eee18b_0 + - zlib=1.2.13=h5eee18b_0 - pip: + - adjusttext==1.1.1 - astroid==2.15.8 - atomicwrites==1.4.1 - attrs==23.2.0 @@ -32,15 +33,14 @@ dependencies: - click==8.1.7 - contourpy==1.1.1 - coverage==7.5.1 - - coverage-badge==1.1.1 - cycler==0.12.1 - - dill==0.3.8 + - dill==0.3.7 - entrypoints==0.3 - - exceptiongroup==1.2.1 + - exceptiongroup==1.1.3 - flake8==3.7.9 - flask==2.2.5 - flask-jwt-extended==4.5.2 - - fonttools==4.51.0 + - fonttools==4.43.1 - gunicorn==22.0.0 - idna==3.7 - importlib-metadata==3.6.0 @@ -58,12 +58,13 @@ dependencies: - netcdf4==1.5.3 - numpy==1.22.4 - olefile==0.46 - - packaging==24.0 + - packaging==23.2 - pandas==2.0.3 - pathlib2==2.3.4 - pillow==10.3.0 - - platformdirs==4.2.1 + - platformdirs==4.1.0 - pluggy==0.12.0 + - py==1.11.0 - pycodestyle==2.5.0 - pyflakes==2.1.1 - pyjwt==2.8.0 @@ -76,14 +77,14 @@ dependencies: - pytz==2023.3 - rdkit==2023.9.1 - regex==2019.4.9 - - requests==2.31.0 + - requests==2.32.2 - scipy==1.7.3 - six==1.11.0 - tomli==2.0.1 - - tomlkit==0.12.4 - - typing-extensions==4.11.0 - - tzdata==2024.1 - - urllib3==1.26.18 + - tomlkit==0.12.3 + - typing-extensions==4.9.0 + - tzdata==2023.3 + - urllib3==1.26.19 - werkzeug==3.0.3 - wrapt==1.16.0 - zipp==0.5.2 diff --git a/requirements.txt b/requirements.txt index 73621146..b5124d14 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,7 +18,6 @@ MarkupSafe==2.1.2 matplotlib==3.7.3 mccabe==0.6.1 more-itertools==7.2.0 -# -e git+https://github.com/ComPlat/nmrglue.git@c5a7d4d0073fedff68808b4e9c95836a8c20413e#egg=nmrglue -e git+https://github.com/ComPlat/nmrglue.git@e6e8a63b1848ae0525f07de0a6ec0cfdb900ba60#egg=nmrglue netCDF4==1.5.3 numpy==1.22.4 @@ -35,10 +34,10 @@ pytest==7.2.0 python-dateutil==2.8.2 pytz==2023.3 regex==2019.4.9 -requests==2.31.0 +requests==2.32.2 scipy==1.7.3 six==1.11.0 -urllib3==1.26.18 +urllib3==1.26.19 Werkzeug==3.0.3 zipp==0.5.2 pyopenms==2.6.0 diff --git a/setup.py b/setup.py index fc5db5fc..c18fe239 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name='chem-spectra-app', - version='1.2.0', + version='1.2.1', packages=find_packages(), include_package_data=True, zip_safe=False, diff --git a/tests/fixtures/source/bagit/dsc/dsc.zip b/tests/fixtures/source/bagit/dsc/dsc.zip new file mode 100644 index 0000000000000000000000000000000000000000..63079a3965fc4e35792237fe98fd5defb53ea883 GIT binary patch literal 13972 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 literal 0 HcmV?d00001 diff --git a/tests/lib/converter/bagit/test_bagit_base_converter.py b/tests/lib/converter/bagit/test_bagit_base_converter.py index 9a0edbde..2e798168 100644 --- a/tests/lib/converter/bagit/test_bagit_base_converter.py +++ b/tests/lib/converter/bagit/test_bagit_base_converter.py @@ -10,6 +10,7 @@ emissions_layout_path = target_dir + 'emissions/emissions.zip' dls_acf_layout_path = target_dir + 'dls_acf/dls_acf.zip' dls_intensity_layout_path = target_dir + 'dls_intensity/dls_intensity.zip' +dsc_layout_path = target_dir + 'dsc/dsc.zip' def assertFileType(file, mimeStr): assert mimetypes.guess_type(file.name)[0] == mimeStr @@ -144,3 +145,12 @@ def test_bagit_has_one_file_no_combined_image(): converter = BagItConveter(td) assert converter.combined_image is None + +def test_bagit_convert_to_jcamp_dsc_layout(): + with tempfile.TemporaryDirectory() as td: + with zipfile.ZipFile(dsc_layout_path, 'r') as z: + z.extractall(td) + + converter = BagItConveter(td) + jcamp = converter.data[0] + assertJcampContent(jcamp, '##DATA TYPE=DIFFERENTIAL SCANNING CALORIMETRY') diff --git a/tests/lib/converter/test_share.py b/tests/lib/converter/test_share.py index cd19ba11..afde62d8 100644 --- a/tests/lib/converter/test_share.py +++ b/tests/lib/converter/test_share.py @@ -28,6 +28,7 @@ def expected_default_params(): 'jcamp_idx': 0, 'axesUnits': None, 'detector': None, + 'dsc_meta_data': None, } def test_parse_params_without_params(expected_default_params): @@ -222,6 +223,11 @@ def test_parse_params_detector(): def test_parse_solvent(): #TODO: need to be updated assert 1==1 + +def test_parse_dsc_meta_data(): + params = {'dsc_meta_data': '{"meltingPoint": "1.0", "tg": "1.0"}'} + parsed_data = parse_params(params) + assert parsed_data['dsc_meta_data'] == {"meltingPoint": "1.0", "tg": "1.0"} def test_reduce_pts_when_does_not_have_any_x(): array_data = []