Commit 85c45f3279a193bb71db11aeecdd79b8fed7e1c8

Authored by Jürgen Knödlseder
1 parent 61bdde15

Add GCsv::append() method (#1773).

.gitignore
@@ -86,6 +86,7 @@ test/reports/*.xml @@ -86,6 +86,7 @@ test/reports/*.xml
86 test/*.trs 86 test/*.trs
87 test/region.reg 87 test/region.reg
88 test/test_python.sh 88 test/test_python.sh
  89 +test/test_csv*.dat
89 90
90 examples/python/*.xml 91 examples/python/*.xml
91 examples/python/*.html 92 examples/python/*.html
ChangeLog
@@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
3 * Version 1.1.0 released 3 * Version 1.1.0 released
4 ========================= 4 =========================
5 5
  6 + Add GCsv::append() method (#1773).
6 Add GTestSuite::test_value() string method (#1772). 7 Add GTestSuite::test_value() string method (#1772).
7 Add component setting methods to GModelSky (#1770). 8 Add component setting methods to GModelSky (#1770).
8 Add GApplication::logFileClose() method. 9 Add GApplication::logFileClose() method.
@@ -24,6 +24,7 @@ The following methods have been added to the public interface: @@ -24,6 +24,7 @@ The following methods have been added to the public interface:
24 - GFilaname::protocol() 24 - GFilaname::protocol()
25 - GFilename::path() 25 - GFilename::path()
26 - GFilename::file() 26 - GFilename::file()
  27 +- GCsv::append()
27 - GModelSky::spatial() 28 - GModelSky::spatial()
28 - GModelSky::spectral() 29 - GModelSky::spectral()
29 - GModelSky::temporal() 30 - GModelSky::temporal()
@@ -228,6 +229,9 @@ safe (#1717). @@ -228,6 +229,9 @@ safe (#1717).
228 The gammalib::number() function was added to convert a singular noun into 229 The gammalib::number() function was added to convert a singular noun into
229 a plural noun is the number of instances of the noun is not one (#1743). 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 14. XML module 236 14. XML module
233 -------------- 237 --------------
include/GCsv.hpp
@@ -78,6 +78,7 @@ public: @@ -78,6 +78,7 @@ public:
78 const int& nrows(void) const; 78 const int& nrows(void) const;
79 const int& precision(void) const; 79 const int& precision(void) const;
80 void precision(const int& precision); 80 void precision(const int& precision);
  81 + void append(const std::vector<std::string>& list);
81 std::string string(const int& row, const int& col) const; 82 std::string string(const int& row, const int& col) const;
82 double real(const int& row, const int& col) const; 83 double real(const int& row, const int& col) const;
83 int integer(const int& row, const int& col) const; 84 int integer(const int& row, const int& col) const;
pyext/GCsv.i
@@ -28,7 +28,10 @@ @@ -28,7 +28,10 @@
28 #include "GCsv.hpp" 28 #include "GCsv.hpp"
29 #include "GTools.hpp" 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 * @brief Tuple to index conversion to provide element access. 37 * @brief Tuple to index conversion to provide element access.
@@ -100,6 +103,7 @@ public: @@ -100,6 +103,7 @@ public:
100 const int& nrows(void) const; 103 const int& nrows(void) const;
101 const int& precision(void) const; 104 const int& precision(void) const;
102 void precision(const int& precision); 105 void precision(const int& precision);
  106 + void append(const std::vector<std::string>& list);
103 std::string string(const int& row, const int& col) const; 107 std::string string(const int& row, const int& col) const;
104 double real(const int& row, const int& col) const; 108 double real(const int& row, const int& col) const;
105 int integer(const int& row, const int& col) const; 109 int integer(const int& row, const int& col) const;
src/support/GCsv.cpp
@@ -36,6 +36,7 @@ @@ -36,6 +36,7 @@
36 36
37 /* __ Method name definitions ____________________________________________ */ 37 /* __ Method name definitions ____________________________________________ */
38 #define G_ACCESS "GCsv::operator()(int&, int&)" 38 #define G_ACCESS "GCsv::operator()(int&, int&)"
  39 +#define G_APPEND "GCsv::append(std::vector<std::string>&)"
39 #define G_LOAD "GCsv::load(GFilename&, std::string&)" 40 #define G_LOAD "GCsv::load(GFilename&, std::string&)"
40 #define G_SAVE "GCsv::save(GFilename&, std::string&, bool&)" 41 #define G_SAVE "GCsv::save(GFilename&, std::string&, bool&)"
41 42
@@ -250,6 +251,48 @@ GCsv* GCsv::clone(void) const @@ -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 * @brief Get string value 296 * @brief Get string value
254 * 297 *
255 * @param[in] row Table row. 298 * @param[in] row Table row.
@@ -482,7 +525,9 @@ void GCsv::save(const GFilename&amp; filename, @@ -482,7 +525,9 @@ void GCsv::save(const GFilename&amp; filename,
482 // Write columns 525 // Write columns
483 for (int col = 0; col < m_cols; ++col) { 526 for (int col = 0; col < m_cols; ++col) {
484 std::fputs(m_data[row][col].c_str(), fptr); 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 // Write linebreak 533 // Write linebreak
@@ -525,7 +570,7 @@ std::string GCsv::print(const GChatter&amp; chatter) const @@ -525,7 +570,7 @@ std::string GCsv::print(const GChatter&amp; chatter) const
525 result.append("default"); 570 result.append("default");
526 } 571 }
527 else { 572 else {
528 - result.append(gammalib::str(m_precision)); 573 + result.append(gammalib::str(m_precision));
529 } 574 }
530 575
531 } // endif: chatter was not silent 576 } // endif: chatter was not silent
test/data/csv.dat 0 → 100644
  1 +ra,dec,duration
  2 +10.0,-12.0,1800.0
  3 +12.0,-10.0,1000.0
  4 +14.0,-8.0,100.0
  5 +
test/test_GSupport.cpp
@@ -53,9 +53,10 @@ void TestGSupport::set(void){ @@ -53,9 +53,10 @@ void TestGSupport::set(void){
53 append(static_cast<pfunction>(&TestGSupport::test_expand_env), "Test Environment variable"); 53 append(static_cast<pfunction>(&TestGSupport::test_expand_env), "Test Environment variable");
54 append(static_cast<pfunction>(&TestGSupport::test_node_array), "Test GNodeArray"); 54 append(static_cast<pfunction>(&TestGSupport::test_node_array), "Test GNodeArray");
55 append(static_cast<pfunction>(&TestGSupport::test_bilinear), "Test GBilinear"); 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 append(static_cast<pfunction>(&TestGSupport::test_url_string), "Test GUrlString"); 57 append(static_cast<pfunction>(&TestGSupport::test_url_string), "Test GUrlString");
58 append(static_cast<pfunction>(&TestGSupport::test_filename), "Test GFilename"); 58 append(static_cast<pfunction>(&TestGSupport::test_filename), "Test GFilename");
  59 + append(static_cast<pfunction>(&TestGSupport::test_csv), "Test GCsv");
59 60
60 // Return 61 // Return
61 return; 62 return;
@@ -858,6 +859,106 @@ void TestGSupport::test_filename(void) @@ -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 * @brief Main test entry point 962 * @brief Main test entry point
862 ***************************************************************************/ 963 ***************************************************************************/
863 int main(void) 964 int main(void)
test/test_GSupport.hpp
1 /*************************************************************************** 1 /***************************************************************************
2 * test_GSupport.hpp - test support module * 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 * This program is free software: you can redistribute it and/or modify * 7 * This program is free software: you can redistribute it and/or modify *
@@ -54,6 +54,7 @@ public: @@ -54,6 +54,7 @@ public:
54 void test_url_file(void); 54 void test_url_file(void);
55 void test_url_string(void); 55 void test_url_string(void);
56 void test_filename(void); 56 void test_filename(void);
  57 + void test_csv(void);
57 58
58 private: 59 private:
59 // Private methods 60 // Private methods
test/test_GSupport.py
1 # ========================================================================== 1 # ==========================================================================
2 # This module performs unit tests for the GammaLib support module. 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 # This program is free software: you can redistribute it and/or modify 6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by 7 # it under the terms of the GNU General Public License as published by
@@ -45,13 +45,14 @@ class Test(gammalib.GPythonTestSuite): @@ -45,13 +45,14 @@ class Test(gammalib.GPythonTestSuite):
45 Set all test functions. 45 Set all test functions.
46 """ 46 """
47 # Set test name 47 # Set test name
48 - self.name("support") 48 + self.name('support')
49 49
50 # Append tests 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 # Return 57 # Return
57 return 58 return
@@ -96,18 +97,16 @@ class Test(gammalib.GPythonTestSuite): @@ -96,18 +97,16 @@ class Test(gammalib.GPythonTestSuite):
96 Test GUrlFile class. 97 Test GUrlFile class.
97 """ 98 """
98 # Test file writing 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 url.close() 103 url.close()
103 104
104 # Test file reading 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 buffer = url.read(99) 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 url.close() 110 url.close()
112 111
113 # Return 112 # Return
@@ -120,16 +119,14 @@ class Test(gammalib.GPythonTestSuite): @@ -120,16 +119,14 @@ class Test(gammalib.GPythonTestSuite):
120 """ 119 """
121 # Test writing 120 # Test writing
122 url = gammalib.GUrlString() 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 # Test reading 125 # Test reading
127 url.rewind() 126 url.rewind()
128 - buffer = "" 127 + buffer = ''
129 buffer = url.read(99) 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 url.close() 130 url.close()
134 131
135 # Return 132 # Return
@@ -141,29 +138,60 @@ class Test(gammalib.GPythonTestSuite): @@ -141,29 +138,60 @@ class Test(gammalib.GPythonTestSuite):
141 Test GFilename class. 138 Test GFilename class.
142 """ 139 """
143 # Test assigning file names 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 self.test_value(filename.extno(), 0) 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 self.test_value(filename.extno(), 0) 154 self.test_value(filename.extno(), 0)
165 self.test_value(filename.extver(), 2) 155 self.test_value(filename.extver(), 2)
166 156
167 # Return 157 # Return
168 return 158 return
169 -  
170 \ No newline at end of file 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