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

Distinguish mapsets by ownership and lock in Data tab #849

Conversation

lindakarlovska
Copy link
Contributor

@lindakarlovska lindakarlovska commented Jul 29, 2020

In the startup screen it would be nice if mapsets could be displayed differently (e.g. different colors, for "my mapsets", "my locked mapsets", "others mapsets").

Mapsets owned by different user and locked mapsets will be displayed in gray and distinguished from "normal mapsets" by special label.

…er to get id for label and function OnGetItemNote which should change the node label according to gislock and user cases.
Comment on lines 94 to 110
def get_user_if_different(mapset_path):
"""Return user if different, None otherwise.

Returns the name as a string or None if nothing was found, so the
return value can be used to test if mapset belongs to another user.
"""
if os.environ.get("GRASS_SKIP_MAPSET_OWNER_CHECK", None):
# Mapset just needs to be accessible for writing.
return os.access(mapset_path, os.W_OK)
# Mapset needs to be owned by user.
stat_info = os.stat(mapset_path)
mapset_uid = stat_info.st_uid
if mapset_uid != os.getuid():
return os.getuid()
return None


Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of this, it should be more get_mapset_owner, which will return the owner even if it's the the user.

Comment on lines 600 to 616
def OnGetItemNote(self, index):
"""Overriden method to return note for each mapset.
Used to distinguish mapsets by lock and ownership."""
node = self._model.GetNodeByIndex(index)
node.label = node.data['name']
if node.data['type'] in ('mapset'):
mapset_path = os.path.join(node.parent.parent.data['name'],
node.parent.data['name'],
node.data['name'])
user = get_user_if_different(mapset_path)
if is_mapset_locked(mapset_path):
node.label = _("<{label}> (locked)").format(mapset=node.label)
if user:
node.label = _("<{label}> ({user})").format(mapset=node.label,
user=user)
return node.label

Copy link
Contributor

Choose a reason for hiding this comment

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

OnGetItemSomething are defined methods in wx that need to be overriden, so you can't just create a new one.

The idea here is to add more info to node.data, e.g. node.data['lock'], and then change DataCatalogNode.label method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No sure where to add more information to node.data. Where is e.g. node.data['name'] defined?

Copy link
Contributor

Choose a reason for hiding this comment

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

No sure where to add more information to node.data. Where is e.g. node.data['name'] defined?

I haven't delved deeply into this code, yet, but IIUC, these are defined whenever you create a dictionary for a tree node, e.g. on lines 336ff (in this PR):

            mapset_node = self._model.AppendNode(
                                parent=location_node,
                                data=dict(type='mapset',
                                          name=mapset,
                                          lock=is_mapset_locked(mapset_path),
                                          user=different_user))

So, "lock" seems to be defined for mapset nodes whenever they are created (also elsewhere in the code). But AFAICT location_nodes do not include the "lock" key.

Copy link
Contributor

Choose a reason for hiding this comment

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

OnGetItemSomething are defined methods in wx that need to be overriden, so you can't just create a new one.

The idea here is to add more info to node.data, e.g. node.data['lock'], and then change DataCatalogNode.label method.

IIUC, this is true for many other OnGetItem method that you propose.

@petrasovaa petrasovaa added gsoc Reserved for Google Summer of Code student(s) GUI wxGUI related labels Jul 30, 2020
@petrasovaa petrasovaa added this to PRs in progress in New GUI Startup via automation Jul 30, 2020
@petrasovaa petrasovaa linked an issue Jul 30, 2020 that may be closed by this pull request
@lindakarlovska lindakarlovska marked this pull request as draft July 30, 2020 12:45
@lindakarlovska
Copy link
Contributor Author

In my opinion, there is a problem that only for mapset node I defined "lock" and "user" keys. But other nodes do not have these keys. The error is:
Screenshot from 2020-07-31 10-58-05

if node.data['lock'] == True or node.data['user']:
text_colour.SetTextColour(wx.SYS_COLOUR_GRAYTEXT)
else:
text_colour.SetTextColour(wx.SYS_COLOUR_WINDOWTEXT)
Copy link
Contributor

Choose a reason for hiding this comment

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

You need to return directly the wx.SYS....

Delete '== True '

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Edited.

gui/wxpython/datacatalog/tree.py Show resolved Hide resolved
data=dict(type='mapset', name=mapset))
data=dict(type='mapset',
name=mapset,
lock=locked,
Copy link
Contributor

Choose a reason for hiding this comment

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

Just pass directly is_mapset_locked result

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Corrected.

…appending a mapset node in _reloadLocationNode, _reloadGrassDBNode, GetCurrentDbLocationMapsetNode. Returned directly wx.SYS colour.
@mlennert
Copy link
Contributor

mlennert commented Aug 3, 2020

In my opinion, there is a problem that only for mapset node I defined "lock" and "user" keys. But other nodes do not have these keys. The error is:
Screenshot from 2020-07-31 10-58-05

Yes, only mapset_nodes have "lock" defined. I'm not familiar enough with the code to be able to provide you with the optimum solution, but for now, can't you change the code so that it

In my opinion, there is a problem that only for mapset node I defined "lock" and "user" keys. But other nodes do not have these keys. The error is:
Screenshot from 2020-07-31 10-58-05

I can get further by working around the issue with something like this:

    @property
    def label(self):
        if self.data["type"] == "mapset":
            if self.data["lock"] and self.data["user"]:
                return self.data["name"] + " <user: {}>".format(self.data["user"]) + " (locked)"
            elif self.data["lock"]:
                return self.data["name"] + " (locked)"
            elif self.data["user"]:
                return self.data["name"] + " <user: {}>".format(self.data["user"])
            else:
                return self.data["name"]
        else:
            return self.data["name"]

but that seems a bit overly complicated to me. Maybe there needs to be some differentiation of methods depending on the type of node ?
When using the above code, I get the following error:

Exception in thread
Thread-1
:
Traceback (most recent call last):
  File "/usr/lib/python3.8/threading.py", line 932, in
_bootstrap_inner

self.run()
  File "/home/mlennert/SRC/GRASS/grass/dist.x86_64-pc-linux-
gnu/gui/wxpython/core/gthread.py", line 99, in run

ret = vars()['callable'](*args, **kwds)
  File "/home/mlennert/SRC/GRASS/grass/dist.x86_64-pc-linux-
gnu/gui/wxpython/datacatalog/tree.py", line 467, in
ReloadTreeItems

self._reloadTreeItems()
  File "/home/mlennert/SRC/GRASS/grass/dist.x86_64-pc-linux-
gnu/gui/wxpython/datacatalog/tree.py", line 452, in
_reloadTreeItems

self.RefreshItems()
  File "/usr/lib/python3/dist-
packages/wx/lib/mixins/treemixin.py", line 371, in
RefreshItems

self.RefreshChildrenRecursively(rootItem)
  File "/usr/lib/python3/dist-
packages/wx/lib/mixins/treemixin.py", line 395, in
RefreshChildrenRecursively

self.RefreshItemRecursively(child, childIndex)
  File "/usr/lib/python3/dist-
packages/wx/lib/mixins/treemixin.py", line 402, in
RefreshItemRecursively

item = self.DoRefreshItem(item, itemIndex, hasChildren)
  File "/usr/lib/python3/dist-
packages/wx/lib/mixins/treemixin.py", line 416, in
DoRefreshItem

self.RefreshTextColour(item, index)
  File "/usr/lib/python3/dist-
packages/wx/lib/mixins/treemixin.py", line 433, in
RefreshTextColour

self.__refreshAttribute(item, index, 'ItemTextColour')
  File "/usr/lib/python3/dist-
packages/wx/lib/mixins/treemixin.py", line 482, in
__refreshAttribute

value = getattr(self, 'OnGet%s'%attribute)(index, *args)
  File "/home/mlennert/SRC/GRASS/grass/dist.x86_64-pc-linux-
gnu/gui/wxpython/datacatalog/tree.py", line 647, in
OnGetItemTextColour

text_colour = self.GetTextColour()
AttributeError
:
'DataCatalogTree' object has no attribute 'GetTextColour'

which sounds like an issue linked to what Anna already highlighted above.

…lace. 'lock' and 'user' attribute are now defined only for mapsets. OnGetItemTextColour was corrected to return system colours.
@lindakarlovska
Copy link
Contributor Author

In my opinion, there is a problem that only for mapset node I defined "lock" and "user" keys. But other nodes do not have these keys. The error is:
Screenshot from 2020-07-31 10-58-05

Yes, only mapset_nodes have "lock" defined. I'm not familiar enough with the code to be able to provide you with the optimum solution, but for now, can't you change the code so that it

In my opinion, there is a problem that only for mapset node I defined "lock" and "user" keys. But other nodes do not have these keys. The error is:
Screenshot from 2020-07-31 10-58-05

I can get further by working around the issue with something like this:

    @property
    def label(self):
        if self.data["type"] == "mapset":
            if self.data["lock"] and self.data["user"]:
                return self.data["name"] + " <user: {}>".format(self.data["user"]) + " (locked)"
            elif self.data["lock"]:
                return self.data["name"] + " (locked)"
            elif self.data["user"]:
                return self.data["name"] + " <user: {}>".format(self.data["user"])
            else:
                return self.data["name"]
        else:
            return self.data["name"]

but that seems a bit overly complicated to me. Maybe there needs to be some differentiation of methods depending on the type of node ?
When using the above code, I get the following error:

Exception in thread
Thread-1
:
Traceback (most recent call last):
  File "/usr/lib/python3.8/threading.py", line 932, in
_bootstrap_inner

self.run()
  File "/home/mlennert/SRC/GRASS/grass/dist.x86_64-pc-linux-
gnu/gui/wxpython/core/gthread.py", line 99, in run

ret = vars()['callable'](*args, **kwds)
  File "/home/mlennert/SRC/GRASS/grass/dist.x86_64-pc-linux-
gnu/gui/wxpython/datacatalog/tree.py", line 467, in
ReloadTreeItems

self._reloadTreeItems()
  File "/home/mlennert/SRC/GRASS/grass/dist.x86_64-pc-linux-
gnu/gui/wxpython/datacatalog/tree.py", line 452, in
_reloadTreeItems

self.RefreshItems()
  File "/usr/lib/python3/dist-
packages/wx/lib/mixins/treemixin.py", line 371, in
RefreshItems

self.RefreshChildrenRecursively(rootItem)
  File "/usr/lib/python3/dist-
packages/wx/lib/mixins/treemixin.py", line 395, in
RefreshChildrenRecursively

self.RefreshItemRecursively(child, childIndex)
  File "/usr/lib/python3/dist-
packages/wx/lib/mixins/treemixin.py", line 402, in
RefreshItemRecursively

item = self.DoRefreshItem(item, itemIndex, hasChildren)
  File "/usr/lib/python3/dist-
packages/wx/lib/mixins/treemixin.py", line 416, in
DoRefreshItem

self.RefreshTextColour(item, index)
  File "/usr/lib/python3/dist-
packages/wx/lib/mixins/treemixin.py", line 433, in
RefreshTextColour

self.__refreshAttribute(item, index, 'ItemTextColour')
  File "/usr/lib/python3/dist-
packages/wx/lib/mixins/treemixin.py", line 482, in
__refreshAttribute

value = getattr(self, 'OnGet%s'%attribute)(index, *args)
  File "/home/mlennert/SRC/GRASS/grass/dist.x86_64-pc-linux-
gnu/gui/wxpython/datacatalog/tree.py", line 647, in
OnGetItemTextColour

text_colour = self.GetTextColour()
AttributeError
:
'DataCatalogTree' object has no attribute 'GetTextColour'

which sounds like an issue linked to what Anna already highlighted above.

I corrected this above-mentioned issue.

@lindakarlovska lindakarlovska added the enhancement New feature or request label Aug 5, 2020
@lindakarlovska lindakarlovska self-assigned this Aug 5, 2020
@lindakarlovska lindakarlovska marked this pull request as ready for review August 5, 2020 11:54
Copy link
Contributor

@petrasovaa petrasovaa left a comment

Choose a reason for hiding this comment

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

I think we need to treat the current mapset differently. Right now it is grey (and 'locked') and I think that's confusing, so I would add another key to data - data['current'] (that would be bool) and then use that in label() and OnGetItemTextColour to show it in black and with mymapset (current).

When you call AppendNode and specify data there, I would just use data['current'] = False and then in GetCurrentDbLocationMapsetNode you need to

  1. find all nodes type=mapset and set their data['current'] = False
  2. set the current mapset node data['current'] = True

Setting the other nodes to false is needed because you can switch current mapsets.

elif self.data["lock"]:
return self.data["name"] + " (locked)"
elif self.data["user"]:
return self.data["name"] + " <user: {}>".format(self.data["user"])
Copy link
Contributor

Choose a reason for hiding this comment

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

I would use "(owner: xxx)" and "(in use, owner: xxx)"
We thought "in use" might be more understandable than 'locked'

also, it should be translatable _()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed.

# Check mapset ownership
different_user = None
if not is_mapset_users(mapset_path):
different_user = get_mapset_owner(mapset_path)
Copy link
Contributor

Choose a reason for hiding this comment

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

You should probably have data["owner"] (that would be result of get_mapset_owner) and additionally data["is_different_owner"] (result of is_mapset_users - bool). Then just adjust the label() code. This is more cosmetics, but it makes sense more like that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

"Check mapset ownership" part removed and mapset_node dictionary (lock, is_different_owner, owner) set. All decisions about what label to use are now made in @Property function.

Comment on lines 626 to 629
name=mapset,
type='mapset',
lock=is_mapset_locked(mapset_path),
user=different_user)
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is needed, it will search the nodes based on name and mapset, that's enough.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Simplified.

lindakladivova added 2 commits August 8, 2020 05:07
…er, InserMapset edited to insert also other keys, 'current' mapset key implemented with function UpdateMapsetKeys.
…er, InserMapset edited to insert also other keys, 'current' mapset key implemented with function UpdateMapsetNodes.
@lindakarlovska
Copy link
Contributor Author

I think we need to treat the current mapset differently. Right now it is grey (and 'locked') and I think that's confusing, so I would add another key to data - data['current'] (that would be bool) and then use that in label() and OnGetItemTextColour to show it in black and with mymapset (current).

When you call AppendNode and specify data there, I would just use data['current'] = False and then in GetCurrentDbLocationMapsetNode you need to

1. find all nodes type=mapset and set their data['current'] = False

2. set the current mapset node data['current'] = True

Setting the other nodes to false is needed because you can switch current mapsets.

The first point is implemented in function UpdateMapsetNodes which is then used in _switchDbLocationMapset where I also performed the second point - set the current mapset node data['current'] = True.
Similarly those two lines self.UpdateMapsetNodes() and mapsetItem[0].data['current'] = True, are used in function GetCurrentDbLocationMapsetNode.

Comment on lines 61 to 62
def is_mapset_users(mapset_path):
"""Check if the mapset belongs to the user"""
def is_different_mapset_owner(mapset_path):
"""Check if the mapset belongs to the different owner than the current user."""
Copy link
Member

Choose a reason for hiding this comment

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

Why did you need to change is_mapset_users() into is_different_mapset_owner()? If it is just about clarity of the name then is_current_user_mapset_owner() might be better. If it is just about which case it reports as true, then mapset being owned by the current users is the primary or positive case, so I would have function for that and negate the result if need. (The point here is that this is a general functionality/API, not limited to data catalog.) If you really feel like function for the opposite case is needed, then you can wrap the existing one.

Besides the naming and what it is supposed to do, the current implementation is not correct: The GRASS_SKIP_MAPSET_OWNER_CHECK case does not correspond to is_different_mapset_owner().

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I preserved the function is_mapset_users() but renamed it to is_current_user_mapset_owner(). Then I made the wrapping function called is_different_mapset_owner().

gui/wxpython/datacatalog/tree.py Show resolved Hide resolved
return self.data["name"] + " <user: {}>".format(self.data["user"])
if self.data['type'] == 'mapset':
if self.data['current']:
return self.data['name'] + _(" (current)")
Copy link
Member

Choose a reason for hiding this comment

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

More general way to do this is:

_("{name} (current)").format(name=data["name"])

or making use of the fact data is a dictionary:

_("{data[name]} (current)").format(data=data)

or making use of the fact that the only values we are using are from the dictionary:

_("{name} (current)").format(**data)

It is more flexible and possibly also more readable as you see the basic structure of the string being constructed, but in the other cases it is also likely faster since format is already used there anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Rewritten to _("{name} (current)").format(**data).

lindakladivova and others added 3 commits August 9, 2020 03:08
…pset_owner, InserMapset edited to insert also other keys, 'current' mapset key implemented with function UpdateMapsetNodes."

This reverts commit 75b993f.
…served the function is_mapset_users() but renamed to is_current_user_mapset_owner(). New wrapper for opposite case is_different_mapset_owner() created. In function CreateMapset is now used _reloadMapsetNode function. UpdateCurrentMapsetNode is applies only for working with current mapset.UpdateMapsetInfo is the general function for updating mapset attributes.
@petrasovaa petrasovaa self-requested a review August 11, 2020 16:28
@petrasovaa petrasovaa merged commit 2aab3b6 into OSGeo:master Aug 11, 2020
New GUI Startup automation moved this from PRs in progress to Done Aug 11, 2020
@wenzeslaus
Copy link
Member

data_tab_current_in_use_owner

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request gsoc Reserved for Google Summer of Code student(s) GUI wxGUI related
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

[Feat] Distinguish mapsets by ownership and lock in Data tab
5 participants