Package gavo :: Package svcs :: Module outputdef
[frames] | no frames]

Source Code for Module gavo.svcs.outputdef

  1  """ 
  2  Output tables and their components. 
  3  """ 
  4   
  5  #c Copyright 2008-2019, the GAVO project 
  6  #c 
  7  #c This program is free software, covered by the GNU GPL.  See the 
  8  #c COPYING file in the source distribution. 
  9   
 10   
 11  import fnmatch 
 12   
 13  from gavo import base  
 14  from gavo import rscdef  
 15  from gavo import utils  
 16   
 17  _EMPTY_TABLE = base.makeStruct(rscdef.TableDef, id="<builtin empty table>") 
 18  _EMPTY_TABLE.getFullId = lambda: None 
19 20 21 -class OutputField(rscdef.Column):
22 """A column for defining the output of a service. 23 24 It adds some attributes useful for rendering results, plus functionality 25 specific to certain cores. 26 27 The optional formatter overrides the standard formatting code in HTML 28 (which is based on units, ucds, and displayHints). You receive 29 the item from the database as data and must return a string or 30 nevow stan. In addition to the standard `Functions available for 31 row makers`_ you have queryMeta and nevow's tags in T. 32 33 Here's an example for generating a link to another service using this 34 facility:: 35 36 <outputField name="more" 37 select="array[centerAlpha,centerDelta] as more" tablehead="More" 38 description="More exposures near the center of this plate"> 39 <formatter><![CDATA[ 40 return T.a(href=base.makeSitePath("/lswscans/res/positions/q/form?" 41 "POS=%s,%s&SIZE=1&INTERSECT=OVERLAPS&cutoutSize=0.5" 42 "&__nevow_form__=genForm"%tuple(data) 43 ))["More"] ]]> 44 </formatter> 45 </outputField> 46 47 Within the code, in addition do data, you see rd and queryMeta. 48 """ 49 name_ = "outputField" 50 51 52 _formatter = base.UnicodeAttribute("formatter", description="Function" 53 " body to render this item to HTML.", copyable=True, expand=True) 54 _wantsRow = base.BooleanAttribute("wantsRow", description="Does" 55 " formatter expect the entire row rather than the colum value only?", 56 copyable="True") 57 _select = base.UnicodeAttribute("select", description="Use this SQL" 58 " fragment rather than field name in the select list of a DB based" 59 " core.", default=base.Undefined, copyable=True, expand=True) 60 _sets = base.StringSetAttribute("sets", description= 61 "Output sets this field should be included in; ALL includes the field" 62 " in all output sets.", 63 copyable=True) 64
65 - def __repr__(self):
66 return "<OutputField %s>"%repr(self.name)
67
68 - def completeElement(self, ctx):
69 if self.restrictedMode and ( 70 self.formatter 71 or self.select): 72 raise base.RestrictedElement(self.name_, hint="formatter and select" 73 " attributes on output fields are not allowed in restricted mode.") 74 if self.select is base.Undefined: 75 self.select = self.name 76 self._completeElementNext(OutputField, ctx)
77 78 @classmethod
79 - def fromColumn(cls, col):
80 res = cls(None, **col.getAttributes(rscdef.Column)) 81 res.stc = col.stc 82 res.dmRoles = rscdef.OldRoles(col.dmRoles) 83 return res.finishElement()
84
85 - def expand(self, *args, **kwargs):
86 return self.parent.expand(*args, **kwargs)
87
88 89 -class OutputTableDef(rscdef.TableDef):
90 """A table that has outputFields for columns. 91 92 Cores always have one of these, but they are implicitly defined by 93 the underlying database tables in case of dbCores and such. 94 95 Services may define output tables to modify what is coming back fromt 96 the core. Note that this usually only affects the output to web browsers. 97 To use the output table also through VO protocols (and when producing 98 VOTables, FITS files, and the like), you need to set the service's 99 votableRespectsOutputTable property to True. 100 """ 101 name_ = "outputTable" 102 103 # Don't validate meta for these -- while they are children 104 # of validated structures (services), they don't need any 105 # meta at all. This should go as soon as we have a sane 106 # inheritance hierarchy for tables. 107 metaModel = None 108 109 _cols = rscdef.ColumnListAttribute("columns", 110 childFactory=OutputField, 111 description="Output fields for this table.", 112 aliases=["column"], 113 copyable=True) 114 115 _verbLevel = base.IntAttribute("verbLevel", 116 default=None, 117 description="Copy over columns from fromTable not" 118 " more verbose than this.") 119 120 _autocols = base.StringListAttribute("autoCols", 121 description="Column names obtained from fromTable; you can use" 122 " shell patterns into the output table's parent table (in a table" 123 " core, that's the queried table; in a service, it's the core's" 124 " output table) here.") 125
126 - def __init__(self, parent, **kwargs):
127 rscdef.TableDef.__init__(self, parent, **kwargs) 128 self.parentTable = None 129 try: 130 # am I in a table-based core? 131 self.parentTable = self.parent.queriedTable 132 except (AttributeError, base.StructureError): 133 # no. 134 pass 135 136 if not self.parentTable: 137 try: 138 # am I in a service with a core with output table? 139 self.parentTable = self.parent.core.outputTable 140 except (AttributeError, base.StructureError): 141 # no. 142 pass 143 144 if not self.parentTable: 145 # no suitable column source, use an empty table: 146 self.parentTable = _EMPTY_TABLE 147 148 self.namePath = None
149
150 - def _adoptColumn(self, sourceColumn):
151 # Do not overwrite existing fields here to let the user 152 # override individually 153 try: 154 self.getColumnByName(sourceColumn.name) 155 except base.NotFoundError: 156 self.feedObject("outputField", OutputField.fromColumn(sourceColumn))
157
158 - def _addNames(self, ctx, names):
159 # since autoCols is not copyable, we can require 160 # that _addNames only be called when there's a real parse context. 161 if ctx is None: 162 raise base.StructureError("outputTable autocols is" 163 " only available with a parse context") 164 for name in names: 165 self._addName(ctx, name)
166
167 - def _addName(self, ctx, name):
168 """adopts a param or column name into the outputTable. 169 170 name may be a reference or a param or column name in the parent 171 table (as determined in the constructor, i.e., the queried table 172 of a core or the output table of a service's core. 173 174 You can also use shell patterns into parent columns. 175 """ 176 if utils.identifierPattern.match(name): 177 refOb = ctx.resolveId(name, self) 178 if refOb.name_=="param": 179 self.feedObject("param", refOb.copy(self)) 180 else: 181 self._adoptColumn(refOb) 182 183 else: 184 # it's a shell pattern into parent table 185 for col in self.parentTable: 186 if fnmatch.fnmatch(col.name, name): 187 self._adoptColumn(col)
188
189 - def completeElement(self, ctx):
190 if self.autoCols: 191 self._addNames(ctx, self.autoCols) 192 193 if self.verbLevel: 194 table = self.parentTable 195 for col in table.columns: 196 if col.verbLevel<=self.verbLevel: 197 self._adoptColumn(col) 198 for par in table.params: 199 if par.verbLevel<=self.verbLevel: 200 self.feedObject("param", par.copy(self)) 201 202 self._completeElementNext(OutputTableDef, ctx)
203 204 @classmethod
205 - def fromColumns(cls, columns, **kwargs):
206 return rscdef.TableDef.fromColumns([OutputField.fromColumn(c) 207 for c in columns])
208 209 @classmethod
210 - def fromTableDef(cls, tableDef, ctx=None):
211 res = cls(None, columns=[OutputField.fromColumn(c) for c in tableDef], 212 forceUnique=tableDef.forceUnique, dupePolicy=tableDef.dupePolicy, 213 primary=tableDef.primary, params=tableDef.params).finishElement(ctx) 214 res.copyMetaFrom(tableDef) 215 return res
216