File indexing completed on 2024-12-01 03:29:59
0001 /* 0002 SPDX-FileCopyrightText: 2021 Valentin Boettcher <hiro at protagon.space; @hiro98:tchncs.de> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #ifndef CATALOGCSVIMPORT_H 0008 #define CATALOGCSVIMPORT_H 0009 0010 #include <QDialog> 0011 #include <QString> 0012 #include <QComboBox> 0013 #include <QFile> 0014 #include "polyfills/qstring_hash.h" 0015 #include <unordered_map> 0016 #include <rapidcsv.h> 0017 #include "catalogobject.h" 0018 #include "skyobject.h" 0019 #include "catalogobjectlistmodel.h" 0020 0021 namespace Ui 0022 { 0023 class CatalogCSVImport; 0024 } 0025 0026 /** 0027 * Custom Conversion Logic 0028 * 0029 * This requires some template magic to give the parser information 0030 * wether to use degrees or hours. 0031 */ 0032 enum coord_unit 0033 { 0034 deg = 0, 0035 hours = 1, 0036 }; 0037 0038 template <coord_unit unit_p> 0039 struct typed_dms 0040 { 0041 dms data; 0042 }; 0043 0044 namespace rapidcsv 0045 { 0046 template <> 0047 inline void 0048 Converter<typed_dms<coord_unit::deg>>::ToVal(const std::string &pStr, 0049 typed_dms<coord_unit::deg> &pVal) const 0050 { 0051 if (!pVal.data.setFromString(pStr.c_str(), true)) 0052 throw std::exception(); 0053 } 0054 0055 template <> 0056 inline void 0057 Converter<typed_dms<coord_unit::hours>>::ToVal(const std::string &pStr, 0058 typed_dms<coord_unit::hours> &pVal) const 0059 { 0060 if (!pVal.data.setFromString(pStr.c_str(), false)) 0061 throw std::exception(); 0062 } 0063 0064 template <> 0065 inline void Converter<QString>::ToVal(const std::string &pStr, QString &pVal) const 0066 { 0067 pVal = QString(pStr.c_str()); 0068 } 0069 } // namespace rapidcsv 0070 0071 class CatalogCSVImport : public QDialog 0072 { 0073 Q_OBJECT 0074 0075 public: 0076 explicit CatalogCSVImport(QWidget *parent = nullptr); 0077 ~CatalogCSVImport(); 0078 0079 /** 0080 * Maps a string to a `SkyObject::TYPE`. 0081 */ 0082 using type_map = std::unordered_map<std::string, SkyObject::TYPE>; 0083 0084 /** 0085 * Maps a field in the Catalog object to a column in the CSV. 0086 * 0087 * The first element of the tuple can take on the following values: 0088 * - -2 :: Default initialize this value. 0089 * - -1 :: Take the value in the second part of the string for every row. 0090 * - everything else :: Read from the column indicated by the integer value. 0091 */ 0092 using column_pair = std::pair<int, QString>; 0093 using column_map = std::unordered_map<QString, column_pair>; 0094 0095 inline const std::vector<CatalogObject> &get_objects() const { return m_objects; }; 0096 private slots: 0097 /** Selects a CSV file and opens it. Calls `init_mapping_selectors`. */ 0098 void select_file(); 0099 0100 /** Reads the header of the CSV and initializes the mapping selectors. */ 0101 void init_mapping_selectors(); 0102 0103 /** Add a row to the type table. */ 0104 void type_table_add_map(); 0105 0106 /** Remove the selected row from the type table. */ 0107 void type_table_remove_map(); 0108 0109 /** Read all the objects from the csv */ 0110 inline void read_objects() { read_n_objects(); }; 0111 0112 private: 0113 void init_column_mapping(); 0114 void init_type_table(); 0115 type_map get_type_mapping(); 0116 column_map get_column_mapping(); 0117 void read_n_objects(size_t n = std::numeric_limits<int>::max()); 0118 0119 // Parsing 0120 SkyObject::TYPE parse_type(const std::string &type, const type_map &type_map); 0121 0122 template <typename T> 0123 T cell_or_default(const column_pair &config, const size_t row, const T &default_val) 0124 { 0125 if (config.first < 0) 0126 return default_val; 0127 0128 return m_doc.GetCell<T>(config.first, row); 0129 }; 0130 0131 template <typename T> 0132 T get_default(const column_pair &config, const T &default_val) 0133 { 0134 if (config.first != -1) 0135 return default_val; 0136 0137 rapidcsv::Converter<T> converter(rapidcsv::ConverterParams{}); 0138 0139 T res{}; 0140 converter.ToVal(config.second.toStdString(), res); 0141 0142 return res; 0143 } 0144 0145 Ui::CatalogCSVImport *ui; 0146 0147 /** Disables the mapping selection and resets everything. */ 0148 void reset_mapping(); 0149 0150 /** Maps the fields to a selector widget. */ 0151 std::map<QString, QComboBox *> m_selectors{}; 0152 0153 /** Rapidcsv Document */ 0154 rapidcsv::Document m_doc{}; 0155 0156 /** The Parsed Objects */ 0157 std::vector<CatalogObject> m_objects; 0158 0159 /** The model to preview the import */ 0160 CatalogObjectListModel m_preview_model; 0161 0162 static const char default_separator = ','; 0163 static const char default_comment = '#'; 0164 static const int default_preview_size = 10; 0165 }; 0166 0167 #endif // CATALOGCSVIMPORT_H