================= Defining commands ================= The final step in being able to actually *use* the data objects we have configured is define Zimagi commands to import their data and query them. By adding a ``station`` command, we automatically add a collection of subcommands associated with querying data. As elsewhere, we often wish to define a reusable ``command_base`` that might be utilized by various commands to avoid repetition. In this module, we define the following:: command_base: # Define a base command with settings # Same name as data model by convention, not requirement station_base: class: StationCommandBase mixins: [station] # Accessible via the API server_enabled: true # Only these groups can use 'station' commands groups_allowed: [noaa-admin] The particular name we chose for this command base is anything we might wish, but ``station_base`` seems like an obvious choice. It has an (optional) internal class name in the generated code, and it uses the mixin we discussed earlier. The two elements where we actually make design decisions are in that we wish to expose commands based on this within the RESTful JSON API (i.e. for web requrests), and that we want the role ``noaa-admin`` to be able to use it. We gave the base an (optional) internal class name in the generated code, and it uses the mixin we discussed earlier. The only elements where we actually make design decisions are ``server_enabled`` and ``groups_allowed``. We set ``server_enabled`` to ``true`` to expose commands within the RESTful JSON API (i.e. for web requests). We also give the ``groups_allowed`` key the role that we want to be able to use: ``noaa-admin``. We need an actual command here. To show that the names of the commands are chosen by use, rather than constrained by the name of the data object or by mixins, we defined too almost-synonyms. Both ``station`` subcommands and ``bahnhof`` subcommands will do the same thing ("Bahnhof" is simply a German word for "station"):: command: station: # Maps back to data object resource: station base: station_base # Show later than core commands priority: 99 groups_allowed: [noaa-admin, admin] # Alternate command (does same thing to demonstrate) bahnhof: # Maps back to data object resource: station base: station_base # Tie into object type (to match prefix for mixin) # I.e. match ref mixin_name base_name: station # Show later than core commands priority: 98 The only differences here, other than the obvious spelling, is that we demonstrate that a command may override its base; in this case we redefine ``groups_allowed`` for the ``station`` command. This is not a real change in behavior since *admin* is always allowed to do everything anyway. We also choose slightly different ``priority`` values for the two spellings, which will cause ``bahnhof`` to appear earlier than ``station`` when you run:: vagrant@zimagi-noaa:~$ zimagi help Inside the Vagrant shell. As the module is configured now, the ``observation`` priority is even higher (105), so appears after both. Import commands --------------- We have provided a ``station`` (or ``bahnhof``) command as a place to put subcommands we use in querying data. But we need to define an ``import`` subcommand to load the data from our remote source(s) into the local RDBMS. For this module, we define that inside ``$ZDIR/lib/default/modules/noaa-stations/spec/import/station_observations.yml``. This YAML file includes a new YAML feature we have not seen before. Using mixins and bases for commands and data models is a way of providing templates for reuse. As well, YAML itself has a feature for literal transclusion of boilerplate. This distinction is very similar to the difference between an ``#include`` directive in languages like C/C++ and *inheritance* of classes in languages like Python (or C++, or Java, etc). For better or worse, because ``import`` is a built-in Zimagi command, we can define subcommands but not new bases or mixins for it. The YAML feature we see is called *anchors* and *aliases*. They always occur in the same physical file, so are somewhat different from C-style ``#include`` directives in that respect. Let us look first at the anchor we use:: _observation: &observation source: noaa_stations data: station: map: # "number" as defined in spec/data/station.yml number: # "station_id" as defined in plugins/source/noaa_stations.py column: station_id name: column: station_name lat: column: latitude lon: column: longitude elevation: column: elevation observation: relations: station_id: # Mapping back to "station" as defined in spec/data/station.yml data: station # Mapping back to plugins/source/noaa_stations.py column: station_id required: true map: date: column: date temp: column: temperature temp_attrs: column: temperature_attrs This anchor is something we are likely to use as we develop more commands. It has an anchor name ``&observation``, but as we will see, when we *alias* it we will spell that as ``*observation`` (these spelling are loosely inspired by references and pointers in C/C++ family languages). The name of the key with a leading underscore, ``_observation`` is irrelevant—you can use any identifier name you like, and it is not used again elsewhere; something merely needs to occur there syntactically. We indicate the source in terms of a *provider*. Recall the definition in ``spec/plugins/source.yml`` that was discussed above; this is where the spelling ``noaa_stations`` comes from. Given that source, we define ``data`` import elements ``station`` and ``observation``. These each have a ``map`` key that maps database table column names to names used within the Zimagi shell and API. They might also have a ``relations`` key that defines a foreign-key relationship. The final component of our (simple) module is define an actual ``import`` subcommand. We can do that as follows:: import: test: # Identical to including the body of _observation here <<: *observation # In concept we could override definition from reference, e.g. # source: something_else tags: [observations] min_year: 1929 max_year: 1931 station_ids: ["03005099999", "99006199999"] The special key ``<<`` is the one that indicates an alias back to the anchor defined above. It is exactly as if we had typed the entire body of ``_observation`` at that same point in the file The key ``tags`` indicates **[TODO]**. For this simple subcommand ``test`` we give a fixed value for a ``min_year`` and ``max_year``, and also a specific list of ``station_ids`` that we will import from the NOAA website. In a more flexible command, you would indicate these elements using switches to a command, but this demonstrates the general pattern. At this point—perhaps after running ``zimagi module save noaa-stations`` again, if needed, we can run:: vagrant@zimagi-noaa:~$ zimagi import test Data is available locally to be queried from the Vagrant shell or the API now.