1 """
2 Generation of VODataService 1.1 tablesets from resources.
3
4 Fudge note: sprinkled in below are lots of lower()s for column names and the
5 like. These were added for the convenience of TAP clients that may
6 want to use these names quoted. Quoted identifiers match regular identifiers
7 only if case-normalized (i.e., all-lower in DaCHS).
8 """
9
10
11
12
13
14
15
16 import itertools
17
18 from gavo import base
19 from gavo import rscdef
20 from gavo import svcs
21 from gavo import utils
22 from gavo.registry.model import VS
23
24
25 _VOTABLE_TO_SIMPLE_TYPE_MAP = {
26 "char": "char",
27 "bytea": "char",
28 "unicodeChar": "char",
29 "short": "integer",
30 "int": "integer",
31 "long": "integer",
32 "float": "real",
33 "double": "real",
34 }
35
42
43
51
52
63
64
66 """returns a VS.foreignKey for a rscdef.ForeignKey.
67
68 If the target table's name is not in nameInSet, the foreign key
69 is not created.
70 """
71 targetName = fk.destTableName
72 if targetName not in namesInSet:
73 return None
74
75 return VS.foreignKey[
76 VS.targetTable[targetName], [
77 VS.fkColumn[
78 VS.fromColumn[fromColName.lower()],
79 VS.targetColumn[toColName.lower()]]
80 for fromColName,toColName in zip(fk.source, fk.dest)]]
81
82
84 """returns a VS.column instance for an rscdef.Column instance.
85
86 typeElement is a factory for types that has to accept an internal (SQL)
87 type as child and generate whatever is necessary from that.
88
89 This module contains simpleDataTypeFactory, voTableDataTypeFactory,
90 and should soon contain tapTypeFactory.
91 """
92 if isinstance(column.name, utils.QuotedName):
93 colName = str(column.name)
94 else:
95 colName = column.name.lower()
96
97 flags = []
98 if column.isIndexed():
99 flags.append("indexed")
100 if column.isPrimary():
101 flags.append("primary")
102 elif not column.required:
103 flags.append("nullable")
104
105 return VS.column[
106 VS.name[colName],
107 VS.description[column.description],
108 VS.unit[column.unit],
109 VS.ucd[column.ucd],
110 VS.utype[column.utype],
111 typeFactory(column.type, column.xtype),
112 [VS.flag[f] for f in flags]]
113
114
116 """returns the "effective name" of tableDef.
117
118 This is mainly for fudging the names of output tables since,
119 by default, they're ugly (and meaningless on top of that).
120 """
121 if isinstance(tableDef, svcs.OutputTableDef):
122 return "output"
123 else:
124 return tableDef.getQName().lower()
125
126
129 """returns a VS.table instance for a rscdef.TableDef.
130
131 namesInSet is a set of lowercased qualified table names; we need this
132 to figure out which foreign keys to create.
133 """
134 name = getEffectiveTableName(tableDef)
135
136
137
138
139 type = None
140 if name=="output":
141 type = "output"
142
143 res = rootElement(type=type)[
144 VS.name[name],
145 VS.title[base.getMetaText(tableDef, "title", propagate=False)],
146 VS.description[base.getMetaText(tableDef, "description", propagate=True)],
147 VS.utype[base.getMetaText(tableDef, "utype")]]
148
149 if not suppressBodies:
150 res[[
151 getTableColumnFromColumn(col, voTableDataTypeFactory)
152 for col in tableDef if not col.hidden], [
153 getForeignKeyForForeignKey(fk, namesInSet)
154 for fk in tableDef.foreignKeys]]
155
156 return res
157
158
161 """returns a vs:tableset element from a sequence of (rd, tables) pairs.
162
163 In each pair, rd is used to define a VODataService schema, and tables is
164 a sequence of TableDefs that define the tables within that schema.
165 """
166
167
168
169 namesInSet = set(getEffectiveTableName(td).lower()
170 for td in itertools.chain(*(tables for rd, tables in schemas)))
171
172 res = rootElement()
173 for rd, tables in schemas:
174 res[VS.schema[
175 VS.name[rd.schema],
176 VS.title[base.getMetaText(rd, "title")],
177 VS.description[base.getMetaText(rd, "description")],
178 VS.utype[base.getMetaText(rd, "utype", None)],
179 [getTableForTableDef(td, namesInSet, suppressBodies=suppressBodies)
180 for td in tables]]]
181 return res
182
183
186 """returns a VS.tableset for a service or a published data resource.
187
188 This is for VOSI queries and the generation of registry records.
189 Where it actually works on services, it uses the service's getTableset
190 method to find out the service's table set; if it's passed a TableDef
191 of a DataDescriptor, it will turn these into tablesets.
192
193 Sorry about the name.
194 """
195 if isinstance(resource, rscdef.TableDef):
196 tables = [resource]
197
198 elif isinstance(resource, rscdef.DataDescriptor):
199 tables = list(resource.iterTableDefs())
200
201 else:
202 tables = resource.getTableSet()
203
204 if not tables:
205 return rootElement[
206 VS.schema[
207 VS.name["default"]]]
208
209
210
211
212
213
214 bySchema, rdForSchema = {}, {}
215 for t in tables:
216 bySchema.setdefault(t.rd.schema, []).append(t)
217 rdForSchema[t.rd.schema] = t.rd
218
219 schemas = []
220 for schemaName, tables in sorted(bySchema.iteritems()):
221 schemas.append((rdForSchema[schemaName], tables))
222
223 return getTablesetForSchemaCollection(
224 schemas,
225 rootElement,
226 suppressBodies=suppressBodies)
227