Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 28 additions & 21 deletions python/grass/temporal/list_stds.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,31 +101,38 @@ def get_dataset_list(

result = {}

for mapset in dbif.tgis_mapsets:
if temporal_type == "absolute":
table = type + "_view_abs_time"
else:
table = type + "_view_rel_time"

if columns and columns.find("all") == -1:
sql = "SELECT " + str(columns) + " FROM " + table
else:
sql = "SELECT * FROM " + table
for dtype in [t.strip() for t in type.split(",") if t.strip()]:
Comment thread
saket0187 marked this conversation as resolved.
Outdated
for mapset in dbif.tgis_mapsets:
if temporal_type == "absolute":
table = dtype + "_view_abs_time"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the intention is to be able to list all STDS in one go, should we not support also multiple / list input for temporal_type?
Sorry for bringing that up somewhat late...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree and t.list already allows that, just not the function.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That probably means the temporal_type should always go into the JSON and CSV output too, no?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The temporal_type is already in CSV and JSON with columns=all, it could be added explicitly as one of the options, but I am not sure it's worth it.

else:
table = dtype + "_view_rel_time"

if columns and columns.find("all") == -1:
cols = [
f"'{dtype}' AS type" if c.strip() == "type" else c.strip()
Comment thread
saket0187 marked this conversation as resolved.
Outdated
for c in str(columns).split(",")
]
sql = "SELECT " + ", ".join(cols) + " FROM " + table
else:
sql = f"SELECT *, '{dtype}' AS type FROM {table}"

if where:
sql += " WHERE " + where
sql += " AND mapset = '%s'" % (mapset)
else:
sql += " WHERE mapset = '%s'" % (mapset)
if where:
sql += " WHERE " + where
sql += " AND mapset = '%s'" % (mapset)
else:
sql += " WHERE mapset = '%s'" % (mapset)

if order:
sql += " ORDER BY " + order
if order:
sql += " ORDER BY " + order

dbif.execute(sql, mapset=mapset)
rows = dbif.fetchall(mapset=mapset)
dbif.execute(sql, mapset=mapset)
rows = dbif.fetchall(mapset=mapset)

if rows:
result[mapset] = rows
if rows:
if mapset not in result:
result[mapset] = []
result[mapset].extend(rows)

if connection_state_changed:
dbif.close()
Expand Down
22 changes: 20 additions & 2 deletions temporal/t.list/t.list.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

*t.list* lists any dataset that is registered in the temporal database.
Datasets are raster, 3D raster and vector maps as well as their
corresponding space time datasets (STRDS, STR3DS and STVDS). The type of
the dataset can be specified using the *type* option, default is STRDS.
corresponding space time datasets (STRDS, STR3DS and STVDS). The type of the
dataset can be specified using the *type* option (default is STRDS).
Multiple comma-separated types can be provided, such as *type=strds,stvds*.
By default all datasets with relative and absolute time are listed.
However, the user has the ability to specify a single temporal type with
the *temporaltype* option. The user can define the columns that should
Expand Down Expand Up @@ -149,7 +150,24 @@ t.list type=raster format=json columns=id,start_time
"start_time": "2012-12-01 00:00:00"
}
]
```

To list multiple dataset types at once, specify a comma-separated list of
types. When using multiple types, including `type` in the `columns` parameter
allows identifying the type of each record:

```sh
t.list type=strds,stvds columns=id,type format=csv
```

```csv
Comment thread
saket0187 marked this conversation as resolved.
Outdated
id,type
lst_daily@PERMANENT,strds
mini_set@PERMANENT,strds
nc_lst_daily@PERMANENT,strds
precip_abs1@PERMANENT,strds
prec_observer@PERMANENT,stvds
schools_stds@PERMANENT,stvds
```

## SEE ALSO
Expand Down
22 changes: 19 additions & 3 deletions temporal/t.list/t.list.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
# % description: Type of the space time dataset or map, default is strds
# % guisection: Selection
# % required: no
# % multiple: yes
# % options: strds, str3ds, stvds, raster, raster_3d, vector
# % answer: strds
# %end
Expand Down Expand Up @@ -64,7 +65,7 @@
# % guisection: Selection
# % required: no
# % multiple: yes
# % options: id,name,semantic_label,creator,mapset,number_of_maps,creation_time,start_time,end_time,north,south,west,east,granularity,all
# % options: id,name,type,semantic_label,creator,mapset,number_of_maps,creation_time,start_time,end_time,north,south,west,east,granularity,all
Comment thread
saket0187 marked this conversation as resolved.
Outdated
# % answer:
# %end

Expand Down Expand Up @@ -209,15 +210,18 @@ def main():
and (not outpath or outpath == "-")
and output_format == "plain"
):
if stds_type in {"raster", "raster_3d", "vector"}:
stds_types = {
s.strip() for s in stds_type.split(",") if s.strip()
}
if stds_types.issubset({"raster", "raster_3d", "vector"}):
sys.stderr.write(
_(
"Time stamped %s maps with %s available in mapset "
"<%s>:\n"
)
% (stds_type, time, mapset)
)
else:
elif stds_types.issubset({"strds", "str3ds", "stvds"}):
sys.stderr.write(
_(
"Space time %s datasets with %s available in "
Comment thread
saket0187 marked this conversation as resolved.
Outdated
Expand All @@ -229,6 +233,18 @@ def main():
mapset,
)
)
else:
sys.stderr.write(
Comment thread
saket0187 marked this conversation as resolved.
Outdated
_(
"Space time datasets and time stamped maps (%s) "
"with %s available in mapset <%s>:\n"
)
% (
stds_type,
time,
mapset,
)
)

if output_format == "json":
for row in rows:
Expand Down
46 changes: 39 additions & 7 deletions temporal/t.list/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""

import os
from io import StringIO
from types import SimpleNamespace

import pytest
Expand All @@ -13,11 +14,11 @@

@pytest.fixture(scope="module")
def space_time_dataset(tmp_path_factory):
"""Start an isolated session and create a raster time series.
"""Start an isolated session and create raster and vector time series.

Returns an object with attributes about the dataset.
Returns an object with attributes about the datasets.
"""
tmp_path = tmp_path_factory.mktemp("raster_time_series")
tmp_path = tmp_path_factory.mktemp("time_series")
project = tmp_path / "test_project"
gs.create_project(project)

Expand Down Expand Up @@ -51,20 +52,47 @@ def space_time_dataset(tmp_path_factory):
flags="i",
)

vector_names = [f"vect_{i}" for i in range(1, 3)]
coords = [f"{i * 10}|{i * 10}" for i in range(1, 3)]
for name, coord in zip(vector_names, coords, strict=True):
tools.v_in_ascii(
input=StringIO(coord),
output=name,
format="point",
)
stvds_name = "vector_dataset"
tools.t_create(
type="stvds",
temporaltype="absolute",
output=stvds_name,
title="Vector dataset",
description="Vector series generated for tests",
)
vector_file = tmp_path / "vector_names.txt"
vector_file.write_text("\n".join(vector_names))
tools.t_register(
type="vector",
input=stvds_name,
file=vector_file,
start="2026-01-01",
increment="1 month",
flags="i",
)

gs.run_command("g.mapset", mapset="user1", flags="c", env=session.env)

user_dataset_name = "precipitation_dataset"
raster_dataset_name = "precipitation_dataset"
tools.r_mapcalc(expression="precip_1 = 2", overwrite=True)
tools.t_create(
type="strds",
temporaltype="absolute",
output=user_dataset_name,
output=raster_dataset_name,
title="Precip",
description="user1 Dataset",
)
tools.t_register(
type="raster",
input=user_dataset_name,
input=raster_dataset_name,
maps="precip_1",
start="2026-01-01",
increment="1 month",
Expand All @@ -76,8 +104,12 @@ def space_time_dataset(tmp_path_factory):
yield SimpleNamespace(
session=session,
name=dataset_name,
raster_dataset1=dataset_name,
raster_dataset2=raster_dataset_name,
map_names=names,
user_dataset=user_dataset_name,
raster_dataset=raster_dataset_name,
stvds_name=stvds_name,
vector_names=vector_names,
)


Expand Down
59 changes: 36 additions & 23 deletions temporal/t.list/tests/test_t_list.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
"""Test t.list functionality"""

import json

import pytest

from grass.experimental import TemporaryMapsetSession
Expand All @@ -15,13 +13,13 @@ def test_t_list_defaults(space_time_dataset):
strds_result = tools.t_list(type="strds", columns="name")
assert strds_result.returncode == 0

strds_lines = [line.strip() for line in strds_result.stdout.strip().splitlines()]
assert space_time_dataset.name in strds_lines
strds_lines = strds_result.text_split("\n")
assert space_time_dataset.raster_dataset1 in strds_lines

# Test Map Listing (raster)
raster_result = tools.t_list(type="raster", columns="name")

raster_lines = [line.strip() for line in raster_result.stdout.strip().splitlines()]
raster_lines = raster_result.text_split("\n")
for map_name in space_time_dataset.map_names:
assert map_name in raster_lines

Expand All @@ -31,9 +29,9 @@ def test_t_list_where_filter(space_time_dataset):
tools = Tools(session=space_time_dataset.session)

match = tools.t_list(type="strds", columns="name", where="name LIKE 'temp_%'")
match_lines = [line.strip() for line in match.stdout.strip().splitlines()]
match_lines = match.text_split("\n")
assert len(match_lines) == 1
assert match_lines[0] == space_time_dataset.name
assert match_lines[0] == space_time_dataset.raster_dataset1

empty = tools.t_list(type="strds", columns="name", where="name LIKE 'land_%'")
assert empty is None
Expand All @@ -46,7 +44,7 @@ def test_t_list_order(space_time_dataset):
result_asc = tools.t_list(type="raster", columns="name", order="start_time")
assert result_asc is not None

lines_asc = [line.strip() for line in result_asc.stdout.strip().splitlines()]
lines_asc = result_asc.text_split("\n")

assert lines_asc == space_time_dataset.map_names

Expand All @@ -56,9 +54,7 @@ def test_t_list_json(space_time_dataset):
tools = Tools(session=space_time_dataset.session)
result = tools.t_list(
type="raster", format="json", columns="name,start_time,end_time"
)

data = json.loads(result.stdout)
).json

expected = [
{
Expand All @@ -78,10 +74,10 @@ def test_t_list_json(space_time_dataset):
},
]

assert len(data) == len(expected)
assert len(result) == len(expected)

for i, expected_item in enumerate(expected):
actual_item = data[i]
actual_item = result[i]
for key, expected_value in expected_item.items():
assert actual_item[key] == expected_value

Expand All @@ -102,7 +98,7 @@ def test_t_list_csv(space_time_dataset, separator):
]
expected = "\n".join(expected_lines)

assert result.stdout.strip() == expected
assert result.text == expected


@pytest.mark.parametrize("separator", [",", ":"])
Expand All @@ -115,7 +111,7 @@ def test_t_list_line(space_time_dataset, separator):

expected = separator.join(space_time_dataset.map_names)

assert result.stdout.strip() == expected
assert result.text == expected


@pytest.mark.parametrize("output_format", ["json", "line", "plain"])
Expand All @@ -141,7 +137,7 @@ def test_t_list_mapset_current(space_time_dataset):
result = tools.t_list(type="strds", mapset=".", columns="name", format="json")

assert len(result) == 1
assert result[0]["name"] == space_time_dataset.name
assert result[0]["name"] == space_time_dataset.raster_dataset1


def test_t_list_mapset_all(space_time_dataset):
Expand All @@ -152,8 +148,8 @@ def test_t_list_mapset_all(space_time_dataset):

assert len(result) == 2
names = [d["name"] for d in result]
assert space_time_dataset.name in names
assert space_time_dataset.user_dataset in names
assert space_time_dataset.raster_dataset1 in names
assert space_time_dataset.raster_dataset2 in names


def test_t_list_mapset_explicit(space_time_dataset):
Expand All @@ -162,13 +158,13 @@ def test_t_list_mapset_explicit(space_time_dataset):

res_user = tools.t_list(type="strds", mapset="user1", columns="name", format="json")
assert len(res_user) == 1
assert res_user[0]["name"] == space_time_dataset.user_dataset
assert res_user[0]["name"] == space_time_dataset.raster_dataset2

res = tools.t_list(type="strds", mapset="PERMANENT,user1", format="json")
assert len(res) == 2
names = [d["name"] for d in res]
assert space_time_dataset.name in names
assert space_time_dataset.user_dataset in names
assert space_time_dataset.raster_dataset1 in names
assert space_time_dataset.raster_dataset2 in names


def test_t_list_empty_database(empty_session):
Expand All @@ -193,9 +189,26 @@ def test_t_list_empty_database(empty_session):

def test_t_list_from_mapset_without_temporal_database(space_time_dataset):
"""Datasets in accessible mapsets are listed even when the current mapset
has no temporal database (https://github.com/OSGeo/grass/issues/7622)."""
has no temporal database."""
with TemporaryMapsetSession(env=space_time_dataset.session.env) as mapset_session:
tools = Tools(session=mapset_session)
result = tools.t_list(type="strds", format="json")
names = [record["name"] for record in result.json]
assert space_time_dataset.name in names
assert space_time_dataset.raster_dataset1 in names


def test_t_list_multiple_types_json(space_time_dataset):
"""Check that listing multiple dataset types in JSON format returns entries from all types."""
tools = Tools(session=space_time_dataset.session)
result = tools.t_list(
type="stvds,strds", mapset=".", columns="name,type", format="json"
)

assert len(result.json) == 2
names = [record["name"] for record in result.json]
assert space_time_dataset.raster_dataset1 in names
assert space_time_dataset.stvds_name in names

types = {record["type"] for record in result.json}
assert "strds" in types
assert "stvds" in types
Loading