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

ENH/REF: refactor shared table views #90

Merged
merged 22 commits into from
Oct 16, 2024

Conversation

tangkong
Copy link
Contributor

@tangkong tangkong commented Sep 27, 2024

Description

  • Refactors shared table models to have their own self-contained table views for ease of use and extensibility
  • Add methods to make columns editable for both shared table models
  • Adds ValueDelegate to provide constrained editing for columns based on their datatype and metadata
  • Add more metadata fields to EpicsData (precision, enums, limits, precision)
  • improve enum handling for LivePVTableView

Motivation and Context

Going about adding more functionality.

How Has This Been Tested?

Interactively, ensuring existing tests run

Where Has This Been Documented?

This PR
enum ValueDelegate
image

double ValueDelegate (must be < 0)
image

Pre-merge checklist

  • Code works interactively
  • Code follows the style guide
  • Code contains descriptive docstrings, including context and API
  • New/changed functions and methods are covered in the test suite where possible
  • Test suite passes locally
  • Test suite passes on GitHub Actions
  • Ran docs/pre-release-notes.sh and created a pre-release documentation page
  • Pre-release docs include context, functional descriptions, and contributors as appropriate

@tangkong tangkong requested a review from ZLLentz October 3, 2024 23:06
@tangkong tangkong marked this pull request as ready for review October 3, 2024 23:06
@tangkong
Copy link
Contributor Author

tangkong commented Oct 3, 2024

Not requesting a review from Devan, as he's on vacation. This was starting to snowball (as always), so I'll cut this off here. Everything was originally in service of making the cells editable. Lots of little things got hit along the way

@tangkong tangkong changed the title ENH/REF/WIP: refactor shared table views ENH/REF: refactor shared table views Oct 3, 2024
Copy link
Member

@ZLLentz ZLLentz left a comment

Choose a reason for hiding this comment

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

Partial review: I'll need to start again at the views definitions when I feel less tired

Comment on lines +40 to +41
value_time = await caget(address, format=dbr.FORMAT_TIME)
value_ctrl = await caget(address, format=dbr.FORMAT_CTRL)
Copy link
Member

Choose a reason for hiding this comment

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

Possibly we can do this in parallel for some minor performance gains to cut the network waiting time in half

not 100% sure on syntax etc

Suggested change
value_time = await caget(address, format=dbr.FORMAT_TIME)
value_ctrl = await caget(address, format=dbr.FORMAT_CTRL)
value_time, value_ctrl = await asyncio.gather(
caget(address, format=dbr.FORMAT_TIME),
caget(address, format=dbr.FORMAT_CTRL),
)

@ZLLentz
Copy link
Member

ZLLentz commented Oct 4, 2024

The result looks great though, next time I'll try to run it myself and read through the code with renewed energy

@ZLLentz
Copy link
Member

ZLLentz commented Oct 7, 2024

@tangkong what's the best way for me to run superscore and try this out myself?

@tangkong
Copy link
Contributor Author

tangkong commented Oct 7, 2024

This should work

$ export SUPERSCORE_CFG=/path/to/repo/superscore/superscore/tests/config.cfg
$ python -m superscore ui

@ZLLentz
Copy link
Member

ZLLentz commented Oct 7, 2024

Nothing in the code stands out as odd and there's a lot of qt stuff that's hard to parse through, so instead I'm going to reset my environment and approach this from a qa perspective

@ZLLentz
Copy link
Member

ZLLentz commented Oct 8, 2024

I'm not sure what I'm doing wrong, maybe I don't understand how to use the ui. I can't view any of the configs.

@tangkong
Copy link
Contributor Author

tangkong commented Oct 8, 2024

We don't have entry-specific pages for anything but the Collection yet, which itself is just a placeholder. Right now the only pages are the collection builder page and the search page.

What are you trying to get to that isn't working?

@ZLLentz
Copy link
Member

ZLLentz commented Oct 8, 2024

I guess I was expecting to be able to open a page and see one of these views in action

@tangkong
Copy link
Contributor Author

tangkong commented Oct 8, 2024

The collection builder page has these views and should be working.
image

Sorry I haven't been the most clear with how to test this here. If you navigate File -> New -> Collection, the collection-builder-page will open, from which you can add PVs and sub-collections. This by default doesn't let you edit the entries added, but the type-specific delegates should appear just fine

@ZLLentz
Copy link
Member

ZLLentz commented Oct 10, 2024

Ok I clicked around on the new collection gui
I couldn't find where to see the "stored value" thing I think because I couldn't figure out how to make a snapshot

Feel free to ignore any of these if they are already known or are pre-existing issues

  • Clicking tags+ crashes the gui
Traceback (most recent call last):
  File "/cds/home/z/zlentz/github/superscore/superscore/widgets/core.py", line 216, in add_tag
    elem = tags_list.add_item('')
  File "/cds/home/z/zlentz/github/superscore/superscore/widgets/core.py", line 297, in add_item
    self.data_list.append(starting_value)
AttributeError: 'QDataclassValueForobject' object has no attribute 'append'
Aborted (core dumped)
  • I get lots of qt.glx errors but it might be my environment
qt.glx: qglx_findConfig: Failed to finding matching FBConfig for QSurfaceFormat(version 2.0, options QFlags<QSurfaceFormat::FormatOption>(), depthBufferSize -1, redBufferSize 1, greenBufferSize 1, blueBufferSize 1, alphaBufferSize -1, stencilBufferSize -1, samples -1, swapBehavior QSurfaceFormat::SingleBuffer, swapInterval 1, colorSpace QSurfaceFormat::DefaultColorSpace, profile  QSurfaceFormat::NoProfile)
No XVisualInfo for format QSurfaceFormat(version 2.0, options QFlags<QSurfaceFormat::FormatOption>(), depthBufferSize -1, redBufferSize 1, greenBufferSize 1, blueBufferSize 1, alphaBufferSize -1, stencilBufferSize -1, samples -1, swapBehavior QSurfaceFormat::SingleBuffer, swapInterval 1, colorSpace QSurfaceFormat::DefaultColorSpace, profile  QSurfaceFormat::NoProfile)
Falling back to using screens root_visual.
  • It'd be nice to have a way to edit PVs that have already been added to a collection before saving it, I instead need to delete and add a new PV
  • Hitting save configuration twice gives this (the first save didn't seem to add it to the tree or make it findable via search). Maybe save configuration should close the edit gui or replace it with the view-only gui?
ERROR:superscore.widgets.views:Failed to set data (False) ->(0, 9): <LivePVHeader.REMOVE: 9>
ERROR:superscore.widgets.views:Failed to set data (False) ->(0, 8): <LivePVHeader.OPEN: 8>
INFO:superscore.widgets.page.collection_builder:Collection saved (10c5933d-79a3-46ff-8bd3-96a1352e3bf1)
qt.glx: qglx_findConfig: Failed to finding matching FBConfig for QSurfaceFormat(version 2.0, options QFlags<QSurfaceFormat::FormatOption>(), depthBufferSize -1, redBufferSize 1, greenBufferSize 1, blueBufferSize 1, alphaBufferSize -1, stencilBufferSize -1, samples -1, swapBehavior QSurfaceFormat::SingleBuffer, swapInterval 1, colorSpace QSurfaceFormat::DefaultColorSpace, profile  QSurfaceFormat::NoProfile)
No XVisualInfo for format QSurfaceFormat(version 2.0, options QFlags<QSurfaceFormat::FormatOption>(), depthBufferSize -1, redBufferSize 1, greenBufferSize 1, blueBufferSize 1, alphaBufferSize -1, stencilBufferSize -1, samples -1, swapBehavior QSurfaceFormat::SingleBuffer, swapInterval 1, colorSpace QSurfaceFormat::DefaultColorSpace, profile  QSurfaceFormat::NoProfile)
Falling back to using screens root_visual.
Traceback (most recent call last):
  File "/cds/home/z/zlentz/github/superscore/superscore/widgets/page/collection_builder.py", line 124, in save_collection
    self.client.save(self.data)
  File "/cds/home/z/zlentz/github/superscore/superscore/client.py", line 174, in save
    self.backend.save_entry(entry)
  File "/cds/home/z/zlentz/github/superscore/superscore/backends/filestore.py", line 271, in save_entry
    raise BackendError("Entry already exists, try updating the entry "
superscore.errors.BackendError: Entry already exists, try updating the entry instead of saving it
Aborted (core dumped)
  • After saving my config, closing, and re-opening, I now have a validation error on opening the config again
$ python -m superscore ui
Traceback (most recent call last):
  File "/cds/home/z/zlentz/miniconda3/envs/superscore/lib/python3.9/runpy.py", line 197, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/cds/home/z/zlentz/miniconda3/envs/superscore/lib/python3.9/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/cds/home/z/zlentz/github/superscore/superscore/__main__.py", line 3, in <module>
    main()
  File "/cds/home/z/zlentz/github/superscore/superscore/bin/main.py", line 97, in main
    func(**kwargs)
  File "/cds/home/z/zlentz/github/superscore/superscore/bin/ui.py", line 21, in main
    main_window = Window()
  File "/cds/home/z/zlentz/github/superscore/superscore/widgets/window.py", line 45, in __init__
    self.setup_ui()
  File "/cds/home/z/zlentz/github/superscore/superscore/widgets/window.py", line 56, in setup_ui
    self.tree_model = RootTree(base_entry=self.client.backend.root,
  File "/cds/home/z/zlentz/github/superscore/superscore/backends/filestore.py", line 256, in root
    with self._load_and_store_context():
  File "/cds/home/z/zlentz/miniconda3/envs/superscore/lib/python3.9/contextlib.py", line 119, in __enter__
    return next(self.gen)
  File "/cds/home/z/zlentz/github/superscore/superscore/backends/filestore.py", line 353, in _load_and_store_context
    db = self._load_or_initialize()
  File "/cds/home/z/zlentz/github/superscore/superscore/backends/filestore.py", line 63, in _load_or_initialize
    self._root = self.load()
  File "/cds/home/z/zlentz/github/superscore/superscore/backends/filestore.py", line 251, in load
    return deserialize(Root, serialized)
  File "/cds/home/z/zlentz/miniconda3/envs/superscore/lib/python3.9/site-packages/apischema/deserialization/__init__.py", line 887, in deserialize
    return deserialization_method(
  File "/cds/home/z/zlentz/miniconda3/envs/superscore/lib/python3.9/site-packages/apischema/deserialization/methods.py", line 697, in deserialize
    raise ValidationError(errors or [], field_errors or {})
apischema.validation.errors.ValidationError: ValidationError: [{'loc': ['entries', 4, 'Collection', 'description'], 'err': 'expected type string, found array'}, {'loc': ['entries', 4, 'Collection', 'title'], 'err': 'expected type string, found array'}]

Here's the diff from my db:

+    },
+    {
+      "Collection": {
+        "uuid": "10c5933d-79a3-46ff-8bd3-96a1352e3bf1",
+        "description": [
+          "Defo neater than your collection haha"
+        ],
+        "creation_time": "2024-10-09T23:28:21.315755+00:00",
+        "title": [
+          "My neat collection"
+        ],
+        "children": [
+          {
+            "uuid": "3de82331-0f13-4ed7-a668-0d33eae8cd1e",
+            "description": "",
+            "creation_time": "2024-10-09T23:32:00.149681+00:00",
+            "pv_name": "IM3L0:PPM:MMS:STATE:SET",
+            "abs_tolerance": null,
+            "rel_tolerance": null,
+            "readback": {
+              "uuid": "c092bad8-ada1-4efa-abd8-46306b0b7836",
+              "description": "",
+              "creation_time": "2024-10-09T23:32:00.149661+00:00",
+              "pv_name": "IM3L0:PPM:MMS:STATE:GET_RBV",
+              "abs_tolerance": null,
+              "rel_tolerance": null,
+              "readback": null,
+              "read_only": true
+            },
+            "read_only": false
+          }
+        ],
+        "tags": []
+      }

Note how the description and title are arrays of strings instead of strings

Anyway sorry for only having bugs to report, I think the snapshot gui from your screenshot above looks the most snazzy but I couldn't figure out how to get to it

@tangkong
Copy link
Contributor Author

No, thank you for the bug reports. Helps keep me honest. The editable delegates aren't immediately accessible, since the existing pages don't support editing values by design. I'm currently working on some additional pages that should expose this more clearly (hopefully)

I'll go through the bugs and see what I can fix. 👍

@tangkong
Copy link
Contributor Author

There were stray commas somehow, making strings into tuples. Validation errors and saving title/descriptions into lists should be fixed in ef4bc1f

@tangkong
Copy link
Contributor Author

Tags will remain broken until we actually implement the feature. Part of the problem is that the TagsWidget was expecting the dataclass field to be a list not a set, and the other part of the problem is that the field is a Set[Tag], where Tag is a Flag. We haven't tested serialization with these flags, and if we intend to centrally manage / restrict the tags that can be chosen, this widget will need further modifications.

All this to say I'm not going to work on this further for now. 😢

@tangkong
Copy link
Contributor Author

If you're interested in running a slightly modified collection builder page to see the delegates work, you can run this file:

python /cds/home/r/roberttk/test/superscore/enh_views_test.py

@tangkong
Copy link
Contributor Author

It has become abundantly clear that this was written during the heat wave. The quality is awful. Thank you for being patient. I've got many test cases to write, now that I've caught so many little bugs

@ZLLentz
Copy link
Member

ZLLentz commented Oct 10, 2024

I think this is probably mature enough to merge with some documentation of things to be fixed later

@tangkong
Copy link
Contributor Author

tangkong commented Oct 10, 2024

Thanks for the thorough QA. I'll make some followup issues and add some tests before re-requesting a review+stamp

ZLLentz
ZLLentz previously approved these changes Oct 16, 2024
Copy link
Member

@ZLLentz ZLLentz 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 I only had testing nitpicks

superscore/tests/test_views.py Outdated Show resolved Hide resolved
yield view

view.model().stop_polling()
qtbot.wait_until(lambda: not view.model()._poll_thread.isRunning())
Copy link
Member

Choose a reason for hiding this comment

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

This test has no assert statements. Is there nothing specific to test for in the basic behavior of getting values?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is actually a fixture that I use in the subsequent tests. In making it I did reveal to myself some smaller bugs, so in spirit it is also sort of like a test

Copy link
Member

Choose a reason for hiding this comment

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

new rule: no code reviews from me after 5pm

@tangkong
Copy link
Contributor Author

I'll merge this now, and try to help Devan either refactor his table usage or fix it up after he merges his effort.

@tangkong
Copy link
Contributor Author

Thanks everyone for the reviews!

@tangkong tangkong merged commit 6ef255c into pcdshub:master Oct 16, 2024
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants