From 98081fcec6d58c2a5e8d7bdd5c65516cd5b0efac 2019-03-18 15:16:22 From: Alexis Jeandet Date: 2019-03-18 15:16:22 Subject: [PATCH] More work on new Variable python bindings Signed-off-by: Alexis Jeandet --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 91d278d..2b1c7c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,6 +95,7 @@ FILE (GLOB_RECURSE core_SRCS ./include/Data/DataSeriesIterator.h ./include/Data/DataSeriesUtils.h ./include/Data/SpectrogramSeries.h + ./include/Data/SpectrogramTimeSerie.h ./include/Data/Unit.h ./include/Data/DataProviderParameters.h ./include/Data/OptionalAxis.h diff --git a/external/TimeSeries b/external/TimeSeries index ce006cf..b844294 160000 --- a/external/TimeSeries +++ b/external/TimeSeries @@ -1 +1 @@ -Subproject commit ce006cf8df2b0bb3d433f901b6e80ce69119a430 +Subproject commit b8442947c778c24a35d33c5f8eed8548cbf9fead diff --git a/include/Data/DateTimeRange.h b/include/Data/DateTimeRange.h index dc00184..44c9fe0 100644 --- a/include/Data/DateTimeRange.h +++ b/include/Data/DateTimeRange.h @@ -1,221 +1,219 @@ #ifndef SCIQLOP_DATETIMERANGE_H #define SCIQLOP_DATETIMERANGE_H -#include -#include - -#include - -#include #include #include #include +#include +#include +#include +#include - -template -struct Seconds : opaque::numeric_typedef> , - opaque::binop::multipliable , true , Seconds, T, T>, - opaque::binop::dividable , true , Seconds, T, T>, - opaque::binop::addable , true , Seconds, T, T>, - opaque::binop::subtractable , true , Seconds, T, T> +template +struct Seconds + : opaque::numeric_typedef>, + opaque::binop::multipliable, true, Seconds, T, T>, + opaque::binop::dividable, true, Seconds, T, T>, + opaque::binop::addable, true, Seconds, T, T>, + opaque::binop::subtractable, true, Seconds, T, T> { - using base = opaque::numeric_typedef>; + using base = opaque::numeric_typedef>; using base::base; - operator T () const {return this->value;} + operator T() const { return this->value; } }; -struct InvalidDateTimeRangeTransformation{}; +struct InvalidDateTimeRangeTransformation +{}; struct DateTimeRangeTransformation { - double zoom; - Seconds shift; - bool operator==(const DateTimeRangeTransformation& other) const - { - return SciQLop::numeric::almost_equal(zoom, other.zoom, 1) && - SciQLop::numeric::almost_equal(shift, other.shift, 1); - } - DateTimeRangeTransformation merge(const DateTimeRangeTransformation& other) const - { - return DateTimeRangeTransformation{zoom*other.zoom,shift+other.shift}; - } + double zoom; + Seconds shift; + bool operator==(const DateTimeRangeTransformation& other) const + { + return SciQLop::numeric::almost_equal(zoom, other.zoom, 1) && + SciQLop::numeric::almost_equal(shift, other.shift, 1); + } + DateTimeRangeTransformation + merge(const DateTimeRangeTransformation& other) const + { + return DateTimeRangeTransformation{zoom * other.zoom, shift + other.shift}; + } }; /** * @brief The SqpRange struct holds the information of time parameters */ -struct DateTimeRange { - DateTimeRange() - :m_TStart(std::nan("")), m_TEnd(std::nan("")) - {} - DateTimeRange(double TStart, double TEnd) - :m_TStart(TStart), m_TEnd(TEnd) - {} - /// Creates SqpRange from dates and times - static DateTimeRange fromDateTime(const QDate &startDate, const QTime &startTime, - const QDate &endDate, const QTime &endTime) - { - return {DateUtils::secondsSinceEpoch(QDateTime{startDate, startTime, Qt::UTC}), - DateUtils::secondsSinceEpoch(QDateTime{endDate, endTime, Qt::UTC})}; - } - - static DateTimeRange fromDateTime(const QDateTime &start, const QDateTime &end) - { - return {DateUtils::secondsSinceEpoch(start), - DateUtils::secondsSinceEpoch(end)}; - } - - /// Start time (UTC) - double m_TStart; - /// End time (UTC) - double m_TEnd; - - Seconds delta()const noexcept{return Seconds{this->m_TEnd - this->m_TStart};} - - bool contains(const DateTimeRange &dateTime) const noexcept - { - return (m_TStart <= dateTime.m_TStart && m_TEnd >= dateTime.m_TEnd); - } - - Seconds center() const noexcept - { - return Seconds((m_TStart + m_TEnd) / 2.); - } - - bool intersect(const DateTimeRange &dateTime) const noexcept - { - return (m_TEnd >= dateTime.m_TStart && m_TStart <= dateTime.m_TEnd); - } - - inline DateTimeRange transform(const DateTimeRangeTransformation& tr)const noexcept; - - bool operator==(const DateTimeRange &other) const - { - return SciQLop::numeric::almost_equal(m_TStart, other.m_TStart, 1) && - SciQLop::numeric::almost_equal(m_TEnd, other.m_TEnd, 1); - } - - bool operator!=(const DateTimeRange &other) const { return !(*this == other); } - - void grow(double factor)noexcept - { - double grow_v{delta()*(factor - 1.)/2.}; - m_TStart -= grow_v; - m_TEnd += grow_v; - } - - void shrink(double factor)noexcept - { - double shrink_v{this->delta()*(1. - factor)/2.}; - m_TStart += shrink_v; - m_TEnd -= shrink_v; - } - - DateTimeRange& operator*=(double k) - { - this->grow(k); - return *this; - } - - DateTimeRange& operator/=(double k) - { - this->shrink(k); - return *this; - } - - // compute set difference - std::vector operator-(const DateTimeRange& other)const +struct DateTimeRange +{ + DateTimeRange() : m_TStart(std::nan("")), m_TEnd(std::nan("")) {} + DateTimeRange(double TStart, double TEnd) : m_TStart(TStart), m_TEnd(TEnd) {} + /// Creates SqpRange from dates and times + static DateTimeRange fromDateTime(const QDate& startDate, + const QTime& startTime, + const QDate& endDate, const QTime& endTime) + { + return { + DateUtils::secondsSinceEpoch(QDateTime{startDate, startTime, Qt::UTC}), + DateUtils::secondsSinceEpoch(QDateTime{endDate, endTime, Qt::UTC})}; + } + + static DateTimeRange fromDateTime(const QDateTime& start, + const QDateTime& end) + { + return {DateUtils::secondsSinceEpoch(start), + DateUtils::secondsSinceEpoch(end)}; + } + + /// Start time (UTC) + double m_TStart; + /// End time (UTC) + double m_TEnd; + + Seconds delta() const noexcept + { + return Seconds{this->m_TEnd - this->m_TStart}; + } + + bool contains(const DateTimeRange& dateTime) const noexcept + { + return (m_TStart <= dateTime.m_TStart && m_TEnd >= dateTime.m_TEnd); + } + + Seconds center() const noexcept + { + return Seconds((m_TStart + m_TEnd) / 2.); + } + + bool intersect(const DateTimeRange& dateTime) const noexcept + { + return (m_TEnd >= dateTime.m_TStart && m_TStart <= dateTime.m_TEnd); + } + + inline DateTimeRange transform(const DateTimeRangeTransformation& tr) const + noexcept; + + bool operator==(const DateTimeRange& other) const + { + return SciQLop::numeric::almost_equal(m_TStart, other.m_TStart, 1) && + SciQLop::numeric::almost_equal(m_TEnd, other.m_TEnd, 1); + } + + bool operator!=(const DateTimeRange& other) const + { + return !(*this == other); + } + + void grow(double factor) noexcept + { + double grow_v{delta() * (factor - 1.) / 2.}; + m_TStart -= grow_v; + m_TEnd += grow_v; + } + + void shrink(double factor) noexcept + { + double shrink_v{this->delta() * (1. - factor) / 2.}; + m_TStart += shrink_v; + m_TEnd -= shrink_v; + } + + DateTimeRange& operator*=(double k) + { + this->grow(k); + return *this; + } + + DateTimeRange& operator/=(double k) + { + this->shrink(k); + return *this; + } + + // compute set difference + std::vector operator-(const DateTimeRange& other) const + { + std::vector result; + if(std::isnan(other.m_TStart) || std::isnan(other.m_TEnd) || + !this->intersect(other)) + { result.emplace_back(m_TStart, m_TEnd); } + else { - std::vector result; - if(std::isnan(other.m_TStart)||std::isnan(other.m_TEnd)||!this->intersect(other)) - { - result.emplace_back(m_TStart, m_TEnd); - } - else - { - if(this->m_TStartm_TStart, other.m_TStart); - } - if(this->m_TEnd>other.m_TEnd) - { - result.emplace_back(other.m_TEnd, this->m_TEnd); - } - } - return result; + if(this->m_TStart < other.m_TStart) + { result.emplace_back(this->m_TStart, other.m_TStart); } + if(this->m_TEnd > other.m_TEnd) + { result.emplace_back(other.m_TEnd, this->m_TEnd); } } - + return result; + } }; -template -DateTimeRange& operator+=(DateTimeRange&r, Seconds offset) +template DateTimeRange& operator+=(DateTimeRange& r, Seconds offset) { - shift(r,offset); - return r; + shift(r, offset); + return r; } -template -DateTimeRange& operator-=(DateTimeRange&r, Seconds offset) +template DateTimeRange& operator-=(DateTimeRange& r, Seconds offset) { - shift(r,-offset); - return r; + shift(r, -offset); + return r; } -template -void shift(DateTimeRange& r, Seconds offset) +template void shift(DateTimeRange& r, Seconds offset) { - r.m_TEnd+=static_cast(offset); - r.m_TStart+=static_cast(offset); + r.m_TEnd += static_cast(offset); + r.m_TStart += static_cast(offset); } inline DateTimeRange operator*(const DateTimeRange& r, double k) { - DateTimeRange result{r}; - result.grow(k); - return result; + DateTimeRange result{r}; + result.grow(k); + return result; } inline DateTimeRange operator/(const DateTimeRange& r, double k) { - DateTimeRange result{r}; - result.shrink(k); - return result; + DateTimeRange result{r}; + result.shrink(k); + return result; } template DateTimeRange operator+(const DateTimeRange& r, Seconds offset) { - DateTimeRange result{r}; - shift(result,offset); - return result; + DateTimeRange result{r}; + shift(result, offset); + return result; } template DateTimeRange operator-(const DateTimeRange& r, Seconds offset) { - DateTimeRange result{r}; - shift(result,-offset); - return result; + DateTimeRange result{r}; + shift(result, -offset); + return result; } -const auto INVALID_RANGE - = DateTimeRange{std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()}; +const auto INVALID_RANGE = + DateTimeRange{std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN()}; inline QDebug operator<<(QDebug d, DateTimeRange obj) { - auto tendDateTimeStart = DateUtils::dateTime(obj.m_TStart); - auto tendDateTimeEnd = DateUtils::dateTime(obj.m_TEnd); + auto tendDateTimeStart = DateUtils::dateTime(obj.m_TStart); + auto tendDateTimeEnd = DateUtils::dateTime(obj.m_TEnd); - d << "ts: " << tendDateTimeStart << " te: " << tendDateTimeEnd; - return d; + d << "ts: " << tendDateTimeStart << " te: " << tendDateTimeEnd; + return d; } - - -DateTimeRange DateTimeRange::transform(const DateTimeRangeTransformation &tr) const noexcept +DateTimeRange +DateTimeRange::transform(const DateTimeRangeTransformation& tr) const noexcept { - return DateTimeRange{*this} * tr.zoom + tr.shift; + return DateTimeRange{*this} * tr.zoom + tr.shift; } // Required for using shared_ptr in signals/slots diff --git a/include/Data/SpectrogramTimeSerie.h b/include/Data/SpectrogramTimeSerie.h new file mode 100644 index 0000000..bc736a4 --- /dev/null +++ b/include/Data/SpectrogramTimeSerie.h @@ -0,0 +1,17 @@ +#ifndef SCIQLOP_SPECTROGRAMTIMESERIE_H +#define SCIQLOP_SPECTROGRAMTIMESERIE_H + +#include "CoreGlobal.h" + +#include + +class SCIQLOP_CORE_EXPORT SpectrogramTimeSerie + : public TimeSeries::TimeSerie +{ +public: + SpectrogramTimeSerie() {} + ~SpectrogramTimeSerie() = default; + using TimeSerie::TimeSerie; +}; + +#endif // SCIQLOP_SPECTROGRAMTIMESERIE_H diff --git a/include/Variable/Variable2.h b/include/Variable/Variable2.h index 08a4bea..c12527d 100644 --- a/include/Variable/Variable2.h +++ b/include/Variable/Variable2.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -18,9 +19,10 @@ #include #include -using AnyTimeSerie = variant_w_base< - TimeSeries::ITimeSerie, - std::variant>; +using AnyTimeSerie = + variant_w_base>; class SCIQLOP_CORE_EXPORT Variable2 : public QObject { @@ -52,6 +54,9 @@ public: void setData(const std::vector& dataSeries, const DateTimeRange& range, bool notify = true); + void setData(const std::vector& dataSeries, + const DateTimeRange& range, bool notify = true); + static QByteArray mimeData(const std::vector>& variables) { diff --git a/src/Variable/Variable2.cpp b/src/Variable/Variable2.cpp index 4718043..973c0d7 100644 --- a/src/Variable/Variable2.cpp +++ b/src/Variable/Variable2.cpp @@ -42,7 +42,7 @@ struct Variable2::VariablePrivate PROPERTY_(m_Name, name, setName, QString) PROPERTY_(m_Range, range, setRange, DateTimeRange) PROPERTY_(m_Metadata, metadata, setMetadata, QVariantHash) - AnyTimeSerie& dataSeries() { return *m_TimeSerie.get(); } + AnyTimeSerie* dataSeries() { return m_TimeSerie.get(); } void setDataSeries(std::unique_ptr&& timeSerie) { QWriteLocker lock{&m_Lock}; @@ -73,45 +73,87 @@ DateTimeRange Variable2::range() const noexcept { return impl->range(); } std::size_t Variable2::nbPoints() const noexcept { return impl->nbPoints(); } -AnyTimeSerie* Variable2::data() const noexcept {} +AnyTimeSerie* Variable2::data() const noexcept { return impl->dataSeries(); } DataSeriesType Variable2::type() const noexcept { return impl->type(); } QVariantHash Variable2::metadata() const noexcept {} +// template +// std::unique_ptr _merge(std::vector source) +//{ +// std::unique_ptr dest = std::make_unique(); +// std::sort(std::begin(source), std::end(source), +// [](AnyTimeSerie* a, AnyTimeSerie* b) { +// return a->get().front().t() < b->get().front().t(); +// }); +// *dest = std::move(*source.front()); +// std::for_each( +// std::begin(source) + 1, std::end(source), [&dest](AnyTimeSerie* serie) { +// std::copy(std::begin(serie->get()), std::end(serie->get()), +// std::back_inserter(dest->get())); +// }); +// return dest; +//} + template -void _merge(const std::vector& source, AnyTimeSerie* dest) +std::unique_ptr +_merge(std::vector source) { - // std::for_each( - // std::cbegin(source) + 1, std::cend(source), [dest](AnyTimeSerie* - // serie) { - // std::copy(std::begin(serie->get()), std::end(serie->get()), - // std::back_inserter(dest->get())); - // }); + std::unique_ptr dest = std::make_unique(); + std::sort(std::begin(source), std::end(source), + [](TimeSeries::ITimeSerie* a, TimeSeries::ITimeSerie* b) { + if(a->size() && b->size()) return a->t(0) < b->t(0); + return false; + }); + *dest = std::move(*static_cast(source.front())); + std::for_each(std::begin(source) + 1, std::end(source), + [&dest](TimeSeries::ITimeSerie* serie) { + std::copy(std::begin(*static_cast(serie)), + std::end(*static_cast(serie)), + std::back_inserter(dest->get())); + }); + return dest; } +// std::unique_ptr +// merge(const std::vector& dataSeries) +//{ +// switch(DataSeriesType(dataSeries.front()->index())) +// { +// case DataSeriesType::NONE: break; +// case DataSeriesType::SCALAR: return _merge(dataSeries); +// case DataSeriesType::VECTOR: return _merge(dataSeries); +// case DataSeriesType::SPECTROGRAM: +// return _merge(dataSeries); +// } +// return std::unique_ptr{}; +//} + std::unique_ptr -merge(const std::vector& dataSeries) +merge(const std::vector& dataSeries) { - std::unique_ptr ts; - *ts = *dataSeries.front(); - switch(DataSeriesType(ts->index())) - { - case DataSeriesType::NONE: break; - case DataSeriesType::SCALAR: - _merge(dataSeries, ts.get()); - break; - case DataSeriesType::VECTOR: - _merge(dataSeries, ts.get()); - break; - case DataSeriesType::SPECTROGRAM: - // merge(dataSeries, ts.get()); - break; - } - return ts; + if(dynamic_cast(dataSeries.front())) + return _merge(dataSeries); + if(dynamic_cast(dataSeries.front())) + return _merge(dataSeries); + if(dynamic_cast(dataSeries.front())) + return _merge(dataSeries); + return std::unique_ptr{}; } -void Variable2::setData(const std::vector& dataSeries, +// void Variable2::setData(const std::vector& dataSeries, +// const DateTimeRange& range, bool notify) +//{ +// if(dataSeries.size()) +// { +// impl->setDataSeries(merge(dataSeries)); +// impl->setRange(range); +// if(notify) emit this->updated(this->ID()); +// } +//} + +void Variable2::setData(const std::vector& dataSeries, const DateTimeRange& range, bool notify) { if(dataSeries.size()) diff --git a/src/pybind11_wrappers/CoreWrappers.cpp b/src/pybind11_wrappers/CoreWrappers.cpp index 2acff7c..2c9e256 100644 --- a/src/pybind11_wrappers/CoreWrappers.cpp +++ b/src/pybind11_wrappers/CoreWrappers.cpp @@ -157,6 +157,8 @@ PYBIND11_MODULE(pysciqlopcore, m) .def_property_readonly( "size", [](const TimeSeries::ITimeSerie& ts) { return ts.size(); }) .def_property_readonly( + "shape", [](const TimeSeries::ITimeSerie& ts) { return ts.shape(); }) + .def_property_readonly( "t", [](TimeSeries::ITimeSerie& ts) -> decltype(ts.axis(0))& { return ts.axis(0); @@ -166,6 +168,19 @@ PYBIND11_MODULE(pysciqlopcore, m) py::class_(m, "ScalarTimeSerie") .def(py::init<>()) .def(py::init()) + .def(py::init([](py::array_t t, py::array_t values) { + assert(t.size() == values.size()); + ScalarTimeSerie::axis_t _t(t.size()); + ScalarTimeSerie::axis_t _values(t.size()); + auto t_vew = t.unchecked<1>(); + auto values_vew = values.unchecked<1>(); + for(int i = 0; i < t.size(); i++) + { + _t[i] = t_vew[i]; + _values[i] = values_vew[i]; + } + return ScalarTimeSerie(_t, _values); + })) .def("__getitem__", [](ScalarTimeSerie& ts, std::size_t key) { return ts[key]; }) .def("__setitem__", [](ScalarTimeSerie& ts, std::size_t key, @@ -191,14 +206,26 @@ PYBIND11_MODULE(pysciqlopcore, m) *(ts.begin() + key) = value; }); + py::class_( + m, "SpectrogramTimeSerie") + .def(py::init<>()) + .def(py::init>()); + py::class_>(m, "Variable2") .def(py::init()) .def_property("name", &Variable2::name, &Variable2::setName) .def_property_readonly("range", &Variable2::range) .def_property_readonly("nbPoints", &Variable2::nbPoints) .def_property_readonly( - "dataSeries", [](const Variable2& var) { return var.data()->base(); }) - .def("set_data", &Variable2::setData) + "data", + [](const Variable2& var) -> TimeSeries::ITimeSerie* { + auto data = var.data(); + if(data) return data->base(); + return nullptr; + }) + .def("set_data", + [](Variable2& var, std::vector ts_list, + const DateTimeRange& range) { var.setData(ts_list, range); }) .def("__len__", &Variable2::nbPoints) .def("__repr__", __repr__); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e5b11d7..1f221e6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -49,3 +49,12 @@ declare_test(TestVariableController2Async TestVariableController2Async Variable/ declare_test(TestVariableController2WithSync TestVariableController2WithSync Variable/TestVariableController2WithSync.cpp "sciqlopcore;TestUtils;Qt5::Test") declare_test(TestCatalogueController TestCatalogueController CatalogueController/TestCatalogueController.cpp "sciqlopcore;TestUtils;Qt5::Test") + + +find_package(PythonInterp 3 REQUIRED) + +add_test(NAME TestVariables + COMMAND ${PYTHON_EXECUTABLE} + ${CMAKE_CURRENT_LIST_DIR}/TestVariables.py + TestVariables) +set_tests_properties(TestVariables PROPERTIES ENVIRONMENT PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}/../) diff --git a/tests/Data/TestDateTimeRange.cpp b/tests/Data/TestDateTimeRange.cpp index 6cc1839..052987f 100644 --- a/tests/Data/TestDateTimeRange.cpp +++ b/tests/Data/TestDateTimeRange.cpp @@ -1,223 +1,248 @@ +#include +#include +#include #include -#include #include +#include #include - -#include -#include -#include - Q_DECLARE_METATYPE(Seconds); Q_DECLARE_METATYPE(DateTimeRangeTransformation); DateTimeRange computeZoom(QDateTime start, QDateTime stop, double zoom) { - double start_epoch = start.toMSecsSinceEpoch(); - double stop_epoch = stop.toMSecsSinceEpoch(); - auto delta = stop_epoch - start_epoch; - auto dt_ms = (zoom-1.)*(double(delta))/2.; - return DateTimeRange{(start_epoch - dt_ms)/1000., (stop_epoch + dt_ms)/1000.}; + double start_epoch = start.toMSecsSinceEpoch(); + double stop_epoch = stop.toMSecsSinceEpoch(); + auto delta = stop_epoch - start_epoch; + auto dt_ms = (zoom - 1.) * (double(delta)) / 2.; + return DateTimeRange{(start_epoch - dt_ms) / 1000., + (stop_epoch + dt_ms) / 1000.}; } -class TestDateTimeRange: public QObject { - Q_OBJECT +class TestDateTimeRange : public QObject +{ + Q_OBJECT private slots: - void testRangeDelta_data() - { - QTest::addColumn("tstart"); - QTest::addColumn("tend"); - QTest::addColumn("expected"); - auto now = QDateTime::currentDateTime(); - auto yesterday = QDateTime::currentDateTime().addDays(-1); - QTest::newRow("No delta") << now << now << 0.; - QTest::newRow("One day delta") << yesterday << now << 60.*60.*24.; - QTest::newRow("Minus one day delta") << now << yesterday << -60.*60.*24.; - } - - void testRangeDelta() + void testRangeDelta_data() + { + QTest::addColumn("tstart"); + QTest::addColumn("tend"); + QTest::addColumn("expected"); + auto now = QDateTime::currentDateTime(); + auto yesterday = QDateTime::currentDateTime().addDays(-1); + QTest::newRow("No delta") << now << now << 0.; + QTest::newRow("One day delta") << yesterday << now << 60. * 60. * 24.; + QTest::newRow("Minus one day delta") + << now << yesterday << -60. * 60. * 24.; + } + + void testRangeDelta() + { + QFETCH(QDateTime, tstart); + QFETCH(QDateTime, tend); + QFETCH(double, expected); + auto range = DateTimeRange::fromDateTime(tstart, tend); + double delta = range.delta(); + // Since it is built from QDateTime don't expect better resolution + QVERIFY((delta - expected) <= 0.002); + } + + void testRangeShift_data() + { + QTest::addColumn("initial"); + QTest::addColumn>("shift"); + QTest::addColumn("expected"); + auto now = QDateTime::currentDateTime(); + auto yestd = QDateTime::currentDateTime().addDays(-1); + auto range = DateTimeRange::fromDateTime(yestd, now); + QTest::newRow("No shift") << range << Seconds{0.} << range; + + QTest::newRow("One milisecond left") + << range << Seconds{-.001} + << DateTimeRange::fromDateTime(yestd.addMSecs(-1.), now.addMSecs(-1.)); + QTest::newRow("One milisecond right") + << range << Seconds{.001} + << DateTimeRange::fromDateTime(yestd.addMSecs(1.), now.addMSecs(1.)); + QTest::newRow("One second left") + << range << Seconds{-1.} + << DateTimeRange::fromDateTime(yestd.addSecs(-1.), now.addSecs(-1.)); + QTest::newRow("One second right") + << range << Seconds{1.} + << DateTimeRange::fromDateTime(yestd.addSecs(1.), now.addSecs(1.)); + QTest::newRow("One year left") + << range << Seconds{-365. * 24. * 60. * 60.} + << DateTimeRange::fromDateTime(yestd.addSecs(-365 * 24 * 60 * 60), + now.addSecs(-365 * 24 * 60 * 60)); + QTest::newRow("One year right") + << range << Seconds{365. * 24. * 60. * 60.} + << DateTimeRange::fromDateTime(yestd.addSecs(365 * 24 * 60 * 60), + now.addSecs(365 * 24 * 60 * 60)); + } + + void testRangeShift() + { + QFETCH(DateTimeRange, initial); + QFETCH(Seconds, shift); + QFETCH(DateTimeRange, expected); + QCOMPARE(initial + shift, expected); + } + + void testRangeZoom_data() + { + QTest::addColumn("initial"); + QTest::addColumn("zoom"); + QTest::addColumn("expected"); + auto now = QDateTime::currentDateTime(); + auto yestd = QDateTime::currentDateTime().addDays(-1); + auto range = DateTimeRange::fromDateTime(yestd, now); + QTest::newRow("No zoom") << range << 1. << range; + + QTest::newRow("Zoom IN 0.001") + << range << 1.001 << computeZoom(yestd, now, 1.001); + QTest::newRow("Zoom OUT 0.001") + << range << 0.999 << computeZoom(yestd, now, 0.999); + } + void testRangeZoom() + { + QFETCH(DateTimeRange, initial); + QFETCH(double, zoom); + QFETCH(DateTimeRange, expected); + QCOMPARE(initial * zoom, expected); + } + + void testRangeContains_data() + { + QTest::addColumn("range"); + QTest::addColumn("range2"); + QTest::addColumn("contains"); + auto now = QDateTime::currentDateTime(); + auto yestd = QDateTime::currentDateTime().addDays(-1); + auto range = DateTimeRange::fromDateTime(yestd, now); + QTest::newRow("Same range") << range << range << true; + QTest::newRow("Smaller range") << range << range * 0.8 << true; + QTest::newRow("Bigger range") << range << range * 1.2 << false; + QTest::newRow("Shifted range with overlap") + << range << range + Seconds{1000.} << false; + QTest::newRow("Shifted range without overlap") + << range << range + Seconds{24. * 60. * 60. * 10} << false; + } + + void testRangeContains() + { + QFETCH(DateTimeRange, range); + QFETCH(DateTimeRange, range2); + QFETCH(bool, contains); + QCOMPARE(range.contains(range2), contains); + } + + void testRangeIntersect_data() + { + QTest::addColumn("range"); + QTest::addColumn("range2"); + QTest::addColumn("contains"); + auto now = QDateTime::currentDateTime(); + auto yestd = QDateTime::currentDateTime().addDays(-1); + auto tomorrow = QDateTime::currentDateTime().addDays(1); + auto range = DateTimeRange::fromDateTime(yestd, now); + auto range2 = DateTimeRange::fromDateTime(now, tomorrow); + QTest::newRow("Same range") << range << range << true; + QTest::newRow("Smaller range") << range << range * 0.8 << true; + QTest::newRow("Bigger range") << range << range * 1.2 << true; + QTest::newRow("Shifted range with overlap") + << range << range + Seconds{1000.} << true; + QTest::newRow("Shifted range with overlaping boundary") + << range << range2 << true; + QTest::newRow("Shifted range .1 seonds outside") + << range << range2 + Seconds{.1} << false; + QTest::newRow("Shifted range without overlap") + << range << range + Seconds{24. * 60. * 60. * 10} << false; + } + + void testRangeIntersect() + { + QFETCH(DateTimeRange, range); + QFETCH(DateTimeRange, range2); + QFETCH(bool, contains); + QCOMPARE(range.intersect(range2), contains); + } + + void testRangeTransformations_data() + { + QTest::addColumn("range1"); + QTest::addColumn("range2"); + QTest::addColumn("transformation"); + auto now = QDateTime::currentDateTime(); + auto yestd = QDateTime::currentDateTime().addDays(-1); + auto range = DateTimeRange::fromDateTime(yestd, now); + + QTest::newRow("Same range") + << range << range + << DateTimeRangeTransformation{1., Seconds{0.}}; + + QTest::newRow("Transformatio 1.1x + 10s") + << range << range * 1.1 + Seconds{10.} + << DateTimeRangeTransformation{1.1, Seconds{10.}}; + + QTest::newRow("Transformatio 1.x + 10s") + << range << range * 1. + Seconds{10.} + << DateTimeRangeTransformation{1., Seconds{10.}}; + + QTest::newRow("Transformatio 1.1x + 0s") + << range << range * 1.1 + Seconds{0.} + << DateTimeRangeTransformation{1.1, Seconds{0.}}; + + QTest::newRow("Transformatio 0.9x - 10s") + << range << range * 0.9 + Seconds{-10.} + << DateTimeRangeTransformation{0.9, Seconds{-10.}}; + } + void testRangeTransformations() + { + QFETCH(DateTimeRange, range1); + QFETCH(DateTimeRange, range2); + QFETCH(DateTimeRangeTransformation, transformation); + auto computed_tr = + DateTimeRangeHelper::computeTransformation(range1, range2).value(); + QCOMPARE(computed_tr, transformation); + QCOMPARE(range1.transform(transformation), range2); + QCOMPARE(range1.transform(computed_tr), range2); + } + + void testRangeDiff_data() + { + QTest::addColumn("source"); + QTest::addColumn("destination"); + QTest::addColumn("count"); + auto now = QDateTime::currentDateTime(); + auto yestd = QDateTime::currentDateTime().addDays(-1); + auto range = DateTimeRange::fromDateTime(yestd, now); + QTest::newRow("Same range") << range << range << 0; + QTest::newRow("No missing data (zoom in)") << range << range * 0.9 << 0; + QTest::newRow("Missing data on both sides (zoom out)") + << range << range * 2. << 2; + QTest::newRow("Missing data on left side (pan left)") + << range << range - Seconds{1000.} << 1; + QTest::newRow("Missing data on right side (pan right)") + << range << range + Seconds{1000.} << 1; + QTest::newRow("Missing data on right side no intersect (big pan right)") + << range << range + Seconds{24. * 60. * 60. * 2} << 1; + QTest::newRow("Missing data on left side no intersect (big pan left)") + << range << range - Seconds{24. * 60. * 60. * 2} << 1; + } + + void testRangeDiff() + { + QFETCH(DateTimeRange, source); + QFETCH(DateTimeRange, destination); + QFETCH(int, count); + auto diff = destination - source; + QCOMPARE(diff.size(), count); + for(const auto& range : diff) { - QFETCH(QDateTime,tstart); - QFETCH(QDateTime,tend); - QFETCH(double,expected); - auto range = DateTimeRange::fromDateTime(tstart, tend); - double delta = range.delta(); - // Since it is built from QDateTime don't expect better resolution - QVERIFY((delta-expected) <= 0.002); - } - - void testRangeShift_data() - { - QTest::addColumn("initial"); - QTest::addColumn>("shift"); - QTest::addColumn("expected"); - auto now = QDateTime::currentDateTime(); - auto yestd = QDateTime::currentDateTime().addDays(-1); - auto range = DateTimeRange::fromDateTime(yestd, now); - QTest::newRow("No shift") << range << Seconds{0.} << range; - - QTest::newRow("One milisecond left") << range << Seconds{-.001} << - DateTimeRange::fromDateTime(yestd.addMSecs(-1.), now.addMSecs(-1.)); - QTest::newRow("One milisecond right") << range << Seconds{.001} << - DateTimeRange::fromDateTime(yestd.addMSecs(1.), now.addMSecs(1.)); - QTest::newRow("One second left") << range << Seconds{-1.} << - DateTimeRange::fromDateTime(yestd.addSecs(-1.), now.addSecs(-1.)); - QTest::newRow("One second right") << range << Seconds{1.} << - DateTimeRange::fromDateTime(yestd.addSecs(1.), now.addSecs(1.)); - QTest::newRow("One year left") << range << Seconds{-365.*24.*60.*60.} << - DateTimeRange::fromDateTime(yestd.addYears(-1.), now.addYears(-1.)); - QTest::newRow("One year right") << range << Seconds{365.*24.*60.*60.} << - DateTimeRange::fromDateTime(yestd.addYears(1.), now.addYears(1.)); - } - void testRangeShift() - { - QFETCH(DateTimeRange,initial); - QFETCH(Seconds,shift); - QFETCH(DateTimeRange,expected); - QCOMPARE(initial+shift, expected); - } - - void testRangeZoom_data() - { - QTest::addColumn("initial"); - QTest::addColumn("zoom"); - QTest::addColumn("expected"); - auto now = QDateTime::currentDateTime(); - auto yestd = QDateTime::currentDateTime().addDays(-1); - auto range = DateTimeRange::fromDateTime(yestd, now); - QTest::newRow("No zoom") << range << 1. << range; - - QTest::newRow("Zoom IN 0.001") << range << 1.001 << - computeZoom(yestd, now, 1.001); - QTest::newRow("Zoom OUT 0.001") << range << 0.999 << - computeZoom(yestd, now, 0.999); - } - void testRangeZoom() - { - QFETCH(DateTimeRange,initial); - QFETCH(double,zoom); - QFETCH(DateTimeRange,expected); - QCOMPARE(initial*zoom, expected); - } - - void testRangeContains_data() - { - QTest::addColumn("range"); - QTest::addColumn("range2"); - QTest::addColumn("contains"); - auto now = QDateTime::currentDateTime(); - auto yestd = QDateTime::currentDateTime().addDays(-1); - auto range = DateTimeRange::fromDateTime(yestd, now); - QTest::newRow("Same range") << range << range << true; - QTest::newRow("Smaller range") << range << range * 0.8 << true; - QTest::newRow("Bigger range") << range << range * 1.2 << false; - QTest::newRow("Shifted range with overlap") << range << range + Seconds{1000.} << false; - QTest::newRow("Shifted range without overlap") << range << range + Seconds{24.*60.*60.*10} << false; - } - - void testRangeContains() - { - QFETCH(DateTimeRange,range); - QFETCH(DateTimeRange,range2); - QFETCH(bool,contains); - QCOMPARE(range.contains(range2), contains); - } - - void testRangeIntersect_data() - { - QTest::addColumn("range"); - QTest::addColumn("range2"); - QTest::addColumn("contains"); - auto now = QDateTime::currentDateTime(); - auto yestd = QDateTime::currentDateTime().addDays(-1); - auto tomorrow = QDateTime::currentDateTime().addDays(1); - auto range = DateTimeRange::fromDateTime(yestd, now); - auto range2 = DateTimeRange::fromDateTime(now, tomorrow); - QTest::newRow("Same range") << range << range << true; - QTest::newRow("Smaller range") << range << range * 0.8 << true; - QTest::newRow("Bigger range") << range << range * 1.2 << true; - QTest::newRow("Shifted range with overlap") << range << range + Seconds{1000.} << true; - QTest::newRow("Shifted range with overlaping boundary") << range << range2 << true; - QTest::newRow("Shifted range .1 seonds outside") << range << range2 + Seconds{.1} << false; - QTest::newRow("Shifted range without overlap") << range << range + Seconds{24.*60.*60.*10} << false; - } - - void testRangeIntersect() - { - QFETCH(DateTimeRange,range); - QFETCH(DateTimeRange,range2); - QFETCH(bool,contains); - QCOMPARE(range.intersect(range2), contains); - } - - void testRangeTransformations_data() - { - QTest::addColumn("range1"); - QTest::addColumn("range2"); - QTest::addColumn("transformation"); - auto now = QDateTime::currentDateTime(); - auto yestd = QDateTime::currentDateTime().addDays(-1); - auto range = DateTimeRange::fromDateTime(yestd, now); - - QTest::newRow("Same range") << range << range << DateTimeRangeTransformation{1.,Seconds{0.}}; - - QTest::newRow("Transformatio 1.1x + 10s") << range << range*1.1 + Seconds{10.} - << DateTimeRangeTransformation{1.1, Seconds{10.}}; - - QTest::newRow("Transformatio 1.x + 10s") << range << range*1. + Seconds{10.} - << DateTimeRangeTransformation{1., Seconds{10.}}; - - QTest::newRow("Transformatio 1.1x + 0s") << range << range*1.1 + Seconds{0.} - << DateTimeRangeTransformation{1.1,Seconds{0.}}; - - QTest::newRow("Transformatio 0.9x - 10s") << range << range*0.9 + Seconds{-10.} - << DateTimeRangeTransformation{0.9, Seconds{-10.}}; - - } - void testRangeTransformations() - { - QFETCH(DateTimeRange,range1); - QFETCH(DateTimeRange,range2); - QFETCH(DateTimeRangeTransformation, transformation); - auto computed_tr = DateTimeRangeHelper::computeTransformation(range1,range2).value(); - QCOMPARE(computed_tr, transformation); - QCOMPARE(range1.transform(transformation),range2); - QCOMPARE(range1.transform(computed_tr),range2); - } - - void testRangeDiff_data() - { - QTest::addColumn("source"); - QTest::addColumn("destination"); - QTest::addColumn("count"); - auto now = QDateTime::currentDateTime(); - auto yestd = QDateTime::currentDateTime().addDays(-1); - auto range = DateTimeRange::fromDateTime(yestd, now); - QTest::newRow("Same range") << range << range << 0; - QTest::newRow("No missing data (zoom in)") << range << range*0.9 << 0; - QTest::newRow("Missing data on both sides (zoom out)") << range << range*2. << 2; - QTest::newRow("Missing data on left side (pan left)") << range << range-Seconds{1000.} << 1; - QTest::newRow("Missing data on right side (pan right)") << range << range+Seconds{1000.} << 1; - QTest::newRow("Missing data on right side no intersect (big pan right)") << range << range+Seconds{24.*60.*60.*2} << 1; - QTest::newRow("Missing data on left side no intersect (big pan left)") << range << range-Seconds{24.*60.*60.*2} << 1; - - } - - void testRangeDiff() - { - QFETCH(DateTimeRange,source); - QFETCH(DateTimeRange,destination); - QFETCH(int, count); - auto diff = destination-source; - QCOMPARE(diff.size(),count); - for(const auto& range:diff) - { - QVERIFY(range.delta().value>0.); - } + QVERIFY(range.delta().value > 0.); } + } }; QTEST_MAIN(TestDateTimeRange) - #include "TestDateTimeRange.moc" diff --git a/tests/TestVariables.py b/tests/TestVariables.py new file mode 100755 index 0000000..997b6d6 --- /dev/null +++ b/tests/TestVariables.py @@ -0,0 +1,41 @@ +import sys +import os + +import sciqlopqt +import pysciqlopcore + +import numpy as np +import datetime +import time +import unittest +import ddt + +def listify(obj): + if hasattr(obj, "__getitem__"): + return obj + return [obj] + +@ddt.ddt +class TimeSeriesCtors(unittest.TestCase): + @ddt.data( + (pysciqlopcore.ScalarTimeSerie,10), + (pysciqlopcore.VectorTimeSerie,10), + (pysciqlopcore.SpectrogramTimeSerie,[10,10]), + ) + def test_construct(self, case): + ts = case[0](case[1]) + self.assertEqual(ts.shape,listify(case[1])) + +class TimeSeriesData(unittest.TestCase): + def test_set_ScalarTimeSerie_values(self): + ts = pysciqlopcore.ScalarTimeSerie(10) + ts.t[0]=111. + self.assertEqual(ts.t[0],111.) + ts[0]=123. + self.assertEqual(ts[0],123.) + + def test_build_ScalarTimeSerie_from_np_arrays(self): + ts = pysciqlopcore.ScalarTimeSerie(np.arange(10),np.zeros(10)) + +if __name__ == '__main__': + unittest.main()