diff --git a/hydra_base/lib/network.py b/hydra_base/lib/network.py index 7566a358..86e4e809 100644 --- a/hydra_base/lib/network.py +++ b/hydra_base/lib/network.py @@ -1783,6 +1783,15 @@ def get_network_extents(network_id,**kwargs): # Default y extent if all None values min_alt_y, max_alt_y = 0, 1 + # min/max default to fake 0/1 (or 0/100 in some callers) ranges when a + # coordinate system has no data at all, which the frontend can't tell + # apart from "genuinely spans 0 to 1" - these booleans let it reliably + # detect presence instead (see hwi's network.html: hasGeographicView/ + # hasSchematicView, which gate whether the map/schematic view - and the + # dual-view switch button - are offered at all). + has_geographic = len(x) > 0 and len(y) > 0 + has_schematic = len(alt_x) > 0 and len(alt_y) > 0 + ne = JSONObject(dict( network_id = network_id, min_x=x_min, diff --git a/hydra_base/lib/project.py b/hydra_base/lib/project.py index d49f17b5..ceb6e356 100644 --- a/hydra_base/lib/project.py +++ b/hydra_base/lib/project.py @@ -198,6 +198,33 @@ def update_project_appdata(project_id, key, value, **kwargs): return appdata +@required_perms('edit_project') +def update_project_appdata(project_id, key, value, **kwargs): + """ + Update a single key in a project's appdata, without touching any + other project fields (name, description, parent_id, etc). + Unlike Network.appdata (a Text column, manually JSON (de)serialized - + see update_network_appdata), Project.appdata is a native JSON column, + so no json.dumps/loads is needed here. + """ + user_id = kwargs.get('user_id') + + proj_i = _get_project(project_id, user_id, check_write=True) + + if proj_i.appdata is None: + appdata = {} + else: + appdata = proj_i.appdata.copy() + + appdata[key] = value + proj_i.appdata = appdata + + db.DBSession.flush() + + Project.clear_cache(user_id) + + return appdata + @required_perms('edit_project') def move_project(project_id, target_project_id, **kwargs): """ diff --git a/tests/test_network.py b/tests/test_network.py index dc4dd0cd..f6e9aae3 100644 --- a/tests/test_network.py +++ b/tests/test_network.py @@ -301,6 +301,77 @@ def test_get_extents(self, client, network_with_data): assert extents.max_x == 100 assert extents.min_y == 9 assert extents.max_y == 99 + assert extents.has_geographic is True + assert extents.has_schematic is True + + def test_get_extents_empty_network(self, client, projectmaker): + """ + A network with no nodes at all has no coordinates in either system. + """ + project = projectmaker.create('test') + + network = dict( + name = 'Network @ %s'%datetime.datetime.now(), + description = 'Test network with no nodes', + project_id = project.id, + links = [], + nodes = [], + layout = {}, + scenarios = [], + resourcegroups = [], + projection = None, + attributes = [], + ) + network = client.add_network(network) + + extents = client.get_network_extents(network.id) + + assert extents.min_x is None + assert extents.max_x is None + assert extents.min_y is None + assert extents.max_y is None + assert extents.has_geographic is False + assert extents.has_schematic is False + + def test_update_network_appdata(self, client, network_with_data): + """ + Test that a single key can be set in a network's appdata without + touching any other network fields, and that the result persists. + """ + net = network_with_data + + newappdata = client.update_network_appdata(net.id, 'dualViewEnabled', True) + + assert newappdata['dualViewEnabled'] is True + + # Setting a second key should not clobber the first. + newappdata = client.update_network_appdata(net.id, 'schematicGridSize', 25) + + assert newappdata['dualViewEnabled'] is True + assert newappdata['schematicGridSize'] == 25 + + updated_net = client.get_network(net.id) + persisted_appdata = json.loads(updated_net.appdata) + + assert persisted_appdata['dualViewEnabled'] is True + assert persisted_appdata['schematicGridSize'] == 25 + + def test_update_network_appdata_no_permission(self, client, projectmaker, networkmaker): + """ + A user with no write access to a network's project must not be able + to update its appdata. + """ + # Create a project that is NOT shared with other users + private_proj = projectmaker.create(name=None, share=False) + net = networkmaker.create(project_id=private_proj.id) + + # UserD has not been granted access to this private network/project + client.login('UserD', 'password') + try: + with pytest.raises(hb.exceptions.HydraError): + client.update_network_appdata(net.id, 'dualViewEnabled', True) + finally: + client.login('root', '') def test_update_network_appdata(self, client, network_with_data): """