Source code for gavo.dm.annotations

"""
The specialised annotations for the various entities of VO-DML.

As it's needed for the definition of models, the annotation of immediate
atoms is already defined in common; also see there for the base class
of these.
"""

#c Copyright 2008-2023, the GAVO project <gavo@ari.uni-heidelberg.de>
#c
#c This program is free software, covered by the GNU GPL.  See the
#c COPYING file in the source distribution.


# topnote[1]: copying TableRelativeAnnotations is probably not useful in
# the typical case; when you copy an annotation, that's probably because
# you copied a table, and hence your columns and params are different
# from the original table.  When copying the annotations, they will
# still point to the old instances.  Still, for consistency, I'm
# implementing the copy methods here.


import weakref

from gavo.dm import common
from gavo.votable import V


[docs]class ParamLikeAnnotation(common.TableRelativeAnnotation): def __init__(self, name, paramlike, instance): super().__init__(name, instance) self.weakref = weakref.ref(paramlike) paramlike.dmRoles.append(weakref.ref(self)) def __repr__(self): dest = self.weakref() if dest is None: return f"<defunct {self.name} annotation>" else: return f"<{self.name} {self.__class__.__name__} of {dest.name}>" @property def value(self): return self.weakref()
[docs] def copy(self, newInstance): # see topnote(1) return self.__class__(self.name, self.weakref(), newInstance)
[docs]class ColumnAnnotation(ParamLikeAnnotation): """An annotation of a table column. These reference DaCHS columns. """
[docs] def getVOT(self, ctx, instance): ctx.curDMAttr( ref=ctx.getOrMakeIdFor(self.value, suggestion=getattr(instance, "name", None))) return None # no new element generated
[docs]class ParamAnnotation(ParamLikeAnnotation): """An annotation of a table param. NOTE: in getVOT, container MUST be the table itself, as the table has params of its own and does *not* share tableDef's one. """
[docs] def getVOT(self, ctx, container): referenced = container.getParamByName(self.value.name) ctx.curDMAttr(ref=ctx.getOrMakeIdFor(referenced, suggestion=getattr(self.value, "name", None))) return None # no new element generated
def _the(gen): """returns the first thing the generator gen spits out and makes sure there's nothing more """ res = next(gen) try: extra = next(gen) except StopIteration: return res raise TypeError("Generator expected to only return one thing returned" " extra %s"%repr(extra))
[docs]class GroupRefAnnotation(common.TableRelativeAnnotation): """An annotation always referencing a group that's not lexically within the parent. """ def __init__(self, name, objectReferenced, instance): common.TableRelativeAnnotation.__init__(self, name, instance) self.objectReferenced = objectReferenced
[docs] def copy(self, newInstance): # see topnote(1) return self.__class__(self.name, self.objectReferenced, newInstance)
[docs] def getVOT(self, ctx, instance): if id(self.objectReferenced) not in ctx.groupIdsInTree: ctx.vodmlTemplates[ self.objectReferenced.getVOT(ctx, instance)( ID=ctx.getOrMakeIdFor(self.objectReferenced))] ctx.groupIdsInTree.add(id(self.objectReferenced)) return V.REFERENCE(dmref=ctx.getIdFor(self.objectReferenced))
[docs]class ForeignKeyAnnotation(common.TableRelativeAnnotation): """An annotation pointing to an annotation in a different table. These are constructed with the attribute name and the foreign key RD object. """ def __init__(self, name, fk, instance): common.TableRelativeAnnotation.__init__(self, name, instance) self.value = weakref.proxy(fk)
[docs] def copy(self, newInstance): return self.__class__(self.name, self.value, newInstance)
[docs] def getVOT(self, ctx, instance): # the main trouble here is: What if there's multiple foreign keys # into destTD? To prevent multiple inclusions of a single # table, we add a reference to our serialised VOTable stan in # destTD's _FKR_serializedVOT attribute. That will fail # if we produce two VOTables from the same table at the same time, # but let's worry about that later. destTD = self.value.inTable srcTD = self.value.parent raise NotImplementedError("Do not know how to annotate a foreign key") pkDecl = V.GROUP(dmrole="vo-dml:ObjectTypeInstance.ID")[[ V.FIELDref(ref=ctx.getOrMakeIdFor( destTD.tableDef.getColumnByName(colName))) for colName in self.foreignKey.dest]] pkDecl(ID=ctx.getOrMakeIdFor(pkDecl)) fkDecl = V.GROUP(ref=ctx.getOrMakeIdFor(pkDecl), dmtype="vo-dml:ORMReference")[ [V.FIELDref(ref=ctx.getIdFor(srcTD.getColumnByName(colName))) for colName in self.foreignKey.source]] targetVOT = getattr(destTD, "_FKR_serializedVOT", lambda: None)() # weakrefs are None if expired if targetVOT is None: targetVOT = ctx.makeTable(destTD) destTD._FKR_serializedVOT = weakref.ref(targetVOT) ctx.getEnclosingResource()[targetVOT] targetVOT[pkDecl] return fkDecl