1"""
2Functions to create space time dataset lists
3
4Usage:
5
6.. code-block:: python
7
8    import grass.temporal as tgis
9
10    tgis.register_maps_in_space_time_dataset(type, name, maps)
11
12
13(C) 2012-2016 by the GRASS Development Team
14This program is free software under the GNU General Public
15License (>=v2). Read the file COPYING that comes with GRASS GIS
16for details.
17
18:authors: Soeren Gebbert
19"""
20from __future__ import print_function
21from .core import get_tgis_message_interface, get_available_temporal_mapsets, init_dbif
22from .datetime_math import time_delta_to_relative_time
23from .space_time_datasets import RasterDataset
24from .factory import dataset_factory
25from .open_stds import open_old_stds
26import grass.script as gscript
27
28###############################################################################
29
30
31def get_dataset_list(type, temporal_type, columns=None, where=None,
32                     order=None, dbif=None):
33    """ Return a list of time stamped maps or space time datasets of a specific
34        temporal type that are registered in the temporal database
35
36        This method returns a dictionary, the keys are the available mapsets,
37        the values are the rows from the SQL database query.
38
39        :param type: The type of the datasets (strds, str3ds, stvds, raster,
40                     raster_3d, vector)
41        :param temporal_type: The temporal type of the datasets (absolute,
42                              relative)
43        :param columns: A comma separated list of columns that will be selected
44        :param where: A where statement for selected listing without "WHERE"
45        :param order: A comma separated list of columns to order the
46                      datasets by category
47        :param dbif: The database interface to be used
48
49        :return: A dictionary with the rows of the SQL query for each
50                 available mapset
51
52        .. code-block:: python
53
54            >>> import grass.temporal as tgis
55            >>> tgis.core.init()
56            >>> name = "list_stds_test"
57            >>> sp = tgis.open_stds.open_new_stds(name=name, type="strds",
58            ... temporaltype="absolute", title="title", descr="descr",
59            ... semantic="mean", dbif=None, overwrite=True)
60            >>> mapset = tgis.get_current_mapset()
61            >>> stds_list = tgis.list_stds.get_dataset_list("strds", "absolute", columns="name")
62            >>> rows =  stds_list[mapset]
63            >>> for row in rows:
64            ...     if row["name"] == name:
65            ...         print(True)
66            True
67            >>> stds_list = tgis.list_stds.get_dataset_list("strds", "absolute", columns="name,mapset", where="mapset = '%s'"%(mapset))
68            >>> rows =  stds_list[mapset]
69            >>> for row in rows:
70            ...     if row["name"] == name and row["mapset"] == mapset:
71            ...         print(True)
72            True
73            >>> check = sp.delete()
74
75    """
76    id = None
77    sp = dataset_factory(type, id)
78
79    dbif, connected = init_dbif(dbif)
80
81    mapsets = get_available_temporal_mapsets()
82
83    result = {}
84
85    for mapset in mapsets.keys():
86
87        if temporal_type == "absolute":
88            table = sp.get_type() + "_view_abs_time"
89        else:
90            table = sp.get_type() + "_view_rel_time"
91
92        if columns and columns.find("all") == -1:
93            sql = "SELECT " + str(columns) + " FROM " + table
94        else:
95            sql = "SELECT * FROM " + table
96
97        if where:
98            sql += " WHERE " + where
99            sql += " AND mapset = '%s'" % (mapset)
100        else:
101            sql += " WHERE mapset = '%s'" % (mapset)
102
103        if order:
104            sql += " ORDER BY " + order
105
106        dbif.execute(sql,  mapset=mapset)
107        rows = dbif.fetchall(mapset=mapset)
108
109        if rows:
110            result[mapset] = rows
111
112    if connected:
113        dbif.close()
114
115    return result
116
117###############################################################################
118
119
120def list_maps_of_stds(type, input, columns, order, where, separator,
121                      method, no_header=False, gran=None, dbif=None,
122                      outpath=None):
123    """ List the maps of a space time dataset using different methods
124
125        :param type: The type of the maps raster, raster3d or vector
126        :param input: Name of a space time raster dataset
127        :param columns: A comma separated list of columns to be printed to stdout
128        :param order: A comma separated list of columns to order the
129                      maps by category
130        :param where: A where statement for selected listing without "WHERE"
131                      e.g: start_time < "2001-01-01" and end_time > "2001-01-01"
132        :param separator: The field separator character between the columns
133        :param method: String identifier to select a method out of cols,
134                       comma,delta or deltagaps
135        :param dbif: The database interface to be used
136
137            - "cols" Print preselected columns specified by columns
138            - "comma" Print the map ids ("name@mapset") as comma separated string
139            - "delta" Print the map ids ("name@mapset") with start time,
140               end time, relative length of intervals and the relative
141               distance to the begin
142            - "deltagaps" Same as "delta" with additional listing of gaps.
143               Gaps can be easily identified as the id is "None"
144            - "gran" List map using the granularity of the space time dataset,
145               columns are identical to deltagaps
146
147        :param no_header: Suppress the printing of column names
148        :param gran: The user defined granule to be used if method=gran is
149                     set, in case gran=None the granule of the space time
150                     dataset is used
151        :param outpath: The path to file where to save output
152    """
153
154    dbif, connected = init_dbif(dbif)
155    msgr = get_tgis_message_interface()
156
157    sp = open_old_stds(input, type, dbif)
158
159    if separator is None or separator == "":
160        separator = "\t"
161
162    if outpath:
163        outfile = open(outpath, 'w')
164
165    # This method expects a list of objects for gap detection
166    if method == "delta" or method == "deltagaps" or method == "gran":
167        if type == "stvds":
168            columns = "id,name,layer,mapset,start_time,end_time"
169        else:
170            columns = "id,name,mapset,start_time,end_time"
171        if method == "deltagaps":
172            maps = sp.get_registered_maps_as_objects_with_gaps(where=where,
173                                                               dbif=dbif)
174        elif method == "delta":
175            maps = sp.get_registered_maps_as_objects(where=where,
176                                                     order="start_time",
177                                                     dbif=dbif)
178        elif method == "gran":
179            if gran is not None and gran != "":
180                maps = sp.get_registered_maps_as_objects_by_granularity(gran=gran,
181                                                                        dbif=dbif)
182            else:
183                maps = sp.get_registered_maps_as_objects_by_granularity(dbif=dbif)
184
185        if no_header is False:
186            string = ""
187            string += "%s%s" % ("id", separator)
188            string += "%s%s" % ("name", separator)
189            if type == "stvds":
190                string += "%s%s" % ("layer", separator)
191            string += "%s%s" % ("mapset", separator)
192            string += "%s%s" % ("start_time", separator)
193            string += "%s%s" % ("end_time", separator)
194            string += "%s%s" % ("interval_length", separator)
195            string += "%s" % ("distance_from_begin")
196            if outpath:
197                outfile.write('{st}\n'.format(st=string))
198            else:
199                print(string)
200
201        if maps and len(maps) > 0:
202
203            if isinstance(maps[0], list):
204                if len(maps[0]) > 0:
205                    first_time, dummy = maps[0][0].get_temporal_extent_as_tuple()
206                else:
207                    msgr.warning(_("Empty map list"))
208                    return
209            else:
210                first_time, dummy = maps[0].get_temporal_extent_as_tuple()
211
212            for mymap in maps:
213
214                if isinstance(mymap, list):
215                    if len(mymap) > 0:
216                        map = mymap[0]
217                    else:
218                        msgr.fatal(_("Empty entry in map list, this should not happen"))
219                else:
220                    map = mymap
221
222                start, end = map.get_temporal_extent_as_tuple()
223                if end:
224                    delta = end - start
225                else:
226                    delta = None
227                delta_first = start - first_time
228
229                if map.is_time_absolute():
230                    if end:
231                        delta = time_delta_to_relative_time(delta)
232                    delta_first = time_delta_to_relative_time(delta_first)
233
234                string = ""
235                string += "%s%s" % (map.get_id(), separator)
236                string += "%s%s" % (map.get_name(), separator)
237                if type == "stvds":
238                    string += "%s%s" % (map.get_layer(), separator)
239                string += "%s%s" % (map.get_mapset(), separator)
240                string += "%s%s" % (start, separator)
241                string += "%s%s" % (end, separator)
242                string += "%s%s" % (delta, separator)
243                string += "%s" % (delta_first)
244                if outpath:
245                    outfile.write('{st}\n'.format(st=string))
246                else:
247                    print(string)
248
249    else:
250        # In comma separated mode only map ids are needed
251        if method == "comma":
252            if columns not in ['id', 'name']:
253                columns = "id"
254
255        rows = sp.get_registered_maps(columns, where, order, dbif)
256
257        if not rows:
258            dbif.close()
259            err = "Space time %(sp)s dataset <%(i)s> is empty"
260            if where:
261                err += " or where condition is wrong"
262            gscript.fatal(_(err) % {
263                            'sp': sp.get_new_map_instance(None).get_type(),
264                            'i': sp.get_id()})
265
266        if rows:
267            if method == "comma":
268                string = ""
269                count = 0
270                for row in rows:
271                    if count == 0:
272                        string += row[columns]
273                    else:
274                        string += ",%s" % row[columns]
275                    count += 1
276                if outpath:
277                    outfile.write('{st}\n'.format(st=string))
278                else:
279                    print(string)
280
281            elif method == "cols":
282                # Print the column names if requested
283                if no_header is False:
284                    output = ""
285                    count = 0
286
287                    collist = columns.split(",")
288
289                    for key in collist:
290                        if count > 0:
291                            output += separator + str(key)
292                        else:
293                            output += str(key)
294                        count += 1
295                    if outpath:
296                        outfile.write('{st}\n'.format(st=output))
297                    else:
298                        print(output)
299
300                for row in rows:
301                    output = ""
302                    count = 0
303                    for col in row:
304                        if count > 0:
305                            output += separator + str(col)
306                        else:
307                            output += str(col)
308                        count += 1
309                    if outpath:
310                        outfile.write('{st}\n'.format(st=output))
311                    else:
312                        print(output)
313    if outpath:
314        outfile.close()
315    if connected:
316        dbif.close()
317
318###############################################################################
319
320if __name__ == "__main__":
321    import doctest
322    doctest.testmod()
323