Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

g.mapsets: Add JSON output #2542

Merged
merged 31 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
032620a
g.mapsets: fixed indenting and added json flag in preperation to add…
cwhite911 Aug 27, 2022
dfbf8cf
Added format options to list mapsets as plain, vertical, csv, or json
cwhite911 Aug 27, 2022
3cd14cc
g.mapsets: Added start testing listing mapsets with different formats
cwhite911 Aug 27, 2022
53e3905
g.mapsets: Reformated python with flake8 and black
cwhite911 Aug 27, 2022
0f05302
g.mapsets: Fixed indent conflict
cwhite911 Aug 27, 2022
5415520
Merge remote-tracking branch 'upstream/main' into g.mapset-json
cwhite911 Aug 27, 2022
a904c05
Fixed implicit declaration of function errors
cwhite911 Aug 27, 2022
d3da3fd
Fixed issues found in code review
cwhite911 Aug 28, 2022
8173965
Added print flag to json output and added tests
cwhite911 Aug 28, 2022
34dd435
Simplified tests
cwhite911 Aug 31, 2022
5fd4e05
Merge branch 'main' into g.mapset-json
echoix Mar 21, 2024
e5e834c
Apply clang format suggestions
echoix Mar 21, 2024
5622b70
Merge branch 'main' into g.mapset-json
cwhite911 Apr 4, 2024
fb21b77
Updated code to use parson and fixed tests
Apr 4, 2024
76dbf30
Updated docs
Apr 4, 2024
4e6b53b
Fixed mapsets array initalization issue
Apr 4, 2024
8163581
Fixed bug setting JSON_ARRAY
Apr 4, 2024
bdceb3d
Removed unused parameter
Apr 4, 2024
db0252e
Update general/g.mapsets/main.c
cwhite911 Apr 5, 2024
6c18a50
Updated default separator to space
Apr 5, 2024
8e99fec
Update general/g.mapsets/main.c
cwhite911 Apr 7, 2024
8961692
Update general/g.mapsets/main.c
cwhite911 Apr 7, 2024
06d04a6
Update general/g.mapsets/main.c
cwhite911 Apr 7, 2024
cf061e6
Merge branch 'main' into g.mapset-json
cwhite911 Apr 7, 2024
ace2374
Removed unneeded logic
Apr 7, 2024
26f47c3
Update general/g.mapsets/list.c
cwhite911 Apr 8, 2024
e7f815a
Update general/g.mapsets/list.c
cwhite911 Apr 8, 2024
4b8f58b
Merge branch 'main' into g.mapset-json
cwhite911 Apr 8, 2024
4d0794c
Implmented suggestion from code review
Apr 8, 2024
6c8509a
Removed duplicate code
Apr 8, 2024
4463bf1
Refactored duplicate code and removed some comments
Apr 10, 2024
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
2 changes: 1 addition & 1 deletion general/g.mapsets/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ MODULE_TOPDIR = ../..

PGM = g.mapsets

LIBES = $(GISLIB)
LIBES = $(PARSONLIB) $(GISLIB)
DEPENDENCIES = $(GISDEP)

include $(MODULE_TOPDIR)/include/Make/Module.make
Expand Down
16 changes: 16 additions & 0 deletions general/g.mapsets/g.mapsets.html
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,22 @@ <h3>Print available mapsets</h3>
PERMANENT user1 user2
</pre></div>

Mapsets can be also printed out as json by setting the format option to "json" (<b>format="json"</b>).

<div class="code">
<pre>
g.mapsets format="json" -l

{
"mapsets": [
"PERMANENT",
"user1",
"user2"
]
}
</pre>
</div>

<h3>Add new mapset</h3>

Add mapset 'user2' to the current mapset search path
Expand Down
99 changes: 74 additions & 25 deletions general/g.mapsets/list.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,53 +3,102 @@
#include <grass/gis.h>
#include <grass/glocale.h>
#include "local_proto.h"
#include <grass/parson.h>

void list_available_mapsets(const char **mapset_name, int nmapsets,
const char *fs)
{
int n;

G_message(_("Available mapsets:"));

for (n = 0; n < nmapsets; n++) {
for (int n = 0; n < nmapsets; n++) {
fprintf(stdout, "%s", mapset_name[n]);
if (n < nmapsets - 1) {
if (strcmp(fs, "newline") == 0)
fprintf(stdout, "\n");
else if (strcmp(fs, "space") == 0)
fprintf(stdout, " ");
else if (strcmp(fs, "comma") == 0)
fprintf(stdout, ",");
else if (strcmp(fs, "tab") == 0)
fprintf(stdout, "\t");
else
fprintf(stdout, "%s", fs);
fprintf(stdout, "%s", fs);
}
}
fprintf(stdout, "\n");
}

void list_accessible_mapsets(const char *fs)
{
int n;
const char *name;

G_message(_("Accessible mapsets:"));
for (n = 0; (name = G_get_mapset_name(n)); n++) {

for (int n = 0; (name = G_get_mapset_name(n)); n++) {
/* match each mapset to its numeric equivalent */
fprintf(stdout, "%s", name);
if (G_get_mapset_name(n + 1)) {
if (strcmp(fs, "newline") == 0)
fprintf(stdout, "\n");
else if (strcmp(fs, "space") == 0)
fprintf(stdout, " ");
else if (strcmp(fs, "comma") == 0)
fprintf(stdout, ",");
else if (strcmp(fs, "tab") == 0)
fprintf(stdout, "\t");
else
fprintf(stdout, "%s", fs);
fprintf(stdout, "%s", fs);
}
}
fprintf(stdout, "\n");
}

void list_accessible_mapsets_json()
{
const char *name;
char *serialized_string = NULL;
JSON_Value *root_value = NULL;
JSON_Object *root_object = NULL;
JSON_Array *mapsets = NULL;

root_value = json_value_init_object();
root_object = json_value_get_object(root_value);

// Create and add mapsets array to root object
json_object_set_value(root_object, "mapsets", json_value_init_array());
mapsets = json_object_get_array(root_object, "mapsets");

// Check that memory was allocated to root json object and array
if (root_value == NULL || mapsets == NULL) {
G_fatal_error(_("Failed to initialize JSON. Out of memory?"));
}

// Add mapsets to mapsets array
for (int n = 0; (name = G_get_mapset_name(n)); n++) {
// Append mapset name to mapsets array
json_array_append_string(mapsets, name);
}

// Serialize root object to string and print it to stdout
serialized_string = json_serialize_to_string_pretty(root_value);
puts(serialized_string);
cwhite911 marked this conversation as resolved.
Show resolved Hide resolved

// Free memory
json_free_serialized_string(serialized_string);
json_value_free(root_value);
}

void list_avaliable_mapsets_json(const char **mapset_name, int nmapsets)
{
char *serialized_string = NULL;
JSON_Value *root_value = NULL;
JSON_Object *root_object = NULL;
JSON_Array *mapsets = NULL;

root_value = json_value_init_object();
root_object = json_value_get_object(root_value);

// Create mapsets array
json_object_set_value(root_object, "mapsets", json_value_init_array());
mapsets = json_object_get_array(root_object, "mapsets");

// Check that memory was allocated to root json object and array
if (root_value == NULL || mapsets == NULL) {
G_fatal_error(_("Failed to initialize JSON. Out of memory?"));
}

// Append mapsets to mapsets array
for (int n = 0; n < nmapsets; n++) {
json_array_append_string(mapsets, mapset_name[n]);
}

// Serialize root object to string and print it to stdout
serialized_string = json_serialize_to_string_pretty(root_value);
puts(serialized_string);

// Free memory
json_free_serialized_string(serialized_string);
json_value_free(root_value);
}
2 changes: 2 additions & 0 deletions general/g.mapsets/local_proto.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ const char *substitute_mapset(const char *);
/* list.c */
void list_available_mapsets(const char **, int, const char *);
void list_accessible_mapsets(const char *);
void list_avaliable_mapsets_json(const char **, int);
void list_accessible_mapsets_json();
89 changes: 77 additions & 12 deletions general/g.mapsets/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
* Markus Neteler <neteler itc.it>,
* Moritz Lennert <mlennert club.worldonline.be>,
* Martin Landa <landa.martin gmail.com>,
* Huidae Cho <grass4u gmail.com>
* Huidae Cho <grass4u gmail.com>,
* Corey White <smortopahri gmail.com>
* PURPOSE: set current mapset path
* COPYRIGHT: (C) 1994-2009, 2012 by the GRASS Development Team
* COPYRIGHT: (C) 1994-2009, 2012-2024 by the GRASS Development Team
*
* This program is free software under the GNU General
* Public License (>=v2). Read the file COPYING that
Expand All @@ -33,6 +34,28 @@
#define OP_ADD 2
#define OP_REM 3

enum OutputFormat { PLAIN, JSON };

void fatal_error_option_value_excludes_flag(struct Option *option,
struct Flag *excluded,
const char *because)
{
if (!excluded->answer)
return;
G_fatal_error(_("The flag -%c is not allowed with %s=%s. %s"),
excluded->key, option->key, option->answer, because);
}

void fatal_error_option_value_excludes_option(struct Option *option,
struct Option *excluded,
const char *because)
{
if (!excluded->answer)
return;
G_fatal_error(_("The option %s is not allowed with %s=%s. %s"),
excluded->key, option->key, option->answer, because);
}

static void append_mapset(char **, const char *);

int main(int argc, char *argv[])
Expand All @@ -45,15 +68,15 @@ int main(int argc, char *argv[])
int no_tokens;
FILE *fp;
char path_buf[GPATH_MAX];
char *path, *fs;
char *path, *fsep;
int operation, nchoices;

enum OutputFormat format;
char **mapset_name;
int nmapsets;

struct GModule *module;
struct _opt {
struct Option *mapset, *op, *fs;
struct Option *mapset, *op, *format, *fsep;
struct Flag *print, *list, *dialog;
} opt;

Expand Down Expand Up @@ -82,10 +105,20 @@ int main(int argc, char *argv[])
opt.op->description = _("Operation to be performed");
opt.op->answer = "add";

opt.fs = G_define_standard_option(G_OPT_F_SEP);
opt.fs->label = _("Field separator for printing (-l and -p flags)");
opt.fs->answer = "space";
opt.fs->guisection = _("Print");
opt.format = G_define_option();
opt.format->key = "format";
opt.format->type = TYPE_STRING;
opt.format->required = YES;
opt.format->label = _("Output format for printing (-l and -p flags)");
opt.format->options = "plain,json";
opt.format->descriptions = "plain;Configurable plain text output;"
"json;JSON (JavaScript Object Notation);";
opt.format->answer = "plain";
opt.format->guisection = _("Print");

opt.fsep = G_define_standard_option(G_OPT_F_SEP);
opt.fsep->answer = NULL;
opt.fsep->guisection = _("Print");

opt.list = G_define_flag();
opt.list->key = 'l';
Expand Down Expand Up @@ -130,7 +163,27 @@ int main(int argc, char *argv[])
}
}

fs = G_option_to_separator(opt.fs);
if (strcmp(opt.format->answer, "json") == 0)
format = JSON;
else
format = PLAIN;
if (format == JSON) {
fatal_error_option_value_excludes_option(
opt.format, opt.fsep, _("Separator is part of the format."));
}

/* the field separator */
if (opt.fsep->answer) {
fsep = G_option_to_separator(opt.fsep);
}
else {
/* A different separator is needed to for each format and output. */
if (format == PLAIN) {
fsep = G_store(" ");
}
else
fsep = NULL; /* Something like a separator is part of the format. */
}

/* list available mapsets */
if (opt.list->answer) {
Expand All @@ -141,7 +194,13 @@ int main(int argc, char *argv[])
if (opt.mapset->answer)
G_warning(_("Option <%s> ignored"), opt.mapset->key);
mapset_name = get_available_mapsets(&nmapsets);
list_available_mapsets((const char **)mapset_name, nmapsets, fs);
if (format == JSON) {
list_avaliable_mapsets_json((const char **)mapset_name, nmapsets);
}
else {
list_available_mapsets((const char **)mapset_name, nmapsets, fsep);
}

exit(EXIT_SUCCESS);
}

Expand All @@ -150,7 +209,13 @@ int main(int argc, char *argv[])
G_warning(_("Flag -%c ignored"), opt.dialog->key);
if (opt.mapset->answer)
G_warning(_("Option <%s> ignored"), opt.mapset->key);
list_accessible_mapsets(fs);
if (format == JSON) {
list_accessible_mapsets_json();
}
else {
list_accessible_mapsets(fsep);
}

exit(EXIT_SUCCESS);
}

Expand Down
35 changes: 35 additions & 0 deletions general/g.mapsets/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""Fixtures for Jupyter tests

Fixture for grass.jupyter.TimeSeries test

Fixture for ReprojectionRenderer test with simple GRASS location, raster, vector.
"""


from types import SimpleNamespace

import grass.script as gs
import pytest

TEST_MAPSETS = ["PERMANENT", "test1", "test2", "test3"]
ACCESSIBLE_MAPSETS = ["test3", "PERMANENT"]


@pytest.fixture(scope="module")
def simple_dataset(tmp_path_factory):
"""Start a session and create a test mapsets
Returns object with attributes about the dataset.
"""
tmp_path = tmp_path_factory.mktemp("simple_dataset")
location = "test"
gs.core._create_location_xy(tmp_path, location) # pylint: disable=protected-access
with gs.setup.init(tmp_path / location):
gs.run_command("g.proj", flags="c", epsg=26917)
gs.run_command("g.region", s=0, n=80, w=0, e=120, b=0, t=50, res=10, res3=10)
# Create Mock Mapsets
for mapset in TEST_MAPSETS:
gs.run_command("g.mapset", location=location, mapset=mapset, flags="c")

yield SimpleNamespace(
mapsets=TEST_MAPSETS, accessible_mapsets=ACCESSIBLE_MAPSETS
)
Loading
Loading