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

Mivot writer #627

Open
wants to merge 48 commits into
base: main
Choose a base branch
from
Open

Mivot writer #627

wants to merge 48 commits into from

Conversation

lmichel
Copy link
Contributor

@lmichel lmichel commented Nov 30, 2024

API for creating and writing MIVOTY annotation

#. Create individual instances (INSTANCE) using the MivotInstance class
#. Wrap the annotations with the MivotAnnotations class
#. Insert the annotations into a VOtable by using the Astropy API (wrapped in the package logic).

The annotation builder does not check whether the XML conforms to any particular model.
It simply validates it against the MIVOT XML Schema if the xmlvalidator package if is installed.

The documentation has been split in 2 separate sections (reading and writing MIVOT)

@lmichel lmichel closed this Nov 30, 2024
@lmichel lmichel reopened this Dec 1, 2024
@lmichel lmichel closed this Dec 1, 2024
@lmichel lmichel reopened this Dec 2, 2024
Copy link

codecov bot commented Dec 2, 2024

Codecov Report

Attention: Patch coverage is 86.74033% with 24 lines in your changes missing coverage. Please review.

Project coverage is 82.93%. Comparing base (f765128) to head (01b6a5c).
Report is 15 commits behind head on main.

Files with missing lines Patch % Lines
pyvo/mivot/writer/annotations.py 82.11% 22 Missing ⚠️
pyvo/mivot/utils/xml_utils.py 75.00% 1 Missing ⚠️
pyvo/mivot/writer/instance.py 98.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #627      +/-   ##
==========================================
+ Coverage   82.30%   82.93%   +0.63%     
==========================================
  Files          72       75       +3     
  Lines        7430     7658     +228     
==========================================
+ Hits         6115     6351     +236     
+ Misses       1315     1307       -8     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@lmichel lmichel marked this pull request as ready for review December 2, 2024 17:18
Forced pull after having squashed tiny commits dues to CI errors
Copy link
Member

@bsipocz bsipocz left a comment

Choose a reason for hiding this comment

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

Some comments mostly focusing on documentation and on how the API is exposed.

Comment on lines 1 to 3
*********************
MIVOT (``pyvo.mivo``)
*********************
Copy link
Member

Choose a reason for hiding this comment

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

typo

Suggested change
*********************
MIVOT (``pyvo.mivo``)
*********************
**********************
MIVOT (``pyvo.mivot``)
**********************

blocks into VOTable files. The MIVOT block, represented as an XML structure, is used for
data model annotations in the IVOA ecosystem.

Features:
Copy link
Member

Choose a reason for hiding this comment

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

While I see the motivation for including some narrative docs with the source code, this section is not at all included in the rendered documentation as is not being part of any testing. So any information listed here likely need to be repeated and then kept in sync with the narrative docs. So my suggestion would be to remove the details from here and only have the narrative docs, as well as docstrings in any user facing functionality.

@lmichel
Copy link
Contributor Author

lmichel commented Dec 15, 2024

The 3.11 step failed on a Simbad TAP server error
I do not understand what the issue is with readthedoc, I guess it is a project-wide scope.

@bsipocz
Copy link
Member

bsipocz commented Dec 16, 2024

The simbad failure is unrelated and I see it in main, too. However the RTD is coming from this PR, I've added a patch to fix it.

This is ready for review for @astropy/pyvo-maintainers

@ManonMarchand
Copy link
Member

(on the current SIMBAD list of tables failure, the new table mes_otype is currently being tested by CDS's users to see if they find it practical. It might stay or be removed in the next months depending on their feedback)


This module contains the new feature of annotations in VOTable.
This module contains the new feature handling model annotations in VOTable.
Copy link
Contributor

Choose a reason for hiding this comment

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

This sounds better IMO.

Suggested change
This module contains the new feature handling model annotations in VOTable.
This module contains the new feature of handling model annotations in VOTable.

- MIVOT module: If the MIVOT annotation block contains a valid instance of the
``mango:EpochPosition`` class, the dynamic object describing the mapped
data can generate a valid SkyCoord instance. [#591]

- RegTAP constraints involving tables other than rr.resource are now
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm wondering what happened here since this is not your change.

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 do not understand the issue, this relates to 1.6.

Copy link
Member

Choose a reason for hiding this comment

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

I'm wondering what happened here since this is not your change.

a rebase or merge conflict resolution went wrong

------------------
The ``ModelViewer`` module manages access to data mapped to a model through dynamically
generated objects (``MivotInstance``class).
The example below shows how can be consumed a VOTable resulting from a cone-search query which data are mapped
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
The example below shows how can be consumed a VOTable resulting from a cone-search query which data are mapped
The example below shows how a VOTable result of a cone-search query can be parsed and data mapped

xml_view = mivot_viewer.xml_view
# do whatever you want with this XML element

It to be noted that ``mivot_viewer.xml_view`` is a shortcut
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
It to be noted that ``mivot_viewer.xml_view`` is a shortcut
It is to be noted that ``mivot_viewer.xml_view`` is a shortcut

value="BARYCENTER",
)
space_frame.add_instance(ref_position)
space_sys.add_instance(space_frame)
Copy link
Contributor

@andamian andamian Jan 22, 2025

Choose a reason for hiding this comment

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

I feel like this is too low level API for it to gain traction. My idea of this (as I probably mentioned to you in the past) would be to have the supported models part of this library. Users only have to define the mapping to the column names, maybe the units and the global values and a utility generates the corresponding json that goes into the header and the rest of the result content is streamed from the database. The utility can take an optional VOTable header to check if the columns in the mapping exist in the table. But all the other details (dmrole, dmtype, etc) are internal to the Model classes and not exposed to the user.

I would have preferred a simple example of a writer for SkyCoords where a utility requires the mapping of SkyCoords fields to column names but can generate the other details the MIVOT uses. Can that be a goal? Is it possible to implement?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Providing a user-friendly API is the final goal of this work but it requires a low-level API allowing to build annotations step by step which is the object of this PR.

I've another PR on the track allowing to add in one callback time frames, space frames and photometric filters, defined as instances of classes of Coordinates DM and PhotDM respectively.

I propose to first merge this incoming work with #627, but this not enough to build annotations on the fly as you suggest.
I'll add another endpoint(s) allowing to add MANGO objects even if MANGO is not a REC yet. I assume that possible requested changes won't alter too much the code.

I propose to start with the 6 parameters EpochPosition, the Brightness (mag or flux) and the Color.

The API will look like:

votable = parse(votable_path)
annotations = Annotation()
annotations.add_epoch_propagation(sky_position, space_frame, time_frame, correlation, errors)
# something similar for the magnitude/color
annotations.build_mivot_block()
annotations.insert_into_votable(votable)
  • the parameters of add_epoch_propagation are basically dictionaries containing tuples connecting model leaves with columns identifiers or literal values.
  • this API could be extended with an automatic column discovery at least for columns having unambiguous UCDs

This work will take a bit of time however.
If you agree, the PR can be frozen meanwhile it is merged with that future code.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks. It makes sense. Maybe just mention in the documentation that this is intended for developers of DM classes that want them to be annotated to VOTables and not for the end users.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Copy link
Member

@bsipocz bsipocz left a comment

Choose a reason for hiding this comment

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

@andamian - I believe all your comments have been addressed. Do you think it's good to go now?

When it gets the green light, I'll go ahead and rebase to get rid of the merge commits and the weirdness in the changelog.

- MIVOT module: If the MIVOT annotation block contains a valid instance of the
``mango:EpochPosition`` class, the dynamic object describing the mapped
data can generate a valid SkyCoord instance. [#591]

- RegTAP constraints involving tables other than rr.resource are now
Copy link
Member

Choose a reason for hiding this comment

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

I'm wondering what happened here since this is not your change.

a rebase or merge conflict resolution went wrong

Comment on lines +18 to +21
try:
from astropy.io.votable.tree import MivotBlock
except ImportError:
pass
Copy link
Member

Choose a reason for hiding this comment

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

maybe add a comment here that this try/except should go away once we don't need to do check_astropy_version and require astropy >=6.0

Copy link
Contributor

@andamian andamian left a comment

Choose a reason for hiding this comment

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

Sorry. I've had these comments on Pending - just forgot to submit review. I'm doing it now.

@@ -1,2 +1 @@
# package entry point
from .viewer.mivot_viewer import MivotViewer
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this because MivotViewer is not part of the user facing API and just meant for library developers?

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 now have 2 different features (read and write). I thought it was better to put the default imports at sub-package level (viewer, writer).
I took example on pyvo.io.
Is that not correct?

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 let it as it is


Attributes
----------
_models : dict
Copy link
Contributor

Choose a reason for hiding this comment

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

These are all private attributes. Document them online when they are declared maybe?

"""
return self._mivot_block

def _get_report(self):
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't all MIVOT components represented by classes should have _to_xml methods to get their representations? It might be more consistent and easier to remember. Just a suggestion.

Copy link
Contributor Author

@lmichel lmichel Feb 18, 2025

Choose a reason for hiding this comment

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

This method just returns one component of the MIVOT block: <REPORT...\> as do the following ones for MODELS/GLOBALS/TEMPLATES.
_to_xml is used for model components such as <INSTANCE>
These methods are used internally to pack the MIVOT block.
I can add add an _xml in their names (e.g. _get_xml_report) if you think it will make the code more clear but in my opinion this not a good option because these methods return strings not XML elements (etree).

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 let it as it is.

@@ -0,0 +1,160 @@
# Licensed under a 3-clause BSD style license - see LICENSE.rst
"""
MivotInstance is a simple API for building MIVOT instances step by step.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you detail in the description what a MIVOT instance is?

Free identifier of the instance

"""
def __init__(self, dmtype=None, *, dmrole=None, dmid=None):
Copy link
Contributor

Choose a reason for hiding this comment

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

If dmtype is required it shouldn't have a default.

"""
if not dmtype:
raise MappingError("Cannot build an instance without dmtype")
self._dmtype = dmtype
Copy link
Contributor

Choose a reason for hiding this comment

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

I invite the others to chime in but I've started using a rule that we are using in Java. If the ctor argument is required, then the corresponding class attribute is read-only and uses a property to enforce that. If it's optional, then it's a read/write attribute and the attribute should be public. In the case above:

def __init__(self, dmtype, *, dmrole=None, dmid=None):
   self._dmtype = dmtype  # check dmtype is correct
   self.dmrole = droll
   self dmid = dmid

@property
def dmtype(self):
    return self._dmtype
'''
What do you think?

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'm using this pattern for read-only properties (e.g. MivotAnnotation.mivot_block) but in this case, all the class attributes are set at instantiation time are not meant to changed later or even read from outside (apart of the tests).
Do you think it worth it to add getters for them?

Copy link
Contributor

Choose a reason for hiding this comment

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

Probably not. Only for those that the caller might need access to.

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 let it as it is

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants