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 86 test/*.trs
87 87 test/region.reg
88 88 test/test_python.sh
  89 +test/test_csv*.dat
89 90  
90 91 examples/python/*.xml
91 92 examples/python/*.html
... ...
ChangeLog
... ... @@ -3,6 +3,7 @@
3 3 * Version 1.1.0 released
4 4 =========================
5 5  
  6 + Add GCsv::append() method (#1773).
6 7 Add GTestSuite::test_value() string method (#1772).
7 8 Add component setting methods to GModelSky (#1770).
8 9 Add GApplication::logFileClose() method.
... ...
... ... @@ -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&amp; 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&amp; 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
  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 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
... ...