From c5990cecf92751c5eff6815df48cfcd955f348c2 Mon Sep 17 00:00:00 2001 From: Alex Zorkin Date: Tue, 10 Dec 2024 21:20:01 -0800 Subject: [PATCH 1/7] feat: compliance report etl work --- backend/lcfs/services/tfrs/redis_balance.py | 4 +- etl/database/nifi-registry-primary.mv.db | Bin 90112 -> 102400 bytes etl/local_nifi_manager.sh | 300 +++++++++ etl/nifi/conf/flow.json.gz | Bin 9539 -> 9602 bytes etl/nifi/conf/flow.xml.gz | Bin 14964 -> 15139 bytes etl/nifi_scripts/compliance_report.groovy | 647 ++++++++++++++++++++ etl/nifi_scripts/user.groovy | 4 +- 7 files changed, 952 insertions(+), 3 deletions(-) create mode 100644 etl/local_nifi_manager.sh create mode 100644 etl/nifi_scripts/compliance_report.groovy diff --git a/backend/lcfs/services/tfrs/redis_balance.py b/backend/lcfs/services/tfrs/redis_balance.py index 2628a831a..a1add9a9a 100644 --- a/backend/lcfs/services/tfrs/redis_balance.py +++ b/backend/lcfs/services/tfrs/redis_balance.py @@ -31,7 +31,9 @@ async def init_org_balance_cache(app: FastAPI): transaction_repo = TransactionRepository(db=session) # Get the oldest transaction year - oldest_year = await transaction_repo.get_transaction_start_year() + oldest_year = await transaction_repo.get_transaction_start_year() or int( + 2019 + ) # Get the current year current_year = datetime.now().year diff --git a/etl/database/nifi-registry-primary.mv.db b/etl/database/nifi-registry-primary.mv.db index 56acc8498b5906b1d5e0a1786bb311a88c50341c..e7d0507ba192a544d1fc602ead8e4a1dc93c765c 100644 GIT binary patch delta 3104 zcmb7`U2GIp6vt<#?Cb|c5NH+c$F_wQ+M&+(+!^F3-zt=KET|qFv*+{1-idseeNij4{QPucCZud`Sx`S&@024slQCx~j zy_x!vW;iM#NP?sRNkR8A+vBQh>Ix7LH`2|j5NL{kPL0&ZEz4B}K}P3Vzd<*$ttfN4 zK6h`lf-XGpI~)dGI^7b!KwGjO=Wb*tnE0YN>oQC;x-)$~nb0MR7l7lUva$NO?#hm( zL8OnZVkKR2frfUEHLxOZ6~)XAjrGNHnen|e%8a+-j~zgd->OI8)<=K4yX&yC6&Eu~tfFc3N4fG83Id-J%oYM$_??A<7XV3$39$j-u$ex|8d-q2V-3r%-cO7wY7bG(hV&)eK2R^23w_ zOA3Ng3EF}h21VL|hxkp<4!jL8lvWT_Bo|Q31&~~T!WVMEv4BZjGS;t|jwX-Qw0U1T zXb6hRbKzBjdjN6|IlPAK-9u8{Vpj@0mH-6BdI><$c?7$bt=9D7O-Pdpf(;@!5&Qx< zXra)~IDTPhCwEgQS>%NaeNTwRkAOw*=#~W)P1B0uW-l3Z1*53}ylMz<-ERcmMG^yK z4DZhkVw@^R|F|ntO2P-XSyM5{M;X+`*P_l%E6}IBSZN5FX}OAN+W*a0MGyG&Le~q? z&rtO;terbUF~|_rr)%3ntmpao;xERv#%7x}G~4eSRptSDf2xhPFxBlz|D6IQy8w0L zg|j}?vGdpi9@HB?B$Jen1*o-R?wTOlqAeqs*@Br~ zn0wrtNOypFh5if#7rrT=&12j%I>r_kxJ4&M|AQF43g6ZAoV+ zG!ySI=jGB0l{WVXDtwt4gc)ucx*Xy1QNU$B;4)7!*DIRVMz~B>r>@dW(-T~N@eJF= z0!$m3^o3=j^fR`eVb;!q>s%{Df!wEV5oM=>EkY#p<|+2<+Yx&6RAS#rpWe#t5qeER zuk|FoGz?4(3;f;<`F>ZL=*9|9dWCtjR`!?(oZoV9~9! zk4E=OXmuF&Q!s|ex~ljCGUKnVa~mJdXg&v!u~{3Po8FkTgvi|V7a#AMeB_#ti~upR z*&M;!vA5Wef$J5d-+pbwU4{GYgE%?8@_PNY?<78P ezcqa~2`Bu`ChVO^3l#cIw{24a8eSd3&E{V%-o{M; delta 1926 zcmai#Uu+ab9LHzoy4Sn5TBt24mG;`wKcro8cW-xRZqep^(wbDH8qh-E_HQASyV^fk zV+dE#pg!2b5R0u56cWfB%S8#8iIB zPHPfBtPk3wn!+E_Cp1w}1@>cDBNFW!Y;8DfkBlD3q&2MaX?tQ!llifXsfnURXl{FN zqyhjmispgacwrcYLIR}I^K}~bLfQ`{d&(crlyF+E$(Tpy`g_-7; zVNFB~v!%8Y$-T7g76%pDl!~$Mo|F-aej8fKO~Tu#Qba@opsXQ!t&Bdr*s=8U9@q`t z>?cn`c%Jq-UG$;J@@gv~@Iwa<+kM^jc0FVUS-EzN%A9&8M<-eX{6@bY3A9DI@ z5icXzN2g>1auG$<792^JjeJIQWh$~;x`#FFvnGk`);!j*o{z)79L2DdVjMP!%eGWJ zsuV<+2tYIh+XGQxAcA)AH$V^quqg|GJiuxapj{2^oe^RX-Le%2fM`uL2T?nqzfCW$ z(=DPVEHi=$QAI0Z)1O+dxK~>NC#(P`-eclq>9$f427Ur>O1 z)sc{oJ6kT){bLb#HZHqU>*HSeZdn2MaIH@}P6S2geD)doA95&`KACESqSpX)??UIy zquXGL3vug?b6ywxg{p7@VV&`{vF5$7*9K6`19l76oP*~6@$^9ri@Y(EF%N1I-OFeb zgOp2qgW(n=PW}s`>>)woj#WrLF504!u-0-X7B52#xKkut%AMhHXGZ+XjEf4rIM|O& z;&obu=}$_SSd6^p4n`fCV1{7Sh3bw6)r*piS%j=IDnH+u)3q{>x;#V$?4=Uw3`drT z>}kpGOm`&{X^ttYoCb;`#Xx+;33S$JeX#H}rI3l|I0?%Vd)O1JXx$0WYD@-bv5IB~ zwse)EbvpV8L|Z(xZqVhH?@CE}hmo>MP>B0*L)$gKH^BXp>g~LjH5jHlCaDiK6H@G#hIRRyX zXN6MOWV&xbrhIBWb6`)(g!a;c?_2C3B6ue2eB3kH%pXbks_(I=Ttb(06x)Tk0+$6+ zA-Y^d=w5YsE|U#I(Dk)U$wj7uSB#_@Mk!N)%X~^z(KdaT1@?|w{;a~@SWox1cg?cK z_hj};4TFb;)pR+iUTAF0!eW)l?CZC4`ak6!R(VKDKqv@bb;N{Z6ov1ud;!n?3!HgM Awg3PC diff --git a/etl/local_nifi_manager.sh b/etl/local_nifi_manager.sh new file mode 100644 index 000000000..ab873acc7 --- /dev/null +++ b/etl/local_nifi_manager.sh @@ -0,0 +1,300 @@ +#!/usr/bin/env bash +# A Simplified NiFi Processor Management Script for Local Databases +# +# This script manages NiFi processors and updates database connection configurations +# for a local setup, without OpenShift or port-forwarding. It assumes: +# - NiFi is running locally and accessible at http://localhost:8091 +# - TFRS and LCFS databases are running locally on specified ports. +# +# Usage: +# ./local_nifi_manager.sh [--debug|--verbose] +# +# Features: +# - No OpenShift or port-forwarding; everything runs locally. +# - Updates NiFi database controller connections to point to local DBs. +# - Triggers NiFi processors to run once and waits for them to finish. +# +# Requirements: +# - `jq` for JSON parsing. +# - NiFi running locally and accessible via the NiFi API. +# +# Notes: +# Update the processor and controller service IDs, as well as the database credentials, +# according to your local environment. + +set -euo pipefail + +# Debug Levels +readonly DEBUG_NONE=0 +readonly DEBUG_ERROR=1 +readonly DEBUG_WARN=2 +readonly DEBUG_INFO=3 +readonly DEBUG_VERBOSE=4 +readonly DEBUG_DEBUG=5 + +# Default Debug Level +DEBUG_LEVEL=${DEBUG_LEVEL:-$DEBUG_INFO} + +# Processor and Controller Service IDs (Update these as needed) +ORGANIZATION_PROCESSOR="328e2539-0192-1000-0000-00007a4304c1" +USER_PROCESSOR="e6c63130-3eac-1b13-a947-ee0103275138" +TRANSFER_PROCESSOR="b9d73248-1438-1418-a736-cc94c8c21e70" +TRANSACTIONS_PROCESSOR="7a010ef5-0193-1000-ffff-ffff8c22e67e" + +# Controller Service IDs for local DB connections (Update these) +LCFS_CONTROLLER_SERVICE_ID="32417e8c-0192-1000-ffff-ffff8ccb5dfa" +TFRS_CONTROLLER_SERVICE_ID="3245b078-0192-1000-ffff-ffffba20c1eb" + +# Local Database Configuration (Update as needed) +# Example: TFRS DB at localhost:5435, LCFS DB at localhost:5432 +TFRS_DB_PORT=5435 +TFRS_DB_USER="tfrs" +TFRS_DB_PASS="development_only" +TFRS_DB_NAME="tfrs" + +LCFS_DB_PORT=5432 +LCFS_DB_USER="lcfs" +LCFS_DB_PASS="development_only" +LCFS_DB_NAME="lcfs" + +# NiFi API URL +NIFI_API_URL="http://localhost:8091/nifi-api" +MAX_RETRIES=5 + +# Logging function +_log() { + local level=$1 + local message=$2 + local timestamp=$(date '+%Y-%m-%d %H:%M:%S') + + if [ "$level" -le "$DEBUG_LEVEL" ]; then + case $level in + $DEBUG_ERROR) printf "\e[31m[ERROR][%s] %s\e[0m\n" "$timestamp" "$message" >&2 ;; + $DEBUG_WARN) printf "\e[33m[WARN][%s] %s\e[0m\n" "$timestamp" "$message" >&2 ;; + $DEBUG_INFO) printf "\e[32m[INFO][%s] %s\e[0m\n" "$timestamp" "$message" >&2 ;; + $DEBUG_VERBOSE)printf "\e[34m[VERBOSE][%s] %s\e[0m\n" "$timestamp" "$message" >&2 ;; + $DEBUG_DEBUG) printf "\e[36m[DEBUG][%s] %s\e[0m\n" "$timestamp" "$message" >&2 ;; + esac + fi +} + +error() { _log $DEBUG_ERROR "$1"; } +warn() { _log $DEBUG_WARN "$1"; } +info() { _log $DEBUG_INFO "$1"; } +verbose() { _log $DEBUG_VERBOSE "$1"; } +debug() { _log $DEBUG_DEBUG "$1"; } + +error_exit() { + error "$1" + exit 1 +} + +curl_with_retry() { + local max_attempts=$1 + shift + local url=$1 + local method=${2:-GET} + local data=${3:-} + local attempt=1 + + info "Curl Request: $method $url" + verbose "Max Attempts: $max_attempts" + + while [ $attempt -le "$max_attempts" ]; do + debug "Attempt $attempt for $url" + local response + local http_code + + if [ -z "$data" ]; then + response=$(curl -sS -w "%{http_code}" -X "$method" "$url") + else + response=$(curl -sS -w "%{http_code}" -X "$method" -H "Content-Type: application/json" -d "$data" "$url") + fi + + http_code="${response: -3}" + response_body="${response:0:${#response}-3}" + + if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then + verbose "Curl successful. HTTP Code: $http_code" + echo "$response_body" + return 0 + fi + + warn "Curl attempt $attempt failed (HTTP $http_code). Retrying..." + ((attempt++)) + sleep $((attempt * 2)) + done + + error_exit "Curl failed after $max_attempts attempts: $url" +} + +enable_controller_service() { + local service_id=$1 + info "Enabling controller service $service_id" + local current_config revision_version + + current_config=$(curl_with_retry 3 "$NIFI_API_URL/controller-services/$service_id") + revision_version=$(echo "$current_config" | jq -r '.revision.version') + + curl_with_retry 3 "$NIFI_API_URL/controller-services/$service_id/run-status" PUT "{\"state\": \"ENABLED\", \"revision\": { \"version\": $revision_version }}" + info "Controller service $service_id enabled." +} + +execute_processor() { + local processor_id=$1 + + info "Triggering single execution for processor $processor_id" + local current_config revision_version + + current_config=$(curl_with_retry 3 "$NIFI_API_URL/processors/$processor_id") + revision_version=$(echo "$current_config" | jq -r '.revision.version') + + local run_once_payload=$(jq -n \ + --argjson revision_version "$revision_version" \ + '{ + "revision": { "version": $revision_version }, + "state": "RUN_ONCE", + "disconnectedNodeAcknowledged": false + }') + + local response + response=$(curl -sS -w "%{http_code}" -X PUT \ + -H "Content-Type: application/json" \ + -d "$run_once_payload" \ + "$NIFI_API_URL/processors/$processor_id/run-status") + + local http_code="${response: -3}" + local response_body="${response:0:${#response}-3}" + + if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then + info "Processor $processor_id triggered for single run successfully" + sleep 10 + else + error "Failed to trigger single run for processor $processor_id (HTTP $http_code)" + error "Response: $response_body" + return 1 + fi + + # Wait for completion + local max_wait_time=300 + local start_time=$(date +%s) + local timeout=$((start_time + max_wait_time)) + + while true; do + local current_time=$(date +%s) + if [ "$current_time" -gt "$timeout" ]; then + error "Processor $processor_id execution timed out after $max_wait_time seconds" + return 1 + fi + + local processor_status + processor_status=$(curl_with_retry 3 "$NIFI_API_URL/processors/$processor_id" | jq '.status.aggregateSnapshot') + + local active_threads=$(echo "$processor_status" | jq '.activeThreadCount') + local processed_count=$(echo "$processor_status" | jq '.flowFilesProcessed') + + verbose "Processor $processor_id - Active Threads: $active_threads, Processed: $processed_count" + + if [ "$active_threads" -eq 0 ]; then + info "Processor $processor_id completed execution" + break + fi + sleep 2 + done +} + +update_nifi_connection() { + local controller_service_id=$1 + local db_url=$2 + local db_user=$3 + local db_pass=$4 + + info "Updating NiFi controller service $controller_service_id" + + local controller_config + controller_config=$(curl_with_retry 3 "$NIFI_API_URL/controller-services/$controller_service_id") + local revision_version=$(echo "$controller_config" | jq '.revision.version') + local current_name=$(echo "$controller_config" | jq -r '.component.name') + + # Disable the controller service first + info "Disabling controller service $controller_service_id" + curl_with_retry 3 "$NIFI_API_URL/controller-services/$controller_service_id/run-status" PUT "{\"state\": \"DISABLED\", \"revision\": { \"version\": $revision_version }}" + + # Wait until disabled + while true; do + controller_config=$(curl_with_retry 3 "$NIFI_API_URL/controller-services/$controller_service_id") + local current_state=$(echo "$controller_config" | jq -r '.component.state') + if [ "$current_state" == "DISABLED" ]; then + break + fi + warn "Controller service not yet disabled. Retrying..." + sleep 2 + done + + local updated_config + updated_config=$(jq -n \ + --arg name "$current_name" \ + --arg db_url "$db_url" \ + --arg db_user "$db_user" \ + --arg db_pass "$db_pass" \ + --argjson version "$revision_version" \ + '{ + "revision": { "version": $version }, + "component": { + "id": "'$controller_service_id'", + "name": $name, + "properties": { + "Database Connection URL": $db_url, + "Database User": $db_user, + "Password": $db_pass + } + } + }') + + curl_with_retry 3 "$NIFI_API_URL/controller-services/$controller_service_id" PUT "$updated_config" + info "Controller service updated." + + enable_controller_service "$controller_service_id" +} + +main() { + while [[ $# -gt 0 ]]; do + case "$1" in + --debug) + DEBUG_LEVEL=$DEBUG_DEBUG + shift + ;; + --verbose) + DEBUG_LEVEL=$DEBUG_VERBOSE + shift + ;; + *) + break + ;; + esac + done + + info "Starting Local NiFi Manager" + info "Debug Level: $DEBUG_LEVEL" + + # Prepare DB URLs for local connections + local TFRS_DB_URL="jdbc:postgresql://localhost:$TFRS_DB_PORT/$TFRS_DB_NAME" + local LCFS_DB_URL="jdbc:postgresql://localhost:$LCFS_DB_PORT/$LCFS_DB_NAME" + + info "Updating TFRS DB connection..." + update_nifi_connection "$TFRS_CONTROLLER_SERVICE_ID" "$TFRS_DB_URL" "$TFRS_DB_USER" "$TFRS_DB_PASS" + + info "Updating LCFS DB connection..." + update_nifi_connection "$LCFS_CONTROLLER_SERVICE_ID" "$LCFS_DB_URL" "$LCFS_DB_USER" "$LCFS_DB_PASS" + + info "Executing processors..." + execute_processor "$ORGANIZATION_PROCESSOR" + execute_processor "$USER_PROCESSOR" + execute_processor "$TRANSFER_PROCESSOR" + execute_processor "$TRANSACTIONS_PROCESSOR" + + info "All processors executed successfully." + info "Check your local databases for the expected data." +} + +main "$@" diff --git a/etl/nifi/conf/flow.json.gz b/etl/nifi/conf/flow.json.gz index 3d193752f596216362a55b884d496abd5032dcb7..7030cbdd222a94a8878f86facc078dea5e05c9f9 100644 GIT binary patch literal 9602 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 literal 9539 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 diff --git a/etl/nifi/conf/flow.xml.gz b/etl/nifi/conf/flow.xml.gz index be40cbe0ae026a6dfc25865505fa4c0f63f0c964..1f2f7014e54df10056e918aa491ad8a774f49739 100644 GIT binary patch literal 15139 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 literal 14964 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 + chainReports.sort { a, b -> + a.traversal <=> b.traversal ?: a.compliance_report_id <=> b.compliance_report_id + } + + // Assign a single UUID for the chain + def groupUuid = UUID.randomUUID().toString() + + def version = 0 + chainReports.each { report -> + def currentStatusId = mapCurrentStatus( + report.fuel_supplier_status_id, + report.analyst_status_id, + report.manager_status_id, + report.director_status_id, + referenceData + ) + + def supplementalInitiator = report.is_supplemental ? "SUPPLIER_SUPPLEMENTAL" : null + def reportingFrequency = "ANNUAL" + + // create_user and update_user are always strings + def reportCreateUser = "imported_user" + def reportUpdateUser = "imported_user" + + // Insert Compliance Report + def lcfsReportId = insertComplianceReport( + statements.insertComplianceReportStmt, + report, + groupUuid, + version, + supplementalInitiator, + reportingFrequency, + currentStatusId, + reportCreateUser, + reportUpdateUser + ) + + // Insert history + def reportHistory = historyMap[report.compliance_report_id] ?: [] + totalHistoryInserted += insertComplianceReportHistory( + lcfsReportId, + reportHistory, + statements.insertComplianceReportHistoryStmt, + referenceData + ) + + def currentStatusName = currentStatusId ? referenceData.statusIdToName[currentStatusId] : null + def shouldReserve = ["Submitted", "Recommended by analyst", "Recommended by manager", "Assessed", "ReAssessed"].contains(currentStatusName) + def createdTransactionId = null + + if (shouldReserve) { + createdTransactionId = insertTransactionForReport( + statements.insertTransactionStmt, + report.organization_id, + 0, + "Reserved", + "imported_user", // create_user as a string + report.create_timestamp + ) + totalTransactionsInserted++ + } + + if (report.credit_transaction_id && creditTradesMap.containsKey(report.credit_transaction_id)) { + def ct = creditTradesMap[report.credit_transaction_id] + + // create_user as a string + def ctCreateUser = "imported_user" + + def quantity = ct.quantity + if (ct.transaction_type == "Credit Reduction") { + quantity = -quantity + } + + def associatedTransactionId = insertTransactionForReport( + statements.insertTransactionStmt, + ct.respondent_id ?: ct.initiator_id, + quantity, + "Adjustment", + ctCreateUser, + ct.create_date, + ct.transaction_effective_date + ) + totalTransactionsInserted++ + + createdTransactionId = associatedTransactionId + } + + if (createdTransactionId != null) { + updateComplianceReportWithTransaction(destinationConn, lcfsReportId, createdTransactionId) + } + + version++ + totalInserted++ + } + } + + destinationConn.commit() + + // Re-enable and refresh the materialized view + destinationConn.createStatement().execute(""" + CREATE OR REPLACE FUNCTION refresh_transaction_aggregate() + RETURNS void AS \$\$ + BEGIN + REFRESH MATERIALIZED VIEW CONCURRENTLY mv_transaction_aggregate; + END; + \$\$ LANGUAGE plpgsql; + """) + destinationConn.createStatement().execute('REFRESH MATERIALIZED VIEW CONCURRENTLY mv_transaction_aggregate') + + // Re-enable and refresh the materialized view + destinationConn.createStatement().execute(""" + CREATE OR REPLACE FUNCTION refresh_mv_director_review_transaction_count() + RETURNS void AS \$\$ + BEGIN + REFRESH MATERIALIZED VIEW CONCURRENTLY mv_director_review_transaction_count; + END; + \$\$ LANGUAGE plpgsql; + """) + destinationConn.createStatement().execute('REFRESH MATERIALIZED VIEW CONCURRENTLY mv_director_review_transaction_count') + + log.info("Inserted ${totalInserted} compliance reports, ${totalHistoryInserted} history records, and ${totalTransactionsInserted} transactions into LCFS.") +} catch (Exception e) { + log.error('Error occurred while processing compliance reports', e) + destinationConn?.rollback() + throw e +} finally { + if (sourceConn != null) sourceConn.close() + if (destinationConn != null) destinationConn.close() +} + +// ========================================= +// Helper Functions +// ========================================= + +def loadReferenceData(Connection conn) { + def statusMap = [:] + def idToName = [:] + def stmt = conn.createStatement() + def rs = stmt.executeQuery("SELECT compliance_report_status_id, status FROM compliance_report_status") + while (rs.next()) { + statusMap[rs.getString('status')] = rs.getInt('compliance_report_status_id') + idToName[rs.getInt('compliance_report_status_id')] = rs.getString('status') + } + rs.close() + stmt.close() + + return [ + statusMap: statusMap, + statusIdToName: idToName + ] +} + +def fetchComplianceReports(Connection conn) { + PreparedStatement stmt = conn.prepareStatement(SOURCE_REPORTS_QUERY) + ResultSet rs = stmt.executeQuery() + + def chains = [:].withDefault { [] } + + while (rs.next()) { + def rootId = rs.getObject('root_report_id') ?: rs.getInt('compliance_report_id') + def record = [ + compliance_report_id : rs.getInt('compliance_report_id'), + type_id : rs.getInt('type_id'), + organization_id : rs.getInt('organization_id'), + compliance_period_id : rs.getInt('compliance_period_id'), + supplements_id : rs.getObject('supplements_id'), + root_report_id : rs.getObject('root_report_id'), + latest_report_id : rs.getObject('latest_report_id'), + traversal : rs.getInt('traversal'), + nickname : rs.getString('nickname'), + supplemental_note : rs.getString('supplemental_note'), + fuel_supplier_status_id: rs.getObject('fuel_supplier_status_id'), + analyst_status_id : rs.getObject('analyst_status_id'), + manager_status_id : rs.getObject('manager_status_id'), + director_status_id : rs.getObject('director_status_id'), + create_user_id : rs.getInt('create_user_id'), + create_timestamp : rs.getTimestamp('create_timestamp'), + update_user_id : rs.getInt('update_user_id'), + update_timestamp : rs.getTimestamp('update_timestamp'), + report_type : rs.getString('report_type'), + compliance_period_desc : rs.getString('compliance_period_desc'), + is_supplemental : rs.getBoolean('is_supplemental'), + credit_transaction_id : rs.getObject('credit_transaction_id') + ] + chains[rootId] << record + } + rs.close() + stmt.close() + + return chains +} + +def fetchComplianceReportHistory(Connection conn) { + PreparedStatement stmt = conn.prepareStatement(SOURCE_HISTORY_QUERY) + ResultSet rs = stmt.executeQuery() + + def historyMap = [:].withDefault { [] } + + while (rs.next()) { + def reportId = rs.getInt('compliance_report_id') + def histRecord = [ + history_id : rs.getInt('history_id'), + compliance_report_id : reportId, + create_user_id : rs.getInt('create_user_id'), + create_timestamp : rs.getTimestamp('create_timestamp'), + fuel_supplier_status_id : rs.getObject('fuel_supplier_status_id'), + analyst_status_id : rs.getObject('analyst_status_id'), + manager_status_id : rs.getObject('manager_status_id'), + director_status_id : rs.getObject('director_status_id') + ] + historyMap[reportId] << histRecord + } + rs.close() + stmt.close() + + return historyMap +} + +def fetchApprovedCreditTrades(Connection conn) { + PreparedStatement stmt = conn.prepareStatement(SOURCE_CREDIT_TRADE_QUERY) + ResultSet rs = stmt.executeQuery() + + def trades = [:] + + while (rs.next()) { + def t = [ + transaction_id : rs.getInt('transaction_id'), + initiator_id : rs.getInt('initiator_id'), + respondent_id : rs.getInt('respondent_id'), + agreement_date : rs.getTimestamp('agreement_date'), + transaction_effective_date: rs.getTimestamp('transaction_effective_date'), + quantity : rs.getInt('quantity'), + create_user : rs.getInt('create_user'), + create_date : rs.getTimestamp('create_date'), + update_user : rs.getInt('update_user'), + update_date : rs.getTimestamp('update_date'), + transaction_type : rs.getString('transaction_type'), + price_per_unit : rs.getBigDecimal('price_per_unit') + ] + trades[rs.getInt('credit_trade_id')] = t + } + + rs.close() + stmt.close() + return trades +} + +def mapCurrentStatus(fuelSupplierStatus, analystStatus, managerStatus, directorStatus, referenceData) { + def fuel = fuelSupplierStatus?.toString()?.toLowerCase() ?: "" + def analyst = analystStatus?.toString()?.toLowerCase() ?: "" + def manager = managerStatus?.toString()?.toLowerCase() ?: "" + def director = directorStatus?.toString()?.toLowerCase() ?: "" + + // Priority: Director > Manager > Analyst > Supplier + + // Director logic + if (director == "accepted") { + return referenceData.statusMap["Assessed"] + } else if (director == "rejected" || director.contains("requested supplemental")) { + return null // exclude + } + + // Manager logic + if (manager == "recommended") { + return referenceData.statusMap["Recommended by manager"] + } else if (manager == "not recommended" || manager.contains("requested supplemental")) { + return null // exclude + } + + // Analyst logic + if (analyst == "recommended") { + return referenceData.statusMap["Recommended by analyst"] + } else if (analyst == "not recommended" || analyst.contains("requested supplemental")) { + return null // exclude + } + + // Supplier logic + if (fuel == "deleted") { + return null // exclude + } else if (fuel == "submitted") { + return referenceData.statusMap["Submitted"] + } else if (fuel == "draft") { + return referenceData.statusMap["Draft"] + } + + // If nothing matches, exclude + return null +} + +def prepareStatements(Connection conn) { + def INSERT_COMPLIANCE_REPORT_SQL = """ + INSERT INTO compliance_report ( + compliance_period_id, + organization_id, + current_status_id, + transaction_id, + compliance_report_group_uuid, + version, + supplemental_initiator, + reporting_frequency, + nickname, + supplemental_note, + create_user, + create_date, + update_user, + update_date + ) VALUES (?, ?, ?, NULL, ?, ?, ?::SupplementalInitiatorType, ?::ReportingFrequency, ?, ?, ?, ?, ?, ?) + RETURNING compliance_report_id + """ + + def INSERT_COMPLIANCE_REPORT_HISTORY_SQL = """ + INSERT INTO compliance_report_history ( + compliance_report_id, + status_id, + user_profile_id, + create_user, + create_date, + update_user, + update_date + ) VALUES (?, ?, ?, ?, ?, ?, ?) + """ + + def INSERT_TRANSACTION_SQL = ''' + INSERT INTO transaction ( + compliance_units, organization_id, transaction_action, effective_date, create_user, create_date, effective_status + ) VALUES (?, ?, ?::transaction_action_enum, ?, ?, ?, TRUE) + RETURNING transaction_id + ''' + + def UPDATE_COMPLIANCE_REPORT_TRANSACTION_SQL = ''' + UPDATE compliance_report + SET transaction_id = ? + WHERE compliance_report_id = ? + ''' + + return [ + insertComplianceReportStmt: conn.prepareStatement(INSERT_COMPLIANCE_REPORT_SQL), + insertComplianceReportHistoryStmt: conn.prepareStatement(INSERT_COMPLIANCE_REPORT_HISTORY_SQL), + insertTransactionStmt: conn.prepareStatement(INSERT_TRANSACTION_SQL), + updateComplianceReportTransactionStmt: conn.prepareStatement(UPDATE_COMPLIANCE_REPORT_TRANSACTION_SQL) + ] +} + +def insertComplianceReport(PreparedStatement stmt, Map report, String groupUuid, int version, String supplementalInitiator, String reportingFrequency, Integer currentStatusId, String createUser, String updateUser) { + stmt.setInt(1, report.compliance_period_id) + stmt.setInt(2, report.organization_id) + + if (currentStatusId == null) { + stmt.setNull(3, java.sql.Types.INTEGER) + } else { + stmt.setInt(3, currentStatusId) + } + + stmt.setString(4, groupUuid) + stmt.setInt(5, version) + stmt.setObject(6, supplementalInitiator) + stmt.setString(7, reportingFrequency) + stmt.setString(8, report.nickname) + stmt.setString(9, report.supplemental_note) + stmt.setString(10, createUser) + stmt.setTimestamp(11, report.create_timestamp ?: Timestamp.valueOf("1970-01-01 00:00:00")) + stmt.setString(12, updateUser) + stmt.setTimestamp(13, report.update_timestamp ?: report.create_timestamp ?: Timestamp.valueOf("1970-01-01 00:00:00")) + + def rs = stmt.executeQuery() + def insertedId = null + if (rs.next()) { + insertedId = rs.getInt('compliance_report_id') + } + rs.close() + return insertedId +} + +def insertComplianceReportHistory(Integer lcfsReportId, List historyRecords, + PreparedStatement historyStmt, + Map referenceData) { + int count = 0 + if (!historyRecords) return count + + historyRecords.each { h -> + def statusId = mapCurrentStatus( + h.fuel_supplier_status_id, + h.analyst_status_id, + h.manager_status_id, + h.director_status_id, + referenceData + ) + + // We'll just use a placeholder "imported_user" string for create_user and update_user + def createUser = "imported_user" + def timestamp = h.create_timestamp ?: Timestamp.valueOf("1970-01-01 00:00:00") + + // For user_profile_id, we can still set it if we have it, or null otherwise + def userProfileId = h.create_user_id + + log.warn("Processing history record:") + log.warn("lcfsReportId: ${lcfsReportId}, statusId: ${statusId}, userProfileId: ${userProfileId}, create_user: ${createUser}, timestamp: ${timestamp}") + log.warn("fuel_supplier_status_id: ${h.fuel_supplier_status_id}, analyst_status_id: ${h.analyst_status_id}, manager_status_id: ${h.manager_status_id}, director_status_id: ${h.director_status_id}") + + if (statusId == null) { + // Skip this history record because we didn't match a status + log.warn("Skipping history record for lcfsReportId: ${lcfsReportId} due to null statusId.") + return + } + + try { + historyStmt.setInt(1, lcfsReportId) + historyStmt.setInt(2, statusId) + + if (userProfileId == null) { + historyStmt.setNull(3, java.sql.Types.INTEGER) + } else { + historyStmt.setInt(3, userProfileId) + } + + // create_user (String) + if (createUser == null) { + historyStmt.setNull(4, java.sql.Types.VARCHAR) + } else { + historyStmt.setString(4, createUser) + } + + // create_date (timestamp) + historyStmt.setTimestamp(5, timestamp) + + // update_user (String) - same as create_user for simplicity + if (createUser == null) { + historyStmt.setNull(6, java.sql.Types.VARCHAR) + } else { + historyStmt.setString(6, createUser) + } + + // update_date (timestamp) + historyStmt.setTimestamp(7, timestamp) + + historyStmt.addBatch() + count++ + } catch (Exception e) { + log.error("Error inserting compliance report history for lcfsReportId: ${lcfsReportId}", e) + } + } + + try { + historyStmt.executeBatch() + } catch (Exception e) { + log.error("Error executing batch insert for compliance report history", e) + throw e + } + + return count +} + +def insertTransactionForReport(PreparedStatement stmt, int orgId, int quantity, String action, String createUser, Timestamp createDate, Timestamp effectiveDate = null) { + def effDate = effectiveDate ?: createDate ?: Timestamp.valueOf("1970-01-01 00:00:00") + + stmt.setInt(1, quantity) + stmt.setInt(2, orgId) + stmt.setString(3, action) + stmt.setTimestamp(4, effDate) + + if (createUser == null) { + stmt.setNull(5, java.sql.Types.VARCHAR) + } else { + stmt.setString(5, createUser) + } + + stmt.setTimestamp(6, createDate ?: Timestamp.valueOf("1970-01-01 00:00:00")) + + def rs = stmt.executeQuery() + def transactionId = null + if (rs.next()) { + transactionId = rs.getInt('transaction_id') + } + rs.close() + return transactionId +} + +def updateComplianceReportWithTransaction(Connection conn, int lcfsReportId, int transactionId) { + def updateStmt = conn.prepareStatement("UPDATE compliance_report SET transaction_id = ? WHERE compliance_report_id = ?") + updateStmt.setInt(1, transactionId) + updateStmt.setInt(2, lcfsReportId) + updateStmt.executeUpdate() + updateStmt.close() +} diff --git a/etl/nifi_scripts/user.groovy b/etl/nifi_scripts/user.groovy index 572d26880..67b4fee35 100644 --- a/etl/nifi_scripts/user.groovy +++ b/etl/nifi_scripts/user.groovy @@ -6,9 +6,9 @@ import java.sql.ResultSet def userProfileQuery = """ SELECT id as user_profile_id, keycloak_user_id, - email as keycloak_email, + COALESCE(NULLIF(email, ''), 'test@gov.bc.ca') as keycloak_email, username as keycloak_username, - email, + COALESCE(NULLIF(email, ''), 'test@gov.bc.ca') as email, title, phone, cell_phone as mobile_phone, From 541ee817a390b58fe5f32b22ba99973c8409cfb2 Mon Sep 17 00:00:00 2001 From: Alex Zorkin Date: Wed, 11 Dec 2024 13:22:57 -0800 Subject: [PATCH 2/7] feat: script updates --- etl/database/nifi-registry-primary.mv.db | Bin 102400 -> 90112 bytes etl/docker-compose.yml | 30 +++++++++++----------- etl/nifi/conf/flow.json.gz | Bin 9602 -> 9590 bytes etl/nifi/conf/flow.xml.gz | Bin 15139 -> 15103 bytes etl/nifi_scripts/compliance_report.groovy | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/etl/database/nifi-registry-primary.mv.db b/etl/database/nifi-registry-primary.mv.db index e7d0507ba192a544d1fc602ead8e4a1dc93c765c..7b385db3292499c1422929dd631ad49ebeb3fdb9 100644 GIT binary patch delta 1836 zcmcJQZ)h839LJyMNs}dK-DI@U&UQ&OYhcaVyUShf&-_#0h$3oJq}WbfFV8(sR@x=C zO>{%i7GF&8#cCfgq7yr(jDpO@41y?F8Ep8{f`1Tn@05W!1VKb(KFK9_iKXt9_u~28 z^Kd-(d%oY#eQ#+ku(au4i6LEl{qDhDT};wBQpCr|f~nY9s@Rq&RIGRRu)p`jZ~jaq z^fcdLj^s@_IdA32qNyg2TPI9OO^f_y-Zaw5g+ifd%3|Y6r72%2&KxJEq${?b7L{!B zIkK=gQ^=cqr#v}PG$sB-0h^L+sI2n%LqP!mw1|#^>cIJV@It7-_1yGRU&6W>qO!%9j?2{XlV| zDTije%JAE|BC?_Br)j-OqpkuDSY>M(KI-RCt+k*s z9H=6jolx<)3)O==L6vf#Qg(ofG@v35RF_<+Qpiv^RNsVd-v$-=PpD@8XQ(PIs0gbp z$JkJ=Z_wt6MfvDFDJr;cJKvvG1eL2Nj!2DccsSW3fxV>h-u32v~s$ghtp`SI)67 zhX!`(*vg7o!j|72t^GO+|M0UF`91d0*~`3UcrCH3Zw^*(J12CZeIHc&&p(UMohCcI z9uD&s0zZ7`WgedgFZ47rum2Ihn|XV8)ArC4JunLpv-_qK4I*)!NEF+wZ7jpTkU)_k zavl@0pV{!m1jHVLdtD-MGiGG=42_guI|X}!#8J**gVDP(PFisDcg!^GnU9;9rqcFI zqfqK@reqpxY%@3R1j?k~L}yyIY?gUAesJpy06qW$2YL1buFI8EPOa<1=Uf+F0SFZ) gD%Ml})#$Fc-N=)a*MaT*`wU8`YH3-Iq|f2nKinxq5C8xG delta 2372 zcmb7_TWl0n7{_O(?d*l+BD6%-UTFpCPRq>hoH=JULDmpsO+bqn0tnVKXJ;t4EtIy_ z2hyU(2W?E-)6xcOY`hW!Nw*V~hbAl!+8E;lNO(|Qc<}P1LZXQe2G3l!v$Qm7ANI?c zZ1rYcAz>$urNI?e7hwymSRQU??lo|fyQ9LGJ%{lN)5&vA8Z zp&oFnFn>^L4I=~LtO+cRi|&lx<^_pKhCf=C%EyKTN=RUu~ud{j5*_%6-hK6Ju?;^+2 zibhXeP7U}`DZ13%)zy<$Od_*CJ!xb$sprTEGp!(nAdo^>>dp0@HnZ$OQUDz@$Cva*b!jteHDUn~0s)6JKy?13b|k`8o)o3I;FGWZ7^h^P~S0fev^l6|iuEVGc9Xxmvp zJ|@ctvw(pAZccQLhAa*ClWki)4exjw*0S4kG-Oqnxz#L{Xc)O0d4U(2nTChfAS-1W zDhjI@$eLyWy5?#P?Hk!<&y5TTG&8=3UKu;Y9Bp?Oc4+d+vQTV~_=~_aX11;k>ne~< z0Fz-ILPbZaLgP~zn@s&$;Rx*8E0+sZHLkv`8!kLpUv^Hhe=^XQt;20M=o25^zZ+jH`U?c4F@GPc?=Sib1ApmGD-waex+*ibj-Ih92h>HpW?g%c zi9nGbXb=k+MmFGQS%6`H1N5&9fl`27drkM|H7%*S_CD+574e<}0w#uc@Kvt8Gq+rO zE500Mx6JG{p7N>_pkr^_4OW@G(e-)!yXe^4h!)$60x)4R1(T-gG;=0z%)WPppB~MG zSUY5BZbPJq=JFkE0ZqfttvDel&g_!daJcJJS@5YWaKc=5qNRk&XkGL(UPvrUC7Tx4 zONoq$q1YvHlOVJ#1EL+Df{Uhz!{NBcVLZ7EhXqbNzpsSD0vA3y=5koGX9$9)~wP4p$&UK!kDlW4%H@A)BKp><*4ri0`M-ZJQ1B&CBtD|J^m043&}@f&&U4 z+(xb0VjIPt5428E53u><&itTtqou9%zRvj{4}s|=Wy1=8E^0`X={03HZ_;(02cq7v z1SSN*$eOG6p#S+fhBkx#^^jMc+j5{kI>su4K5;XTFM1=(SPZ=pK(A-B20)NajN-`p E2R2gB0RR91 diff --git a/etl/docker-compose.yml b/etl/docker-compose.yml index 982c29ef0..8f0a3eec6 100755 --- a/etl/docker-compose.yml +++ b/etl/docker-compose.yml @@ -62,23 +62,23 @@ services: networks: - shared_network # TFRS database loaded with TFRS data - # tfrs: - # image: postgres:14.2 - # container_name: tfrs - # environment: - # POSTGRES_USER: tfrs - # POSTGRES_PASSWORD: development_only - # POSTGRES_DB: tfrs - # ports: - # - "5435:5432" - # volumes: - # - tfrs_data:/var/lib/postgresql/data - # restart: unless-stopped - # networks: - # - shared_network + tfrs: + image: postgres:14.2 + container_name: tfrs + environment: + POSTGRES_USER: tfrs + POSTGRES_PASSWORD: development_only + POSTGRES_DB: tfrs + ports: + - "5435:5432" + volumes: + - tfrs_data:/var/lib/postgresql/data + restart: unless-stopped + networks: + - shared_network volumes: - # tfrs_data: + tfrs_data: nifi_output: nifi_scripts: diff --git a/etl/nifi/conf/flow.json.gz b/etl/nifi/conf/flow.json.gz index 7030cbdd222a94a8878f86facc078dea5e05c9f9..89b79e452420f97efe40d13f7866b4b9ba20ba8b 100644 GIT binary patch delta 8698 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 YG(R8Ey!**7!cV9FA4?JPZPFtF0N*Vj+W-In delta 8709 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 diff --git a/etl/nifi/conf/flow.xml.gz b/etl/nifi/conf/flow.xml.gz index 1f2f7014e54df10056e918aa491ad8a774f49739..632c9e954ac199c903f408f80059bbb59cd95097 100644 GIT binary patch literal 15103 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 diff --git a/etl/nifi_scripts/compliance_report.groovy b/etl/nifi_scripts/compliance_report.groovy index 34b181ef7..04437b564 100644 --- a/etl/nifi_scripts/compliance_report.groovy +++ b/etl/nifi_scripts/compliance_report.groovy @@ -29,7 +29,7 @@ Process: // Fetch compliance reports with workflow states SOURCE_REPORTS_QUERY = """ WITH cr_chain AS ( - SELECT + SELECT DISTINCT cr.id AS compliance_report_id, cr.type_id, cr.organization_id, From 9744d969ac64b6fef350bcc9d2b2a6eaf346283a Mon Sep 17 00:00:00 2001 From: Alex Zorkin Date: Fri, 13 Dec 2024 16:11:24 -0800 Subject: [PATCH 3/7] feat: compliance report etl work --- .../lcfs/web/api/compliance_report/repo.py | 7 +- etl/database/nifi-registry-primary.mv.db | Bin 90112 -> 94208 bytes etl/nifi/conf/flow.json.gz | Bin 9904 -> 9990 bytes etl/nifi/conf/flow.xml.gz | Bin 15430 -> 15551 bytes etl/nifi_scripts/compliance_report.groovy | 937 +++++++++++------- etl/nifi_scripts/organization.groovy | 54 +- 6 files changed, 588 insertions(+), 410 deletions(-) diff --git a/backend/lcfs/web/api/compliance_report/repo.py b/backend/lcfs/web/api/compliance_report/repo.py index 194afb8d0..d633ffabe 100644 --- a/backend/lcfs/web/api/compliance_report/repo.py +++ b/backend/lcfs/web/api/compliance_report/repo.py @@ -3,6 +3,7 @@ from collections import defaultdict from datetime import datetime from lcfs.db.models.organization.Organization import Organization +from lcfs.db.models.user.UserProfile import UserProfile from lcfs.db.models.fuel.FuelType import FuelType from lcfs.db.models.fuel.FuelCategory import FuelCategory from lcfs.db.models.fuel.ExpectedUseType import ExpectedUseType @@ -381,9 +382,9 @@ async def get_reports_paginated( joinedload(ComplianceReport.history).joinedload( ComplianceReportHistory.status ), - joinedload(ComplianceReport.history).joinedload( - ComplianceReportHistory.user_profile - ), + joinedload(ComplianceReport.history) + .joinedload(ComplianceReportHistory.user_profile) + .joinedload(UserProfile.organization), ) ) diff --git a/etl/database/nifi-registry-primary.mv.db b/etl/database/nifi-registry-primary.mv.db index 35bd40492ff57ec847675a5539359c8067eedb22..c02fd000c0568f6388ea0a127a8a36a40f99f1fe 100644 GIT binary patch delta 9519 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 aX`g7^|2K`x3S*+YMb1|e99=b%&iCJrf{i%< delta 12334 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 diff --git a/etl/nifi/conf/flow.json.gz b/etl/nifi/conf/flow.json.gz index 5780d2a559a199dd8d2bf6d2dec99ced3c7111db..8f11783001e360b6dd75d691cf63bee303add2ff 100644 GIT binary patch delta 9688 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 ap>uCyf)(f@I%$)CCMgJ6!z%8sFaZDzxaf!g delta 9605 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 diff --git a/etl/nifi/conf/flow.xml.gz b/etl/nifi/conf/flow.xml.gz index 78bde91d751cffb8e7fa4f5ae0fbe55aeddf67b7..021f4e38307e5278389b231d868187c718aabba7 100644 GIT binary patch literal 15551 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 GApaj6nZKL> literal 15430 zcmZ9zV{|4@_@EtoV%vUV+jb_lZQGc5Vw)4&ww))oGqJt%`@j3{*|YtjyZTF=yXsU` zcU|{YMA0xH|KD}#XXUWTwQ%$NiXl?hN2s}8Sk5~35_Dj~@y^o%bT<#Qxb54e&_UBW z|Go5!KjihAv7$(^LZ@NbooFh89PoP>RjVpmZ5ZkM{sq~3$1@Si>EVF4t*m#^Q_KWY z%tNr}-Pr|h(?dIRFLIA5?j`=Z@MY^8|Kp|~>EK$@DVf3v`_6O;Qcj3vhu|aMiiq&$ zK>nsL>45xYq$uk>)WjVfspv1rInh(rPO6{c&_PwH!*=st$=}SIPU7jrz_PnCPh#dC zT0#Mg_@Ti-0%Gtp1~;a;0t=Ax3!b^kUe`&I8mk4Q_d=s(ssW^rU9M6CFR-fvwgb7! zUH{ct19bq7q7^Cv3p2@kQ3TtNl{Ye#hmU9A8UIc+nBXeiyx)3~NAJR8km0WB*G0 z6ufA?5hG%K{T%h( zx+0N{(F`XbKm0c5Bk-c&8ajR%6{exEa6>VPx2_o08ZS~|E|l@>Fu-WCjvg_V=2+F9 zje%=xQ0N2CEDr*mU~TRW)=@4V>`)lmP!48ys(9&y@BlsT>C_cSQ~LDlSA_6WG4rjR zcIqzmqZdn~IOQ)So>Hxayoyc}xa^{SEG45_*Hv(enovv)@z@^^T$IHs;Q>(}TwdvK z6ZHswZ%gVj3a;3qB%&m=6xFW@KDmkrfp(K1!G=tIO30m8+=N9u#)NkzhtyfKB3uSy zg=q34pX3xULN#WNNbA_EJ)=WyJP^4GH2I;O**xKAD%uItOXYXMh)TBl9JsUq?Ufi; zU9w9mu@ZJFis4DNXf)r^S!3-fzs9AF*{}yGfdgSBf0vFObA6ZM#ZL}Z<${bW_+$$d zeAtPS2i4myu8f!M?4$P0x&#eLX+)0S_A=%eb+0tp$GUNG{G(C zBu#p=uD(-Ptld$UdgLBlaQ?IP219S=>3K%j822IJW{S?ezGitMxe5XqN0G4|yO#S4 z`~?leJGq3<3WVGQ<{xDrCvFPntlEaLgsa+U*^7{qMp;X}M{v7caGI9)WLLRcyAL+n ze{LHUsmzskr@{;B=o!3Fhl0IrW$Aw_HJxnB(WAyYYSvgRS07%o*N%MVf)O^{(a^6e z3S?$m8akU-t49gR=T`4C(Vx74RXml7#oop8Ku8)$w4YB@Mpbi&eX7GJCSTN zFff)_W_#&#@&bd6S^b|~9q-TX$Coc}m&@`LXQ-&GmJBzuPS_|Y_xa2}c5nXv9sGJI zf)!?piHitB<{MScCXB?$^WmW3EUsp0xTW-0m?}WlufGmBe$cn=dDz>%JN&r$yZD{E znVYNk;ru2obJxzH+VoKGsRKx_;fGMpTwHv}f*nY2gr~(+LhfGAQR64+GydxSAHUElz#6a!+7=~c~#X|@mF`l>r8Za!7hc&Ci{=B>3xPFQ|oV8(OGAb z{?&5UhXe=dy}c$Bw@vnfcwKTK7#k!K2@t2S*l51N6+L^hm{k?!=l8P6TgmpI$!~TT z&Pf7zPGAm(egCy?=SlyTA0>f)!`^eC*|T7e=v+BQsOnIH;KHHz_sa~HJ8QztHi3}K z4fT)9GMb8dLHK;~iHjJdnCEq;4}Lh4DYo__HhgAzB|2bmJweT@^hf@uql>p&j;u$p zYK>Wa>N9H5==$3H*~@aahM%=K-U{#r*5hIY*wA)$uIbR&6Z4LED+j)stOJ5m0~u*1 zBE-Fq$?6tb*BJF!UB5;JyVb~^5s2|yLTNEjfMN&tz_VqpjX4SFr6V@H7}ocUApq-_S43% z?N9SESLH?j(_YO=CjN>xU0U->(xUc7zg;!|hEsK{F0k~9SQbWhmn#=qZg8-#(GLRq z_^xlxm41Jwv!)sLM>u4Xm5@scybC0B+nsGB&|B z{8IV>N-_pHW)6c~5CL+$u_;s<-6mS*Wkklw7B=UC4V~aER?dbAfo+1PWR9F?6pG+b zL11t@xS(D0_mI2VJ&{fu=JIF*gk;MJfzcu%&ITO`Fv*$#ed+Oj55|z=&oL{qf&Bv6 zeK~E&XR-!dePXqf|2w=%PbtiD*nNW3>#Mc5moZM@`w2*O3+?-bfZh2C?Cl!_ms;%o z`c#~^Bw12EWfXeR@z56>~-muRO{ z60FE$bjOJp4m4+q-1RgZ^8f1I{4`C+oF`~yz{)o4hbyQ;nv3X?3V;tne%Ng#aqz)! zms|Ndk+Tg{@@-rJaec@DQdv+*a;^(DEmlU)uAjoRjxt6sR*fpY#}G9eI3}>Kz7$;0TL~HrL?V&Wqz?|uh>a7z;i`nA!BZSFXjnCr^@~u+$D6>9YgFXs&q-p&`1m{d zFYHjI?fr6=oXgjL&QV%>BjUtR?M|qB!%3X%Ceg%`+P{v*Y({{(v>O}9frXhl{bY%4 zn|1R0ngEv}LQlZet&4Q$+=oEaNid1m2_z8%aYlh8e8x9)K7LQM2zUCF^TWt!0{XzTSGM6`)GPG0PwdWfBB=sB+ywOMB0PeN z4d2oY#J9S-!Jtko=b~k{;+mjyB>)pCs!>W4t>qI4aETYt!aQT3Vq#B$yYiJ@vGiflRG0 zi#`>L9d5kQ1F@cIc8Vvme|#_GE$PbZ8AD?DVA`KDcUqsP!$A9ixRJdZ66=s{8blHK z(_bbZad$nH!gY>bzDJ6m5Hs?kjHzNPaU;UyXi|$i$8Z|YObCc(C|40i>5w4_@sJ~Q z+`?ou_;W6r0Y_JY;S`oZHPqCM4SC56=pNQq}9Xlt)+eEpcu zN+7)TI{m%(kLtxM!68;R*2L8q#VrYkE0SFs86U4avOgee-gU39$s}5I!piLx5OypC z%!a@aR|o>-MxkpIsEOlDR23z8?zGKwGFyUNEyu|TY{5-k@I1N6qx7Z#BN;*KJj^xF zMhTz1)sKe&H+{|2_w}A=Sl^UckyrZU*?kN@Oy>>hPPc;ZfT8X0yPbd!1Ht_xT_u&h zXsne+d@b8JUuUe6Gy#MLD*0`G)UC%{!hK*K2@4Rityp-_)- z5Z(28zCg&P1pZsihDw*bEY5)0Rvqz#xtNOna&>tq^dznWtJ`gpxcEK!=&uOaqr7#A zleo;;!7EU41?L99;tgUA4>mvZPoXMOJBeV*$ZtKs@tS8K`z^ zZN=Fth{sc>xqvwDOd|KC)zT{Lt#{@a609^yS*{-tFb+VB_F@5gLuo37&!zf5nE>O>k?hg4c7_ z2oJ7gp4uRRo^MYVsw^}6l4&PwjmoGnW*x>BXFkmsIG4Z@=ztjZ*H~~M4hJWS_?V;u zZ_5B6%VjYw!>ndCQ_LgigapC1K)y3I#4j4q4SrC)D$|Kw^k`0ZNsB?7G?zYoW|jy4rYMu0qmA6kFGU1ug16t6c?eg-z#P;aO-hJpZ<)ps8z5$daR za=Z}LS$2CnJQQrLtstiBRn?I_9l>msXf z!LJ`d*BAsy3WGg#1CiD@)u?SlH#fx55GsDHbf&`$g1MnK$I+aBu)egkf%)p~*0M0e zSifysvPF_$O!p%PUFR&R8hewiJf8Vxcm)e@B0LxoGR~Q_!;Xsu@z7LG2am81V7tJlLqSCQCN&DBn zg_#6Z+meDLV>-VEj59$eK6?-(69!j=NerKH_rNZ`=|}uyZ2^Zfoq51vyU6}U990bQ zq+5{Wu;NB|sfChl~Y*mg%0-&n07NsWPy+jLrp}ne1zA1~F*= z8Hg#WwMU()wN4s3uQ|DJhIaCUD3v?g$|CS|^N`W7;^Tvr2>PYQp}KxAzsQ#ex=|&k zT;U(P1B9~1a7&89cF6YOP2GvX6BAIJ=(|bVj?+QZ+xqc5AnT7+CYz${8-QN@boegl zGtjG>=RZgF%MkkV3Q8Z2?jYU`qqef5Zzs{S)^Z*A^KhvjQjpFWO8*TfW0PoJFC(Ne znWL5g!iT??E4LQ;u_5R}>fGx-1X?72ArX!KJ-bkMef;+F$VgtsQW8RY$cG{3=`Yu2 zftmQBr|)30fe z5}u;X;@r#TPWvpOjpsJlatk>574|T&3BUTZ-%Nly`8o>#e+eMXpe_}d{Fj?93;(C| zqk>9~Ao2{hg^)sV;+0H?VtgAlsH5_mUafA;aTH)54GS60FNqf_!k?ER0=p2IWcR#- zrY)O}SVWdktaHku<5I+itN>(+aqyBpM#QHnT=L8ZVnED=MeB$g4vBjbkNXMrJ%J;my~ zr-#d++Aj$ay4?X;Ohk%pL*+Lz0c<($vq_)cVm@`JQ1g8ojcom_13(t)WPp0s%@(`L zHX;dPcIR_bt_kA*DNWU^Z4<+IDg&lPrla64b}MYZ#){O|MsLt z8XXYo>gj3c;r{S_pX^nbaX<NoyPv$r(*)JK&lmtDf^Dg*sC+u9S2_&{6`+=o7UtxM~7ppz1}%hTb|cxN^_j$KtQ5^Og&5obO!mpQS{#mcnzZIT0&VhEOLC?4tc9*l?WSZRSWVaDprI0*RK{ zNnLdeE{sWf*D^pZZ59z8GjCl17WZ}7e8eTlkr|kqAn1;$;}zrm*^tR9@d>t zMAeY0YZd!rj$+Uwh)+yHGI>e~gblt=Y#Hw_{w#GLHk4TqlY~8yl6R!ZRf`w4zL|U` z$5zlhERetQlS1$ZBRaYsf}XNallSg{kIsix7^7@FRBzw`&gW44y3OdQe zPmjq=9Ylb~Z9A&RK{F^NXfTMdN+rr>Nc&=7kvY3Q@(xV=l6j*$t1S-bh^Jsh={ltJ zib;l=W2;*Lan}TA!XEN!Mz`D$2%HlFA=`imzjB@(=&;K&udLI=Q(+KezQI2Q6AM!y zVS|OS6~iAO=A`dv!dCTaA|}rXXZs+alAGMWyUL}18DaNLyD7{LfKCgvz>%R*k_l3Q zi#q15MTa2sclJ!Iho+VeJMEeW8^0`h7q)KabzG#ME}#B$Kg6I9O_7VyIF; zOkB9pN7jK<=it@ChtAZMx*e4DOO@!dZFw!w-rFbl>*_N0$SE4i|$g$3fiHXKrWY#t~uYzumtmOuy(>pvtlI#{yD^2ZdHI$dP8m}D4(8r zZ`AN_@x20goeGGW2!I4P6x6aA3GzAUz7V`27RzF@-Bz+2Mp`+@d9-v8vXt=9ty8c4j?2T=7*_{?M?W!9e>d35i0gV3fl#0D<%%XU`C? z<(_T_n-UH(V9KnL;}j@A2~}EaV%{mxdX{KgHy#6oY&X3BJyv~Le)gd- z>gh)GuhvT5?2f7$4sN|I$v=9rSBB?zZ5^T&le^YQy~{?$M_x=vfhSlejT9Um2#e`* zo7pm(pnUO%e#@}fL&l;2Y)Bm05zCn>E$US3g=BtN%_t~WJ{^w=hYrVfP2-xqOwH9$ z0db{BiY>63sa<=wXLBe$P)Nrq)EMT~No&^>5#f7EKl@ z2TjWsFxE(j{|-^39mlgFA5DR_{McGgt9&I64xaGGNbe;YvT~=2yl=hv{QByCsUJ0d zOQG@!4!I>C*z=LC`s?rH_y8tgug+t2sOO;8EA_<`WeKc20rPQF+FY*q~gS-$41&;p&B2%^DQBcT_w-AZi zQ_Q}c9QEjjQ?0jB$OXpmcT^DB)y`ZM4G%&Tjc3z?smX6=!ETm`V00h9bI7AD%i{Ts zBS--2+7yyz)a33C*;IR|tYg#-?N@c2-eQw4nsLZ0p?8o3`32WkaEM$It@I$3W=9My z%-}9$Bl&1MiZfrfA%=VuL{qNd%VjEZ`IJ;@7#iJf(|q2jjHAz04DMjXN-6k!Xs zW4b)O6knP&=c2RS1!?=B@)_svAnm~i`nX<1Xkha|iHA7-6g1k~>bBbr6O7mJmqmhR zn6^4XH;;j`)p3*BG&W5R9)*6Awn-6uqYIxxnICJcQoRl43(r2RN{76Q_PN7D?T7Tu zpySJ&sTwADPk@0ZMJ}%`xAwN*ttY#I4_?=sx$pX;w5`Qr0!a>z@}u6+CSC-py1beT-R9U0I% z335Qwun~8s`Vd=mcbe05!}(`Tj{NYV25AxBoe{HOWxU0|Xh&uue8~{$BK-Bf;4lL8 z4w-FH;<}Q1NlRTMEo(MeiG`#-!rJ=-k1}g4oZ^+2{^%R#XjD3$&@k>(dZAQ*l0xZw zp)_mtX5Nts#G_(*q8Lcn0JCK9BIJq9KhVQyZX6fEYqj^)daJl=* zz|_;!w^!mZ42ILEpTFit@DG-Zkxw=GjWW57a%L3h)QCpjk;aXzsWTS7%jggJXWLGs zyiC*#b=Z6&?0`$1bX>Ze^G@m2gnO;F)WwFa77~Juq2Dlh0Q&ew!hVS|lT2Lh5!-zNG37KYxIpz#ifBhXZPMY@A;8 zB_S>Mntk9Iwl464mtD8?1)VgqaH5|#MR~*!pixyWUK-_hoL~$XwB)N;i?bLEduEDF zheZTBA($kX#2L`fKNC9)4-N}S(_jANUOqACMgLG`e(jdoyj(VFz%9_hObyU^&7^{d z)h+H+!Nty&cPuLPdt&*{VUu+zX>3MaYBu>GA?!*LnD@AD_6Q5VyUM%Eum$AF=W@sF z6dOa{0fP;nUdU3R!Gofj4LgAd@pT7DnDI) zOVm#Cl8V8<3Ix2J2wp^tecOd+9os&5xK;EL3&Ce}sy+uv=7P&trAfpm5W zfl_VwMZte!QS1W#)6u4{=N%QpSBRSF?^Xk7=`0*i>;aOOCmndfU?5dAG%a_*3$Bs7 zX=^$&PFrW8RV5UiWP5C*PtcC_Xrb^^(>@8r#XF|hD>+DL1f1Q+flJGCR*Dx9<<+eqcyu4faFbl?{MNi=N#`up7|1LaMQ zp!0a$`Y#SopwdaHz1JL2=X6zs@d05nyzlm3qOQ2`(%2kiHYC*~L&S|r_D{MTGN-nsjCnDc*ItW@c>lJn`c&zii0%jWycp~>9Hgl3Trr}sW z2#Mqk+YC(|ADT-1dIB+*o|nZYe;$3soHYxQe^08;GDB=|f1o19XtuSIF=+CBr&Ck`-#^2l4&foFk>qT)ax?)vr2egCG%B8LS( zx4fgHyOXEy`|Zu&n~&3Cv4@X`MMZ-e)7aJ5W4%%8i^0?+Chk>)hc%Ig=^Khs+sAqu zq+y912AYgGc+HhwdvH!dN>@2%3~_4)~q zXycO4FGsdEDb=1)%%_p-J73KL-F%RL_QY3AG93V;8XmqQ5*XM9Qq=Y3nd=%bx47dv>1 z8UZxp>1}cHZ((4@$NGV8s8huegcX^bms&9!;?_*Twcu0Wd+2igas2m8aomL3vpl6_ zp~18zfCB&>-6-&RVI}u@hnB)J`hGirKmg3R|DPxwTL+w0=v@u(L&-c`liGS<>GkxZ zFHl8cM+vavY}|hq{Qu2yY^5J%=Oxkt-{Z<5TUVD5k#XBXZj6xEnK^iqJZh5HH9#=s z;rEmFx#V`MN*lCIL+iz#Neq1s+p=if!(s@R+Wn&!2kFNSk_<^5O0A>PVxbZEB%)td;#c;ob4-Xx%gbRlgEydfyB1 z6!7Z6emvuJwHIr8kBVS5aTpR{?wH=GPof-_zkn*nAD<&mV}e%p7+!y;d~@RljGwBs zh=cfSbjmV@oI&`bm6x4a$FL{ZOkQ1_1_Pn0hCaFh4joX@wZG58=b@(j5BT*j3S1d` z^s3;E@#-*T8Rxuwj+ef?^B7?4`_`v6DO(6Ti>}NY8BT5cx;QueTYsPD=+*$2FEVQp z{z7=5!ITVl>pIK|bZ+9UKn}}m<{d{f@3`x&>#eT+XqMNE?|_Hd@sFVIL9K^Z!K;Hx zyKtIWagDdal2d2i5R_}Nb0X$h>uIAre>nHPm6UhK8FSfTlQp}UI4!L@i?GW_KPn43 zMhcsNJS1N0mB0zBM*m38UWMVo)#<<6 zxPZ>Spw}5`x#0mjf7`}1D=&sE--ZnypSPbB9ENRGaO}oGMR2TIos%Eq|GfSy&0AzC zNO~J7(5`Ai;#C@&VAytb0b2-|p9Tcf>gwS*>`$9tBj&rc1bJOn^MhkG@N9kEUH=5f zcJ{0GkKo-L{fnrf2##Y#-IJ{#I2QQ6Xh65hRqRh_YkTs&5t^agKk~n8K4|yP!nOVT zzf)O+ppkE>a2&ZdwbM{|RM~JF>fXZO_)xuAl0GGhY-;nP&9OVAZ(JBjFM*)aj$+q4 zGh_oVfs!$jt`(3wVCxWFe=UA#Y5SQc)Tf8Sx6&qSy}LPOl~I}OER0XzSur~X!w{g!GLSa zgv~>(sb7(~A{l&8O$1_-nP*prLU(h_+F&yt#N(7}ZdD@qet)7Qhfr-ow{>h#_%+X$ zICIx`QEBv zn#<{rJB57f?|L5i&TV`QlF>}TSN*6cxW22beXi6jEPTR9ec9JrYV#rDy@uRfhOD^j zws14<(%3=&EW)}AaVEu8Jl1`dGOe9+JD}=0R3dT`H^b8@j|AB=ZfJyi^5;uW5TW!l zD0T7GYBv7s7-0th<0i_xI5C?V$Etu+-%M*y++Km-3J0f@dmm9I zEmg(?rYEt}Fn@VlrskSwi8zfksu{{J-VXmBO08FZQMs6B*mr&t6{q?GDG6A!tz_D2E2j?$nGy-bfEa;Yd{ty`{`tV-&f9| zJegyJJ&u3g4hbBGruQ9Dg2AXt9(_$U1AksA+Nq|zFBWz>d%58&9eq^=+1Hf&6;=2_ zE8O2%swF`6_PRm#NQg1>DFaOMVG)CAB2;ssE(< zQ(5rw&ruAwh<=SwU>SSl);L5YoucG%WZnny!c;qna=Bb?H}+F?t644VWZ~dNET6$J zz`l0@90NS?RWA8voR6C@Z-<*b?H;{hptz-mIDYSwBoA2mc+G8H_ZvP=SvZS)vM@U6 z;V4e;JFwTM#XQAMTH9H=mFbnA6`$|OKPzK2{|l87E!RwO-3(PMKDm)#>z@u0ARCoyjLo=#cT3~A#sD-lv- zKNEO68e`w55V#)Xym9SCFeJ+nnuBvP5xqq*5jXs_No3;_#qcwon<0_4WFY9)ni=(#xLc* zwQXnLyU?Xn_Xit~BEG-V@{p z^u$B!D8p7RB0i^Lrj5LbtYen#$3mB=a3abe>_!Rt;26?XhoJ=1C+_=~g~GKHdNC4o znz3Y_-`h^o#$sc%fr4O8PUNk{hHKFGCd3ktp)4!)1+Tbk`WOIRK>9KJhp3#0f5>93 zwTe3LGtkEkAWw0ue4#wc=6Su1SOWQfiq3XQcnj4)d z3`#qVX>RQ=gwh!;>K7{eO^X-K$&MLjxBsLaFZeMhVs!iZAn@(&U^}p#Yh!?jYnjGP zA{CPGm7{hmMb?0A8Ss?VAAv9&Y26Ch8IV?K)AG{|=IS9TXOC@BU{nrjm05nota>eK znWSxzPF*_mDYuN$@{?f+LZ7!=K#~QYH$TIDc##WVMz!soTX+$K{{k0Yc`H;cn`3Gn zUjz7f5TAW)5J1T4@961HDUs*B1gef*b|#O12LWHyz&DfSy#l%d?*E~wc`PyvW9$y% zc1e<;J+hgFs07V7VD#k5CwF&$1};009PxKCzEjB7Yg2#G!{p(e9;byruL|;E#kRts zzCMq4H|oApJ~vIn5cr?E)C2W6RX2DN)=#b*^OI-h0qGr0uoDDF0)y%mKh!9JP)f-K ziC0JtzBXrd3xjn@MM3cIEPnvzl=V20y@208(SpC{?csj>mHb9I1aUX-&@P%RTzjAQ zvkql_D0Ykss?lj4BI9+4wNOJi7-0q>4pAMi z$O^#&h{cxaX8!)EOAIgeTAl&hAvppg`va6?x40nno9X7eXgQ-6So?&IT)&$6|8zT> z4jBx4>l~I#lc|;IQLeBxh1&TOO-I|B>dJ3_!ftuZYODHE{64#U`x5ZB06XIV5n;vW zPZA^tGu-!FrK$r5yP`*%2u~Y@Cb){PMVsk~skj|`qOuy_?0b2QzXyEd@At+@T^(hO zA23JfwjxfDizU7}cD^~U{;OGQ1P&+Hu5LRIu4&+=CPc)G`p;RJ`m;L~wbiYvM>h1s za@@SF^K0XaJ0ip}90@}^K!Cp&RI5DG`-S&YgF?(ppc4_2s8&z-V!o=14cehUuo84M zN56IUMfn{PdW|QXX?C||@xnERRnCz%%rZGyNHuBJOOPq)P^KYE zOEQ>DMI3Ye&kG~w2fZj=;~;6jr_-p2uSid3M{RRCxBIoDP^-Z}j@r`$W*f7iP`d~( zv(FxM=~1umzNU>9hu3A?F2yKYhmt+-^Y6P{6o@IS$xcNZZ)=S<5eSG_4BF?q(uFX<7?ydu}?^s59j-h95Q>W zhZ)*oeqY*&RwLNkS4%%kb#Mn2D<-zn?cZe9@V>cK#jUj;erDkDCbTQ7Rb*`9Ii$B4 z{kMgKl8P8;;n4M?L*ukPbU=WXOSpO=v`QJ8kI9hPUmYQjGchVK)#b(C zDz3@lQy=-xBuD{BxS=V`oc|jDQU1tx3RtV`pcfv)BbNVS(v2oyU|c~jG@r(_glo*v zCyOfLn)p0;F;X!XS1ash6(&K{?%)``N`XSraDLlv48f6vj?`=TNO?YPs5 zI$7z4y^pL;(){hUM|TYCk5 z3GUuo^sQd@enAov3T1vhUI#>(4;uA-O)*|<+J>*RqB`N*C}&$)A#wpg9IigPQb8hW zQ7M=QrGe*~{dGU3%cli@z9e?`NQ^v~V}_~p++p_I{q|tp%F=mgb;&S+cmC@H&lCzH z#IQc2E!pzgb$-?2oY5LqqE+f7HF>9h*Gq+qt6J zK6L`6!r*emVzA;NkC7N0mt3p+I#C8nZQy0cc|@ntbH_6_p;6;|lwU8a4*e&7O5XHTjwU3hUza*xv1by`I(=au`g0gJ9g@NlGEmrF7|NRoLlMQ=4 z(>;e=l;wO&?Xn({YGufD&whT(*f9`z+yarfH>eK;;p`Xx32aQW%ao=BFK+k_1On|g zB99bmx26OD<`zF@8!>k)Rvg$Edz#{cl)zc=#RmRDojL5i(HAHFLm31YR*H5r^AMjL zF|0E}BoPvv`Q$dmJu6k#tP8x|4Z!8pWqb1X3}e)bhD^T(onc74=SfnP)iZvU?ykX8 zVRfDZ6|vC42$=s7-<%o*@Jho!0e#xlmzJs2Gmh9h)xBFxvE|zR^FyYvaSKXNrdZb5 z*_n^(a=1+DHawXm65}An=lgw#W6N`E5A?KmnRN)BcMuJ`L8F6ioii3BrM%tY&XZ#y zOvE;K>X45s)|b}In`i7gwp7*2`v3{G>U~RVDP*Jm6Wz@Izf%B#~U7+Yd3yQ#yhtI z=`%O7BLyd9Yo}trijn!aJ135}uL~f&#ri{Zwgw@&Y361#JsM~hFNe@-UF${g&2mJ) z?QrZvWi;Cwi+^uoV4UK~@Y#Bc+yd=BMbBn7m`74s`Ho}gB-iv+;y&EebWV~6PuKIbo;85ya{CvZAj0sM%*^Fi9=dM5&Xe?2yY(4RWcCeoplB` z-^ZGL;Qpg(EwBotpynLO-v6_I(#T!+Ds{iyij@x8eGIOA4X;)usIKgc)>ob}tNMd? zSr-6aYOSLVNLy~HI!C!$u1n&qvXicdC6NTd?Ey~!FZg4Z@$ALJw7b3yR${HgFeTU<@1s9<9r;mSi@p?Z+vuC!Q_vM>vy00%$bREHGcUZCkOAJR50?p4MV{8?vLNLpeyJmQku=ao)*`S7C+-z>fhPE!Xe4GN?q(#Hk z=j)~DFJ~$^n2{Vj6q{(||H-AI(-{QZPrKyLhn|r51eZNQyAoI7;UbtH?KKMha(fr> z$bH9`c}bj+ginoTv;v^{UcO%VX+yycrb7I21iVWU@-+z-2EK2JyH z=6Sn+H6ZH-@7*O{v=1KnVRE+6P6|j$i*-S>;L0{6av5zu@@&K);XxpqdJR0XLL)7kLN(Y*oU4>O z0=*|tf=TYH@72NG-Ju?};7u!&kyot3+;I0zGi-^VOe0aL8grQJpFzjBRI6`lxDk|k zfioAF3Q7>ca`=kiWUrKSx_Xov6uC#Dda_!_zvU_~B9{eU9pO01iZV21R7&DXNs!s` z0|f}!T7-4;n_WyHEPfy^fwn6n{CUp>BoQF@MTS=<+8Qq}A{~+*fs3kV&DEv6Z4HEF bX3Py(P1}1oFl`PY`V!Qm5M$|81_$|Hgc)gk diff --git a/etl/nifi_scripts/compliance_report.groovy b/etl/nifi_scripts/compliance_report.groovy index 04437b564..166ad9bcc 100644 --- a/etl/nifi_scripts/compliance_report.groovy +++ b/etl/nifi_scripts/compliance_report.groovy @@ -2,104 +2,100 @@ import groovy.json.JsonSlurper import java.sql.Connection import java.sql.PreparedStatement import java.sql.ResultSet -import java.time.OffsetDateTime import java.sql.Timestamp import java.util.UUID +import java.nio.charset.StandardCharsets +import java.time.OffsetDateTime +import java.time.Instant /* -This script migrates compliance reports and their supplemental versions from TFRS -to LCFS, including compliance report history and associated transactions. - -Process: -1. Extract compliance reports from TFRS. -2. Identify chains of reports (root and supplements), assign a compliance_report_group_uuid, and version the chain. -3. Map TFRS workflow states to LCFS compliance_report_status. -4. Insert LCFS compliance_report records. -5. Migrate compliance_report_history records and map their statuses. -6. For each compliance report that reaches a "Submitted" or above state, create a corresponding "In reserve" transaction in LCFS. -7. If a TFRS compliance report references a credit_transaction that is one of "Credit Validation", "Part 3 Award", - "Credit Reduction", or "Administrative Adjustment" and "Approved", insert a corresponding LCFS transaction. -8. Update the compliance_report.transaction_id column in LCFS if a transaction is created for that report. +Simplified and revised script for migrating compliance reports from Source DB to Destination DB. + +Key Features: +1. Processes each compliance report individually using a single comprehensive query. +2. Assigns a deterministic group_uuid based on root_report_id. +3. Excludes "Deleted" reports and includes "Rejected" reports. +4. Maps create_user and update_user to user.display_name. +5. Inserts compliance reports, their histories, and associated transactions. +6. Ensures sequence integrity for compliance_report_id. +7. Facilitates efficient testing by allowing deletion of inserted records based on display_name. +8. Adds handling for 'in reserve' transactions based on snapshot data. */ // ========================================= // Queries // ========================================= -// Fetch compliance reports with workflow states -SOURCE_REPORTS_QUERY = """ - WITH cr_chain AS ( - SELECT DISTINCT - cr.id AS compliance_report_id, - cr.type_id, - cr.organization_id, - cr.compliance_period_id, - cr.supplements_id, - cr.root_report_id, - cr.latest_report_id, - cr.traversal, - cr.nickname, - cr.supplemental_note, - cws.fuel_supplier_status_id, - cws.analyst_status_id, - cws.manager_status_id, - cws.director_status_id, - cr.create_user_id, - cr.create_timestamp, - cr.update_user_id, - cr.update_timestamp, - crt.the_type AS report_type, - cp.description AS compliance_period_desc, - CASE WHEN cr.supplements_id IS NOT NULL THEN TRUE ELSE FALSE END AS is_supplemental, - cr.credit_transaction_id - FROM compliance_report cr - JOIN compliance_report_type crt ON cr.type_id = crt.id - JOIN compliance_period cp ON cp.id = cr.compliance_period_id - JOIN compliance_report_workflow_state cws ON cr.status_id = cws.id +// Consolidated source query that gathers all relevant information for each compliance report +def SOURCE_CONSOLIDATED_QUERY = """ +WITH + compliance_history AS ( + SELECT + crh.compliance_report_id, + json_agg( + json_build_object( + 'history_id', crh.id, + 'create_user_id', crh.create_user_id, + 'create_timestamp', crh.create_timestamp, + 'fuel_supplier_status_id', cws.fuel_supplier_status_id, + 'analyst_status_id', cws.analyst_status_id, + 'manager_status_id', cws.manager_status_id, + 'director_status_id', cws.director_status_id + ) + ) AS history_json + FROM compliance_report_history crh + JOIN compliance_report_workflow_state cws ON crh.status_id = cws.id + GROUP BY crh.compliance_report_id ) - SELECT * FROM cr_chain - ORDER BY root_report_id NULLS FIRST, traversal, compliance_report_id; -""" - -// Fetch compliance report history -SOURCE_HISTORY_QUERY = """ - SELECT - crh.id AS history_id, - crh.compliance_report_id, - crh.create_user_id, - crh.create_timestamp, - hws.fuel_supplier_status_id, - hws.analyst_status_id, - hws.manager_status_id, - hws.director_status_id - FROM compliance_report_history crh - JOIN compliance_report_workflow_state hws ON crh.status_id = hws.id - ORDER BY crh.compliance_report_id, crh.create_timestamp; -""" - -// Fetch associated credit trades (validations/reductions/etc.) -SOURCE_CREDIT_TRADE_QUERY = """ - SELECT - ct.id AS transaction_id, - ct.initiator_id AS initiator_id, - ct.respondent_id AS respondent_id, - ct.date_of_written_agreement AS agreement_date, - ct.trade_effective_date AS transaction_effective_date, - ct.number_of_credits AS quantity, - ct.create_user_id AS create_user, - ct.create_timestamp AS create_date, - ct.update_user_id AS update_user, - ct.update_timestamp AS update_date, - ctt.the_type AS transaction_type, - ct.fair_market_value_per_credit AS price_per_unit, - ct.id AS credit_trade_id - FROM - credit_trade ct - JOIN credit_trade_type ctt ON ct.type_id = ctt.id - JOIN credit_trade_status cts ON ct.status_id = cts.id - WHERE - ctt.the_type IN ('Credit Validation', 'Part 3 Award', 'Credit Reduction', 'Administrative Adjustment') - AND cts.status = 'Approved'; +SELECT + cr.id AS compliance_report_id, + cr.type_id, + cr.organization_id, + cr.compliance_period_id, + cr.supplements_id, + cr.root_report_id, + cr.latest_report_id, + cr.traversal, + cr.nickname, + cr.supplemental_note, + cws.fuel_supplier_status_id, + cws.analyst_status_id, + cws.manager_status_id, + cws.director_status_id, + cr.create_user_id, + cr.create_timestamp, + cr.update_user_id, + cr.update_timestamp, + crt.the_type AS report_type, + cp.description AS compliance_period_desc, + CASE WHEN cr.supplements_id IS NOT NULL THEN TRUE ELSE FALSE END AS is_supplemental, + cr.credit_transaction_id, + ch.history_json, + crs.snapshot AS compliance_report_snapshot, -- Added snapshot + -- CreditTrade Fields (One-to-One Relationship) + ct.id AS credit_trade_id, + ct.initiator_id, + ct.respondent_id, + ct.date_of_written_agreement, + ct.trade_effective_date, + ct.number_of_credits, + ct.create_user_id AS ct_create_user_id, + ct.create_timestamp AS ct_create_timestamp, + ct.update_user_id AS ct_update_user_id, + ct.update_timestamp AS ct_update_timestamp, + ctt.the_type AS credit_trade_type, + ct.fair_market_value_per_credit +FROM compliance_report cr +JOIN compliance_report_type crt ON cr.type_id = crt.id +JOIN compliance_period cp ON cp.id = cr.compliance_period_id +JOIN compliance_report_workflow_state cws ON cr.status_id = cws.id +LEFT JOIN compliance_history ch ON cr.id = ch.compliance_report_id +LEFT JOIN compliance_report_snapshot crs ON cr.id = crs.compliance_report_id -- Added join +LEFT JOIN credit_trade ct ON cr.credit_transaction_id = ct.id +LEFT JOIN credit_trade_type ctt ON ct.type_id = ctt.id +LEFT JOIN credit_trade_status cts ON ct.status_id = cts.id +WHERE cr.type_id = 1 +ORDER BY cr.root_report_id NULLS FIRST, cr.traversal, cr.id; """ // ========================================= @@ -120,9 +116,10 @@ try { destinationConn = destinationDbcpService.getConnection() destinationConn.setAutoCommit(false) - // Disable refresh of the materialized view - destinationConn.createStatement().execute('DROP FUNCTION IF EXISTS refresh_transaction_aggregate() CASCADE;') - destinationConn.createStatement().execute(""" + // Disable refresh of the materialized views + def stmt = destinationConn.createStatement() + stmt.execute('DROP FUNCTION IF EXISTS refresh_transaction_aggregate() CASCADE;') + stmt.execute(""" CREATE OR REPLACE FUNCTION refresh_transaction_aggregate() RETURNS void AS \$\$ BEGIN @@ -131,136 +128,231 @@ try { \$\$ LANGUAGE plpgsql; """) - // Disable the refresh function - destinationConn.createStatement().execute('DROP FUNCTION IF EXISTS refresh_mv_director_review_transaction_count() CASCADE;') - destinationConn.createStatement().execute(""" - CREATE OR REPLACE FUNCTION refresh_mv_director_review_transaction_count() - RETURNS void AS \$\$ - BEGIN - -- Temporarily disable the materialized view refresh - END; - \$\$ LANGUAGE plpgsql; - """) + stmt.execute('DROP FUNCTION IF EXISTS refresh_mv_director_review_transaction_count() CASCADE;') + stmt.execute(""" + CREATE OR REPLACE FUNCTION refresh_mv_director_review_transaction_count() + RETURNS void AS \$\$ + BEGIN + -- Temporarily disable the materialized view refresh + END; + \$\$ LANGUAGE plpgsql; + """) + stmt.close() + // Load reference data for status mapping def referenceData = loadReferenceData(destinationConn) - // Fetch compliance reports and chains - def chains = fetchComplianceReports(sourceConn) - - // Fetch compliance report history - def historyMap = fetchComplianceReportHistory(sourceConn) - - // Fetch associated credit trades - def creditTradesMap = fetchApprovedCreditTrades(sourceConn) + // Fetch user profiles to map user IDs to display names + def userProfiles = fetchUserProfiles(sourceConn) - // Prepare statements + // Prepare database statements def statements = prepareStatements(destinationConn) + // Initialize counters int totalInserted = 0 int totalHistoryInserted = 0 int totalTransactionsInserted = 0 - chains.each { rootId, chainReports -> - chainReports.sort { a, b -> - a.traversal <=> b.traversal ?: a.compliance_report_id <=> b.compliance_report_id - } + // Execute the consolidated compliance reports query + PreparedStatement consolidatedStmt = sourceConn.prepareStatement(SOURCE_CONSOLIDATED_QUERY) + ResultSet rs = consolidatedStmt.executeQuery() - // Assign a single UUID for the chain - def groupUuid = UUID.randomUUID().toString() - - def version = 0 - chainReports.each { report -> - def currentStatusId = mapCurrentStatus( - report.fuel_supplier_status_id, - report.analyst_status_id, - report.manager_status_id, - report.director_status_id, - referenceData - ) - - def supplementalInitiator = report.is_supplemental ? "SUPPLIER_SUPPLEMENTAL" : null - def reportingFrequency = "ANNUAL" - - // create_user and update_user are always strings - def reportCreateUser = "imported_user" - def reportUpdateUser = "imported_user" - - // Insert Compliance Report - def lcfsReportId = insertComplianceReport( - statements.insertComplianceReportStmt, - report, - groupUuid, - version, - supplementalInitiator, - reportingFrequency, - currentStatusId, - reportCreateUser, - reportUpdateUser - ) - - // Insert history - def reportHistory = historyMap[report.compliance_report_id] ?: [] - totalHistoryInserted += insertComplianceReportHistory( - lcfsReportId, - reportHistory, - statements.insertComplianceReportHistoryStmt, - referenceData - ) - - def currentStatusName = currentStatusId ? referenceData.statusIdToName[currentStatusId] : null - def shouldReserve = ["Submitted", "Recommended by analyst", "Recommended by manager", "Assessed", "ReAssessed"].contains(currentStatusName) - def createdTransactionId = null - - if (shouldReserve) { - createdTransactionId = insertTransactionForReport( - statements.insertTransactionStmt, - report.organization_id, - 0, - "Reserved", - "imported_user", // create_user as a string - report.create_timestamp - ) - totalTransactionsInserted++ + log.warn("Creating Compliance Report Objects") + + while (rs.next()) { + def record = [ + compliance_report_id : rs.getInt('compliance_report_id'), + type_id : rs.getInt('type_id'), + organization_id : rs.getInt('organization_id'), + compliance_period_id : rs.getInt('compliance_period_id'), + supplements_id : rs.getObject('supplements_id'), + root_report_id : rs.getObject('root_report_id'), + latest_report_id : rs.getObject('latest_report_id'), + traversal : rs.getInt('traversal'), + nickname : rs.getString('nickname'), + supplemental_note : rs.getString('supplemental_note'), + fuel_supplier_status_id : rs.getString('fuel_supplier_status_id'), // Now String + analyst_status_id : rs.getString('analyst_status_id'), + manager_status_id : rs.getString('manager_status_id'), + director_status_id : rs.getString('director_status_id'), + create_user_id : rs.getInt('create_user_id'), + create_timestamp : rs.getTimestamp('create_timestamp'), + update_user_id : rs.getInt('update_user_id'), + update_timestamp : rs.getTimestamp('update_timestamp'), + report_type : rs.getString('report_type'), + compliance_period_desc : rs.getString('compliance_period_desc'), + is_supplemental : rs.getBoolean('is_supplemental'), + credit_transaction_id : rs.getObject('credit_transaction_id'), + history_json : rs.getString('history_json'), + compliance_report_snapshot : rs.getString('compliance_report_snapshot'), // Added snapshot + // CreditTrade Fields + credit_trade_id : rs.getObject('credit_trade_id'), + initiator_id : rs.getObject('initiator_id'), + respondent_id : rs.getObject('respondent_id'), + date_of_written_agreement : rs.getDate('date_of_written_agreement'), + trade_effective_date : rs.getDate('trade_effective_date'), + number_of_credits : rs.getInt('number_of_credits'), + ct_create_user_id : rs.getObject('ct_create_user_id'), + ct_create_timestamp : rs.getTimestamp('ct_create_timestamp'), + ct_update_user_id : rs.getObject('ct_update_user_id'), + ct_update_timestamp : rs.getTimestamp('ct_update_timestamp'), + credit_trade_type : rs.getString('credit_trade_type'), + fair_market_value_per_credit : rs.getBigDecimal('fair_market_value_per_credit') + ] + + // Insert Compliance Report History from JSON + def historyJson = record.history_json + def historyRecords = historyJson ? new JsonSlurper().parseText(historyJson).sort { a, b -> + def aTime = a.create_timestamp ? OffsetDateTime.parse(a.create_timestamp).toInstant() : Instant.EPOCH + def bTime = b.create_timestamp ? OffsetDateTime.parse(b.create_timestamp).toInstant() : Instant.EPOCH + aTime <=> bTime + } : [] + + // Parse the snapshot JSON to extract in reserve quantity and convert it to an Integer + def snapshotJson = record.compliance_report_snapshot + def inReserveQuantity = null + if (snapshotJson) { + try { + def snapshot = new JsonSlurper().parseText(snapshotJson) + def quantityStr = snapshot?.summary?.lines?.get('25') + if (quantityStr != null) { + inReserveQuantity = safeConvertToInt(quantityStr) + if (inReserveQuantity == null) { + log.error("Invalid in reserve quantity format for compliance_report_id: ${record.compliance_report_id}") + } + } else { + log.warn("In reserve quantity not found in snapshot for compliance_report_id: ${record.compliance_report_id}") + } + } catch (Exception e) { + log.error("Error parsing snapshot JSON for compliance_report_id: ${record.compliance_report_id}", e) } + } else { + log.warn("No snapshot found for compliance_report_id: ${record.compliance_report_id}") + } + + // Determine the root ID for UUID generation + def rootId = record.root_report_id ?: record.compliance_report_id + + // Generate a deterministic UUID based on rootId + def groupUuid = UUID.nameUUIDFromBytes(rootId.toString().getBytes(StandardCharsets.UTF_8)) + + def version = (record.compliance_report_id == record.root_report_id) ? 0 : (record.traversal ?: 0) + + // Determine the current status ID based on workflow states and history + def currentStatusId = mapCurrentStatus( + record.fuel_supplier_status_id, + record.analyst_status_id, + record.manager_status_id, + record.director_status_id, + referenceData, + historyRecords + ) - if (report.credit_transaction_id && creditTradesMap.containsKey(report.credit_transaction_id)) { - def ct = creditTradesMap[report.credit_transaction_id] + // Exclude reports with non-matching statuses like deleted + if (currentStatusId == null) { + continue + } + + // Map create_user and update_user to display_name + def reportCreateUser = userProfiles[record.create_user_id] ?: "imported_user" + def reportUpdateUser = userProfiles[record.update_user_id] ?: reportCreateUser - // create_user as a string - def ctCreateUser = "imported_user" + def supplementalInitiator = record.is_supplemental ? "SUPPLIER_SUPPLEMENTAL" : null + def reportingFrequency = "ANNUAL" - def quantity = ct.quantity - if (ct.transaction_type == "Credit Reduction") { + // Insert Compliance Report into LCFS + def lcfsReportId = insertComplianceReport( + statements.insertComplianceReportStmt, + record, + groupUuid.toString(), + version, + supplementalInitiator, + reportingFrequency, + currentStatusId, + reportCreateUser, + reportUpdateUser + ) + totalInserted++ + + def insertedHistoryCount = insertComplianceReportHistory( + lcfsReportId, + historyRecords, + statements.insertComplianceReportHistoryStmt, + referenceData, + userProfiles + ) + totalHistoryInserted += insertedHistoryCount + + // Determine organization ID + def orgId = record.organization_id + + if (orgId == null) { + log.error("No organization_id found for compliance_report_id: ${record.compliance_report_id}. Skipping transaction creation.") + } else { + if (record.credit_trade_id) { + // **Accepted Report:** Create only an Adjustment transaction + def ctCreateUser = userProfiles[record.ct_create_user_id] ?: "imported_user" + def quantity = record.number_of_credits + if (record.credit_trade_type == "Credit Reduction") { quantity = -quantity } def associatedTransactionId = insertTransactionForReport( statements.insertTransactionStmt, - ct.respondent_id ?: ct.initiator_id, + record.organization_id, quantity, "Adjustment", ctCreateUser, - ct.create_date, - ct.transaction_effective_date + record.ct_create_timestamp, + record.trade_effective_date ? Timestamp.valueOf(record.trade_effective_date.toLocalDate().atStartOfDay()) : null ) totalTransactionsInserted++ - createdTransactionId = associatedTransactionId - } - - if (createdTransactionId != null) { - updateComplianceReportWithTransaction(destinationConn, lcfsReportId, createdTransactionId) + if (associatedTransactionId != null) { + updateComplianceReportWithTransaction(destinationConn, lcfsReportId, associatedTransactionId) + } + } else { + // **Non-Accepted Report:** Determine if an In Reserve transaction is needed + def currentStatusName = referenceData.statusIdToName[currentStatusId]?.toLowerCase() + + if (["submitted", "recommended by analyst", "recommended by manager", "not recommended"].contains(currentStatusName)) { + // Create an In Reserve transaction + if (inReserveQuantity != null) { + def inReserveTransactionId = insertTransactionForReport( + statements.insertTransactionStmt, + orgId, + inReserveQuantity, + "Reserved", + reportCreateUser, + record.create_timestamp, + null + ) + totalTransactionsInserted++ + + if (inReserveTransactionId != null) { + updateComplianceReportWithTransaction(destinationConn, lcfsReportId, inReserveTransactionId) + } + } + } else if (currentStatusName == "rejected") { + // **Rejected Report:** Do nothing + log.debug("Compliance report_id: ${record.compliance_report_id} is rejected. No transaction created.") + } else { + // **Unhandled Status:** Log or handle as needed + log.warn("Compliance report_id: ${record.compliance_report_id} has an unhandled status: ${currentStatusName}. No transaction created.") + } } - - version++ - totalInserted++ } } + rs.close() + consolidatedStmt.close() + + // Commit all changes destinationConn.commit() - // Re-enable and refresh the materialized view - destinationConn.createStatement().execute(""" + // Re-enable and refresh the materialized views + stmt = destinationConn.createStatement() + stmt.execute(""" CREATE OR REPLACE FUNCTION refresh_transaction_aggregate() RETURNS void AS \$\$ BEGIN @@ -268,10 +360,9 @@ try { END; \$\$ LANGUAGE plpgsql; """) - destinationConn.createStatement().execute('REFRESH MATERIALIZED VIEW CONCURRENTLY mv_transaction_aggregate') + stmt.execute('REFRESH MATERIALIZED VIEW CONCURRENTLY mv_transaction_aggregate') - // Re-enable and refresh the materialized view - destinationConn.createStatement().execute(""" + stmt.execute(""" CREATE OR REPLACE FUNCTION refresh_mv_director_review_transaction_count() RETURNS void AS \$\$ BEGIN @@ -279,9 +370,10 @@ try { END; \$\$ LANGUAGE plpgsql; """) - destinationConn.createStatement().execute('REFRESH MATERIALIZED VIEW CONCURRENTLY mv_director_review_transaction_count') + stmt.execute('REFRESH MATERIALIZED VIEW CONCURRENTLY mv_director_review_transaction_count') + stmt.close() - log.info("Inserted ${totalInserted} compliance reports, ${totalHistoryInserted} history records, and ${totalTransactionsInserted} transactions into LCFS.") + log.warn("Inserted ${totalInserted} compliance reports, ${totalHistoryInserted} history records, and ${totalTransactionsInserted} transactions into LCFS.") } catch (Exception e) { log.error('Error occurred while processing compliance reports', e) destinationConn?.rollback() @@ -295,161 +387,207 @@ try { // Helper Functions // ========================================= -def loadReferenceData(Connection conn) { - def statusMap = [:] - def idToName = [:] - def stmt = conn.createStatement() - def rs = stmt.executeQuery("SELECT compliance_report_status_id, status FROM compliance_report_status") - while (rs.next()) { - statusMap[rs.getString('status')] = rs.getInt('compliance_report_status_id') - idToName[rs.getInt('compliance_report_status_id')] = rs.getString('status') +/** + * Safely converts a value to Integer. + * @param value The value to convert. + * @return The Integer value or null if conversion fails. + */ +def safeConvertToInt(def value) { + if (value instanceof Number) { + return value.intValue() + } else if (value instanceof String) { + try { + return Integer.parseInt(value) + } catch (NumberFormatException e) { + log.error("Failed to convert value to Integer: ${value}", e) + return null + } } - rs.close() - stmt.close() - - return [ - statusMap: statusMap, - statusIdToName: idToName - ] + return null } -def fetchComplianceReports(Connection conn) { - PreparedStatement stmt = conn.prepareStatement(SOURCE_REPORTS_QUERY) - ResultSet rs = stmt.executeQuery() - - def chains = [:].withDefault { [] } - +/** + * Fetches user profiles and maps id to display_name. + * @param conn Source database connection. + * @return A map of id to display_name. + */ +def fetchUserProfiles(Connection conn) { + def stmt = conn.createStatement() + def rs = stmt.executeQuery("SELECT id, display_name FROM public.user") + def userMap = [:] while (rs.next()) { - def rootId = rs.getObject('root_report_id') ?: rs.getInt('compliance_report_id') - def record = [ - compliance_report_id : rs.getInt('compliance_report_id'), - type_id : rs.getInt('type_id'), - organization_id : rs.getInt('organization_id'), - compliance_period_id : rs.getInt('compliance_period_id'), - supplements_id : rs.getObject('supplements_id'), - root_report_id : rs.getObject('root_report_id'), - latest_report_id : rs.getObject('latest_report_id'), - traversal : rs.getInt('traversal'), - nickname : rs.getString('nickname'), - supplemental_note : rs.getString('supplemental_note'), - fuel_supplier_status_id: rs.getObject('fuel_supplier_status_id'), - analyst_status_id : rs.getObject('analyst_status_id'), - manager_status_id : rs.getObject('manager_status_id'), - director_status_id : rs.getObject('director_status_id'), - create_user_id : rs.getInt('create_user_id'), - create_timestamp : rs.getTimestamp('create_timestamp'), - update_user_id : rs.getInt('update_user_id'), - update_timestamp : rs.getTimestamp('update_timestamp'), - report_type : rs.getString('report_type'), - compliance_period_desc : rs.getString('compliance_period_desc'), - is_supplemental : rs.getBoolean('is_supplemental'), - credit_transaction_id : rs.getObject('credit_transaction_id') - ] - chains[rootId] << record + userMap[rs.getInt("id")] = rs.getString("display_name") } rs.close() stmt.close() - - return chains + return userMap } -def fetchComplianceReportHistory(Connection conn) { - PreparedStatement stmt = conn.prepareStatement(SOURCE_HISTORY_QUERY) - ResultSet rs = stmt.executeQuery() - - def historyMap = [:].withDefault { [] } - +/** + * Loads reference data for status mapping. + * @param conn Destination database connection. + * @return A map containing status mappings. + */ +def loadReferenceData(Connection conn) { + def statusMap = [:] + def idToName = [:] + def stmt = conn.createStatement() + def rs = stmt.executeQuery("SELECT compliance_report_status_id, status FROM compliance_report_status") while (rs.next()) { - def reportId = rs.getInt('compliance_report_id') - def histRecord = [ - history_id : rs.getInt('history_id'), - compliance_report_id : reportId, - create_user_id : rs.getInt('create_user_id'), - create_timestamp : rs.getTimestamp('create_timestamp'), - fuel_supplier_status_id : rs.getObject('fuel_supplier_status_id'), - analyst_status_id : rs.getObject('analyst_status_id'), - manager_status_id : rs.getObject('manager_status_id'), - director_status_id : rs.getObject('director_status_id') - ] - historyMap[reportId] << histRecord + statusMap[rs.getString('status').toLowerCase()] = rs.getInt('compliance_report_status_id') + idToName[rs.getInt('compliance_report_status_id')] = rs.getString('status').toLowerCase() } rs.close() stmt.close() - return historyMap + return [ + statusMap : statusMap, + statusIdToName : idToName + ] } -def fetchApprovedCreditTrades(Connection conn) { - PreparedStatement stmt = conn.prepareStatement(SOURCE_CREDIT_TRADE_QUERY) - ResultSet rs = stmt.executeQuery() - - def trades = [:] - - while (rs.next()) { - def t = [ - transaction_id : rs.getInt('transaction_id'), - initiator_id : rs.getInt('initiator_id'), - respondent_id : rs.getInt('respondent_id'), - agreement_date : rs.getTimestamp('agreement_date'), - transaction_effective_date: rs.getTimestamp('transaction_effective_date'), - quantity : rs.getInt('quantity'), - create_user : rs.getInt('create_user'), - create_date : rs.getTimestamp('create_date'), - update_user : rs.getInt('update_user'), - update_date : rs.getTimestamp('update_date'), - transaction_type : rs.getString('transaction_type'), - price_per_unit : rs.getBigDecimal('price_per_unit') - ] - trades[rs.getInt('credit_trade_id')] = t +/** + * Maps workflow statuses to LCFS compliance_report_status_id. + * Handles 'requested supplemental' and 'not recommended' by referencing the history. + * @param fuelStatusName Fuel supplier status name. + * @param analystStatusName Analyst status name. + * @param managerStatusName Manager status name. + * @param directorStatusName Director status name. + * @param referenceData Reference data for status mapping. + * @param historyRecords List of history records for the compliance report. + * @return Mapped compliance_report_status_id or null if excluded. + */ +def mapCurrentStatus(fuelStatusName, analystStatusName, managerStatusName, directorStatusName, referenceData, List historyRecords) { + // Normalize to lowercase for consistent comparison + fuelStatusName = fuelStatusName?.toLowerCase() + analystStatusName = analystStatusName?.toLowerCase() + managerStatusName = managerStatusName?.toLowerCase() + directorStatusName = directorStatusName?.toLowerCase() + + // Flags to determine special conditions + def isRequestedSupplemental = [ + fuelStatusName, + analystStatusName, + managerStatusName, + directorStatusName + ].any { it?.contains("requested supplemental") } + + def isNotRecommended = [ + fuelStatusName, + analystStatusName, + managerStatusName, + directorStatusName + ].any { it == "not recommended" } + + // Handle 'requested supplemental' + if (isRequestedSupplemental) { + log.debug("Status contains 'requested supplemental'. Processing history to determine previous status.") + if (historyRecords) { + // Iterate through history records in reverse (most recent first) + for (int i = historyRecords.size() - 1; i >= 0; i--) { + def previousRecord = historyRecords[i] + def prevFuelStatus = previousRecord.fuel_supplier_status_id ? previousRecord.fuel_supplier_status_id.toLowerCase() : null + def prevAnalystStatus = previousRecord.analyst_status_id ? previousRecord.analyst_status_id.toLowerCase() : null + def prevManagerStatus = previousRecord.manager_status_id ? previousRecord.manager_status_id.toLowerCase() : null + def prevDirectorStatus = previousRecord.director_status_id ? previousRecord.director_status_id.toLowerCase() : null + + // Check if the previous record does not have 'requested supplemental' + def wasRequestedSupplemental = [ + prevFuelStatus, + prevAnalystStatus, + prevManagerStatus, + prevDirectorStatus + ].any { it?.contains("requested supplemental") } + + if (!wasRequestedSupplemental) { + // Determine the previous status based on priority: Director > Manager > Analyst > Supplier + def previousStatusName = null + if (prevDirectorStatus) { + previousStatusName = prevDirectorStatus + } else if (prevManagerStatus) { + previousStatusName = prevManagerStatus + } else if (prevAnalystStatus) { + previousStatusName = prevAnalystStatus + } else if (prevFuelStatus) { + previousStatusName = prevFuelStatus + } + + if (previousStatusName) { + if (previousStatusName == "accepted") { + log.debug("Previous status was 'accepted'. Mapping to 'assessed'.") + return referenceData.statusMap["assessed"] + } else if (previousStatusName == "recommended" || previousStatusName == "submitted") { + log.debug("Previous status was '${previousStatusName}'. Mapping back to 'submitted'.") + return referenceData.statusMap["submitted"] + } + // Add additional mappings if necessary + } + } + } + } + // If no valid previous status is found, exclude the report + log.debug("No valid previous status found for 'requested supplemental'. Excluding report.") + return null } - rs.close() - stmt.close() - return trades -} - -def mapCurrentStatus(fuelSupplierStatus, analystStatus, managerStatus, directorStatus, referenceData) { - def fuel = fuelSupplierStatus?.toString()?.toLowerCase() ?: "" - def analyst = analystStatus?.toString()?.toLowerCase() ?: "" - def manager = managerStatus?.toString()?.toLowerCase() ?: "" - def director = directorStatus?.toString()?.toLowerCase() ?: "" + // Handle 'not recommended' + if (isNotRecommended) { + log.debug("Status contains 'not recommended'. Mapping back to 'submitted'.") + return referenceData.statusMap["submitted"] + } + // Determine the current status based on all status fields // Priority: Director > Manager > Analyst > Supplier - - // Director logic - if (director == "accepted") { - return referenceData.statusMap["Assessed"] - } else if (director == "rejected" || director.contains("requested supplemental")) { - return null // exclude + // Adjust mappings based on your specific business rules + + // Check Director Status + if (directorStatusName) { + if (directorStatusName == "accepted") { + return referenceData.statusMap["assessed"] + } else if (directorStatusName == "rejected") { + return referenceData.statusMap["rejected"] + } + // Handle other director statuses if necessary } - // Manager logic - if (manager == "recommended") { - return referenceData.statusMap["Recommended by manager"] - } else if (manager == "not recommended" || manager.contains("requested supplemental")) { - return null // exclude + // Check Manager Status + if (managerStatusName) { + if (managerStatusName == "recommended") { + return referenceData.statusMap["recommended by manager"] + } + // Handle other manager statuses if necessary } - // Analyst logic - if (analyst == "recommended") { - return referenceData.statusMap["Recommended by analyst"] - } else if (analyst == "not recommended" || analyst.contains("requested supplemental")) { - return null // exclude + // Check Analyst Status + if (analystStatusName) { + if (analystStatusName == "recommended") { + return referenceData.statusMap["recommended by analyst"] + } + // Handle other analyst statuses if necessary } - // Supplier logic - if (fuel == "deleted") { - return null // exclude - } else if (fuel == "submitted") { - return referenceData.statusMap["Submitted"] - } else if (fuel == "draft") { - return referenceData.statusMap["Draft"] + // Check Fuel Supplier Status + if (fuelStatusName) { + if (fuelStatusName == "submitted") { + return referenceData.statusMap["submitted"] + } else if (fuelStatusName == "draft") { + return referenceData.statusMap["draft"] + } else if (fuelStatusName == "deleted") { + // Exclude deleted reports + return null + } + // Handle other fuel supplier statuses if necessary } - // If nothing matches, exclude + // Default case return null } +/** + * Prepares SQL statements for insertion and updates. + * @param conn Destination database connection. + * @return A map of prepared statements. + */ def prepareStatements(Connection conn) { def INSERT_COMPLIANCE_REPORT_SQL = """ INSERT INTO compliance_report ( @@ -497,13 +635,26 @@ def prepareStatements(Connection conn) { ''' return [ - insertComplianceReportStmt: conn.prepareStatement(INSERT_COMPLIANCE_REPORT_SQL), - insertComplianceReportHistoryStmt: conn.prepareStatement(INSERT_COMPLIANCE_REPORT_HISTORY_SQL), - insertTransactionStmt: conn.prepareStatement(INSERT_TRANSACTION_SQL), + insertComplianceReportStmt : conn.prepareStatement(INSERT_COMPLIANCE_REPORT_SQL), + insertComplianceReportHistoryStmt : conn.prepareStatement(INSERT_COMPLIANCE_REPORT_HISTORY_SQL), + insertTransactionStmt : conn.prepareStatement(INSERT_TRANSACTION_SQL), updateComplianceReportTransactionStmt: conn.prepareStatement(UPDATE_COMPLIANCE_REPORT_TRANSACTION_SQL) ] } +/** + * Inserts a compliance report into LCFS. + * @param stmt PreparedStatement for inserting compliance_report. + * @param report Compliance report data. + * @param groupUuid Group UUID as string. + * @param version Version number. + * @param supplementalInitiator Supplemental initiator type. + * @param reportingFrequency Reporting frequency. + * @param currentStatusId Mapped status ID. + * @param createUser Display name of the creator. + * @param updateUser Display name of the updater. + * @return Inserted compliance_report_id. + */ def insertComplianceReport(PreparedStatement stmt, Map report, String groupUuid, int version, String supplementalInitiator, String reportingFrequency, Integer currentStatusId, String createUser, String updateUser) { stmt.setInt(1, report.compliance_period_id) stmt.setInt(2, report.organization_id) @@ -525,18 +676,34 @@ def insertComplianceReport(PreparedStatement stmt, Map report, String groupUuid, stmt.setString(12, updateUser) stmt.setTimestamp(13, report.update_timestamp ?: report.create_timestamp ?: Timestamp.valueOf("1970-01-01 00:00:00")) - def rs = stmt.executeQuery() - def insertedId = null - if (rs.next()) { - insertedId = rs.getInt('compliance_report_id') + def rs = null + try { + rs = stmt.executeQuery() + if (rs.next()) { + def insertedId = rs.getInt('compliance_report_id') + return insertedId + } + } catch (Exception e) { + log.error("Failed to insert compliance report for compliance_report_id: ${report.compliance_report_id}", e) + } finally { + if (rs != null) rs.close() } - rs.close() - return insertedId + return null } +/** + * Inserts compliance report history records into LCFS. + * @param lcfsReportId Inserted compliance_report_id in LCFS. + * @param historyRecords List of history records. + * @param historyStmt PreparedStatement for inserting compliance_report_history. + * @param referenceData Reference data for status mapping. + * @param userProfiles Map of user_id to display_name. + * @return Number of history records inserted. + */ def insertComplianceReportHistory(Integer lcfsReportId, List historyRecords, PreparedStatement historyStmt, - Map referenceData) { + Map referenceData, + Map userProfiles) { int count = 0 if (!historyRecords) return count @@ -546,54 +713,32 @@ def insertComplianceReportHistory(Integer lcfsReportId, List historyRecords, h.analyst_status_id, h.manager_status_id, h.director_status_id, - referenceData + referenceData, + [] // Pass an empty list to prevent recursive history processing ) - // We'll just use a placeholder "imported_user" string for create_user and update_user - def createUser = "imported_user" - def timestamp = h.create_timestamp ?: Timestamp.valueOf("1970-01-01 00:00:00") - - // For user_profile_id, we can still set it if we have it, or null otherwise - def userProfileId = h.create_user_id - - log.warn("Processing history record:") - log.warn("lcfsReportId: ${lcfsReportId}, statusId: ${statusId}, userProfileId: ${userProfileId}, create_user: ${createUser}, timestamp: ${timestamp}") - log.warn("fuel_supplier_status_id: ${h.fuel_supplier_status_id}, analyst_status_id: ${h.analyst_status_id}, manager_status_id: ${h.manager_status_id}, director_status_id: ${h.director_status_id}") - if (statusId == null) { - // Skip this history record because we didn't match a status - log.warn("Skipping history record for lcfsReportId: ${lcfsReportId} due to null statusId.") + // Skip history with no mapped status return } + // Convert create_timestamp from String to Timestamp + def timestamp = h.create_timestamp ? Timestamp.from(OffsetDateTime.parse(h.create_timestamp).toInstant()) : Timestamp.valueOf("1970-01-01 00:00:00") + + def userProfileId = h.create_user_id + + def reportCreateUser = userProfiles[h.create_user_id] ?: "imported_user" + def reportUpdateUser = userProfiles[h.update_user_id] ?: reportCreateUser + try { historyStmt.setInt(1, lcfsReportId) historyStmt.setInt(2, statusId) - if (userProfileId == null) { - historyStmt.setNull(3, java.sql.Types.INTEGER) - } else { - historyStmt.setInt(3, userProfileId) - } - - // create_user (String) - if (createUser == null) { - historyStmt.setNull(4, java.sql.Types.VARCHAR) - } else { - historyStmt.setString(4, createUser) - } + historyStmt.setInt(3, userProfileId) - // create_date (timestamp) + historyStmt.setString(4, reportCreateUser) historyStmt.setTimestamp(5, timestamp) - - // update_user (String) - same as create_user for simplicity - if (createUser == null) { - historyStmt.setNull(6, java.sql.Types.VARCHAR) - } else { - historyStmt.setString(6, createUser) - } - - // update_date (timestamp) + historyStmt.setString(6, reportUpdateUser) historyStmt.setTimestamp(7, timestamp) historyStmt.addBatch() @@ -613,35 +758,65 @@ def insertComplianceReportHistory(Integer lcfsReportId, List historyRecords, return count } -def insertTransactionForReport(PreparedStatement stmt, int orgId, int quantity, String action, String createUser, Timestamp createDate, Timestamp effectiveDate = null) { +/** + * Inserts a transaction into LCFS. + * @param stmt PreparedStatement for inserting transaction. + * @param orgId Organization ID. + * @param quantity Number of compliance units. + * @param action Transaction action type. + * @param createUser Display name of the creator. + * @param createDate Creation timestamp. + * @param effectiveDate Effective date of the transaction. + * @return Inserted transaction_id or null. + */ +def insertTransactionForReport(PreparedStatement stmt, Integer orgId, int quantity, String action, String createUser, Timestamp createDate, Timestamp effectiveDate = null) { + if (orgId == null) { + log.error("Organization ID is null. Cannot insert transaction.") + return null + } + def effDate = effectiveDate ?: createDate ?: Timestamp.valueOf("1970-01-01 00:00:00") stmt.setInt(1, quantity) stmt.setInt(2, orgId) stmt.setString(3, action) stmt.setTimestamp(4, effDate) - - if (createUser == null) { - stmt.setNull(5, java.sql.Types.VARCHAR) - } else { - stmt.setString(5, createUser) - } - + stmt.setString(5, createUser) stmt.setTimestamp(6, createDate ?: Timestamp.valueOf("1970-01-01 00:00:00")) - def rs = stmt.executeQuery() - def transactionId = null - if (rs.next()) { - transactionId = rs.getInt('transaction_id') + def rs = null + try { + rs = stmt.executeQuery() + if (rs.next()) { + return rs.getInt('transaction_id') + } + } catch (Exception e) { + log.error("Failed to insert transaction for compliance_report_id", e) + } finally { + if (rs != null) rs.close() } - rs.close() - return transactionId + return null } +/** + * Updates a compliance report with the associated transaction ID. + * @param conn Destination database connection. + * @param lcfsReportId Compliance report ID in LCFS. + * @param transactionId Transaction ID to associate. + */ def updateComplianceReportWithTransaction(Connection conn, int lcfsReportId, int transactionId) { def updateStmt = conn.prepareStatement("UPDATE compliance_report SET transaction_id = ? WHERE compliance_report_id = ?") - updateStmt.setInt(1, transactionId) - updateStmt.setInt(2, lcfsReportId) - updateStmt.executeUpdate() - updateStmt.close() + try { + updateStmt.setInt(1, transactionId) + updateStmt.setInt(2, lcfsReportId) + updateStmt.executeUpdate() + } catch (Exception e) { + log.error("Failed to update compliance_report with transaction_id: ${transactionId} for lcfsReportId: ${lcfsReportId}", e) + } finally { + updateStmt.close() + } } + +// ========================================= +// Script Termination +// ========================================= diff --git a/etl/nifi_scripts/organization.groovy b/etl/nifi_scripts/organization.groovy index dfbd435f0..e89e0a21d 100644 --- a/etl/nifi_scripts/organization.groovy +++ b/etl/nifi_scripts/organization.groovy @@ -38,8 +38,10 @@ def sourceQuery = """ INNER JOIN organization_actions_type oat ON oat.id = o.actions_type_id INNER JOIN organization_address oa ON oa.organization_id = o.id and oa.expiration_date is null; """ + // SQL query to check if the record with the same organization_id already exists def checkDuplicateQuery = "SELECT COUNT(*) FROM organization WHERE organization_id = ?" + // SQL queries to fetch the status and type IDs from the destination database def getStatusIdQuery = "SELECT organization_status_id FROM organization_status WHERE status = ?::org_status_enum" def getTypeIdQuery = "SELECT organization_type_id FROM organization_type WHERE org_type = ?::org_type_enum" @@ -122,25 +124,25 @@ try { } // If no duplicate exists, proceed with the insert logic - def name = resultSet.getString("name") + def name = resultSet.getString("name") ?: "" def operatingName = resultSet.getString("operating_name") ?: "" // not nullable string field - def email = resultSet.getString("email") - def phone = resultSet.getString("phone") - def edrmsRecord = resultSet.getString("edrms_record") - def orgStatusChar = resultSet.getString("org_status") - def orgTypeChar = resultSet.getString("organization_type") - def serviceStreetAddress = resultSet.getString("service_street_address") - def serviceAddressOther = resultSet.getString("service_address_other") - def serviceCity = resultSet.getString("service_city") - def serviceProvinceState = resultSet.getString("service_province_state") - def servicePostalCodeZipCode = resultSet.getString("service_postalCode_zipCode") - def serviceCountry = resultSet.getString("service_country") - def attorneyStreetAddress = resultSet.getString("attorney_street_address") - def attorneyAddressOther = resultSet.getString("attorney_address_other") - def attorneyCity = resultSet.getString("attorney_city") - def attorneyProvinceState = resultSet.getString("attorney_province_state") - def attorneyPostalCodeZipCode = resultSet.getString("attorney_postalCode_zipCode") - def attorneyCountry = resultSet.getString("attorney_country") + def email = resultSet.getString("email") ?: "" + def phone = resultSet.getString("phone") ?: "" + def edrmsRecord = resultSet.getString("edrms_record") ?: "" + def orgStatusChar = resultSet.getString("org_status") ?: "" + def orgTypeChar = resultSet.getString("organization_type") ?: "" + def serviceStreetAddress = resultSet.getString("service_street_address") ?: "" + def serviceAddressOther = resultSet.getString("service_address_other") ?: "" + def serviceCity = resultSet.getString("service_city") ?: "" + def serviceProvinceState = resultSet.getString("service_province_state") ?: "" + def servicePostalCodeZipCode = resultSet.getString("service_postalCode_zipCode") ?: "" + def serviceCountry = resultSet.getString("service_country") ?: "" + def attorneyStreetAddress = resultSet.getString("attorney_street_address") ?: "" + def attorneyAddressOther = resultSet.getString("attorney_address_other") ?: "" + def attorneyCity = resultSet.getString("attorney_city") ?: "" + def attorneyProvinceState = resultSet.getString("attorney_province_state") ?: "" + def attorneyPostalCodeZipCode = resultSet.getString("attorney_postalCode_zipCode") ?: "" + def attorneyCountry = resultSet.getString("attorney_country") ?: "" // Fetch organization_status_id and organization_type_id statusStmt.setString(1, orgStatusChar) @@ -176,12 +178,12 @@ try { PreparedStatement insertAttorneyAddressStmt = destinationConn.prepareStatement(insertAttorneyAddressSQL) def organizationAttorneyAddressId = null insertAttorneyAddressStmt.setString(1, name) - insertAttorneyAddressStmt.setString(2, attorneyStreetAddress) - insertAttorneyAddressStmt.setString(3, attorneyAddressOther) - insertAttorneyAddressStmt.setString(4, attorneyCity) - insertAttorneyAddressStmt.setString(5, attorneyProvinceState) - insertAttorneyAddressStmt.setString(6, attorneyPostalCodeZipCode) - insertAttorneyAddressStmt.setString(7, attorneyCountry) + insertAttorneyAddressStmt.setString(2, attorneyStreetAddress) + insertAttorneyAddressStmt.setString(3, attorneyAddressOther) + insertAttorneyAddressStmt.setString(4, attorneyCity) + insertAttorneyAddressStmt.setString(5, attorneyProvinceState) + insertAttorneyAddressStmt.setString(6, attorneyPostalCodeZipCode) + insertAttorneyAddressStmt.setString(7, attorneyCountry) ResultSet attorneyAddressResultSet = insertAttorneyAddressStmt.executeQuery() if (attorneyAddressResultSet.next()) { organizationAttorneyAddressId = attorneyAddressResultSet.getInt("organization_attorney_address_id") @@ -214,8 +216,8 @@ try { insertOrgStmt.setString(7, edrmsRecord) insertOrgStmt.setInt(8, orgStatusId) insertOrgStmt.setInt(9, orgTypeId) - insertOrgStmt.setInt(10, organizationAddressId) // Service address ID - insertOrgStmt.setInt(11, organizationAttorneyAddressId) // Attorney address ID + insertOrgStmt.setInt(10, organizationAddressId) // Service address ID + insertOrgStmt.setInt(11, organizationAttorneyAddressId) // Attorney address ID insertOrgStmt.executeUpdate() } From 6e2f6b4b9dcaf6e56e2c8d3e4a7eb89f7780e21d Mon Sep 17 00:00:00 2001 From: Alex Zorkin Date: Tue, 17 Dec 2024 09:57:21 -0800 Subject: [PATCH 4/7] feat: etl logic updates for compliance period and transfer --- etl/nifi_scripts/compliance_report.groovy | 165 ++++++++++++++++++---- etl/nifi_scripts/transfer.groovy | 38 ++++- 2 files changed, 171 insertions(+), 32 deletions(-) diff --git a/etl/nifi_scripts/compliance_report.groovy b/etl/nifi_scripts/compliance_report.groovy index 166ad9bcc..f4572ac18 100644 --- a/etl/nifi_scripts/compliance_report.groovy +++ b/etl/nifi_scripts/compliance_report.groovy @@ -199,7 +199,10 @@ try { credit_trade_type : rs.getString('credit_trade_type'), fair_market_value_per_credit : rs.getBigDecimal('fair_market_value_per_credit') ] - + + // Map TFRS compliance period ID to LCFS compliance period ID + record.compliance_period_id = mapCompliancePeriodId(record.compliance_period_id) + // Insert Compliance Report History from JSON def historyJson = record.history_json def historyRecords = historyJson ? new JsonSlurper().parseText(historyJson).sort { a, b -> @@ -312,33 +315,101 @@ try { updateComplianceReportWithTransaction(destinationConn, lcfsReportId, associatedTransactionId) } } else { - // **Non-Accepted Report:** Determine if an In Reserve transaction is needed + // After determining currentStatusName and having processed "In Reserve" transactions: def currentStatusName = referenceData.statusIdToName[currentStatusId]?.toLowerCase() - if (["submitted", "recommended by analyst", "recommended by manager", "not recommended"].contains(currentStatusName)) { - // Create an In Reserve transaction - if (inReserveQuantity != null) { - def inReserveTransactionId = insertTransactionForReport( - statements.insertTransactionStmt, - orgId, - inReserveQuantity, - "Reserved", - reportCreateUser, - record.create_timestamp, - null - ) - totalTransactionsInserted++ - - if (inReserveTransactionId != null) { - updateComplianceReportWithTransaction(destinationConn, lcfsReportId, inReserveTransactionId) + // Check if not the latest report in the chain or status is rejected + boolean isLatestReport = (record.compliance_report_id == record.latest_report_id) + + // Determine if we should consider a "Reserved" transaction + def shouldReserve = ["submitted", "recommended by analyst", "recommended by manager", "not recommended"].contains(currentStatusName) + + if (shouldReserve && inReserveQuantity != null && inReserveQuantity < 0) { + boolean mustRelease = false + + // If current report is rejected, release immediately + if (currentStatusName == "rejected") { + mustRelease = true + } else { + // If not the latest report, check the source DB for a future report + if (!isLatestReport) { + def futureQuery = """ + SELECT c.id, + s.fuel_supplier_status_id, + s.analyst_status_id, + s.manager_status_id, + s.director_status_id + FROM compliance_report c + JOIN compliance_report_workflow_state s ON c.status_id = s.id + WHERE c.root_report_id = ? + AND c.id > ? + ORDER BY c.id ASC LIMIT 1 + """ + + PreparedStatement futureStmt = sourceConn.prepareStatement(futureQuery) + futureStmt.setObject(1, record.root_report_id) + futureStmt.setInt(2, record.compliance_report_id) + ResultSet futureRs = futureStmt.executeQuery() + + if (futureRs.next()) { + def futureFuelStatus = futureRs.getString("fuel_supplier_status_id") + def futureAnalystStatus = futureRs.getString("analyst_status_id") + def futureManagerStatus = futureRs.getString("manager_status_id") + def futureDirectorStatus = futureRs.getString("director_status_id") + + // Map these future statuses to a final mapped status ID + def futureStatusId = mapCurrentStatus( + futureFuelStatus, + futureAnalystStatus, + futureManagerStatus, + futureDirectorStatus, + referenceData, + [] + ) + + if (futureStatusId != null) { + def futureStatusName = referenceData.statusIdToName[futureStatusId]?.toLowerCase() + log.warn("Future report found with mapped status: ${futureStatusName}") + + // If the future report progresses beyond current (e.g., submitted or recommended), we must release immediately + if (["submitted", "recommended by analyst", "recommended by manager", "assessed"].contains(futureStatusName)) { + mustRelease = true + } + } else { + log.warn("Future report found but no valid mapped status. Not releasing at this time.") + } + } + + futureRs.close() + futureStmt.close() } } + + // Determine final action: if mustRelease is true, directly release; otherwise reserve + def finalAction = mustRelease ? "Released" : "Reserved" + + log.warn("Inserting a single ${finalAction} transaction for compliance_report_id: ${record.compliance_report_id} with quantity: ${inReserveQuantity}") + + def transactionId = insertTransactionForReport( + statements.insertTransactionStmt, + orgId, + inReserveQuantity, + finalAction, + reportCreateUser, + record.create_timestamp, + null + ) + totalTransactionsInserted++ + if (transactionId != null) { + updateComplianceReportWithTransaction(destinationConn, lcfsReportId, transactionId) + } + } else if (currentStatusName == "rejected") { - // **Rejected Report:** Do nothing - log.debug("Compliance report_id: ${record.compliance_report_id} is rejected. No transaction created.") + // Report is rejected but no inReserveQuantity or not in the reservable statuses, no transaction + log.debug("Rejected report_id: ${record.compliance_report_id}. No transaction created.") } else { - // **Unhandled Status:** Log or handle as needed - log.warn("Compliance report_id: ${record.compliance_report_id} has an unhandled status: ${currentStatusName}. No transaction created.") + // Unhandled status or inReserveQuantity is null + log.warn("No action for compliance_report_id: ${record.compliance_report_id}, status: ${currentStatusName}, inReserveQuantity: ${inReserveQuantity}.") } } } @@ -547,7 +618,6 @@ def mapCurrentStatus(fuelStatusName, analystStatusName, managerStatusName, direc } else if (directorStatusName == "rejected") { return referenceData.statusMap["rejected"] } - // Handle other director statuses if necessary } // Check Manager Status @@ -555,7 +625,6 @@ def mapCurrentStatus(fuelStatusName, analystStatusName, managerStatusName, direc if (managerStatusName == "recommended") { return referenceData.statusMap["recommended by manager"] } - // Handle other manager statuses if necessary } // Check Analyst Status @@ -563,7 +632,6 @@ def mapCurrentStatus(fuelStatusName, analystStatusName, managerStatusName, direc if (analystStatusName == "recommended") { return referenceData.statusMap["recommended by analyst"] } - // Handle other analyst statuses if necessary } // Check Fuel Supplier Status @@ -576,13 +644,58 @@ def mapCurrentStatus(fuelStatusName, analystStatusName, managerStatusName, direc // Exclude deleted reports return null } - // Handle other fuel supplier statuses if necessary } // Default case return null } +// ========================================= +// Add a mapping function for compliance_period_id +// ========================================= +def mapCompliancePeriodId(Integer tfrsId) { + // This mapping adjusts for the doubled up years in TFRS (2012-13, 2013-14) + // and ensures correct alignment with LCFS single-year periods. + // + // TFRS IDs vs. Descriptions: + // 3 -> "2012-13" + // 4 -> "2013-14" + // LCFS has each year individually: + // 3 -> "2012" + // 4 -> "2013" + // 5 -> "2014" + // + // Because TFRS combines two-year periods, align them as follows: + // TFRS:3 (2012-13) -> LCFS:3 (2012) + // TFRS:4 (2013-14) -> LCFS:5 (2014), skipping LCFS:4 (2013) + // After handling these two combined periods, subsequent years shift by 1. + + def compliancePeriodMapping = [ + 1: 1, + 2: 2, + 3: 3, // 2012-13 mapped to 2012 + 4: 5, // 2013-14 mapped to 2014, skipping 2013 + 5: 6, + 6: 7, + 7: 8, + 8: 9, + 9: 10, + 10: 11, + 11: 12, + 12: 13, + 13: 14, + 14: 15, + 15: 16, + 16: 17, + 17: 18, + 18: 19, + 19: 20, + 20: 21 + ] + + return compliancePeriodMapping[tfrsId] ?: tfrsId +} + /** * Prepares SQL statements for insertion and updates. * @param conn Destination database connection. diff --git a/etl/nifi_scripts/transfer.groovy b/etl/nifi_scripts/transfer.groovy index aa6c5362d..219a63c78 100644 --- a/etl/nifi_scripts/transfer.groovy +++ b/etl/nifi_scripts/transfer.groovy @@ -193,6 +193,15 @@ try { def internalCommentsJson = internalComments ? jsonSlurper.parseText(internalComments) : [] def creditTradeHistoryJson = creditTradeHistory ? jsonSlurper.parseText(creditTradeHistory) : [] + // First, determine if the transfer already exists + def transferIdFromSource = resultSet.getInt('transfer_id') + if (transferExists(destinationConn, transferIdFromSource)) { + log.warn("Duplicate transfer detected with transfer_id: ${transferIdFromSource}, skipping insertion.") + // Since this transfer already exists, do not insert transactions or history again. + continue + } + + // Only if transfer does not exist, proceed to create transactions and then insert the transfer. def (fromTransactionId, toTransactionId) = processTransactions(resultSet.getString('current_status'), resultSet, statements.transactionStmt) @@ -205,7 +214,7 @@ try { processInternalComments(transferId, internalCommentsJson, statements.internalCommentStmt, statements.transferInternalCommentStmt) } else { - log.warn("Transfer not inserted for record: ${resultSet.getInt('transfer_id')}") + log.warn("Transfer not inserted for record: ${transferIdFromSource}") } } resultSet.close() @@ -344,19 +353,24 @@ def processTransactions(String currentStatus, ResultSet rs, PreparedStatement st case ['Draft', 'Deleted', 'Refused', 'Declined', 'Rescinded']: break case ['Sent', 'Submitted', 'Recommended']: - fromTransactionId = insertTransaction(stmt, rs, 'Reserved', rs.getInt('from_organization_id')) + fromTransactionId = insertTransaction(stmt, rs, 'Reserved', rs.getInt('from_organization_id'), true) break case 'Recorded': - fromTransactionId = insertTransaction(stmt, rs, 'Adjustment', rs.getInt('from_organization_id')) - toTransactionId = insertTransaction(stmt, rs, 'Adjustment', rs.getInt('to_organization_id')) + fromTransactionId = insertTransaction(stmt, rs, 'Adjustment', rs.getInt('from_organization_id'), true) + toTransactionId = insertTransaction(stmt, rs, 'Adjustment', rs.getInt('to_organization_id'), false) break } return [fromTransactionId, toTransactionId] } -def insertTransaction(PreparedStatement stmt, ResultSet rs, String action, int orgId) { - stmt.setInt(1, rs.getInt('quantity')) +def insertTransaction(PreparedStatement stmt, ResultSet rs, String action, int orgId, boolean isDebit) { + def quantity = rs.getInt('quantity') + if (isDebit) { + quantity *= -1 // Make the transaction negative for the sender + } + + stmt.setInt(1, quantity) stmt.setInt(2, orgId) stmt.setString(3, action) stmt.setDate(4, rs.getDate('transaction_effective_date') ?: rs.getDate('agreement_date')) @@ -367,6 +381,18 @@ def insertTransaction(PreparedStatement stmt, ResultSet rs, String action, int o return result.next() ? result.getInt('transaction_id') : null } +def transferExists(Connection conn, int transferId) { + def duplicateCheckStmt = conn.prepareStatement('SELECT COUNT(*) FROM transfer WHERE transfer_id = ?') + duplicateCheckStmt.setInt(1, transferId) + def duplicateResult = duplicateCheckStmt.executeQuery() + duplicateResult.next() + def count = duplicateResult.getInt(1) + duplicateResult.close() + duplicateCheckStmt.close() + + return count > 0 +} + def processHistory(Integer transferId, List creditTradeHistory, PreparedStatement historyStmt, Map preparedData) { if (!creditTradeHistory) return From e5aaec7d13aa1a5fda470a8b92a5ee3f1656fbef Mon Sep 17 00:00:00 2001 From: Alex Zorkin Date: Tue, 17 Dec 2024 15:32:13 -0800 Subject: [PATCH 5/7] feat: unassociated credit trade calculations, credit validation script --- etl/database/nifi-registry-primary.mv.db | Bin 94208 -> 94208 bytes etl/nifi/conf/flow.json.gz | Bin 9990 -> 9966 bytes etl/nifi/conf/flow.xml.gz | Bin 15551 -> 15490 bytes etl/nifi_scripts/compliance_report.groovy | 81 +++++++++++- etl/nifi_scripts/credit_validation.groovy | 143 ++++++++++++++++++++++ 5 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 etl/nifi_scripts/credit_validation.groovy diff --git a/etl/database/nifi-registry-primary.mv.db b/etl/database/nifi-registry-primary.mv.db index c02fd000c0568f6388ea0a127a8a36a40f99f1fe..7d5887b3c10103f8cacc6577f8b4b86872b12612 100644 GIT binary patch delta 7235 zcmeHMYj73i8UA+n>^aLJBoITA69Y*Q5HP^rFUL{?4r%E~!o@n+(aK&0fh1g|)7CwB z`Jsi1tPpRlBUMYOQ%FzoOn9jxEaRz@tXDEI5`*!ziaw3RY z`=@7ydC$AM*?rHq-*b6iN@v*88Fp_M?TU|GH+!lpc6%G$ZT32MOHp$?O%XgZKVm-H z5L|4SdF5+5CcrfTx1xQKWp-~F9Nbovbe)Hf=n8_|y>0y_wsYJvcoK&2u0KLFr&I+cNP9np#jkaq!!8 zaBkki^DUjW4B9dgVm5G-7}SI~d)@Yd4GSD2{^CVn;0`O7K9GN&!WR@BBN!TJ_WACO z>vy;-%)8yK@H4wNnLCT7Dw3niA<-S3hoeJ9(~DL|mBYo6RLKyd)iFHHbTwV--afE> ztLrS@c3ZScj+-o)W8jrCc!lpCwC(Lf=79Z;BJXSA-E{qhxGJyP+Bj%$C~Dms-GQRi zJ%lSWQdP>hNKvz^MGCS?inLVnTH-ee7r{q!`P7?~WZBc~xYla1E?+Iy!^MgcFQV?@{Ss-fBj)QwjH?k&V;1j}&j;GbXb^23c< zbUw#7PIf+EH{4sowCcjAC314m*S;3FCkgFS~jg0q8-w_6&a0x8}qAroLRiRlua zt;m9*MO1**$Krx%I9_|h^K9FZmE=)yCk#`KP%p1*pybQA zJVNwVnvPNX^!TXN#-LUss69(iD-t=E2x@`zLOF+kQEOtkkSFIce_7KP-k z)EJ0VF*bffp>(JGgBNNedCfe*gg;gsKOHFW-2lH9uf(c(d2L)gRvbSX6j9wZ+Nf-v zL+u+ePuB12%+5--dMqq(Ao07ZD9gHLYH6{$0~B?%cM44%_DkWO0E@wacd6Y+9 zrJ2~F5FD!k9B3ITBANlStDQh-(QPb*tICS* zz!%Rl6kQ1Vcl8FpdiCjY?tz!6VDJ4iqks37UwXAfL8a_T=2n_6pkV*5g*nyKp+7K# zqbCmsr(Rnf_`m))Tz_x*t=GGlT=wRCg`o?<`8Qu!#ESxWT8}d%8=MgS;yAYMN}M4p zysKy;XDFsF=#Iu2nkE>!EO3UWTCQbS!OA~eU+&l=u(^;$Ccl!w+P|RbZgyPlhZSS{ zo~UX+3wj#2eJB> zNDOfT`9uH+d_gF8rfI~K>o8Hqw3lM9DpAUcryHgj6D5@&Uc87pmmB(U%2XDy!;dYc zW)EMyiF&pk{eJU{jV&yqXePs=VPP?xkZ+=%$!*ztO>6qn=a?P8Y^prhLoH-;-<&nI ztr<8gZDb;vYFn8jDhmDm!d5(wdx!Ipth43?Jm&g!U~ zy^C5%XIqBl&g#VBkr@VUnzU&Fv%U2c*Ol@tQp_#^e{4791iv`e;zOsEOcz2+va@K3 zso_H8Qmjakrq1bb&^E=I!fLn-lFMSQihE*gs z=E!wTkFUYUo42;&*xJ$tI)>1-R3pS5bnW+E0VRk0McDLg4{E>hcMwmY_Q6{a$|BT$ z5b`V>e+PIZTSC+Q8Nd$>CDcLfhIj&XC`|}W1~k?8LM)-q>mG!70(J5<`~o%Q0zmXDtN5uGxRp@|F)}#RQXTp#>2`3nGRVM1WTPq&obW($GFf z?zv@+0~K9p{65Rp6V=P+xC!}*b(88*SLf_CtIN~=_CCkV+?*|6`k+0>%zryqo__w_ z9HW20l`mY{pJ%kC6Dp78sZ)7o$%pxhHD_bt{cupk~myq4VP?^x^Rp`k5Mq z{%NAnb88iPO03Z9Y8CoetkCXgg)YVlZ4-rt6&*F}S7H06XZ?`VQyvF1j}`g{i9+9x z75Z&9LrV{jRQ?i5Z5#c{qx(BjimJqls)v5v|5+?sO5}_MG9*vHcy($^G@w>(6LAgY zE;ZO+Hfpl3r#U11KM;`H@zwUpxNJQtJ9NcrdaFEy8m64(dN$!a+gYii0NmaW`L`qJOS~0>5^O9#;p-K4yrH&%~k}C7(c) uiDD5=_WuW>oL08pPbJFW0^oC<4O0@HOi5y0N~ud?{w3A%6x+0_=KnwV{>&Kw delta 8943 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 A9RL6T diff --git a/etl/nifi/conf/flow.json.gz b/etl/nifi/conf/flow.json.gz index 8f11783001e360b6dd75d691cf63bee303add2ff..f27e12b1ddacb020920b9b401ffb838683a475e7 100644 GIT binary patch literal 9966 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 diff --git a/etl/nifi/conf/flow.xml.gz b/etl/nifi/conf/flow.xml.gz index 021f4e38307e5278389b231d868187c718aabba7..03c72fb35d09376c2a4571d0fd6d6d6983fe6c84 100644 GIT binary patch literal 15490 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 delta 15531 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 sI`fza2}{U;QA0tVuZ5L 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 positive credits + // type_id = 4 (Credit Reduction) => negative credits + def adjustedCredits = (credit_trade_type_id == 4) ? -number_of_credits : number_of_credits + + // Create a standalone transaction for this credit trade + def transactionId = insertTransactionForReport( + statements.insertTransactionStmt, + organization_id, + adjustedCredits, + "Adjustment", + createUser, + ct_create_timestamp, + trade_effective_date + ) + totalTransactionsInserted++ + + log.warn("Created standalone transaction ${transactionId} for credit_trade_id ${credit_trade_id} (no associated compliance report)") + } + + unassocRs.close() + unassociatedTradesStmt.close() + + // Commit changes for the newly created transactions destinationConn.commit() + log.warn("Inserted ${totalInserted} compliance reports (from earlier processing), ${totalHistoryInserted} history records, and ${totalTransactionsInserted} transactions into LCFS, including standalone transactions for unassociated credit trades.") + // Re-enable and refresh the materialized views stmt = destinationConn.createStatement() stmt.execute(""" diff --git a/etl/nifi_scripts/credit_validation.groovy b/etl/nifi_scripts/credit_validation.groovy new file mode 100644 index 000000000..31289e22f --- /dev/null +++ b/etl/nifi_scripts/credit_validation.groovy @@ -0,0 +1,143 @@ +import java.sql.Connection +import java.sql.PreparedStatement +import java.sql.ResultSet + +/* +This NiFi Groovy script: +1. Fetches all credit trades from the TFRS database with status_id = 7. +2. Sums the credit balances for each organization based on credit_trade_type logic: + - Sell (type_id = 1): initiator loses credits, respondent gains credits + - Buy (type_id = 2): initiator gains credits, respondent loses credits + - Credit Validation (type_id = 3): respondent_id gains credits + - Credit Reduction (type_id = 4): respondent_id loses credits + - Part 3 Award (type_id = 5): respondent_id gains credits + (Ignore type_id = 6 for now as instructed) + +3. Fetches LCFS transactions and sums them per organization. +4. Prints out organizations where TFRS balance differs from LCFS balance, along with the discrepancy. +5. Logs how many organizations have discrepancies and how many have matching balances. +*/ + +log.warn("******* STARTING CREDIT VALIDATION *******") + +// NiFi DBCP Controller Services +def sourceDbcpService = context.controllerServiceLookup.getControllerService('3245b078-0192-1000-ffff-ffffba20c1eb') +def destinationDbcpService = context.controllerServiceLookup.getControllerService('3244bf63-0192-1000-ffff-ffffc8ec6d93') + +Connection sourceConn = null +Connection destinationConn = null + +try { + + sourceConn = sourceDbcpService.getConnection() + destinationConn = destinationDbcpService.getConnection() + + // Query TFRS for all credit trades with status_id = 7 + def TFRS_QUERY = """ + SELECT ct.id AS credit_trade_id, + ct.type_id, + ct.initiator_id, + ct.respondent_id, + ct.number_of_credits + FROM credit_trade ct + WHERE ct.status_id = 7 + """ + + PreparedStatement tfrsStmt = sourceConn.prepareStatement(TFRS_QUERY) + ResultSet tfrsRs = tfrsStmt.executeQuery() + + // Map for TFRS balances: org_id -> credits + def tfrsBalances = [:].withDefault {0} + + while (tfrsRs.next()) { + def typeId = tfrsRs.getInt("type_id") + def initiator = tfrsRs.getInt("initiator_id") + def respondent = tfrsRs.getInt("respondent_id") + def credits = tfrsRs.getInt("number_of_credits") + + // Apply logic based on type_id + switch(typeId) { + case 1: // Sell + tfrsBalances[initiator] = tfrsBalances[initiator] - credits + tfrsBalances[respondent] = tfrsBalances[respondent] + credits + break + case 2: // Buy + tfrsBalances[initiator] = tfrsBalances[initiator] + credits + tfrsBalances[respondent] = tfrsBalances[respondent] - credits + break + case 3: // Credit Validation + tfrsBalances[respondent] = tfrsBalances[respondent] + credits + break + case 4: // Credit Reduction + tfrsBalances[respondent] = tfrsBalances[respondent] - credits + break + case 5: // Part 3 Award + tfrsBalances[respondent] = tfrsBalances[respondent] + credits + break + // type_id = 6 (Admin Adjustment) not considered per requirement + } + } + + tfrsRs.close() + tfrsStmt.close() + + // Query LCFS for all transactions and sum by org + def LCFS_QUERY = """ + SELECT organization_id, SUM(compliance_units) AS total_units + FROM transaction + WHERE transaction_action = 'Adjustment' + GROUP BY organization_id + """ + + PreparedStatement lcfsStmt = destinationConn.prepareStatement(LCFS_QUERY) + ResultSet lcfsRs = lcfsStmt.executeQuery() + + def lcfsBalances = [:].withDefault {0} + + while (lcfsRs.next()) { + def orgId = lcfsRs.getInt("organization_id") + def totalUnits = lcfsRs.getInt("total_units") + lcfsBalances[orgId] = totalUnits + } + + lcfsRs.close() + lcfsStmt.close() + + // Compare balances + def allOrgs = (tfrsBalances.keySet() + lcfsBalances.keySet()).unique() + def discrepancies = [] + allOrgs.each { orgId -> + def tfrsVal = tfrsBalances[orgId] + def lcfsVal = lcfsBalances[orgId] + if (tfrsVal != lcfsVal) { + def diff = tfrsVal - lcfsVal + discrepancies << [orgId: orgId, tfrs: tfrsVal, lcfs: lcfsVal, difference: diff] + } + } + + // Print out discrepancies + if (discrepancies) { + log.warn("******** Organizations with balance discrepancies between TFRS and LCFS: ********") + discrepancies.each { d -> + log.warn("OrgID: ${d.orgId}, TFRS: ${d.tfrs}, LCFS: ${d.lcfs}, Difference (TFRS-LCFS): ${d.difference}") + } + } else { + log.warn("No discrepancies found. Balances match for all organizations.") + } + + // Log counts + def discrepancyCount = discrepancies.size() + def totalOrgs = allOrgs.size() + def matchingCount = totalOrgs - discrepancyCount + + log.warn("Number of organizations with discrepancies: ${discrepancyCount}") + log.warn("Number of organizations with matching balances: ${matchingCount}") + log.warn("Total organizations considered: ${totalOrgs}") + +} catch (Exception e) { + log.error("Error while validating organization balances", e) +} finally { + log.warn("**** FINISHED CREDIT VALIDATION ****") + if (sourceConn != null) sourceConn.close() + if (destinationConn != null) destinationConn.close() +} From a3becafe8a82a93c71aedd3231f74e2a6d581c94 Mon Sep 17 00:00:00 2001 From: Alex Zorkin Date: Tue, 17 Dec 2024 17:21:59 -0800 Subject: [PATCH 6/7] feat: validation fix --- etl/database/nifi-registry-primary.mv.db | Bin 94208 -> 94208 bytes etl/nifi/conf/flow.json.gz | Bin 9966 -> 9970 bytes etl/nifi/conf/flow.xml.gz | Bin 15490 -> 15493 bytes etl/nifi_scripts/credit_validation.groovy | 4 ++-- 4 files changed, 2 insertions(+), 2 deletions(-) diff --git a/etl/database/nifi-registry-primary.mv.db b/etl/database/nifi-registry-primary.mv.db index 7d5887b3c10103f8cacc6577f8b4b86872b12612..c2cf74344ec06a42cc84bbee5514c547bc900cba 100644 GIT binary patch delta 1315 zcmb8vU2M}<6ae6R?KlYyEb0bqMNqsVU~Apf_O+APTGf`fPD8W^A*zAI_09FDnkH$R zqJltU+K&VRsqK;4r)^B^eMOmsctENUVte3W8d8DsQiQgbJ?wdipItjilT{PbtVqWv zvX67l`Qi&}%7ry$wgX#pd-iX&G(XSF>=d7LspDsC+ovtp>9VqBb4q( z>Y?pneD~x6LJmS`;8>Vn(GDy?6&MkQ{-N4ML9 zkPiZZ%eNgxemn7vRC#eHF2CVFLlvKa8_*KJ{@tdXivj$z@o;oOv$g)JC|=XpIto&sMZFiG>R_rx zFc?HpB+0@^b{V1GqX=b|5nd2RM2^3E7#fL8uC}%9fJdd>(Rt+yEVaSQ(zUq7eBEG{ zQMo#?D8tgzVVJ$Y54u)<*@RH^YxI{S?SMZX{{Tt8cB9X{uQeD5%TkQ8c;>9+=C&a$??Mq41D=>yiRqa(GaJy^+B|<>^_sW z1U3;ih=^jzR~F{z%DfueQ-|8*ZT+oB Q9Glshb=sf5D&m&E0pV4SnE(I) delta 853 zcmaizO=}ZD7{_NeFO4x8Vrwf^+che_bZqw}$!u1z$+6JtO)65_+1XuNv&qILDfMKe z=t;2c16B|$cu=SwEcpOJQP7K^2M>bMgI~b2Xx(g@#+!4QnTMI@dH%ouOnp04-wrJ& zv9faVOpiir$ExyKYhI_OP1CZ?qMK|z4!`54|94_AiWP{6$1wx7m`p5K^OZTbq+1Fp zS&LOY4cEj;p%1~+>E1*gq5LF5nH)-f`HYZOhp(a(e2B3Kch!$w;UX&!8?2GIp9sT@ zo&;xWK(J(Zu$2u3`u0B-8o%F00trJCjI3ZVgaU=gfdJ}((VNHCHm2|r5b_*iqc89? zNIB;Z%cRH_by>|w#F<;LCcKR*!7Ycz^3|*DK4p04h;a+cF;&x~=1>d?a;vXkAm0x^ zhX-J}a2Dw3*btwsl?qK^j#biS@{iIzda9N((ixhD&5_Gc7Q>C5kuFg*P5+8Q%It!b-9!yPpja%SMP7KH8 z1zl~?fIm+E>Pq(=dH11^dDpG#Ds6dSDrSaM=ZcmtXKl(XY8a$es#Pqr8L&AwQ|aA6 z$4sxp9!Bo-vneMo=e05`@!Ps&H*KcPz9flLPGf2Y7IG77-=^_(Okps839oLlPY0pC b-gT<&+FnYt^Gz~JDy30I+eR<#xH$S3a%>4v diff --git a/etl/nifi/conf/flow.json.gz b/etl/nifi/conf/flow.json.gz index f27e12b1ddacb020920b9b401ffb838683a475e7..06afa1e6092f0aa24376ae9ac37d7ec13f34f902 100644 GIT binary patch delta 8749 zcmV+|BGTRNP4Z2U8h-$SAOU~N*v=fk*RlQD&YZgUB;~^Da7Gq2l$2#s`tP^FMWQ59 zrq?)$GBt@!>_GR^-9V%9wQq+yHOcVmH=B)2T8DB{)w$fbWy0^iA!wO z-|f?YVyd2J#0eyabl8TycpC)V`N ziavdT^vRczPfxR}9XXQvgWVZI3p-`6zwEd{`f2AFK2>F2!-wkUy+Hy!f?CvcXa?3# z=T(+YN9z?r9)FoZ-=&6G>H9Cp*Nm!mkj|?@LzzL=kJaq=$xaaO;65zEC`(5cs_^sp zwLi@UkosXV8STD&InZuAuW6TX%t$#kv(vPshS@~T< zt}Go4Y<6z5kBM%gfk=hg86jXDu|(AB0EtjpM7fQ%5maj?+G!vpvE{l(0}lS=e{ro9 zGfviMKu)BRW7~ty53TmY@13UUpqPMbsCQgUM+;3DrAIsazwDhZ%9q9Ovi!a_G%lv4 z^RnlKfPXMYw-gPnux;rLk;|rYxh%Xz;!7dAFkdvPRHh)*Z+OImc zu``Ot7)Me%Dkzgo(ohH*#E~_zinw+z)`|y$xq#^?j)=iha!rI*1}Hpoiot&|QA9gH zHWtN9MVM+E#g+ulXpQ681T3~biZAouk<>ls8kw1j+{Z|cl2)4c zI5URY%yeMQ`9w~9&kl}X{eN6`$tO8jp0*M6=A{z%>x`;6o*#L7&1ko}u@GT}=6xoC#@4ZetN}+ed zs|z{)qttaK_IfmcW*Sr+E2#-JW5`g@S1FAR;?`=!L`byI5mA(rI0PRtxKT_5BG<4; z0dvB{A(Dz&12iOJ5;Qlzreu0jhXG#u)yCpw5Wfs?5cY7i8^pVW^^=GMCx0FXlmW5> zr!YhvQ8kDz|MYX^73TZb`H%x8K`OHV#Zpm_3QjZ^OUKEiw4pwOr4z(t2aKUF{oR-Y z1QRrT*(hm6Jk|oaG9v>eb|LSLc@+EI)=BF75$HVJ~ zWK>LOwZJkNOf%mtIh~{zHh%+1%j=nBQ|rrlqiWa5!Pt6fTb3G6wI5)oQm~fxv(;1I z{4r3{nf1n_2CJ>ikpqxRu1X52Nv%CFJSVpICK-=`HGBI>23VgKmS^8L z=5v)!C!?v?{y;dm0kj0x;W!=S9q64;GN|&(=LVi1NC2;z{Zv3=|9{tFUFO3reusP~ zpN5`?=LyB{kg%zz^V}OUK^3Qd7RTd42F}aWzs_=znOsk=d!CefrAa=|+mHxFj^udK zdy^WEKc!`}`(xG&KaB!9m;^5S`9!H@tkH_c>;Rm^J~kn$z515^86wbdd!KpUtszbQ1L zU7QoEo@O3l24BvLPI~F@1;=8KWLYm9q~q~L3i4I@T2RTf} z*M9Lqyd53>zk^F}zP~;^I_T$nGrtr$7gJVhRI@@K&P{L{DSxOCDMdM=k)e+Wl(97cUBAD2`>vtHN1K{z^pIpEfBM**gYV#n6(@N=73mC#`vWd z=9gZW@4!T+AN^)onf+jRl?+Ry^9k#axK!_aRQ0d3wc1v*mERW`e@V@k^=$c9k9Db6 z^?FTUw3gHYCVzMfCie#uj$J4)w}2zk0V+iL3BUw5K}l|X-9qPDD)%F+T@MT*-g3_E z)-3&0?GqT~kg-lq2NN^`wo9s+Xxc=``7Rxrr9Fg|OAxMT%jA+bw zBemo`^Mgm8DU!)T5A^->COR6VSBaJbG8?&LDgKLPa-8J`U4s#e^mwZL}ypN01#Dk%E8j=n1kGAD@T@3YobniptJpRKK6U8k&lpLJZ$uQ%<`*K@yE*w*8{ z)vbswyMGU!3t=Aot`92be8Doy39}{=TAt2&(yX1XmZr2J!>p0w8W*BSt45L8=0VKA z&zfm=p1UD&)=tXHgI~`uYasOHGPRUh6a6pb`)bl`jRThEXYsv7ywiyFjQld*O89$9 z_yC7A)Dlrl0DJ?EA5p?bRFpEFb6S`3Z$}0e?tj$;q?7`w-AUBTD))G2z4%=u{xq)z z&Fg(|$^TR`+<{JB0Ybj-a0zgqX6DjnS(;t`?dvf8^pAh;769>&zkh*0=*=5s%;mrT zjjpfJc>MQ&&LG=#nwZ(n@8i^S#5o8g+p9KT_tV}mo%F`}$6_|1KXRB3a~%9A2U82E zn1B8;9U<)}y^@mz;|3wO5702Nz4;1`dk3E@Agha%P1&H<#lRQZR`qGNGK2**)xOR; z4PpVeH|EV-g__-T(ECkhiRb@u?{ElQA^jI9wbYdxvOb}$Q$WRoKw-eX4=j|7SShUE zP^St}P9M|SiNeL^NwmenC&0o2ake111%JUU2)2Wu;*vh= zTV*|-<(kW_&)e3De;->wn>Uh+&(HF>&RdQ~p+e6@a^T$|uff1gMgxoTNvdeN+LI~# zCOorV%XM^ou~Oro(-gXaDrbx8Oub&=a)Iga+8eS^MyG@6^$;ohL4Hh9e+>T`rhmWZ z-A1ZBy}dloc&!!ykm1;$yCdfT0QpI+9Y8K;Hd@WSb0AL`TFzKVQDox?Q5i(d9I-M^ zFphIa5-`VCcGLXki05?Vjr)><+J--$a#Zro;qm#w*~MBu1=Iiy&Nsi_Uc5$y4^CC^ zv~Z{?Hc}fGrOR>@3s>_jsJ6k{R)4LS@7kgrUJbp7TCCY?pJRQ={Fd0#ejRC==lMU< zSv*tCwe{4FE>N|1DkmTM?`Q948@PEsq<^*?vo+E! zvaF~u?RnNJC`iY9Hpyh@&jH7O4q9t!%=~PowJQj=46e06rIGo*^t^l2Qrj!KSaYVw znxmU0VFR$Y%tCvKwaPW{_aMF6Ongh3)~aX`87*}+=U%IHS+um&wUp^0cGfqFrdE=x z=bE)ht%XX{kh+ctE5fV|6n~bptyQxk>so4A&NZuv!rBIPOADpd{D=M0zT)*f&j4rO z%W0_@ygl82b#YLfV{O8pTOiL0=CtbsZS1kgvH=KIb%h&oV5PuX)vG#0ONA@h)~Z=i zGFoa`&UIV91%6NbK}l|C>=Y||mRI#!&V9DLHPnLeYe};kw44POY=6kL^~rcd16|9I z5K9pWoIfrnc{rf|} z+_|0nC$FuBbZwCP1b-lV!+g!VvN_)tIUf%>oik1w#Q*{^AZHl6CqT|HT=Hi-9}SYp z{}iVQw?NH$u^#|2(=54ye1qEpt)GQQhgt9U1l;BQl#cB1%BxHL0QcVf0xcf{PZY-x zpfveY7oe1iZ|wDG@})PqV#?Z5we-+-D8ZGSHLVJ)Z4Hg;rQuszSL zrF-+DO{rUGdU$Ah@8t&A1E*De=NsqU={PiI1mki*EKq@;p1oQfHP9cAl zKf=lvo@^F$fPZCFoNI#b3+g!atOMwHqJD?`KA6V%Lpm^?eBjS=9&d=9gBe-N1?2v# z2OX~+ct3mK#bnM3726F`X?~Fdf7h_acl#%o$0rw;M<=iLFMoM;^y+x;U^(xpmk_?H z#pXas`SUD$3unSbje93=PS0R=S6h~ospp+~gJu5X*MEn8Pzz>-c01mOc{JL6O5V8_?N*X}OS_wBmtu;sjKO3Q3do6xW@q&ugLXUm0(^Vg z-QsQ++6@CHV_^d%q_l|AmWvh-L%SV~tw*EXr{tac(QaDSg-e*9$Th#4ByK#uakQo#P9E2!l&D*UHM7!Otb8IcOE$w!p zUFBjGVt^t`U4STQIbnDh+I>oGvx#<}l6SV2+N0C1jd7^B3lYN(gW;`$$E4jSl-W(R z`;^-D7Iz2Nszr8WL)zw;IPJChj?g#izfpAUrrN0TKGTm*f`|2~t2 z5M=>Bv+EEo0!C0t>*YZUU9Jaj91MxWu!@3$d6yAI zE|5wahOCXeG9LvFheAW)nY^r7L$Q(u^7?o+UvF( z2N49ge_9j~M~#Dcf19^#Ydyv)5XuDvSptwkSp&lJBV!@}DvM^%>-F1vJ{+5lCiz>5#e<1u zq>%iV%R4+L+xpUWEtpP25S1U}>C>gSJng>!lYkTs0iKhO6cvBemc_;*qP@fsi~!fl z2x${2XC$S8&}|&waG_a6R3hUd3sQ_z8|!}S6RaEgo>98a_Aw9c!uC)5kn6@5P&aV) zo!&*gBX9p00g^WJ_Rq%190aTr1elQv?l1LH#PdTXE)}Q=%m{(o7`fq|p$b5fQ_PiC zSj5&u{SD6nZ5DswBo5Yj8T31H2WI}3&kB8aPnta=bods})@<>iUdjlmo$!$(0x3YX zX9k3|q6mu+>_*3l;j#@M8t#$`%E3Sg92b_FkYUkp4aIwVK~HJ8j1Ba53jPyC^u2W~ zT)~0>1tb;`=Gujv^gG4El_J215Nt!t1aUauR_+)J_Zfd-z;n|Oep;O$OnN4iaVVr- z$O{!98N^VD!-1fd1TkT)P_P~nK?>!s`I1mECt_l1I)Br=P^=KP6MrM%G*G_fAwCUoxJJQf!p_K=-V6=72Fs6 zXvBI(<_|Od(D?VWs^U&lYT__JI3|EyE_O)DFhU^-G{9aEI~TU$UPBybVMUBWYn+pU zg`j_F-DlUXaR)y9Q8w+K-P_yGNIlx_)_rSSfNT^K=Fi||1d5G~kibeKr6re^8EXRC zh6@dm*LYqwjw3BF#Z=UG>+0{g++H1GjNivHe}YG3zQIc>&xn(souIG1v-7ja{P%Zy zQKjxj#3E}$=a2R{C$7jskv!vvhXdpyile&0JeJ_gh7@Z|U*_uB(w9M%>Q69`zrKqKz`BegRG>ai5W zao)E24RPib!x3_UzgQFmk)+|1G8iO(B-ciV95EJ25TY~&Q7l6w9Iz*K5w>C6w)zdv z^T48Gqztg2lw8C_Z(`t%4tkHkz|D?&clQKr;o**_kq;|=Z#b2_`_bo?kDoCgQxL-> zat1}#fhq+wi)4&LkT;Him|?L~t)~nPPsFL%g~+H7IMX?c_0P@6KbWQPeY}Ey@H29; zztt3z*dT5tn6v?o5f_?*h8xOQ5iUsFdP%C`=^IY4Ko%5o{}`Fof!oBz9ldP7HIa96 z=-rybTPgfa_!qcF0M~$^@RtM`2aQ<6uTexo5oj5iu=UDG!-IkO0}~c=463x@%-K!+ z+i_q00g1bPfBo*8_br_JL#Y^l-^06wKO^_X&ZrnGgADc?$}tq>!U*J~CUI&Lv7qPaq$nMXl(W9=fb~#?{E7Q+xxjk zgf%|Y$6gAgpP$Q@7nX|fKRK;YAXlzqS##H11g&GS;$g0od+#QBWsl0(_)ze}WHKt> z1)0Yw@?je|ep$wtE50E)N?xj|{=@o9&cq7gy!eh7SaKlRkR;9l`^Um)OpT!KrC;#z z%MgRmrw-^e76Qz6ArSz71Vu(#AJZg^nc^?YVUAxGH>!#j-)4i2{p)-82j8E30=jgm zq{2LZmwxqYE-3_f>7&dN?=exjgOtvzuQw5Pndg@deAJM213R^NI$ZvGcJlVL#%_C+ z(}(s<^Y-|clefqFxo1X|<77Hf>2zpL(?ODFI=?tOc=hJ;KmR^|IKF)K+pEJP@5|9h z_?_a#%Z5DZNV zYA*$)lTcpMpvJJww+lrR1VI&w2F%_fjLXfQQ$1`6go~v+wfI_Z&`~&auU4=flx8HRCVZEtn#g)&-o$5ylVA2f?*6dL%Ma+#s zJg`toQ?My*XhD@FB;vrbPPRHnfz@II1{PX_(2HeAahQjzDw+HwyMmc0LNnf-*8{fY z^@`Wi+Kg^_e#`S46o6ZLP}X(j)j$Z=#Y%($inKpK2slH3iiHGeOTjJ+F{QHInS=4b zDa)GxgE7AeEpY?;Grq?DdGEZgG_fPH+grjGIW)aw^7nC18sinYp0N{HY4|;}J(GSK zdP}6|vh=#=UqJ4?8~PIdqcy!r+Vih2_Nq&n{Uw|2Q)0Z&a~S3kH|=P@6&?2 z`VNeV`Q?OvaP+w;5+dky2S*PGgYF^1pk5eoQ+GqKu#j|^zqE(=-t@Eo}L#5TpJF6Q{PEaDg5^CHK5dgUl- zo#NSMf!89D8#ZMw&Q6YwKr1*uIQ#8z?>;u_8lF)@HK7!7j6>iKju9u!ZU zkQ_R{Ci{buJ@ zRzEsR{t)LPoCQ{pNFq?e4P?ax39L2JV4hlk9S0%TdT}nop6pztOTX%b!nWrk>vY`d z9e_2_qLffV8w#bO(0gD9NC86tSW(JM7!Yh*Ewa#I>%;GRI;Sgi9)yu}T-RK4F3OG1 zaj^v|%YB!dVoF3LDlKl1cn=!5K8%RAAUZ*o5->)!XZ$!ID55G>hS-4V)?MJh5Sib9 z%OO$Z5Fs!LlEMTAm|MXlj-s$0xE|oJM|<)UAfPw&4~H;Bpw5rBC$B(c`r-EuE^dr2 z7DEl!(m2P%<2a@vB*lYvTg7Pv){Es}Ia63sy$4kN$-<;@jBE!gv+MWN9l zi%q~t&>E9lC;$!$dQOa~K!h9OH6DlJ7(;7JMWJA|Ep!9J>b)|*PsfuSK02XP38%qa zMd4dCc|tUie!XD1wz|m{QEr1MJ`#bo00Ra~e|TzQ#A6vDE^!E!ys|NC)lE!f9N6e_ zqX`(tfpoZL2km9nBoiMmD>22w-}v?#tI&jolbjzKfAXjCK%15cfv8pr07YvQQNx&_ zCKBz(6$)~PEObqPf`z|C#+NQ=T#p8cuf&JnOS5jfC<{Lb1Pf zZlc&Se=Qqe28Y#?t9-?QZ|)xPfG1W4n z+;F5Z0U{%kBjsX$V_h>Z$&8V-iDcrrWGW9k)=1`Oql;WbFX%!%q}%D;=znqQ;oQ-d^tDy4e^s3g%DfN=4U|B8$WOZOviJ5_eaQz@7Etta zbEfu`&wiFbBfY(W9FKd)#T59ij*55A#(xfWiY%T-sI!wAgKfFMfYFKGjV z7!SE%7Q`5Bb+_ucJ5z#D+lyOUfch2ywJ84(D8*jp8+La_se0+VqZD69sesrButzw; zgTP5)wbmj~B%~21G6MZg1(F+UF%A?~!f70VmWBa`jEg`}hd3}g<}4&8e+t1$_IJGm zaG5gFU_rMi^&}_-gA)OZ2#OuBD|Of)Eg_eX?KQpclsiI+ z4aFfx#xn*DeH5R7D`bq4nu2a6+M`EkssgY`Rii->g)WZO3U?IO%+5-)xGR?w6jB+} zJJrs;>?`28ayZx{4hP~dqd<5_MyYN%Nl<)d4iUdCiMMC|i z`%>~4`dH?HY1y%#<@IP_=UMVwIb)yxU;m-k`&#_7TzfsD#AQ;oU*xJf_g^Rgqz7s6 z5k&F3{`*?4(yaFx^IUnt?8SyQi=S?4sCYZv_# zHTLqaWaR&S@;Ucsf92VeO}m_cPBpY&z7d(voBt+sK5zXg34Pw^x1sb!^WTir7t{Xm zwC?|T|8D5*p4@@~af3?dK`?5H`_Z||)-PXce^}$CMc;i8UH6BxV1$gAjHE?I3m-dH zU?eSP$OvPtj&6`onHhnWO%#jyxL} zty6!qlB`(;sYQPu8l&cEw6aup%MG+lTT6D&a&QE@Za_>pa)=CPs1&tV6kAvXezKRWW2-2rOyb=(5Y X&j&Q`e{x6o+3f!VkH29SnJ@tWEBw{m delta 8722 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 Gg8g4Vt1YGg diff --git a/etl/nifi_scripts/credit_validation.groovy b/etl/nifi_scripts/credit_validation.groovy index 31289e22f..503f83978 100644 --- a/etl/nifi_scripts/credit_validation.groovy +++ b/etl/nifi_scripts/credit_validation.groovy @@ -62,8 +62,8 @@ try { tfrsBalances[respondent] = tfrsBalances[respondent] + credits break case 2: // Buy - tfrsBalances[initiator] = tfrsBalances[initiator] + credits - tfrsBalances[respondent] = tfrsBalances[respondent] - credits + tfrsBalances[initiator] = tfrsBalances[initiator] - credits + tfrsBalances[respondent] = tfrsBalances[respondent] + credits break case 3: // Credit Validation tfrsBalances[respondent] = tfrsBalances[respondent] + credits From a6cb6fa5c6be2cbff3963f1c8bd9546658e57463 Mon Sep 17 00:00:00 2001 From: Alex Zorkin Date: Tue, 17 Dec 2024 17:31:21 -0800 Subject: [PATCH 7/7] feat: add legacy_id --- etl/nifi_scripts/compliance_report.groovy | 6 +- etl/nifi_scripts/transactions.groovy | 99 ----------------------- 2 files changed, 4 insertions(+), 101 deletions(-) delete mode 100644 etl/nifi_scripts/transactions.groovy diff --git a/etl/nifi_scripts/compliance_report.groovy b/etl/nifi_scripts/compliance_report.groovy index 6bef3f29c..f9345daa3 100644 --- a/etl/nifi_scripts/compliance_report.groovy +++ b/etl/nifi_scripts/compliance_report.groovy @@ -796,8 +796,9 @@ def prepareStatements(Connection conn) { create_user, create_date, update_user, - update_date - ) VALUES (?, ?, ?, NULL, ?, ?, ?::SupplementalInitiatorType, ?::ReportingFrequency, ?, ?, ?, ?, ?, ?) + update_date, + legacy_id + ) VALUES (?, ?, ?, NULL, ?, ?, ?::SupplementalInitiatorType, ?::ReportingFrequency, ?, ?, ?, ?, ?, ?, ?) RETURNING compliance_report_id """ @@ -867,6 +868,7 @@ def insertComplianceReport(PreparedStatement stmt, Map report, String groupUuid, stmt.setTimestamp(11, report.create_timestamp ?: Timestamp.valueOf("1970-01-01 00:00:00")) stmt.setString(12, updateUser) stmt.setTimestamp(13, report.update_timestamp ?: report.create_timestamp ?: Timestamp.valueOf("1970-01-01 00:00:00")) + stmt.setInt(14, report.compliance_report_id) def rs = null try { diff --git a/etl/nifi_scripts/transactions.groovy b/etl/nifi_scripts/transactions.groovy deleted file mode 100644 index 9cb4c40f4..000000000 --- a/etl/nifi_scripts/transactions.groovy +++ /dev/null @@ -1,99 +0,0 @@ -import groovy.json.JsonSlurper -import java.sql.Connection -import java.sql.PreparedStatement -import java.sql.ResultSet - -def SOURCE_QUERY = """ - SELECT - ct.id AS transaction_id, - ct.initiator_id AS initiator_id, - ct.respondent_id AS respondent_id, - ct.date_of_written_agreement AS agreement_date, - ct.trade_effective_date AS transaction_effective_date, - ct.number_of_credits AS quantity, - ct.create_user_id AS create_user, - ct.create_timestamp AS create_date, - ct.update_user_id AS update_user, - ct.update_timestamp AS update_date, - ctt.the_type AS transaction_type, - ct.fair_market_value_per_credit AS price_per_unit - FROM - credit_trade ct - JOIN credit_trade_type ctt ON ct.type_id = ctt.id - JOIN credit_trade_status cts ON ct.status_id = cts.id - WHERE - ctt.the_type IN ('Credit Validation', 'Part 3 Award', 'Credit Reduction', 'Administrative Adjustment') - AND cts.status = 'Approved'; -""" - -// Fetch connections to both the source and destination databases -def sourceDbcpService = context.controllerServiceLookup.getControllerService('3245b078-0192-1000-ffff-ffffba20c1eb') -def destinationDbcpService = context.controllerServiceLookup.getControllerService('3244bf63-0192-1000-ffff-ffffc8ec6d93') - -Connection sourceConn = null -Connection destinationConn = null - -try { - sourceConn = sourceDbcpService.getConnection() - destinationConn = destinationDbcpService.getConnection() - destinationConn.setAutoCommit(false) - - def transactionStmt = destinationConn.prepareStatement(''' - INSERT INTO transaction ( - compliance_units, organization_id, transaction_action, effective_status - ) VALUES (?, ?, ?::transaction_action_enum, TRUE) - RETURNING transaction_id - ''') - - PreparedStatement sourceStmt = sourceConn.prepareStatement(SOURCE_QUERY) - ResultSet resultSet = sourceStmt.executeQuery() - - int recordCount = 0 - - while (resultSet.next()) { - recordCount++ - def transactionType = resultSet.getString('transaction_type') - def organizationId = resultSet.getInt('respondent_id') - def quantity = resultSet.getInt('quantity') - def action = 'Adjustment' - - // Adjust quantity for 'Credit Reduction' transactions - if (transactionType == 'Credit Reduction') { - quantity = -quantity - } - - if (organizationId > 0 && quantity != null) { - insertTransaction(transactionStmt, organizationId, quantity, action) - } else { - log.warn("Skipping transaction_id ${resultSet.getInt('transaction_id')}: Missing required data.") - } - } - - resultSet.close() - destinationConn.commit() - log.debug("Processed ${recordCount} records successfully.") -} catch (Exception e) { - log.error('Error occurred while processing data', e) - destinationConn?.rollback() - throw e // Rethrow the exception to allow NiFi to handle retries or failure routing -} finally { - // Close resources in reverse order of their creation - if (transactionStmt != null) transactionStmt.close() - if (resultSet != null) resultSet.close() - if (sourceStmt != null) sourceStmt.close() - if (sourceConn != null) sourceConn.close() - if (destinationConn != null) destinationConn.close() -} - -def insertTransaction(PreparedStatement stmt, int orgId, int quantity, String action) { - stmt.setInt(1, quantity) - stmt.setInt(2, orgId) - stmt.setString(3, action) - - def result = stmt.executeQuery() - if (result.next()) { - def transactionId = result.getInt('transaction_id') - log.debug("Inserted transaction_id ${transactionId} for organization_id ${orgId}") - } - result.close() -}