- '<h1>Patron Blocking Rules — Allowed Functions</h1>\n<p>Patron blocking rule expressions are evaluated by a locked-down <a href="https://github.com/danthedeckie/simpleeval">simpleeval</a> sandbox. Only the functions listed below may be called inside a rule expression. Any reference to an unlisted function causes the rule to <strong>fail open</strong> (the patron is not blocked at runtime; the rule is accepted at admin-save time, though there is feedback in the Admin UI warning the user that there is a problem with the rule).</p>\n<hr>\n<h2><code>age_in_years</code></h2>\n<p>Calculates the age of a person in <strong>whole years</strong> from a date string. Use this to write rules that gate access by age (e.g. block minors or enforce senior-only services).</p>\n<h3>Signature</h3>\n<pre><code class="language-text">age_in_years(date_str, fmt=None) -> int</code></pre>\n<h3>Parameters</h3>\n<table class="table table-condensed table-bordered"><thead><tr><th>Parameter</th><th>Type</th><th>Required</th></tr></thead><tbody><tr><td><code>date_str</code></td><td><code>str</code></td><td>Yes</td></tr>\n<tr><td><code>fmt</code></td><td><code>str</code> or <code>None</code></td><td>No</td></tr></tbody></table>\n<ul>\n<li><strong><code>date_str</code></strong> — A date string representing the person\'s date of birth.</li>\n</ul>\n<p> ISO 8601 format (<code>YYYY-MM-DD</code>) is tried first; if that fails, <code>dateutil.parser</code> is used as a fallback, accepting most common human-readable formats (e.g. <code>"Jan 1, 1990"</code>, <code>"01/01/1990"</code>). - <strong><code>fmt</code></strong> — An explicit <a href="https://docs.python.org/3/library/datetime.html#datetime.datetime.strptime"><code>strptime</code></a> format string (e.g. <code>"%d/%m/%Y"</code>). When supplied, no automatic parsing is attempted.</p>\n<h3>Returns</h3>\n<p><code>int</code> — The person\'s age in complete years (fractional years are truncated, not rounded).</p>\n<h3>Raises</h3>\n<p><code>ValueError</code> — If <code>date_str</code> cannot be parsed (either by ISO 8601, the supplied <code>fmt</code>, or <code>dateutil</code>). At runtime this causes the rule to <strong>fail open</strong>.</p>\n<h3>Examples</h3>\n<pre><code class="language-python"># Block patrons under 18 (field returned verbatim from the SIP2 server)\nage_in_years({polaris_patron_birthdate}) < 18\n\n# Block patrons under 18 using an explicit strptime format\nage_in_years({dob_field}, "%d/%m/%Y") < 18\n\n# Block patrons aged 65 or over (e.g. senior-only restriction)\nage_in_years({polaris_patron_birthdate}) >= 65</code></pre>\n<hr>\n<h2><code>int</code></h2>\n<p>Converts a value to a Python <code>int</code>. Useful when the SIP2 server returns a numeric field as a string (a common occurrence) and you need to compare it numerically rather than lexicographically.</p>\n<h3>Signature</h3>\n<pre><code class="language-text">int(value) -> int</code></pre>\n<h3>Parameters</h3>\n<table class="table table-condensed table-bordered"><thead><tr><th>Parameter</th><th>Type</th><th>Required</th></tr></thead><tbody><tr><td><code>value</code></td><td><code>Any</code></td><td>Yes</td></tr></tbody></table>\n<ul>\n<li><strong><code>value</code></strong> — The value to convert. Typically a string such as <code>"3"</code> or</li>\n</ul>\n<p> a float such as <code>2.9</code>. Any value accepted by Python\'s built-in <code>int()</code> is valid. Passing a non-numeric string (e.g. <code>"adult"</code>) raises a <code>ValueError</code> and causes the rule to <strong>fail open</strong>.</p>\n<h3>Returns</h3>\n<p><code>int</code> — The integer representation of <code>value</code>. Floating-point values are <strong>truncated</strong> toward zero (e.g. <code>int("2.9")</code> raises <code>ValueError</code>; pass a float literal or cast via <code>{field} * 1</code> first if you need truncation of floats).</p>\n<h3>Raises</h3>\n<p><code>ValueError</code> — If <code>value</code> cannot be converted to an integer. At runtime this causes the rule to <strong>fail open</strong>.</p>\n<h3>Examples</h3>\n<pre><code class="language-python"># Block patron class codes above 2 (SIP2 returns the code as a string)\nint({sipserver_patron_class}) > 2\n\n# Block if a numeric expiry-year field indicates an expired account\nint({expire_year}) < 2025</code></pre>\n<hr>\n<h2>Notes</h2>\n<ul>\n<li><strong>String methods are available</strong> — methods on Python <code>str</code> values can be</li>\n</ul>\n<p> called directly on string-valued placeholders. For example, to check whether a patron identifier starts with a certain prefix:</p>\n<p> ``<code>python {patron_identifier}.startswith("1234") </code>``</p>\n<ul>\n<li><strong>Fail-open behaviour</strong> — any function call that raises an exception</li>\n</ul>\n<p> (e.g. an unparseable date or a non-numeric string passed to <code>int()</code>) causes the rule to be <strong>skipped</strong> at runtime (the patron is not blocked by that rule) and the rule to be <strong>accepted</strong> at admin-save time (with a warning in the Admin UI that there is a problem with the rule). Write test rules carefully using representative patron data before enabling them in production. - <strong>No other builtins</strong> — Python builtins such as <code>len</code>, <code>str</code>, <code>float</code>, <code>abs</code>, and <code>round</code> are <strong>not</strong> available. If you need additional functions, request them via the standard feature-request process so they can be reviewed and added to <code>DEFAULT_ALLOWED_FUNCTIONS</code> in <code>rule_engine.py</code>. - <strong>Placeholder syntax</strong> — field values from the SIP2 response are referenced as <code>{field_name}</code>. All fields returned by the SIP2 <code>patron_information</code> command are available, plus the normalised <code>{fines}</code> key (a <code>float</code> derived from <code>fee_amount</code>).</p>';
0 commit comments