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

Add rename and delete of Location and Mapset in Datacatalog #771

Conversation

lindakarlovska
Copy link
Contributor

@lindakarlovska lindakarlovska commented Jul 6, 2020

No description provided.

…this option is disabled. So far rename mapset and location is implemented for all cases.
@lindakarlovska lindakarlovska marked this pull request as draft July 6, 2020 15:54
@petrasovaa petrasovaa linked an issue Jul 6, 2020 that may be closed by this pull request
@petrasovaa petrasovaa added this to In progress in New GUI Startup via automation Jul 6, 2020
@petrasovaa petrasovaa added the gsoc Reserved for Google Summer of Code student(s) label Jul 6, 2020
from startup.guiutils import NewMapsetDialog

from grass.pydispatch.signal import Signal

from grass.script import core as grass
Copy link
Member

Choose a reason for hiding this comment

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

Use already imported gscript

Comment on lines 706 to 707
new_location_path = os.path.join(gisenv()['GISDBASE'],
self.selected_location[0].label)
Copy link
Member

Choose a reason for hiding this comment

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

Does this work for you? When I enter any name, I always get Location <definitevely-not-an-existing-directory> already exists in GRASS database.

gui/wxpython/datacatalog/tree.py Show resolved Hide resolved
lindakladivova added 3 commits July 7, 2020 08:17
…elete mapset or location in guiutils.py that uses function from utils.py. Created LocationDialog and edited MapsetDialog.
Copy link
Member

@wenzeslaus wenzeslaus left a comment

Choose a reason for hiding this comment

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

Thanks. The rename of mapsets and locations works for me in general. The GUI freezes when I rename the current mapset. Otherwise, it looks good and behaves quite well.

Comment on lines 151 to 162


def get_default_mapset_name():
"""Returns default name for mapset."""
try:
defaultName = getpass.getuser()
defaultName.encode('ascii')
except UnicodeEncodeError:
# raise error if not ascii (not valid mapset name)
defaultName = 'user'

return defaultName
Copy link
Member

Choose a reason for hiding this comment

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

The function is a good fit here. It is totally non-gui and fits with the other functions in this file.

locations to detect existing GRASS Database.

Copy link
Member

Choose a reason for hiding this comment

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

An empty line (without whitespace) is actually desired here. It separates a "brief" from a longer description of the function.

@@ -48,8 +47,7 @@ def get_possible_database_path():

def create_database_directory():
"""Creates the standard GRASS GIS directory.

Copy link
Member

Choose a reason for hiding this comment

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

Same as above.

Comment on lines 192 to 197
def location_exists(database, location):
"""Returns True whether location path exists."""
location_path = os.path.join(database, location)
if os.path.exists(location_path):
return True
return False
Copy link
Member

Choose a reason for hiding this comment

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

I don't know how mapset_exists() got here but both mapset_exists() and location_exists() should be in utils.py.

return False


def create_mapset_interactively(guiparent, grassdb, location):
Copy link
Member

Choose a reason for hiding this comment

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

Can you please take these new functions, paste them to a separate file, apply Black, and paste them back here? Perhaps check with Flake8 with default settings while you are at it. I see issues here and there, but this will likely just solve them automatically.

gisenv()['GISDBASE'],
self.selected_location[0].label,
self.selected_mapset[0].label)
self.SwitchLocationMapset(new_mapset=newmapset)
Copy link
Member

Choose a reason for hiding this comment

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

Why switch always? I though we talked about renaming not being trigger for switching. Switching is only needed when current mapset or current location are being renamed.

Comment on lines 986 to 999
def SwitchLocationMapset(self, selected_mapset=None, selected_location=None,
new_mapset=None, new_location=None):
genv = gisenv()
if (selected_location == genv['LOCATION_NAME']
and selected_mapset == genv['MAPSET']):
print("bla")
self.changeMapset.emit(mapset=new_mapset)
if (selected_location == genv['LOCATION_NAME']
and not selected_mapset):
print("bla2")
self.changeLocation.emit(location=new_location)
self.UpdateCurrentLocationMapsetNode()
self.ExpandCurrentMapset()
self.RefreshItems()
Copy link
Member

Choose a reason for hiding this comment

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

I would need you to document your intentions here in the code to understand what this is supposed to solve. Documentation and comments describing the intent are often useful even when writing the code as they force us to think about the code is supposed to do. For example, for a line add_to_list(abc) we should aim at answering questions such as: Why we are adding to the list? What is the intended outcome? What are the possible outcomes? What are the assumptions about abc or other things before add_to_list() is called?

How is this function different from OnSwitchLocationMapset()? Can there be only one function? Or two functions and one calling the other?

self.bmapset.Bind(wx.EVT_BUTTON, self.OnCreateMapset)
self.bmapset.Bind(wx.EVT_BUTTON, self.CreateMapset)
Copy link
Member

Choose a reason for hiding this comment

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

Why to change the name?

Comment on lines 609 to 624
def CreateMapset(self, event):
"""Create new mapset"""
gisdbase = self.tgisdbase.GetValue()
location = self.listOfLocations[self.lblocations.GetSelection()]
try:
mapset = create_mapset_interactively(self, gisdbase, location)
self.OnSelectLocation(None)
self.lbmapsets.SetSelection(self.listOfMapsets.index(mapset))
self.bstart.SetFocus()
return True
except Exception as e:
GError(parent=self,
message=_("Unable to create new mapset: %s") % e,
showTraceback=False)
return False

Copy link
Member

Choose a reason for hiding this comment

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

If you move this to where the old function was in the file, you will get a better view of your changes in the diff. (A more substantial change would be needed to create some better ordering of the functions here, so just keeping what is here seems to be the best course of action.)


dlg.Destroy()
try:
delete_mapset_interactively(self, self.gisbase, location, mapset)
Copy link
Contributor

Choose a reason for hiding this comment

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

…ot working. OnCreateMapset in gis_set.py moved to the initial place and names of functions changed to 'OnSomething'. Formatting in guiutils.py and utils.py corrected using Black and mapset_exists, location_exists and def_defalt_mapset_name functions moved to utils.py.
@lindakarlovska
Copy link
Contributor Author

After 4th commit:

I have still a problem with deleting mapset in gis_set.py. Do not know why it looks for file in dist.x86_64-pc-linux-gnu. And this file really does not exist.

Screenshot from 2020-07-09 04-19-03

The second problem is to switch to current mapset when selecting current mapset for renaming. The program still falls down.

@petrasovaa
Copy link
Contributor

After 4th commit:

I have still a problem with deleting mapset in gis_set.py. Do not know why it looks for file in dist.x86_64-pc-linux-gnu. And this file really does not exist.

It looks for it there because you told it to do so, did you read my comment with the link I sent you?

Comment on lines 982 to 996
def OnSwitchLocationMapset(self, event):
"""Switch to location and mapset"""
self._SwitchLocationMapset()

def _SwitchLocationMapset(self):
"""Switch to location and mapset"""
genv = gisenv()
if self.selected_location[0].label == genv['LOCATION_NAME']:
self.changeMapset.emit(mapset=self.selected_mapset[0].label)
else:
self.changeLocation.emit(mapset=self.selected_mapset[0].label, location=self.selected_location[0].label)
self.changeLocation.emit(mapset=self.selected_mapset[0].label,
location=self.selected_location[0].label)
self.UpdateCurrentLocationMapsetNode()
self.ExpandCurrentMapset()
self.RefreshItems()
Copy link
Member

Choose a reason for hiding this comment

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

This looks like a good start. Now you can maybe make use of the option of calling _SwitchLocationMapset() with different parameters based on the the context. However, still:

I would need you to document your intentions here in the code to understand what this is supposed to solve. Documentation and comments describing the intent are often useful even when writing the code as they force us to think about the code is supposed to do. For example, for a line add_to_list(abc) we should aim at answering questions such as: Why we are adding to the list? What is the intended outcome? What are the possible outcomes? What are the assumptions about abc or other things before add_to_list() is called?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Vashek, ok, I have a function OnRenameMapset that calls function rename_mapset_interactively and returns new mapset name. I think this is implemented right.
But in the case of "if (self.selected_mapset[0].label == gisenv()['MAPSET'])" I need to switch my old mapset to the new one because my current mapset has been actually changed by renaming. But not literally so I need to self.selected_mapset[0].label = newmapset and then call the self._SwitchLocationMapset() function which can be generally the same for both functions OnRenameMapset, OnRenameLocation.
Where is the mistake in my thinking?

Copy link
Member

Choose a reason for hiding this comment

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

...and _SwitchLocationMapset() uses the currently selected location/mapset to switch to. That all makes sense and should work. So, we have to continue to explore the actual behavior:

When I test renaming the current mapset in GUI, it freezes, but first it shows an error message "Error in g.mapset" saying that the mapset "old_name" does not exist. So, it seems g.mapset is used for the switch and it requires the old mapset to exist.

When I create the same situation in the command line and use g.mapset, I get the same behavior. Also I confirmed that g.mapset is used in ChangeLocationMapset() in datacatalog/frame.py to switch the mapset.

The conclusion here is that mapset cannot be changed when the current does not exist, or in our case, we cannot switch into the one with the new name after we renamed it as long as we are using g.mapset. g.mapset checks correctness of the state of the database connection, i.e., the current mapset, but we are of course breaking that correctness by our rename, so g.mapset is not the right tool for the task.

Therefore, for this ticket, use the same check to prevent user from renaming the current location or mapset. This is something which could be added in the future, but it is a separate issue/additional feature.

The freeze with "Error in g.mapset" window is possibly a separate issue too, but something we should get back to (at least open an issue - please do that if you can reproduce it outside of your PR, e.g., by deleting a the mapset directory in a file manager right before you switch to it in the data catalog).

@lindakarlovska
Copy link
Contributor Author

lindakarlovska commented Jul 10, 2020

After 4th commit:
I have still a problem with deleting mapset in gis_set.py. Do not know why it looks for file in dist.x86_64-pc-linux-gnu. And this file really does not exist.

It looks for it there because you told it to do so, did you read my comment with the link I sent you?

Oh. There are two similar variables gisbase and gisdbase and I omitted "d" by mistake and did not notice that before.

lindakladivova added 2 commits July 10, 2020 10:51
Copy link
Member

@wenzeslaus wenzeslaus left a comment

Choose a reason for hiding this comment

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

Great, this is almost finished! Just couple things to fix (and later merge with #761).

newmapset = dlg.GetValue()
try:
rename_mapset(grassdb, location, mapset, newmapset)
return newmapset
Copy link
Member

Choose a reason for hiding this comment

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

When you return here, it never goes to dlg.Destroy() (which may leave some stuff behind).

newlocation = dlg.GetValue()
try:
rename_location(grassdb, location, newlocation)
return newlocation
Copy link
Member

Choose a reason for hiding this comment

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

Same as for mapset.

message=_("Unable to create new mapset: %s") % err,
showTraceback=False,
)
return mapset
Copy link
Member

Choose a reason for hiding this comment

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

What happens when you press the Cancel button in the Create new mapset dialog?


def _locationAlreadyExists(self, ctrl):
message = _(
"Location '{}' already exists. Please consider to use "
Copy link
Member

Choose a reason for hiding this comment

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

"consider using"

@@ -565,7 +565,7 @@ def OnWizard(self, event):
defineRegion.Destroy()

if gWizard.user_mapset:
self.OnCreateMapset(event)
self.CreateMapset(event)
Copy link
Member

Choose a reason for hiding this comment

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

Is CreateMapset() defined?

GError(parent=self,
message=_("Unable to rename location: %s") % e,
showTraceback=False)
return False
Copy link
Member

Choose a reason for hiding this comment

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

Since this is an event handler (On...(self, event): used as ...Bind(wx.EVT_..., self...), nobody is checking any return value. That would make sense for function which is directly called and then you would have to consider if returning True/False or some other way is the best way to report success/failure (examples of this are rename_location_interactively() and rename_location()).

The code is wrong anyway because it is returning false on failure, but nothing on success (defaults to None in Python, but bad practice not being explicit reason being exactly this situation).

Just remove the return False/True. This applies to the other event handlers (typically On...(self, event):)

…pressing Cancel button in Create, Rename functions corrected. True/false returns removed.
@petrasovaa petrasovaa marked this pull request as ready for review July 13, 2020 03:06
@wenzeslaus wenzeslaus merged commit 6c125e9 into OSGeo:master Jul 14, 2020
New GUI Startup automation moved this from In progress to Done Jul 14, 2020
@neteler neteler added this to the 8.0.0 milestone Dec 9, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
gsoc Reserved for Google Summer of Code student(s)
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

[Feat] Add rename and delete of Location and Mapset in Datacatalog
5 participants