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