Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
protogrid:agents [2020-04-22 09:22] – [Example] jusprotogrid:agents [2023-01-16 23:16] (current) – [Example] dru
Line 1: Line 1:
 ====== Agents ====== ====== Agents ======
- 
 Agents run periodically and execute code specified by the user. Agents run periodically and execute code specified by the user.
- 
-Note: Agents are only available in environments that run Protogrid 2.1 or higher. 
  
 ===== Setup ===== ===== Setup =====
- 
 To set up an agent, there are two necessary steps: To set up an agent, there are two necessary steps:
   * Under Design -> Code, create an Agent Library and add your code to the //on_schedule// function.    * Under Design -> Code, create an Agent Library and add your code to the //on_schedule// function. 
-  *  Under Design -> Proto, select the Proto you want to use and add corresponding the Agent Library+  * Under Design -> Proto, select the Proto you want to use and add corresponding the Agent Library
  
 ==== Example ==== ==== Example ====
   - Create a Proto with a DateTime Field and a Text Field. Copy the key of the Text Field.   - Create a Proto with a DateTime Field and a Text Field. Copy the key of the Text Field.
-  - Create a user profile with rights to edit the proto created above. +  - Create a user profile with rights to edit the proto created above but without Environment Administrator rights
-  - Create an Agent Library, specify the user created above as context user and copy the following code. Replace the value of //defkey_status_field// with the key from above.+  - Create an Agent Library, specify the user created above as context user and copy the following code. Replace the value of //fieldkey_status_field// with the key from above
 +  - Create a Proto for parameter values with a Text Field, copy the key of the Text Field and replace //fieldkey_parameter_value// with that key. Create a parameter Card with this Proto, copy the key of that Card and replace //cardkey_parameter_deadline_reached// with that key.
   - Add this Agent Library to your Proto.   - Add this Agent Library to your Proto.
   - Based on this proto, create some cards where the date lies within the next 60 minutes. Wait one hour.   - Based on this proto, create some cards where the date lies within the next 60 minutes. Wait one hour.
   - The value in your Text Field should have been replaced by //Frist abgelaufen.//   - The value in your Text Field should have been replaced by //Frist abgelaufen.//
 +
 <code javascript> <code javascript>
 function agent_library() { function agent_library() {
-    var defkey_status_field "3b47a5fe-894d-4580-bc0c-563aa83d9a02"// TODO change this value+    var fieldkey_status_field '3b47a5fe-894d-4580-bc0c-563aa83d9a02'; 
 +    var cardkey_parameter_deadline_reached = '9e13a5b0-97d5-405e-9f50-8a2f808cf541'; 
 +    var fieldkey_parameter_value = 'a9a8143f-ad68-4df1-d57b-d8df1569c83b';
  
-    function set_value(card, field_keyvalue) { +    // Make sure that this function does not return until all operations and JSON-API requests are completed / fulfilled. 
-        //sets the value of a single-value field given by field_key +    // We recommend to use await / async syntax: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await  
-        for (var 0; i < card.design_elements.length; i+++    // * card [object]:    The card that triggered the agent 
-            if (card.design_elements[i]['definition_key'=== field_key{ +    // * cookie [string]:  A valid cookie that can be used. 
-                card.design_elements[i]['value'] = value+    // * axios [object]:   An axios object. You can use e.g. axios.get() and axios.post() for API requests 
-                break+    // * baseurl [string]: Contains the base url needed for API requests 
-            }+    // * appname [string]: Contains the application name 
 +    // most URLs needed for API calls are structured like baseurl + 'api/v2/apps/' + appname + (...) 
 +    // return value: A boolean. If set to true, it will save the current card 
 +    async function on_schedule(card, cookieaxios, baseurl, appname) { 
 +        // Here is an example for an authenticated axios GET request: 
 +        try { 
 +            var response await axios.get(baseurl 'api/v2/apps/' appname + '/cards/' + cardkey_parameter_deadline_reached, { 
 +                'headers':
 +                    'Cookie': cookie 
 +                } 
 +            }); 
 +            console.log('axios GET request finished'); 
 + 
 +            var loaded_card monkey_patch_card(response.data.result); 
 +            var loaded_message = loaded_card.get_value(fieldkey_parameter_value, 'Frist abgelaufen')
 +            card.set_element_value(fieldkey_status_field, loaded_message); 
 + 
 +            return true; 
 +        catch (e) { 
 +            console.error(e); 
 +            return false;
         }         }
-        return card; 
     }     }
-     +
-    function on_schedule(card, cookie) { +
-        card = set_value(card, defkey_status_field, 'Frist abgelaufen'); +
-        return card; +
-    } +
-    +
     return {     return {
-        set_value: set_value, 
         on_schedule: on_schedule         on_schedule: on_schedule
     };     };
 } }
 +
 </code> </code>
  
 ===== Execution ====== ===== Execution ======
 +Agents are by default checked every hour (this can be modified for your environment, please contact the Protogrid Operations Team). If any DateTime Field on a card has a smaller value than the current time and the Agent has not yet run since that time, the Agent gets triggered and the code in the Agent Library is executed in the context of this card. For Date-Only Fields, the time taken into account is determined by the value of 'Time Offset Value for Periodic Agents' in the Environment Properties (defaults to 03:00 GMT).
 +
 +Agents run in the context of a specific user profile which must not have Environment Administrator rights. This user's password is automatically reset during the execution process. So this user profile shall only be used for agents and not for physical users. All agent executions are logged in the //agent_log// on the respective card.
 +
 +While an agent is running in the context of a specific card, it is also possible to load and save other cards by calling the JSON API endpoints using axios. A valid axios module is given in the //on_schedule// function. Agents have a global runtime limit (usually one hour). If you want to restrict this limit for certain agents, you can do so by entering the desired maximum runtime (in milliseconds) in the Agent card. After reaching the runtime limit, the code will terminate instantly and the next card will be processed. The process will not be started if the time until the next agent check is less than the maximum runtime.
 +
 +The //on_schedule// function should return a boolean. If the boolean is true, the card that triggered the agent will be saved.
  
-Agents are checked every hour. If any DateTime Field on a card has a smaller value than the current time and the Agent has not yet run since that time, the Agent gets triggered and the code in the Agent Library is executed in the context of this cardAgents run in the context of a specific user profileThis user's password is automatically reset during the execution process. So this user profile shall only be used for agents and not for physical usersAll agent executions are logged in the //agent_log// on the respective card.+===== Best Practices for Calculation Code ====== 
 +In the following, we have compiled some recommendations based on our experience for high stability, performance, reliability and maintainability of backend code that performs calculations on a larger number of Cards (i.e. more than 100): 
 +  - It is recommended to use [[protogrid:JSON API Database Views#Views of Dedicated Search Dialog Boxes|views of dedicated Search Dialog Boxes]] for retrieving the needed source data for calculation code. Do not use [[protogrid:Filter#Enable Multi Column Indexing for this Field|multi column indexing views]] for backend code. 
 +  - Note: Use multi column indexind views at all only if the free combination of several filters is required in the user interface. 
 +  - Write to as few Relation Fields as possible in the code. Do this only if it is needed for the user interface (e.g. for filtering TableViews). Do this only on Cards that are rarely/never editedDo this only on Cards that do not (already) have a large number of Relating Cards. 
 +  - Procedure if the calculation code requires a "relation" that does not need a Relation Field in the user interface: Use a Text Field instead of a Relation FieldWrite the key of the relation to this Text Field with an individual prefix (important!). Use a Text Filter or a dedicated [[protogrid:TableView#Search Dialog Boxes|Search Dialog Box]] to retrieve Cards with the desired relation. 
 +  - If editing or deleting a Card in the user interface subsequently to calculation has to be prevented, this can be done in both cases by restricting the editing rights ([[protogrid:Role|roles]] at Proto and/or Card level). 
 +  - Read/load as few specific/single cards as possible. Work with views whenever possible. Use "include_cards" - but only if necessary and on as few view rows as possible. Do not hesitate to contact Protogrid Customer Support proactively in case of special requirements (e.g. certain values needed in the value of a view row or special reduction views needed). 
 +  - Analogous to the previous point, write as few specific/single cards as possible. Whenever possible, save several cards together. You can use the Endpoint [[protogrid:API Endpoints#/api/v2/apps/<app_name>/cards|/api/v2/apps/<app_name>/cards]] with a list of Card objects in the POST body.
Print/export