========= How Do I? ========= Recipies and tricks for solving problems using GAVO DaCHS ========================================================================= :Author: Markus Demleitner :Email: gavo@ari.uni-heidelberg.de .. contents:: :depth: 1 :backlinks: entry :class: toc ...skip a row from a rowmaker? ------------------------------ Raise IgnoreThisRow in a procedure application, like this:: if 2+colX>22: raise IgnoreThisRow() However, it's probably more desirable to use the rowmakers' built-in ``ignoreOn`` feature, possibly in connection with a procedure, since it is more declarative. Still, the following is the recommended way to selectively ignore broken records defined via certain identifiers:: This proc filters out records too broken to ingest. for the set of ids in the toIgnorePar set([ 202, 405]) if @catid in toIgnore: raise IgnoreThisRow("Manually ignored from RD") ...skip a single source? ------------------------ If you want to skip processing of a source, you can raise SkipThis from an appropriate place. Usually, this will be a sourceFields element, like this:: if len(sourceToken)>22: raise base.SkipThis("%s skipped since I didn't like the name"% sourceToken) ...set a constant input to a core? ---------------------------------- Use a service input key with a Hidden widget factory and a default:: ... ...get a multi-line text input for an input key? ------------------------------------------------ Use a widgetFactory, like this:: ...add computed columns to a dbCore output? ------------------------------------------- Easy: define an output field with a select attribute, e.g.:: This will add an output field that looks to the service like it comes from the DB proper but contains the value of the ``ev_i`` column multiplied with 5.434. The expression must be valid SQL. ...make an input widget to select which columns appear in the output table? --------------------------------------------------------------------------- In general, selecting fancy output options currently requires custom cores or custom renderers. Ideas on how to change this are welcome. For this specific purpose, however, you can simply define an service key named _ADDITEM. This would look like this:: .... ... Setting showItems to -1 gives you checkboxes rather than a select list, which is mostly what you want. Try with and without and see what you like better. If you do that, you *probably* do not want the standard "additional fields" widget at the bottom of the form. To suppress it, add a line :: True to the service definition. The "True" in there actually is a bit of a red herring, the widget is suppressed for any value. ...add and image to query forms? -------------------------------- There are various variations to that theme -- you could go for a custom template if you want to get fancy, but usually putting an image into an _intro or _bottominfo meta section should do. In both cases, you need a place to get your image from. While you could put it somewhere into rootDir/web/nv_static, it's probably nicer to have it within a resource's input directory. So, add a static renderer to your service, like this:: static This lets you put service-local static data into resdir/static/ and access it as /static/ Usually, your _intro or _bottominfo will be in reStructured text. Plain images work in there using substitution references or simply the naked image directive:: The current data set comprises these fields: .. image:: \servicelink{cars/q/cat/static/fields-inline.png} The servicelink macro would ensure that the image would still be found if the server ran off-root. This is the recommended way of doing things. If, however, you insist on fancy layouts or need complete control over the appearance of your image (or whatever), you can use the evil "raw" meta format:: ]]> Make sure you enter valid HTML here, no checks are done by the DC software. ...import data coming in to a service? -------------------------------------- In a custom renderer or core, you can use code like:: from gavo import api ... def import(self, srcName, srcFile): dd = self.service.rd.getById("myDataId") with api.getWritableAdminConn() as conn: self.nAffected = api.makeData(dd, forceSource=srcFile, connection=conn).nAffected You want to use a separate connection since the default connections obtained by cores and friends are unprivileged and typically cannot write to table. The nAffected should contain the total number of records imported and could be used in a custom render function. srcName and srcFile come from a formal File form item. In submitActions, you obtain them like:: srcName, srcFile = data["inFile"] Note that you can get really fancy and manipulate ``data`` in some way up front. That could look like this:: from gavo import rsc ... data = rsc.Data.create(dd, parseOptions=api.parseValidating, connection=conn) data.addMeta("_stationId", self.stationRecord["stationId"]) self.nAffected = api.makeData(dd, forceSource=srcFile, data=data, connection=conn).nAffected ...change the query issued on SCS queries? ------------------------------------------ You may want to do that because for some reason there is no q3c index on the queried table, or the semantics aren't quite a point vs. point cone search but close enough. Sadly, this is quite complicated right now since our inheritance mechanism ("original") is so simple-minded. This will hopefully improve with a generic record/replay mechanism we're thinking about. That said, the current way looks like this (for a query that does a proximity search on bboxes):: bbox < %%(%s)s"%( vizierexprs.getSQLKey("RA", inPars["RA"], outPars), vizierexprs.getSQLKey("DEC", inPars["DEC"], outPars), vizierexprs.getSQLKey("SR", inPars["SR"], outPars)) ]]> -- so, you are inheriting from the SCS condition on three levels and then override the genQuery function defined in the common setup code. The way the condDescs are written, you must return rather than yield the actual query string. See the tutorial on how condDesc code works in general. The semi-good news is that if you want the same thing for an SCS query, you can reuse part of what you did above:: ...create database views in data elements? ------------------------------------------ There's a catch going beyond using simpleView or tables with viewStatements: views evidently depend on the existence of tables. It would seem this does not hurt when you have a data definition like:: where ``v`` is the view depending on the table ``a``. The trouble with this is that as soon as you change ``a``, the data build cannot be re-created; you cannot even drop it if you deleted the view v (in that case, just manually drop table a, and things work again. The reason for this odd behaviour is somewhat subtle, and I'll explain it here when someone asks. The upshot, however, is: Never ``make`` a view in the same ``data`` as a table it depends on. This right way to do what's intended above is:: ...fix duplicate values? ------------------------ There are many reasons why you could violate the uniqueness constraints on primary keys, but let's say you just got a message saying:: Primary key could not be added ('could not create unique index "data_pkey" DETAIL: Table contains duplicated values.)' The question at this point is: What are the duplicated values? For a variety of reasons, DaCHS only applies constraints only after inserting all the data, so the error will occur at the end of the input. Not even the ``-b1`` trick will help you here. Instead, temporarily remove the primary key condition from the RD and import your data. Then, exececute a query like:: select * from ( select , count(*) as ct from group by ) as q where ct>1; ...define an input field doing google-type full text searches? -------------------------------------------------------------- Since version 8.3 (or so), postgres supports query modes inspired by information retrieval on text columns -- basically, you enter a couple of terms, and postgres matches all strings containing them. Within DaCHS, this is currently only supported using custom phrase makers. This would look like this:: yield ("to_tsvector('english', description)" " @@ plainto_tsquery('english', %%(%s)s)"%( base.getSQLKey("columnwords", inPars["columnwords"], outPars)) -- here, ``description`` is the column containing the strings, and the ``'english'`` in the function arguments gives the language according to which the strings should be interpreted. You may want to create an index supporting this type of query on the respective columns, too. To do that, say:: to_tsvector('english', bibref) ...put more than one widget into a line in web forms? ----------------------------------------------------- Use input table groups with a compact. In DB cores, however, you probably do not want to give inputTables explicitely since it's much less hassle to have them computed from the condDescs. In this case, the widgets you want to group probably come from a single condDesc. To have them in a group, define a group within the condDesc without any paramRefs (or colRefs) -- they cannot be resolved anyway. Give the group style and label properties, and it will be added to the input table for all fields of the condDesc:: compact Example vals If you are doing this, you probably want to use the ``cssClass`` property of input keys and the ``customCSS`` property of services. The latter can contain css specifications. They are added into form pages by the defaultresponse template (in your custom templates, you should have ``