Repository: mie-lab/trackintel
Pull request #531 (2023-09-19 11:17:47)

TST: remove debug print statements
Small PR that removes `print` statements that weren't removed after debugging.


Patch:
diff --git a/docs/modules/model.rst b/docs/modules/model.rst
index a726b9f..ed0f32a 100644
--- a/docs/modules/model.rst
+++ b/docs/modules/model.rst
@@ -80,16 +80,16 @@ Locations
 .. autoclass:: trackintel.model.locations.Locations
 	:members:
 
-TripsAccessor
+Trips
 -------------
 
-.. autoclass:: trackintel.model.trips.TripsAccessor
+.. autoclass:: trackintel.model.trips.Trips
 	:members:
 
-ToursAccessor
+Tours
 -------------
 
-.. autoclass:: trackintel.model.tours.ToursAccessor
+.. autoclass:: trackintel.model.tours.Tours
 	:members:
 
 
diff --git a/trackintel/__init__.py b/trackintel/__init__.py
index ff35ec6..59a32bc 100644
--- a/trackintel/__init__.py
+++ b/trackintel/__init__.py
@@ -17,6 +17,7 @@ from trackintel.model.staypoints import Staypoints
 from trackintel.model.trips import Trips
 from trackintel.model.trips import TripsDataFrame
 from trackintel.model.trips import TripsGeoDataFrame
+from trackintel.model.tours import Tours
 
 from trackintel.visualization import plot, plot_modal_split
 
diff --git a/trackintel/io/file.py b/trackintel/io/file.py
index f20bd35..dc4d0f9 100644
--- a/trackintel/io/file.py
+++ b/trackintel/io/file.py
@@ -573,4 +573,4 @@ def write_tours_csv(tours, filename, *args, **kwargs):
     --------
     >>> tours.as_tours.to_csv("export_tours.csv")
     """
-    tours.to_csv(filename, index=True, *args, **kwargs)
+    pd.DataFrame.to_csv(tours, filename, index=True, *args, **kwargs)
diff --git a/trackintel/io/postgis.py b/trackintel/io/postgis.py
index ff338b2..2a59715 100644
--- a/trackintel/io/postgis.py
+++ b/trackintel/io/postgis.py
@@ -563,9 +563,6 @@ def read_trips_postgis(
 def write_trips_postgis(
     trips, name, con, schema=None, if_exists="fail", index=True, index_label=None, chunksize=None, dtype=None
 ):
-    if "trips" in trips.columns:
-        dtype = dtype or {}
-        dtype.setdefault("trips", JSON)
     if isinstance(trips, gpd.GeoDataFrame):
         gpd.GeoDataFrame.to_postgis(
             trips,
@@ -691,10 +688,12 @@ def read_tours_postgis(
 def write_tours_postgis(
     tours, name, con, schema=None, if_exists="fail", index=True, index_label=None, chunksize=None, dtype=None
 ):
-    write_trips_postgis(
-        tours,
-        name=name,
-        con=con,
+    if "trips" in tours.columns:
+        dtype = dtype or {}
+        dtype.setdefault("trips", JSON)
+    tours.to_sql(
+        name,
+        con,
         schema=schema,
         if_exists=if_exists,
         index=index,
diff --git a/trackintel/model/__init__.py b/trackintel/model/__init__.py
index 65a4f39..5f8ba98 100644
--- a/trackintel/model/__init__.py
+++ b/trackintel/model/__init__.py
@@ -3,4 +3,4 @@ from .locations import Locations
 from .triplegs import Triplegs
 from .staypoints import Staypoints
 from .trips import Trips
-from .tours import ToursAccessor
+from .tours import Tours
diff --git a/trackintel/model/tours.py b/trackintel/model/tours.py
index 2e89834..17553d3 100644
--- a/trackintel/model/tours.py
+++ b/trackintel/model/tours.py
@@ -1,10 +1,15 @@
 import pandas as pd
+
 import trackintel as ti
-from trackintel.model.util import _register_trackintel_accessor
+from trackintel.io.file import write_tours_csv
+from trackintel.io.postgis import write_tours_postgis
+from trackintel.model.util import _register_trackintel_accessor, TrackintelBase, TrackintelDataFrame, _copy_docstring
+
+_required_columns = ["user_id", "started_at", "finished_at"]
 
 
 @_register_trackintel_accessor("as_tours")
-class ToursAccessor(object):
+class Tours(TrackintelBase, TrackintelDataFrame):
     """A pandas accessor to treat DataFrames as collections of `Tours`.
 
     Requires at least the following columns:
@@ -27,18 +32,21 @@ class ToursAccessor(object):
     >>> df.as_tours.to_csv("filename.csv")
     """
 
-    required_columns = ["user_id", "started_at", "finished_at"]
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self._validate(self)
 
-    def __init__(self, pandas_obj):
-        self._validate(pandas_obj)
-        self._obj = pandas_obj
+    # createte circular reference directly -> avoid second call of init via accessor
+    @property
+    def as_tours(self):
+        return self
 
     @staticmethod
     def _validate(obj):
-        if any([c not in obj.columns for c in ToursAccessor.required_columns]):
+        if any([c not in obj.columns for c in _required_columns]):
             raise AttributeError(
                 "To process a DataFrame as a collection of tours, it must have the properties"
-                f" {ToursAccessor.required_columns}, but it has {', '.join(obj.columns)}."
+                f" {_required_columns}, but it has {', '.join(obj.columns)}."
             )
 
         # check timestamp dtypes
@@ -49,14 +57,27 @@ class ToursAccessor(object):
             obj["finished_at"].dtype, pd.DatetimeTZDtype
         ), f"dtype of finished_at is {obj['finished_at'].dtype} but has to be datetime64 and timezone aware"
 
+    @staticmethod
+    def _check(obj):
+        """Check does the same as _validate but returns bool instead of potentially raising an error."""
+        if any([c not in obj.columns for c in _required_columns]):
+            return False
+        if not isinstance(obj["started_at"].dtype, pd.DatetimeTZDtype):
+            return False
+        if not isinstance(obj["finished_at"].dtype, pd.DatetimeTZDtype):
+            return False
+        return True
+
+    @_copy_docstring(write_tours_csv)
     def to_csv(self, filename, *args, **kwargs):
         """
         Store this collection of tours as a CSV file.
 
         See :func:`trackintel.io.file.write_tours_csv`.
         """
-        ti.io.file.write_tours_csv(self._obj, filename, *args, **kwargs)
+        ti.io.file.write_tours_csv(self, filename, *args, **kwargs)
 
+    @_copy_docstring(write_tours_postgis)
     def to_postgis(
         self, name, con, schema=None, if_exists="fail", index=True, index_label=None, chunksize=None, dtype=None
     ):
@@ -65,4 +86,4 @@ class ToursAccessor(object):
 
         See :func:`trackintel.io.postgis.write_tours_postgis`.
         """
-        ti.io.postgis.write_tours_postgis(self._obj, name, con, schema, if_exists, index, index_label, chunksize, dtype)
+        ti.io.postgis.write_tours_postgis(self, name, con, schema, if_exists, index, index_label, chunksize, dtype)


Test patch:
diff --git a/tests/io/test_postgis.py b/tests/io/test_postgis.py
index abb1598..aeb64f7 100644
--- a/tests/io/test_postgis.py
+++ b/tests/io/test_postgis.py
@@ -276,7 +276,6 @@ class TestPositionfixes:
             with pytest.warns(UserWarning):
                 pfs_db = ti.io.read_positionfixes_postgis(sql, conn, geom_col)
             pfs_db = pfs_db.set_index("id")
-            print(pfs_db)
             assert_geodataframe_equal(pfs, pfs_db)
         finally:
             del_table(conn, table)
diff --git a/tests/model/test_tours.py b/tests/model/test_tours.py
new file mode 100644
index 0000000..281a244
--- /dev/null
+++ b/tests/model/test_tours.py
@@ -0,0 +1,44 @@
+import pytest
+import os
+
+import trackintel as ti
+from trackintel import Tours
+
+
+@pytest.fixture
+def test_tours():
+    """Read test data from file"""
+    tours_file = os.path.join("tests", "data", "tours.csv")
+    tours = ti.read_tours_csv(tours_file, sep=";", index_col="id")
+    return tours
+
+
+class TestTours:
+    """Tests for the Tours class."""
+
+    def test_missing_column(self, test_tours):
+        """Test if as_tours accessor check required columns"""
+        with pytest.raises(AttributeError, match="To process a DataFrame as a collection of tours"):
+            test_tours.drop(columns="user_id").as_tours
+
+    def test_accessor_recursive(self, test_tours):
+        tours = test_tours.as_tours
+        assert type(tours) is Tours
+        assert id(tours) == id(tours.as_tours)
+
+    def test_check_succeeding(self, test_tours):
+        """Test if check returns True on valid tours"""
+        assert Tours._check(test_tours)
+
+    def test_check_missing_columns(self, test_tours):
+        """Test if check returns False if column is missing"""
+        assert not Tours._check(test_tours.drop(columns="user_id"))
+
+    def test_check_no_tz(self, test_tours):
+        """Test if check returns False if datetime columns have no tz"""
+        tmp = test_tours["started_at"]
+        test_tours["started_at"] = test_tours["started_at"].dt.tz_localize(None)
+        assert not Tours._check(test_tours)
+        test_tours["started_at"] = tmp
+        test_tours["finished_at"] = test_tours["finished_at"].dt.tz_localize(None)
+        assert not Tours._check(test_tours)
diff --git a/tests/model/test_util.py b/tests/model/test_util.py
index 02f9d69..1c84dcd 100644
--- a/tests/model/test_util.py
+++ b/tests/model/test_util.py
@@ -232,8 +232,6 @@ class Test_copy_docstring:
             pass
 
         old_docs = """Old docstring."""
-        print(type(old_docs))
-
         for wa in WRAPPER_ASSIGNMENTS:
             attr_foo = getattr(read_trips_postgis, wa)
             attr_bar = getattr(bar, wa)
diff --git a/tests/preprocessing/test_triplegs.py b/tests/preprocessing/test_triplegs.py
index ec760ab..648f75c 100644
--- a/tests/preprocessing/test_triplegs.py
+++ b/tests/preprocessing/test_triplegs.py
@@ -672,7 +672,6 @@ def _generate_trips_user(df, gap_threshold):
             )
         )
 
-    # print(trip_ls)
     trips = pd.DataFrame(trip_ls)
     return trips