Commit 85c45f3279a193bb71db11aeecdd79b8fed7e1c8
1 parent
61bdde15
Add GCsv::append() method (#1773).
Showing
10 changed files
with
236 additions
and
45 deletions
.gitignore
ChangeLog
NEWS
| ... | ... | @@ -24,6 +24,7 @@ The following methods have been added to the public interface: |
| 24 | 24 | - GFilaname::protocol() |
| 25 | 25 | - GFilename::path() |
| 26 | 26 | - GFilename::file() |
| 27 | +- GCsv::append() | |
| 27 | 28 | - GModelSky::spatial() |
| 28 | 29 | - GModelSky::spectral() |
| 29 | 30 | - GModelSky::temporal() |
| ... | ... | @@ -228,6 +229,9 @@ safe (#1717). |
| 228 | 229 | The gammalib::number() function was added to convert a singular noun into |
| 229 | 230 | a plural noun is the number of instances of the noun is not one (#1743). |
| 230 | 231 | |
| 232 | +The GCsv::append() method appends a list of strings to a CSV file. This allow | |
| 233 | +easy creation of a CSV file, in particular from Python lists (#1773). | |
| 234 | + | |
| 231 | 235 | |
| 232 | 236 | 14. XML module |
| 233 | 237 | -------------- | ... | ... |
include/GCsv.hpp
| ... | ... | @@ -78,6 +78,7 @@ public: |
| 78 | 78 | const int& nrows(void) const; |
| 79 | 79 | const int& precision(void) const; |
| 80 | 80 | void precision(const int& precision); |
| 81 | + void append(const std::vector<std::string>& list); | |
| 81 | 82 | std::string string(const int& row, const int& col) const; |
| 82 | 83 | double real(const int& row, const int& col) const; |
| 83 | 84 | int integer(const int& row, const int& col) const; | ... | ... |
pyext/GCsv.i
| ... | ... | @@ -28,7 +28,10 @@ |
| 28 | 28 | #include "GCsv.hpp" |
| 29 | 29 | #include "GTools.hpp" |
| 30 | 30 | %} |
| 31 | -%include stl.i | |
| 31 | +%include "stl.i" | |
| 32 | +%include "std_vector.i" | |
| 33 | +%template(vectors) std::vector<std::string>; | |
| 34 | + | |
| 32 | 35 | |
| 33 | 36 | /***********************************************************************//** |
| 34 | 37 | * @brief Tuple to index conversion to provide element access. |
| ... | ... | @@ -100,6 +103,7 @@ public: |
| 100 | 103 | const int& nrows(void) const; |
| 101 | 104 | const int& precision(void) const; |
| 102 | 105 | void precision(const int& precision); |
| 106 | + void append(const std::vector<std::string>& list); | |
| 103 | 107 | std::string string(const int& row, const int& col) const; |
| 104 | 108 | double real(const int& row, const int& col) const; |
| 105 | 109 | int integer(const int& row, const int& col) const; | ... | ... |
src/support/GCsv.cpp
| ... | ... | @@ -36,6 +36,7 @@ |
| 36 | 36 | |
| 37 | 37 | /* __ Method name definitions ____________________________________________ */ |
| 38 | 38 | #define G_ACCESS "GCsv::operator()(int&, int&)" |
| 39 | +#define G_APPEND "GCsv::append(std::vector<std::string>&)" | |
| 39 | 40 | #define G_LOAD "GCsv::load(GFilename&, std::string&)" |
| 40 | 41 | #define G_SAVE "GCsv::save(GFilename&, std::string&, bool&)" |
| 41 | 42 | |
| ... | ... | @@ -250,6 +251,48 @@ GCsv* GCsv::clone(void) const |
| 250 | 251 | |
| 251 | 252 | |
| 252 | 253 | /***********************************************************************//** |
| 254 | + * @brief Append list of strings | |
| 255 | + * | |
| 256 | + * @param[in] list List of strings. | |
| 257 | + * | |
| 258 | + * @exception GException::invalid_argument | |
| 259 | + * Invalid number of elements in list. | |
| 260 | + * | |
| 261 | + * Appends a list of strings to the CSV table. If the table is empty, the | |
| 262 | + * number of elements in the list will determine the number of columns of | |
| 263 | + * the table. If a table exists already, the method will throw an exception | |
| 264 | + * if the number of elements in the list does not correspond to the number | |
| 265 | + * of table columns. | |
| 266 | + ***************************************************************************/ | |
| 267 | +void GCsv::append(const std::vector<std::string>& list) | |
| 268 | +{ | |
| 269 | + // If we have already rows then check columns size ... | |
| 270 | + if (m_rows > 0) { | |
| 271 | + if (m_cols != list.size()) { | |
| 272 | + std::string msg = "Invalid attempt to append "+ | |
| 273 | + gammalib::str(list.size())+" elements to a CSV " | |
| 274 | + "table with "+gammalib::str(m_cols)+" columns."; | |
| 275 | + throw GException::invalid_argument(G_APPEND, msg); | |
| 276 | + } | |
| 277 | + } | |
| 278 | + | |
| 279 | + // ... otherwise store the number of columns | |
| 280 | + else { | |
| 281 | + m_cols = list.size(); | |
| 282 | + } | |
| 283 | + | |
| 284 | + // Append | |
| 285 | + m_data.push_back(list); | |
| 286 | + | |
| 287 | + // Increment number of rows | |
| 288 | + m_rows++; | |
| 289 | + | |
| 290 | + // Return | |
| 291 | + return; | |
| 292 | +} | |
| 293 | + | |
| 294 | + | |
| 295 | +/***********************************************************************//** | |
| 253 | 296 | * @brief Get string value |
| 254 | 297 | * |
| 255 | 298 | * @param[in] row Table row. |
| ... | ... | @@ -482,7 +525,9 @@ void GCsv::save(const GFilename& filename, |
| 482 | 525 | // Write columns |
| 483 | 526 | for (int col = 0; col < m_cols; ++col) { |
| 484 | 527 | std::fputs(m_data[row][col].c_str(), fptr); |
| 485 | - std::fputs(sep.c_str(), fptr); | |
| 528 | + if (col < m_cols-1) { | |
| 529 | + std::fputs(sep.c_str(), fptr); | |
| 530 | + } | |
| 486 | 531 | } |
| 487 | 532 | |
| 488 | 533 | // Write linebreak |
| ... | ... | @@ -525,7 +570,7 @@ std::string GCsv::print(const GChatter& chatter) const |
| 525 | 570 | result.append("default"); |
| 526 | 571 | } |
| 527 | 572 | else { |
| 528 | - result.append(gammalib::str(m_precision)); | |
| 573 | + result.append(gammalib::str(m_precision)); | |
| 529 | 574 | } |
| 530 | 575 | |
| 531 | 576 | } // endif: chatter was not silent | ... | ... |
test/data/csv.dat
0 → 100644
test/test_GSupport.cpp
| ... | ... | @@ -53,9 +53,10 @@ void TestGSupport::set(void){ |
| 53 | 53 | append(static_cast<pfunction>(&TestGSupport::test_expand_env), "Test Environment variable"); |
| 54 | 54 | append(static_cast<pfunction>(&TestGSupport::test_node_array), "Test GNodeArray"); |
| 55 | 55 | append(static_cast<pfunction>(&TestGSupport::test_bilinear), "Test GBilinear"); |
| 56 | - append(static_cast<pfunction>(&TestGSupport::test_url_file), "Test GUrlFile"); | |
| 56 | + append(static_cast<pfunction>(&TestGSupport::test_url_file), "Test GUrlFile"); | |
| 57 | 57 | append(static_cast<pfunction>(&TestGSupport::test_url_string), "Test GUrlString"); |
| 58 | 58 | append(static_cast<pfunction>(&TestGSupport::test_filename), "Test GFilename"); |
| 59 | + append(static_cast<pfunction>(&TestGSupport::test_csv), "Test GCsv"); | |
| 59 | 60 | |
| 60 | 61 | // Return |
| 61 | 62 | return; |
| ... | ... | @@ -858,6 +859,106 @@ void TestGSupport::test_filename(void) |
| 858 | 859 | |
| 859 | 860 | |
| 860 | 861 | /***********************************************************************//** |
| 862 | + * @brief Test GCsv class | |
| 863 | + * | |
| 864 | + * Test the GCsv class. | |
| 865 | + ***************************************************************************/ | |
| 866 | +void TestGSupport::test_csv(void) | |
| 867 | +{ | |
| 868 | + // Test void constructor | |
| 869 | + GCsv csv1; | |
| 870 | + test_value(csv1.size(), 0); | |
| 871 | + test_value(csv1.ncols(), 0); | |
| 872 | + test_value(csv1.nrows(), 0); | |
| 873 | + | |
| 874 | + // Test rows and columns constructor | |
| 875 | + GCsv csv2(3,4); | |
| 876 | + test_value(csv2.size(), 12); | |
| 877 | + test_value(csv2.ncols(), 4); | |
| 878 | + test_value(csv2.nrows(), 3); | |
| 879 | + | |
| 880 | + // Test filename constructor | |
| 881 | + GCsv csv3("data/csv.dat", ","); | |
| 882 | + test_value(csv3.size(), 12); | |
| 883 | + test_value(csv3.ncols(), 3); | |
| 884 | + test_value(csv3.nrows(), 4); | |
| 885 | + | |
| 886 | + // Test copy constructor | |
| 887 | + GCsv csv4(csv3); | |
| 888 | + test_value(csv3.size(), 12); | |
| 889 | + test_value(csv3.ncols(), 3); | |
| 890 | + test_value(csv3.nrows(), 4); | |
| 891 | + | |
| 892 | + // Test clear method | |
| 893 | + csv4.clear(); | |
| 894 | + test_value(csv4.size(), 0); | |
| 895 | + test_value(csv4.ncols(), 0); | |
| 896 | + test_value(csv4.nrows(), 0); | |
| 897 | + | |
| 898 | + // Test clone method | |
| 899 | + GCsv* csv5 = csv3.clone(); | |
| 900 | + test_value(csv5->size(), 12); | |
| 901 | + test_value(csv5->ncols(), 3); | |
| 902 | + test_value(csv5->nrows(), 4); | |
| 903 | + | |
| 904 | + // Test append method | |
| 905 | + std::vector<std::string> row1; | |
| 906 | + std::vector<std::string> row2; | |
| 907 | + row1.push_back("ra"); | |
| 908 | + row1.push_back("dec"); | |
| 909 | + row1.push_back("durations"); | |
| 910 | + row2.push_back("10.0"); | |
| 911 | + row2.push_back("-10.0"); | |
| 912 | + row2.push_back("1000.0"); | |
| 913 | + csv4.append(row1); | |
| 914 | + csv4.append(row2); | |
| 915 | + test_value(csv4.size(), 6); | |
| 916 | + test_value(csv4.ncols(), 3); | |
| 917 | + test_value(csv4.nrows(), 2); | |
| 918 | + | |
| 919 | + // Test operator access method | |
| 920 | + test_value(csv4(0,0), "ra"); | |
| 921 | + test_value(csv4(0,1), "dec"); | |
| 922 | + test_value(csv4(0,2), "durations"); | |
| 923 | + test_value(csv4(1,0), "10.0"); | |
| 924 | + test_value(csv4(1,1), "-10.0"); | |
| 925 | + test_value(csv4(1,2), "1000.0"); | |
| 926 | + | |
| 927 | + // Test access methods | |
| 928 | + test_value(csv4.string(1,0), "10.0"); | |
| 929 | + test_value(csv4.real(1,0), 10.0); | |
| 930 | + test_value(csv4.integer(1,0), 10); | |
| 931 | + | |
| 932 | + // Test set methods | |
| 933 | + csv4.string(1,0,"11.0"); | |
| 934 | + test_value(csv4.string(1,0), "11.0"); | |
| 935 | + csv4.real(1,0, 12.0); | |
| 936 | + test_value(csv4.real(1,0), 12.0); | |
| 937 | + csv4.integer(1,0, 13); | |
| 938 | + test_value(csv4.integer(1,0), 13); | |
| 939 | + | |
| 940 | + // Test save method (make sure that former result is overwritten) | |
| 941 | + csv4.save("test_csv.dat", ",", true); | |
| 942 | + | |
| 943 | + // Test load method | |
| 944 | + GCsv csv6; | |
| 945 | + csv6.load("test_csv.dat", ","); | |
| 946 | + test_value(csv6.size(), 6); | |
| 947 | + test_value(csv6.ncols(), 3); | |
| 948 | + test_value(csv6.nrows(), 2); | |
| 949 | + test_value(csv6(0,0), "ra"); | |
| 950 | + test_value(csv6(0,1), "dec"); | |
| 951 | + test_value(csv6(0,2), "durations"); | |
| 952 | + test_value(csv6(1,0), "13"); | |
| 953 | + test_value(csv6(1,1), "-10.0"); | |
| 954 | + test_value(csv6(1,2), "1000.0"); | |
| 955 | + | |
| 956 | + // Return | |
| 957 | + return; | |
| 958 | +} | |
| 959 | + | |
| 960 | + | |
| 961 | +/***********************************************************************//** | |
| 861 | 962 | * @brief Main test entry point |
| 862 | 963 | ***************************************************************************/ |
| 863 | 964 | int main(void) | ... | ... |
test/test_GSupport.hpp
| 1 | 1 | /*************************************************************************** |
| 2 | 2 | * test_GSupport.hpp - test support module * |
| 3 | 3 | * ----------------------------------------------------------------------- * |
| 4 | - * copyright (C) 2012-2015 by Jean-Baptiste Cayrou * | |
| 4 | + * copyright (C) 2012-2016 by Jean-Baptiste Cayrou * | |
| 5 | 5 | * ----------------------------------------------------------------------- * |
| 6 | 6 | * * |
| 7 | 7 | * This program is free software: you can redistribute it and/or modify * |
| ... | ... | @@ -54,6 +54,7 @@ public: |
| 54 | 54 | void test_url_file(void); |
| 55 | 55 | void test_url_string(void); |
| 56 | 56 | void test_filename(void); |
| 57 | + void test_csv(void); | |
| 57 | 58 | |
| 58 | 59 | private: |
| 59 | 60 | // Private methods | ... | ... |
test/test_GSupport.py
| 1 | 1 | # ========================================================================== |
| 2 | 2 | # This module performs unit tests for the GammaLib support module. |
| 3 | 3 | # |
| 4 | -# Copyright (C) 2012-2015 Juergen Knoedlseder | |
| 4 | +# Copyright (C) 2012-2016 Juergen Knoedlseder | |
| 5 | 5 | # |
| 6 | 6 | # This program is free software: you can redistribute it and/or modify |
| 7 | 7 | # it under the terms of the GNU General Public License as published by |
| ... | ... | @@ -45,13 +45,14 @@ class Test(gammalib.GPythonTestSuite): |
| 45 | 45 | Set all test functions. |
| 46 | 46 | """ |
| 47 | 47 | # Set test name |
| 48 | - self.name("support") | |
| 48 | + self.name('support') | |
| 49 | 49 | |
| 50 | 50 | # Append tests |
| 51 | - self.append(self.test_node_array, "Test GNodeArray") | |
| 52 | - self.append(self.test_url_file, "Test GUrlFile") | |
| 53 | - self.append(self.test_url_string, "Test GUrlString") | |
| 54 | - self.append(self.test_filename, "Test GFilename") | |
| 51 | + self.append(self.test_node_array, 'Test GNodeArray') | |
| 52 | + self.append(self.test_url_file, 'Test GUrlFile') | |
| 53 | + self.append(self.test_url_string, 'Test GUrlString') | |
| 54 | + self.append(self.test_filename, 'Test GFilename') | |
| 55 | + self.append(self.test_csv, 'Test GCsv') | |
| 55 | 56 | |
| 56 | 57 | # Return |
| 57 | 58 | return |
| ... | ... | @@ -96,18 +97,16 @@ class Test(gammalib.GPythonTestSuite): |
| 96 | 97 | Test GUrlFile class. |
| 97 | 98 | """ |
| 98 | 99 | # Test file writing |
| 99 | - url = gammalib.GUrlFile("test_url.dat", "w") | |
| 100 | - self.test_value(url.write("abcd", 4), 4) | |
| 101 | - url.put_char(ord("e")) | |
| 100 | + url = gammalib.GUrlFile('test_url.dat', 'w') | |
| 101 | + self.test_value(url.write('abcd', 4), 4) | |
| 102 | + url.put_char(ord('e')) | |
| 102 | 103 | url.close() |
| 103 | 104 | |
| 104 | 105 | # Test file reading |
| 105 | - buffer = "" | |
| 106 | - url = gammalib.GUrlFile("test_url.dat", "r") | |
| 106 | + buffer = '' | |
| 107 | + url = gammalib.GUrlFile('test_url.dat', 'r') | |
| 107 | 108 | buffer = url.read(99) |
| 108 | - self.test_assert(buffer == "abcde", | |
| 109 | - "Expected \"abcde\" in file, found \""+ | |
| 110 | - buffer+"\"") | |
| 109 | + self.test_value(buffer, 'abcde') | |
| 111 | 110 | url.close() |
| 112 | 111 | |
| 113 | 112 | # Return |
| ... | ... | @@ -120,16 +119,14 @@ class Test(gammalib.GPythonTestSuite): |
| 120 | 119 | """ |
| 121 | 120 | # Test writing |
| 122 | 121 | url = gammalib.GUrlString() |
| 123 | - self.test_value(url.write("abcd", 4), 4) | |
| 124 | - url.put_char(ord("e")) | |
| 122 | + self.test_value(url.write('abcd', 4), 4) | |
| 123 | + url.put_char(ord('e')) | |
| 125 | 124 | |
| 126 | 125 | # Test reading |
| 127 | 126 | url.rewind() |
| 128 | - buffer = "" | |
| 127 | + buffer = '' | |
| 129 | 128 | buffer = url.read(99) |
| 130 | - self.test_assert(buffer == "abcde", | |
| 131 | - "Expected \"abcde\" in file, found \""+ | |
| 132 | - buffer+"\"") | |
| 129 | + self.test_value(buffer, 'abcde') | |
| 133 | 130 | url.close() |
| 134 | 131 | |
| 135 | 132 | # Return |
| ... | ... | @@ -141,29 +138,60 @@ class Test(gammalib.GPythonTestSuite): |
| 141 | 138 | Test GFilename class. |
| 142 | 139 | """ |
| 143 | 140 | # Test assigning file names |
| 144 | - filename = gammalib.GFilename("myfile.fits") | |
| 145 | - self.test_assert(filename.url() == "myfile.fits", | |
| 146 | - "Expected \"myfile.fits\", found \""+ | |
| 147 | - filename.url()+"\"") | |
| 148 | - filename = gammalib.GFilename("myfile.fits[EVENTS]") | |
| 149 | - self.test_assert(filename.url() == "myfile.fits", | |
| 150 | - "Expected \"myfile.fits\", found \""+ | |
| 151 | - filename.url()+"\"") | |
| 152 | - self.test_assert(filename.extname() == "EVENTS", | |
| 153 | - "Expected extension \"EVENTS\", found \""+ | |
| 154 | - filename.extname()+"\"") | |
| 155 | - filename = gammalib.GFilename("myfile.fits[0]") | |
| 156 | - self.test_assert(filename.url() == "myfile.fits", | |
| 157 | - "Expected \"myfile.fits\", found \""+ | |
| 158 | - filename.url()+"\"") | |
| 141 | + filename = gammalib.GFilename('myfile.fits') | |
| 142 | + self.test_value(filename.url(), 'myfile.fits') | |
| 143 | + # | |
| 144 | + filename = gammalib.GFilename('myfile.fits[EVENTS]') | |
| 145 | + self.test_value(filename.url(), 'myfile.fits') | |
| 146 | + self.test_value(filename.extname(), 'EVENTS') | |
| 147 | + # | |
| 148 | + filename = gammalib.GFilename('myfile.fits[0]') | |
| 149 | + self.test_value(filename.url(), 'myfile.fits') | |
| 159 | 150 | self.test_value(filename.extno(), 0) |
| 160 | - filename = gammalib.GFilename("myfile.fits[0,2]") | |
| 161 | - self.test_assert(filename.url() == "myfile.fits", | |
| 162 | - "Expected \"myfile.fits\", found \""+ | |
| 163 | - filename.url()+"\"") | |
| 151 | + # | |
| 152 | + filename = gammalib.GFilename('myfile.fits[0,2]') | |
| 153 | + self.test_value(filename.url(), 'myfile.fits') | |
| 164 | 154 | self.test_value(filename.extno(), 0) |
| 165 | 155 | self.test_value(filename.extver(), 2) |
| 166 | 156 | |
| 167 | 157 | # Return |
| 168 | 158 | return |
| 169 | - | |
| 170 | 159 | \ No newline at end of file |
| 160 | + | |
| 161 | + # Test GCsv class | |
| 162 | + def test_csv(self): | |
| 163 | + """ | |
| 164 | + Test GCsv class. | |
| 165 | + """ | |
| 166 | + # Create CSV file using the append method | |
| 167 | + csv = gammalib.GCsv() | |
| 168 | + csv.append(['ra','dec','duration']) | |
| 169 | + csv.append(['20.0','-70.0','1800.0']) | |
| 170 | + | |
| 171 | + # Test created file | |
| 172 | + self.test_value(csv.size(), 6) | |
| 173 | + self.test_value(csv.ncols(), 3) | |
| 174 | + self.test_value(csv.nrows(), 2) | |
| 175 | + self.test_value(csv[0,0], 'ra') | |
| 176 | + self.test_value(csv[0,1], 'dec') | |
| 177 | + self.test_value(csv[0,2], 'duration') | |
| 178 | + self.test_value(csv[1,0], '20.0') | |
| 179 | + self.test_value(csv[1,1], '-70.0') | |
| 180 | + self.test_value(csv[1,2], '1800.0') | |
| 181 | + | |
| 182 | + # Test saving and loading of file | |
| 183 | + csv.save('test_csv_py.dat', ',', True) | |
| 184 | + csv.load('test_csv_py.dat', ',') | |
| 185 | + | |
| 186 | + # Test loaded file | |
| 187 | + self.test_value(csv.size(), 6) | |
| 188 | + self.test_value(csv.ncols(), 3) | |
| 189 | + self.test_value(csv.nrows(), 2) | |
| 190 | + self.test_value(csv[0,0], 'ra') | |
| 191 | + self.test_value(csv[0,1], 'dec') | |
| 192 | + self.test_value(csv[0,2], 'duration') | |
| 193 | + self.test_value(csv[1,0], '20.0') | |
| 194 | + self.test_value(csv[1,1], '-70.0') | |
| 195 | + self.test_value(csv[1,2], '1800.0') | |
| 196 | + | |
| 197 | + # Return | |
| 198 | + return | ... | ... |