Functions#
Functions are the most powerful extension to Arches. Functions associated with a Resource are called during various CRUD operations, and have access to any server-side model. Proficient Python/Django developers will find few limitations extending an Arches Project with Functions.
Function must be created, registered, and then associated with a Resource Model.
Functions are similar to database triggers. On get, save, post_save, and delete operations of a tile, the Python code in a function is run. For example, the primary descriptor function saves the primary descriptors of a resource model on the save event of a tile. Hypothetically, you could also do something like update an external system on the creation of a tile or send an email notification.
It is important to note that functions are not run during import operations (with the exception of non-bulk import via the command line interface, see Import business data).
Primary Descriptors#
Functions are used to make primary descriptors. The primary descriptors function is used to generate the name of the resource, which is used to identify the resource in places such as the search results card, map popup and report title. It is also used to generate the resource descriptions displayed in the body of the search results and map popup cards. For more information on configuring primary descriptor functions please review: Set Resource Display Names
The resource descriptors are generated on resource instance save. They can also be
regenerated for all the resources of a particular type by running the elasticsearch
reindex management command with the --recalculate-descriptors
flag. A description
of the elasticsearch management commands can be found here:
ElasticSearch Management
Creating a Function#
A Function comprises three separate files, which should be seen as front-end/back-end complements. On the front-end, you will need a component made from a Django HTML template and JavaScript pair, which should share the same basename.
In your Project, these files must be placed like so:
/myproject/myproject/media/js/views/components/functions/spatial_join.js
/myproject/myproject/templates/views/components/functions/spatial_join.htm
The third file is a Python file which contains a dictionary telling Arches some important details about your Function, as well as its main logic.
/myproject/myproject/functions/spatial_join.py
Note
As in the example above, its advisable that all of your files share the same basename. (If your Function is moved into a Package, this is necessary.) A new Project should have an example function in it whose files you can copy to begin this process.
Defining the Function’s Details#
The first step in creating a function is defining the details
that
are in the top of your Function’s .py
file.
details = {
'name': 'Sample Function',
'type': 'node',
'description': 'Just a sample demonstrating node group selection',
'defaultconfig': {"selected_nodegroup":""},
'classname': 'SampleFunction',
'component': 'views/components/functions/sample-function'
}
- name:
Required Name is used to unregister a function, and shows up in the
fn list
command.- type:
Required As of version 4.2, this should always be set to
node
orprimarydescriptors
- description:
Optional Add a description of what your Function does.
- defaultconfig:
Required A JSON object with any configuration needed to serve your function’s logic
- classname:
Required The name of the python class that holds this Function’s logic.
- component:
Required Canonical path to html/js component.
More about the defaultconfig
field#
Any configuration information you need your Function to access can be
stored here. If your function needs to calculate something based on
the value of an existing Node, you can refer to it here. Or, if you
want your Function to e-mail an administrator whenever a specific node
is changed, both the Node ID and the email address to be used are good
candidates for storage in the defaultconfig
dictionary.
The defaultconfig
field serves both as a default, and as your
user-defined schema for your function’s configuration data. Your
front-end component for the function will likely collect some of this
configuration data from the user and store it in the config
attribute of the pertinent FunctionXGraph
.
Writing your Function Logic#
In your Function’s Python code, you have access to all your server-side models. You’re basically able to extend Arches in any way you please. You may want to review the Data Model documentation.
Function Hooks#
Your function needs to extend the BaseFunction
class. Depending on
what you are trying to do, you will need to implement the get
,
save
, post_save
, delete
, on_import
, and/or after_function_save
methods.
class MyFunction(BaseFunction):
def get(self, *args, **kwargs):
raise NotImplementedError
def save(self, *args, **kwargs):
raise NotImplementedError
# occurrs after Tile.save
def post_save(self, *args, **kwargs):
raise NotImplementedError
def delete(self, *args, **kwargs):
raise NotImplementedError
def on_import(self, *args, **kwargs):
raise NotImplementedError
# saves changes to the function itself
def after_function_save(self, *args, **kwargs):
raise NotImplementedError
Note
Not all of these methods are called in the current Arches
software. You can also leave any of them unimplemented, and the
BaseFunction
class will raise a NotImplementedError
for
you. Arches is designed to gracefully ignore these exceptions for
functions.
A detailed description of current functionality is below.
save
and delete
#
The Tile
object will look up all its Graph’s associated Functions
upon being saved. Before writing to the database, it calls each
function’s save
method, passing itself along with the Django
Request
object. This is likely where the bulk of your function’s
logic will reside.
The Tile
object similarly calls each of its graph’s
functions’ delete
methods with the same parameters. Here, you can
execute any cleanup or other desired side effects of a Tile’s
deletion. Your delete
implementation will have the same signature
as save
.
after_function_save
#
The Graph view passes a FunctionXGraph object to
after_function_save
, along with the request.
The FunctionXGraph object has a config
attribute which stores that
instance’s version of the defaultconfig
dictionary. This is a good
opportunity, for example, to programmatically manipulate the
Function’s configuration based on the Graph or any other server-side
object.
You can also write any general logic that you’d like to fire upon the assignment of a Function to a Resource.
on_import
#
The import module calls on_import if the file format is a JSON-formatted Arches file, and passes an associated Tile object.
CSV imports do not call this hook.
The UI Component#
Having implemented your function’s logic, it’s time to develop the front-end components required to associate it with Resources and provide any configuration data.
The component you develop here will be rendered in the Resource Manager when you associate the function with a Resource, and this is where you’ll put any forms or other UI artifacts used to configure the Function.
Developing your Function’s UI component is very similar to developing
Widgets. More specific guidelines are in progress, but for now,
refer to the sample code in your project’s
templates/views/components/functions/
directory, and gain a little
more insight from the templates/views/components/widgets/
directory. The complementary JavaScript examples will be located in
media/js/views/components/functions/
and
media/js/views/components/widgets
directories.
Registering Functions#
First, list the names of functions you already have registered:
(ENV)$ python manage.py fn list
Now you can register your new function with
(ENV)$ python manage.py fn register --source <path to your function's .py file>
For example:
(ENV)$ python manage.py fn register --source /Documents/projects/mynewproject/mynewproject/functions/sample_function.py
Now navigate to the Function Manager in the Arches Designer to confirm that your new function is there and functional. If it’s not, you may want to unregister your function, make additional changes, and re-register it. To unregister your function, simply run
(ENV)$ python manage.py fn unregister --name 'Sample Function'
All commands are listed in Command Line Reference - Function Commands.