1 """
2 The SSAP core and supporting code.
3
4 """
5
6
7
8
9
10
11
12 import itertools
13
14 from gavo import base
15 from gavo import rsc
16 from gavo import rscdef
17 from gavo import svcs
18 from gavo.protocols import datalink
19 from gavo.svcs import outputdef
20
21
22 RD_ID = "//ssap"
23 MS = base.makeStruct
28
31 """SSA descriptors have ssaRow and limits attributes.
32
33 These both reference SSA results. ssaRow is the first result of
34 the query, which also provides the accref. limits is a table.Limits
35 instance for the total result set.
36
37 Warning: limits will be None if this is constructed with fromSSARow.
38 """
39 ssaRow = None
40
41 @classmethod
43 """returns a descriptor from a row in an ssa table and
44 the params of that table.
45
46 Don't use this; the limits attribute will be {} for these.
47 """
48 paramDict.update(ssaRow)
49 ssaRow = paramDict
50 res = cls.fromAccref(ssaRow["ssa_pubDID"], ssaRow['accref'])
51 res.ssaRow = ssaRow
52 res.limits = {}
53 return res
54
55 @classmethod
57 """returns a descriptor from an SSA query result (an InMemoryTable
58 instance).
59 """
60 if not ssaResult.rows:
61 raise base.NotFoundError("any", "row", "ssa result for SODA block"
62 " generation")
63 res = cls.fromSSARow(ssaResult.rows[0], ssaResult.getParamDict())
64 res.limits = ssaResult.getLimits()
65 res.pubDID = None
66 return res
67
70 """makes a "total row" from ssaRows.
71
72 In the resulting row, minima and maxima are representative of the
73 whole result set, and enumerated columns are set-valued.
74
75 This is useful when generating parameter metadata.
76 """
77
78
79 if not ssaRows:
80 raise base.ReportableError("Datalink meta needs at least one result row")
81
82 totalRow = ssaRows[0].copy()
83 totalRow["mime"] = set([totalRow["mime"]])
84 calibs = set()
85
86 for row in ssaRows[1:]:
87 if row["ssa_specstart"]<totalRow["ssa_specstart"]:
88 totalRow["ssa_specstart"] = row["ssa_specstart"]
89 if row["ssa_specend"]>totalRow["ssa_specend"]:
90 totalRow["ssa_specend"] = row["ssa_specend"]
91 totalRow["mime"].add(row["mime"])
92 calibs.add(row.get("ssa_fluxcalib", None))
93
94 totalRow["collect_calibs"] = set(c for c in calibs if c is not None)
95 return totalRow
96
106
109 """A core doing SSAP queries.
110
111 This core knows about metadata queries, version negotiation, and
112 dispatches on REQUEST. Thus, it may return formatted XML data
113 under certain circumstances.
114
115 Interpreted Properties:
116
117 * previews: If set to "auto", the core will automatically add a preview
118 column and fill it with the URL of the products-based preview. Other
119 values are not defined.
120 """
121 name_ = "ssapCore"
122
123 outputTableXML = """
124 <outputTable verbLevel="30">
125 <property name="virtual">True</property>
126 <FEED source="//ssap#coreOutputAdditionals"/>
127 </outputTable>"""
128
129 previewColumn = base.parseFromString(svcs.OutputField,
130 '<outputField name="preview" type="text"'
131 ' ucd="meta.ref.url;datalink.preview" tablehead="Preview"'
132 ' description="URL of a preview for the dataset"'
133 ' select="NULL" displayHint="type=product" verbLevel="15"/>')
134
135
136
137
142
154
156 for row in resultTable:
157 if "preview" in row and row["preview"] is None:
158 row["preview"] = row["accref"]+"?preview=True"
159
161 limitThroughTOP = inputTable.getParam("TOP")
162 if limitThroughTOP and limitThroughTOP<queryMeta["dbLimit"]:
163 queryMeta["dbLimit"] = limitThroughTOP
164
165 res = svcs.DBCore.run(self, service, inputTable, queryMeta)
166 if self.getProperty("previews", default=False)=="auto":
167 self._addPreviewLinks(res)
168
169 if service.hasProperty("datalink"):
170
171
172
173
174 try:
175 res.getMeta("_associatedDatalinkService", raiseOnFail=True)
176 except base.NoMetaKey:
177
178 res.addMeta(
179 "_associatedDatalinkService.serviceId",
180 service.getProperty("datalink"))
181 res.addMeta(
182 "_associatedDatalinkService.idColumn",
183 "ssa_pubDID")
184
185 return res
186
187
188
189 - def run(self, service, inputTable, queryMeta):
190 defaultRequest = service.getProperty("defaultRequest", "")
191 requestType = (inputTable.getParam("REQUEST") or defaultRequest).upper()
192 if requestType=="QUERYDATA":
193 return self._run_queryData(service, inputTable, queryMeta)
194 elif requestType=="GETTARGETNAMES":
195 return self._run_getTargetNames(service, inputTable, queryMeta)
196 else:
197 raise base.ValidationError("Missing or invalid value for REQUEST.",
198 "REQUEST")
199
200
201 _VIEW_COLUMNS_CACHE = []
204 """returns a list of column objects for building the SSA view
205 mixin's columns.
206
207 The argument is the DaCHS RD parse context.
208
209 This is probably only useful for the //ssap#view mixin. The argument
210 is that mixin's context. This could go if we drop the hcd and mixc
211 mixins and instead have a normal STREAM with the columns, as it's really
212 only necessary to make columns from the stupid params and remove their
213 defaults.
214
215 This will always return the same column objects -- don't change them.
216 """
217 if not _VIEW_COLUMNS_CACHE:
218 dontCopyAtts = frozenset(["value", "content_"])
219 protoTable = context.getById("instance")
220
221 _VIEW_COLUMNS_CACHE.append([MS(rscdef.Column,
222 **c.getCopyableAttributes(ignoreKeys=dontCopyAtts))
223 for c in itertools.chain(
224 protoTable.columns, protoTable.params)])
225
226 return _VIEW_COLUMNS_CACHE[0]
227